Update the document in sync with a media playing

Mixing JSON cue content with track and cue events, makes the synchronization of elements in the HTML document (while the video is playing) much easier.

Example of track event listeners that use JSON cue contents

Here is a small code extract that shows how we can capture the JSON content of a cue when the video reaches its start time. We do this within a cuechange listener attached to a TextTrack:

  1. textTrack.oncuechange = function (){
  2.     // “this” is the textTrack that fired the event.
  3.     // Let’s get the first active cue for this time segment
  4.     var cue = this.activeCues[0]; 
  5.     var obj = JSON.parse(cue.text);
  6.     // do something
  7. }

Here is a very impressive demo by Sam Dutton that uses JSON cues containing the latitude and longitude of the camera used for filming the video, to synchronize two map views: every time the active cue changes, the Google map and equivalent Google street view are updated.

Video synced with google map and google street map

Example of a cue content from this demonstration:

  1. {“lat”:37.4219276, “lng”:-122.088218, “t”:1331363000}

Cue events and cue content:

We can acquire a cue DOM object using the techniques we have seen previously, or by using the new HTML5 TextTrack getCueById() method. 

  1. var videoElement = document.querySelector(“#myvideo”);
  2. var textTracks = videoElement.textTracks; // one for each track element
  3. var textTrack = textTracks[0]; // corresponds to the first track element
  4. // Get a cue with ID=”wikipedia”
  5. var cue = textTrack.getCueById(“Wikipedia”); 

And once we have a cue object, it is possible to add event listeners to it:

  1. cue.onenter = function(){
  2.    // display something, play a sound, update any DOM element…
  3. };
  4. cue.onexit = function(){
  5.    // do something else
  6. };

If the getCueById method is not implemented (this is the case in many browsers, as at November 2015), we use the polyfill presented in the previous section:

  1.  // for browsers that do not implement the getCueById() method
  2.  // let’s assume we’re adding the getCueById function to a TextTrack object
  3.  //named “track”
  4. if (typeof track.getCueById !== “function”) {
  5.    track.getCueById = function(id) {
  6.      var cues = track.cues;
  7.      for (var i = 0; i != track.cues.length; ++i) {
  8.        if (cues[i].id === id) {
  9.          return cues[i];
  10.        }
  11.      }
  12.   };
  13.  }

Example that displays a wikipedia page and a google map while a video is playing

Try the example at JSBin

video synced with an iframe that shows external URLs and with a google map

HTML code extract:

  1. <!DOCTYPE html>
  2. <html lang=”en”>
  3. <head>
  4. <meta charset=”utf-8″>
  5. <title>Example syncing element of the document with video metadata in webVTT file</title>
  6. </head>
  7. <body >
  8. <main>
  9. <video id=”myVideo” controls crossorigin=”anonymous” >
  10.    <source src=”https://mainline.i3s.unice.fr/mooc/samuraiPizzacat.mp4&#8243;
  11.            type=”video/mp4″>
  12.    …
  13.    </source>
  14. <track label=”urls track”
  15.         src=”https://&#8230;../SamuraiPizzaCat-metadata.vtt”
  16.         kind=”metadata” >
  17.  </track>
  18. </video>
  19.    
  20. </main>
  21.  
  22. <aside>
  23.      
  24. </aside>
  25. <h3>Wikipedia URL: <span id=”currentURL”> Non défini </span></h3>
  26.  

JavaScript code:

  1. window.onload = function() {
  2.     var videoElement = document.querySelector(“#myVideo”);
  3.     var myIFrame = document.querySelector(“#myIframe”);
  4.     var currentURLSpan = document.querySelector(“#currentURL”);
  5.     var textTracks = videoElement.textTracks; // one for each track element
  6.     var textTrack = textTracks[0]; // corresponds to the first track element
  7.    
  8.     // change mode so we can use the track
  9.     textTrack.mode = “hidden”;
  10.     // Default position on the google map
  11.     var centerpos = new google.maps.LatLng(48.579400,7.7519);
  12.  
  13.     // default options for the google map
  14.     var optionsGmaps = {
  15.        center:centerpos,
  16.        navigationControlOptions: {style:
  17.                  google.maps.NavigationControlStyle.SMALL},
  18.        mapTypeId: google.maps.MapTypeId.ROADMAP,
  19.        zoom: 15
  20.     };
  21.  
  22.     // Init map object
  23.     var map = new google.maps.Map(document.getElementById(“map”),
  24.                                   optionsGmaps);
  25.  
  26.     // cue change listener, this is where the synchronization between
  27.     // the HTML document and the video is done
  28.     textTrack.oncuechange = function (){
  29.        // we assume that we have no overlapping cues
  30.        var cue = this.activeCues[0];
  31.        if(cue === undefined) return;
  32.        // get cue content as a JavaScript object
  33.        var cueContentJSON = JSON.parse(cue.text);
  34.        // do different things depending on the type of sync (wikipedia, gmap)
  35.        switch(cueContentJSON.type) {
  36.          case’WikipediaPage’:
  37.             var myURL = cueContentJSON.url;
  38.             var myLink = “<a href=\”” + myURL + “\”>” + myURL + “</a>”;
  39.             currentURLSpan.innerHTML = myLink;
  40.             myIFrame.src = myURL; // assign url to src property
  41.             break;
  42.          case ‘LongLat’:
  43.             drawPosition(cueContentJSON.long, cueContentJSON.lat);
  44.             break;
  45.        }
  46.    };
  47.  
  48.    function drawPosition(long, lat) {
  49.       // Make new object LatLng for Google Maps
  50.       var latlng = new google.maps.LatLng(lat, long);
  51.  
  52.       // Add a marker at position
  53.       var marker = new google.maps.Marker({
  54.           position: latlng,
  55.           map: map,
  56.           title:”You are here”
  57.       });
  58.       // center map on longitude and latitude
  59.       map.panTo(latlng);
  60.    }
  61. };

All the critical work is done by the cuechange event listener, lines 27-50. We have only the one track, so we set its mode to “hidden” (line 10)  in order to be sure that it will be loaded, and that playing the video will fire cuechange events on it. The rest is just Google map code and classic DOM manipulation for updating HTML content (a span that will display the current URL, line 42).

Leave a comment