http-streaming icon indicating copy to clipboard operation
http-streaming copied to clipboard

Uncaught exception at 'addSourceBuffer' when changing source or disposing/re-creating video

Open carlosllera opened this issue 7 years ago • 11 comments

Description

In our scenario, we need to dynamically create a video, change its source several times within few seconds (user will be previewing videos), and dispose and re-create it at some point.

Using our video source files, when in the middle of a video, we change the video source through videojs().src function, an Uncaught DOMException: Failed to execute 'addSourceBuffer' on 'MediaSource': This MediaSource has reached the limit of SourceBuffer objects it can handle. is received.

Here is a reduced test case on jsbin: http://jsbin.com/tojewagula/edit?html,console,output

Here are the source files you can download and check (npm install and npm start should be enough): https://drive.google.com/file/d/0B-Ny3L4qtHdUXzRaZlltWjF0YXM/view?usp=sharing

Here are some example M3U8 URLs we are using: https://scdn.golden-race.net/hlsvideos/dog6/d6-5-4-3-1437.m3u8?st=JMUY2EVWtAHdK-GDFcNyMw&e=1485420418 https://scdn.golden-race.net/hlsvideos/sp/s4-4-3-054.m3u8?st=BvMPTBAMEamHQICmFvEoJg&e=1485424078 https://scdn.golden-race.net/hlsvideos/mt/mt-5-4-1-0229.m3u8?st=yYu5J6ml4wkTdgSrbAkY9Q&e=1485424498

Sources

We realized that inserting a debug point in the function createRealSourceBuffers_() provoked that this exception is never thrown, so we just inserted a simple wait of just before the buffer is created with _this2.mediaSource_.nativeMediaSource_.addSourceBuffer(...), and it's working almost all the times (we still get the exception sometimes on extreme conditions).

Steps to reproduce

Using the given jsbin example:

  1. Choose one of our URLs, paste it in the text input, and press Load. In the browser console you can check that the video is created with the given url and it starts playing.
  2. After waiting a couple of seconds, press Load again with the same video or choose a different one (may have to do that several times until the error is found).
  3. Encounter the error.

Note: Can be also discovered even though video is disposed.

Results

Expected

Video should be working correctly, even though is Loaded several times and/or disposed.

Error output

An unexpected error is found and video looses sound.

Additional Information

We were're NOT able to reproduce this issue with a couple of example videos found on the internet ( e.g. https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8 ), but trying to figure out what were the differences between both videos, we found that they're almost the same: same enconding, videos with different resolutions and several audio streams... It's driving us crazy!

videojs-contrib-hls version

videojs-contrib-hls 4.1.0

videojs version

video.js 5.15.1

Browsers

All

Platforms

All

Other Plugins

No other plugins are being used.

Other JavaScript

jQuery 1.8.0

carlosllera avatar Jan 19 '17 17:01 carlosllera

Hello,

Any news on this?

Taking a look at the video-contrib-hls.js class, we did some logs around the line that is causing the trouble ( buffer = _this2.mediaSource_.nativeMediaSource_.addSourceBuffer(type + '/mp4;codecs="' + _this2[type + 'Codec_'] + '"'); ) and we found that there seems to be an active source buffer in the nativeMediaSource_ object that is not being destroyed on time, and it seems to be the audio buffer...

If in the function createRealSourceBuffers_(), at the point where the buffer is either recovered from the mediaSource_[] array or created by the previously mentioned addSourceBuffer() function, if we insert a little delay (~100ms) before its creation we managed to avoid the error (but not every time). We tried to look for a way to correctly free the buffers but we couldn't. Disposing the videojs object doesn't resolve the problem.

