OSMF 0.93 Examples

Posted: 30/03/10

An image of OSMF 0.93 Examples

So we're told that "API Lockdown" is now in effect - the point at which we can all rest a little easier a night, safe in the knowledge that the API won't have changed again before we've managed to get back to the office! This is great news as it means we're in a position where there's a high level of confidence in the API, and we can consider our production implementations using this framework. Things have largely got better; the amount of code you now need to write to get at most of the features is low in comparison to what you'd have to write to create this functionality in your app from scratch. Below are a load of examples that I've been refactoring which each sprint, and now includes code to demonstrate sub-clipping.

Some notable changes in these examples from previous ones:

1) You now create an instance of MediaPlayer and add your MediaElements directly to it.

2) MediaPlayerSprite is no more. MediaContainer replaces the functionality that it used to perform; however, it no longer encapsulates a MediaPlayer instance, instead bonding a relationship to the MediaPlayer through the current MediaElement.

3) Concrete MediaElement types now all live in the org.osmf.elements package and packages for specific media types (such as video) have been removed.

4) The constructor for LoadableElementBase has changed, and the URLResource object now accepts a String on its constructor rather than requiring a URL object - which has been removed.

5) TemporalProxyElement is now called DurationElement, and lives in the elements package with the other concrete MediaElement types.

If you're new to OSMF - 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 Intro to OSMF post, as this will help you get your workspace set-up, then refer back here for the code.

Example code

Health warning:

Please note that these examples relate specifically to OSMF v0.93 and will not work with earlier versions of the framework. The framework is in beta at the time of writing and 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. Despite being in API lockdown, please tread with caution when considering using this in a production app.

Now the fun bit. Feel free to copy and paste the code into your IDE and compile as you wish.

HTTP/Progressive download

This example loads a single video into the MediaPlayer instance over HTTP, and then displays it using the MediaContainer object in the display list. Notice that the VideoElement is added to both the MediaPlayer and the MediaContainer. This is how the link between the player and the container (the visual output) is created.

package

