Send metadata to flash client thru serverside API

Hi everyone,

I play live stream via flash client. My main goal is to send duration of stream to client. I found out we can do it passing this duration to metadata. In Wowza server side API in Publisher class there is a method addDataData(). Using it we can send matadata to client. Here is the question: how to correctly invoke this method to achieve this ?

Thanks

Try this:

IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("duration", new AMFDataItem(240);
stream.sendDirect("@setDataFrame","onMetaData",data);

Richard

Yes, it should work. For example in the IMediaStreamActionNotify2 onPlay handler.

You can also insert cuepoints similarly:

IMediaStream stream = client.getAppInstance().getStreams().getStream("yourStream");
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("message", new AMFDataItem("hello Wowza"));
stream.sendDirect("clientsideCallbackFunction", data);

client-side:

var clientObj:Object = new Object();
clientObj.clientsideCallBackFunction(data:Object):void
{
trace(data.message);
});
netstream.client = clientObj;
netstream.play("myStream");

Richard

Is the onPlay handler in the context of IMediaStreamActionNotify interface?

Has to be setup like this:

https://www.wowza.com/docs/imediastreamactionnotify2-example

Richard

I’m not sure what this line is:

out("RTMPMediaEventsListener","onPlay()");

Put a break poin in the IDE, or getLogger statement, to see if you are hitting that line. Put a break point or trace on the client-side as well.

Richard

You can use the LiveVideoStreaming example. There is an AS3 version with FLA for Flash CS, or a Flex Builder 3 version. In either other one look for:

nsPlayClientObj.onMetaData = function(infoObject:Object):void
{
	for (var propName:String in infoObject)
	{
		trace("  "+propName + " = " + infoObject[propName]);
	}
}

You are missing a paren in your snip. Start with this:

IMediaStream stream = client.getAppInstance().getStreams().getStream("myStream");
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("message", new AMFDataItem("HELLO WOWZA"));
stream.sendDirect("@setDataFrame","onMetaData",data);

Richard

Use cuepoints instead. It’s very similar. See 2nd (compact) example in this post:

https://www.wowza.com/docs/how-to-inject-cue-points-or-metadata

Server-side:

