How to Play Flash Video Files (FLV) Sequentially

ActionScript 2.0 ActionScript 3.0

Half a year ago, I wrote a brief article on “How to Play Sound Files Sequentially” (AS2).  Recently, a reader was asking how to apply the same principle to video.  In his case, Paul had to play a commercial first, then follow it with a longer content video — and the user controls (buttons, scrubber, etc.) needed to be disabled wile the commercial was playing.  As it turns out, Paul worked out a solution of his own, partly based on some of the other articles here, but I thought it would be fun to do a quick version too.  That way, he can compare notes and anyone else can look on. 

An answer, short and sweet

To my thinking, the key is to reuse as much code as possible.  Even though we’re playing two (or any number) of videos, we only need a single instance of Video, NetConnection, and NetStream apiece.  That’s if we’re going with a non-component approach, like the one described in “How to Load External Video (FLV)” (AS2).  We’ll start with that one, to get the basic idea across, then I’ll also show how to accomplish the same thing with the FLVPlayback component.  The operative mechanism, in all cases, is to detect when the current video has ended, then automatically start playing the next one, until the list of videos is complete.

Let’s start with ActionScript 2.0.  Here’s the chunk of code:

ActionScript 2.0

var videos:Array = new Array("a.flv", "b.flv", "c.flv");
var currentVideo:Number = 0;
var duration:Number = 0;
var ready:Boolean = true;

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachVideo(ns);
ns.play(videos[currentVideo]);

ns.onMetaData = function(evt:Object):Void {
  duration = evt.duration;
  ready = true;
};
ns.onStatus = function(evt:Object):Void {
  if (ready && this.time > 0 && this.time >= (duration - 0.5)) {
    ready = false;
    currentVideo++;
    if (currentVideo < videos.length) {
      ns.play(videos[currentVideo]);
    } else {
      delete this.onStatus;
    }
  }
}

How it works

The first line declares an arbitrarily named variable, videos, and sets it to an instance of the Array class.  This Array instance is populated with three strings, “a.flv”, “b.flv”, and “c.flv”.  Arrays are just lists, and this is a list of three FLV files.  The second line declares another variable, currentVideo, which is a number, and sets its value to zero.  Arrays start at zero, so in a moment, you’ll see this variable used to pull the first video from the list.

The third line declares a variable named duration, which will be updated later from its initial value of zero in response to the NetStream.onMetaData event, as described in “How to Determine the Completion of a Flash Video (FLV) File.”  Finally, a Boolean (true/false) variable named ready is declared and set to true.  This will be used to ensure that the onStatus event handler doesn’t perform its tasks too often in a row.

The next few lines — starting with var nc:NetConnection — use the approach suggested in “How to Load External Video (FLV)” to load the first video.  In fact, the videos array already makes its entrance:

ns.play(videos[currentVideo]);

Remember, currentVideo is 0, so the preceding line is effectively the same as saying videos[0], which pulls the first element from the videos array (the string “a.flv”, or whatever the name of your own video is; for Paul, this would be his commercial).  Ultimately, this expression resolves to ns.play("a.flv");.

The onMetaData event handler is explained in the “How to Determine” article, which leaves the NetStream.onStatus event handler.  Most of what it does is covered in the same onMetaData article, but in a nutshell, the onStatus event is dispatched whenever the video does something interesting, like start, download data, stop, etc.  It fires a couple times near the beginning and the end of FLVs, so the if statement checks if its current position (NetStream.time, represented by the expression this.time) is greater than zero and (&&) is greater than half a second less than the video’s full duration.  The very first item in the if condition, ready &&, adds one more filter level, so to speak.  Because onStatus fires so often, it can be triggered more than once even while the other two portions of the condition are true.  For this reason, ready is set to false as soon as the whole condition evaluates to true.

If all three sub-conditions are true, the ready variable is set to false.  This keeps onStatus from perfroming the next part too many times in a row.  The currentVideo variable is incremented by one (currentVideo++), which changes it from 0 to 1.  Next, a second if statement checks of currentVideo (now 1) is less than the Array.length property of videosArray.length returns the number of items in an array (we have three strings, so the property returns 3 in this case).  1 is less than 3, so the following line is executed:  ns.play(videos[currentVideo]);.  That’s the same line we saw earlier, but this time, the value of current Video has changed, and the second video plays.  When it does, the onMetaData handler will again set ready to true, which re-allows onStatus to do its thing.

This procedure will occur again at the end of the second video, at which point currentVideo will increment to 2.  2 is still less than 3, so eventually, the third video will also play.  At that point, currentVideo will increment to 3.  Obviously, 3 is not less 3, so the else clause will execute, which simple shuts down the onStatus event handler.

To fulfill the requirement of disabling user controls (see “How to Build a Basic Slider Widget (AS2)” and “How to Build a Basic Toggle Button (AS2)”), the same area of the NetStream.onStatus event handler could be used:

ns.onStatus = function(evt:Object):Void {
  if (ready && this.time > 0 && this.time >= (duration - 0.5)) {
    ready = false;
    currentVideo++;
    if (currentVideo > 0) {
      toggleButton.enabled = true;
      sliderKnob.enabled = true;
    }
    if (currentVideo < videos.length) {
      ns.play(videos[currentVideo]);
    } else {
      delete this.onStatus;
    }
  }
}

All that has changed is the addition of a new if statement, which checks if currentVideo is greater than zero.  If it is, then we’re obviously past the first video, which was the commercial.  At that point, any user controls can be turned on (they would have been disabled earlier by default).  Re-enabling your user controls will, of course, depend on how they were made.  If you’re using UI Components that ship with Flash, you’ll have to consult their respective APIs in the ActionScript 2.0 (or 3.0) Language Reference or Components Reference.  In Paul’s case, the controls were made from movie clips, which means the MovieClip class entry is what he’d need to consult.  In ActionScript 2.0, the MovieClip class happens to feature _visible and enabled properties.

ActionScript 3.0

Here’s the same basic outline in AS3:

import flash.events.NetStatusEvent;
var videos:Array = new Array("a.flv", "b.flv", "c.flv");
var currentVideo:uint = 0;
var duration:uint = 0;
var ready:Boolean = true;

var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachNetStream(ns);
ns.play(videos[currentVideo]);

var listener:Object = new Object();
listener.onMetaData = function(evt:Object):void {
  duration = evt.duration;
  ready = true;
};
ns.client = listener;

ns.addEventListener(
  NetStatusEvent.NET_STATUS,
  nsHandler
);

function nsHandler(evt:NetStatusEvent):void {
  if (ready && ns.time > 0 && ns.time >= (duration - 0.5)) {
    ready = false;
    currentVideo++;
    if (currentVideo < videos.length) {
      ns.play(videos[currentVideo]);
    } else {
      ns.removeEventListener(
        NetStatusEvent.NET_STATUS,
        nsHandler
      );
    }
  }
};

How it works

Must of the syntax overlaps.  I’ve replaced the Number type with uint in the first few lines (see “Mind Your ints and uints”).  The onMetaData event handler is structured differently — one of the rare cases in which AS3 requires a liaison listener Object instance — but the same basic activity takes place.  The NetStatusEvent.NET_STATUS event handler replaces AS2’s onStatus.  The NetStream instance (ns) is associated with a custom function, nsHandler, by way of the addEventListener() method.  The nsHandler function repeats what we’ve already seen, except that instead of using delete to unhook the event handler, it uses removeEventListener().

How About the FLVPlayback Component?

If you’re using the FLVPlayback component, fewer lines of code are needed.  You’ll simply drag an instance of FLVPlayback to the Stage, use the Property inspector to associate it with the first of your FLVs, then employ the following:

ActionScript 2.0

var videos:Array = new Array("a.flv", "b.flv", "c.flv");
 var currentVideo:Number = 0;
videoPlayer.skin = "";
var listener:Object = new Object();
listener.complete = function(event:Object):Void {
  currentVideo++;
  if (currentVideo > 0) {
    videoPlayer.skin = "MojaveOverPlaySeekMute.swf";
  }
  if (currentVideo < videos.length) {
    videoPlayer.contentPath = videos[currentVideo];
    videoPlayer.play();
  }
};
videoPlayer.addEventListener("complete", listener);

Note that the skin part is a different approach toward disabling user interface.  Because skins are actually separate SWF files, we would need a way to access the MovieClip instance in the main SWF that represents the loaded skin in order to disabled it via, say, MovieClip.enabled.  Rather than hassle with that, I’ve chosen to set the FLVPlayback.skin property to an empty string at first (""), then set it to the file name of an existing skin later (obviously, that skin must be present, or it won’t show).

ActionScript 3.0

var videos:Array = new Array("a.flv", "b.flv", "c.flv");
 var currentVideo:uint = 0;

videoPlayer.addEventListener(
  Event.COMPLETE,
  completeHandler
);

videoPlayer.mouseChildren = false;

function completeHandler(evt:Event):void {
  currentVideo++;
  if (currentVideo > 0) {
    videoPlayer.mouseChildren = true;
  }
  if (currentVideo < videos.length) {
    videoPlayer.source = videos[currentVideo];
    videoPlayer.play();
  }
}

In AS3, the DisplayObjectContainer.mouseChildren property, inherited by FLVPlayback, is enough to disable the optional skin controls, then re-enable them later.