{

import flash.display.Sprite;

import org.osmf.containers.MediaContainer;

import org.osmf.elements.VideoElement;

import org.osmf.events.LoaderEvent;

import org.osmf.media.MediaPlayer;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

public class BasicOSMFProgressive extends Sprite

{

private const PROGRESSIVE:String = "http://localhost/video.mp4";

private var _display:MediaContainer;

private var _player:MediaPlayer;

private var _netLoader:NetLoader;

public function BasicOSMFProgressive()

{

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

var media:VideoElement = new VideoElement( new URLResource( PROGRESSIVE ), _netLoader );

_player = new MediaPlayer();

_player.media = media;

_display = new MediaContainer();

_display.addMediaElement( media );

addChild( _display );

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

}

}

Adding a LOAD_STATE_CHANGE listener to the NetLoader instance allows you to listen to when load operations start and complete.

To display the video, I simply add an instance of MediaContainer to the display list, which avoids me needing to add/remove the MediaPlayer instance's display object each time it loads new media. MediaContainer checks the traits of the MediaElement that MediaPlayer has loaded to check whether or not it should be visible, saving you the hassle.

RTMP Streaming - you'll need the free FMS development server running for this example.

This is pretty much the same as the progressive example, only the video is streamed from an FMS server, rather than being progressively downloaded.

package

{

import flash.display.Sprite;

import org.osmf.containers.MediaContainer;

import org.osmf.elements.VideoElement;

import org.osmf.events.LoaderEvent;

import org.osmf.media.MediaPlayer;

import org.osmf.net.NetLoader;

import org.osmf.net.StreamingURLResource;

public class BasicOSMFStream extends Sprite

{

private const STREAM:String = "rtmp://localhost/vod/mp4:video.mp4";

private var _display:MediaContainer;

private var _player:MediaPlayer;

private var _netLoader:NetLoader;

public function BasicOSMFStream()

{

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

var media:VideoElement = new VideoElement( new StreamingURLResource( STREAM ), _netLoader );

_player = new MediaPlayer( media );

_display = new MediaContainer();

_display.addMediaElement( media );

addChild( _display );

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

}

}

Sub-clipping example

This example shows how to inject clips so that they play in the middle of other streamed clips. This is a particularly useful feature when you want to include mid-roll advertisements in your streaming video.

The new API in v0.93 provides an easier way of specifying start and end points for sub-clips using the StreamingURLResource. In doing this, some confusion has been removed where it was possible in earlier APIs to add sub-clip start and end points to progressive video content, even though they would be ignored during playback.

package

{

import flash.display.Sprite;

import org.osmf.containers.MediaContainer;

import org.osmf.elements.SerialElement;

import org.osmf.elements.VideoElement;

import org.osmf.events.DisplayObjectEvent;

import org.osmf.events.LoaderEvent;

import org.osmf.events.TimeEvent;

import org.osmf.media.MediaPlayer;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

import org.osmf.net.StreamingURLResource;

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" ];

private const SHOW_PREROLLS:Boolean = false;

private var _player:MediaPlayer;

private var _masterSerialElement:SerialElement;

private var _display:MediaContainer;

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 MediaPlayer( _masterSerialElement );

_player.addEventListener( TimeEvent.CURRENT_TIME_CHANGE, onCurrentTimeChange );

_display = new MediaContainer();

_display.addMediaElement( _masterSerialElement );

addChild( _display );

}

private function createSubClip( item:String, advertUrls:Array, start:Number, end:Number ) :void

{

var resource:URLResource = new StreamingURLResource( item, null, start, end == 0 ? NaN : end );

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

for each( var advert:String in advertUrls )

{

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

}

}

private function addPreRolls( advertUrls:Array ) :void

{

for each( var advert:String in advertUrls )

{

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

}

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

private function onCurrentTimeChange( e:TimeEvent ) :void

{

trace( e.time );

}

}

}

Notice how you can use a single instance of NetLoader with all of your VideoElement instances; this reduces object creation in your application.

Serial composition example using an XML playlist

In this example, I load an XML file in that contains a playlist with media assigned to each hour of the day. The app parses the XML dependant on the current time on the user's system clock. So, if they start the app at 13:03, they'll be shown the media that was due to start at 1pm, and every piece of media consecutively thereafter. There's a mixture of audio, video and image content, so during the parsing the type attribute assigned to each item in the playlist is checked to ensure the correct MediaElement is created, before adding it to the composition media element (SerialElement).

package

{

import flash.display.Sprite;

import flash.events.Event;

import flash.events.IOErrorEvent;

import flash.net.URLLoader;

import flash.net.URLRequest;

import org.osmf.containers.MediaContainer;

import org.osmf.elements.AudioElement;

import org.osmf.elements.DurationElement;

import org.osmf.elements.ImageElement;

import org.osmf.elements.ImageLoader;

import org.osmf.elements.SerialElement;

import org.osmf.elements.SoundLoader;

import org.osmf.elements.VideoElement;

import org.osmf.events.LoaderEvent;

import org.osmf.media.MediaPlayer;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

public class PlaylistOSMFPlayer extends Sprite

{

private const PLAYLIST_LOCATION:String = "http://localhost/osmfplaylist.xml";

private var _composition:SerialElement;

private var _loader:URLLoader;

private var _player:MediaPlayer;

private var _display:MediaContainer;

private var _playlist:XML;

private var _netLoader:NetLoader;

private var _imageLoader:ImageLoader;

private var _soundLoader:SoundLoader;

public function PlaylistOSMFPlayer()

{

loadPlaylist();

}

private function loadPlaylist() :void

{

_loader = new URLLoader();

_loader.addEventListener( Event.COMPLETE, onPlaylistLoaded );

_loader.addEventListener( IOErrorEvent.IO_ERROR, onPlaylistFail );

_loader.load( new URLRequest( PLAYLIST_LOCATION ) );

}

private function onPlaylistLoaded( e:Event ) :void

{

_playlist = XML( e.target.data );

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

_imageLoader = new ImageLoader();

_imageLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

_soundLoader = new SoundLoader();

_soundLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

parsePlaylist();

playPlaylist();

}

private function parsePlaylist() :void

{

_composition = new SerialElement();

var currentHour:int = new Date().getHours();

for each( var media:XML in _playlist.media )

{

if( currentHour <= int( String( media.@startTime ).split(":")[0] ) )

{

switch( media.@type.toString() )

{

case "video":

_composition.addChild( new VideoElement( new URLResource( media.@source ), _netLoader ) );

break;

case "advert":

_composition.addChild( new VideoElement( new URLResource( media.@source ), _netLoader ) );

break;

case "image":

_composition.addChild( new DurationElement( 5, new ImageElement( new URLResource( media.@source ), _imageLoader ) ) );

break;

case "audio":

_composition.addChild( new AudioElement( new URLResource( media.@source ), _soundLoader ) );

break;

}

}

}

}

private function playPlaylist() :void

{

if( _composition.numChildren )

{

_player = new MediaPlayer( _composition );

_display = new MediaContainer();

_display.addMediaElement( _composition );

addChild( _display );

}

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

private function onPlaylistFail( e:IOErrorEvent ) :void

{

trace( "The playlist failed to load" );

}

}

}

Here's the playlist XML document (osmfplaylist.xml) that the code above attempts to load:

<?xml version="1.0" encoding="utf-8"?>

<playlist>

<media startTime="00:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="01:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="02:00" type="video" source="http://localhost/b2.mp4" title="video2"/>

<media startTime="03:00" type="audio" source="http://localhost/2009_25a.mp3" title="audio1"/>

<media startTime="04:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="05:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="06:00" type="video" source="http://localhost/b2.mp4" title="video2"/>

<media startTime="07:00" type="audio" source="http://localhost/2009_25a.mp3" title="audio1"/>

<media startTime="08:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="09:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="11:00" type="video" source="http://localhost/b2.mp4" title="video2"/>

<media startTime="12:00" type="audio" source="http://localhost/2009_25a.mp3" title="audio1"/>

<media startTime="13:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="14:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="15:00" type="image" source="http://localhost/welcome.png" title="image1"/>

<media startTime="16:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="17:00" type="audio" source="http://localhost/2009_25a.mp3" title="audio1"/>

<media startTime="18:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="19:00" type="image" source="http://localhost/welcome.png" title="image1"/>

<media startTime="20:00" type="audio" source="http://localhost/2009_25a.mp3" title="audio1"/>

<media startTime="21:00" type="video" source="http://localhost/a2.mp4" title="video1"/>

<media startTime="22:00" type="advert" source="http://localhost/pcworld.flv" title="advert1"/>

<media startTime="23:00" type="video" source="http://localhost/b2.mp4" title="video2"/>

</playlist>

Don't forget that there's now a thriving OSMF community that you can participate in at http://adobe.com/go/osmf_usergroup.

You can also follow @OSMF on Twitter.

Visit the Adobe Open Source OSMF site

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