Order Now AdSolution Sign Up | Login » Bits on the Run Sign Up | Login »

Forums

/

Streaming to chapter

33 replies [Last post]

I need to make something like a "skip to chapter" section. Under the player will be some links, on click the movie should jump to a given time. I tried something but it's a little buggy. The movie supports streaming, but it seems like that player cannot receive 2 acctions, jump to time and play.. do you have any ideas ?

<script type="text/javascript">

// some variables to save
var currentPosition;
var currentVolume;
var currentItem;

// these functions are caught by the JavascriptView object of the player.
function sendEvent(typ,prm) { thisMovie("mpl").sendEvent(typ,prm); };
function getUpdate(typ,pr1,pr2,pid) {
if(typ == "time") { currentPosition = pr1; }
else if(typ == "volume") { currentVolume = pr1; }
else if(typ == "item") { currentItem = pr1; setTimeout("getItemData(currentItem)",100); }
var id = document.getElementById(typ);
id.innerHTML = typ+ ": "+Math.round(pr1);
pr2 == undefined ? null: id.innerHTML += ", "+Math.round(pr2);
if(pid != "null") {
document.getElementById("pid").innerHTML = "(received from the player with id <i>"+pid+"</i>)";
}
};

// These functions are caught by the feeder object of the player.
function loadFile(obj) { thisMovie("mpl").loadFile(obj); };
function addItem(obj,idx) { thisMovie("mpl").addItem(obj,idx); }
function removeItem(idx) { thisMovie("mpl").removeItem(idx); }
function getItemData(idx) {
var obj = thisMovie("mpl").itemData(idx);
var nodes = "";
for(var i in obj) {
nodes += "<li>"+i+": "+obj[i]+"</li>";
}
document.getElementById("data").innerHTML = nodes;
};

// This is a javascript handler for the player and is always needed.
function thisMovie(movieName) {
    if(navigator.appName.indexOf("Microsoft") != -1) {
return window[movieName];
} else {
return document[movieName];
}
};

function openPlay(x){
if(state == 0){
sendEvent('playitem', 0);
}
else{
s = "sendEvent('scrub', " + x + ")";
setTimeout(s, 500);
}

if(state != 2){
p = "openPlay(" + x + ")";
setTimeout(p, 500);
}
}

</script>

Actions are sent like this:

<a href="javascript:sendEvent('playpause')">Play/Pause</a> | <a href="javascript:sendEvent('stop')">Stop</a> | <a href="javascript:openPlay(50);">Jump to article 2</a>

Something seems wrong in the javascript.

@Sorin,

