• How to insert a pre-roll or mid-roll for video on demand playback in Flash RTMP client

    This example shows how to create a video on demand, server-side playlist. In this example, the requested video is played in two parts with a midroll in between. You could play any number of streams whole or in part as one stream. You can use the stream name requested by the client along with other videos (as in this example) or substitute anything you want.

    This requires Flash client. There is not an equivalent for non-Flash clients at present. There is a client-side work-around for iOS here, but it will not have seamless playback. Similar is possible in a Silverlight client. There is no RTSP work-around.

    Code:
    package com.wowza.wms.example;
    
    import com.wowza.wms.module.*;
    import com.wowza.wms.amf.*;
    import com.wowza.wms.client.*;
    import com.wowza.wms.request.*;
    import com.wowza.wms.stream.IMediaStream;
    import com.wowza.wms.util.StreamUtils;
    import com.wowza.wms.logging.WMSLogger;
    import com.wowza.wms.logging.WMSLoggerFactory;
    
    public class ModuleInsertMidRoll extends ModuleBase {
    	
    	public void play(IClient client, RequestFunction function,
            AMFDataList params) {
    		
    		String streamName = params.getString(PARAM1);
    		IMediaStream stream =getStream(client, function);
    		
    		String midRoll = client.getAppInstance().getProperties().getPropertyStr("MidRoll");
    		Integer featureBreak  = client.getAppInstance().getProperties().getPropertyInt("FeatureBreak", 10000);
    		
    		getLogger().info("Insert Midroll for: " + streamName);
    		
    		//Feature start
    		AMFDataList customDataList = new AMFDataList();
    		customDataList.add("play");
    		customDataList.add(0.0);
    		customDataList.add("null");
    		customDataList.add(streamName);
    		customDataList.add(new AMFDataItem(0)); // start value in milliseconds
    		customDataList.add(new AMFDataItem(featureBreak)); //duration (-1 means play to the end).  Value in milliseconds.
    		customDataList.add(new AMFDataItem(true)); //reset
    		invokePrevious(this, client, function, customDataList);
    		
    		//Midroll
    		customDataList.set(PARAM1, new AMFDataItem(midRoll));
    		customDataList.set(PARAM2, new AMFDataItem(0)); // start
    		customDataList.set(PARAM3, new AMFDataItem(-1)); // duration
    		customDataList.set(PARAM4, new AMFDataItem(false));
    		invokePrevious(this, client, function, customDataList);
    		
    		// Feature continued
    		customDataList.set(PARAM1, new AMFDataItem(streamName));
    		customDataList.set(PARAM2, new AMFDataItem(featureBreak)); // start
    		customDataList.set(PARAM3, new AMFDataItem(-1)); // duration
    		customDataList.set(PARAM4, new AMFDataItem(false));
    		invokePrevious(this, client, function, customDataList);
    
    		// To send the Flash client the correct duration you must calculate total duration and inject into the metadata.
    		// In this example we can calculate total duration by adding the duration of feature and midroll. 
    		// If you use start property > then 0 and/or duration property other than -1  in any item,
    		// you would have to adjust to get actual totalDuration for the playlist
    
    		double overrideDuration = StreamUtils.getStreamLength(client.getAppInstance(), streamName);
    		overrideDuration += StreamUtils.getStreamLength(client.getAppInstance(), midRoll);
    		
    		//Store the calculated  duration in a property of the stream to be extracted by the MediaReaderMP4InjectMetadata shown below.
    		stream.getProperties().put("overrideDuration", overrideDuration); // Value in seconds.
    
    		}
    }
    Add this Module to the /conf/[app-name]/Application.xml Module list. Add it to the bottom, make it the last Module.

    Code:
    <Module>
    	<Name>ModuleInsertMidRoll</Name>
    	<Description>ModuleInsertMidRoll</Description>
    	<Class>com.wowza.wms.example.ModuleInsertMidRoll</Class>
    </Module>
    Add these Properties to Application.xml /Properties

    <Property>
    <Name>MidRoll</Name>
    <Value>mp4:sample.mp4</Value>
    </Property>

    <Property>
    <Name>FeatureBreak</Name>
    <Value>10000</Value>
    <Type>Integer</Type>
    </Property>

    To send the calculated duration from playlist above to the Flash client, create this MediaReaderMP4InjectMetadata Class, then modify /conf/MediaReaders.xml as shown
    Code:
    package com.wowza.wms.mediareader;
    
    import java.nio.ByteBuffer;
    import java.util.List;
    import com.wowza.wms.mediareader.h264.MediaReaderH264;
    import com.wowza.wms.amf.AMFDataItem;
    import com.wowza.wms.amf.AMFDataList;
    import com.wowza.wms.amf.AMFDataMixedArray;
    import com.wowza.wms.logging.WMSLogger;
    import com.wowza.wms.logging.WMSLoggerFactory;
    
    public class MediaReaderMP4InjectMetadata extends MediaReaderH264
    {
    	public List<ByteBuffer> getMetadata()
    	{
    		List<ByteBuffer> ret = super.getMetadata();
    		WMSLogger getLogger = WMSLoggerFactory.getLogger(null); 
    
    		// Here the calculated duration stored by above is extracted.
    		// Note that the default duration is used if there is no duration property in the stream, so simple video streaming will work as usual
    		double overrideDuration = this.stream.getProperties().getPropertyDouble("overrideDuration", (double) this.duration / 1000); // Value in seconds.
    		getLogger.info("overrideDuration: " + duration);
    		
    		while(true)
    		{
    			if (ret == null)
    				break;
    			if (ret.size() <= 0)
    				break;
    			
    			ByteBuffer packet = ret.get(0);
    			
    			AMFDataList myMetadata = new AMFDataList(packet);
    
    			AMFDataMixedArray dataObj = (AMFDataMixedArray)myMetadata.get(1);
    			dataObj.put("duration", new AMFDataItem(overrideDuration));  // duration in seconds.
    			
    			byte[] data = myMetadata.serialize();
    			ByteBuffer newPacket = ByteBuffer.wrap(data);			
    			ret.set(0, newPacket);
    			break;
    		}
    		return ret;
    	}
    }
    To hook up this class edit [install-dir]/conf/MediaReaders.xml and change the BassClass for the mp4 file type like this:
    Code:
    from:
    <ClassBase>com.wowza.wms.mediareader.h264.MediaReaderH264</ClassBase>
    To:
    <ClassBase>com.wowza.wms.mediareader.MediaReaderMP4InjectMetadata</ClassBase>
    This only works for h.264 videos, but the same methodology can be used to inject calculated duration (and other metadata) into FLV streams.


    Comments 17 Comments
    1. fsilvestremorais -
      this works with JWplayer? play() method will be called?
    1. daren_j -
      Yes, any flash based player that uses RTMP.

      Daren
    1. amagee -
      Is there an analog for this that works with Cupertino streaming yet? I don't need midrolls or to be able to patch different streams together, all I need to be able to do is allow the user to be able to stream a particular section of a file and not the rest.
    1. rrlanham -
      Yes, that can be done now. Take a look a this article:

      http://www.wowza.com/forums/content....HTTP-streaming

      Richard
    1. chris098 -
      Unlike amagee, I do need the ability to do prerolls

      Is it possible to insert a server-side preroll using Cupertino and SmoothStreaming, or is the recommended solution additional client-side logic?
    1. Ajibola -
      Is there an RTSP workaround for this now? I need to implement Server Side playlists for mp3 files. Any info will help. Thanks in advance.
    1. rrlanham -
      There is not a RTSP work-around for vod playlist. Vod playlist are only support for Flash RTMP playback. There is an HLS work-around, but it is a client-side hack that is not seamless

      http://www.wowza.com/forums/content....e-work-around)

      Richard
    1. saxonytv -
      Hi Richard,

      your proposed solution for playing midroll clips is great and works on Desktop browsers with a Flash-based video client as expected.

      However, I just tested it on my mobile device (HTC One X, Android 4.1.1, with Adobe Flash Plugin) where I use the same Flash-based video player.
      The VoD stream is loaded and the midroll starts to play, but is interrupted after approx. three seconds.

      Without the midroll integration and configuration, the VoD RTMP stream is played without any problems.
      The only error I get in the Wowza error log states something like

      Code:
      WARN	server	comment	2013-05-15	16:24:28	-	-	-	-	-	231.365	-	-	-	-	-	-	-	-	Missing function: startTransmit
      but I'm not entirely sure if this entry is really related to my test case or is thrown from another application being invoked in parallel.

      Andre
    1. saxonytv -
      Quote Originally Posted by saxonytv View Post
      The only error I get in the Wowza error log states something like

      Code:
      WARN	server	comment	2013-05-15	16:24:28	-	-	-	-	-	231.365	-	-	-	-	-	-	-	-	Missing function: startTransmit
      ok, I just found a thread related to this question:

      http://www.wowza.com/forums/showthre...tTransmit-quot

      so it really seems to be an Android issue.

      Unfortunately I have not found a way on how to handle this issue so far. Whenever I playback the target midroll video as a regular file via RTMP over Wowza, everything works fine even on an Android device with Flash Player support, but when I use the exact same video file as a midroll integrated in another video clip as decribed above I get this buffer underrun message (expressed throught Missing function:starttransmit). Changing the bufferlength in the Flash video client does not seem to help, I do not know if extending ModuleBase with a custom starttransmit function on server side is the right way to go.

      André
    1. mnavas -
      I want to play some random intro video before going to the RTMP live streaming ... It means whenever start the player it need to go to the live after finishing short advertisement video only ... is it possible with the above pre roll setup ?
    1. rrlanham -
      No, that is not possible with that. The only practical way to do that is with a RTMP client, one that you are developing or know the source code and how to compile. The client plays a static video, listens for "NetStream.Play.Stop" notification in the NetStream.onPlayStatus handler, then plays the live stream.

      Richard
    1. Hunternif -
      Hello.
      This method has been working nicely so far with WMS 3, but with the lastest Wowza Streaming Engine 4 I can't find the config MediaReaders.xml anymore. Is there a way to invoke the MediaReaderMP4InjectMetadata now? Or perhaps a new way of implementing server-side vod playlists?
    1. rrlanham -
      We'll have to update the instructions for this. It will probably be next week at soonest.

      Richard
    1. gstrat88 -
      hello richard, i have used this method to create a "server side playlist" from smaller parts of a video, so that i can have smooth play.
      The problem is that when i seek to a point it tries to open all videos in order to reach the correct one...
      how could i rewrite the seek method in order to avoid this(as it causes me some side problems)? thanks...
    1. richard0806 -
      Hi Richard,
      you said will update the instructions for Wowza Streaming Engine 4 but I can't find it now? Do I miss something?
    1. rrlanham -
      hi Richard,

      This still needs to be updated. We will try to get that done as soon as possible.

      Thanks
    1. FrEaKmAn -
      Can someone please confirm this was updated.

      @gstrat88 were you able to solve your problem?