LongTail Video is now JW Player - New Name, Same Passion For Video

Close

JW Player Forums

CATEGORY:

/

Double-Caption

30 replies [Last post]

Does anyone know how to double caption like this:

http://xdep.aquops.qc.ca/zoom/exemple_double%20caption.jpg

Anyone...please.

seems like i'm ignored... *snif*

You would have to use xmlHTTP (AJAX) to get the second captions file, then CSS to position it over the player, then JavaScript to coordinate the caption with the time. Not worth the effort, in my opinion.

However, if you really, really, really, really gotta do this, see AJAX's code in this thread: http://www.jeroenwijering.com/?thread=9885 starting with his post on 02.04.2008 and continuing down through that thread.

That code retrieves the playlist XML file (change to the captions XML file), parses it to get the contents of the annotation element as getTag('annotation") (change to the paragraph element which holds your caption text), and use the CSS to position the caption on top of the player.

thanks a lot for the hints I'll try this

streamBabie, can you help me with something? If I do <annotation>caption.xml</annotation> in my playlist file will it work with the code you mentionned above?
Thanks

Here's my playlist file:

<playlist version="1" xmlns="http://xspf.org/ns/0/">

<title>Sample XSPF Playlist</title>

<info>http://www.jeroenwijering.com/?item=Flash_Media_Player</info>

<annotation>An example of a playlist with commercial</annotation>

<trackList>

<track>

<title>Video 1</title>

<location>http://xserve.animare.org/zoom/videos/animare/320_640.mp4</location>

<image>http://zoom.animare.org/images/default_video_label.png</image>

<!--<info>http://www.spotajob.nl</info>

<album>commercial</album>

<meta rel='captions'>http://xdep.aquops.qc.ca/zoom/captions1.xml</meta>

<strong><annotation>http://xdep.aquops.qc.ca/zoom/captions3.xml</annotation></strong>

<meta rel="type">flv</meta>

</track>

<track>

<title>Video 2</title>

<location>http://xserve.animare.org/zoom/videos/animare/321_640.mp4</location>

<image>http://zoom.animare.org/images/default_video_label.png</image>

<meta rel='captions'>http://xdep.aquops.qc.ca/zoom/captions1.xml</meta>

<meta rel="type">flv</meta>
</track>

<track>

<title>Video 3</title>

<location>http://xserve.animare.org/zoom/videos/animare/322_640.mp4</location>

<image>http://zoom.animare.org/images/default_video_label.png</image>

<meta rel='captions'>http://xdep.aquops.qc.ca/zoom/captions1.xml</meta>

<meta rel="type">flv</meta>

</track>

</trackList>

</playlist>

Yes. When the item is selected, get the contents of the annotation element as the flashvar 'description' (this will be the URI for the captions file ).

Call the xmlHTTP with that URI to retrieve the captions file.

Parse the captions file into an array with start, end, and captiontext.

Monitor the time and display the caption text from the 'begin' time to the 'end' time.

You might want to set bufferlength to 3-5 seconds to have a little time to retrieve and parse the captions file before the video starts.

I haven't tested this code very much, but it does work. Maybe it's a starting point for you.

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

<html lang="en">

  <head>

    <title>Display Second Captions File</title>

    <script type="text/javascript" src="swfobject-2.0.js"></script>

    <style type="text/css">
      div.playercontainer
      {
        position:           absolute;
        top:                    70px;
        left:                  100px;
        width:                 640px;
        height:                440px;
        font-size:               8pt;
        font-family:           Arial;
        z-index:                 100;
      }

      div.captions
      {
        position:           absolute;
        top:                    80px;
        left:                  110px;
        width:                 620px;
        height:                100px;
        font-size:              14pt;
        font-family:           Arial;
        color:               #E0E0E0;
        z-index:                 120;
      }

      div.captions1
      {
        position:           absolute;
        top:                    79px;
        left:                  109px;
        width:                 620px;
        height:                100px;
        font-size:              14pt;
        font-family:           Arial;
        color:               #303030;
        z-index:                 115;
      }

      div.captions2
      {
        position:           absolute;
        top:                    79px;
        left:                  111px;
        width:                 620px;
        height:                100px;
        font-size:              14pt;
        font-family:           Arial;
        color:               #303030;
        z-index:                 115;
      }

      div.captions3
      {
        position:           absolute;
        top:                    81px;
        left:                  111px;
        width:                 620px;
        height:                100px;
        font-size:              14pt;
        font-family:           Arial;
        color:               #303030;
        z-index:                 115;
      }

      div.captions4
      {
        position:           absolute;
        top:                    81px;
        left:                  109px;
        width:                 620px;
        height:                100px;
        font-size:              14pt;
        font-family:           Arial;
        color:               #303030;
        z-index:                 115;
      }
      </style>

    <script type="text/javascript">
      var Captions = [0];
      var xmlhttp;
      var xmlDoc;

      function loadXMLDoc(aFile)
      {
        xmlhttp = null;
        if(window.XMLHttpRequest)
        {
          xmlhttp = new XMLHttpRequest();
        } 
        else if(window.ActiveXObject)
        {
          xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        }

        if (xmlhttp != null)
        {
          xmlhttp.onreadystatechange = loaded;
          xmlhttp.open("GET", aFile, true);
          xmlhttp.send(null);
        }
        else
        {
          alert('Sorry, your browser can\'t handle this script');
          return;
        }
      };

      function checkReadyState(obj)
      {
        if(obj.readyState == 4)
        {
          if(obj.status == 200)
          {
            return true;
          }
          else
          {
            alert('Problem retrieving XML data');
          }
        }
      };

      function loaded()
      {
        if(checkReadyState(xmlhttp))
        {
          xmlDoc = xmlhttp.responseXML.documentElement;
          captionObject = xmlDoc.getElementsByTagName('p');
          // build array of begin, end, and caption
          for (i = 0; i < captionObject.length; i++)
          {
            Captions[i] = ['begin', 'end', 'caption'];
            Captions[i]['begin']   = convert_time_to_value(captionObject[i].getAttribute('begin'));
            Captions[i]['end']     = convert_time_to_value(captionObject[i].getAttribute('end'));
            Captions[i]['caption'] = captionObject[i].firstChild.data;
          }
  
//...debug
  pL = document.getElementById('captions');
//pL.innerHTML = 'First Caption: ' + 'Begin: ' + captionObject[0].getAttribute('begin') + ' End: ' + captionObject[0].getAttribute('end') + ' Caption: ' + captionObject[0].firstChild.data;
  pL.innerHTML = 'First Caption: ' + 'Begin: ' +      Captions[0]['begin']                   + ' End: ' + Captions[0]['end']                   + ' Caption: ' + Captions[0]['caption'];
//...debug

        }
      };

      function convert_time_to_value(time_format)
      {
        var hh = time_format.substring(0,1);
        var mm = time_format.substring(2,4);
        var ss = time_format.substring(5,7);
        var hs = time_format.substring(8,10);
        var time_value = (eval(hh) * 3600) + (eval(mm) * 60) + eval(ss) + Math.round(eval(hs)/100);
        return time_value;
      };
    </script>

    <script type='text/javascript'>
      function createPlayer()
      {
        var flashvars =
        {
          width:                             '640',
          height:                            '480',
          displayheight:                     '480',
          file:                              'playlist_second_captions_file.xml',
          bufferlength:                      '8',
          overstretch:                       'false',
          showstop:                          'true',
          showicons:                         'true',
          showdigits:                        'true',
          autostart:                         'true',
          shuffle:                           'false',
          repeat:                            'list',
          thumbsinplaylist:                  'true',
          enablejs:                          'true',
          javascriptid:                      'mpl',
          searchbar:                         'false',
          backcolor:                         '0xFFFFFF', // face of buttons
          frontcolor:                        '0x404040', // button symbols & playlist text
          lightcolor:                        '0x808080', // highlighted playlist item
          screencolor:                       '0xD5D5D5'  // screen background color
        };

        var params =
        {
          menu:                              'true',
          allowfullscreen:                   'true',
          wmode:                             'opaque'
        };

        var attributes =
        {
          id:                                'mpl',
          name:                              'mpl'
        };

        swfobject.embedSWF('mediaplayer.swf', 'player', '640', '480', '7.0.0', false, flashvars, params, attributes);

      };
    </script>

    <script type='text/javascript'>
      var currentPlayer;
      var currentPosition =  0;
      var currentItem     =  0;
      var previousItem    = -1;
      var beginTime       =  0;
      var endTime         =  0;

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

      function getUpdate(typ, pr1, pr2, pid)
      {
        currentPlayer = pid;
        if(typ == 'time')
        {
          currentPosition = pr1;
          // look for a new caption
          if(currentPosition > endTime)
          {
            // clear current caption
            cA = document.getElementById('captions');
            cA.innerHTML = 'CLEAR';
            for( i = 0; i < Captions.length; i++)
            {
              if(Captions[i]['begin'] ==  currentPosition)
              {
                // display caption
                cA = document.getElementById('captions');
                cA.innerHTML = Captions[i]['caption'];
                cA1 = document.getElementById('captions1');
                cA1.innerHTML = Captions[i]['caption'];
                cA2 = document.getElementById('captions2');
                cA2.innerHTML = Captions[i]['caption'];
                cA3 = document.getElementById('captions3');
                cA3.innerHTML = Captions[i]['caption'];
                cA4 = document.getElementById('captions4');
                cA4.innerHTML = Captions[i]['caption'];
                beginTime = Captions[i]['begin'];
                endTime   = Captions[i]['end'];
                break;
              }
            }
          }

//...debug
tI = document.getElementById('time');
tI.innerHTML = 'Current Position: ' + currentPosition + ' Begin: ' + beginTime + ' End: ' + endTime;
//...debug

        }
        else if(typ == 'item')
        {
          currentItem = pr1;
          if(currentItem != previousItem)
          {
            previousItem = currentItem;
            beginTime = 0;
            endTime   = 0;
            setTimeout("getItemData(currentPlayer, currentItem)", 100);
          }
        }
      };

      function getItemData(swf, idx)
      {
        var obj = thisMovie(swf).itemData(idx);
        loadXMLDoc(obj['description']);
      };

      function thisMovie(movieName)
      {
        return document.getElementsByName(movieName)[0];
      };
    </script>

  </head>

  <body onload="createPlayer();">

    <div id="playercontainer" class="playercontainer">
      <a id="player" class="player"  href="http://www.macromedia.com/go/getflashplayer">Get the Flash Plugin to see this gallery.</a>
    </div>
    <div id="captions" class="captions">
    </div>
    <div id="captions1" class="captions1">
    </div>
    <div id="captions2" class="captions2">
    </div>
    <div id="captions3" class="captions3">
    </div>
    <div id="captions4" class="captions4">
    </div>

    <div id="time" class="time">
    </div>

  </body>

</html>

Thank you very much!
I just have a few question does it work with the swfobject v1.5 ?
I can't seem to find where it says to get the annotation tag in the xml file.
Do I have to change 'player' for 'mpl'?


swfobject.embedSWF('mediaplayer.swf', 'player', '640', '480', '7.0.0', false, flashvars, params, attributes);

from Ajax' previous posting:

get the contents of the annotation element as the flashvar 'description'

I'm sorry, im such a newbie at this, ill have to bother you again.It still doesnt work

Here's my problem:

When I test the page I get the get the flashplayer comment.
I tried to change the flasvar 'file' into my xml file, but it still doesn't work. So for the flashvar description, I don't really understand...
Do I have to change something here?

     function getItemData(swf, idx)
      {
        var obj = thisMovie(swf).itemData(idx);
        l<strong>oadXMLDoc(obj['description']);</strong>
      };

To sum up I really don't know what I have to change in the code to make it work.

Sorry:$

This code gets the next caption file i the item (track) changes:

        else if(typ == 'item')
        {
          currentItem = pr1;
          <strong>if(currentItem != previousItem)</strong>
          {
            previousItem = currentItem;
            beginTime = 0;
            endTime   = 0;
            setTimeout("getItemData(currentPlayer, currentItem)", 100);
          }
        }
      };

      function getItemData(swf, idx)
      {
        var obj = thisMovie(swf).itemData(idx);
        <strong>loadXMLDoc(obj['description']);</strong>
      };

