1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-02 09:11:54 +02:00

fix: user and programmatic seeks with live streams (#7210)

This commit is contained in:
Brandon Casey 2021-05-10 18:42:17 -04:00 committed by GitHub
parent d4a08deb10
commit 39485fc4c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 64 additions and 14 deletions

View File

@ -186,6 +186,21 @@ class SeekBar extends Slider {
return percent; return percent;
} }
/**
* Prevent liveThreshold from causing seeks to seem like they
* are not happening from a user perspective.
*
* @param {number} ct
* current time to seek to
*/
userSeek_(ct) {
if (this.player_.liveTracker && this.player_.liveTracker.isLive()) {
this.player_.liveTracker.nextSeekedFromUser();
}
this.player_.currentTime(ct);
}
/** /**
* Get the value of current time but allows for smooth scrubbing, * Get the value of current time but allows for smooth scrubbing,
* when player can't keep up. * when player can't keep up.
@ -303,7 +318,7 @@ class SeekBar extends Slider {
} }
// Set new time (tell player to seek to new time) // Set new time (tell player to seek to new time)
this.player_.currentTime(newTime); this.userSeek_(newTime);
} }
enable() { enable() {
@ -366,14 +381,14 @@ class SeekBar extends Slider {
* Move more quickly fast forward for keyboard-only users * Move more quickly fast forward for keyboard-only users
*/ */
stepForward() { stepForward() {
this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS); this.userSeek_(this.player_.currentTime() + STEP_SECONDS);
} }
/** /**
* Move more quickly rewind for keyboard-only users * Move more quickly rewind for keyboard-only users
*/ */
stepBack() { stepBack() {
this.player_.currentTime(this.player_.currentTime() - STEP_SECONDS); this.userSeek_(this.player_.currentTime() - STEP_SECONDS);
} }
/** /**
@ -409,6 +424,8 @@ class SeekBar extends Slider {
* @listens keydown * @listens keydown
*/ */
handleKeyDown(event) { handleKeyDown(event) {
const liveTracker = this.player_.liveTracker;
if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) { if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
@ -416,25 +433,33 @@ class SeekBar extends Slider {
} else if (keycode.isEventKey(event, 'Home')) { } else if (keycode.isEventKey(event, 'Home')) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.player_.currentTime(0); this.userSeek_(0);
} else if (keycode.isEventKey(event, 'End')) { } else if (keycode.isEventKey(event, 'End')) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.player_.currentTime(this.player_.duration()); if (liveTracker && liveTracker.isLive()) {
this.userSeek_(liveTracker.liveCurrentTime());
} else {
this.userSeek_(this.player_.duration());
}
} else if (/^[0-9]$/.test(keycode(event))) { } else if (/^[0-9]$/.test(keycode(event))) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
const gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0; const gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;
this.player_.currentTime(this.player_.duration() * gotoFraction); if (liveTracker && liveTracker.isLive()) {
this.userSeek_(liveTracker.seekableStart() + (liveTracker.liveWindow() * gotoFraction));
} else {
this.userSeek_(this.player_.duration() * gotoFraction);
}
} else if (keycode.isEventKey(event, 'PgDn')) { } else if (keycode.isEventKey(event, 'PgDn')) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.player_.currentTime(this.player_.currentTime() - (STEP_SECONDS * PAGE_KEY_MULTIPLIER)); this.userSeek_(this.player_.currentTime() - (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
} else if (keycode.isEventKey(event, 'PgUp')) { } else if (keycode.isEventKey(event, 'PgUp')) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
this.player_.currentTime(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER)); this.userSeek_(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
} else { } else {
// Pass keydown handling up for unsupported keys // Pass keydown handling up for unsupported keys
super.handleKeyDown(event); super.handleKeyDown(event);

View File

@ -191,8 +191,8 @@ class LiveTracker extends Component {
handleSeeked() { handleSeeked() {
const timeDiff = Math.abs(this.liveCurrentTime() - this.player_.currentTime()); const timeDiff = Math.abs(this.liveCurrentTime() - this.player_.currentTime());
this.seekedBehindLive_ = this.skipNextSeeked_ ? false : timeDiff > 2; this.seekedBehindLive_ = this.nextSeekedFromUser_ && timeDiff > 2;
this.skipNextSeeked_ = false; this.nextSeekedFromUser_ = false;
this.trackLive_(); this.trackLive_();
} }
@ -215,7 +215,7 @@ class LiveTracker extends Component {
this.behindLiveEdge_ = true; this.behindLiveEdge_ = true;
this.timeupdateSeen_ = false; this.timeupdateSeen_ = false;
this.seekedBehindLive_ = false; this.seekedBehindLive_ = false;
this.skipNextSeeked_ = false; this.nextSeekedFromUser_ = false;
this.clearInterval(this.trackingInterval_); this.clearInterval(this.trackingInterval_);
this.trackingInterval_ = null; this.trackingInterval_ = null;
@ -227,6 +227,15 @@ class LiveTracker extends Component {
this.off(this.player_, 'timeupdate', this.seekToLiveEdge_); this.off(this.player_, 'timeupdate', this.seekToLiveEdge_);
} }
/**
* The next seeked event is from the user. Meaning that any seek
* > 2s behind live will be considered behind live for real and
* liveTolerance will be ignored.
*/
nextSeekedFromUser() {
this.nextSeekedFromUser_ = true;
}
/** /**
* stop tracking live playback * stop tracking live playback
*/ */
@ -375,8 +384,7 @@ class LiveTracker extends Component {
if (this.atLiveEdge()) { if (this.atLiveEdge()) {
return; return;
} }
// skipNextSeeked_ this.nextSeekedFromUser_ = false;
this.skipNextSeeked_ = true;
this.player_.currentTime(this.liveCurrentTime()); this.player_.currentTime(this.liveCurrentTime());
} }

View File

@ -148,7 +148,7 @@ QUnit.module('LiveTracker', () => {
assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge'); assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge');
}); });
QUnit.test('is behindLiveEdge when seeking backwards', function(assert) { QUnit.test('is behindLiveEdge when seeking behind liveTolerance with API', function(assert) {
this.liveTracker.options_.liveTolerance = 6; this.liveTracker.options_.liveTolerance = 6;
this.player.seekable = () => createTimeRanges(0, 20); this.player.seekable = () => createTimeRanges(0, 20);
this.player.trigger('timeupdate'); this.player.trigger('timeupdate');
@ -157,6 +157,23 @@ QUnit.module('LiveTracker', () => {
assert.ok(this.liveTracker.atLiveEdge(), 'at live edge'); assert.ok(this.liveTracker.atLiveEdge(), 'at live edge');
this.player.currentTime = () => 14;
this.player.trigger('seeked');
assert.equal(this.liveEdgeChanges, 1, 'should have one live edge change');
assert.ok(this.liveTracker.behindLiveEdge(), 'behindLiveEdge live edge');
});
QUnit.test('is behindLiveEdge when seeking >2s behind with ui', function(assert) {
this.liveTracker.options_.liveTolerance = 6;
this.player.seekable = () => createTimeRanges(0, 20);
this.player.trigger('timeupdate');
this.player.currentTime = () => 20;
this.clock.tick(1000);
assert.ok(this.liveTracker.atLiveEdge(), 'at live edge');
this.liveTracker.nextSeekedFromUser();
this.player.currentTime = () => 17; this.player.currentTime = () => 17;
this.player.trigger('seeked'); this.player.trigger('seeked');