Continued tweaking of latency compensation
This commit is contained in:
@@ -11,7 +11,7 @@ It will:
|
|||||||
- Completely give up on all compensation if too many buffering events occur.
|
- 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 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 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.
|
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 {
|
class LatencyCompensator {
|
||||||
constructor(player) {
|
constructor(player) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.enabled = true;
|
this.enabled = false;
|
||||||
this.running = false;
|
this.running = false;
|
||||||
this.inTimeout = false;
|
this.inTimeout = false;
|
||||||
this.timeoutTimer = 0;
|
this.timeoutTimer = 0;
|
||||||
@@ -64,13 +64,24 @@ class LatencyCompensator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let proposedPlaybackRate = bandwidthRatio * 0.34;
|
let proposedPlaybackRate = bandwidthRatio * 0.34;
|
||||||
console.log('proposed rate', proposedPlaybackRate, this.running);
|
console.log('proposed rate', proposedPlaybackRate);
|
||||||
|
|
||||||
proposedPlaybackRate = Math.max(
|
proposedPlaybackRate = Math.max(
|
||||||
Math.min(proposedPlaybackRate, MAX_SPEEDUP_RATE),
|
Math.min(proposedPlaybackRate, MAX_SPEEDUP_RATE),
|
||||||
1.0
|
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 {
|
try {
|
||||||
const segment = getCurrentlyPlayingSegment(tech);
|
const segment = getCurrentlyPlayingSegment(tech);
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
@@ -103,7 +114,7 @@ class LatencyCompensator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
start(rate = 1.0) {
|
start(rate = 1.0) {
|
||||||
if (this.inTimeout || this.disabled) {
|
if (this.inTimeout || !this.enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,6 +127,16 @@ class LatencyCompensator {
|
|||||||
this.player.playbackRate(1);
|
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 means we're done for good and should no longer compensate for latency.
|
||||||
disable() {
|
disable() {
|
||||||
clearInterval(this.checkTimer);
|
clearInterval(this.checkTimer);
|
||||||
@@ -158,25 +179,28 @@ class LatencyCompensator {
|
|||||||
this.endTimeout();
|
this.endTimeout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clearInterval(this.checkTimer);
|
|
||||||
clearTimeout(this.bufferingTimer);
|
|
||||||
|
|
||||||
this.checkTimer = setInterval(() => {
|
|
||||||
this.check();
|
|
||||||
}, CHECK_TIMER_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEnded() {
|
handleEnded() {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.disable();
|
this.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleError() {
|
handleError() {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.timeout();
|
this.timeout();
|
||||||
}
|
}
|
||||||
|
|
||||||
countBufferingEvent() {
|
countBufferingEvent() {
|
||||||
this.bufferingCounter++;
|
this.bufferingCounter++;
|
||||||
if (this.bufferingCounter > REBUFFER_EVENT_LIMIT) {
|
if (this.bufferingCounter > REBUFFER_EVENT_LIMIT) {
|
||||||
|
console.log('disabling latency compensation');
|
||||||
this.disable();
|
this.disable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -192,6 +216,10 @@ class LatencyCompensator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
handleBuffering() {
|
handleBuffering() {
|
||||||
|
if (!this.enabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.bufferStartedTimestamp = new Date().getTime();
|
this.bufferStartedTimestamp = new Date().getTime();
|
||||||
this.timeout();
|
this.timeout();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -236,28 +236,35 @@ class OwncastPlayer {
|
|||||||
this.appPlayerPlayingCallback();
|
this.appPlayerPlayingCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
setInterval(() => {
|
this.latencyCompensator.enable();
|
||||||
const tech = this.vjsPlayer.tech({ IWillNotUseThisInPlugins: true });
|
|
||||||
const bandwidth = tech.vhs.systemBandwidth;
|
|
||||||
this.playbackMetrics.trackBandwidth(bandwidth);
|
|
||||||
|
|
||||||
try {
|
setInterval(() => {
|
||||||
const segment = getCurrentlyPlayingSegment(tech);
|
this.collectPlaybackMetrics();
|
||||||
const segmentTime = segment.dateTimeObject.getTime();
|
|
||||||
const now = new Date().getTime();
|
|
||||||
const latency = now - segmentTime;
|
|
||||||
this.playbackMetrics.trackLatency(latency);
|
|
||||||
} catch (err) {
|
|
||||||
console.warn(err);
|
|
||||||
}
|
|
||||||
}, 5000);
|
}, 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() {
|
handleEnded() {
|
||||||
this.log('on Ended');
|
this.log('on Ended');
|
||||||
if (this.appPlayerEndedCallback) {
|
if (this.appPlayerEndedCallback) {
|
||||||
this.appPlayerEndedCallback();
|
this.appPlayerEndedCallback();
|
||||||
}
|
}
|
||||||
|
this.latencyCompensator.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
handleError(e) {
|
handleError(e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user