Home >
There are many challenges involved in building Qos (quality of service) video player, let’s take a look:
Last year, Akamai has started an open source (sourceforge BSD through V1) project called the Open Video Player (OVP) (http://openvideoplayer.sourceforge.net/) that is an open source video player that address many of the challenges involved in building a high quality video player.
Adobe recently announced that they would adopt the OVP project and include it as part of the Adobe Strobe (http://www.adobe.com/products/strobe/).
Dynamic switching of video profiles
To understand how the OVP player work let’s take a look at the business diagram at figure below:
Getting the video information
The implementation or entry point will begin with a media XML file called SMIL that the user selects. The SMIL file can be hosted on a CMS and once the user selects a video file we can call the SMIL file, which will contain all the information that we need such as the host name and the different profile files. The DynamicSmilParser is based on ParserBase and includes URLLoader to load the SMIL file and method to check the integrity of the XML nodes.
Once the SMIL is loaded it is turned into a class called DynamicStreamItem, which holds the streams URLs and points to the video files and the bitrate of each video file.
Creating a NetConnection
Once the SMIL file is loaded the AkamaiConnection (which is the implementation of the super class OvpConnection) connect to the host name we received from the SMIL. OvpConnection builds an array of connection strings to be used in streaming and tries to connect to find out available connections.
Play stream
Once a connection was found OvpConnection.handleGoodConnect method calls NetConnection.Connect.Success event. The event will reach the AkamaiDynamicNetStream, which is a wrapper for the NetStream. At this point we can play the video. Note that to play the video. The AkamaiDynamicNetStream.play method calls the super class OvpDynamicNetStream.play method, which calls startPlay method to start playing the video as well as start a timer. The timer has an event listener that checks every 500 ms the switching rules.
Switching rules
During the first time the video is playing the application checks for the user’s bandwidth and adjusts to the appropriate profile. Every 500 ms there are three rules currently available with the OPV that will be examined:
Switching of video
Once the logic indicates that there is a need to switch the video with a different profile, the NetStream.play2 method is used.
Let’s take a look at part of implementation of the OVP core classes, so you can better understand how it works;
Once the application was created the init method is called, which creates new instances and adds event listeners to some of the class variables such as the SMIL parser and the AkamaiConnection. It also adds the video to the stage and sets the transition timer and location.
The addVideoToStage method will attach the video to the stage using the videoHolder placeholder that was set
The startPlayback method gets called from a load button and clears the old session as well as starts the load process of the SMIL file.
Once the SMIL file was loaded we have the host name and we can establish a connection to the FMS server, _nc.connect(_smilMetafile.hostName); When the connection will be established the netStatusHandler handler will be called since it was set in the init method.
netStatusHandler method listens to a successful connection and errors. connectedHandler method sends a request to play the video based on the SMIL file and once that was achieved it calls connectedHandler.
Once a connection was found and established, overcoming proxy firewalls when needed. The connectedHandler is called, which uses the AkamaiDynamicNetStream instance and passes the SMIL file information: _ns.play(_smilMetafile.dsi);
The errorHandler method will handle all the error events generated from the SMIL parser (DynamicSmilParser), net stream (AkamaiDynamicNetStream) and net connection (AkamaiConnection).
See screen shot below:
Enhance progressive download seeking
We showed how the OVP handles dynamic switching of bitrates profiles. Additionally, OVP provides service called Akamai JumpPoint (TM) which provides the ability to instantly seek progressively delivered FLV files beyond the point at which they have been downloaded, as well as preventing the FLV data from being cached in the client system. This feature is a major enhancement since it allows us to overcome two disadvantages that HTTP service has over streaming. Keep in mind that the service requires using the Akamai JumpPoint service and will not work on a standard HTTP server. The JumpPoint service can be used with the AkamaiEnhancedNetStream class, which extends the AkamaiNetStream class.
- User bandwidth detection
- Support streaming and progressive download
- Dynamic switching of bitrate profiles
- Buffer policy
- Monitor of NetStream messages
- Improvement of seeking
- DVR integration
- Working with playlists
- Frame drop policy
- DRM
- Overcome network proxy issues
- Error handling
- Live streams
Last year, Akamai has started an open source (sourceforge BSD through V1) project called the Open Video Player (OVP) (http://openvideoplayer.sourceforge.net/) that is an open source video player that address many of the challenges involved in building a high quality video player.
Adobe recently announced that they would adopt the OVP project and include it as part of the Adobe Strobe (http://www.adobe.com/products/strobe/).
Dynamic switching of video profiles
To understand how the OVP player work let’s take a look at the business diagram at figure below:
Getting the video information
The implementation or entry point will begin with a media XML file called SMIL that the user selects. The SMIL file can be hosted on a CMS and once the user selects a video file we can call the SMIL file, which will contain all the information that we need such as the host name and the different profile files. The DynamicSmilParser is based on ParserBase and includes URLLoader to load the SMIL file and method to check the integrity of the XML nodes.
Once the SMIL is loaded it is turned into a class called DynamicStreamItem, which holds the streams URLs and points to the video files and the bitrate of each video file.
Creating a NetConnection
Once the SMIL file is loaded the AkamaiConnection (which is the implementation of the super class OvpConnection) connect to the host name we received from the SMIL. OvpConnection builds an array of connection strings to be used in streaming and tries to connect to find out available connections.
Play stream
Once a connection was found OvpConnection.handleGoodConnect method calls NetConnection.Connect.Success event. The event will reach the AkamaiDynamicNetStream, which is a wrapper for the NetStream. At this point we can play the video. Note that to play the video. The AkamaiDynamicNetStream.play method calls the super class OvpDynamicNetStream.play method, which calls startPlay method to start playing the video as well as start a timer. The timer has an event listener that checks every 500 ms the switching rules.
Switching rules
During the first time the video is playing the application checks for the user’s bandwidth and adjusts to the appropriate profile. Every 500 ms there are three rules currently available with the OPV that will be examined:
- Bandwidth rule - holds the switching rule for bandwidth detection.
- Frame drop rule - holds the switching rule for drop of frames detection.
- Buffer rule - holds the switching rule for buffer detection.
Switching of video
Once the logic indicates that there is a need to switch the video with a different profile, the NetStream.play2 method is used.
Let’s take a look at part of implementation of the OVP core classes, so you can better understand how it works;
Once the application was created the init method is called, which creates new instances and adds event listeners to some of the class variables such as the SMIL parser and the AkamaiConnection. It also adds the video to the stage and sets the transition timer and location.
private function init():void {
stage.addEventListener(FullScreenEvent.FULL_SCREEN, handleReturnFromFullScreen);
_smilMetafile = new DynamicSmilParser();
_smilMetafile.addEventListener(OvpEvent.PARSED,bossParsedHandler);
_smilMetafile.addEventListener(OvpEvent.ERROR,errorHandler);
_nc = new AkamaiConnection();
_nc.addEventListener(OvpEvent.ERROR,errorHandler);
_nc.addEventListener(NetStatusEvent.NET_STATUS,netStatusHandler);
addVideoToStage();
_transitionMsgTimer = new Timer(_TRANSITION_MSG_DISPLAY_TIME_);
_transitionMsgTimer.addEventListener(TimerEvent.TIMER, onTransitionMsgTimer);
var pt:Point = videoWindow.contentToGlobal(new Point(videoWindow.x, videoWindow.y));
pt = transitionInfoContainer.globalToContent(pt);
transitionInfoContainer.x = pt.x;
transitionInfoContainer.y = pt.y + videoWindow.height - transitionInfoContainer.height ;
_lastSwitch = 0;
}
The addVideoToStage method will attach the video to the stage using the videoHolder placeholder that was set
private function addVideoToStage():void {
_videoHolder= new UIComponent();
_video = new Video(480, 270);
_video.smoothing = true;
_video.visible = false;
_video.x = (_videoHolder.width-_video.width) / 2;
_video.y = (_videoHolder.height-_video.height) / 2;
_videoHolder.addChild(_video);
videoWindow.addChild(_videoHolder);
}
The startPlayback method gets called from a load button and clears the old session as well as starts the load process of the SMIL file.
private function startPlayback():void {
output.text = "";
bPlayPause.enabled = false;
bFullscreen.enabled = false;
_hasEnded = false;
multiBRCtrls.visible = false;
// Clean up from previous session, if it exists
if (_nc.netConnection is NetConnection) {
_ns.useFastStartBuffer = false;
_nc.close();
}
// Start parsing the SMIL file
_smilMetafile.load(bossLink.text)
}
Once the SMIL file was loaded we have the host name and we can establish a connection to the FMS server, _nc.connect(_smilMetafile.hostName); When the connection will be established the netStatusHandler handler will be called since it was set in the init method.
private function bossParsedHandler(e:OvpEvent):void {
write("SMIL parsed successfully:");
write(" Host name: " + _smilMetafile.hostName);
write(" Stream name: " + _smilMetafile.streamName);
// Establish the connection
_nc.connect(_smilMetafile.hostName);
}
netStatusHandler method listens to a successful connection and errors. connectedHandler method sends a request to play the video based on the SMIL file and once that was achieved it calls connectedHandler.
// Receives all status events dispatched by the active NetConnection
private function netStatusHandler(e:NetStatusEvent):void {
write(e.info.code);
switch (e.info.code) {
case "NetConnection.Connect.Rejected":
write("Rejected by server. Reason is "+e.info.description);
break;
case "NetConnection.Connect.Success":
connectedHandler();
break;
}
}
Once a connection was found and established, overcoming proxy firewalls when needed. The connectedHandler is called, which uses the AkamaiDynamicNetStream instance and passes the SMIL file information: _ns.play(_smilMetafile.dsi);
// Once a good connection is found, this handler will be called
private function connectedHandler():void {
_ns = new AkamaiDynamicNetStream(_nc);
_ns.addEventListener(OvpEvent.ERROR,errorHandler);
_ns.addEventListener(OvpEvent.DEBUG, debugMsgHandler);
_ns.addEventListener(OvpEvent.COMPLETE,completeHandler);
_ns.addEventListener(OvpEvent.PROGRESS,update);
_ns.addEventListener(NetStatusEvent.NET_STATUS,streamStatusHandler); _ns.addEventListener(OvpEvent.NETSTREAM_PLAYSTATUS,streamPlayStatusHandler);
_ns.addEventListener(OvpEvent.NETSTREAM_METADATA,metadataHandler);
_ns.addEventListener(OvpEvent.NETSTREAM_CUEPOINT,cuepointHandler);
_ns.addEventListener(OvpEvent.SUBSCRIBE_ATTEMPT, handleSubscribeAttempt);
_ns.isLive = true;
_ns.maxBufferLength = 10;
_video.visible = false;
_video.attachNetStream(_ns);
write("Successfully connected to: " + _nc.netConnection.uri);
write("Port: " + _nc.actualPort);
write("Protocol: " + _nc.actualProtocol);
write("Server IP address: " + _nc.serverIPaddress);
_ns.play(_smilMetafile.dsi);
}
The errorHandler method will handle all the error events generated from the SMIL parser (DynamicSmilParser), net stream (AkamaiDynamicNetStream) and net connection (AkamaiConnection).
private function errorHandler(e:OvpEvent):void {
switch(e.data.errorNumber) {
case OvpError.INVALID_INDEX:
handleInvalidIndexError();
break;
case OvpError.STREAM_NOT_FOUND:
Alert.show("Connected to the server at " + _nc.serverIPaddress + " but timed-out trying to locate the live stream " + _smilMetafile.streamName, "UNABLE TO FIND STREAM ", Alert.OK);
break;
default:
Alert.show("Error #" + e.data.errorNumber+": " + e.data.errorDescription, "ERROR", Alert.OK);
break;
}
}
Enhance progressive download seeking
We showed how the OVP handles dynamic switching of bitrates profiles. Additionally, OVP provides service called Akamai JumpPoint (TM) which provides the ability to instantly seek progressively delivered FLV files beyond the point at which they have been downloaded, as well as preventing the FLV data from being cached in the client system. This feature is a major enhancement since it allows us to overcome two disadvantages that HTTP service has over streaming. Keep in mind that the service requires using the Akamai JumpPoint service and will not work on a standard HTTP server. The JumpPoint service can be used with the AkamaiEnhancedNetStream class, which extends the AkamaiNetStream class.




Facebook Application Development
looks like competition for JW Player
I hope the other CDNs launch initiatives like this. Perhaps standards can be established based on OVP.
Strobe:
http://www.adobe.com/products/strobe/
That's what Adobe are trying to achieve with Strobe:
http://www.insideria.com/2009/05/adobe-strobe-framework-review.html