Flash Streaming with Akamai in AS3

November 2, 2007 | Uncategorized | 7 Comments

We use Akamai (formerly ninesystems) for all of our video streaming (media page, online education, grad stories, etc.) and we tend to use the FLVPlayback component for our various video players. When we hit an Akamai stream URL (http://dts.edgeboss.net/xxxxx.flv), it is an XML file with information on servers based on the user’s location. This XML must be parsed in order to construct a stream URL to play. Ninesystems provided a sample player, but it used Flash 7 style coding which is very hard to manage. I’ve developed a little AS3 class that encapsulates all the functionality.

Here is the usage (assuming an FLVPlayback component called: flvPlayback);

using edu.dts.video.AkamaiPlayer;
var akamaiPlayer:AkamaiPlayer = new AkamaiPlayer(flvPlayback);
akamaiPlayer.playStream("http://edgeboss.net/xxxxx.flv");

That’s it. The full source code is below:

package edu.dts.video
{
    
    import flash.display.MovieClip;

    import flash.events.EventDispatcher;
    import flash.events.Event;
    import flash.events.TimerEvent;

    
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    
    import flash.utils.Timer;
    import fl.video.FLVPlayback;
    import fl.video.VideoEvent;    
    
    public class AkamaiPlayer extends EventDispatcher
    {
        private var _player:FLVPlayback;
        
        private var _protocols_array:Array = [
            {protocol:"rtmp", port:1935}, 
            {protocol:"rtmp", port:80} 
        ];
        private var _connections_array:Array = new Array();

        private var _duration:Number = 0;    

        private var _connectionIndex:Number = 0;
        private var _connectTimeout = null;

        private var _akamaiUrl:String = "";
        private var _akamaiConn:String = "";
        private var _connTimeout = 10000;        
        
        private var _streamXml:XML;

        private var _timer:Timer;
        
        public var connectionFailure:Function;
            
        public function AkamaiPlayer(player:FLVPlayback)
        {
            _player = player;
            _player.addEventListener(VideoEvent.READY, playerReady);
        }
        
        public function playStatic(flvUrl:String) {
            trace("AkaimiPlayer playing " +  flvUrl);
            _player.play(flvUrl);
        }
        
        public function playStream(akamaiUrl:String) {
            trace("AkaimiPlayer playing " +  akamaiUrl);
            
            // check for XML version (xmlvers)
            
            if (akamaiUrl.indexOf("?") == -1) {
                akamaiUrl += "&xmlvers=2";
            } 
            else
            {
                if (akamaiUrl.indexOf("xmlvers=2") == -1) {
                    akamaiUrl += "&xmlvers=2";
                }                
            }
            
            
            _akamaiUrl = akamaiUrl;
            _akamaiConn = "";
        
            if (_player.playing)
                _player.stop();
        
            var xmlLoader:URLLoader = new URLLoader();
            xmlLoader.addEventListener(Event.COMPLETE, loadComplete);
            xmlLoader.load(new URLRequest(_akamaiUrl));
        }
        
        private function loadComplete(event:Event):void {
            trace("FLV XML is Loaded");
            
            // get XML data
            _streamXml = new XML(event.currentTarget.data);
            
            // clear out existing data
            _connectionIndex = 0;
            _connections_array = new Array();
            
            // find //             
            var entries:XMLList = _streamXml.stream.entry;
        
        
            var item_xml:XML;
            
            // loop through the array of matching XMLNodes and remap the child nodes into an tempObject
            for each (item_xml in entries) {
                var tempObj:Object = new Object();
                var node:XML;
                for each (node in item_xml.children()) {
                    tempObj[node.localName()] = node.text();
                }
                
                // create connection string array entries
                for ( var i = 0; i < _protocols_array.length; i++ ) {
                    var connString:String = 
                        _protocols_array[i].protocol + "://" + 
                                tempObj["serverName"] + ":" + _protocols_array[i].port + "/" + 
                                tempObj["appName"]+  "/" + tempObj["streamName"];
                    _connections_array.push( connString );
                    
                    trace("added: " + connString);
                }                                                            
            }
            
            _timer = new Timer(_connTimeout);
            _timer.addEventListener("timer", timerHandler);
            _timer.start();
        
            tryNextConnection();

        }
        
        private function tryNextConnection():void {

            var connString:String = _connections_array[_connectionIndex];
            
            if (_connectionIndex == _connections_array.length ) {
                
                // make sure to reset to last connection
                connString = _connections_array[_connectionIndex-1];
                 _timer.stop();
                 
                if (connectionFailure != null)
                    connectionFailure();
                 
                trace("tried all protocols with no luck");
                return;
            }
                    
            // just in case it happens to start playing from another source
            if (_player.playing)
                return;
                    
            trace("connecting to " + (_connectionIndex+1).toString() + " of " + (_connections_array.length).toString() + ": " + connString + " ; count: " + _timer.currentCount.toString());
            _player.autoPlay = true;
            _player.play(connString);
        
            
            // iterate the connection so that if it fails, we go to the next one
            _connectionIndex++;            
        }
                
        private function timerHandler(event:TimerEvent):void {
            tryNextConnection();                    
        }
        
        private function stopConnectionAttempts():void {
            _timer.stop();
        }        
        
        private function playerReady(event:VideoEvent):void {
            // successful play
            stopConnectionAttempts();
        }        
    }    
}

7 responses to “Flash Streaming with Akamai in AS3”

  1. John says:

    Thank you so much for posting this. I found it really useful.

    I did have to do a little bit of debugging to get it working, though. If anyone else is having trouble, I had to change the following:

    – Changed "public class AkamaiPlayer extends EventDispatcher" to "public class AkamaiPlayer extends MovieClip".

    – In function "playStream", I had to remove the whole "if (akamaiUrl.indexOf("?") == -1)" block. The concatenation of "&xmlvers=2" was causing Akamai to return a 404 page.

  2. John says:

    Whoops, I forgot to include a couple of other changes

    – Removed "var entries:XMLList = _streamXml.stream.entry;"

    – Changed "for each (item_xml in entries) {" to "for each (item_xml in _streamXml) {"

  3. John Dyer says:

    @John, glad to be of help. I think I have done some modifications to it since then. You might try enabling the version=2 as it give you additional connections.

  4. Jason says:

    I am having problems creating an FLVplayback component that connect to a stream from akamai. I have read through the post but not sure how to implement and what files to create Flash swh and as. Do i copy the code and creat an as file if so what should i name it to connect to the FLVplayback. and the what code to put into the Flash. Also do i drag an FLVplayback to the stage and the name it? Sorry for the long message but i am in a real bind. Thanks for the help.

  5. John Dyer says:

    Jason,
    I would recommend using Akamai’s http://www.openvideoplayer.com/ which has code for connected to Akamia specific Flash streaming servers.

  6. Luke says:

    Great post… I’m having some issues though and I don’t know whether or not it’s my feed or something in the code, but I am getting a "connecting 1 of 2" followed by a "connecting 2 of 2" and finally a "tried all protocols with no luck". Is there any way for me to test to make sure my feed exists?

  7. I have read a lot of the comments and I just wonder why people say the things they do, I mean they can find the bad in anything. I guess that is where we are in this world. Just hurt hurt hurt, no matter what the subject is. Lawrence Williams http://www.trybw.com Fort Myers, Naples, Bonita,Cape Coral Computer Repair Service

Leave a Reply

Hi, I'm John Dyer. In my day job, I build websites and create online seminary software for a seminary in Dallas. I also like to release open source tools including a pretty popular HTML5 video player and build tools that help people find best bible commentaries and do bible study. And just for fun, I also wrote a book on the theology of technology and media.

Fork me on GitHub

Social Widgets powered by AB-WebLog.com.