How to Fast Forward and Rewind Video (FLV) Content

ActionScript 2.0

Traditional VCR controls usually incorporate fast forward and rewind buttons, which either increase the playback speed of the video or quickly play it in reverse, respectively.  The result is that you can skip around in a video but still maintain some sense of where you are.  Useful as these controls might be, you won’t find them in any of the FLVPlayback Component skins.  (What appear, at first glance, to be FF/RW buttons, are actually a means to jump to optional navigation cue points in the video, much like jumping to chapters on a DVD.)  Is it possible, then, to fast forward and rewind FLV files?  Sure thing.  Let’s take a look. 

An answer, short and sweet

As explained in “How to Load External Video (FLV)” and “How to Control Video (FLV) without a Component,” it’s entirely possible to load and manage FLV files without the use of the FLVPlayback Component.  Is there anything wrong with FLVPlayback?  Not a bit.  I use it on occasion, just like I occasionally go out to eat at a restaurant, where the food is all prepared for me.  But as it happens, I enjoy cooking.  :)   I like experimenting with things on my own, and I also appreciate the file size savings I get from using ActionScript to control a Video object.  So we’ll start from a Component-less point of view, because the concept here is the important part and can be implemented more or less the same way with FLVPlayback.

Assuming a Video object already on the Stage with the instance name videoPlayer — see the earlier articles, if necessary, to get up to speed — here’s your ActionScript 2.0:

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachVideo(ns);
ns.play("externalVideo.flv");

var id:Number;

ff.onPress = function():Void {
  id = setInterval(function():Void {
    ns.seek(ns.time + 0.5);
  }, 100);
}
ff.onRelease = function():Void {
  clearInterval(id);
}

rw.onPress = function():Void {
  var dest:Number = ns.time;
  id = setInterval(function():Void {
    ns.seek(dest -= 2);
  }, 100);
}
rw.onRelease = function():Void {
  clearInterval(id);
}

How it works

The first block (var nc, etc., through ns.play()) establishes an HTTP connection to make an FLV file request.  At this point, a file named externalVideo.fla has begun its loading process and will play as soon as enough data have been buffered (usually a few seconds, and it starts to play, even while continuing to download).

Next, a Number variable, id, is declared, but not yet set to anything.  The reason it’s declared here, rather than inside the event handler functions immediately following, is that by putting it here, we’re scoping it to the main timeline.  When clearInterval() looks for the id variable later, it will look first in the scope of its own function, won’t find it, and will then look “up the chain” until it sees id, which is the same variable used by a number of functions here — they all share this variable.

Now, have have this:

ff.onPress = function():Void {
  id = setInterval(function():Void {
    ns.seek(ns.time + 0.5);
  }, 100);
}

… which expects a button symbol with the instance name ff (for fast forward).  A function is assigned to the Button.onPress event of this Button instance.  When the user presses down on the fast forward button, the following happens:  a setInterval() loop causes a function of its own to be triggered every 100 milliseconds (every tenth of a second).  This function references the ns (NetStream) instance declared earlier and invokes on it the NetStream.seek() method, which sends the video to a given point in its video timeline. What point is that?  Well, the value must be in seconds, but decimal places are okay.  For this particular example, we’re sending the video to an expression that reads ns.time + 0.5, which means, “Where you are right now (ns.time) plus half a second”; in other words, half a second from the current position.  Experiment with this value as you please.  Add 0.25 if you like.  For super fast forward, add 5.  Up to you.

In ActionScript 2.0, setInterval() is simply a function that repeats other functions at the interval specified.  setInterval() happens to return a value (simply a number) that we’re collecting in that id variable.  Why?  So that we can turn this particular function-triggering loop off again.  This will happen when the user lifts up from the FF button:

ff.onRelease = function():Void {
  clearInterval(id);
}

Pretty straight forward.  Now for reverse.

