How to Determine the Completion of a Flash Video (FLV) File (AS2)

ActionScript 2.0

Nearly a year ago, I posted a short entry on Flash video, “How to Load External Video (FLV).”  As of today, that article has received more feedback than any other on this blog, often asking how to tell when a video file has completed.  The article describes a technique for loading external FLV files without the use of either the FLVPlayback Component or the older Media Components.  Looking back, I believe I should have referenced that detail somehow in the title, because many of the questions have ended up pertaining to FLVPlayback, which has its own complete event.  If you’re using that Component, handling the built-in event is the easiest way to respond to the end of a Flash video, and we’ll cover that in just a moment.  In the spirit of the original article, however, we’ll also take a look at how to determine the completion of a Flash video loaded with ActionScript and a Video object only. 

An answer, short and sweet

If you’re using the FLVPlayback Component, here’s what you can do.  The following code sample assumes:

  • you have an instance of the FLVPlayback Component on the Stage
  • its instance name is videoPlayer (or use what you like, but update the code to suit)
  • its contentPath parameter, in the Parameters tab of the Property inspector, is set to the location of your FLV file

Type the following into your scripts layer:

var listener:Object = new Object();
listener.complete = function(evt:Object):Void {
  trace("Video complete");
  trace(evt.state);
  trace(evt.playheadTime);
}
videoPlayer.addEventListener("complete", listener);

How it works

An arbitrarily named variable, listener, is declared and set to an instance of the Object class.  This object will be used, in a similar fashion described in the “Event Listener” heading under “Event Handlers versus Event Listeners,” to act as liaison for the FLVPlayback.complete event.  This is shown in line 2, where a function is assigned to a complete property of the listener object.  The function receives an arbitrarily named parameter, evt, which is also an Object instance.  This evt object can be used inside the function to report two properties (state and playheadTime) dispatched by FLVPlayback along with its complete event.  In fact, that’s the very next part.  Inside the function, there are three trace() statements.  The first sends a short “Video complete” message to the Output panel.  The next two reference the evt object and the values of the two passed-in properties (state will be “stopped” and playheadTime will indicate the length of the video).  Finally, the complete event is associated with the listener object and subscribed to the FLVPlayback instance itself.

In actual practice, you’ll want to use something other than trace() statements inside that function.  You might, for example, have paused the timeline as the video began; if so, now would be the time to start it again with a play() action.  The choice is yours.  :)

What if I’m using one of the Media Components?

You’re in luck.  The ActionScript works practically the same way.  Drag an instance of MediaDisplay (for example) to the Stage and give it an instance name.  Use the same ActionScript, making sure to match that last line with whatever instance name you choose.  The Media Components don’t pass along properties with their complete event, so omit the evt:Object part between the parentheses.  (That means you should also omit the evt.state and evt.playheadTime references, because they no longer apply.)

What if I’m using no Components at all?

This is where it gets interesting, because the non-Component approach has taken me on a wild goose chase, off and on, since I first wrote the “How to Load” article last April.  The NetStream class, used in the non-Component approach, has no complete event.  The closest we get is an onStatus event that carries with it a code property — similar in principle to the state and playheadTime properties mentioned above — whose message can be “NetStream.Play.Stop” under the right circumstances.  When I first experimented with this property, I wasn’t sure exactly when it might occur.  Sure, it would presumably indicate that a Flash video had ended, but might it not also happen if a video paused because of network traffic?

To make matters worse, I’ve seen notes on other blogs that seem to indicate the “NetStream.Play.Stop” value doesn’t always appear when expected.  In light of that, I’ve been looking for another approach and believe I’ve found it in the NetStream.onMetaData event.  My suggestion still uses the onStatus event, but with assistance from onMetaData.

Another answer, short and sweet

This code sample assumes:

  • no FLVPlayback or Media Component on the Stage; instead, a Video object as described in “How to Load External Video (FLV)
  • an instance name of videoPlayer on this Video object (or use what you like, but update the code to suit)

Enter the following ActionScript into your scripts layer.

var duration:Number = 0;
var nc:NetConnection = new NetConnection();
nc.connect(null);
var ns:NetStream = new NetStream(nc);
videoPlayer.attachVideo(ns);
ns.play("myVideo.flv");

ns.onMetaData = function(evt:Object):Void {
  duration = evt.duration;
};
ns.onStatus = function(evt:Object):Void {
  if (this.time > 0 && this.time >= duration) {
    trace("Video complete")
    delete this.onStatus;
  }
}

How it works

If you’re familiar with non-Component video playing, most of the first several lines should already make sense (if not, read that “How to Load” article).  The only different is an arbitrary new variable, duration, declared and initialized to zero in the first line.  This variable will have its value set by feedback from the NetStream.onMetaData event.

Following the first six lines, a function literal is assigned to the onMetaData event of the ns (NetStream) instance.  This event relies on a smattering of extra information attached to the FLV file itself by the video encoder.  For this example, I used Flash 8 to encode an AVI, but 3rd party encoders exist, and their documentation should tell you if they include a duration property among the data they attach.  The onMetaData event passes along an object, which is collected as evt in the function.  This evt object contains a duration property whose value is passed to the previously declared duration variable.  This value represents the playing length of the video.

Finally, in like fashion, a function literal is assigned to the NetStream.onStatus event of the ns instance.  NetStream does support a time property, which indicates the current position of the video as it plays.  An if statement checks the value of ns’s time property to ensure that it’s a) greater than 0 and b) equal to or greater than our duration variable.  If so, the video must have finished.  In addition, the onStatus function is deleted, otherwise this event may fire more than once.  In your own testing, comment out the delete line to see what I mean.

Some readers have noticed (by using trace()) that the time property never quite reaches duration. If that’s true for you, change the if condition to something like this:

if (this.time > 0 && this.time >= (duration - 0.5))

… where you’re subtracting half a second (or whatever works).

Note:  this approach depends entirely on metadata bring present in your FLV file(s).  Not only metadata, but specifically a duration property.  The Flash video encoder itself supports this property, but your own preferred encoder may not.  To see for sure what metadata (if any) is getting included in your FLVs, change the above onMetaData handler to the following.  You’ll get a list of the whole shebang.

ns.onMetaData = function(evt:Object):Void {
  // duration = evt.duration;
  for (prop:String in evt) {
    trace(prop + ": " + evt[prop]);
  }
};

My test video, encoded in Flash, returned the following output:

canSeekToEnd: true
audiocodecid: 2
audiodelay: 0.038
audiodatarate: 96
videocodecid: 4
framerate: 29.9699859619141
videodatarate: 400
height: 480
width: 720
duration: 71.538

Lots of useful information!

Leave a Reply