MediaSource error: This SourceBuffer has been removed from the parent media source

Chris Nolet picture Chris Nolet · Jun 8, 2014 · Viewed 23.3k times · Source

I'm experimenting with the new MediaSource API available in Chrome.

I'm trying to append binary data on the fly from a WebSocket to the video media source.

Starting with the example at https://html5-demos.appspot.com/static/media-source.html, my code is currently:

var websocket = new WebSocket('ws://localhost:8080');
websocket.binaryType = 'arraybuffer';

var mediaSource = new MediaSource();
var buffer;
var queue = [];

var video = $('.video')[0];
video.src = window.URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', function(e) {
  video.play();

  buffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.64001E"');

  buffer.addEventListener('updatestart', function(e) { console.log('updatestart: ' + mediaSource.readyState); });
  buffer.addEventListener('update', function(e) { console.log('update: ' + mediaSource.readyState); });
  buffer.addEventListener('updateend', function(e) { console.log('updateend: ' + mediaSource.readyState); });
  buffer.addEventListener('error', function(e) { console.log('error: ' + mediaSource.readyState); });
  buffer.addEventListener('abort', function(e) { console.log('abort: ' + mediaSource.readyState); });

  buffer.addEventListener('update', function() { // Note: Have tried 'updateend'
    if (queue.length > 0 && !buffer.updating) {
      buffer.appendBuffer(queue.shift());
    }
  });
}, false);

mediaSource.addEventListener('sourceopen', function(e) { console.log('sourceopen: ' + mediaSource.readyState); });
mediaSource.addEventListener('sourceended', function(e) { console.log('sourceended: ' + mediaSource.readyState); });
mediaSource.addEventListener('sourceclose', function(e) { console.log('sourceclose: ' + mediaSource.readyState); });
mediaSource.addEventListener('error', function(e) { console.log('error: ' + mediaSource.readyState); });

websocket.addEventListener('message', function(e) {
  if (typeof e.data !== 'string') {
    if (buffer.updating || queue.length > 0) {
      queue.push(e.data);
    } else {
      buffer.appendBuffer(e.data);
    }
  }
}, false);

I consistently get the error message: InvalidStateError: Failed to execute 'appendBuffer' on 'SourceBuffer': This SourceBuffer has been removed from the parent media source. after one append. It looks like the MediaSource is closing immediately after the call to buffer.appendData().

Any way to do this elegantly?

Note: chrome://media-internals/ doesn't return any useful information.

Answer

Chris Nolet picture Chris Nolet · Jun 11, 2014

Ultimately the issue was that I was sending h264 video down the websocket. The MediaSource API only supports MPEG-DASH and VP8 with keyframed segments currently (on Chrome 35).

Additionally, once I tried VP8, I saw that I was adding some frames out of order.

  • Adding if (buffer.updating || queue.length > 0) in websocket.onmessage was required.
  • Adding if (queue.length > 0 && !buffer.updating) in buffer.addEventListener('update', ...) was also required.

Note: I applied the edits mentioned here to the code in the question, so the only issue with the code in the question is that the codec is wrong