Have you read this post [url=http://www.jeroenwijering.com/?thread=6080#msg33286]Chapterizer - Town Meeting[/url]?

I have published several systems to chapterize a video file, automatically create a playlist with thumbnails, and stream the chapters.

If the application in the post referenced above does not meet your exact needs, I can modify it, provided you can give me a detailed description of how you want it to work.

i only need a way (probably javascript) to jump to a given (and known) time.

some link below the movie, when clicked, the player jumps to that time.

:s

@Sorin,

Well, you're no fun at all. :d

Here's the code to jump with JavaScript.

  <script type="text/javascript">
    var currentState;

    function sendEvent(typ,prm)
    {
      thisMovie("mpl").sendEvent(typ, prm);
    };

    function getUpdate(typ, pr1, pr2, pid)
    {
      if(pid != "null")
      {
        if((typ == "state") && (pr1 != undefined))
        {
          currentState = pr1;
        }
      }
    };

    // This is a javascript handler for the player and is always needed.
    function thisMovie(movieName)
    {
      if(navigator.appName.indexOf("Microsoft") != -1)
      {
        return window[movieName];
      }
      else
      {
        return document[movieName];
      }
    };

    function openPlay(x)
    {
      if(currentState == 0)
      {
        sendEvent('playitem', 0);
        setTimeout("sendEvent('scrub', " + x + ")", 300);
      }
      else
      {
        sendEvent('scrub', x);
      }
    };
  </script>

200ms seems like the minimum time for the player to get started before you can send a scrub, so I set it to 300 for a little margin of safety.

The reason that your code didn't work is that the value of the 'state' variable is not available outside of the getUpdate() function unless you make a global variable (currentState) and set it's value equal to state.

Here's how the code works:

If the player is stopped, we start it and then 300ms later send the scrub.

If the player is already running, we just send it the scrub.

For anybody who wants the whole package, ready to run, here's a complete HTML doc.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">

  <head>

  <title>Select Chapters with JavaScript</title>

  <script type='text/javascript' src='swfobject.js'></script>

  <script type="text/javascript">
    var currentState;

    function sendEvent(typ,prm)
    {
      thisMovie("mpl").sendEvent(typ, prm);
    };

    function getUpdate(typ, pr1, pr2, pid)
    {
      if(pid != "null")
      {
        if((typ == "state") && (pr1 != undefined))
        {
          currentState = pr1;
        }
      }
    };

    // This is a javascript handler for the player and is always needed.
    function thisMovie(movieName)
    {
      if(navigator.appName.indexOf("Microsoft") != -1)
      {
        return window[movieName];
      }
      else
      {
        return document[movieName];
      }
    };

    function openPlay(x)
    {
      if(currentState == 0)
      {
        sendEvent('playitem', 0);
        setTimeout("sendEvent('scrub', " + x + ")", 300);
      }
      else
      {
        sendEvent('scrub', x);
      }
    };
  </script>

  <script type='text/javascript'>
    function createPlayer()
    {
      var s1 = new SWFObject('mediaplayer.swf', 'mpl', '475', '350', '7');
          s1.addParam('allowfullscreen',     'true');
          s1.addVariable('width',            '475');
          s1.addVariable('height',           '350');
          s1.addVariable('displayheight',    '330');
          s1.addVariable('file',             'video.flv');
        //s1.addVariable('overstretch',      'true');  // expands to fit h & v   "true"  -will stretch them proportionally to fill the display
          s1.addVariable('overstretch',      'false'); // expands to fit h or v  "false" -will stretch them to fit
        //s1.addVariable('overstretch',      'fit');   // expands to fit h & v   "fit"   -will stretch them disproportionally to fit both height and width
        //s1.addVariable('overstretch',      'none');  // displays native size   "none"  -will show all items in their original dimensions
          s1.addVariable('showdigits',       'true');
          s1.addVariable('autostart',        'true');
          s1.addVariable('showicons',        'true');
        //s1.addVariable('logo',             'logo.png');
          s1.addVariable('enablejs',         'true');
          s1.addVariable('javascriptid',     'mpl');
          s1.addVariable('backcolor',        '0xFFFFFF'); // face of buttons
          s1.addVariable('frontcolor',       '0x404040'); // button symbols & playlist text
          s1.addVariable('lightcolor',       '0x808080'); // highlighted playlist item
          s1.write('player');
    };
  </script>

</head>

<body onload="createPlayer();">

  <div id="player">
    <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Plugin</a>
    to see this gallery.
  </div>

  <a href="javascript:sendEvent('playpause');">Play/Pause</a> | <a href="javascript:sendEvent('stop');">Stop</a>
  <br />
  <a href="javascript:openPlay(5);">Jump to article 2</a>
  <br />
  <a href="javascript:openPlay(10);">Jump to article 3</a>
  <br />
  <a href="javascript:openPlay(15);">Jump to article 4</a>
  <br />
  <a href="javascript:openPlay(20);">Jump to article 5</a>

</body>

</html>

ohh.. you're so kind :) i works now

it seems there is a little bug left behind.. if the player is loaded by clicking on a jump to time link.. the 300ms timeout is not enough.. at least when hosted on a server.

@Sorin,

Maybe your original timeout of 500 was about right for the real world of a video coming off of a server. I reduced it slightly because there was a moment when the video played at the beginning before the scrub happened. Experiment a little with it to find a happy medium, maybe 350 or 400???

Buttons:

  <button onclick="javascript:sendEvent('stop');">Stop</button>
  <button onclick="openPlay(30);">2</button>
  <button onclick="openPlay(60);">3</button>
  <button onclick="openPlay(90);">4</button>
  <button onclick="openPlay(120);">5</button>
  <button onclick="openPlay(150);">6</button>
  <button onclick="openPlay(180);">7</button>
  <button onclick="openPlay(210);">SubSection 8</button>
  <button onclick="openPlay(240);">Article 9</button>
  <button onclick="openPlay(270);">Chapter 10</button>

@Sorin,

Try using this code:

  <script type="text/javascript">
    var currentState;
    var currentPosition;
   
    function sendEvent(typ,prm)
    {
      thisMovie("mpl").sendEvent(typ, prm);
    };

    function getUpdate(typ, pr1, pr2, pid)
    {
      if(pid != "null")
      {
        if((typ == "state") && (pr1 != undefined))
        {
          currentState = pr1;
        }
        else if(typ == "time")
        {
          currentPosition = pr1;
        }
      }
    };

    // This is a javascript handler for the player and is always needed.
    function thisMovie(movieName)
    {
      if(navigator.appName.indexOf("Microsoft") != -1)
      {
        return window[movieName];
      }
      else
      {
        return document[movieName];
      }
    };

    function openPlay(x)
    {
      if(currentState == 0)
      {
        sendEvent('playitem', 0);
        setTimeout("sendEvent('scrub', " + x + ")", 500);
      }
      else
      {
        sendEvent('scrub', x);
      }

      if(currentState == 0)
      {
        setTimeout("openPlay(" + x + ");", 250);
      }
      else if((currentState == 2) && (currentPosition < (x - 2)))
      {
        setTimeout("openPlay(" + x + ");", 250);
      }
    };
  </script>

If the player hasn't started yet (it may be seeking in a big video file) it waits 250ms and tries openPlay() again.

If the player has started, but the currentPosition is less than 2 seconds less than where you wanted to start, it waits 250ms then tries openPlay() again. The reason it's 2 seconds less than where you wanted to jump to, is that the player jumps to the nearest keyframe, which may be a bit less than where you wanted to start.

There can be a long delay while the video file is downloaded and the player seeks to the point where you want to start (if you are starting on say, article 9 of a 10 minute video), so you may want to display an image that says something like "Make a selection, then please wait while the video loads...".

The new script doesn't work, the first one did.. but not when you started the video to a specified time. I think this is the part where i got stuck. The same behaviour (yours is a little better, mine got into a continuous loop, play/stop :d)

My video has about 33 minutes, and now when i click the link the player starts, no scrub, if i click after the video starts the player stops, no scrub. If i start the player (play from controls) and then click the link the player stops, no scrub (previous version worked). I think it has something to do with the timeouts.

PS: is there a way we can chat 'bout this.. y!messenger or anything else ? :P

@Sorin,

Here's the correct code. Unfortunately, this just isn't practical to do with JavaScript for the following reasons:

1) you can't scrub to a part of the video that isn't downloaded yet. This isn't a big problem with a short video, but a large video takes some time to download and if the user tries to scrub ahead of the download, the only thing that the code can do, is to keep checking to see if the scrub point is available yet and moving ahead until it is. You could pause the player until the video is downloaded, but what is the user going to do during that time?

