Skip to content

Commit

Permalink
fix: rework HLS seek/scrub bug
Browse files Browse the repository at this point in the history
90% working seek/scrub functionality with HLS streams. The issue still
remains upon "quick scrubs", when scrubbing for a half a second or so.
  • Loading branch information
kontrollanten committed Mar 4, 2024
1 parent 76ee38a commit 50447c0
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 22 deletions.
53 changes: 31 additions & 22 deletions src/js/tech/ChromecastTech.js
@@ -1,5 +1,7 @@
var ChromecastSessionManager = require('../chromecast/ChromecastSessionManager'),
ChromecastTechUI = require('./ChromecastTechUI');
ChromecastTechUI = require('./ChromecastTechUI'),
Throttler = require('../throttler');


/**
* Registers the ChromecastTech Tech with Video.js. Calls {@link
Expand Down Expand Up @@ -82,7 +84,12 @@ module.exports = function(videojs) {
this._initialStartTime = options.startTime || 0;
this._isScrubbing = false;
this._isSeeking = false;
this._scrubbingTime = this._initialStartTime;

this.seekThrottler = new Throttler((time) => {
this._remotePlayer.currentTime = time;
this._remotePlayerController.seek();
this._triggerTimeUpdateEvent();
});

this._playSource(options.source, this._initialStartTime);
this.ready(function() {
Expand Down Expand Up @@ -246,30 +253,35 @@ module.exports = function(videojs) {
* @see {@link http://docs.videojs.com/Tech.html#setCurrentTime}
*/
setCurrentTime(time) {
if (this.scrubbing()) {
this._scrubbingTime = time;
return false;
}
const duration = this.duration();

if (time > duration || !this._remotePlayer.canSeek) {
return;
}

if (this.scrubbing()) {
this._isSeeking = true;
// We need to throttle the actual seeking, because when you
// scrub, videojs does pause() -> setCurrentTime() -> play()
// and that triggers a weird bug where the chromecast stops sending
// time_changed events.
this.seekThrottler.throttle(time);
return false;
}

// We need to delay the actual seeking, because when you
// scrub, videojs does pause() -> setCurrentTime() -> play()
// and that triggers a weird bug where the chromecast stops sending
// time_changed events.
this._isSeeking = true;
setTimeout(() => {
// Seeking to any place within (approximately) 1 second of the end of the item
// causes the Video.js player to get stuck in a BUFFERING state. To work
// around this, we only allow seeking to within 1 second
// of the end of an item.
this._remotePlayer.currentTime = Math.min(duration - 1, time);
this._remotePlayerController.seek();
this.player().one('play', () => {
if (this.seeking()) {
this.seekThrottler.executeRemaining();
} else {
this._remotePlayer.currentTime = time;
this._remotePlayerController.seek();
}
this._isSeeking = false;
}, 500);
});

this._triggerTimeUpdateEvent();
}
Expand All @@ -284,11 +296,12 @@ module.exports = function(videojs) {

setScrubbing(newValue) {
if (newValue === true) {
this._scrubbingTime = this.currentTime();
this._isScrubbing = true;
} else {
return;
}

if (newValue === false) {
this._isScrubbing = false;
this.setCurrentTime(this._scrubbingTime);
}
}

Expand All @@ -311,10 +324,6 @@ module.exports = function(videojs) {
return this._initialStartTime;
}

if (this.scrubbing() || this.seeking()) {
return this._scrubbingTime;
}

return this._remotePlayer.currentTime;
}

Expand Down
26 changes: 26 additions & 0 deletions src/js/throttler.js
@@ -0,0 +1,26 @@
module.exports = class Throttler {
timerId = null;
latestArgs;

constructor(mainFunction) {
this.mainFunction = mainFunction;
}

throttle(...args) {
this.latestArgs = args;

if (this.timerId === null) {
this.mainFunction(...this.latestArgs);
this.timerId = setTimeout(() => {
this.timerId = null;
}, 300);
}
}

executeRemaining() {
if (this.timerId) {
this.mainFunction(...this.latestArgs);
clearTimeout(this.timerId);
}
}
};

0 comments on commit 50447c0

Please sign in to comment.