Here is our modification of the createRealSourceBuffers_() function:

  }, {
    key: 'createRealSourceBuffers_',
    value: function createRealSourceBuffers_() {
      var _this2 = this;

      var types = ['audio', 'video'];

      types.forEach(function (type) {
        // ...
		
        // If the mediasource already has a SourceBuffer for the codec
        // use that
        if (_this2.mediaSource_[type + 'Buffer_']) {
          buffer = _this2.mediaSource_[type + 'Buffer_'];
        } else {
			
			// JUST WAIT FOR A LITTLE ---------------------------
			function delayTime(delay) {
				var start = new Date().getTime();
				while (new Date().getTime() < start + delay);
			}
			delayTime(75);
			// --------------------------------------------------
			
			buffer = _this2.mediaSource_.nativeMediaSource_.addSourceBuffer(type + '/mp4;codecs="' + _this2[type + 'Codec_'] + '"');
			_this2.mediaSource_[type + 'Buffer_'] = buffer;
        }

        _this2[type + 'Buffer_'] = buffer;
		
	// ...

Any help will be very appreciated.

Thanks!

carlosllera avatar Jan 23 '17 18:01 carlosllera

Hey @carlosllera , sorry this ticket has gone a while without much attention. I noticed those sources were down, but tried a bunch of times in your example page and locally with the latest player with a source provided in https://github.com/videojs/videojs-contrib-hls/issues/1033 and couldn't reproduce the exact issue. I was only able to get: "Failed to execute 'appendBuffer' on 'SourceBuffer': This SourceBuffer has been removed from the parent media source." however the video still played. Are you still able to reproduce the issue?

gesinger avatar Mar 28 '17 23:03 gesinger

Hi @gesinger, I also get exactly this error. This error only occurs for HLS v4. If I understand correctly in this version of HLS has the ability to split video and audio stream. The problem related to the processing of the audio stream. In my case the plugin successfully processes the video stream, but the processing of the audio stream, the problem arises.

I used HLS http://cds.v8d5x6g5.hwcdn.net/00992/492441_24522882544b9d9636c1e92af1d6f533/492441.m3u8 to reproduce this error in this template. An error occurs after repeated attempt to play.

Thanks, Oleg

omakogon avatar Mar 29 '17 04:03 omakogon

Hello @gesinger ,

Thanks for your reply. I'm afraid we had to continue with the project and decided to move to other library, so now we're using HLS.js and we are not suffering that problem.

I hope you are able to solve the problem and everything goes alright. Great job guys.

carlosllera avatar Mar 29 '17 08:03 carlosllera

@gesinger Hey. We also see this issue and I can confirm it is related to a race-condition around creating native SourceBuffers.

The race becomes much more probably it seems when respective segments are cached, which leads them to "load" faster and thus creation sequence to happen in an undefined state somehow.

So if you make sure that caching is enabled (in dev tools mode you might need to untick the disable-cache box for that)

We are currently looking it how we could fix this properly. Any help or advice from videojs folks is greatly appreciated! :)

Otherwise, we can also confirm that this issue i.e the native DOM exception displayed does never happen with Hls.js or other libs.

tchakabam avatar Jan 26 '18 16:01 tchakabam

Here is a full log / stacktrace of the crash:

video.js:498 VIDEOJS: ERROR: DOMException: Failed to execute 'addSourceBuffer' on 'MediaSource': This MediaSource has reached the limit of SourceBuffer objects it can handle. No additional SourceBuffer objects may be added.
    at makeWrappedSourceBuffer (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:18987:34)
    at https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:19307:20
    at Array.forEach (<anonymous>)
    at VirtualSourceBuffer.createRealSourceBuffers_ (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:19274:13)
    at HtmlMediaSource.addSourceBuffer (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:18508:18)
    at createSourceBuffer (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:6960:41)
    at new SourceUpdater (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:6990:7)
    at SegmentLoader.init_ (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:5900:29)
    at SegmentLoader.load (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:5876:21)
    at PlaylistLoader.<anonymous> (https://pbm-dev1.media.compuccino.tv/vendor/videojs-contrib-hls/dist/videojs-contrib-hls.js:1633:38)
logByType @ video.js:498
videojs-contrib-hls.js:6144 Uncaught TypeError: Cannot read property 'updating' of null
    at SegmentLoader.fillBuffer_ (videojs-contrib-hls.js:6144)
    at SegmentLoader.monitorBufferTick_ (videojs-contrib-hls.js:6121)
videojs-contrib-hls.js:6144 Uncaught TypeError: Cannot read property 'updating' of null
    at SegmentLoader.fillBuffer_ (videojs-contrib-hls.js:6144)
    at SegmentLoader.monitorBufferTick_ (videojs-contrib-hls.js:6121)

tchakabam avatar Jan 26 '18 16:01 tchakabam

@gesinger Another update: It seems we found a fairly good way to prevent the race from within the SourceUpdater class constructor.

I'll post a patch in a bit :)

tchakabam avatar Jan 26 '18 16:01 tchakabam

Note: this seems to also happen with much higher probability when there audio stream is in a seperate rendition - as opposed to having a single segment-loader "main" type, with audio/video muxed into the same transport stream.

tchakabam avatar Jan 26 '18 16:01 tchakabam

Here you go: https://github.com/videojs/videojs-contrib-hls/pull/1330

tchakabam avatar Jan 29 '18 13:01 tchakabam

👋 Thanks for opening your first issue here! 👋

If you're reporting a 🐞 bug, please make sure you include steps to reproduce it. We get a lot of issues on this repo, so please be patient and we will get back to you as soon as we can. To help make it easier for us to investigate your issue, please follow the contributing guidelines.

welcome[bot] avatar Jan 11 '19 18:01 welcome[bot]

onsourceopen因为play()的原因会重复执行,sourceBuffer不要重复初始化, 判断mediaSource.sourceBuffers.length等于0的时候初始化!

var mediaSource = new MediaSource(); mediaSource.onsourceopen = function () { if (mediaSource.sourceBuffers.length == 0) { var sourceBuffer = mediaSource.addSourceBuffer(mimeType); map.set(msg.session, sourceBuffer); sourceBuffer.onupdateend = function () { mediaSource.endOfStream(); video[0].play(); }; sourceBuffer.appendBuffer(buffer); } }; video.attr("src", URL.createObjectURL(mediaSource));

redguardofchina avatar May 06 '20 02:05 redguardofchina