2) for a file that has already been downloaded and is in the browser's cache, Internet Explorer has a very quirky behavior that has also been reported by other users. When you try to replay that file, there is a very long pause (20-30 seconds) while IE does "something", before the video will play.

These are the reasons that I developed the Chapterizer method. As soon as the user selects a chapter, the player requests that chunk of the video and it starts to play immediately. The user can jump around as much as he wants and the response is instantaneous. There is no "IE delay".

So, try this latest code, and if it meets your needs, use it. But if not, seriously consider a different method of implementing your selection of "Articles" within a video file. :)

  <script type="text/javascript">
    var currentState;
    var currentPosition;
   
    function sendEvent(typ,prm)
    {
      thisMovie("mpl").sendEvent(typ, prm);
    };

    function getUpdate(typ, pr1, pr2, pid)
    {
      if(pid != "null")
      {
        if((typ == "state") && (pr1 != undefined))
        {
          currentState = pr1;
        }
        else if(typ == "time")
        {
          currentPosition = pr1;
        }
        // var id = document.getElementById(typ);
        // id.innerHTML = typ + ": " + Math.round(pr1);
      }
    };

    // This is a javascript handler for the player and is always needed.
    function thisMovie(movieName)
    {
      if(navigator.appName.indexOf("Microsoft") != -1)
      {
        return window[movieName];
      }
      else
      {
        return document[movieName];
      }
    };

    function openPlay(x)
    {
      if(currentState == 0)
      {
        sendEvent('playitem', 0);
        setTimeout("sendEvent('scrub', " + x + ")", 400);
      }
      else
      {
        sendEvent('scrub', x);
      }

      setTimeout("checkState(" + x + ")", 1000)
     
    };

    function checkState(xx)
    {
      if((currentState == 0) || ((currentState == 2) && (currentPosition < (xx - 10))))
      {
        openPlay(xx);
      }
    };
  </script>

This version works.. but not for the start part. I'll try to solve this thing. I will also reconsider the solution if no luck.

Thank you for your time, you're so kind.
cheers (b)

@Sorin,

but not for the start part

Can you elaborate on that? What doesn't work?

Because if this is adequate for your needs, I would continue developing it until it had a nice "polish" and was usable for some applications. I know the process and the code now, so it's easy to improve it, if you can tell me exactly what doesn't work.

Hi I'm interested in this too but it's not clear to me if this way to do it isn't taking advantage of the php streaming possibilities?
We're working on external controls too, but it seems a bit buggy at times.

@Nick,

This JavaScript method is simple to implement and works great on short videos. The problem appears on larger files where you have to wait for the file to download before you can scrub into the later parts of the video.

If your files are large, I would look at the chapterizing method where the user is given a playlist of selections. When the user makes a selection, the appropriate chunk of the file is downloaded, using the metadata to start on a keyframe. The playback starts immediately and only the requested chunk of the file needs to be downloaded. The PHP scripts to implement this are already finished and tested. As soon as Harry returns from his vacation we will finish up his implementation for chapterizing his videos of Town Meetings based upon the meeting agenda. Depending on your needs, you could use that set of scripts or alter them to suit. (The PHP streaming isn't really streaming, it's just downloading the requested portion of the file. In some ways, it's MUCH better than RTMP streaming.)

Hi Will,

thank you for your advice :)
We're setting up a system that extracts a frame every x seconds from
every movie and then we pair the frames with the time. Now we need a safe way to load the stream.php with the right timecode based on the thumb:time pair - I was told on this board that the only way was to tell the FLV injector to create the index .xml's:

- <keyframes>
- <filepositions>
<pos>13384</pos>
<pos>35773</pos>
<pos>67362</pos>
<pos>96622</pos>
....
and

- <times>
<time>0</time>
<time>2</time>
<time>3,999</time>
<time>6</time>
<time>8</time>

but the content looks a bit strange.
Is this still the best/only way to pull this off?

thank again :)

Nick

@Nick,

Let's take this from the beginning.