Reverse is a little more tricky, because the natural tendency of the FLV file is to play forward.  The way NetStream seeks — for this sort of setup (progressive download) — is that it looks for video keyframes — special frames in the video file itself — and navigates to those.  The frequency of video keyframes is determined when the video is encoded.  You might have keyframes every 300 frames.  For a video encoded at 25 frames per second (fps), that’s one keyframe every 12 seconds.  Sending the video ahead is one thing … it’s already traveling in that direction.  Sending the video back is somewhat akin to swimming upstream.  The actual numbers you find useful may vary, depending on the framerate, keyframe distribution, and length of the FLV in question, so the following values are just numbers that happened to work for me:

rw.onPress = function():Void {
  var dest:Number = ns.time;
  id = setInterval(function():Void {
    ns.seek(dest -= 2);
  }, 100);
}

Same sort of concept as before, but this time we declare a new variable, dest (for destination, but the name is arbitrary) and set it to the video’s current position — again, taken from the NetStream.time property.  Why take a “snapshot” of the position and use that, rather than a direct call to ns.time?  Well, unless we go to the trouble of pausing the video first, which means we’d have to unpause it later, the video wants to move forward.  Rather than deal with a constantly “struggling” value — time would exasperatedly be asking, “Do you want me to go forward or not!?” — we’ll just deal with a static number.  Here, again, a setInterval() loop decrements the seek value repeatedly.  This example decrements by 2 and does so every 100 milliseconds.  Start with those values, but experiment and find your own sweet spot.

An onRelease event of the rw button stops the loop, as before.

Things to keep in mind

Nothing stops the rewind routine from heading into negative numbers.  If the user holds down the RW button long enough, ns will be asked to seek to -100, -220, and so on.  Fortunately, seek() doesn’t care.  Anything lower than 0 gets sent to 0.  Same goes for the upper end:  anything higher than the duration of the video obviously can’t be sent higher than the number of seconds the video contains (and it won’t).

Perhaps most importantly, seek() cannot send progressively downloaded videos to keyframes that haven’t yet loaded.  If the FLV file is still downloading and your user mashes the fast forward button, nothing may happen for a while.  Them’s the brakes.

Can it be done with the FLVPlayback Component?

You bet.  Remember, the principle is that same, but the mechanics of it change a bit.  The FLVPlayback class in the Help docs serves up what you need. (this is AS2, remember, so if you’re in an AS3 environment you’ll have to do some digging anyway).

var id:Number;
ff.onPress = function():Void {
  videoPlayer.pause();
  var dest:Number = videoPlayer.playheadTime;
  id = setInterval(function():Void {
    videoPlayer.seek(dest += 2);
  }, 100);
}
ff.onRelease = function():Void {
  videoPlayer.play();
  clearInterval(id);
}
rw.onPress = function():Void {
  videoPlayer.pause();
  var dest:Number = videoPlayer.playheadTime;
  id = setInterval(function():Void {
    videoPlayer.seek(dest -= 2);
  }, 100);
}
rw.onRelease = function():Void {
  videoPlayer.play();
  clearInterval(id);
}

In this case, I found it helped after all to pause the video while seeking, which is why the onRelease handlers invoke FLVPlayback.play() again.  I also found that even fast forwarding benefits from the temporary dest variable; otherwise things got ugly pretty quickly.  Note, too, that I adjusted the increment value for fast forwarding.

So … dash o’ this, splash o’ that.  This particular article is more of a loose recipe than a bulleted list of steps to follow, but I hope it gives you a bit to play with.  :)   Thanks to Dean Nixon for suggesting this topic.

