Subclips in OSMF

Posted: 01/03/10

An image of Subclips in OSMF

I've been very interested in the capabilities of the Open Source Media Framework (OSMF) to allow the division of a single piece of media into virtual sub-clips; allowing the insertion of other pieces of media in between these pseudo chapter-markers.

In short sub-clipping could be explained as taking a single video file, specifying chapter markers as required, then playing or showing another piece of media, before resuming the video file again. For me, this screams adverts! Love it or loathe it, advertising makes the Internet go-round, and it is becoming an increasingly popular way of making premium video content free for consumers to watch online.

In addition to updating my previous OSMF examples to keep pace with the API changes in 0.9, I wanted to demonstrate an implementation of the very useful sub-clip functionality offered by the framework.

In the example below I demonstrate how two 'feature' clips can have pre-roll ads placed at the start of them, and also be injected with mid-roll ads - without the need to modify the source media or the playlist. Using Sub-Clip Meta-data I'm able to specify a point on the source media where OSMF will need to pause it, and begin playing a sequence of video advertisements. Once the adverts have completed, OSMF will then resume playback of the source media.

For my example, I am mimicking a typical Video-on-demand (VOD) set-up, where the 'feature' content is streamed from FMS over RTMP, and the shorter advert clips are progressively downloaded over HTTP.

Please note: A newer version of my subclip example is available - these examples relate specifically to OSMF v0.9 and will not work with earlier/newer versions of the framework. The framework is in beta at the time of writing; whilst I hope it will be helpful in the months to follow, I can't offer assurances that this code will work with a later 1.0 version.

Feel free to copy and paste this code into your favourite code editor and have a play:

package

{

import flash.display.Sprite;

import org.osmf.composition.SerialElement;

import org.osmf.display.MediaPlayerSprite;

import org.osmf.events.DisplayObjectEvent;

import org.osmf.events.LoaderEvent;

import org.osmf.events.TimeEvent;

import org.osmf.media.URLResource;

import org.osmf.metadata.KeyValueFacet;

import org.osmf.metadata.MetadataNamespaces;

import org.osmf.net.NetLoader;

import org.osmf.utils.FMSURL;

import org.osmf.utils.URL;

import org.osmf.video.VideoElement;

public class OSMFSubClip extends Sprite

{

private const PLAYLIST:Array = [ "rtmp://localhost/vod/mp4:feature1.mp4",

"rtmp://localhost/vod/mp4:feature2.mp4" ];

private const ADVERTS:Array = [ "http://localhost/advert1.flv",

"http://localhost/advert2.flv" ];

private const SHOW_PREROLLS:Boolean = true;

private var _player:MediaPlayerSprite;

private var _masterSerialElement:SerialElement;

private var _netLoader:NetLoader;

public function OSMFSubClip()

{

init();

}

private function init() :void

{

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

_masterSerialElement = new SerialElement();

for each( var item:String in PLAYLIST )

{

if( SHOW_PREROLLS )

{

addPreRolls( ADVERTS );

}

createSubClip( item, ADVERTS, 0, 15 );

createSubClip( item, null, 15, 0 );

}

_player = new MediaPlayerSprite();

_player.mediaPlayer.addEventListener( DisplayObjectEvent.DISPLAY_OBJECT_CHANGE, onDisplayObjectChange );

_player.mediaPlayer.addEventListener( TimeEvent.CURRENT_TIME_CHANGE, onCurrentTimeChange );

_player.visible = false;

_player.mediaElement = _masterSerialElement;

addChild( _player );

}

private function createSubClip( item:String, advertUrls:Array, start:int=0, end:int=0 ) :void

{

var resource:URLResource = new URLResource( new FMSURL( item ) );

_masterSerialElement.addChild( new VideoElement( _netLoader, resource ) );

var keyFacet:KeyValueFacet = new KeyValueFacet( MetadataNamespaces.SUBCLIP_METADATA );

if( start )

{

keyFacet.addValue( MetadataNamespaces.SUBCLIP_START_ID, start );

}

if( end )

{

keyFacet.addValue( MetadataNamespaces.SUBCLIP_END_ID, end );

}

resource.metadata.addFacet( keyFacet );

for each( var advert:String in advertUrls )

{

_masterSerialElement.addChild( new VideoElement( _netLoader, new URLResource( new URL( advert ) ) ) );

}

}

private function addPreRolls( advertUrls:Array ) :void

{

for each( var advert:String in advertUrls )

{

_masterSerialElement.addChild( new VideoElement( _netLoader, new URLResource( new URL( advert ) ) ) );

}

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

trace( "MediaElement is: " + e.newState );

}

private function onCurrentTimeChange( e:TimeEvent ) :void

{

trace( e.time );

}

private function onDisplayObjectChange( e:DisplayObjectEvent ) :void

{

_player.visible = Boolean( e.newDisplayObject );

}

}

}

Integral to a successful implementation is understanding the KeyValueFacet object and the resource metadata property.

If you're new to OSMF, and particularly if you haven't yet set-up your development environment to start writing OSMF apps, might I suggest you read through Getting set up on the my Intro to OSMF post, as this will get you up and running, then refer back here for the code examples.

You can read more about Subclips in the specification

Keywords for this post: osmf, open source, adobe, media, framework, video, stream, progressive, audio, image, actionscript 3, flash, flex