1) encode the video at 20 frames per second—that's the framerate of the JW Players. Put a keyframe every 20 frames or more often if you want to scrub to less than 1 second intervals. The keyframe interval must be an even divisor of 20 (2, 4, 5, 10). Too many keyframes make the video file huge.

2) create and inject the metadata with FLVMDI (or equivalent) and also create the metadata XML file. Scrubbing is to keyframes in bytes, so in your example above, to scrub to 2 seconds, you would seek into the file 35733 bytes.

3) use ffmpeg (or equivalent) to output the thumbnail images (every 2 seconds in your example).

4) pair those thumbnail images with the position in bytes to create a starting point for a chunk of the file. If you want only the next 2 seconds, then you also need to create a duration by subtracting the starting position from the next keyframe position. Request this chunk from the PHP streamer. You need one that can accept a request with duration, see below.

This whole process has been fully developed and tested here: [url=http://www.jeroenwijering.com/?thread=6080#msg33286]Chapterizer - Town Meeting[/url]

Even if you can't use that exact code, it's a good starting point and the complete process is fully explained.

Post back if you need to know more... :)

Hi again Will,

Again thank you #)

We're goign to test your code, I just have a few extra questions:

You wrote:
1) encode the video at 20 frames per second—that's the framerate of the JW Players. Put a keyframe every 20 frames or more often if you want to scrub to less than 1 second intervals. The keyframe interval must be an even divisor of 20 (2, 4, 5, 10). Too many keyframes make the video file huge.

Our content info:

high quality = 30FPS and keyframe every 1 sec
low quality = 15FPS and keyframe every 2 sec

We did this after reading a tutorial somewhere and it would be a big pain to re-encode to 20FPS so what do you suggest in regards to the problems you see from this?
Do you for example suggest we change the FPS info in the player and create 2 seperate players for high and low with 30/15FPS?

Thanks again
Nick

@Nick,

Do you for example suggest we change the FPS info in the player and create 2 seperate players for high and low with 30/15FPS?

To make this decision, you would have to modify the JW Player (to play 30fps) and compare that to the standard player with the standard player playing both the 15fps and 30fps.

Before you modify the standard JW Player, I would watch the video at both framerates and see if there is really much difference between them. The standard player may give you the high/low quality viewing experience even with dropping 1/3 of the frames at 30fps.

We did this after reading a tutorial somewhere and it would be a big pain to re-encode to 20FPS so what do you suggest in regards to the problems you see from this?

It might be an even bigger pain to modify the player for 15/30fps and to have two players to maintain. I don't know what is involved, but I would guess that it's not a trivial modification.

Hi Will,

If i'm correct it's very simple to change the frame rate (in the flash environment properties) and export the new player again? Maybe it's not as simple as that.
I just thought that if the player is set to 20FPS it wouldn't be able to play 30FPS content but maybe it doesn't make much difference.

Also the code example you showed me it seems like it's based on chapters like 1 movie 10 chapters with descriptions, but what we need is to divide a 20min movie into 1 chapter(keyframe) pr second will this still work the way you mean?

20 * 60 = 1200 Chapters

That's quite a large playlist. How do you plan to have the user make a selection?

The player can play at lower and higher framerates. I don't know the limits. I've seen 59.95 work OK.

The PHP scripts will make the 1200 chapters and a playlist if that's what you really want. The 1200 chapter playlist may be slow to load and scroll; or maybe you have some other way of making the selection.

Can you please explain a little more (actually much more!) exactly how your application works. :)

Hi Will,

As i wrote earlier in the thread, we're taking screendumps for every second of the movie (based on the time codes from the xml) and then we resize them to make a list of small thumbs that show the content of the entire movie so the user can click on the thumb (part of the movie he wants) We're not using this as a "playlist" we just need a way to jump to 12min and 22sec when that thumb is clicked with no delays in internet explorer and so on.

@Nick,

So you have a matrix of 1200 thumbnails, each linked to a keyframe by byte position. Definitely a job for a server-side script to parse the metadata file, generate the thumbnails, generate the HTML with the link information (including duration?), then "stream.php" will deliver the video on request.

When the user requests a video file starting at some point, do they continue watching from that point to the end, or do you only serve a small portion of the video?

Now that I understand the details of your application, do you need any help with implementing it?

Will (26.08.2007):

@Sorin,

but not for the start part

Can you elaborate on that? What doesn't work?

Because if this is adequate for your needs, I would continue developing it until it had a nice "polish" and was usable for some applications. I know the process and the code now, so it's easy to improve it, if you can tell me exactly what doesn't work.

Sorry for my late answer, i was a little busy these days. The script seems to work fine, just the start part: it never starts by going to given time. It's always necesary to click on the start button and than the chapter link or click twice on the chapter link. It's rather important to start from there, do you have any ideea ?

@Sorin,

What is the source of your video files?

I have found that YouTube doesn't reliably send the metadata.

If the videos are coming off of your own server maybe you won't have a problem.

I have also found that the player won't scrub ahead of the download point. It goes to the new position and reports that it is at that position, but when it starts playing, it is at the download point. I can't scrub ahead of the download point because when I check the position, the player reports that it's at the new position.

