Subclips in OSMF
Posted: 01/03/10
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
Jodie's Twitter
Popular categories:
Recent posts:
- I'll be at Adobe MAX
- Flash Camp Birmingham
- Flash Media Playback launches
- Flash Builder to Android workflow
- Strobe Media Playback presentation
- Flash Camp Manchester
- Welcome OSMF 1.0!
- Get the best out of blinkbox on PS3
- H.264 Flash Lite 3.1 on Nintendo Wii
- OSMF plug-in example
- more...
Places you'll find me:
Archive:
- August 2010
- July 2010
- June 2010
- May 2010
- April 2010
- March 2010
- February 2010
- January 2009
- December 2009
- November 2009
- October 2009
- September 2009