I noticed that I forgot to clear the background divs, so find this code:

            // clear current caption
            cA = document.getElementById('captions');
            cA.innerHTML = 'CLEAR';

 and replace it with this code:

            // clear current caption
            cA = document.getElementById('captions');
            cA.innerHTML = '';
            cA = document.getElementById('captions1');
            cA.innerHTML = '';
            cA = document.getElementById('captions2');
            cA.innerHTML = '';
            cA = document.getElementById('captions3');
            cA.innerHTML = '';
            cA = document.getElementById('captions4');
            cA.innerHTML = '';

You should change this line to your playlist file:file:                              'playlist_second_captions_file.xml',Your playlist should look like this:

<?xml version='1.0' encoding='UTF-8'?>
<playlist version='1' xmlns='http://xspf.org/ns/0/'>
  <trackList>
    <track>
      <creator><![CDATA[<font face="Arial">1: capo420420</font>]]></creator>
      <title><![CDATA[<font face="Arial">Charlotte North Carolina's Jeff Williams "Helpless"</font>]]></title>
      <location>http://my.domain.com/path/YouTube_URL.php?v=FELu606lidw</location>
      <image>http://img.youtube.com/vi/FELu606lidw/2.jpg</image>
      <meta rel='type'>flv</meta>
      <meta rel='midroll'>midroll_longtail_example.xml</meta>
      <strong><meta rel='captions'>captions-1.xml</meta></strong>
      <info>http://http://my.domain.com/</info>
      <strong><annotation>captions-3.xml</annotation></strong>
    </track>
    <track>
      <creator><![CDATA[<font face="Arial">2: sniper22b</font>]]></creator>
      <title><![CDATA[<font face="Arial">Piano Lesson With A Difference</font>]]></title>
      <location>http://my.domain.com/path/YouTube_URL.php?v=dYhhQhtrlFI</location>
      <image>http://img.youtube.com/vi/dYhhQhtrlFI/2.jpg</image>
      <meta rel='type'>flv</meta>
      <meta rel='midroll'>midroll_longtail_example.xml</meta>
      <meta rel='captions'>captions-1.xml</meta>
      <info>http://http://my.domain.com/</info>
      <annotation>captions-4.xml</annotation>
    </track>
    <track>
      <creator><![CDATA[<font face="Arial">3: Clint Eastwood</font>]]></creator>
      <title><![CDATA[<font face="Arial">Gorillaz</font>]]></title>
      <location>http://my.domain.com/path/YouTube_URL.php?v=qpDer9wdUEw</location>
      <image>http://img.youtube.com/vi/qpDer9wdUEw/2.jpg</image>
      <meta rel='type'>flv</meta>
      <meta rel='midroll'>midroll_longtail_example.xml</meta>
      <meta rel='captions'>captions-1.xml</meta>
      <info>http://http://my.domain.com/</info>
      <annotation>captions-3.xml</annotation>
    </track>
    <track>
      <creator><![CDATA[<font face="Arial">4: Snow Patrol</font>]]></creator>
      <title><![CDATA[<font face="Arial">Open Your Eyes</font>]]></title>
      <location>http://my.domain.com/path/YouTube_URL.php?v=1l5ipSldZNI</location>
      <image>http://img.youtube.com/vi/1l5ipSldZNI/2.jpg</image>
      <meta rel='type'>flv</meta>
      <meta rel='midroll'>midroll_longtail_example.xml</meta>
      <meta rel='captions'>captions-1.xml</meta>
      <info>http://http://my.domain.com/</info>
      <annotation>captions-4.xml</annotation>
    </track>
  </trackList>
</playlist>

The relevant lines being <meta rel='captions'>captions-1.xml</meta> for your normal captions file and <annotation>captions-4.xml</annotation> for your second captions file.

This code does use swfobject v2.0, so make sure that you have downloaded it from the Google Code site and uploaded it to your server with the filename swfobject-2.0.js so you can also use SWFObject v1.5 for older applications.

The playlist file name is the only thing that you should hav to change.

If your mediaplayer.swf and/or swfobject-2.0.js are not in the same directory as this HTML page, then you should add the correct path to them. Perhaps something like this:<script type="text/javascript" src="http://my.domain.com/path/swfobject-2.0.js"></script>Of course, substituting your real domain and path.

I get the video player (yes finally), but the second caption file doesn't appear

Can you provide a link to your test page or at least post your current code with real URIs so I can check the location of files and verify that you are using the correct paths.

Depending on your server setup, you may also need to use a full URI for the second caption file in the annotation element, like this:<annotation>http://my.domain.com/path/caption-2.xml</annotation>Of course, substitute your domain, path, and caption file values.

ok here are the links

http://xdep.aquops.qc.ca/zoom/Essai_DoubleCaption.html

the captions appears, but its undefined

Your convert time function doesnt work

      function convert_time_to_value(time_format)
      {
        var hh = time_format.substring(0,1);
        var mm = time_format.substring(2,4);
        var ss = time_format.substring(5,7);
        var hs = time_format.substring(8,10);
        var time_value = (eval(hh) * 3600) + (eval(mm) * 60) + eval(ss) + Math.round(eval(hs)/100);
        return time_value;
      };

I think there's a problem in your code and it's the substrings

    var hh = time_format.substring<strong>(0,2)</strong>;
        var mm = time_format.substring<strong>(3,5)</strong>;
        var ss = time_format.substring<strong>(6,8)</strong>;
        //var hs = time_format.substring<strong>(10,12)</strong>;// i don't use this

Are you using the correct time format according to the standard, like this:<p begin="0:00:03.50" end="0:00:05.00">this is the first caption</p>You need 'hs' for the hundredths of seconds, even though we have to round it because the player only reports the position once per second.

I use this time format
<p begin="00:08" end="00:10">- Nothing is going on.</p>

I did this and its working

      function convert_time_to_value(time_format)
      {
        var hh = time_format.substring(0,2);
        var mm = time_format.substring(3,5);
        var ss = time_format.substring(6,8);
        var hs = time_format.substring(8,10);

var time_value  = hh * 3600 + mm * 60 + ss

       //var time_value = (eval(hh) * 3600) + (eval(mm) * 60) + eval(ss) /*+ Math.round(eval(hs)/100*/);

        return time_value;
      };

Thanks a lot for the great support AJAX! Nice job, I hope someday i'll be a great programmer like that.

Great! I'm really happy to see it's working. When I have some time, I'm going to clean up and optimize the code a bit more.

Also, I want to find a better way to make the "glow" for the captions. Writing and clearing four divs is really kludgy, but it works.

You really should eval() the time strings, but JavaScript is pretty forgiving if you don't. And, you don't need the hh (hours) substring. For your time format 00:08 maybe:

function convert_time_to_value(time_format)
{
  var mm = time_format.substring(0,2);
  var ss = time_format.substring(3,5);
  var time_value = (eval(mm) * 60) + eval(ss);
  return time_value;
};

Hi AJAX! just wanted to know if the program could work if your video is Fullscreen? If so, do you know to do this?

The Adobe Flash Player goes off into it's own world in fullscreen. Anything that we do in the browser is left behind.

You should either not allow fullscreen, or be OK with the loss of the second captions if the user goes fullscreen.

There are other ways to enlarge the video within the browser which will retain the second captions. Perhaps that is what you should do if you want to retain the second captions.

I've updated the code a bit to deal with user scrubbing. You should be able to just replace this chunk of JS code:

    <script type='text/javascript'>
      var currentPlayer;
      var currentPosition =  0;
      var currentItem     =  0;
      var previousItem    = -1;
      var beginTime       =  0;
      var endTime         =  0;

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

      function getUpdate(typ, pr1, pr2, pid)
      {
        currentPlayer = pid;
        if(typ == 'time')
        {
          currentPosition = pr1;
          // look for a new caption
          if(currentPosition > endTime)
          {
            // clear current caption
            cA = document.getElementById('captions');
            cA.innerHTML = '';
            cA = document.getElementById('captions1');
            cA.innerHTML = '';
            cA = document.getElementById('captions2');
            cA.innerHTML = '';
            cA = document.getElementById('captions3');
            cA.innerHTML = '';
            cA = document.getElementById('captions4');
            cA.innerHTML = '';

            for( i = 0; i < Captions.length; i++)
            {
              if(Captions[i]['begin'] ==  currentPosition)
              {
                // display caption
                cA = document.getElementById('captions');
                cA.innerHTML = Captions[i]['caption'];
                cA1 = document.getElementById('captions1');
                cA1.innerHTML = Captions[i]['caption'];
                cA2 = document.getElementById('captions2');
                cA2.innerHTML = Captions[i]['caption'];
                cA3 = document.getElementById('captions3');
                cA3.innerHTML = Captions[i]['caption'];
                cA4 = document.getElementById('captions4');
                cA4.innerHTML = Captions[i]['caption'];
                beginTime = Captions[i]['begin'];
                endTime   = Captions[i]['end'];
                break;
              }
            }
          }
          else if(currentPosition < beginTime)
          {
            beginTime = 0;
            endTime   = 0;
          }

//...debug
tI = document.getElementById('time');
tI.innerHTML = 'Current Position: ' + currentPosition + ' Begin: ' + beginTime + ' End: ' + endTime;
//...debug

        }
        else if(typ == 'item')
        {
          currentItem = pr1;
          if(currentItem != previousItem)
          {
            previousItem = currentItem;
            beginTime = 0;
            endTime   = 0;
            setTimeout("getItemData(currentPlayer, currentItem)", 100);
          }
        }
      };

      function getItemData(swf, idx)
      {
        var obj = thisMovie(swf).itemData(idx);
        loadXMLDoc(obj['description']);
      };

      function thisMovie(movieName)
      {
        return document.getElementsByName(movieName)[0];
      };
    </script>

Ok, thanks!

It's me again. I just wanted to know what this variable means:

  
<strong>var attributes</strong> =
        {
          id:                                'mpl',
          name:                              'mpl'
        };

        swfobject.embedSWF('mediaplayer.swf', 'player', '640', '480', '7.0.0', false, flashvars, params, attributes);

      };

Thanks again

See: Google "Adobe Flash Player object embed"

First result: http://kb.adobe.com/selfservice/viewContent.do?externalId=tn_12701

id is the id of the HTML object element that holds the data elements for the Flash movie
name is the name of the HTML embed HTML that holds the data elements for the Flash movie

Thanks

Hi AJAX, I think there's a problem with the duration of the caption, they disappear earlier than they should...Why?

-EDIT-
I found my error i missed these 2 lines
beginTime = Captions[i]['begin'];
endTime = Captions[i]['end'];

@AJAX, I want to show my playlist with this code do you know why it doesn't show?

Also, I found a bug in the script, where it says:

<strong>if(currentPosition > endTime)</strong>
          {
            // clear current caption
            cA = document.getElementById('captions');
            cA.innerHTML = '';
            cA = document.getElementById('captions1');
            cA.innerHTML = '';
            cA = document.getElementById('captions2');
            cA.innerHTML = '';
            cA = document.getElementById('captions3');
            cA.innerHTML = '';
            cA = document.getElementById('captions4');
            cA.innerHTML = '';

When you seek back (rewind) the caption field doesn't clear itself. So if we add :
if(currentPosition > endTime || <strong>currentPosition < beginTime</strong> )

it works!

Hope I helped!

newbie87

Playlist displays below the player if height is greater than displayheight.

Control bar is 20px.

Playlist tracks w/o thumbs are 23px .

Playlist tracks w/thumbs are 41px.

Height = 592 (change two places) = 4 tracks w/o thumbs.
Height = 664 (change two places) = 4 tracks w/thumbs.

vidios

Still don't have the new JW Player? Get It Here