OSMF 0.9 Examples

Posted: 26/02/10

An image of OSMF 0.9 Examples

Another sprint (9 and counting) and yet another truckload of enhancements to the OSMF API. This version brings the framework much closer to what the development team are called "API Lockdown" - 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!

Key changes in these examples from earlier ones I've shown are:

1) The framework favours the use of the MediaPlayerSprite object as a DisplayObject wrapper for the MediaPlayer instance. Most developers will find using MediaPlayerSprite the easiest approach.

2) MediaPlayerCapabilityChangeEvent.VIEWABLE_CHANGE is no more. Instead use DisplayObjectEvent.DISPLAY_OBJECT_CHANGE to determine whether a piece of media can be added to the display list.

3) MediaPlayerCapabilityChangeEvent now has types like CAN_LOAD_CHANGE, CAN_SEEK_CHANGE and CAN_PLAY_CHANGE.

4) Use of a single LoaderBase (NetLoader, ImageLoader or SoundLoader) instance across all MediaElements, allowing better object pooling and easy registration for LoaderEvents

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.

Example code

PLEASE NOTE: The framework has moved on since these examples were written, please refer to the more recent 0.93 examples.

You'll see the examples here are less verbose than earlier ones, and I think this goes a long way to showing how the API changes that are being made with each successive sprint are helping the end developer.

Please note that these examples relate specifically to OSMF v0.9 and will not work with earlier 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.

HTTP/Progressive download

Notes that there have been some big changes here concerning the loading of media. Improvements to the MediaPlayer class now allow the listeners to be registered directly, and the LoadableStateChangeEvent is no more. LoadEvent is the new event to use, which exposes a loadState property that you can interrogate in the handler.

package

{

import flash.display.Sprite;

import org.osmf.display.MediaPlayerSprite;

import org.osmf.events.DisplayObjectEvent;

import org.osmf.events.LoaderEvent;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

import org.osmf.utils.URL;

import org.osmf.video.VideoElement;

public class BasicOSMFProgressive extends Sprite

{

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

private var _player:MediaPlayerSprite;

private var _netLoader:NetLoader;

public function BasicOSMFProgressive()

{

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

_player = new MediaPlayerSprite();

_player.visible = false;

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

_player.mediaElement = new VideoElement( _netLoader, new URLResource( new URL( PROGRESSIVE ) ) );

addChild( _player );

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

private function onDisplayObjectChange( e:DisplayObjectEvent ) :void

{

_player.visible = Boolean( e.newDisplayObject );

}

}

}

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

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

package

{

import flash.display.Sprite;

import org.osmf.display.MediaPlayerSprite;

import org.osmf.events.DisplayObjectEvent;

import org.osmf.events.LoaderEvent;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

import org.osmf.utils.FMSURL;

import org.osmf.video.VideoElement;

public class BasicOSMFStream extends Sprite

{

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

private var _player:MediaPlayerSprite;

private var _netLoader:NetLoader;

public function BasicOSMFStream()

{

_netLoader = new NetLoader();

_netLoader.addEventListener( LoaderEvent.LOAD_STATE_CHANGE, onLoaderStateChange );

_player = new MediaPlayerSprite();

_player.visible = false;

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

_player.mediaElement = new VideoElement( _netLoader, new URLResource( new FMSURL( STREAM ) ) );

addChild( _player );

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

private function onDisplayObjectChange( e:DisplayObjectEvent ) :void

{

_player.visible = Boolean( e.newDisplayObject );

}

}

}

Here you'll see I simply add an instance of MediaPlayerSprite to the display list, which avoids me needing to add/remove the MediaPlayer instance's display object each time it loads new media. MediaPlayerSprite checks internally to see if the current MediaElement has a viewable trait, saving you the hassle!

Serial composition example using an XML playlist

More context about this implementation can be found on my original Intro to OSMF post.

package

{

import flash.display.Sprite;

import flash.events.Event;

import flash.events.IOErrorEvent;

import flash.net.URLLoader;

import flash.net.URLRequest;

import org.osmf.audio.AudioElement;

import org.osmf.audio.SoundLoader;

import org.osmf.composition.SerialElement;

import org.osmf.display.MediaPlayerSprite;

import org.osmf.events.DisplayObjectEvent;

import org.osmf.events.LoaderEvent;

import org.osmf.image.ImageElement;

import org.osmf.image.ImageLoader;

import org.osmf.media.URLResource;

import org.osmf.net.NetLoader;

import org.osmf.proxies.TemporalProxyElement;

import org.osmf.utils.URL;

import org.osmf.video.VideoElement;

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:MediaPlayerSprite;

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( _netLoader, new URLResource( new URL( media.@source ) ) ) );

break;

case "advert":

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

break;

case "image":

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

break;

case "audio":

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

break;

}

}

}

}

private function playPlaylist() :void

{

if( _composition.numChildren )

{

_player = new MediaPlayerSprite();

_player.visible = false;

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

_player.mediaElement = _composition;

addChild( _player );

}

}

private function onLoaderStateChange( e:LoaderEvent ) :void

{

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

}

private function onDisplayObjectChange( e:DisplayObjectEvent ) :void

{

_player.visible = Boolean( e.newDisplayObject );

}

private function onPlaylistFail( e:IOErrorEvent ) :void

{

trace( "The playlist failed to load" );

}

}

}

Using a single LoaderBase instance allows you to register a single LOAD_STATE_CHANGE listener, as well as pool your loader objects for better performance on resource-constrained devices.

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.

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