JW FLV Player and Red5 apparent buffering bug

Update 12-18-09: Longtailvideo has apparently resolved this bug with its player v.4.7beta. At this time v. 5 does NOT have the fix in it. The code changes that fixed the bug can be seen here: [Changes to RTMPModel.as]

A few months ago, we installed Red5 0.7.0 on a Windows server to stream Flash video inside the firewall. This server is used mainly to stream short clips that are embedded in learning modules.

The JW FLV player v. 4.6 is included in the learning module template and enables us to display the videos with a high degree of flexibility.

Picture 41.jpeg

Example of a player that contains multiple short video clips.
The player generates a navigation listing on the right from the XML playlist.



To break a long clip into chapters, it is possible to specify in and out points in the playlist for each segment desired. So recently, I put online a 57-minute video clip, with 16 chapters. A screenshot is shown below.

Picture 40.jpeg

Out of the 16 segments, all played fine except the last two. Those two would play for a few seconds, then stop. Checking the debugging output in Firebug’s console, showed a “Buffer Empty” entry at that point. The file would play fine once or twice, then never again, for anyone, on any browser.

Picture 42.jpegBuffer.Empty error

Thinking there might be something wrong with the video file or the encoding, I had the author re-output the video to DVD, then I encoded it to FLV using several different conversion tools and settings. All of the new test files also had the same issue. I also tried using the metadata injector, even though I was pretty sure the metadata was OK.

At that point, I thought of using a javascript playlist since javascript allows you to build in some error-catching into the functions for seeking and playing items in the playlist. But, once a basic javascript-based playlist was built, I was very surprised to find that all the video’s segments now played just fine without adding in any special error-catching or modification of the standard functions.

Picture 44.jpeg
This is what the Javascript-based playlist looks like. I used the same timings for all the segments as on the XML-based playlist, and the exact same streaming video file.

I suspect the cause of the problem is a bug in Red5, but am not sure why using the Javascript functions works when the FLV-player playlist functions do not.

I’ve posted some demo pages, but since I don’t have an external red5 server to use, the video cannot be seen. However they will be of use to understand the code and structure of the player.

DEMO PAGES

  • Page that demonstrates the bug:
    [LINK]
  • Page that demonstrates the fix:
    [LINK]

Source code for the javascript-based page

<!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> </TITLE>
  <link href="css/allBrowsers.css" rel="stylesheet" type="text/css" />
  <link href="css/header.css" rel="stylesheet" type="text/css" />
  <style>
  /*these shut off the navbar & progress bar on this page*/
  #whiteArea { background:url(images/whiteAreaAllWhite.jpg) repeat-y;  }
  #leftColumn { display:none;}
  #subTitleBar {visibility:hidden;}
  #leftColumn.leftColumnOpen {display:block;}
  </style>
  <!--#include file="includes/headContent.htm"-->
<script>
  function showNavbar(){
  document.getElementById('whiteArea').style.background = 'url(images/whiteArea2shadows.jpg)';
  document.getElementById('leftColumn').style.display = 'block';
  document.getElementById('leftColumn').className = 'leftColumnOpen';
  document.getElementById('subTitleBar').style.visibility = 'visible';
}
  </script>
  <script type="text/javascript">
  
  var flashvars = {
  resizing:            'true',
  backcolor:           '#00487a',
  frontcolor:          '#dff0fb',
  playlist:            'right',
  playlistsize:        '200',
  shuffle:             'false',
  icons:               'true', 
  bufferlength:        '2',
  autostart:           'false', 
  debug:               'console',
  repeat:              'list', 
  stretching:          'exactfit',
  file:                'media/playlist_10-5.xml',
  streamer:            'rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/lms/measuringsuccess/'
  }; 
  var params =
  {
  allowscriptaccess:    'always',
  allowfullscreen:      'true',
 bgcolor:              '#000000'
  };
  var attributes =
  {
  id:                   'player',
  name:                 'player'
  };
  
  swfobject.embedSWF('includes/jw_media_player/player-licensed.swf', 'player', '898', '413', '9.0.124', false, flashvars, params, attributes);
  //function loadFile(theFile){   player.sendEvent('LOAD',theFile); };
  // --> 
  </SCRIPT>

