diff --git a/src/master-playlist-controller.js b/src/master-playlist-controller.js index 977c801c0..b1dc03b12 100644 --- a/src/master-playlist-controller.js +++ b/src/master-playlist-controller.js @@ -623,10 +623,11 @@ export class MasterPlaylistController extends videojs.EventTarget { if (this.mediaTypes_.AUDIO.activePlaylistLoader) { // if the audio playlist loader exists, then alternate audio is active - if (this.mainSegmentLoader_.startingMedia_ && + if (!this.mainSegmentLoader_.startingMedia_ || this.mainSegmentLoader_.startingMedia_.containsVideo) { - // if the main segment loader contains video, then we need to wait for both the - // main and audio segment loaders to call endOfStream + // if we do not know if the main segment loader contains video yet or if we + // definitively know the main segment loader contains video, then we need to wait + // for both main and audio segment loaders to call endOfStream isEndOfStream = isEndOfStream && this.audioSegmentLoader_.ended_; } else { // otherwise just rely on the audio loader diff --git a/test/master-playlist-controller.test.js b/test/master-playlist-controller.test.js index 032eedbfa..6da48882b 100644 --- a/test/master-playlist-controller.test.js +++ b/test/master-playlist-controller.test.js @@ -589,7 +589,7 @@ function(assert) { MPC.mediaSource.sourceBuffers[0].trigger('updateend'); - assert.equal(videoEnded, 1, 'main segment loader triggered endded'); + assert.equal(videoEnded, 1, 'main segment loader triggered ended'); assert.equal(audioEnded, 0, 'audio segment loader did not trigger ended'); assert.equal(MPC.mediaSource.readyState, 'open', 'Media Source not yet ended'); @@ -603,6 +603,70 @@ function(assert) { assert.equal(MPC.mediaSource.readyState, 'ended', 'Media Source ended'); }); +QUnit.test('waits for both main and audio loaders to finish before calling endOfStream' + +' if main loader starting media is unknown', function(assert) { + openMediaSource(this.player, this.clock); + + const videoMedia = '#EXTM3U\n' + + '#EXT-X-VERSION:3\n' + + '#EXT-X-PLAYLIST-TYPE:VOD\n' + + '#EXT-X-MEDIA-SEQUENCE:0\n' + + '#EXT-X-TARGETDURATION:10\n' + + '#EXTINF:10,\n' + + 'video-0.ts\n' + + '#EXT-X-ENDLIST\n'; + + const audioMedia = '#EXTM3U\n' + + '#EXT-X-VERSION:3\n' + + '#EXT-X-PLAYLIST-TYPE:VOD\n' + + '#EXT-X-MEDIA-SEQUENCE:0\n' + + '#EXT-X-TARGETDURATION:10\n' + + '#EXTINF:10,\n' + + 'audio-0.ts\n' + + '#EXT-X-ENDLIST\n'; + + let videoEnded = 0; + let audioEnded = 0; + + const MPC = this.masterPlaylistController; + + MPC.mainSegmentLoader_.on('ended', () => videoEnded++); + MPC.audioSegmentLoader_.on('ended', () => audioEnded++); + + MPC.audioSegmentLoader_.startingMedia_ = { containsAudio: true }; + + // master + this.standardXHRResponse(this.requests.shift(), manifests.demuxed); + + // video media + this.standardXHRResponse(this.requests.shift(), videoMedia); + + // audio media + this.standardXHRResponse(this.requests.shift(), audioMedia); + + // this.requests === [videoSegment, audioSegment] + + // audio segment + this.standardXHRResponse(this.requests[1]); + // audio source buffer + MPC.mediaSource.sourceBuffers[1].trigger('updateend'); + + assert.equal(videoEnded, 0, 'main segment loader did not trigger ended'); + assert.equal(audioEnded, 1, 'audio segment loader triggered ended'); + assert.equal(MPC.mediaSource.readyState, 'open', 'Media Source not yet ended'); + + // video segment + this.standardXHRResponse(this.requests[0]); + + MPC.mainSegmentLoader_.startingMedia_ = { containsVideo: true }; + // main source buffer + MPC.mediaSource.sourceBuffers[0].trigger('updateend'); + + assert.equal(videoEnded, 1, 'main segment loader triggered ended'); + assert.equal(audioEnded, 1, 'audio segment loader did not trigger ended again'); + assert.equal(MPC.mediaSource.readyState, 'ended', 'Media Source ended'); +}); + QUnit.test('does not wait for main loader to finish before calling endOfStream with' + ' audio only stream and alternate audio active', function(assert) { openMediaSource(this.player, this.clock);