Wednesday, January 27, 2010

How to retrieve XMP Data/Cuepoints using the FLVPlayback

Some users may wonder how they can retrieve the XMP data using the FLVplayback Component.

The best approach is based on the following article

http://www.adobe.com/devnet/flash/articles/flvplayback_fplayer9u3_04.html

and consist of implementing your own Custom callbacks via a custom client class


The FLVPlayback component automatically creates an instance of the class fl.video.VideoPlayerClient, assigns it to the NetStream's client property, and handles all callback messages that come through the NetStream. The default class fl.video.VideoPlayerClient handles only the callbacks onMetaData() and onCuePoint(). Both of these messages generate events (MetadataEvent.METADATE_RECEIVED and MetadataEvent.CUE_POINT) so that you do not need to register a custom class to handle these callbacks.

However, if you do wish to use custom callbacks, you must register a custom client class. You should extend fl.video.VideoPlayerClient to ensure that the onMetaData() and onCuePoint() handling supplied by VideoPlayer and FLVPlayback will continue to work properly.

The requirements for a custom client implementation are as follows:

* The constructor method must take an fl.video.VideoPlayer instance as its only parameter.
* The custom class must include a ready property which should be set to true when all messages expected at the very beginning of the NetStream have been received. This step is necessary because the VideoPlayer class initially hides the video and plays the beginning of the file to access the correct dimensions and duration of the video. It then quickly rewinds the video to the beginning and unhides it. If the rewind occurs before all messages at the very beginning of the file are received by VideoPlayer, there's a chance those messages may never be received.
* It is highly recommended that you declare the custom class as dynamic to avoid encountering runtime errors. Errors may appear if any callbacks are triggered that the class is not set up to handle.
* It is also recommended to extend fl.video.VideoPlayerClient to ensure proper onMetaData() and onCuePoint() handling.

1) Create a new extented Metadata EVENT

Copy IVPEvent.as from the source code of FLVplayback locally under your FLA project under fl\video sub folder

Use the code below to register a new extended event save it in a file ExtendedMetadataEvent.as


package fl.video {

import flash.events.Event;

/**
* Flash® Player dispatches a ExtentedMetadataEvent object when the user
* requests the FLV/F4V/H264 file's metadata information packet (NetStream.onXMPData)
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*/
public class ExtendedMetadataEvent extends fl.video.MetadataEvent {


/**
* Defines the value of the
* type property of a xmpDataReceived event object.
*
* @see FLVPlayback#event:xmpDataReceived xmpDataReceived event
* @eventType xmpDataReceived
* @langversion 3.0
* @playerversion Flash 10.0.0.0
*/
public static const XMPDATA_RECEIVED:String = "xmpDataReceived";


/**
* Creates an Event object that contains information about metadata events.
* Event objects are passed as parameters to event listeners.
*
* @param type The type of the event. Event listeners can access this information
* through the inherited type property. Possible values are MetadataEvent.XMPDATA_RECEIVED.
*
* @param bubbles Determines whether the Event object participates in the bubbling
* stage of the event flow. Event listeners can access this information through the
* inherited bubbles property.
*
* @param cancelable Determines whether the Event object can be canceled. Event listeners can
* access this information through the inherited cancelable property.
*
* @param info Determines the dynamic properties to add.
*
* @param vp Determines the index of the VideoPlayer object.
*
* @langversion 3.0
* @playerversion Flash 9.0.28.0
*
*/
public function ExtendedMetadataEvent( type:String, bubbles:Boolean=false, cancelable:Boolean=false,
info:Object=null, vp:uint=0) {
super(type, bubbles, cancelable);
super.info = info;
super.vp = vp;
}


/**
* @private
*/
override public function clone():Event
{
return new ExtendedMetadataEvent(type, bubbles, cancelable, info, vp);
}

} // class ExtendedMetadataEvent

} // package fl.video



2) Use the code below to register the custom client class:


