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

3

36

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.

Charbonnier answered 8/6, 2014 at 1:2 Comment(9)
your code has a typo above. $('video) <-- missing 'Aquiculture
Try watching for 'updateend' instead of 'update', and add some logs for 'error', 'sourceended' and 'sourceclose' events so you know when it's failing. If it's right after the very first .appendBuffer(), it might be something with the video file itself.Corpuz
Oh, and 'error' is on the buffer, the source ones are on the mediaSource. See the events for each listed on the w3 siteCorpuz
Thanks @AdamHart. Added some additional logging. Calling the second append in 'updateend' also errored, saying that the buffer had already closed.Charbonnier
It looks like you have a race condition with the websocket and the mediaSource, and you might be trying to append before it's open or appending out of order (though I'm still not clear if that's ok or not for mediaSource). You can check if (buffer.updating || mediaSource.readyState != "open" || queue.length > 0) to make sure it's not updating, is open, and doesn't already have a queue lined up before appending directly from the websocket. Also, if possible, be sure you're using a video that you know works with mediaSource, like this one.Corpuz
Awesome! I think some packets were being added out-of-order: checking for queue.length > 0 seems to have helped. Also, it looks like only H264-DASH is support - so I've switch for VP8 and am seeing some frames now. Getting an abort message now consistently after 5-6 frames though. (updatestart, abort, updateend, sourceclose).Charbonnier
I'm getting an error: Media segment did not begin with keyframe. in chrome://media-internals/ which I think is causing this secondary issue. Have updated my question with all the latest code/details.Charbonnier
Have opened a second question for the secondary issue: #24153310. Thanks @AdamHart for suggesting queue.length > 0 and pointing out that the file may not be MediaSource compatible.Charbonnier
@ChrisNolet I'm now facing the same problem! Could you please give the correct code?Puglia
C
16

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

Charbonnier answered 11/6, 2014 at 0:34 Comment(11)
Hi, I see that you seems to be the only one so far who tried to do exactly what I'm trying to do now: Stream low latency live H264 video through web sockets, into html5 video using the media source API. Is there any chance you can share what was your final solution regarding what kind of data and format you feed the media source with? I'm stuck at the same place you were: wanting to stream raw H264 but it doesn't look like media source api will accept it). I'd really appreciate any help. Thanks.Submultiple
Hi! I ended up switching to VP8 for the proof of concept. Here's how I got VP8 working: #24153310. Once I was streaming VP8 instead of h264, I used the exact same code that is posted in the question, except with buffer = mediaSource.addSourceBuffer('video/webm; codecs="vp8"') instead of video/mp4.Charbonnier
It's worth noting that I have already applied the edits mentioned in the Answer here to the code in the Question, so the only issue with the code in the Question is that the codec is wrong.Charbonnier
Hi, thanks for the response. I understand that you used vp8 with key-frames only, for the sake of a PoC. I guess you never got back to playing live low-latency H264, or finding a better solution than encoding everything as key-frames. I'll keep investigating and post my findings in case someone finds it relevantSubmultiple
That's correct - I finished at the Proof of Concept. Hopefully one day ffmpeg will add support for MPEG-DASH. In the mean time, you might be able to pipe from ffmpeg to MP4Box to create an MPEG-DASH stream.Charbonnier
are you sure they don't support mp4/h264 format? my h264 streaming could work with previous version of chrome. but today I'm debugging the chromium 43, which shows this problem on the worked code.Schreibe
At the time of writing, Chrome supported MP4/h264 so long as it conformed to MPEG-DASH (jwplayer.com/html5/mediasource). If it worked for you previously, it might be another error in the stream which is causing the SourceBuffer to drop. (It is rather temperamental! It seems to drop when it encounters data it doesn't like).Charbonnier
The following 2 questions that I asked and their respones + comments will show how to do relatively low latency live h264 streaming: #30618173 & #30869354Submultiple
@ChrisNolet it was a long time, i hope you still remember something about this, i tried your code and send two buffers , the first one get played but as soon as the second buffer comes the player play it before the first one get finished ( every chunk is 30 seconds ), does that happened with you back then ? thanksTeratoid
Interesting... In that situation, I would expect the second chunk to get added to the queue. Perhaps try logging the state of buffer.updating when the websocket message comes in, and make sure that the second chunk is indeed being added to the queue. (appendBuffer should only ever be called when buffer.updating == false.)Charbonnier
Will add that VP9 also works currently (oct 9 2019).Bicyclic
D
4

Chrome is unbelievably picky when it comes to SourceBuffer codecs. Worse, it will return useless and misleading error messages as in case of the OP.

It looks like the MediaSource is closing immediately after the call to buffer.appendData()

That's exactly the case: Chrome is not happy with the video as it somehow doesn't exactly conform to its likings and simply closes the source buffer without notice.

Possible solutions to look for:

  1. Transcode your MP4 files using ffmpeg, explained here (look for the Heading "Fragmenting").
  2. Use mp4file of mp4v2 to find out the exact MP4 codec, explained here and here.
  3. If you don't have audio, omit the audio codec part: e.g. 'video/mp4; codecs="avc1.64001F"' instead of 'video/mp4; codecs="avc1.64001F, mp4a.40.2"'.
Diagnostic answered 11/3, 2020 at 19:36 Comment(0)
M
1

check it before

const mimeType = 'video/mp4; codecs="avc1.64001E"';
if (MediaSource.isTypeSupported(mimeType)) {
  console.info('Mimetype is', mimeType);
  // TODO ...
} else {
  console.error('Mimetype not supported', mimeType);
}
Maris answered 2/6, 2022 at 8:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.