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;
|
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);
|
||||||
|
@ -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());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user