Anyway, this version starts after a scrub, so try it. It may meet your needs. :)

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html lang="en">

  <head>

  <title>Sorin - Select Chapters with JavaScript</title>

  <script type='text/javascript' src='swfobject.js'></script>

  <script type="text/javascript">
    var currentPosition;
    var currentVolume;
    var currentItem;
    var currentState;

    // these functions are caught by the JavascriptView object of the player.
    function sendEvent(typ,prm)
    {
      thisMovie("mpl").sendEvent(typ, prm);
    };

    function getUpdate(typ, pr1, pr2, pid)
    {
      if(pid != "null")
      {
        if((typ == "state") && (pr1 != undefined))
        {
          currentState = pr1;
        }
        else if(typ == "time")
        {
          currentPosition = pr1;
        }
        else if(typ == "volume")
        {
          currentVolume = pr1;
        }
        else if(typ == "item")
        {
          currentItem = pr1;
          setTimeout("getItemData(currentItem)", 100);
        }
        var id = document.getElementById(typ);
        id.innerHTML = typ + ": " + Math.round(pr1);
        pr2 == undefined ? null: id.innerHTML += ", " + Math.round(pr2);

        document.getElementById("pid").innerHTML = "(received from the player with id <i>" + pid + "</i>)";
        cP = document.getElementById("position");
        cP.innerHTML = "Current Position: " + currentPosition;
      }
    };

    // These functions are caught by the feeder object of the player.
    function loadFile(obj)
    {
      thisMovie("mpl").loadFile(obj);
    };

    function addItem(obj, idx)
    {
      thisMovie("mpl").addItem(obj, idx);
    };

    function removeItem(idx)
    {
      thisMovie("mpl").removeItem(idx);
    };

    function getItemData(idx)
    {
      var obj = thisMovie("mpl").itemData(idx);
      e = document.getElementById("title");
      f = document.getElementById("author");
      g = document.getElementById("link");
      e.innerHTML = obj['title'];
      f.innerHTML = obj['author'];
      g.innerHTML = obj['link'];

      if(obj['link'] != "")
      {
        var theURL = '<a target="_blank" title="Go to band site" href="' + obj['link'] + '">' + obj['author'] + '</a>';
        artist.innerHTML = theURL;
      }
      else
      {
        artist.innerHTML = obj['author'];
      }
    }

    // This is a javascript handler for the player and is always needed.
    function thisMovie(movieName)
    {
      if(navigator.appName.indexOf("Microsoft") != -1)
      {
        return window[movieName];
      }
      else
      {
        return document[movieName];
      }
    };

    function openPlay(x, scrubbing)
    {
      if((currentState == 0) && (!scrubbing))
      {
        sendEvent('playpause');
        sendEvent('playpause');
        setTimeout("sendEvent('scrub', " + x + ")", 25);
      }
      else if(currentState == 2)
      {
        sendEvent('scrub', x);
      }

      if((currentState == 0) || (currentState == 1))
      {
        if(((currentPosition > (x - 2)) && (currentPosition < (x + 2))) && (scrubbing)) // found desired position
        {
          scrubbing = false;
          sendEvent('playpause');
        }
        else if(((currentPosition < (x - 2)) || (currentPosition > (x + 2))) && (scrubbing)) // keep scrubbing until the scrub point is downloaded
        {
          sendEvent('scrub', x);
          scrubbing = true;
          setTimeout("openPlay(" + x + ", " + scrubbing + ")", 500);
        }
        else // one time to get the loop started, since we're scrubbing, come back in 1 second to check on the position, then we fall into one of the two positions above: play or scrub some more
        {
          scrubbing = true;
          setTimeout("openPlay(" + x + ", " + scrubbing + ")", 500);
        }
      }

      dP = document.getElementById("desiredposition");
      dP.innerHTML = "Desired Position: " + x;
      s = document.getElementById("scrubbing");
      s.innerHTML = "Scrubbing: " + scrubbing;
    };

    function createPlayer()
    {
      var s1 = new SWFObject('mediaplayer.swf', 'mpl', '375', '434', '7');
          s1.addParam('allowfullscreen',     'true');
          s1.addVariable('width',            '375');
          s1.addVariable('height',           '434');
          s1.addVariable('displayheight',    '250');

        //s1.addVariable('file',             'video.flv');
        //s1.addVariable('title',            'Video Test File');
        //s1.addVariable('author',           'Jeroen Wijering');
        //s1.addVariable('link',             'http://www.willswonders.myip.org');

          s1.addVariable('file',             'playlist.xml');

        //s1.addVariable('overstretch',      'true');  // expands to fit h & v   "true"  -will stretch them proportionally to fill the display
          s1.addVariable('overstretch',      'false'); // expands to fit h or v  "false" -will stretch them to fit
        //s1.addVariable('overstretch',      'fit');   // expands to fit h & v   "fit"   -will stretch them disproportionally to fit both height and width
        //s1.addVariable('overstretch',      'none');  // displays native size   "none"  -will show all items in their original dimensions
          s1.addVariable('showdigits',       'true');
          s1.addVariable('autostart',        'false');
          s1.addVariable('shuffle',          'false');
          s1.addVariable('repeat',           'list');
          s1.addVariable('showicons',        'true');
        //s1.addVariable('thumbsinplaylist', 'true');
        //s1.addVariable('logo',             'logo.png');
          s1.addVariable('enablejs',         'true');
          s1.addVariable('javascriptid',     'mpl');
          s1.addVariable('backcolor',        '0xFFFFFF'); // face of buttons
          s1.addVariable('frontcolor',       '0x404040'); // button symbols & playlist text
          s1.addVariable('lightcolor',       '0x808080'); // highlighted playlist item
          s1.write('player');
    };
  </script>

