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:
parent
d4a08deb10
commit
39485fc4c9
@ -186,6 +186,21 @@ class SeekBar extends Slider {
|
||||
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,
|
||||
* when player can't keep up.
|
||||
@ -303,7 +318,7 @@ class SeekBar extends Slider {
|
||||
}
|
||||
|
||||
// Set new time (tell player to seek to new time)
|
||||
this.player_.currentTime(newTime);
|
||||
this.userSeek_(newTime);
|
||||
}
|
||||
|
||||
enable() {
|
||||
@ -366,14 +381,14 @@ class SeekBar extends Slider {
|
||||
* Move more quickly fast forward for keyboard-only users
|
||||
*/
|
||||
stepForward() {
|
||||
this.player_.currentTime(this.player_.currentTime() + STEP_SECONDS);
|
||||
this.userSeek_(this.player_.currentTime() + STEP_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move more quickly rewind for keyboard-only users
|
||||
*/
|
||||
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
|
||||
*/
|
||||
handleKeyDown(event) {
|
||||
const liveTracker = this.player_.liveTracker;
|
||||
|
||||
if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
@ -416,25 +433,33 @@ class SeekBar extends Slider {
|
||||
} else if (keycode.isEventKey(event, 'Home')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.player_.currentTime(0);
|
||||
this.userSeek_(0);
|
||||
} else if (keycode.isEventKey(event, 'End')) {
|
||||
event.preventDefault();
|
||||
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))) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
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')) {
|
||||
event.preventDefault();
|
||||
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')) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.player_.currentTime(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
|
||||
this.userSeek_(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
|
||||
} else {
|
||||
// Pass keydown handling up for unsupported keys
|
||||
super.handleKeyDown(event);
|
||||
|
@ -191,8 +191,8 @@ class LiveTracker extends Component {
|
||||
handleSeeked() {
|
||||
const timeDiff = Math.abs(this.liveCurrentTime() - this.player_.currentTime());
|
||||
|
||||
this.seekedBehindLive_ = this.skipNextSeeked_ ? false : timeDiff > 2;
|
||||
this.skipNextSeeked_ = false;
|
||||
this.seekedBehindLive_ = this.nextSeekedFromUser_ && timeDiff > 2;
|
||||
this.nextSeekedFromUser_ = false;
|
||||
this.trackLive_();
|
||||
}
|
||||
|
||||
@ -215,7 +215,7 @@ class LiveTracker extends Component {
|
||||
this.behindLiveEdge_ = true;
|
||||
this.timeupdateSeen_ = false;
|
||||
this.seekedBehindLive_ = false;
|
||||
this.skipNextSeeked_ = false;
|
||||
this.nextSeekedFromUser_ = false;
|
||||
|
||||
this.clearInterval(this.trackingInterval_);
|
||||
this.trackingInterval_ = null;
|
||||
@ -227,6 +227,15 @@ class LiveTracker extends Component {
|
||||
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
|
||||
*/
|
||||
@ -375,8 +384,7 @@ class LiveTracker extends Component {
|
||||
if (this.atLiveEdge()) {
|
||||
return;
|
||||
}
|
||||
// skipNextSeeked_
|
||||
this.skipNextSeeked_ = true;
|
||||
this.nextSeekedFromUser_ = false;
|
||||
this.player_.currentTime(this.liveCurrentTime());
|
||||
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ QUnit.module('LiveTracker', () => {
|
||||
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.player.seekable = () => createTimeRanges(0, 20);
|
||||
this.player.trigger('timeupdate');
|
||||
@ -157,6 +157,23 @@ QUnit.module('LiveTracker', () => {
|
||||
|
||||
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.trigger('seeked');
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user