SyntaxScrewer's Blog

February 16, 2010

How to preload a video using NetStream

Filed under: ActionScript 3.0,Flash — geekymaira @ 10:09 am

I ran into a small problem at work and I did a little research but couldn’t find any concrete solutions to this. But, I managed to get it working somehow.
The requirement was that I had to show a small progress bar before playing a video stream. In other words, I had to make sure that the video started
playing only once it had completely buffered.

The thing with NetStream is that it doesn’t provide any nice ProgressEvent that can be used to track the progress. So there was a problem. How do we
detect the progress?

Well, I did it as follows, but this is not the only solution. I’ve seen examples where people have tried using the “bytesLoaded” and the “bytesTotal”
properties of the NetStream object to detect progress every millisecond (using a timer) but I don’t think this really works well. Maybe it does, but
it didn’t work for me. So I’m going to use the bufferTime and bufferLength properties instead.

Here goes…

package {
	import flash.display.Sprite;
	import flash.display.MovieClip;
	import flash.events.NetStatusEvent;
	import flash.events.SecurityErrorEvent;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.events.Event;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.text.TextField;
	import flash.net.navigateToURL;
	import flash.net.URLRequest;

	public class NetConnectionExample extends MovieClip
	{

		private var videoURL:String="Funny dog hands.flv";
		private var connection:NetConnection;
		private static var stream:NetStream;
		private var timer:Timer=new Timer(1); //Set a timer for 1 ms.
		private static var vidInfoObj:Object = new Object();

		public function NetConnectionExample() {
			connection = new NetConnection();
			connection.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			connection.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
			connection.connect(null);
			timer.addEventListener(TimerEvent.TIMER, onTimer);
		}

		private function netStatusHandler(event:NetStatusEvent):void {
			switch (event.info.code) {
				case "NetConnection.Connect.Success" :
					connectStream();
					break;
				case "NetStream.Play.StreamNotFound" :
					trace("Stream not found: " + videoURL);
					break;
			}
		}

		private function securityErrorHandler(event:SecurityErrorEvent):void {
			trace("securityErrorHandler: " + event);
		}

		private function connectStream():void {
			timer.start();
			stream=new NetStream(connection);
			stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			stream.client = new CustomClient();

			var video:Video = new Video();
			video.attachNetStream(stream);
			stream.play(videoURL);

			addChild(video);
		}

		private function onTimer(evt:TimerEvent):void {
			//Check to see if the video is completely buffered! if yes, remove the timer. If no, show progress!
			if (Math.ceil((stream.bufferLength / stream.bufferTime) * 100) < 100)
			{
			this.progress.text = "Buffer Len : " + stream.bufferLength + " Buff Time : " + stream.bufferTime + "\n" + "Percent Loaded : " + Math.ceil((stream.bufferLength / stream.bufferTime) * 100) + "%";
			}
			else
			{
				timer.stop();
				timer.removeEventListener(TimerEvent.TIMER, onTimer);
			}

		}

public static function setVideoParams(info:Object):void {
			vidInfoObj=info;
			stream.bufferTime=vidInfoObj.duration;
		}

	}
}

class CustomClient {
	public function onMetaData(info:Object):void {
		NetConnectionExample.setVideoParams(info);
		trace("metadata: duration=" + info.duration + " width=" + info.width + " height=" + info.height + " framerate=" + info.framerate);
	}
	public function onCuePoint(info:Object):void {
		trace("cuepoint: time=" + info.time + " name=" + info.name + " type=" + info.type);
	}
}
}

Sorry to bombard you with all that code. Let’s see what’s happening step by step.
Advice : if you’re like me and HATE scrolling to find the code for an explanation, do yourself a favor and copy the code above in notepad file or something
before reading the explanation. 😀

1. First we import all the required classes (Nothing exciting here 😦 ).

2. Next, we set the link to the video , create the NetStream, the Timer & the vidInfoObj (To hold the object returned from the onMetaData function) Objects.
Our timer is set to a 1 millisecond duration, to check the progress every millisecond. I think we can alternatively use an ENTER_FRAME event too but I haven’t
tried doing that. If you try it, I’d like to know if it works.

3. Next we create the Connection object and add the NetStatusEvent.NET_STATUS , SecurityErrorEvent.SECURITY_ERROR event listeners for it. We also set the
connection to null (this is pretty standard). We also add our Timer’s TIMER event here.

4. Then we just add event handlers for the listeners added in the previous step. In the NET_STATUS handler, we check the event.info.code for success or failure.
In case of a “NetConnection.Connect.Success” code we call the connectStream() function to attach the stream to our video.

5. In the connectStream function , we do the following
– Attach the stream to the connection.
– Add the NET_STATUS event listener for the stream too (earlier we did it for the connection). The handler function remains the same.
– We assign a new instance of the CustomClient class to stream.client. What this does is offers stream.client the onMetaData and onCuePoint callbacks.
– Create a new video object and attach the stream to it.
– Finally play the video in the videoURL and add it to the stage.
– Oh yeah, we also start our timer :).

6. Next we have our onTimer event handler. What we want here is to typically show progress. Right now I’m just using a dynamic text field to show the percentage loaded
but you could do this visually, by adding a progress bar or something based on the percentage loaded. It’s really upto you.

7. We write the setVideoParams() function. What this function basically does is, it sets the streams bufferTime to be equal to the duration of the video loaded. The duration
of the loaded video is available in the onMetaData function. The onMetaData function is in a different class. What we need to do
is somehow set the stream.bufferTime property in the NetConnectionExample class from within the CustomClient.onMetaData(infoObject:Object) function. Off the top of my
head I thought that I should create a public static function in NetConnectionExample and pass to it, the infoObject from the CustomClient.onMetaData function. Hence, I wrote
setVideoParams(). NOTE : I had to change all the vars used in this function to static too, since static functions can only work on static vars.

8. Finally we write the CustomClient class which offers our stream.client the onMetaData and onCuePoints callbacks.

That’s about it…..
NOTE : There is a problem with flash player (from what I’ve heard). It doesn’t simulate downloads for a video playback. This means that if you will test your movie
and choose Simulate Download from the flash player menu, you wouldn’t see the progress. This seems to be true , it doesn’t work for me too (Hey! that rhymed :lol:) . So the best way to test this
script is, to link to a video on the internet OR if you have a hosting server, then host the FLV on it and give the relative path in the script.

CORRECTIONS : I have a few queries about how and when the onMetaData() callback is made. Since, our progress and eventually the video playback is dependent on the
metadata, I want to know exactly when the event is fired – just to have more controls over things , because this script seems to behave a little differently when I run it from home
and when I do it at work. I think it’s got something to do with the connection speed or the callback to onMetaData, or the stream.play() call (WHICH I REALLY THINK SHOULD BE IN THE setVideoParams
FUNCTION INSTEAD OF WHERE I HAVE IT, ARGH! 😯 😯 ). Anyway, hope this helped. I will try and update this post when I find out and if there’s anything worth sharing.

Chao till then :),

Cheers!

-SyntaxScrewer

Leave a Comment »

No comments yet.

RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.