36 Responses to “How to Fast Forward and Rewind Video (FLV) Content”

  1. Justin Putney Says:

    David,

    Thanks so much for posting! Your blog is one of the best Actionscript resources on the net. I really appreciate your detailed explanations.

    Just a heads up… In the first code block, there are two different instance names for the onPress and onRelease events of the rewind button. I thought I’d mention it in case anybody copies and pastes the code directly.

    Thanks!

  2. David Stiller Says:

    Justin,

    Hey, thanks for hawk eyes! :) I corrected the article. Thanks for the encouraging words, too, bro.

  3. Sashi Says:

    David:

    Great post(s). Keep’em coming.

    I have a question. Is there a (simple) way to get frame-level control in a flv file. Iam trying to create a video mashup type of application (add stuff on top of frames like thought blurbs) but was told that we can only get to the nearest key frame and not the exact next frame. This kinda sucks, because we cannot get the precision required to position at the a particular frame on the flv clip. Any suggestions how to do this.

    Sashi

  4. David Stiller Says:

    Sashi,

    You’re right, Flash can only seek to the nearest video keyframe, but this limitation is only true when the video is being pulled in as described above. If you’re using a streaming server (Flash Media Server, Red5), you can seek to any point in time. Other than an approach like that, you could theoretically encode your FLV files with as many keyframes as you have frames. Beware, if you go that route, it would increase the FLV file size, perhaps considerably.

  5. Greg Nevius Says:

    David,
    Thanks for this great bit of scripting. I did get this functioning, but for some reason, when I release either the FF (or RW button), it acts a little sticky. In other words, the video will fast forward (or rewind) a few seconds after releasing each button. Any idea why it’s not a bit more responsive?
    Kind regards!

  6. David Stiller Says:

    Greg,

    My sample code uses 100 as the second parameter for setInterval(), which is 100/1000s of a second (that is, a tenth of a second). If you decrease that value, you’ll get a tighter loop, which may do it for you, but then you may also want to compensate by increasing the values supplied to seek().

  7. Ian Says:

    I can’t seem to figure out how to get a simple task completed. I am simply trying to get a video to play, pause, and a “restart”, with the buttons I have included. I have the video “play” and “pause” working correctly. I can’t seem to figure out how to get the video to simply start over when I click “restart”. I am new to Flash all together, and am trying to learn and use AS3. AS3 is also what I must finish this task in. Can you suggest a solution for my situation?
    Additional info: The video file is imported from a server, not imbeded in the timeline.
    Thank you in advance!

  8. David Stiller Says:

    Ian,

    Syntax in ActionScript 3.0 is often significantly different from the format used in ActionScript 2.0 — but it sounds like you’ve already achieved two of your three goals. Good stuff! :) To start a video again from the beginning, use the NetStream.seek() method on your ns instance and supply 0 as the parameter.

  9. Aneurin Says:

    This really helped me out with a Flash video player I’m preparing for a client. Thanks David! Thavid!

  10. David Stiller Says:

    Aneurin,

    Glad to help!

  11. Zardoz Says:

    Hi,

    Thanks for the article! This is just what I’ve been searching for.

    I am attempting to build an FLV player which allows me to speed up and slow down the framerate - sound is not required (and not used for any of the FLVs). We could encode with more keyframes but we want to keep the size down. If I use the FLVplayer component (since we’re not streaming, we’re only downloading and playing files here) does that mean I am limited to only displaying keyframes?

    I am thinking of a control or some buttons to change a variable which determines the speed that loop executes using a slider control or similar.

    I’d appreciate any sanity check you can offer me :) Once again, thanks!

  12. David Stiller Says:

    Zardoz,

    For better or worse, in a progressive download scenario, seeking only applies to video keyframes — and honestly, I’m already wincing a bit at the thought of a simulated variable speed playback. The idea is great, for sure, but even fast forwarding and rewinding are dicey, given how much tweaking it can take to find the sweet spot for a given video.

    If you want to experiment with a slider control, check out “How to Build a Basic Slider Widget (AS2)” and use the output from that slider to influence the NetStream.seek() method as outlined above. Take your dest (destination) variable — you can use that both for forward speeds and reverse speeds — and add the result of the slider’s position. If you use the suggestion for panning, you could even have your slider spit out positive or negative numbers. Juggle those against the rate at which your setInterval() function loops. You’ll have to toy with the math a bit to make sure the slider gives you numbers between, say, 0 and 5 (or -5 and 5), rather than 0 through 100.

  13. Rajesh Bhadra Says:

    Hey,
    Can you build flv player in as3. I m hunting for the past week for this.
    Finally got ur link. But its all in AS 2. can you write in AS3.
    I want to load a video from a http server. The major functionality which i want to achieve is rewinding and forwarding the video. I had done every other thing except that. can you please help me out. Send me ur comments on rbhadra@gmail.com.Thanks

  14. David Stiller Says:

    Rajesh,

    Although the syntax can differ greatly between AS2 and AS3, the principles are usually the same. If all that remains for you in your project is the fast forwarding and rewinding, you’re pretty close! ;) Instead of setInterval(), you’ll want to use the Timer class in AS3 to repeatedly invoke the NetStream.seek() method.

    Here’s a quick run-down on the Timer class, from Peter deHaan’s blog:

    http://blogs.adobe.com/pdehaan/2006/07/using_the_timer_class_in_actio.html

  15. Cathy Crisenbery Says:

    David,

    I hope you may be able to help me understand how to correct a problem I am experiencing. I have a large Flash video (1hour 17minutes) that is streaming from a FMS. The video plays just fine. I have used one of the adobe supplied skins for controls. If you try to move forward or backward in the video, the video has no problem advancing or rewinding. However, the audio just seems to get lost. Any ideas how I can remedy this situation?

    Here is a page that shows the problem.
    http://www.pgtest.photonicsg.com/speaker/videos/011_serio_f_wholebody_flash_stream.htm
    Thanks, Cathy

  16. David Stiller Says:

    Cathy,

    I do see what you mean about the audio. That’s interesting.

    I don’t know for sure, but I have a hunch that the video file itself may be problematic. I did find that the audio disappears when I scrubbed far to the right (toward the end of the video), but I also found that I get could it back by scrubbing again to the left. After a bit of dragging, I found the spot where the audio drops out, and I moved back to let the video progress there on its own. Even without scrubbing (at that point), the audio dropped again.

    It’s probably worth testing the FLV file on its own, running from your desktop, to make sure the audio is correct throughout.

  17. Pawel Michalak Says:

    Hi,

    I’d like to ask if it’s possible to play an FLV video frame by frame (slow down the video). I’ve tried to modify the code provided by you, but without success. I’d be very glad if you could give me some advice.

    Thanks,
    Pawel

  18. David Stiller Says:

    Pawel,

    Unless the video is being streamed by special server software, such as Flash Media Server, then seeking only works with video keyframes (as distinct from normal Flash timeline keyframes). For example, you might have a video keyframe every 300 hundred frames; if that’s so, you wouldn’t be able to send your FLV file to frames 301, 302 … 599, because those frames would be inaccessible until 600.

    Theoretically, you could encode your FLV file with a keyframe on every frame, but that would definitely increase the size if your video file.

    Here’s a quick bit of sample code that seems to work all right for me:

    var nc:NetConnection = new NetConnection();
    nc.connect(null);
    var ns:NetStream = new NetStream(nc);
    videoPlayer.attachVideo(ns);
    ns.play("videoFile.flv");
    
    ns.pause(true);
    
    var offSet:Number = 0;
    
    setInterval(function():Void {
      offSet += 0.5;
      ns.seek(offSet);
    }, 1000);

    After the loading the file with NetStream.play(), I immediately pause it. A setInterval() loop updates the (new) variable offSet by 0.5 every second. That means the in the first second, I’m sending that video to whatever video keyframe comes closest to the 0.5 second mark. That might be a half second in, or it might not be, depending on how the video was encoded.

    Because I’m not using Flash Media Server, the result (for me) is not a smooth slow motion display, but more like a rapid slideshow.

  19. Venkat Says:

    Hi David,

    This is in continuation to Pawel’s doubt.
    1) Is there an option where-in I could step to a key-frame (using Flex).

    Example:
    step(100) should take me to the 100th key-Frame in the video (assuming its present). Is there any Class which exposes this functionality? I saw NetStream gives a flexibility to seek only w.r.t time and not w.r.t key Frames.

    2) Also can i get to know the number of keyFrames available in a flv?

    Thanks in advance

    Venkat

  20. David Stiller Says:

    Venkat,

    I have a better knowledge of the standalone ActionScript 2.0 and 3.0 APIs than I do the Flex framework itself. Flex may have a slightly different implementation of NetStream and related classes, but as far as I know, you would use NetStream.seek() as shown here. I see that the VideoDisplay class (in the Flex framework API) doesn’t have a seek() method, which really surprises me.

    I haven’t yet used the VideoDisplay component in Flex, so if I ran into this myself, I would probably just use instances of Video, NetConnection, and NetStream instead.

    Also can i get to know the number of keyFrames available in a flv?

    That’s a really good question! I don’t think there is a way to determine that easily. There may be a way to seek repeatedly to very small increments of time and compare that against the FLV’s framerate, but then again, there may not!

  21. Venkat Says:

    Hi David,

    Yeah, currently I am using a combination of Video, NetConnection, and NetStream.

    However, getting the number of keyframes by “seek repeatedly to very small increments of time and compare that against the FLV’s framerate” isnt a good idea, coz that doesnt really give the number of key-frames.

    Getting the number of keyframes in a flv file is possible, when we get the metaData of the flv file (by over-riding the OnMetadata() event of NetStream and getting all the ‘fileposition’ and ‘time’). But sometimes, the meta data of an flv file doesnt have this info too!!

    Sample for metaData handling (getting keyFrames info):
    —————————————————————–
    var ns:NetStream = new NetStream(nc);
    ns.client = customClient;
    customClient.onMetaData = metaDataHandler;

    private function metaDataHandler(infoObject:Object):void
    {
    var key:String;
    var n:int = 0;
    for (key in infoObject)
    {
    trace(key + “: ” + infoObject[key]);
    }
    trace(’ keyframes:’);
    var fp:Array = infoObject.keyframes.filepositions;
    var ti:Array = infoObject.keyframes.times;
    for (n=0; n

  22. David Stiller Says:

    Venkat,

    As you noted, it all comes down to what metadata (if any) is embedded in the FLV. Your approach seems to have been cut off, but I’ll follow up with you for the complete code. Thanks for sharing it! :)

  23. Cyril Says:

    Hi,

    I am developing a flashlite application which plays device video.I placed a video instance on the screen by creating and dragged on to the stage.But now i am not able to spot the end of that video. vedio.onStatus is also not working. Can anybody help me in this issue..??

  24. David Stiller Says:

    Cyril,

    Flash Lite doesn’t support the NetStream class, so you’ll have to reference your onStatus handler in terms of the Video class. Check out the Video.onStatus event of the Flash Lite 2.x ActionScript Language Reference for a small bit of sample code. Note that error or status messages, found in the code property of the event handler’s function parameter, are device dependent. That means you’ll have to use trace() to experiment a bit with what status messages your particular device transmits.

  25. Corey Stroeder Says:

    David,

    Thanks for the great tip. It helped me out lots. I did modify it and I thought I would share it for others to ponder. Not sure if this is the best practice, but for now I think it will work great for my needs.

    Fast Forward code:

    this.ffButt.onPress = function() {
    this.onEnterFrame = function(){
    trace(”PRESSED”);
    ns.seek(ns.time + 0.5);
    }
    }
    this.ffButt.onRelease = function() {
    trace(”RELEASED”);
    delete this.onEnterFrame;
    }

    Rewind Code:

    this.rwdButt.onPress = function() {
    trace(”PRESSED”);
    var dest:Number = ns.time;
    this.onEnterFrame = function(){
    ns.seek(dest -= 1);
    }

    }
    this.rwdButt.onRelease = function() {
    trace(”RELEASED”);
    delete this.onEnterFrame;
    }

    Like anything there is many ways to skin a cat. What I liked about this is that speeding through the video is directly related to the user holding the related button.

    Thanks again and hope this can help others out.

    Corey

  26. David Stiller Says:

    Corey,

    Like anything there is many ways to skin a cat.

    You betcha! That’s one of my favorite things about Flash, too. Thanks for sharing this alternate approach! :)

  27. Claudia Says:

    Hi, thanks to explain in so much detail
    I think this is what I ‘ m looking for.
    But I can’t make it work : (
    I do not know what is wrong
    Can you help me by simple upload your fla example?

    This is what I trying to do:
    http://www.whalefootage.org/start.php?lang=en
    Look at the botton menu.

    But for the moment I only want go back and forward as you explain.
    Sorry for my English!
    Thank’s from Uruguay
    Have a Happy new Year!

  28. Claudia Says:

    Hi again!
    I made it!!
    The proble was the video
    I do not know what is the difference but the same code works for one and not for the other.
    Any one have notice that?

  29. Nimisha Says:

    How to Fast Forward and Rewind sound using actionscript3 for Adobe CS3?
    Mail back to me please, thank you

  30. Valdemar Says:

    It was very usefull!!!
    Many thanks for the code, David.

  31. David Stiller Says:

    To Nimisha …

    Your question makes a good topic for a tutorial of its own, so I’ve added it to my list. Mailing you an answer personally doesn’t help other visitors to this blog. ;)

    To Valdemar …

    Thanks! Glad to hear that.

  32. Dan Says:

    Hi David,

    While I have found this very helpful indeed, I am also building a player using AS3. I’ve gotten pretty far including buttons and a scrubber using this and other help sites, but seem to have hit a wall when it comes to buffering. The buffer itself works fine but the movieClip I have to show the buffering status doesn’t remove itself for whatever reason.

    The code I have for AS2 is:

    ns.bufferTime = 30;

    ns.onStatus = function(info) {
    if(info.code == “NetStream.Buffer.Full”) {
    bufferClip.visible = false;
    }
    // when the buffer is empty show buffer text
    if(info.code == “NetStream.Buffer.Empty”) {
    bufferClip.visible = true;
    }
    // when the video has finished return to beginning
    if(info.code == “NetStream.Play.Stop”) {
    ns.seek(0);
    }

    }

    Like I said I’m trying to convert this to AS3, so any help would be great as I am beginning to lose my hair…!

  33. David Stiller Says:

    Dan,

    If that’s your AS2 code, then you’ll have to change that bufferClip.visible reference to bufferClip._visible (note the underscore). Many AS2 MovieClip properties begin with an underscore for historical reasons.

    It’s always a good idea to doing some troubleshooting, while you’re at it. The trace() function sends message to the Output panel for you, so you could do something like this:

    ns.onStatus = function(info) {
      if (info.code == "NetStream.Buffer.Full") {
      bufferClip.visible = false;
      trace("buffer is is full");
    }
    // when the buffer is empty show buffer text
      if (info.code == "NetStream.Buffer.Empty") {
      bufferClip.visible = true;
      trace("buffer is empty");
    }
    // etc.

    … to ensure that the event is firing at all, even if bufferClip isn’t doing what it should.

  34. David Says:

    Have you tried this with h.264 video content? The ff and rewind buttons don’t seem to work just right. Do you think it has to do with the metadata within the MPEG-4 file? I’d really like to get your ff and rewind method to work with MPEG-4 H.264 videos. Thanks.

  35. John David Hutton Says:

    Hi David, I was reading through your excellent scripting article notes but wasn’t sure if the reverse section is what I need or not. I currently have a flvplayback component in my flash AS 2.0 file I’m working with. I’ve made 3D renders so that we look at a layout and start in on each section when a button is pressed. All of that works great. The problem I’m having is that I’d like the same video to be reversed each time they hit a new link so we’re taken back to the home position first before proceeding to the next section they clicked on. I’m not finding an easy way to reverse in the flvplayback component and wondered if you could comment on that?

  36. Zack B Says:

    Hey dude, great article and great blog! Keep on coding brother!

Leave a Reply