51 Responses to “How to Play Flash Video Files (FLV) Sequentially”

  1. JTR Says:

    First, this code is great! Nicely done. One change: the incrementer, currentVideo, is updated too soon. Move it to the end of the function…

    function completeHandler(evt:Event):void {
    if (currentVideo > 0) {
    videoPlayer.mouseChildren = true;
    }
    if (currentVideo

  2. Dimi Says:

    Hi, thanx for tutorials - I’m really enjoying them even though I’m new to flash:) But maybe you can help me with sequential video player in AS3:)

    When Im trying to launch your code for AS3 it gives me an error :
    1120: Access of undefined property videoPlayer.

    I assume there should be something like videoPlayer instance , but have no idea how to implement it :( . I would be very gratefull if you could maybe email me the fla file with the code done correctly , as it seems like i’m too lame to implement this exmaple :)

    Thank you.

  3. Dimi Says:

    OH, i did it I think :D I’ve added a new Video Object to scence :) Sorry for my lame :D

  4. Gary Says:

    David
    I just picked up a copy of the actionscript 3.0 bible and tried building an FLV playback app using code and I get the error “1037: Packages cannot be nested.” I can’t find anything on this error or how to fix it. Here is a copy of the code itself. This code is right out of the book.

    package
    {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    public class Timeline extends Sprite
    {
    // lots of AS3 class-based code, snipped
    // by David Stiller in the interest of brevity

  5. David Stiller Says:

    To Dimi …

    No worries! Glad you solved it. :)

    To Gary …

    Did you paste this code into a timeline keyframe? If so, that’s probably the reason for the warning. The code shown is intended to be saved in an external text file.

  6. Gary Says:

    how is that supposed to work??? i typed it in a key frame

  7. David Stiller Says:

    Gary,

    Flash CS3 has a new feature that allows every last shred of your ActionScript to be stored outside of the FLA. You certainly can write frame-based ActionScript 3.0, but if you do, it’s not permitted to use the package or class keywords — because under the hood, Flash has already created an automatic class and package for you. By using the same keywords in your keyframes, you’re unwittingly nesting these entities, which goes against the AS3 compiler’s rules.

    I haven’t read the new AS3 bible yet, so I can’t flip through the book, but based on what you pasted, you’ll be using one of those classes as your Document class. Here’s a Quick Start article on Adobe that introduces the concept:

    http://www.adobe.com/devnet/flash/quickstart/external_files_as3/

  8. Md Nawed Says:

    The event handler is firing twice so the counter is incremented by two.It will always play two videos “a.flv” and “c.flv” the video b.flv is surpassed.

  9. Md Nawed Says:

    The used your actionscript 3 example without flvplayer component

  10. David Stiller Says:

    Md Nawed,

    You’re right! JTR (first comment) noticed the same thing, so I need to address that. Thanks, to both of you, for pointing this out! :)

  11. Holland Oats Says:

    (using as2 code, no disabler) my array has four videos in its list, playback runs the first from start to finish, then jumps ahead to the last…?

  12. David Stiller Says:

    Holland Oats,

    That was my bad! Thanks to JTR’s, Md Nawed’s, and your comments, I was able to work up a correction to the code — in all the examples that needed it — which now appears in the updated article.

  13. Patrik Says:

    Hey David, to begin with thanks for a great blog, it’s helping me out a lot!

    I used your code to solve my problem with running sequential videos but now I’ve stumbled on another problem related to this. Since you’re the guru here I thought I’d just ask you directly. In my project I need to have a progress bar for the playback of all the video clips, combined. So I figure I need to somehow preload all the clips first to find out the total time and then build the progress bar out of that value. But since I’m relatively new to AS I’m not totally sure how to accomplish this. Would you mind helping me out?

    Again, thanks a lot for a great blog.
    Patrik

  14. David Stiller Says:

    Patrik,

    Off the top of my head, this should be possible via the NetStream.onMetaData event, and I don’t think you’d need to preload each video first. As long as the metadata of each FLV states the video’s length, you should be able to load each FLV in turn and handle the onMetaData event to update a running grandTotal variable. When all the videos have been tapped (but not completely loaded), you could then restart the process and load each for actual play.

    Of course, onMetaData will be dispatched even for the second round of loading, so you’ll want to put some mechanism in place to keep track — maybe a Boolean gatheringDuration variable that’s true by default and updated to false when the total duration has been determined. You’re still going to need a duration variable in order to track of the duration of individual videos.

  15. Patrik Says:

    Thanks for your answer, I did solve my immediate problem by somewhat using your code twice. The first time calculating a total time and then the second time playing the videos.

    But do you mean I don’t need to use the onStatus event to load the next video? And don’t I need to call the play method of my ns, since as far as I understand, the onMetaData is only invoked after the play method is called?

    Again, thanks for a great blog.

  16. David Stiller Says:

    Patrik,

    That last reply of mine wasn’t especially lucid. ;) I apologize for that. It was mostly hypothetical, and if I wasn’t so behind in my work lately, I would have already started in on a more practical attempt.

    You wouldn’t necessarily need the onStatus event to run the first batch of loads, because you could make that determination based on onMetaData alone. (As soon as the metadata is retrieved, you know you’re safe to load the next video in the list.) Once the grand total of durations is gathered, you could go back to using onStatus to handle the end of your videos, if you want. Of course, you could ultimately use any event that triggers often enough, such as MovieClip.onEnterFrame (or even a timer loop, like setInterval()), but I decided on onStatus because it doesn’t fire nearly as often, which presumably saves on processor cycles.

    You would indeed need to invoke play() on your NetStream instance, because that not only causes onMetaData to occur, as you noted; it also informs the NetStream instance which file to load.

    My schedule has been unusually hectic lately, otherwise I would try to put together something more solid for you to go on. (I’m working on a new book.) It’s always luck of the draw as to when I have spare time … though it does sound like you’re wrestling with the problem and getting somewhere with it. That’s encouraging!

  17. Chris Says:

    nice code. How can i play sibling movie clips that are on the main stage one after another with a two second pause between?

    ctd {at} roadrunner {dot} com

    much appreciated.

  18. David Stiller Says:

    Chris,

    Movie clips are defined by the MovieClip class, so that’ll be your source for what tools are available. (Classes define objects and usually provide one or more of the following categories: properties [characteristics], methods [things the object can do], and events [things the object can react to].)

    Here’s a quick throw-down on how to accomplish the same concept with movie clips:

    var clips:Array = new Array(clipA, clipB, clipC);
    var currentClip:Number = 0;
    
    for (var i:Number = 0; i < clips.length; i++) {
      clips[i].stop();
    }
    
    clips[currentClip].play();
    clips[currentClip].onEnterFrame = catchEnd;
    
    function catchEnd():Void {
      if (this._currentframe == this._totalframes) {
        delete this.onEnterFrame;
        this.stop();
        currentClip++;
        if (currentClip < clips.length) {
          clips[currentClip].play();
          clips[currentClip].onEnterFrame = catchEnd;
    	}
      }
    }

    Compare that against the code in the article, and you’ll see how the themes overlap. To introduce the two-second pause, you could use the setTimeout() function:

    var clips:Array = new Array(clipA, clipB, clipC);
    var currentClip:Number = 0;
    
    for (var i:Number = 0; i < clips.length; i++) {
      clips[i].stop();
    }
    
    clips[currentClip].play();
    clips[currentClip].onEnterFrame = catchEnd;
    
    function catchEnd():Void {
      if (this._currentframe == this._totalframes) {
        delete this.onEnterFrame;
        this.stop();
        currentClip++;
        if (currentClip < clips.length) {
          setTimeout(
            function():Void {
              clips[currentClip].play();
              clips[currentClip].onEnterFrame = catchEnd;
            },
            2000
          );
        }
      }
    }
  19. Juan Says:

    David,

    I must say you have a fantastic blog going here buddy. I’ve really enjoy this tutorial it has helped a lot. Keep up the great work man!

    I only have one question…is it possible for the non-component approach to have some sort of pre-loader function.

    I have a couple of videos that I’m playing one after the other but I want to be able to download the first one in its entirety before it plays and have the second one load while the first one plays. This would make users with slower connection enjoy the video as well.

    Any thoughts?
    thanks.

  20. gary Says:

    I know this may not belong under this title, but I was wondering if there is a way to resize a movie by using XML. If so how would I go about doing it????

  21. Emma Says:

    Hi David,

    I’ve spent ages trying to find tutorials and blogs that will help me with this kind of thing. Even posted on a few forums, but to no avail (everyone keeps ignoring me!) Your blog is fab!

    To quickly summarise what I’m trying to accomplish:

    I have an hour long lecture that I’m cutting into short flv files that directly relate to a powerpoint slide used in the lecture. What I’d like to do, is have each flv file play sequentially when the swf file is first opened so the user can watch the whole of the lecture, or then can choose a section using buttons which relate to each flv clip and powerpoint slide. Any ideas on how to do this?

    I cut the original video up into chunks, as I thought it may be easier and smaller than using an hour long flv file with cue points embedded in it, and using these cuepoints as event triggers. Which do you think is a more sensible option?

    Thanks,
    Emma

  22. David Stiller Says:

    To Juan …

    Sure thing! Check out the NetStream class entry in the ActionScript 2.0 Language Reference, and you’ll see two properties (bytesLoaded and bytesTotal) that give you access to the information you need. You’ll have to loop repeatedly to compare those property values against each other, and the technique is similar in principle to described in “How to Tell When an External SWF has Fully Loaded.”

    To gary …

    I was wondering if there is a way to resize a movie by using XML

    In and of itself, XML doesn’t really do anything — not in the sense of performing tasks, such as resizing a movie, resizing a video, or the like. What XML does do is store information, so you could certainly use XML to store the path to an FLV file and store preferred dimensions for that video. Once the XML is loaded, you could retrieve that information and use it to resize an FLV.

    To be honest, though, I’m not sure if you mean FLV files when you say “movie.” If you mean the SWF file itself, that’s not something Flash is capable of doing. ActionScript can’t tell Flash Player to resize a SWF, but you can certainly publish a SWF so that it responds when the user resizes the browser. Is that what you mean?

    To Emma …

    It shouldn’t really matter if you divide up the FLV files or not. I’ve seen hour-long Flash videos play just fine, and cue points are cue points. :) That said, if you want the user to be able to jump from section to section — and if you’re not using a streaming server like Flash Media Server — then dividing up the files is a good idea. Why? Because you can’t program progressively downloaded video to skip ahead unless that portion of the video has already loaded. In your divide-and-conquer approach, there’s technically no skipping ahead … it’s just a matter of requesting a new FLV file.

    In this scenario, you could have your buttons explicitly set the value of the curentVideo variable, then invoke the expression ns.play(videos[currentVideo]);. Off the top of my head, that should be enough, but let me know if that doesn’t do it for you, or if my suggestion isn’t enough to get you rolling.

  23. Emma Says:

    Thanks David,

    That certainly clears up my dilemma.

    As for the buttons, sorry to be a pain, but the little I know of AS means that I don’t even know where to start! At the moment I have:

    b1.onRelease = function () {
    ns.play(”LB_clareIntro_Sl1.flv”);

    Which worked with a ‘video symbol’ but not the flvplayback component, as I think I need some other code. Do you have another tutorial I should read? I really do appreciate the help!

    Thanks,
    Emma

  24. JeffreyJ Says:

    Greetings David,

    First off, thanks for the clarity in your blog, it is quite refreshing to have detailed non-confusing insight into this.

    My question is in line with this section, and has to do with even more dynamic sequentially appended videos. The problem is put forth as:

    I know a.flv and I know c.flv ahead of time, but I will not know b.flv until the user logs in. Therefore, I can’t pre-fill the array as in all your examples with b.flv.

    var videos:Array = new Array(”a.flv”, “.flv”, “c.flv”);

    Would would be the easiest way to tackle this?

    Thanks,
    JeffreyJ

  25. Tiemen Says:

    Woa David, incredible! It seems your helping me out again! But I’m unsure whether this will completely solve my problem. What I am asked to do, is the dynamic creation of a movie, that consists of an arbitrary collection of clips. It’s not like Jeffrey that those clips will be added while the movie is playing, but the sequence of movies should really behave as one big movie. Is there even a way of just recompiling thoise clip to one actual flv at runtime?

  26. David Stiller Says:

    To Emma …

    You’ve got the general structure right: b1.onRelease = function():Void { ... } (where b1 is the instance name of your button), but if you’re dealing with FLVPlayback, then the inside of that function needs to reference your FLVPlayback instance, rather than ns, which is presumably a NetStream instance.

    Instance names are you all-important connection between objects and the ActionScript that instructs them. It just happens that the FLVPlayback and NetStream classes both feature a play() method, so if you make sure to give your FLVPlayback component an instance name, that instance name-dot-play() should do it. :)

    To JeffreyJ …

    First off, thanks for the clarity in your blog, it is quite refreshing to have detailed non-confusing insight into this.

    Thanks! :)

    Therefore, I can’t pre-fill the array as in all your examples with b.flv.

    var videos:Array = new Array(”a.flv”, “.flv”, “c.flv”);

    Gotcha. Well, how is it that the logged-in user makes that “b” choice known? How does the logging-in part work? Is there a database, for example, where you can find out that user’s choices? If so, you could populate your array from there. You could possibly store an array of known choices, then — when the user’s “b” choice becomes available — use that existing array to create a new array, ultimately using the new array for your playback.

    To Tiemen …

    Hi! Good to hear from you again. :)

    I imagine there’s server software that can stitch together FLV files, but I haven’t worked with any first hand. I would probably play individual FLVs in sequence, but the trick in this case (I’m guessing) is to provide a progress bar, or some other indicator, that shows the multiple clips as one single clip, right?

    That is a challenge. ;)

    Someone else asked a similar question in another post on this blog (or it might even be this one), and my answer — hypothetically, admittedly — was something along the lines of requesting the clips in two passes: in the first pass, do nothing more than gather duration (via onMetaData); in the second pass, play the FLVs as usual, but now with the knowledge of the total playback time for all clips.

    Not sure if that helps, but maybe it helps steer you in the right direction?

  27. Ryan Says:

    Hi,
    Great Code. I was wondering if you could help me out Im pulling out my hair just trying to loop 2 flvs on the same timeline of my movie. The first FLV is an intro and it loops when clicked it calls the second flv. After that FLV is finished my movie stops but I want it to return to the first video.

    If you could help me out that would be great.

    Thanks.

  28. David Stiller Says:

    Ryan,

    When you say your FLVs are “on the same timeline,” I’m not sure what approach you’re using to play those FLVs. My hunch is that you’re using FLVPlayback, but I need more info go to on. For example, how are handling the click that “calls” the second FLV? What does “calls” mean in this context? Are you pausing the timeline while the first video plays, then advancing the timeline to the next one? If so, you could handle the event that signifies the completion of your second video, then send the timeline back to the first video.

    Does that make sense?

  29. Richard Says:

    Hey there David, great job, thanks!!!

    OK big question. I created a video player from Lee Brimelow’s directions at lynda.com. It worked out well, but I need to play 5 videos sequentially with out components (the links are different places on the stage so can’t be in a list fashion).

    So I tried combining your code with his, but now the progress bar, scrub bar and time code don’t work, and it’s not loading the next video in the sequence. I’m guessing there’s a problem with the way I’m getting the meta data and how it’s used.

    Any ideas on how I can have sequential play back with a progress bar, scrubber and time code read out?

    Thanks,

    Richard

  30. David Stiller Says:

    Richard,

    Another reader wrote to me about Lee’s video player, months ago. We did eventually work that one out, but it’s often tough to mesh code from two different sources, especially when both sets of code do a lot. ;) My schedule gets pretty crazy sometimes, so I’m not always able to spend that kind of time without charging.

    If memory serves me, it had to do with the onMetaData event handler. I think Lee’s version had a metadata handler of its own, and the combination of his and mine canceled each other out, as you can imagine. (I haven’t seen his Lynda.com tutorial, so I’m not familiar with the full extent of his approach.)

    The “trick,” really, is to make sure the progress bar and scrub bar variables — along with whatever other prepping is necessary — are initialized for each new video as it loads. Pop a few trace() statements into your onMetaData and onStatus event handlers to show you the values of relevant variables at those points in the application’s execution.

    Looking under the hood is often a real eye-opener! :) When you can “see” your data like that, code often seems to snap into place. You may very well see what’s going askew.

  31. Michael J Says:

    David -

    Excellent Blog! You’re helping a lot of people - thank you!
    I’m using AS2 in Flash 8 and imported the script (above) into the actions layer. Bing! The flv starts but only the audio - no video. It does transition from flv to flv… I’m just not getting the actual video feed. Any clues? Thanks Again!

    Michael

  32. Tiemen Says:

    Hey David, as I’m using this method in a real life project, here some feedback from the field: I find that now we use the onMetaData method to determine the duration, offset between the ‘real’ and advertised duration as in evt.duration occurs when playback of the flv is slowed down (because you opened four programs at once, dropped your glass of beer on your laptop, or simply have a slow one) In any way, in that case the flv will still be chopped off at the advertised duration, even though it hasn’t finished playing.
    Had some trouble with this, as it also chopped off some late cuepoints..

  33. Ruth Says:

    Hi David, first I want to thank you for all of the helpful information you provide. It is priceless.

    My issue is the exact same as what Richard posted on April 22nd. I know your schedule is quite busy so Richard if you have time and actually read this post, I would like to discuss how you solved your issue. Or David, if you have some way of contacting Richard and could forward this post to him, I would appreciate it! Thank you!

  34. Angie Says:

    Hi David, thanks for the tutorial. The information you provide is so helpful. Using your above example with arrays, the flvplayback component and AS3 - how can i turn it into a more robust playlist? So that each array was a category of videos and the user could select the category they want to browse through? I’ve looked everywhere for information on this. Any insight you could give me I would really appreciate. Again, thanks for the tutorial!

  35. Peter Says:

    Hi David, many thanks for this tutorial. My scripting level is really basic so pardon me if this is a dumb question ; )

    If you could help me on this - I wanted the flvs to loop, meaning after playing file no.4 for example, I wanted it to go back to file 1 then 2, 3, 4 and again indefinitely.

    I did this by copying and pasting the filenames 10times (which should be enough) - which means it goes a.flv, b.flv. c.flv, d.flv, a.flv, b.flv. c.flv, d.flv, a.flv, b.flv. c.flv, d.flv, …

    I think this might not be really a “proper” method. Is there any other way?

    Thank you.

  36. Matt Says:

    All the examples are for local or progressive file download.
    I’ve also seen implementations for streaming…
    ————————————————
    var connection_nc:NetConnection = new NetConnection();

    // Put the RTMP URL here, leaving off the name of the video:
    connection_nc.connect(”rtmp://rtmp.myserver.com/path/folder”);
    var stream_ns:NetStream = new NetStream(connection_nc);
    my_video.attachVideo(stream_ns);

    // Put the name of the FLV here *WITHOUT* “.flv” at the end:
    stream_ns.play(”my_video”);
    ————————————————

    How would I combine both option s inorder to create a complex FLV player that takes in a URL or an URL array as a variable and plays them progressively or via streaming?

  37. David Stiller Says:

    To Michael J …

    I have occasionally encountered the audio-only issue you mentioned, but sadly, nothing comes to mind as an immediate “Ah, it’s probably this” solution. If I remember right, I might have had to re-encode the FLV(s) in question, but I suspect that doesn’t help you … especially after such a long delay on my part. Sorry!

    As a matter of habit, when I run into seemingly inexplicable issues like this myself, I try to break it down into smaller, simpler parts. For example, can you load a single FLV — no arrays, no looping — with the basic video-loading ActionScript?

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

    If so, then add an Array instance with only a single entry, and use that in place of the file’s literal name:

    var videos:Array = new Array("nameOfFile.flv");
    // . . .
    ns.play(videos[0]);

    Then keep trying to re-build the rest of the code in steps.

    To Tiemen …

    Thanks for the note, buddy! That may well be a reason to use the if (evt.code == “NetStream.Play.Stop”) { ... } approach.

    To Ruth …

    I’ve responded to you via email to see if I can help you with that.

    To Angie …

    That’s a really neat idea! If I were going to encode a list like that, I would probably format it as XML. That way, you could create as many categories as you please — just create a <category> tag, for example, and nest <video> tags inside them. Illustrating something like that would take some thought … I’m not sure how familiar or comfortable you are with XML, but does that at least give you a place to start?

    To Peter …

    To loop your videos indefinitely, you could use an if statement to reset the currentVideo variable back to 0 when it gets to the end. In fact, you could use the if statement already in place in the onStatus handler. Note the difference here:

    ns.onStatus = function(evt:Object):Void {
      if (ready && this.time > 0 && this.time >= (duration - 0.5)) {
        ready = false;
        currentVideo++;
        if (currentVideo >= videos.length) {
          currentVideo = 0;
        }
        ns.play(videos[currentVideo]);
      }
    }

    To Matt …

    I don’t know RTMP as well as I’d like to (and probably not as well as I should), but for example … assuming the RTMP feed always begins with the rtmp:// protocol, you could use, say, String.indexOf() to check of the string “rtmp://” is present in the path. If it is, it’s a stream; if not, it’s a progressive download.

    Does that help?

  38. Matt Says:

    Thanks David… I thought I’d have to do some crazy ReEx thing.
    String.indexOf() could work…
    Once I matched RTMP:// how would I find all the characters after the last “/” ?
    I want to isolate the filename and use that as the NetStream.Play
    and the rest of the previous characters in the url string for the NetConnection.

    I’m going to work on this tonight. I’ll post back anything I find.
    -Matt

  39. David Stiller Says:

    Matt,

    Sure thing. :) Unfortunately, AS2 doesn’t support regex directly, but Pavils Jurjans’ Flash 5/MX-era custom RegExp class is terrific. You’ll find an AS2 port by Joey Lott on the same link:

    http://www.jurjans.lv/flash/RegExp.html

    Fortunately, AS3 supports regex directly. And fortunately again, you just don’t need it here.

    To rip the rest of the string, use the String.substr() method. It accepts two parameters, begin and end, and the second one is optional. Since "rtmp://" contains seven characters, you’ll pass in 7 as the only parameter, which basically means “Start at character 7 and continue till you hit the end of the string.”

    e.g.

    var str:String = "rtmp://rtmp.myserver.com/path/folder";
    
    if (str.indexOf("rtmp://") > -1) {
    	trace(str.substr(7));
    }

    The if statement checks if "rtmp://" is present at all (the return value would be 0, because "rtmp://" is at the first character in the string, and 0 is bigger than -1) … and if so, the substr() method kicks out the remainder of that string.

  40. Matt Says:

    Thanks David! Really appreciate the help.
    -Matt

  41. Peter Says:

    Thank you so much David. That works wonderfully :)

  42. David Stiller Says:

    Matt and Peter,

    Rock on, guys! Glad you’re rolling now. :)

  43. Matt Says:

    Was easier then I thought… Thought I’d post my code. I’m sure it can be done better but it works for me. Just need to close the stream after playing now.

    ————————-

    function loadURL():void {
    //trace(”vidURL1: ” + _vidURL);

    _appURL = _vidURL.substr(0 , (_vidURL.lastIndexOf(”/”) + 1));
    //trace(”_appURL1: ” + _appURL);

    _lastSlash = _vidURL.lastIndexOf(”/”);
    //trace(”_lastSlash1: ” + _lastSlash);

    _lastDot = _vidURL.lastIndexOf(”.”);
    //trace(”_lastDot1: ” + _lastDot);

    _vidEXT = _vidURL.substr(_lastDot + 1);
    //trace(”_vidEXT1: ” + _vidEXT);

    _vidFile = _vidURL.substr(_lastSlash + 1);
    //trace(”_vidFile1: ” + _vidFile);

    if (_vidURL.indexOf(”rtmp://” || “RTMP://” || “RTMPE://” || “RTMPS://” || “rtmpe://” || “rtmps://”) > -1) {

    trace(”_appURL1a: ” + _appURL);
    //_debugField.appendText(”RTMP URL Found ” + ” \n “);

    conn.connect(_appURL);

    }

    else {

    conn.connect(null);
    //_debugField.appendText(”HTTP URL Found ” + ” \n “);
    }

    }

    function onBWDone():void{
    //trace(”onBWDone: “);
    //_debugField.appendText(”onBWDone” + ” \n “);
    }

    function streamRTMP():void {
    //trace(”StreamRTMP: ” + _vidFile);
    //_debugField.appendText(”StreamRTMP function: ” + ” \n “);
    stream = new NetStream(conn);
    stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
    stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
    stream.play(_vidFile);
    player();
    }

    function streamHTTP():void {
    //_debugField.appendText(”streamHTTP function: ” + ” \n “);
    //trace(”StreamHTTP: ” + _vidURL);
    stream = new NetStream(conn);
    stream.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    stream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, asyncErrorHandler);
    stream.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
    stream.play(_vidURL);
    player();
    }

    ————————–

  44. David Stiller Says:

    Matt,

    I’m sure it can be done better but it works for me.

    I find that to be true of most of my work! Sometimes it depends on deadlines, budget, a combination of those, or something else altogether. ;)

    You might want to consider a tweak to this portion of your code:

    if (_vidURL.indexOf(
        "rtmp://" || "RTMP://" || "RTMPE://" ||
        "RTMPS://" || "rtmpe://" || "rtmps://"
      ) > -1) {

    … only because, if you really want to play it safe, you may want to account for variants besides all-lowercase or all-uppercase. One trick I’ve seen pretty often is to use String.toLowerCase() in the comparison expression to evaluate the target string in all lowercase …

    if (_vidURL.toLowerCase().indexOf(
        "rtmp://" || "rtmpe://" || "rtmps://"
      ) > -1) {

    That’ll even cover bizarre variants such as “RtMP” and the like (not that that’s especially likely). And then, since the “rtmpe” and “rtmps” versions contain the pivotal string “rtmp,” it might be enough just to search for “rtmp” … but like you said, this is already working for you, and I often find the adage “if it ain’t broke, don’t fix it” a good one.

  45. becca Says:

    GREAT POST!

    I was wondering if there is a way to add fade in and out transition between movies and loop?

    Thanks in advance for all your help.

  46. Arunava Says:

    David,

    Thanks very much for such an excellent article. It has helped me to a great extent.

    However, will you please help me out with the following problem:

    My video player will be playing two videos sequentially. When the first video plays, the second video will preload in the background. How to implement the background preloading with the existing code?

    Thanks in advance!!

  47. George Says:

    David,
    I’m a total nube in flash, but I’ve got this working, and the flvs are playing great, I have one items to complicate this though. Maybe you can help. my first video is more like a banner for a site with an odd size. 824X285 and the second video is a promo video with the size of 480×270. Any way to keep that second video from stretching? And one last thing. Is there any way to load an image after the videos have played. So it goes from a banner, to a short promo video and then just a place holder. Does that make sense? Thanks for you help.

  48. George Says:

    David.

    I’m sure you’re a busy man!. Thanks for the help!

    ok. I’ve scrapped the resizing idea, and have just embeded the first animation, and then the video plays, and then I want to gotoandstop(232); or go back to the last frame in the animation, .http://www.aos5.com You can see the animation here. At the end of this animation, they want a video to play, on top of this, and then after the video is done. go back to the last frame of the animation. Any ideas?

  49. anand Says:

    Hi, thanx for tutorials -

    I’m really enjoying them even though I’m new to flash:)

    But maybe you can help me with sequential video player with source for the same, working,….
    :)

  50. Eli Says:

    Please Help me!!

    I have this array working but I also need to have buttons to go to the 4 flv’s I have in my array. The videos fall out of sequence once I do that. What I want to do is have 4 flv’s play in sequence and if a user wants to whatch one out of that sequence they can and it will go to the next one and also at the last one go back to the first. make sense?

    //array
    var videos:Array = new Array(”flv/ownbank.flv”, “flv/obligations.flv”, “flv/rewards.flv” , “flv/helen.flv”);
    var currentVideo:Number = 0;
    var duration:Number = 0;
    var ready:Boolean = true;
    var nc:NetConnection = new NetConnection();
    nc.connect(null);
    var stream_ns:NetStream = new NetStream(nc);
    video_container.my_video.attachVideo (stream_ns);
    stream_ns.setBufferTime(10);
    video_container.my_video.smoothing = true;
    stream_ns.play(videos[currentVideo]);

    stream_ns.onMetaData = function(evt:Object):Void {
    duration = evt.duration;
    ready = true;
    };

    stream_ns.onStatus = function(evt:Object):Void {
    if (ready && this.time > 0 && this.time >= (duration - 0.5)) {
    ready = false;
    currentVideo++;
    if (currentVideo >= videos.length) {
    currentVideo = 0;
    }
    stream_ns.play(videos[currentVideo]);
    }
    }

  51. Raymond Monday Says:

    Hi David,

    The sequential flv playpack component code works perfectly, but the loop code you gave after is going right over my head. I have no idea where to put it in the AS3 code block or how to modify it to my project. I’d just want a simple 1-2-3-4-back to flv 1 forever. :-)
    Whatever I try, I get errors every time. Any help appreciated.
    Thanks

Leave a Reply