</head>

<body onload="createPlayer();">

  <table>
    <tr>
      <td>
        <div id="player">
          <a href="http://www.macromedia.com/go/getflashplayer">Get the Flash Plugin</a>
          to see this gallery.
        </div>
      </td>
      <td>
        <div id="title">
          
        </div>
        <div id="author">
          
        </div>
        <div id="link">
          
        </div>
        <div id="artist">
          
        </div>

        <a href="javascript:sendEvent('playpause');">Play/Pause</a> | <a href="javascript:sendEvent('stop');">Stop</a> | <a href="javascript:sendEvent('playitem', currentItem);">playitem, currentItem</a>
        <br />
        Scrub:
        <br />
        <a href="javascript:sendEvent('scrub',   1);">1</a>
        <a href="javascript:sendEvent('scrub',   5);">5</a>
        <a href="javascript:sendEvent('scrub',  10);">10</a>
        <a href="javascript:sendEvent('scrub',  15);">15</a>
        <a href="javascript:sendEvent('scrub',  20);">20</a>
        <a href="javascript:sendEvent('scrub',  25);">25</a>
        <a href="javascript:sendEvent('scrub',  30);">30</a>
        <a href="javascript:sendEvent('scrub',  35);">35</a>
        <a href="javascript:sendEvent('scrub',  40);">40</a>
        <a href="javascript:sendEvent('scrub',  45);">45</a>
        <a href="javascript:sendEvent('scrub',  50);">50</a>
        <a href="javascript:sendEvent('scrub',  55);">55</a>
        <a href="javascript:sendEvent('scrub',  60);">60</a>
        <a href="javascript:sendEvent('scrub',  65);">65</a>
        <a href="javascript:sendEvent('scrub',  70);">70</a>
        <a href="javascript:sendEvent('scrub',  75);">75</a>
        <a href="javascript:sendEvent('scrub',  80);">80</a>
        <a href="javascript:sendEvent('scrub',  85);">85</a>
        <a href="javascript:sendEvent('scrub',  90);">90</a>
        <a href="javascript:sendEvent('scrub',  95);">95</a>
        <a href="javascript:sendEvent('scrub', 100);">100</a>
        <a href="javascript:sendEvent('scrub', 105);">105</a>
        <a href="javascript:sendEvent('scrub', 110);">110</a>
        <a href="javascript:sendEvent('scrub', 115);">115</a>
        <a href="javascript:sendEvent('scrub', 120);">120</a>
        <a href="javascript:sendEvent('scrub', 125);">125</a>
        <a href="javascript:sendEvent('scrub', 130);">130</a>
        <a href="javascript:sendEvent('scrub', 135);">135</a>
        <a href="javascript:sendEvent('scrub', 140);">140</a>
        <a href="javascript:sendEvent('scrub', 145);">145</a>
        <a href="javascript:sendEvent('scrub', 150);">150</a>
        <a href="javascript:sendEvent('scrub', 155);">155</a>
        <a href="javascript:sendEvent('scrub', 160);">160</a>
        <a href="javascript:sendEvent('scrub', 165);">165</a>
        <a href="javascript:sendEvent('scrub', 170);">170</a>
        <a href="javascript:sendEvent('scrub', 175);">175</a>
        <a href="javascript:sendEvent('scrub', 180);">180</a>
        <a href="javascript:sendEvent('scrub', 185);">185</a>
        <a href="javascript:sendEvent('scrub', 190);">190</a>
        <a href="javascript:sendEvent('scrub', 195);">195</a>
        <a href="javascript:sendEvent('scrub', 200);">200</a>
        <a href="javascript:sendEvent('scrub', 205);">205</a>
        <a href="javascript:sendEvent('scrub', 210);">210</a>
        <a href="javascript:sendEvent('scrub', 215);">215</a>
        <a href="javascript:sendEvent('scrub', 220);">220</a>
        <a href="javascript:sendEvent('scrub', 225);">225</a>
        <a href="javascript:sendEvent('scrub', 230);">230</a>
        <a href="javascript:sendEvent('scrub', 235);">235</a>
        <a href="javascript:sendEvent('scrub', 240);">240</a>
        <a href="javascript:sendEvent('scrub', 245);">245</a>
        <a href="javascript:sendEvent('scrub', 250);">250</a>
        <a href="javascript:sendEvent('scrub', 255);">255</a>
        <a href="javascript:sendEvent('scrub', 260);">260</a>
        <a href="javascript:sendEvent('scrub', 265);">265</a>
        <a href="javascript:sendEvent('scrub', 270);">270</a>
        <a href="javascript:sendEvent('scrub', 275);">275</a>
        <a href="javascript:sendEvent('scrub', 280);">280</a>
        <a href="javascript:sendEvent('scrub', 285);">285</a>
        <a href="javascript:sendEvent('scrub', 290);">290</a>
        <a href="javascript:sendEvent('scrub', 295);">295</a>
        <a href="javascript:sendEvent('scrub', 300);">300</a>
        <br />
        Open:
        <br />
        <a href="javascript:openPlay( 0,  false);">1</a>
        <a href="javascript:openPlay( 5,  false);">5</a>
        <a href="javascript:openPlay(10,  false);">10</a>
        <a href="javascript:openPlay(15,  false);">15</a>
        <a href="javascript:openPlay(20,  false);">20</a>
        <a href="javascript:openPlay(25,  false);">25</a>
        <a href="javascript:openPlay(30,  false);">30</a>
        <a href="javascript:openPlay(35,  false);">35</a>
        <a href="javascript:openPlay(40,  false);">40</a>
        <a href="javascript:openPlay(45,  false);">45</a>
        <a href="javascript:openPlay(50,  false);">50</a>
        <a href="javascript:openPlay(55,  false);">55</a>
        <a href="javascript:openPlay(60,  false);">60</a>
        <a href="javascript:openPlay(65,  false);">65</a>
        <a href="javascript:openPlay(70,  false);">70</a>
        <a href="javascript:openPlay(75,  false);">75</a>
        <a href="javascript:openPlay(80,  false);">80</a>
        <a href="javascript:openPlay(85,  false);">85</a>
        <a href="javascript:openPlay(90,  false);">90</a>
        <a href="javascript:openPlay(95,  false);">95</a>
        <a href="javascript:openPlay(100, false);">100</a>
        <a href="javascript:openPlay(105, false);">105</a>
        <a href="javascript:openPlay(110, false);">110</a>
        <a href="javascript:openPlay(115, false);">115</a>
        <a href="javascript:openPlay(120, false);">120</a>
        <a href="javascript:openPlay(125, false);">125</a>
        <a href="javascript:openPlay(130, false);">130</a>
        <a href="javascript:openPlay(135, false);">135</a>
        <a href="javascript:openPlay(140, false);">140</a>
        <a href="javascript:openPlay(145, false);">145</a>
        <a href="javascript:openPlay(150, false);">150</a>
        <a href="javascript:openPlay(155, false);">155</a>
        <a href="javascript:openPlay(160, false);">160</a>
        <a href="javascript:openPlay(165, false);">165</a>
        <a href="javascript:openPlay(170, false);">170</a>
        <a href="javascript:openPlay(175, false);">175</a>
        <a href="javascript:openPlay(180, false);">180</a>
        <a href="javascript:openPlay(185, false);">185</a>
        <a href="javascript:openPlay(190, false);">190</a>
        <a href="javascript:openPlay(195, false);">195</a>
        <a href="javascript:openPlay(200, false);">200</a>
        <a href="javascript:openPlay(205, false);">205</a>
        <a href="javascript:openPlay(210, false);">210</a>
        <a href="javascript:openPlay(215, false);">215</a>
        <a href="javascript:openPlay(220, false);">220</a>
        <a href="javascript:openPlay(225, false);">225</a>
        <a href="javascript:openPlay(230, false);">230</a>
        <a href="javascript:openPlay(235, false);">235</a>
        <a href="javascript:openPlay(240, false);">240</a>
        <a href="javascript:openPlay(245, false);">245</a>
        <a href="javascript:openPlay(250, false);">250</a>
        <a href="javascript:openPlay(255, false);">255</a>
        <a href="javascript:openPlay(260, false);">260</a>
        <a href="javascript:openPlay(265, false);">265</a>
        <a href="javascript:openPlay(270, false);">270</a>
        <a href="javascript:openPlay(275, false);">275</a>
        <a href="javascript:openPlay(280, false);">280</a>
        <a href="javascript:openPlay(285, false);">285</a>
        <a href="javascript:openPlay(290, false);">290</a>
        <a href="javascript:openPlay(295, false);">295</a>
        <a href="javascript:openPlay(300, false);">300</a>

        <div id="data">
          
        </div>
        <div id="pid">
          
        </div>
        <div id="item">
          
        </div>
        <div id="volume">
          
        </div>
        <div id="state">
          
        </div>
        <div id="time">
          
        </div>
        <div id="load">
          
        </div>
        <div id="size">
          
        </div>
        <div id="position">
          
        </div>
        <div id="desiredposition">
          
        </div>
        <div id="scrubbing">
          
        </div>
      </td>
    </tr>
  </table>

