diff --git a/src/master-playlist-controller.js b/src/master-playlist-controller.js index 6fb766fb7..5fc84df40 100644 --- a/src/master-playlist-controller.js +++ b/src/master-playlist-controller.js @@ -686,34 +686,51 @@ export class MasterPlaylistController extends videojs.EventTarget { * player and video are loaded and initialized. */ setupFirstPlay() { - let seekable; let media = this.masterPlaylistLoader_.media(); - // check that everything is ready to begin buffering in the live - // scenario - // 1) the active media playlist is available - if (media && - // 2) the player is not paused - !this.tech_.paused() && - // 3) the player has not started playing - !this.hasPlayed_()) { - - // when the video is a live stream - if (!media.endList) { - this.trigger('firstplay'); - - // seek to the latest media position for live videos - seekable = this.seekable(); - if (seekable.length) { + // Check that everything is ready to begin buffering for the first call to play + // If 1) there is no active media + // 2) the player is paused + // 3) the first play has already been setup + // then exit early + if (!media || this.tech_.paused() || this.hasPlayed_()) { + return false; + } + + // when the video is a live stream + if (!media.endList) { + const seekable = this.seekable(); + + if (!seekable.length) { + // without a seekable range, the player cannot seek to begin buffering at the live + // point + return false; + } + + if (videojs.browser.IE_VERSION && + this.mode_ === 'html5' && + this.tech_.readyState() === 0) { + // IE11 throws an InvalidStateError if you try to set currentTime while the + // readyState is 0, so it must be delayed until the tech fires loadedmetadata. + this.tech_.one('loadedmetadata', () => { + this.trigger('firstplay'); this.tech_.setCurrentTime(seekable.end(0)); - } + this.hasPlayed_ = () => true; + }); + + return false; } - this.hasPlayed_ = () => true; - // now that we are ready, load the segment - this.load(); - return true; + + // trigger firstplay to inform the source handler to ignore the next seek event + this.trigger('firstplay'); + // seek to the live point + this.tech_.setCurrentTime(seekable.end(0)); } - return false; + + this.hasPlayed_ = () => true; + // we can begin loading now that everything is ready + this.load(); + return true; } /** @@ -893,14 +910,9 @@ export class MasterPlaylistController extends videojs.EventTarget { } // In flash playback, the segment loaders should be reset on every seek, even - // in buffer seeks - const isFlash = - (this.mode_ === 'flash') || - (this.mode_ === 'auto' && !videojs.MediaSource.supportsNativeMediaSources()); - - // if the seek location is already buffered, continue buffering as + // in buffer seeks. If the seek location is already buffered, continue buffering as // usual - if (buffered && buffered.length && !isFlash) { + if (buffered && buffered.length && this.mode_ !== 'flash') { return currentTime; } diff --git a/test/videojs-contrib-hls.test.js b/test/videojs-contrib-hls.test.js index 2124ee81f..6ff3724b2 100644 --- a/test/videojs-contrib-hls.test.js +++ b/test/videojs-contrib-hls.test.js @@ -275,7 +275,37 @@ QUnit.test('autoplay seeks to the live point after media source open', function( assert.notEqual(currentTime, 0, 'seeked on autoplay'); }); -QUnit.test('duration is set when the source opens after the playlist is loaded', function(assert) { +QUnit.test('autoplay seeks to the live point after tech fires loadedmetadata in ie11', +function(assert) { + videojs.browser.IE_VERSION = 11; + let currentTime = 0; + + this.player.autoplay(true); + this.player.on('seeking', () => { + currentTime = this.player.currentTime(); + }); + this.player.src({ + src: 'liveStart30sBefore.m3u8', + type: 'application/vnd.apple.mpegurl' + }); + + this.clock.tick(1); + + openMediaSource(this.player, this.clock); + this.player.tech_.trigger('play'); + this.standardXHRResponse(this.requests.shift()); + this.clock.tick(1); + + assert.equal(currentTime, 0, 'have not played yet'); + + this.player.tech_.trigger('loadedmetadata'); + this.clock.tick(1); + + assert.notEqual(currentTime, 0, 'seeked after tech is ready'); +}); + +QUnit.test('duration is set when the source opens after the playlist is loaded', +function(assert) { this.player.src({ src: 'media.m3u8', type: 'application/vnd.apple.mpegurl'