• How to inject cue points or metadata

    This article provides example code for injecting cuepoints (timed data) and metadata into live streams and the recordings of live streams, for use in RTMP clients. Cuepoints can also be converted to ID3 tags for HLS playback.

    Note: This article describes examples that work with closed captioning. Wowza Media Server 3.5 and later includes a more complete closed-captioning implementation. For more information, see the Closed Captioning Overview article.
    package com.wowza.example.module;
    
    import com.wowza.wms.amf.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.request.*;
    import com.wowza.wms.stream.*;
    
    public class ModuleInjectData extends ModuleBase {
    
    	public void setCaption (IClient client, RequestFunction function, AMFDataList params)
    	{
    		String streamname = params.getString(PARAM1);
    		String text =  params.getString(PARAM2);
    		String language = params.getString(PARAM3);
    		String trackid = params.getString(PARAM4);
    
    		IMediaStream stream = client.getAppInstance().getStreams().getStream(streamname);
    
    		//essential code
    		AMFDataMixedArray data = new AMFDataMixedArray();
    		data.put("text", new AMFDataItem(text));
    		data.put("language", new AMFDataItem(language));
    		data.put("trackid", new AMFDataItem(trackid));
    		stream.sendDirect("onTextData", data);
    		((MediaStream)stream).processSendDirectMessages();
    		getLogger().info("Caption: " + text);
    	}
    
    	public void injectMetaData(IClient client, RequestFunction function, AMFDataList params)
    	{
    		String streamName = params.getString(PARAM1);
    		String data =  params.getString(PARAM2);
    		IMediaStream stream = client.getAppInstance().getStreams().getStream(streamName);
    		if (stream != null)
    		{
    			AMFDataList amfList = new AMFDataList();
    			
    			amfList.add(new AMFDataItem("@setDataFrame"));
    			amfList.add(new AMFDataItem("onMetaData"));
    			
    			AMFDataMixedArray metaData = new AMFDataMixedArray();
    			
    			metaData.put("param1", data);
    			metaData.put("param2", new AMFDataItem("data2"));
    
    			amfList.add(metaData);
    			
    			synchronized(stream)
    		    {
    				byte[] dataData = amfList.serialize();
    				int size = dataData.length;
    	            long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
    	            stream.setDataTC(timecode);
    	            stream.setDataSize(size);
    	            stream.startDataPacket();
    	            stream.addDataData(dataData, 0, size);
    		    }
    		}
    	}
    }
    Flash Client 1, a live stream player with controls to inject the caption:
    // call the function:
    netconnection.call("setCaption",null,"myStream","hello room","eng","0");
    Flash Client 2, a VOD player:
    // set up netstream for callback on recorded stream
    // set up netstream for the callback on the live stream
    nsPlayClientObj:Object = new Object();
    nsPlayClientObj.onTextData = function(obj:Object):void
    {
    trace(obj.text);
    }
    nsPlay.client = nsPlayClientObj;
    This following is a legacy method:
    package com.wowza.example.module;
    
    import com.wowza.wms.amf.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.module.*;
    import com.wowza.wms.request.*;
    import com.wowza.wms.stream.*;
    
    public class ModuleInjectData extends ModuleBase {
    
    	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);
    
    		AMFDataList amfList = new AMFDataList();
    
    		AMFDataMixedArray data = new AMFDataMixedArray();
    		data.put("caption", new AMFDataItem(caption));
    
    		amfList.add(new AMFDataItem("setCaption"));
    		amfList.add(data);
    
    		String streamtype = stream.getClient().getAppInstance().getStreamType().substring(0,11);       
    		if (streamtype.equals("live-record"))
    		{
    			stream.send("setCaption", caption); // this works on the live stream.
    		}
    
    		byte[] dataData = amfList.serialize();
    		int size = dataData.length;
    		synchronized(stream)
    		{
    			long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
    			stream.setDataTC(timecode);
    			stream.setDataSize(size);
    			stream.addDataData(dataData, 0, size);
    		}
    
    		getLogger().info("Caption: " + caption);
    	}
    
    	public void injectMetaData(IClient client, RequestFunction function, AMFDataList params)
    	{
    		String streamName = params.getString(PARAM1);
    		String data =  params.getString(PARAM2);
    		IMediaStream stream = client.getAppInstance().getStreams().getStream(streamName);
    		if (stream != null)
    		{
    			AMFDataList amfList = new AMFDataList();
    			
    			amfList.add(new AMFDataItem("@setDataFrame"));
    			amfList.add(new AMFDataItem("onMetaData"));
    			
    			AMFDataMixedArray metaData = new AMFDataMixedArray();
    			
    			metaData.put("param1", data);
    			metaData.put("param2", new AMFDataItem("data2"));
    
    			amfList.add(metaData);
    			
    			synchronized(stream)
    		    {
    				byte[] dataData = amfList.serialize();
    				int size = dataData.length;
    	            long timecode = Math.max(stream.getAudioTC(), stream.getVideoTC());
    	            stream.setDataTC(timecode);
    	            stream.setDataSize(size);
    	            stream.startDataPacket();
    	            stream.addDataData(dataData, 0, size);
    		    }
    		}
    	}
    }

    Originally Published: 10-03-2010.

    If you're having problems or want to discuss this article, post in our forum.