The authors of our learning modules often want to embed one one or more videos embedded in a page. A typical request is to be able to “edit” the video using a duration set in the playist, so that the player shows only a few seconds of the video, focusing on only the relevant material for the current page.

Stop the press! Update! The new beta 4.3 player appears to have this functionality built in, without need for any javascript. I still need to test it with different combinations of multi-item/single item playlists, but so far it looks good. However there are some issues with the display which need to be resolved.

The flash video player that we use (JW Player) does not have a built-in time-limiting function, so you need to create it using the player’s Javascript API. There IS a duration attribute, but it simply specifies the length of time the associated chapter button will be highlighted if the chapter navigation is being displayed. In other words, you can break a long video into clickable chapters, but it will still play the entire video, not just one clip.

"KLink" provided some code that is the basis of this solution, but it needed to be adapted for the multiple player configuration shown below.

page3 of mrsafety.jpg

 

The two-player version can be downloaded here:

Download 2clipplayers.zip

kLink’s idea involved using meta tags “start” and “dur” to indicate the start point and duration for each clip, as described in this post:

Stop Points: Defining playback duration. The modifications mostly involved adding separate functions for the two players so that they do not get confused with each other’s settings. I ended up hard coding the individual functions "timeMonitor1", timeMonitor2", and variables "currentStart1", currentStart2, etc., instead of using dynamically generated variable names, because it was not working and I began to suspect some sort of timing issue. If I can get that to work, I’ll replace this code with the more generally useful version.

Other piecs of this solution were drawn from Jeroen Wigering’s example: Simultaneous players.

It requires the JW 4.2.9 FLV player and SWFObject 2.1. Both are included in the zip file since both items undergo substantial updates from time to time, and I can’t guarantee that this will not break when used with newer versions.

Unfortunately, I don’t have an externally facing streaming server to demonstrate this in a working example, but it will look like the image below once you edit the playlists and streamer variables to point to your own streaming video files. The two places you absolutely must edit to get this working are:

  1. in page03minimalexample.htm, lines 47 and 48, you must replace my streamer address with the path to the folder on the streaming server containing your FLV files, and the FLV name must go into the “Location” tag in the playlist.

     function init() {
                  createPlayer("placeholder1", "player1", "rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/safety/MRI_SafetyNonPro", "media/playlist3.xml", false );
                  createPlayer("placeholder2", "player2", "rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/safety/MRI_SafetyNonPro", "media/playlist3b.xml", false);
          
            } 
    
    
    in the playlists, you must replace my "location" "01_Presentation" and "02_Presentation" with your FLV's filename.
    <?xml version="1.0" encoding="utf-8"?>
    <playlist version="1" xmlns="http://xspf.org/ns/0/">
    <trackList>
    <track>
    <title>Magnetic Resonance Safety</title>
    <location>02_Presentation.flv</location>
      

    You will probably need to adjust the clip durations also.

minimalexample.jpg

Folder structure contained in the zip file:

folderstructure.jpg

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml"> <head> <TITLE>2 clip players </TITLE> <link href="css/allBrowsers.css" rel="stylesheet" type="text/css" /> <link href="css/header.css" rel="stylesheet" type="text/css" />

<script type="text/javascript" src="js/swfobject/swfobject.js"></script> <script type="text/javascript"> //http://www.jeroenwijering.com/?thread=11566#msg73578 //icons: false gets rid of buffering "clock" /*mutually exclusive players - another option for setting up 2 players http://home5.inet.tele.dk/nyboe/flash/mediaplayer4/JW_API_xmpl_1-1-3-0.html */