</head>
  <body>
<!--#include file="includes/header.htm" -->
  <div id="leftColumn"><!--#include file="includes-local/navbar.htm" -->&nbsp;</div>
  <div id="rightColumnFullWidth">
  <div id="subTitleBar">
  <div align="right"><!--<span id="pageTitle"></span>--><span id="pageNumberHolder"></span> <a href="javascript:window.print()" id="printIcon"><img src="images/images.jpg" alt="print this page" /></a> </div>
  </div>
  <div id="contentFullWidth">
 <!--*********************put content below this line!!!!************-->
  <br />
  <span id="VideoPageTitle">Measuring Success: Setting and Reaching Your Learning Goals (Duration: 59 minutes)</span>
  <p><a id="player" name="player" class="player" href="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash"><img src="media/poster001.jpg"/>Get the Adobe Flash Player to see this video.</a>
  <!--*********put content above this line!!!!!*****-->
  </div><!--end content-->
 
  </div><!--end rightColumn-->
  
  <div id="footer"><!--#include file="includes/footer.htm" --></div>
  </div>
 
  </body>
  </html>
  <script language="JavaScript" src="js/endScripts.js" type="text/javascript"></script>

Source code for the javascript-based page

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!--http://www.longtailvideo.com/support/forum/Feature-Suggestions/2756/Multiple-playlists-a-Success-#msg109471--> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <!--/*v 1.5 12-11-2007*/--> <title>single clip player with javascript playlist</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> <link href="css/allBrowsers.css" rel="stylesheet" type="text/css" /> <link href="css/header.css" rel="stylesheet" type="text/css" /> <!--#include file="includes/headContent.htm"--> <!--<script src="http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>-->

<style type="text/css"> /* these shut off the navbar & progress bar on this page #whiteArea { background:url(images/whiteAreaAllWhite.jpg) repeat-y; } #leftColumn { display:none;} #subTitleBar {visibility:hidden;}*/ #leftColumn.leftColumnOpen {display:block;} div#subnav a,#subnav a:link,#subnav a:visited {border-left:6px solid #9F12F3;display:block;width:164px;background-color:#000;font:12px normal Arial, Helvetica, sans-serif;text-decoration:none;color:#FFF;padding:3px 3px 6px 6px;border-bottom:1px solid #333;} #subnav a:hover{background-color:#9F12F3;color:#000;} #subnav a.navbtn.current{background-color:#3FF312;color:#FFF;border-left:6px solid #3FF312;}

#subnav h2 {width:164px;height:20px;padding:6px 3px 0px 3px;background-color:#000;font:12px bold Arial, Helvetica, sans-serif;color:#FFF;margin-bottom:0px;border-bottom:1px solid #9F12F3;} #rightColumn {padding-top:0px;} #content #contenTable td {padding:0px;} </style>

<script type="text/javascript" language="javascript"> function showNavbar(){ document.getElementById('whiteArea').style.background = 'url(images/whiteArea2shadows.jpg)'; document.getElementById('leftColumn').style.display = 'block'; document.getElementById('leftColumn').className = 'leftColumnOpen'; document.getElementById('subTitleBar').style.visibility = 'visible'; } </script>

<!--[if IE]> <script> </script> <style type="text/css"> #rightColumn, #contentTable{width:755px; } </style> <![endif]--> </head> <body>

