Recently I’ve been on the hunt for a means to bypass the aggregate timelines of SerialElements in OSMF. I want to do preroll and postroll ads that don’t add to the timeline of the main element I’m playing. After asking around in the Adobe forums, I was pointed to the AdvertisementPlugin, which is explained in this post on the OSMF blog. After playing around with it, this looks like just what I needed.
I work with the REOPS framework, so I went ahead and integrated it in. I have the code below, but first a word on what I had to do. There are some limitations to how REOPS currently can take in Plugin metadata, so I ended up doing a little trickery in the config XML for the plugin:
<plugin path="assets/plugins/AdvertisementPlugin.swf">
<metaData namespace="preroll">
<value key="preroll" type="class">
<url><![CDATA[http://gcdn.2mdn.net/MotifFiles/html/1379578/PID_938961_1237818260000_women.flv]]></url>
</value>
</metaData>
<metaData namespace="midroll">
<value key="midroll" type="class">
<url><![CDATA[http://gcdn.2mdn.net/MotifFiles/html/1379578/PID_938961_1237818260000_women.flv]]></url>
</value>
</metaData>
<metaData namespace="midrollTime">
<value key="10">10</value>
</metaData>
</plugin>
There are three types of metadata for this plug-in. The first are the components, the MediaContainer and the MediaPlayer. Those are handled in the application class below. In the config, I’m handling the other two types: files and times. The files are fairly straightforward, but due to REOPS handling of plug-in metadata, I had to cast them as OSMF URLs using the org.osmf.utils.URL class. I can then pull the URL out in the application class. The times I didn’t want to handle as URLs, so I fudged it a bit and just made the keys the number of seconds to wait before displaying a midroll ad.
The reason for this is that the Advertisement Plugin is expecting its metadata as a simple string or number, but REOPS casts metadata as Metadata objects. So in the app file, I extracted the data from the plugin’s metadata and reapplied it in the format it was expecting. In addition to that, the plugin metadata needs to hold references to the MediaPlayer and MediaContainer. Since REOPS dynamically creates its media elements and plugins, I had to hijack that process a little and pass in the two components before the plugin was loaded. I also had to change the start up order for REOPS around a bit, because it loads its plugins before it creates the MediaPlayer and MediaContainer it uses. I called the initMediaPlayer method early and blocked later calls to it. Unfortunately this breaks the authorized domain functionality in REOPS, but for this proof of concept I didn’t feel the need to circumvent that.
Once the plug-in was integrated, it simply was a matter of knowing when to hide the controls for the player. The plugin works by running two media players, and swapping the media element in the media container for the ad and then swapping back. By listening for the LayoutTargetEvent.ADD_CHILD_AT event, I could check to see if the media element that was added was my main media element. If it was, I would make the skin visible. If it wasn’t, I would make the skin invisible. That seemed to do the trick. I’m rather pleased to find this plugin, as it does something many people want and makes an otherwise difficult task much easier.
Here’s my application class:
package
{
import com.realeyes.osmfplayer.model.Plugin;
import flash.display.Sprite;
import org.osmf.layout.LayoutTargetEvent;
import org.osmf.layout.MediaElementLayoutTarget;
import org.osmf.metadata.Metadata;
import org.osmf.utils.URL;
public class REOPSAdPluginTest extends REOPS
{
public function REOPSAdPluginTest()
{
super();
}
override protected function _loadPluginsFromConfig():void
{
//Call this early so it's available for the plugin
super._initMediaPlayer();
_mediaPlayerShell.addEventListener( LayoutTargetEvent.ADD_CHILD_AT, _onContainerAddChild );
for each( var plugin:Plugin in _playerConfig.plugins )
{
if( plugin.path && plugin.path.indexOf( "AdvertisementPlugin" ) >= 0 )
{
//Extract strings from normal metadata
var namespaces:Vector.<String> = plugin.resource.metadataNamespaceURLs;
for each( var name:String in namespaces )
{
var meta:Metadata = Metadata( plugin.resource.getMetadataValue( name ) );
var key:String = meta.keys[0];
if( isNaN( Number( key ) ) )
{
var value:URL = URL( meta.getValue( key ) );
plugin.resource.addMetadataValue( key, value.rawUrl );
}
else
{
plugin.resource.addMetadataValue( name, Number( key ) );
}
}
plugin.resource.addMetadataValue( "MediaPlayer", _mediaPlayerCore );
plugin.resource.addMetadataValue( "MediaContainer", _mediaPlayerShell );
}
_pluginLoader.addPlugin( plugin );
}
}
override protected function _initMediaPlayer():void
{
//block the default time this gets called
}
private function _onContainerAddChild( event:LayoutTargetEvent ):void
{
_skin.visible = ( MediaElementLayoutTarget( event.layoutTarget ).mediaElement == _mediaElement );
}
}
}