</body>

</html>

Will,
thank you for your help, I think we can get it up and running, I'll let you know if we encounter any other problems!

Thanks
Again
Nick :)

thanx for your time.. i will check this new version

Hi

This discussion and the Chapterize Town Meeting thread look like they bear directly on what I'm looking for, but I wonder if you could break it down more for a newbie. I've been working with FLV for only a few months. I understand the basic idea of having keyframes you can jump to, but I can't quite follow this thread.

Let me be more specific. What I want to do is post speeches with associated transcripts, and at various points in the transcripts (maybe 3-12 times per transcript) have links that basically say "click here to begin playing video from this point." Similar to the idea of having thumbnails to click on to jump to a point and start playing. I still can't quite follow *how* to create the metadata and how to configure the JW Player to utilize it, though. For example, this discussion thread starts right in with blocks of sample code, but I don't know where they're coming from -- what types of files this code would be put into.

To take another example, in the Chapterize Town Meeting discussion, the tool FLVMDI.exe plays a large role. But I don't understand how it adds the metadata or what it's doing. One of the main people who's working on our project is working on a Mac, and he would like to figure out a way to do something like the Chapterize Town Meeting procedure, but FLVMDI.exe is only for Windows. If we understood more clearly what kind of data files we're supposed to be creating with it and how this works, we might be able to do something parallel on a Mac. Or maybe we couldn't. In any case, I wonder if you could explain what these steps are a bit more clearly for the newbies.

