From d96ddcc6c6cc61b9b7a70d28066eec9d105595f5 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Sat, 19 Mar 2022 13:17:37 -0700 Subject: [PATCH] Continued tweaking of latency compensation --- webroot/js/components/latencyCompensator.js | 50 ++++++++++++++++----- webroot/js/components/player.js | 33 ++++++++------ 2 files changed, 59 insertions(+), 24 deletions(-) diff --git a/webroot/js/components/latencyCompensator.js b/webroot/js/components/latencyCompensator.js index 6e0678303..32b69f0eb 100644 --- a/webroot/js/components/latencyCompensator.js +++ b/webroot/js/components/latencyCompensator.js @@ -11,7 +11,7 @@ It will: - Completely give up on all compensation if too many buffering events occur. */ -const REBUFFER_EVENT_LIMIT = 8; // Max number of buffering events before we stop compensating for latency. +const REBUFFER_EVENT_LIMIT = 5; // Max number of buffering events before we stop compensating for latency. const MIN_BUFFER_DURATION = 300; // Min duration a buffer event must last to be counted. const MAX_SPEEDUP_RATE = 1.07; // The playback rate when compensating for latency. const TIMEOUT_DURATION = 20_000; // The amount of time we stop handling latency after certain events. @@ -24,7 +24,7 @@ const REQUIRED_BANDWIDTH_RATIO = 2.0; // The player:bitrate ratio required to en class LatencyCompensator { constructor(player) { this.player = player; - this.enabled = true; + this.enabled = false; this.running = false; this.inTimeout = false; this.timeoutTimer = 0; @@ -64,13 +64,24 @@ class LatencyCompensator { } let proposedPlaybackRate = bandwidthRatio * 0.34; - console.log('proposed rate', proposedPlaybackRate, this.running); + console.log('proposed rate', proposedPlaybackRate); proposedPlaybackRate = Math.max( Math.min(proposedPlaybackRate, MAX_SPEEDUP_RATE), 1.0 ); - console.log('playback rate', proposedPlaybackRate, this.running); + console.log( + 'playback rate', + proposedPlaybackRate, + 'enabled:', + this.enabled, + 'running: ', + this.running, + 'timedout: ', + this.inTimeout, + 'buffer count:', + this.bufferingCounter + ); try { const segment = getCurrentlyPlayingSegment(tech); if (!segment) { @@ -103,7 +114,7 @@ class LatencyCompensator { } start(rate = 1.0) { - if (this.inTimeout || this.disabled) { + if (this.inTimeout || !this.enabled) { return; } @@ -116,6 +127,16 @@ class LatencyCompensator { this.player.playbackRate(1); } + enable() { + this.enabled = true; + clearInterval(this.checkTimer); + clearTimeout(this.bufferingTimer); + + this.checkTimer = setInterval(() => { + this.check(); + }, CHECK_TIMER_INTERVAL); + } + // Disable means we're done for good and should no longer compensate for latency. disable() { clearInterval(this.checkTimer); @@ -158,25 +179,28 @@ class LatencyCompensator { this.endTimeout(); } } - clearInterval(this.checkTimer); - clearTimeout(this.bufferingTimer); - - this.checkTimer = setInterval(() => { - this.check(); - }, CHECK_TIMER_INTERVAL); } handleEnded() { + if (!this.enabled) { + return; + } + this.disable(); } handleError() { + if (!this.enabled) { + return; + } + this.timeout(); } countBufferingEvent() { this.bufferingCounter++; if (this.bufferingCounter > REBUFFER_EVENT_LIMIT) { + console.log('disabling latency compensation'); this.disable(); return; } @@ -192,6 +216,10 @@ class LatencyCompensator { } handleBuffering() { + if (!this.enabled) { + return; + } + this.bufferStartedTimestamp = new Date().getTime(); this.timeout(); } diff --git a/webroot/js/components/player.js b/webroot/js/components/player.js index 64ba824c6..08574878c 100644 --- a/webroot/js/components/player.js +++ b/webroot/js/components/player.js @@ -236,28 +236,35 @@ class OwncastPlayer { this.appPlayerPlayingCallback(); } - setInterval(() => { - const tech = this.vjsPlayer.tech({ IWillNotUseThisInPlugins: true }); - const bandwidth = tech.vhs.systemBandwidth; - this.playbackMetrics.trackBandwidth(bandwidth); + this.latencyCompensator.enable(); - try { - const segment = getCurrentlyPlayingSegment(tech); - const segmentTime = segment.dateTimeObject.getTime(); - const now = new Date().getTime(); - const latency = now - segmentTime; - this.playbackMetrics.trackLatency(latency); - } catch (err) { - console.warn(err); - } + setInterval(() => { + this.collectPlaybackMetrics(); }, 5000); } + collectPlaybackMetrics() { + const tech = this.vjsPlayer.tech({ IWillNotUseThisInPlugins: true }); + const bandwidth = tech.vhs.systemBandwidth; + this.playbackMetrics.trackBandwidth(bandwidth); + + try { + const segment = getCurrentlyPlayingSegment(tech); + const segmentTime = segment.dateTimeObject.getTime(); + const now = new Date().getTime(); + const latency = now - segmentTime; + this.playbackMetrics.trackLatency(latency); + } catch (err) { + console.warn(err); + } + } + handleEnded() { this.log('on Ended'); if (this.appPlayerEndedCallback) { this.appPlayerEndedCallback(); } + this.latencyCompensator.disable(); } handleError(e) {