wiki:FlashPlayer

Implementing Pseudo Streaming for H264 in a Flash Player

back

Implementing Pseudo Streaming for MP4 is similar to FLV, with a few exceptions.

MetaData

Information about the available seekpoints is stored in the metadata in the fields seekpoints[keyframe][“time“] and seekpoints[keyframe][“offset”].

The first time a video is played you store the metadata. When you seek in an MP4 file, the plugin basically sends you a new MP4 file with patched headers. Remember that seeking times are always relative to the original file.

Seeking

You can specify a starting time by adding a query to the URL in the form "?start=" + metadata_.seekpoints[keyframe][“time“]; Calling the play function on the NetStream object starts playback of the video at the specified time.

The Flash Player resets the NetStream.time to zero. When you have a timeline you will see that it jumps back to the beginning. To compensate you have to adjust the NetStream.time by adding the time of your latest seekpoint. See the example code below.

Falling back to FLV

The Flash Player supports playback of H264 since majorVersion 9, revision 60. If you have both an MP4 and FLV version available of the video, it is recommended to check if the player supports H264 playback, and if not then fall back to playing FLV.


Code Snippets

Below some codesnippets on how to implement this.

compensate

How to compensate the timeline:

 var net_stream:NetStream = object.net_stream_;
 var time_fixed:Number = net_stream.time;
 time_fixed += video_streamer.flv_beginning_;

 var percentage:Number = 100 * (time_fixed / net_stream.totalTime);

seek

An example seek funcion. Note that you can call seek on the NetStream object when the video is already buffered for that seekpoint. (The funtions get_nearest_keyframe and get_nearest_seekpoint are defined below too).

  public function seek(second:Number)
  {
    trace("seek: " + second);

    var cached_seconds = Math.floor(
      (net_stream_.totalTime - flv_beginning_) *
      (net_stream_.bytesLoaded / net_stream_.bytesTotal)) - 1;

    if(second >= flv_beginning_ && second < flv_beginning_ + cached_seconds)
    {
      if(Math.abs(net_stream_.time - second) > 5)
      {
        net_stream_.seek(second);
      }
    }
    else
    if(metadata_.keyframes)
    {
      var keyframe = get_nearest_keyframe(second, metadata_.keyframes.times);
      trace("seek: nearest keyframe=" + keyframe);

      second = metadata_.keyframes.times[keyframe];
      if(flv_beginning_ != second)
      {
        flv_beginning_ = second;

        var url = net_connection_.play_url +
                  "?start=" + metadata_.keyframes.filepositions[keyframe];
        trace("net_stream_.play(" + url + ")");
        net_stream_.play(url);
      }
    }
    else
    if(metadata_.seekpoints)
    {
      var keyframe = get_nearest_seekpoint(second, metadata_.seekpoints);
      trace("seek: nearest keyframe=" + keyframe);

      second = metadata_.seekpoints[keyframe]["time"];
      if(flv_beginning_ != second)
      {
        flv_beginning_ = second;

        var url = net_connection_.play_url +
                  "?start=" + metadata_.seekpoints[keyframe]["time"];
        trace("net_stream_.play(" + url + ")");
        net_stream_.play(url);
      }
    }
  }

get_nearest_keyframe

  private function get_nearest_keyframe(second:Number, keytimes)
  {
    var index1 = 0;
    var index2 = 0;
    // Iterate through array to find keyframes before and after scrubber second
    for(var i = 0; i != keytimes.length; i++)
    {
      if(keytimes[i] < second)
      {
        index1 = i;
      }
      else
      {
        index2 = i;
        break;
      }
    }
    // Calculate nearest keyframe
    if(second - keytimes[index1] < keytimes[index2] - second)
    {
      return index1;
    }
    else
    {
      return index2;
    }
  }

get_nearest_seekpoint

  private function get_nearest_seekpoint(second:Number, seekpoints)
  {
    var index1 = 0;
    var index2 = 0;
    // Iterate through array to find keyframes before and after scrubber second
    for(var i = 0; i != seekpoints.length; i++)
    {
      if(seekpoints[i]["time"] < second)
      {
        index1 = i;
      }
      else
      {
        index2 = i;
        break;
      }
    }
    // Calculate nearest keyframe
    if(second - seekpoints[index1]["time"] < seekpoints[index2]["time"] - second)
    {
      return index1;
    }
    else
    {
      return index2;
    }
  }

Questions

If you have any questions etc, do not hesitate to ask them the on the blog

Last modified 6 years ago Last modified on 10/29/07 21:43:12