package {

import fl.video.VideoPlayer;
import fl.video.VideoPlayerClient;
import fl.video.ExtendedMetadataEvent;

import flash.events.Event;
import flash.events.EventDispatcher;

import flash.display.Loader;
import flash.display.DisplayObjectContainer;
import flash.utils.ByteArray;


/**
* NetstreamDataVideoClient subclasses VideoPlayerClient, the default
* VideoPlayer.netStreamClientClass value. This way all
* the metadata and cue point handling built in the
* VideoPlayer works properly.
*
* The class is declared dynamic so if any other
* messages are received that we do not support in
* this class (onTextData(), other custom
* messages) no runtime errors will occur.
*/

dynamic public class NetstreamDataVideoClient extends VideoPlayerClient
{

/**
* This variable is set in onImageData and is used to help
* determine when the ready property should be true.
*/

protected var gotXMPData:Boolean;
protected var gotImageData:Boolean;
public var XMPString:String = "";
protected var verbose:Boolean = false;

/**
* The constructor must take a VideoPlayer as its
* only parameter. It needs to pass that argument
* along to the super constructor.
*/

public function NetstreamDataVideoClient(vp:VideoPlayer) {
super(vp);
gotImageData = false;
gotXMPData = false;
}

/**
* Handling for onImageData() message
* Loads the image bytes into a flash.display.Loader
* and adds the image to the
*/

public function onImageData(info:Object):void {
// Only handle onImageData() once. Any time we seek to the
// start of the file, the message will call this function
// again
trace("onImageData");
if (gotImageData)
return;
var loader:Loader = new Loader();
loader.loadBytes(info.data);
var parent:DisplayObjectContainer = _owner.root as DisplayObjectContainer;
if (parent) {
parent.addChildAt(loader, 0);
}
gotImageData = true;
}

public function onTextData(info:Object):void {
trace(info);
recurseTrace(info, "");
}


// FOR LOCAL FILE OR HTTP
public function onXMPData(info:Object):void {
if (gotXMPData)
return;

if (verbose)
trace("NetstreamDataVideoClient onXMPData");

// F4V/H264 uses data to pass raw XMP
if (info.data!= undefined)
{
if (verbose)
trace(info.data);
}
else
{
if (info.liveXML!= undefined)
{
// FLV uses liveXML to pass raw XMP
if (verbose)
trace(info.liveXML);
}
}

gotXMPData = true;

// for (var i in info)
// {
// trace(i + " is " + info[i]);
// }
// trace("####\n");

// XMP came from Adobe Media Encoder for Flash
if (info.data!= undefined)
{
XMPString = info.data;

_owner.dispatchEvent(new ExtendedMetadataEvent(ExtendedMetadataEvent.XMPDATA_RECEIVED, false, false, info));
}
else
{
if (info.liveXML!= undefined)
{
XMPString = info.liveXML;

_owner.dispatchEvent(new ExtendedMetadataEvent(ExtendedMetadataEvent.XMPDATA_RECEIVED, false, false, info));
}
}
}


private function recurseTrace(info:Object, indent:String):void
{
for (var i:* in info) {
if (typeof info[i] == "object") {
trace(indent + i + ":");
recurseTrace(info[i], indent + " ");
} else {
trace(indent + i + " : " + info[i]);
}
}
}

/**
* property that specifies whether early messages have been
* received so it is OK for the player to rewind back to the
* beginning. If we allow the VideoPlayer to rewind before
* all messages at the very beginning of the file are received,
* we may never receive them.
*
* The default class, VideoPlayerClient, only requires
* onMetaData() to be received before rewinding. onImageData()
* also appears at the beginning of the file, so we might miss
* that if we do not override this property and include a check
* for this data.
*/

override public function get ready():Boolean {
return (super.ready && gotImageData);
}

}
}


IMPORTANT: Don't try to parse the XMP data in the callback as it might be causing some timing issue and you want to give the hand back to the Netstream Object ASAP. Instead fire an event back to your client event listener and make sure you will interpret the data when the video will be in a READY to seek state.


3) Receive XMP Data using FLVPlayback

import NetstreamDataVideoClient;
import fl.video.ExtendedMetadataEvent;
import flash.events.*;
import fl.video.*;

VideoPlayer.netStreamClientClass = NetstreamDataVideoClient;

_playback = new FLVPlayback();
_playback.width = 1280;
_playback.height = 720;
_playback.autoRewind = false;
_playback.bufferingBarHidesAndDisablesOthers = true;
_playback.visible = true;
addChild(_playback);
_playback.addEventListener(MetadataEvent.METADATA_RECEIVED, metadataListener);
// the XMP one
_playback.addEventListener(ExtendedMetadataEvent.XMPDATA_RECEIVED,xmpListener);



private function xmpListener(inEvt:fl.video.ExtendedMetadataEvent):void {
var XMPString:String = "";
if (this.verbose)
trace("--- xmpListener ----");
if (this.verbose) {
if (inEvt.info.data!= undefined)
{
XMPString = inEvt.info.data;

trace("RECEIVED XMP DATA = " + XMPString);
// ready to parse
}
else
{
if (inEvt.info.liveXML!= undefined)
{
XMPString = inEvt.info.liveXML;

trace("RECEIVED XMP DATA = " + XMPString);
// ready to parse
}
}
}
}


Hope you enjoy this article !
Time to interpret the data now

http://younsi.blogspot.com/2008/12/interactive-video-in-flash-using-f4v.html

and store some useful XMP Data

http://www.actimus.com/

Free AS3 Interactive Video FLVPlayback component for FLASH CS5

http://shop.actimus.com/product_info.php?products_id=122

No comments: