index.html 9.83 KB
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>video.js HLS Stats</title>

  <link href="../../node_modules/video.js/dist/video-js.css" rel="stylesheet">

  <!-- video.js -->
  <script src="../../node_modules/video.js/dist/video.js"></script>

  <!-- HLS plugin -->
  <script src="../../dist/videojs-contrib-hls.js"></script>

  <!-- player stats visualization -->
  <link href="stats.css" rel="stylesheet">
  <script src="/node_modules/d3/d3.min.js"></script>

  <style>
    body {
      font-family: Arial, sans-serif;
      margin: 20px;
    }
    .info {
      background-color: #eee;
      border: thin solid #333;
      border-radius: 3px;
      padding: 0 5px;
      margin: 20px 0;
    }
    input {
      margin-top: 15px;
      min-width: 450px;
      padding: 5px;
    }
  </style>

</head>
<body>
  <div class="info">
    <p>The video below is an <a href="https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008332-CH1-SW1">HTTP Live Stream</a>. On desktop browsers other than Safari, the HLS plugin will polyfill support for the format on top of the video.js Flash tech.</p>
    <p>Due to security restrictions in Flash, you will have to load this page over HTTP(S) to see the example in action.</p>
  </div>

  <div id="fixture">
  </div>

  <form id="load-url">
    <label>
      URL to Load:
      <input id="url-to-load" type="url" value="https://s3.amazonaws.com/_bc_dml/example-content/bipbop-advanced/bipbop_16x9_variant.m3u8">
    </label>
    <br>
    <label>
      Player Options:
      <input id="player-options" type="text" value='{}'>
    </label>
    <br>
    <label>
      URL Content Type:
      <input id="url-content-type" type="text" value="application/x-mpegURL">
    </label>
    <br>
    <label>
      Default Caption Track Label (blank for none):
      <input id="caption-track" type="text" value="">
    </label>
    <br>
    <label>
      Technology Mode:
      <input type="radio" name="technology-mode" value="auto" checked> Auto
      <input type="radio" name="technology-mode" value="html"> HTML
      <input type="radio" name="technology-mode" value="flash"> Flash
    </label>
    <br>
    <label>
      Autoplay:
      <input type="radio" name="autoplay" value="on"> On
      <input type="radio" name="autoplay" value="off" checked> Off
    </label>

    <br>
    <br>
    <button type="submit">Load</button>
  </form>
  <br>

  <section class="stats">
    <div class="player-stats">
      <h2>Player Stats</h2>
      <dl>
        <dt>Current Time:</dt>
        <dd class="current-time-stat">0</dd>
        <dt>Buffered:</dt>
        <dd class="buffered-stat">-</dd>
        <dt>Seekable:</dt>
        <dd><span class="seekable-start-stat">-</span> - <span class="seekable-end-stat">-</span></dd>
        <dt>Video Bitrate:</dt>
        <dd class="video-bitrate-stat">0 kbps</dd>
        <dt>Measured Bitrate:</dt>
        <dd class="measured-bitrate-stat">0 kbps</dd>
      </dl>
    </div>
    <div class="event-counts">
      <h2>Event Counts</h2>
      <dl>
        <dt>Play:</dt>
        <dd class="play-count">0</dd>
        <dt>Playing:</dt>
        <dd class="playing-count">0</dd>
        <dt>Seeking:</dt>
        <dd class="seeking-count">0</dd>
        <dt>Seeked:</dt>
        <dd class="seeked-count">0</dd>
      </dl>
    </div>
    <h3 class="bitrate-switching">Bitrate Switching</h3>
    <div class="switching-stats">
      Once the player begins loading, you'll see information about the
      operation of the adaptive quality switching here.
    </div>
    <h3>Timed Metadata</h3>
    <div class="segment-timeline"></div>
  </section>

  <script src="stats.js"></script>
  <script>
    function getCheckedValue(name) {
      var radios = document.getElementsByName(name);
      var value;
      for (var i = 0, length = radios.length; i < length; i++) {
        if (radios[i].checked) {
          value = radios[i].value;
          break;
        }
      }
      return value;
    }

    function createPlayer(cb) {
      if (window.stats_timer) {
        clearInterval(window.stats_timer);
      }
      // dispose of existing player
      if(window.player) {
        window.player.dispose();
      }

      // create video element in the dom
      var video = document.createElement('video');
      video.id = 'videojs-contrib-hls-player';
      video.className = 'video-js vjs-default-skin';
      video.setAttribute('controls', true);
      video.setAttribute('height', 300);
      video.setAttribute('width', 600);
      document.querySelector('#fixture').appendChild(video);
      var techRadios = document.getElementsByName('technology-mode');
      var techMode = getCheckedValue('technology-mode') || 'auto';
      var autoplay = getCheckedValue('autoplay') || 'off';
      var captionTrack = document.getElementById('caption-track').value || "";
      var url = document.getElementById('url-to-load').value || "";
      var options = {};
      // try to parse options from the form
      try {
        options = JSON.parse(document.getElementById('player-options').value);
      } catch(err) {
        console.log("Reseting options to {}, JSON Parse Error:", err);
      }
      if(typeof options.techOrder === 'undefined') {
        if (techMode === 'html') {
          options.techOrder = ['html5'];
        } else if (techMode === 'flash') {
          options.techOrder = ['flash'];
        }
      }
      if(typeof options.autoplay === 'undefined') {
        if (autoplay === 'on') {
          options.autoplay = true;
        } else if (techMode === 'off') {
          options.autoplay = false;
        }
      }
      var type = document.getElementById('url-content-type').value;
      // use the form data to add a src to the player
      try {
        window.player = videojs(video.id, options);
        if(captionTrack) {
          // hackey way to show captions
          window.player.on('loadeddata', function(event) {
            textTracks = this.textTracks().tracks_;
            for(var i = 0; i < textTracks.length; i++) {
              if(textTracks[i].label === captionTrack) {
                console.log("Found matching track, going to show " + captionTrack);
                textTracks[i].mode = "showing";
                break;
              }
            }
          });
        }
        window.player.src({
          src: url,
          type: type
        });

        cb(player);
      } catch(err) {
        console.log("caught an error trying to create and add src to player:", err);
      }
    }

    function setup(player) {
      player.ready(function() {

        // ------------
        // Player Stats
        // ------------

        var currentTimeStat = document.querySelector('.current-time-stat');
        var bufferedStat = document.querySelector('.buffered-stat');
        var seekableStartStat = document.querySelector('.seekable-start-stat');
        var seekableEndStat = document.querySelector('.seekable-end-stat');
        var videoBitrateState = document.querySelector('.video-bitrate-stat');
        var measuredBitrateStat = document.querySelector('.measured-bitrate-stat');

        player.on('timeupdate', function() {
          currentTimeStat.textContent = player.currentTime().toFixed(1);
        });

        window.stats_timer = window.setInterval(function() {
          var bufferedText = '', oldStart, oldEnd, i;

          // buffered
          var buffered = player.buffered();
          if (buffered.length) {
            bufferedText += buffered.start(0) + ' - ' + buffered.end(0);
          }
          for (i = 1; i < buffered.length; i++) {
            bufferedText += ', ' + buffered.start(i) + ' - ' + buffered.end(i);
          }
          bufferedStat.textContent = bufferedText;

          // seekable
          var seekable = player.seekable();
          if (seekable && seekable.length) {

            oldStart = seekableStartStat.textContent;
            if (seekable.start(0).toFixed(1) !== oldStart) {
              seekableStartStat.textContent = seekable.start(0).toFixed(1);
            }
            oldEnd = seekableEndStat.textContent;
            if (seekable.end(0).toFixed(1) !== oldEnd) {
              seekableEndStat.textContent = seekable.end(0).toFixed(1);
            }
          }

          // bitrates
          var playlist = player.tech_.hls.playlists.media();
          if (playlist && playlist.attributes && playlist.attributes.BANDWIDTH) {
            videoBitrateState.textContent = (playlist.attributes.BANDWIDTH / 1024).toLocaleString(undefined, {
              maximumFractionDigits: 1
            }) + ' kbps';
          }
          if (player.tech_.hls.bandwidth) {
            measuredBitrateStat.textContent = (player.tech_.hls.bandwidth / 1024).toLocaleString(undefined, {
              maximumFractionDigits: 1
            }) + ' kbps';
          }
        }, 1000);

        var trackEventCount = function(eventName, selector) {
          var count = 0, element = document.querySelector(selector);
          player.on(eventName, function() {
            count++;
            element.innerHTML = count;
          });
        };
        trackEventCount('play', '.play-count');
        trackEventCount('playing', '.playing-count');
        trackEventCount('seeking', '.seeking-count');
        trackEventCount('seeked', '.seeked-count');

        videojs.Hls.displayStats(document.querySelector('.switching-stats'), player);
        videojs.Hls.displayCues(document.querySelector('.segment-timeline'), player);
      });
    }

    (function(window, videojs) {
      videojs.options.flash.swf = '../../node_modules/videojs-swf/dist/video-js.swf';
      createPlayer(setup);
      // hook up the video switcher
      document.getElementById('load-url').addEventListener('submit', function(event) {
        event.preventDefault();
        createPlayer(setup);
        return false;
      });
    }(window, window.videojs));
  </script>
</body>
</html>