</script> <script type="text/javascript"> function createPlayer(thePlaceholder, thePlayerId, theStreamer, theFile, theAutostart) {

var flashvars = { streamer:theStreamer, file:theFile, autostart:theAutostart, repeat:'none', shuffle:'false', volume:'50', icons:'false' } var params = { allowfullscreen:"true", allowscriptaccess:"always" } var attributes = { id:thePlayerId, name:thePlayerId } swfobject.embedSWF("includes/jw_media_player4/player.swf", thePlaceholder, "320", "260", "9.0.115", false, flashvars, params, attributes); //alert('thePlaceholder'+thePlaceholder+'n thePlayerId= '+thePlayerId+'n theStreamer= '+theStreamer+ 'ntheFile= '+ theFile + 'ntheAutostart= '+theAutostart); }//end function createPlayer

function init() { createPlayer("placeholder1", "player1", "rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/safety/MRI_SafetyNonPro", "media/playlist3.xml", false ); createPlayer("placeholder2", "player2", "rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/safety/MRI_SafetyNonPro", "media/playlist3b.xml", false); } </script> <script type="text/javascript" src="js/videoClip2players.js"></script> </head> <body onload="init();"> <div id="rightColumn"> <div id="content">

<!--*********************put content below this line!!!!************--> <table width="100%" border="0" id="contenTable"> <tr> <td valign="top"><h1>Title </h1> <p>2 players that play a specific piece out of a long video, honoring the duration value you set. </p> <p><br /> </p> <p> <table width="670" border="0"> <tr> <td width="328" valign="top"> Watch a video clip depicting the fringe field. (16 sec.)</td> <td width="332" valign="top">How strong is the magnet? Watch this video clip to learn more. (35 sec.)</td> </tr> <tr> <td> <div id="wrapper1"> <div id="placeholder1"></div> </div>

</td> <td> <div id="wrapper2"> <div id="placeholder2"></div> </div> </td> </tr> </table> <div id="generalMsg" style="float:left;"></div> <div id="timeMonitor" style="float:left;padding-left:6px;"></div> <div id="itemMonitor" style="float:left;padding-left:6px;"></div> <hr style="clear:both;" /> <div id="currentDur1"></div> <div id="currentDur2"></div> <div id="currentStart1"></div> <div id="currentStart2"></div> <div id="currentItem1"></div> <div id="currentItem2"></div> <div id="currentPosition1"></div> <div id="currentPosition2 "></div> <div id="stopPosition1"></div> <div id="stopPosition2"></div> <div id="dump0"></div> <div id="dump1"> </div> <div id="dump2"> </div> <div id="dump3"> </div> <div id="dump4"> </div> <div id="dump5"> </div> </td> </tr> <tr> <td valign="top">&nbsp;</td> </tr> </table> <!--*********put content above this line!!!!!*****--> </div><!--end content--> </div><!--end rightColumn--> </body> </html>

"videoClip2Players.js" (goes in "js" folder)

// JavaScript Document //this is a workaround for the fact that there is no "stop" flashvar in the JW Player. This script uses the Javascript API to force the player to play ONLY a specified clip out of the video, and do a hard stop.

var player1 = null; var player2 = null; var playlist1 = null; var playlist2 = null; var currentItem1 = 0; var currentItem2 = 0; var previousItem1 = -1; var previousItem2 = -1; var currentFile1 = null; var currentFile2 = null; var previousFile1 = null; var previousFile2 = null; var currentPosition1 = 0; var currentPosition2 = 0; var currentStart1 = null; var currentStart2 = null; var currentEnd1 = null; var currentEnd2 = null; var currentDur1 = null; var currentDur2 = null; var stopPos1 = 0; var stopPos2 = 0; var advance1 = 'dur'; var advance2 = 'dur'; var testing = false; var thegetPlayers = false; //used to prevent calling the getPlayers function twice

/* function playerReady(obj) { player = document.getElementsByName(obj.id)[0]; alert(player.id); player.addModelListener('TIME', 'timeMonitor'); player.addControllerListener('ITEM', 'itemMonitor');

setTimeout("playlist = player.getPlaylist()", 100); player.addControllerListener("PLAYLIST", "playlistHandler"); //setTimeout("dumpPlaylist()", 200); };*/ function playerReady(obj) { getPlayers(); if (testing){ gid('generalMsg').innerHTML += "<br/>playerReady being called now";} // getPlayers(); };

//http://www.longtailvideo.com/support/forum/Setup-Problems/13910/Two-Players-in-parallel- function getPlayers() { // if(thegetPlayers ==false){ //alert("getPlayers being called now"); player1 = gid('player1'); player2 = gid('player2'); if (testing){gid('generalMsg').innerHTML += '<br/><b>Im in getPlayers</b><br/> player1='+player1.id+'<br>player2='+player2.id;} player1.addModelListener("TIME", "timeMonitor1"); player2.addModelListener("TIME", "timeMonitor2"); player1.addViewListener("ITEM", "itemMonitor1"); player2.addViewListener("ITEM", "itemMonitor2"); player1.addControllerListener("PLAYLIST", "playlistHandler1"); player2.addControllerListener("PLAYLIST", "playlistHandler2"); if (testing){gid('generalMsg').innerHTML += "<br/>Player1 Ready(): " + player1.id+"<br/>Player2 Ready(): " + player2.id;} thegetPlayers=true; // }//end if the getplayers == false };

 

function playlistHandler1(){ playlist1 = player1.getPlaylist(); if (testing){ gid('generalMsg').innerHTML += "<br/>inPlaylistHandler1:"; gid('generalMsg').innerHTML += "<br/>playlist1[currentItem1].file= "+playlist1[currentItem1]['file']; gid('generalMsg').innerHTML += "<br>playlist1[currentItem1].start= "+playlist1[currentItem1]['dur']; dumpPlaylist1();} currentStart1 = 1 * playlist1[currentItem1]['start']; if (testing){ gid('currentStart1').innerHTML += "<br/>" +currentStart1;} currentEnd1 = 1 * playlist1[currentItem1]['end']; currentDur1 = 1 * playlist1[currentItem1]['dur']; if (testing){ gid('currentDur1').innerHTML += "<br/>" +currentDur2;} playlist1[currentItem1].duration = currentDur1; stopPos1 = currentStart1 + currentDur1; if (testing){ gid("stopPosition1").innerHTML += "<br/>" +stopPos1;} currentFile1 = playlist1[currentItem1]['file']; } function playlistHandler2(){ playlist2 = player2.getPlaylist(); if (testing){ gid('generalMsg').innerHTML += "<br/><b>inPlaylistHandler2:</b> playlist[currentItem2].start= "+playlist2[currentItem2].start; dumpPlaylist2();} currentStart2 = 1 * playlist2[currentItem2]['start']; if (testing){ gid('currentStart2').innerHTML += "<br/>currentStart2= " +currentStart2;} currentEnd2 = 1 * playlist2[currentItem2]['end']; currentDur2 = 1 * playlist2[currentItem2]['dur']; if (testing){ gid('currentDur2').innerHTML += "currentDur2=" +currentDur2;} duration = currentDur2; stopPos2 = currentStart2 + currentDur2; if (testing){ gid('stopPosition2').innerHTML += "stopPos2 =" +stopPos2 ; } currentFile2 = playlist2[currentItem2]['file']; } function timeMonitor1(obj) { currentPosition1 = obj.position; if (testing){ gid('timeMonitor').innerHTML += "<br>obj.id="+obj.id+" currentPosition1: " + currentPosition1 + "<br/>currentDur1: '" + currentDur1 + "<br/>currentStart: " + currentStart1 + "<br/>Stop Position1: " + stopPos1;} if(currentPosition1 >= stopPos1){ if(playlist1[currentItem1+1]) { player1.sendEvent('NEXT'); if (testing){ gid('timeMonitor').innerHTML += '<br>AAA sendEvent NEXT';} } else { player1.sendEvent('STOP'); if (testing){ gid('timeMonitor').innerHTML += '<br>AAA sendEvent STOP';} } } }; function timeMonitor2(obj) { currentPosition2 = obj.position; if (testing){ gid('timeMonitor').innerHTML += "<br>obj.id="+obj.id+" currentPosition2: " + currentPosition2 + "<br/>currentDur2: '" + currentDur2 + "<br/>currentStart: " + currentStart2 + "<br/>Stop Position2: " + stopPos2;} if(currentPosition2 >= stopPos2) { if (testing){ gid('timeMonitor').innerHTML += '<br>YES!'; gid('timeMonitor').innerHTML += '<br>currentPosition2: ' + currentPosition2 + '<br/>currentDur2: ' + currentDur2 + '<br/>currentStart: ' + currentStart2 + '<br/>Stop Position2: ' + stopPos2; } if(playlist2[currentItem2+1]) { player2.sendEvent('NEXT'); if (testing){ gid('timeMonitor').innerHTML += '<br>sendEvent NEXT';} } //end if(playlist2[currentItem2+1]) else { player2.sendEvent('STOP'); if (testing){ gid('timeMonitor').innerHTML += '<br>sendEvent STOP';} } //end else }//end if(currentPosition2 >= stopPos2) else if(currentPosition2 <=currentStart2){ player2.sendEvent('PLAY'); if (testing){ gid('timeMonitor').innerHTML += '<br>BBBBsendEvent PLAY';} } };

 

function itemMonitor1(obj) { alert(obj.index); currentItem1 = obj.index; if (testing){ gid('itemMonitor').innerHTML += '<br/>currentItem1.title='+currentItem1.title; gid('itemMonitor').innerHTML += '<br/>ItemMonitor1 says:<br/>currentItem1: ' + currentItem1 + '<br>previousItem1: ' + previousItem1 + '<br>currentItem Start1: ' + playlist1[currentItem1]['start'];} if(currentItem1==previousItem1){ setTimeout("player1.sendEvent('SEEK', currentStart1); gid('itemMonitor').innerHTML += '<br>sendEvent SEEK1';", 500); setTimeout("player1.sendEvent('NEXT'); gid('itemMonitor').innerHTML += '<br>sendEvent NEXT1';",200); } currentStart1 = 1 * playlist1[currentItem1]['start']; if (testing){ gid('currentStart1').innerHTML += "<br/>b" +currentStart1;} currentEnd1 = 1 * playlist1[currentItem1]['end']; currentDur1 = 1 * playlist1[currentItem1]['dur']; if (testing){ gid('currentDur1').innerHTML += "<br/>b" +currentDur2;} playlist1[currentItem1].duration = currentDur1; stopPos1 = currentStart1 + currentDur1; if (testing){gid('stopPosition1').innerHTML += "<br/>b" +stopPos1;} currentFile1 = playlist1[currentItem1]['file']; };

function itemMonitor2(obj) { alert(obj.index); currentItem2 = obj.index; if (testing){gid('itemMonitor').innerHTML += '<br>currentStart2=' + currentStart2 +'<br>currentItem2= ' + currentItem2 + '<br>previousItem2: ' + previousItem2 + '<br>CurrentItemStart2: ' + playlist2[currentItem2]['start']; } if(currentItem2==previousItem2){ setTimeout("player2.sendEvent('SEEK', currentStart2); gid('itemMonitor').innerHTML += '<br>sendEvent SEEK2';", 500); setTimeout("player2.sendEvent('NEXT'); gid('itemMonitor').innerHTML += '<br>sendEvent NEXT2';",200); } if(currentItem2 != previousItem2) { previousItem2 = currentItem2; currentStart2 = 1 * playlist2[currentItem2]['start']; if (testing){ gid('currentDur2').innerHTML += "playlist2[currentItem2]['start']; " + playlist2[currentItem2]['start']+ " currentDur2="+currentDur2; gid('currentStart2').innerHTML += "currentStart2" +currentStart2; } currentEnd2 = 1 * playlist2[currentItem2]['end']; currentDur2 = 1 * playlist2[currentItem2]['dur']; if (testing){gid('currentDur2').innerHTML += " currentDur2" + currentDur2;} stopPos2 = currentStart2 + currentDur2; //upDate2 = true; // alert('currentItem='+currentItem); // extra seek if the file has changed currentFile2 = playlist2[currentItem2]['file']; /* if(currentFile2 != previousFile2) { // alert('currentFile != previousFile'); previousFile2 = currentFile2; setTimeout("player2.sendEvent('SEEK', currentStart2)", 500); setTimeout("player2.sendEvent('PLAY')",200); alert('currentStart2 after Seek='+currentStart2); } */ } };

function dumpPlaylist1() { //alert('attempting to dumpPlaylist1'); for(var j in playlist1) { var nodes = '<br />'; for(var k in playlist1[j]) { nodes += '<li>' + k + ': ' + playlist1[j][k] + '</li>'; } gid('dump1').innerHTML = '<br><b>playlist1</b>'+nodes; } }; function dumpPlaylist2() { //alert('attempting to dumpPlaylist2'); for(var a in playlist2) { var nodes = '<br />'; for(var b in playlist2[a]) { nodes += '<li>' + b + ': ' + playlist2[a][b] + '</li>'; } gid('dump2').innerHTML = '<br><b>playlist2</b>'+nodes; } };

function gid(name) { return document.getElementById(name); };

The playlists (go in "media" folder)

Playlist 1

<?xml version="1.0" encoding="utf-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track>
<title>Magnetic Resonance Safety</title>
<location>02_Presentation.flv</location>
<type>rtmpt</type>
<image>media/posterframevid20.jpg</image>
<meta rel="type">video</meta>
<meta rel="start">0</meta>
<meta rel="end">16</meta>
<meta rel="dur">16</meta>
</track>
</trackList>
</playlist>

Playlist 2

<?xml version="1.0" encoding="utf-8"?>
<playlist version="1" xmlns="http://xspf.org/ns/0/">
<trackList>
<track>
<title>Magnetic Resonance Safety</title>
<location>01_Presentation</location>
<type>rtmpt</type>
<image>media/posterframevid20.jpg</image>
<meta rel="type">video</meta>
<meta rel="start">174</meta>
<meta rel="end">215</meta>
<meta rel="dur">41</meta>
</track>
</trackList>
</playlist>