Live Flash with FLVPlayback 2.5 (Limelight & Akamai)
You can download the most recent FLVPlayback 2.5 from the Adobe Flash Media Server Tools download page. I had originally posted this solution in the Adobe forums and have decided to re-post it on my own site. You can find the full thread of the conversation here.
So you’re using the FLVPlayback 2.5 component because it offers the ability to define your multiple bitrate streams in a SMIL file and uses its built-in Dynamic Streaming capability to switch seamlessly between those streams, depending on bitrate variations and computer playback performance. Awesome. However, when you go to play back a live stream through either Akamai or Limelight, nothing happens. What gives?
The problem is that both Limelight and Akamai rely on a client-to-server call before the it will begin to send the live stream to the player and begin buffering and then playing. The method is called “FCSubscribe” and it must be passed the stream name(s) that you wish to “subscribe” to. Originally, I had written a completely custom player from scratch in order to handle this. However, for my company’s new Flash player, I knew we wanted to take advantage of the Dynamic Streaming features of Adobe Flash Media Server 3.5 and it seemed that the recently updated FLVPlayback 3.5 component would be the perfect solution.
However, I quickly realized that there was no way to write the live stream support from scratch. Turns out, you can give FLVPlayback your own custom NCManager class (which in turn uses a custom ConnectClient class) that overrides the onConnected method in order to make the appropriate FCSubscribe server call(s). Please note that this first method is the method you want to use for live streams that do not utilize Dynamic Streaming (FMS 3.0 or earlier).
The first thing you want to do is create a new ActionScript file in your project src folder (I am using Adobe Flex Builder). Please keep in mind that I am using my own package namespace (com.seanhsmith) so in order for this to build properly, you will need to place these source files in a matching subfolder structure off of your src folder… in this case, src/com/seanhsmith. The following code is the first piece of the puzzle and is the custom NCManager class, named NCManagerLive.as:
package com.seanhsmith { use namespace flvplayback_internal; import fl.video.*; // Import FLVPlayback classes import flash.net.NetConnection; public class NCManagerLive extends NCManager { public function NCManager() { super(); // Constructor } override flvplayback_internal function onConnected(p_nc:NetConnection, p_bw:Number):void { super.onConnected(p_nc, p_bw); // Custom ConnectClient this.netConnection.client = com.seanhsmith.ConnectClientLive; // Call FCSubscribe this.netConnection.call("FCSubscribe", null, this.streamName); } } } |
This class is designed to provide all of the default functionality built into the NCManager class, but with a slightly augmented onConnected method where we specify a custom ConnectClient class, which we will get to in a second, and then make the necessary FCSubscribe call, passing along with it the name of the stream we are connecting to.
The following is the custom ConnectClient class, which I named ConnectClientLive.as and placed in my src/com/seanhsmith folder:
package com.seanhsmith { use namespace flvplayback_internal; import fl.video.*; // Import FLVPlayback classes import flash.net.NetConnection; public class ConnectClientLive extends ConnectClient { public function ConnectClientLive(owner:NCManager, nc:NetConnection, connIndex:uint=0) { super(owner, nc); // Constructor } public function onFCSubscribe(info:Object):void { // Do nothing. Prevents debug error. } public function onFCUnsubscribe(info:Object):void { // Do nothing. Prevents a debug error. } } } |
The only reason we are specifying this custom ConnectClient class that doesn’t seem to really do anything custom is because we need to define the onFCSubscribe and onFCUnsubscribe methods on the client/player so that a debug error is not thrown when the server tries to call back in acknowledgment of receiving the calls.
Now that you have defined the custom NCManager and ConnectClient classes, the last part is your implementation. Before you instantiate your FLVPlayback variable, you’ll want to specify the new NCManager interface. You can do this by doing the following:
// Specify custom class VideoPlayer.iNCManagerClass = com.seanhsmith.NCManagerLive; // video is the FLVPlayback instance. Assume it has been previously scoped and typed. video = new FLVPlayback(); video.isLive = true; // Set isLive to true // ... more FLVPlayback setup code |
That’s it! Your player should now connect to your server, call FCSubscribe on the stream name, and then begin streaming. However, what if you want to take advantage of live Dynamic Streaming that is available on Flash Media Server 3.5? Surprisingly, the code is very similar and only differs slightly in the onConnected imlementation and the classes your custom classes are based off. The following code is the live Dynamic Streaming NCManager class (FMS 3.5 or later):
package com.seanhsmith { use namespace flvplayback_internal; import fl.video.*; // Import FLVPlayback classes import flash.net.NetConnection; public class NCManagerLive extends NCManagerDynamicStream { public function NCManagerLive() { // Constructor not needed - NCManagerDynamicStream does not have one } override flvplayback_internal function onConnected(p_nc:NetConnection, p_bw:Number):void { super.onConnected(p_nc, p_bw); this.netConnection.client = com.seanhsmith.ConnectClientLive; // Custom ConnectClient for (var x:uint = 0; x < this.streams.length; x++) { // Call FCSubscribe for all available stream names this.netConnection.call("FCSubscribe", null, this.streams[x].src); } } } } |
The following is the revised code for the custom ConnectClient class… the only difference being the class it is based off:
package com.seanhsmith { use namespace flvplayback_internal; import fl.video.*; // Import FLVPlayback classes import flash.net.NetConnection; public class ConnectClientLive extends ConnectClientDynamicStream { public function ConnectClientLive(owner:NCManager, nc:NetConnection, connIndex:uint=0) { super(owner, nc); // Constructor } public function onFCSubscribe(info:Object):void { // Do nothing. Prevents debug error. } public function onFCUnsubscribe(info:Object):void { // Do nothing. Prevents a debug error. } } } |
So I hope this code ends up helping somebody that maybe has been stuck trying to figure out how to make FLVPlayback play nice with live streams through either Limelight or Akamai. If it helped you out, please drop me a line and let me know!
- Sean