Search code examples
amazon-s3video-streaminghtml5-videoblobm3u8

URL.createObjectURL(mediaSource) - play back video from URL - MOOV atom - Elastic Transcoder


I am trying to play back a video (currently hosted on S3 with public access) by creating a blob URL.

I have used Elastic Transcoder to encode the video since it is supposed to set the MOOV atom to the top (beginning).

I am unable to get the code to work but also found a working example: link here

Here is my code:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8"/>
  </head>
  <body>
    <video controls></video>
    <script>
      var video = document.querySelector('video');
      var assetURL = 'https://ovation-blob-url-test.s3.amazonaws.com/AdobeStock_116640093_Video_WM_NEW.mp4';
      // Need to be specific for Blink regarding codecs
      // ./mp4info frag_bunny.mp4 | grep Codec
      var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
      if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
        var mediaSource = new MediaSource;
        //console.log(mediaSource.readyState); // closed
        video.src = URL.createObjectURL(mediaSource);
        mediaSource.addEventListener('sourceopen', sourceOpen);
      } else {
        console.error('Unsupported MIME type or codec: ', mimeCodec);
      }
      function sourceOpen (_) {
        //console.log(this.readyState); // open
        var mediaSource = this;
        var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
        fetchAB(assetURL, function (buf) {
          sourceBuffer.addEventListener('updateend', function (_) {
            mediaSource.endOfStream();
            video.play();
            //console.log(mediaSource.readyState); // ended
          });
          sourceBuffer.appendBuffer(buf);
        });
      };
      function fetchAB (url, cb) {
        console.log(url);
        var xhr = new XMLHttpRequest;
        xhr.open('get', url);
        xhr.responseType = 'arraybuffer';
        xhr.onload = function () {
          cb(xhr.response);
        };
        xhr.send();
      };
    </script>
  </body>
</html>

What am I doing wrong? I looked at tools ie.e MP4Box or QT-FastStart but they seem to be kind of old school. I would also be willing to change from MP4 to M3U8 playlist but then I don't know what MIME types to use.

At the ned of the day I am trying to play back a video/stream and hide the URL (origin) potentially using blob.

Thank you guys!


Solution

  • So, first, even though this code seems to be taken from mozilla documentation site, there are a few issues - you are not checking the readyState before calling endOfStream thus the error you get is valid, secondly, the play() call is blocked by the autoplay policy changes. If you add an error handler, you will actually see that the appendBuffer fails. Here is the updated snippet:

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8"/>
      </head>
      <body>
        <video controls></video>
        <script>
          var video = document.querySelector('video');
          var assetURL = 'https://ovation-blob-url-test.s3.amazonaws.com/AdobeStock_116640093_Video_WM_NEW.mp4';
          // Need to be specific for Blink regarding codecs
          // ./mp4info frag_bunny.mp4 | grep Codec
          var mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
          if ('MediaSource' in window && MediaSource.isTypeSupported(mimeCodec)) {
            var mediaSource = new MediaSource;
            //console.log(mediaSource.readyState); // closed
            video.src = URL.createObjectURL(mediaSource);
            mediaSource.addEventListener('sourceopen', sourceOpen);
          } else {
            console.error('Unsupported MIME type or codec: ', mimeCodec);
          }
          function sourceOpen (_) {
            //console.log(this.readyState); // open
            var mediaSource = this;
            var sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
            fetchAB(assetURL, function (buf) {
              sourceBuffer.addEventListener('updateend', function (_) {
                // console.log(mediaSource.readyState); // ended
                if (mediaSource.readyState === "open") {
                    mediaSource.endOfStream();
                    video.play();
                }
              });
              sourceBuffer.addEventListener('error', function (event) {
                console.log('an error encountered while trying to append buffer');
              });
              sourceBuffer.appendBuffer(buf);
            });
          };
          function fetchAB (url, cb) {
            console.log(url);
            var xhr = new XMLHttpRequest;
            xhr.open('get', url);
            xhr.responseType = 'arraybuffer';
            xhr.onload = function () {
              cb(xhr.response);
            };
            xhr.send();
          };
        </script>
      </body>
    </html>
    

    So lets advance to next issue - the actual error. So, using chrome://media-internals/ we can see that the video actually fails to load do to incompatibility with the ISOBMFF format:

    Chrome media internals error

    I am not familiar with Elastic Transcoder, but it seems that is it not producing an mp4 file suitable for live streaming. Also, if using mse, putting moov at the beginning is not enough, the video actually has to meet all of the ISOBMFF requirements - see chapters 3. and 4.

    The working sample you mentioned is not a valid comparison since it uses the blob for the src, where the ISOBMFF rules do not apply. If it is fine for you to go that way, don't use MSE and put the blob directly in the src. If you need MSE, you have to mux it correctly.