<!--#include file="includes/header.htm" --> <div id="leftColumn"><!--#include file="includes-local/navbar.htm" -->&nbsp;</div> <div id="rightColumn"> <div id="subTitleBar"> <div align="right"> <!--<span id="pageNumberHolder"></span>--> <a href="javascript:window.print()" id="printIcon"><img src="images/images.jpg" alt="print this page" /></a> </div> </div> <div id="content"> <!--*********************put content below this line!!!!************--> <table width="auto" border="0" id="contenTable" cellpadding="0" cellspacing="0"> <tr> <td valign="top" class="col1"><h1>Single video clip with chapters created in playlist</h1> </td> </tr> <tr> <td valign="top"> <script type="text/javascript"> var player=document.getElementById('player'); var seek = 0; var seekFlag = false; var currentTime;

function gid(name) { return document.getElementById(name); }; var flashvars = { fullscreen: 'true', resizing: 'true', width: '356', height: '288', backcolor: '#111111', frontcolor: '#999999', lightcolor: '#66cc00', playlist: 'none', repeat: 'none', shuffle: 'false', icons: 'false', bufferlength: '2', autostart: 'false', stretching: 'fill', skin: 'includes/mediaplayer4.5/modieus.swf', oncomplete: 'none', file: 'mpegStreamclipOutput.flv', streamer: 'rtmpt://red502.mcit.med.umich.edu:80/oflaDemo/lms/measuringsuccess', debug: 'console' }; var params ={ allowscriptaccess: 'always', allowfullscreen: 'true', bgcolor: '#000000' }; var attributes ={ id: 'player', name: 'player' }; swfobject.embedSWF('includes/jw_media_player/player-licensed.swf', 'player', '500', '310', '9.0.124', false, flashvars, params, attributes); </script> <script type="text/javascript"> function playerReady(obj) { //alert(gid(obj)); player = gid('player'); player.addModelListener('TIME', 'timeMonitor'); }; //function loadFile(theFile){ player.sendEvent('LOAD',theFile); }; function timeMonitor(obj) { //alert('in timeMonitor'); if((obj.position > 0) && (seekFlag)) { seekFlag = false; player.sendEvent('SEEK', seekto); } currentTime = obj.position; };

function seekSome(seconds, id) { // alert(player); seekto = seconds; if(currentTime > 0) { //alert('currentTime > 0'); player.sendEvent('SEEK', seekto); makeCurrent(id); } else { // alert('currentTime <= 0'); seekFlag = true; makeCurrent(id); player.sendEvent('PLAY', 'true'); } }; function makeCurrent(selectedLayer){ // Written By: WillyDuitt@hotmail.com || 03-22-2005 \; var links = getElementsByClass(document, 'navbtn', 'a'); for(var i=0; i<links.length; i++){ links[i].className = 'navbtn'; } // alert(selectedLayer); gid(selectedLayer).className = 'navbtn current'; }; function gid(name) { return document.getElementById(name); }; </script> <a id="player" name="player" class="player" href="http://www.adobe.com/shockwave/download/download.cgi?P1_Prod_Version=ShockwaveFlash">Get the Adobe Flash Player to see this video.</a> </td> <td valign="top" class="col1"> <div id="subnav" style="height:309px;overflow:auto;"> <h2>Jump to</h2> <a href="javascript:seekSome('0', 'int');" id="int" class="navbtn">Introduction</a> <a href="javascript:seekSome('312', 'q4u')" id="q4u" class="navbtn">Questions for you</a> <a href="javascript:seekSome('439', 'why')" id="why" class="navbtn">Why Measure?</a> <a href="javascript:seekSome('651','cst')" id="cst" class="navbtn">Cost</a> <a href="javascript:seekSome('875','stk')" id="stk" class="navbtn">Stakeholders</a> <a href="javascript:seekSome('915','msr')" id="msr" class="navbtn">Measure What?</a> <a href="javascript:seekSome('1021','rca')" id="rca" class="navbtn">Root Cause Analysis</a> <a href="javascript:seekSome('1395','bsl')" id="bsl" class="navbtn">Baseline & Audit Data]</a> <a href="javascript:seekSome('1580','aud')" id="aud" class="navbtn">3 Audit Types</a> <a href="javascript:seekSome('1896','cse')" id="cse" class="navbtn">A Case Study</a> <a href="javascript:seekSome('2138','whn')" id="whn" class="navbtn">When to Measure</a> <a href="javascript:seekSome('2344','lvl')" id="lvl" class="navbtn">4 Learning Levels</a> <a href="javascript:seekSome('2525','chl')" id="chi" class="navbtn">Children & Adults</a> <a href="javascript:seekSome('2627','stc')" id="stc" class="navbtn">Sticky Learning</a> <a href="javascript:seekSome('3250','cnc')" id="cnc" class="navbtn">Conclusion</a> <a href="javascript:seekSome('3342','crd')" id="crd" class="navbtn">Credits</a> </div> </td> </tr> </table> <!--*********put content above this line!!!!!*****--> </div><!--end content--> </div><!--end rightColumn--> <div id="footer"><!--#include file="includes/footer.htm" --></div> <!--</div>-->

</body> </html> <script language="JavaScript" src="js/endScripts.js" type="text/javascript"></script>

XML playlist

<?xml version="1.0" encoding="utf-8"?>
  <playlist version="1" xmlns="http://xspf.org/ns/0/">
  <trackList>
  <track>
  <title>Introduction</title>
  <creator>Steve Burdick</creator>
  <annotation>Steve Burdick</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">0</meta>
  <meta rel="duration">311</meta>
  </track>
<track>
  <title>Questions for you</title>
  <annotation>...and for me.</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">312</meta>
  <meta rel="duration">438</meta>
  </track>
<track>
  <title>Why Measure?</title>
  <annotation>Is it worth it?</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">439</meta>
  <meta rel="duration">650</meta>
  </track>
<track>
  <title>Cost</title>
  <annotation>Learning as an investment</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">651</meta>
  <meta rel="duration">869</meta>
  </track>
<track>
  <title>Stakeholders</title>
  <annotation>Who benefits?</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">875</meta>
  <meta rel="duration">914</meta>
  </track>
<track>
  <title>Measure What?</title>
  <annotation>Define the problem.</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">915</meta>
  <meta rel="duration">1020</meta>
  </track>
<track>
  <title>Root Cause Analysis</title>
  <annotation>Drilling down</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">1021</meta>
  <meta rel="duration">1394</meta>
  </track>
<track>
  <title><![CDATA[Baseline & Audit Data]]></title>
  <annotation>Gather evidence</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">1395</meta>
  <meta rel="duration">1579</meta>
  </track>
<track>
  <title>3 Audit Types</title>
  <annotation>What works for you?</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">1580</meta>
  <meta rel="duration">1895</meta>
  </track>
<track>
  <title>A Case Study</title>
  <annotation>The "rage to conclude"...</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">1896</meta>
  <meta rel="duration">2137</meta>
  </track>
<track>
  <title>When to Measure</title>
  <annotation><![CDATA[Retrieval & retention]]></annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">2138</meta>
  <meta rel="duration">2343</meta>
  </track>
<track>
  <title>4 Learning Levels</title>
  <annotation>The Kirkpatrick model</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">2344</meta>
  <meta rel="duration">2524</meta>
  </track>
<track>
  <title><![CDATA[Children & Adults]]></title>
  <annotation><![CDATA[Pedagogy & Andragogy]]></annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">2525</meta>
  <meta rel="duration">2626</meta>
  </track>
<track>
  <title>Sticky Learning</title> 
  <annotation>6 Concrete examples</annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">2627</meta>
  <meta rel="duration">3249</meta>
  </track>
<track>
  <title>Conclusion</title> 
  <annotation> </annotation>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">3250</meta>
  <meta rel="duration">3341</meta>
  </track>

<track>
  <title>Credits</title>
  <location>mpegStreamclipOutput.flv</location>
  <type>rtmp</type>
  <meta rel="start">3342</meta>
  <meta rel="duration">3460</meta>
  </track>
  </trackList>
  </playlist>