String streamname = params.getString(PARAM1);
String caption =  params.getString(PARAM2);
IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);	
AMFDataMixedArray data = new AMFDataMixedArray();
data.put("caption", new AMFDataItem(capton);
stream.sendDirect("setCaption", data);

Flash client-side:

var nsPlayClientObj:Object = new Object();
nsPlayClientObj.setCaption = function(obj:Object):void
{
	trace(obj.caption)
}
nsPlay.client = nsPlayClientObj;

Richard

Pedro,

Sounds like the Application.xml /StreamType is “default”. It should be “live”.

Make sure the StreamType is “live”, and that Application.xml is below conf:

/conf[app-name]/Application.xml

And that there is no xml errors in the Application.xml. That would cause the default /conf/Application.xml to be used. Check the Wowza access and error logs, it will report any problems with the configuration file around the app-start event for that application.

Richard

Pedro,

That’s is the way metadata works, the player gets onMetaData event once per stream. Using the Stream class (e.g. the Scheduler), the player can get onMetaData on switch, as you describe

Richard

Are you adding “cuepoints” actually? Something like this:

public void setCaption (IClient client, RequestFunction function, AMFDataList params)
	{
		String streamname = params.getString(PARAM1);
		String caption =  params.getString(PARAM2);
		IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);
		
		//essential code
		AMFDataMixedArray data = new AMFDataMixedArray();
		data.put("caption", new AMFDataItem(capton);
		stream.sendDirect("setCaption", data);
		getLogger().info("Caption: " + caption);
	}

This method will add data to the stream that will trigger a callback in the client if it is setup. Which is something like:

// set up netstream for callback on recorded stream
var nsPlayClientObj:Object = new Object();
nsPlayClientObj.setCaption = function(obj:Object):void
{
	trace(obj.caption)
}
nsPlay.client = nsPlayClientObj;

The callback that works for a live stream will also work for vod, if you recorded in Wowza, when you playback.

Rciared

Pedro,

Server-side, you are inserting two cuepoints at the same time. I don’t think this can work, or one might work but not the other. Probably the 2nd one might work, setCaption, but you are not handling cuepoint named setCaption in the client.

And you are naming one of the cuepoints “onMetaData”, which is probably okay, but very confusing. I would use any other name.

Cuepoints are associated with a frame in the video and the function is triggered when that frame plays. Metadata is associated with the stream. The onMetaData event is built in and is triggered when the stream starts. See the source code for the Wowza example players: LiveVideoStreaming and SimpleVideoStreaming for how onMetaData event is handled.

Richard

I copied that to a module to try it, but after adding the imports, there is still a bunch of other stuff going on. You have to step through in in your environment, use the Wowza debugger, see if it hits that line:

auxStream.sendDirect("setCaption",data);

Suggest you simplify your code enough so this basic feature is working, then build your custom logic up again around that.

Richard

Also, onStreamCreate is probably not the place to do this. onStreamCreate runs for publish and play streams.

Implement IMediaStreamActionNotify2 interface, and use its onPublish event handler instead:

https://www.wowza.com/docs/imediastreamactionnotify2-example

But strip it down still, make sure that line is getting hit.

Richard

Pedro,

onPublish can run many times for a connection using an encoder like FMLE or Wirecast. In FMLE every time you click the Start button you should get onPublish event, and an onUnPublish event when you click Stop. The connect/disconnect button will trigger onConnect and onDisconnect

Richard

You have to inject metadata per stream. You can handle multiple streams in one application instance. onPublish will run once for each stream.

Richard

Pedro,

At present this only works for Flash RTMP (and possibly Flash HTTP). Eventually there will be examples of an equivalent for iOS with ID3 tags, and in Smooth streams maybe. I don’t think there will ever be an equivalent of this for RTSP.

Richard

There is live example below the vod example:

https://www.wowza.com/docs/how-to-add-poster-frames-to-apple-http-streams-id3-metadata-for-app-store-audio-renditions)

Richard

I can call this code in any place after stream was created ?

Richard,

Thank you a lot for your reply

But unfortunatelly this does not work:

Code:

public void onPlay(IMediaStream stream, String streamName, double playStart, double playLen, int playReset)
	{
		out("RTMPMediaEventsListener","onPlay()");
		AMFDataMixedArray data = new AMFDataMixedArray();
		data.put("message", new AMFDataItem("hello Wowza"));
		stream.sendDirect("clientsideCallbackFunction", data);
		
	}

client-side:

Code:

nsPlay = new NetStream(nc);
			nsPlay.addEventListener(NetStatusEvent.NET_STATUS, nsOnStatus);
			
			var nsPlayClientObj:Object = new Object();
						
			nsPlayClientObj.clientsideCallbackFunction = function(data:Object):void {
				trace(data.message);
			}
			
			nsPlayClientObj.onMetaData = function(infoObject:Object):void
			{
				trace("onMetaData");
				//clearInterval(progressTimer);
				
				// print debug information about the metaData
				for (var propName:String in infoObject)
				{
					trace("  "+propName + " = " + infoObject[propName]);
				}
		
				// grab the movies duration from the metadata
				if (duration == 0)
				{
					duration = infoObject.duration;
					//duration = 1000;
					slider.maximum = duration ;
					progressTimer = setInterval(updateProgress, 250);
				} 
			};		
			
			// print debug information when we encounter a cuePoint
			nsPlayClientObj.onCuePoint = function(infoObject:Object):void
			{
				trace("onCuePoint: "+infoObject.name+" ("+infoObject.type+")");
				var param:String;
				for(param in infoObject.parameters)
				{
					trace("  param: "+param+"="+infoObject.parameters[param]);
				}
			};
			
			// print debug information when we play status changes
			nsPlayClientObj.onPlayStatus = function(infoObject:Object):void
			{
				trace("onPlayStatus");
				for (var prop:String in infoObject)
				{
					trace("\t"+prop+":\t"+infoObject[prop]);
				}
			};
			
			nsPlay.client = nsPlayClientObj;
			
			// set the buffer time and attach the video and audio
			nsPlay.bufferTime = 3;
			videoObj.attachNetStream(nsPlay);
			
			// start playback
			isProgressUpdate = false;
			isPlaying = true;
			nsPlay.play(streamStr.text);

I use flash client which Wowza supplies in examples. Do you have any ideas how to make ti work ?

Thanks

Yes, onPlay is in the context of IMediaStreamActionNotify and listener is registred as in link, but I still can not call function on client… Do you have any ideas ?