How to Build a Flash Video (FLV) Progress Bar (Part 1)
Following on the heels, at least conceptually, of “How to Control Video (FLV) without a Component” here’s a quick look at how to indicate the progression of an FLV by way of a custom made progress bar (thanks for the suggestion, Rick!). In a follow-up article, I’ll show how to make the progress bar interactive by having the draggable knob seek to keyframes in the FLV. It turns out that much of the code for this first part derives from “How to Determine the Completion of a Flash Video (FLV) File,” which shows how to determine video length, with and without the use of Components, in ActionScript 2.0. In this article, we’ll be going the non-Component route, because FLVPlayback already has a progress bar. Before we delve into the code, we need to prepare two small movie clips.
Creating the progress bar itself
The artwork can get as pretty as you like, but for illustrative purposes, all you need for the progress bar’s track is a rectangle. Mine happens to be 12 x 180. Draw the rectangle and convert it to a movie clip, making sure the registration point is in the upper left corner. Give it the instance name track. For the knob, draw a smallish circle or almond shape. My circle is 15 x 15. Convert it to a movie clip, making sure the registration point is in the center, and give it the instance name knob. These can be on separate layers or the same, it doesn’t matter; just make sure the knob is above the track and the registration points are set as described. Position the knob at the left edge of the track.
I’ve you’ve been reading the other video-related articles on this blog, you’ll already be familiar with the non-Component display of FLVs. If not, you may want to give a quick read through the previous material. In addition to the two movie clips that comprise the progress bar, you’ll also need a Video object on the Stage. In this example, we’ll use the instance name videoPlayer.
An answer, short and sweet
Here’s the ActionScript, and we’ll step through it afterward.
var duration:Number = 0;
var ratio:Number = 0;
var id:Number = 0;
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachVideo(ns);
ns.play("externalVideo.flv");
ns.onMetaData = function(evt:Object):Void {
duration = evt.duration;
ratio = track._width / duration;
id = setInterval(
function ():Void {
knob._x = track._x + ns.time * ratio;
}, 50
);
};
ns.onStatus = function(evt:Object):Void {
if (this.time > 0 && this.time >= (duration - 0.5)) {
trace("Video complete");
clearInterval(id);
delete this.onStatus;
}
};
How it works
The first three variables, duration, ratio, and id, prepare the way for three values used later in the code. The duration value is later determined in a NetStream.onMetaData event handler as described in the article on determining video length. The ratio value — this is the new part — is also set in the same event handler. id is used to track a setInterval() timer loop, which is also introduced in the event handler. The zeroes, for now, simply give these numbers a default value.
The next block (five lines beginning with var nc) prepares the Video object for play and loads an external FLV file. This block of code was originally explained in “How to Load External Video (FLV).”
Now comes the brains of it.
ns.onMetaData = function(evt:Object):Void {
duration = evt.duration;
ratio = track._width / duration;
id = setInterval(
function ():Void {
knob._x = track._x + ns.time * ratio;
}, 50
);
};
As recounted elsewhere on this blog, the onMetaData event is dispatched as an FLV file begins to play. Depending on how a video file is converted to FLV, it may be given a handful of extra descriptive information, including the video’s duration. In the above code, this information gets a free ride in via the arbitrarily named parameter evt. This incoming object carries with it a duration property that is: a) passed to the previously declared duration variable and b) used to determine a ratio between the width of the track movie clip and the length of the video. Thanks to this ratio, the knob movie clip can be sent to any location between the left and right edges of the track movie clip in correspondence with how much of the video has been played. Let’s see how that works.
The id variable is simply a value that keeps track of setInterval() loops. The setInterval() function returns a value, much like the Math.round() method. With Math.round(), you feed in a number, and the value you get back is the nearest integer. With setInterval(), you feed in a) a function to perform and b) in interval in which to perform it. The function gives back a number that says, “Okay, this is the identifier for the particular loop you just started.” And what function have we fed in? In this case, we’re setting the MovieClip._x property of the knob instance to the horizontal position of track plus the result of the video’s current location (the NetStream.time property of the ns instance) multiplied by the ratio variable. This is performed every 50 milliseconds once the video begins.
To save on processor cycles, the looping should stop when the video ends, so the final block of code uses clearInterval(), with id as a parameter, to do just that. This happens in response to a NetStream.onStatus event that checks if the video’s progression is both past zero and equal to or greater than a half second shy of the duration value.
July 5th, 2007 at 7:37 am
David,
So can you make the knob draggable so that one can seek through the downloaded video? Thanks
July 5th, 2007 at 8:10 am
kweku,
That’s coming in Part 2:
July 22nd, 2007 at 9:33 am
I love posts like this! Keep up the great blog, I’ve bookmarked it and added it to my RSS reader to check out more often.
July 26th, 2007 at 3:34 pm
Kevin,
Thanks!
August 3rd, 2007 at 10:24 am
David,
Can this be used on loaded SWFs? IE Older than Flash 8 and FLVs?
Thanks
Jason
August 5th, 2007 at 7:27 pm
Jason,
The key to this one is the
NetStreamclass, which works as early as Flash Player 7, according to the ActionScript 2.0 Language Reference. In fact, that’s your best bet, typically: as you work through a chunk of sample code, look up each class (or function, or keyword, etc.) you encounter in the relevant Language Reference (AS2, AS3, what have you) and look for an Availability heading. That tells you what version of Flash Player first supports the functionality at hand.The
NetStreamclass is specifically geared toward loading FLV (Flash video) files, so it can’t be used to load external SWFs — I would recommend theMovieCipLoaderclass for that in AS2 — but this will certainly work in cahoots with loaded SWFs if those SWFs themselves contain a Video object. Just use dot notation to reference each object in turn until you hit the video object’s instance name.September 7th, 2007 at 11:00 am
Thanks so much!
This was a life saver. I just spent a week trying to replace a hard coded “video_length” with an “ns.onMetaData” function for duration. My function was going nowhere and I stumbled onto this solution while taking a break from banging my head on the keyboard.
The listener Object was the better way to handle this and it worked like a charm.
Thanks again
September 14th, 2007 at 2:45 pm
how do you go about setting up a playAgain button, I have tried the ns.seek(0) it works and then stops after that replay, any hints, new and embarrassed!
September 14th, 2007 at 5:38 pm
To Eric …
Nice! Glad to help, man.
To al …
It may be that the
NetStream.pause()method was invoked somewhere in your code. When your button calls NetStream.seek(0), follow that withns.pause(false);.October 4th, 2007 at 12:17 pm
I like what you did. But I want to use the the progressbar component in Flash. How would I do so???
October 16th, 2007 at 4:14 pm
mike05,
The ProgressBar component has an API in the Components Language Reference for ActionScript 2.0. (In AS3, you’ll find it in the ActionScript 3.0 Language Reference, but keep in mind, this particular blog entry was written in AS2.)
Rather than use the above approach to, say, update the
MovieClip._xposition of some movie clip, you would reference your ProgressBar instance by its instance name (let’s saymyProgressBar) and invoke itsProgressBar.setProgress()method:The
setProgress()method accepts two parameters, the first of which is whatever the current progress is; the second of which is the maximum value ( … thedurationwould make good sense here).October 30th, 2007 at 5:45 am
Hello,
Thank you David, it was really a good help.Shall I ask u one more doubt that if we need to know how much amount of video is being viewed bya particular patron.What would we do?Is it possible by using the progress bar as u mentioned above.Please help me in this.
Regards,
Ninu
October 30th, 2007 at 2:01 pm
Ninu,
Because Flash is displayed locally on the user’s computer (usually within the context of the user’s browser), any ActionScript performed is considered local — that is, nothing is executed on the server unless Flash communicates with the server by way of PHP, ASP.NET, etc. There is no way the server (and therefore you) can track the user’s progress unless Flash conveys that information to middleware that would, in turn, email you the data or store it in a database for later retrieval. On its own, Flash only communicates with the user interacting with the SWF.
December 6th, 2007 at 5:53 pm
Hello, and thanks for your suggestions.
I’ve done a progress bar in the past, but it was with an FLVPlayback instance which had a totalTime. Now I’m working on Flash Lite 3, which basically limits my options to the NetStream class which is also the key to this and the other referenced tutorials. I guess the bottom line of my question is, if the FLV file does not come with metadata, is there any other way to find out its duration beforehand (that is to say, without having to play it first and count it up)? The files that will be played come from many sources and thus I have no way to force the metadata to be present.
Best regards,
Immano
December 14th, 2007 at 11:18 am
Immano,
To the best of my knowledge, there’s no way to determine the video’s length without metadata. I’m not especially well versed in Flash Lite, but if it’s possible to wrap your video in a movie clip, you may be able to determine the approximate length by way of the
MovieClip._totalframesproperty.December 15th, 2007 at 6:05 pm
David,
thanks for your reply. It is possible and I will give it a try, the limitations of the Flash Lite version of the class are mainly about the support for mouse-related events, methods and properties, so it shouldn’t be a problem. I’ll have to see whether this has an impact on performance, which is pretty crucial on handheld devices.
About the
MovieClip._totalframesproperty, I guess in order to figure out the length I will also need to know the framerate of the video and also set that of the MovieClip instance to the same value, right?December 18th, 2007 at 10:18 am
Immano,
If you import the video into a movie clip, Flash will automatically (at your option) extend the movie clip’s timeline to the length required to display the video. In that case, you shouldn’t really have to know the framerate of the video or the SWF, because
_totalframeswill let you know when the movie clip has reached its end (when_currentframeequals_totalframes.December 18th, 2007 at 8:24 pm
David,
Thanks again for your patience. I realize now I wasn’t entirely clear about what I meant. The progress bar issue is resolved the way you point out, but I was alsp looking for a way to make a time counter for the clip, and the frames are about, well, frames, not seconds. Anyways, I think I could still access the NetStream instance I import the file from, and read the time from that.
December 19th, 2007 at 4:28 pm
Immano,
Aha! Yes, then you would indeed have to know the video’s and SWF’s framerates, then do the math to provide an approximate time counter. If you can read the time from
NetStreamit would likely be more accurate. But without a known duration, of course, you wouldn’t be able to say “mm:ss of total mm:ss” (sounds like a real challenge, in any case!).December 19th, 2007 at 4:49 pm
Well, luckily, I don’t need the total to be displayed. As it is, handheld devices really have very little Stage space to spare, so the essential information will have to do. Besides, a progress bar will in general be sufficient for the user’s need to have an approximate idea of how long the movie will be playing, at least during playback. Any further needs may be addressed through a Video Info dialog, in case.
January 1st, 2008 at 8:21 pm
Any chance you might update this tutorial for AS3? I’m trying to do the exact same thing, but in AS3. I just can’t seem to get a progress bar working to show the download progress of the video and can’t seem to find any info on the web. This article is perfect, except I need the same info for AS3. If anyone could point me to a similar tutorial for AS3 I’d be very grateful. Thanks!
January 7th, 2008 at 8:56 am
Steve,
There’s a very good chance!
The only question is when. I’m writing chapters for a new book that will almost certainly carry me through the end of January, 2008 — possibly into February — so my blog posts may be sparse for several weeks.
January 26th, 2008 at 9:46 pm
Is there a way to have another movie clip follow the x position of the knob so the loadbar is lighter on the right side of the knob and darker on the other?
April 14th, 2008 at 5:46 pm
Hello Dave I have been doing research, on creating your own components, for video. I came about your site, lots of great explanations and tutorials. I’m currently using 2004 mx. I’m getting an error in the output panel.
**Error** Scene=Scene 1, layer=as, frame=1:Line 15: There is no property with the name ‘onMetaData’.
ns.onMetaData = function(evt:Object):Void {
any help would be greatly appreciated thanks so much
April 20th, 2008 at 10:02 pm
To HeavyPops …
You betcha. You can have as many movie clips following the knob as you like. Just use the
setInterval()loop update additional clips’_xproperties in the same way asknob’s.To randy …
Flash MX 2004 may not “realize” what the
NetStream.onMetaDataevent is, even though Flash Player 7 does. Try this version instead (just another way of accessing the same event) and write back if you still run into problems.ns["onMetaData"] = function(evt:Object):Void {...}July 23rd, 2008 at 7:45 pm
Hi David
I just wanted to say that you are an absolute legend!
Ive been looking for some good flv help for ages!
All of the stuff on flv’s without components is gold dust.
My head was close to exploding until I stumbled upon your blog.
I swear I got an erection after reading your flv articles.
Thanks again man. You saved my life.
Liam
September 13th, 2008 at 8:46 pm
I am destroying my brain with all of the searching and reading and picking up a bread crumbs here and there . I need a video controller with most, if not all, the functionality of the MediaController. I am using the technique outline in your post titled “How to Control Video (FLV) without a Component.” I can already control the player with the External API and JavaScript but my boss wants a controller in the flash player as well. The player expands in a but keeps playing when the div is hidden. That’s why I call the function to stop it in JavaScript when the user hides it. However, when the player is visible, my boss wants to control it. Please help… I’m desperate.
December 15th, 2008 at 12:23 pm
I have an intro page which has a bit of animation, which at the end displays two buttons that should link to two flv.
On scene as2
stop();
Stage.scaleMode = “noscale”;
chap1_btn.onRelease = function (){
gotoAndStop(”Scene 2″, 1);
clearInterval(yourInterval); // clear your interval function
};
chap2_btn.onRelease = function (){
gotoAndStop(”Scene 3″, 1);
clearInterval(yourInterval); // clear your interval function
};
On the second scene i have the player which has an flvplayer which has the following code attached.
stop();
Stage.scaleMode = “noscale”;
chap1_btn.onRelease = function (){
gotoAndStop(”Scene 2″, 1);
clearInterval(yourInterval); // clear your interval function
};
chap2_btn.onRelease = function (){
gotoAndStop(”Scene 3″, 1);
clearInterval(yourInterval); // clear your interval function
};
//below allow to cycle through the scenes.
var duration:Number = 240000; // set the call delay
var yourInterval:Number = setInterval(doSomething, duration);
function doSomething():Void
{
gotoAndStop(”Scene 3″, 1); // play Scene 2 from frame 1
clearInterval(yourInterval); // clear your interval function
On the third scene I have the second flvplayer with the following code attached
stop();
Stage.scaleMode = “noscale”;
chap1_btn.onRelease = function (){
gotoAndStop(”Scene 2″, 1);
clearInterval(yourInterval); // clear your interval function
};
chap2_btn.onRelease = function (){
gotoAndStop(”Scene 3″, 1);
clearInterval(yourInterval); // clear your interval function
};
//below allow to cycle through the scenes.
var duration:Number = 508000;
var yourInterval:Number = setInterval(doSomething, duration);
function doSomething():Void
{
gotoAndPlay(”Scene 1″, 1); // play Scene 2 from frame 1
clearInterval(yourInterval); // clear your interval function
What i am trying to do is have an intro page with buttons linked to two different video.
I know i probably approaching the project wrong by using scenes, but i am new to action script and know no better.
Is there a simpler way to approach my goals on this project.
I have looked at your ‘How to Play Flash Video Files (FLV) Sequentially’ and got the flvplayer playing the two videos continuously, but cannot figure out how to attached the intro..
Can you help?