Skip to content
This repository has been archived by the owner on Jan 12, 2019. It is now read-only.

fix InvalidStateError for live playback in IE11 #1266

Merged
merged 3 commits into from
Oct 10, 2017
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 28 additions & 9 deletions src/master-playlist-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -698,17 +698,41 @@ export class MasterPlaylistController extends videojs.EventTarget {
// 3) the player has not started playing
!this.hasPlayed_()) {

let setHasPlayed = false;

// 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) {
this.tech_.setCurrentTime(seekable.end(0));
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.
this.tech_.one('loadedmetadata', () => {
// we hold off on setting hasPlayed on true until after we seek to prevent
// segment loaders from requesting more than 1 segment before the seek to
// live point
this.tech_.setCurrentTime(seekable.end(0));
this.hasPlayed_ = () => true;
});
} else {
setHasPlayed = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this set here, and not where hasPlayed_ is set? You could move this into the loadedmetadata function above, and set it on line 733 as well.

I'm mostly suggesting this because I think it will be easier from a maintenance standpoint.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow. This function setupFirstPlay is called when play is triggered until this.hasPlayed_() returns true. Currently, with a VOD, we update hasPlayed_ to return true the first time we get a play call and we've loaded the media. Currently in a live stream, we update hasPlayed_ to true the first time we get a play call, we've loaded the media, and have calculated seekable, so that we can seek to the live point and that segmentloader knows it can start filling the buffer. Since now we have to wait for loadedmetadata from the tech in IE11 before we can seek, we wait to update hasPlayed_ until we've gotten loadedmetadata which happens after we append the first bytes to the source buffer. Since this is asynchronous, this local var setHasPlayed is used to know if we can set hasPlayed_ to true yet. Does this answer your question?

this.tech_.setCurrentTime(seekable.end(0));
}
}
} else {
// always set hasPlayed to true for first play of VOD
setHasPlayed = true;
}
this.hasPlayed_ = () => true;

if (setHasPlayed) {
this.hasPlayed_ = () => true;
}

// now that we are ready, load the segment
this.load();
return true;
Expand Down Expand Up @@ -893,14 +917,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());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I snuck this change into this PR because the mode can only be flash or html5, so checking for the case of auto is unnecessary.


// 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;
}

Expand Down