Thanks for any help you can give.

Karen

@Karen,

FLVMDI is a tool to read through the FLV file and find the key frames. It then publishes an XML file containing some data about the FLV such as the size and duration and an array of the keyframe starting byte position and time.

FLVMDI can also "inject" the metadata into the FLV file so the metadata is available to a Flash player such as the JW Player. The equivalent tool for *nix and OS X is flvtool2 [url=http://www.inlet-media.de/]inlet media[/url].

You then associate the time that you want to start watching the video with the byte position and request a chunk of the FLV file starting at that position (with the appropriate headers). You can continue to the end of the video or request only a chunk of the FLV file, where the chunk size in bytes corresponds to the amount of time that you want to watch.

You then make a request to a specialized stream script for a FLV file beginning at a byte position and continuing for as many bytes (corresponding to time) as you want to watch. This is a chapter.

The Chapterizer scripts only automate this process.

In all cases, someone has to make a list of Topic or Chapter and the beginning (and ending, if desired) time.

This list is then used manually or in the automated process to generate a playlist or links to request the appropriate chunk of the file.

In your case, with a transcript, you would locate the appropriate time in the video that matches the transcript and create a link to request the corresponding chunk of the video file.

Example:

Let's say that we're talking about trees and the transcript is describing a Fir tree. The video file shows and describes Fir trees starting at 25 seconds and continues for 15 seconds.

So you would look through your metadata file and see that 25 (23.733s) seconds corresponds to a byte position of 391508 bytes and that 40 (43.733) seconds corresponds to a byte position of 1038507 bytes.

Portion of metadata file:
...
<pos>292023</pos>
<pos>391508</pos>
<pos>510374</pos>
<pos>640531</pos>
<pos>781223</pos>
<pos>910140</pos>
<pos>1038507</pos>
<pos>1149309</pos>
...
<time>19.733</time>
<time>23.733</time>
<time>27.733</time>
<time>31.733</time>
<time>35.733</time>
<time>39.733</time>
<time>43.733</time>
<time>47.733</time>
...

So your HTML doc would look like this:

<head>

...lots of code for the JW Player and the JavaScript API...

</head>

<body>

<div id="player">player placeholder</div>

<!-- Your transcript -->
Blah, blah, blah...
<p>
Fir trees commonly grow in colder climates.
<a href="javascript:loadFile('mpl', {file:'http://my.domain.com/path-to-file/stream_chapters.php?file=videofilename.flv&pos=391508&dur=647000'})">Click here to see a video of Fir trees</a>
Blah, blah, blah...
</p>

</body>

Clicking on the link will then serve 20 seconds of video to the player. Adding more keyframes to the video will give you the ability to request more exact starting and ending times.

You will have to place these links in the transcript manually unless you have some method of identifying the correct place in the transcript to insert the link, such as an id number.

Then you could have a list something like this:

ID,TITLE,STARTING TIME
001,Introduction,0
002,Types of Trees,10,
003,Palm Trees,15
004,Fir Trees,25
005,Pine Trees,40
...

A PHP script could then look through the transcript, when it finds an ID number, it could look up the TITLE and the corresponding STARTING TIME, look up the corresponding byte position, calculate the duration in bytes, generate and insert the link into the transcript.

Will,

Thanks for getting back to me so quickly, and explaining so much. I understand more than I did, though still not quite everything. I'm going to try working with this and see if I need to ask more questions once I've had a bit more experience. Thanks again.

Karen

Hi,

We are already converting videos into flv looking to stream live so we can play video from any part same as google videos beside this we don't want our videos to be cached..

Any help???

Install [url=http://osflash.org/red5]Red5[/url]. :)

already did.... but no real success as didn't find any such player .... i one i got having connection button and a list of videos..

I want a player with standard features+ability to seek different portions of video directly same as google videos..

Any suggestions?

Thanks

@NICK,

already did.... but no real success as didn't find any such player .... i one i got having connection button and a list of videos..

I don't understand this. The Red6 server only serves your videos.

You need to implement the JW Player and connect to the Red5 stream. The JW Player has full seek and playlist features.

Anybody has a code for php?