mirror of
https://github.com/owncast/owncast.git
synced 2024-10-10 19:16:02 +00:00
Continued tweaking of latency compensation
This commit is contained in:
parent
22578f0ac2
commit
5a2584d8ea
@ -13,10 +13,10 @@ It will:
|
||||
|
||||
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 MAX_SPEEDUP_RATE = 1.08; // The playback rate when compensating for latency.
|
||||
const TIMEOUT_DURATION = 20_000; // The amount of time we stop handling latency after certain events.
|
||||
const CHECK_TIMER_INTERVAL = 5_000; // How often we check if we should be compensating for latency.
|
||||
const BUFFERING_AMNESTY_DURATION = 2 * 1000 * 60; // How often until a buffering event expires.
|
||||
const BUFFERING_AMNESTY_DURATION = 4 * 1000 * 60; // How often until a buffering event expires.
|
||||
const HIGH_LATENCY_ENABLE_THRESHOLD = 20_000; // ms;
|
||||
const LOW_LATENCY_DISABLE_THRESHOLD = 4500; // ms;
|
||||
const REQUIRED_BANDWIDTH_RATIO = 2.0; // The player:bitrate ratio required to enable compensating for latency.
|
||||
@ -33,7 +33,7 @@ class LatencyCompensator {
|
||||
this.minLatencyThreshold = 8000;
|
||||
this.bufferingCounter = 0;
|
||||
this.bufferingTimer = 0;
|
||||
this.bufferStartedTimestamp = 0;
|
||||
this.playbackRate = 1.0;
|
||||
this.player.on('playing', this.handlePlaying.bind(this));
|
||||
this.player.on('error', this.handleError.bind(this));
|
||||
this.player.on('waiting', this.handleBuffering.bind(this));
|
||||
@ -44,12 +44,43 @@ class LatencyCompensator {
|
||||
|
||||
// This is run on a timer to check if we should be compensating for latency.
|
||||
check() {
|
||||
console.log(
|
||||
'playback rate',
|
||||
this.playbackRate,
|
||||
'enabled:',
|
||||
this.enabled,
|
||||
'running: ',
|
||||
this.running,
|
||||
'timeout: ',
|
||||
this.inTimeout,
|
||||
'buffer count:',
|
||||
this.bufferingCounter
|
||||
);
|
||||
|
||||
if (this.inTimeout) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
||||
|
||||
try {
|
||||
// Check the player buffers to make sure there's enough playable content
|
||||
// that we can safely play.
|
||||
if (tech.vhs.stats.buffered.length === 0) {
|
||||
this.timeout();
|
||||
}
|
||||
|
||||
let totalBuffered = 0;
|
||||
|
||||
tech.vhs.stats.buffered.forEach((buffer) => {
|
||||
totalBuffered += buffer.end - buffer.start;
|
||||
});
|
||||
|
||||
if (totalBuffered < 20) {
|
||||
this.timeout();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Determine how much of the current playlist's bandwidth requirements
|
||||
// we're utilizing. If it's too high then we can't afford to push
|
||||
// further into the future because we're downloading too slowly.
|
||||
@ -70,18 +101,7 @@ class LatencyCompensator {
|
||||
Math.min(proposedPlaybackRate, MAX_SPEEDUP_RATE),
|
||||
1.0
|
||||
);
|
||||
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) {
|
||||
@ -113,18 +133,23 @@ class LatencyCompensator {
|
||||
}
|
||||
}
|
||||
|
||||
setPlaybackRate(rate) {
|
||||
this.playbackRate = rate;
|
||||
this.player.playbackRate(rate);
|
||||
}
|
||||
|
||||
start(rate = 1.0) {
|
||||
if (this.inTimeout || !this.enabled) {
|
||||
if (this.inTimeout || !this.enabled || rate === this.playbackRate) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.running = true;
|
||||
this.player.playbackRate(rate);
|
||||
this.setPlaybackRate(rate);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.running = false;
|
||||
this.player.playbackRate(1);
|
||||
this.setPlaybackRate(1.0);
|
||||
}
|
||||
|
||||
enable() {
|
||||
@ -161,24 +186,11 @@ class LatencyCompensator {
|
||||
}
|
||||
|
||||
handlePlaying() {
|
||||
clearTimeout(this.bufferingTimer);
|
||||
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.bufferStartedTimestamp !== 0) {
|
||||
const bufferingDuration =
|
||||
new Date().getTime() - this.bufferStartedTimestamp;
|
||||
this.bufferStartedTimestamp = 0;
|
||||
|
||||
// If the buffering event lasted long enough then we will stay in
|
||||
// a timeout and count it as a real buffering event. Otherwise
|
||||
// we will ignore it.
|
||||
if (bufferingDuration > MIN_BUFFER_DURATION) {
|
||||
this.countBufferingEvent();
|
||||
} else {
|
||||
this.endTimeout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleEnded() {
|
||||
@ -189,18 +201,18 @@ class LatencyCompensator {
|
||||
this.disable();
|
||||
}
|
||||
|
||||
handleError() {
|
||||
handleError(e) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('handle error', e);
|
||||
this.timeout();
|
||||
}
|
||||
|
||||
countBufferingEvent() {
|
||||
this.bufferingCounter++;
|
||||
if (this.bufferingCounter > REBUFFER_EVENT_LIMIT) {
|
||||
console.log('disabling latency compensation');
|
||||
this.disable();
|
||||
return;
|
||||
}
|
||||
@ -220,8 +232,11 @@ class LatencyCompensator {
|
||||
return;
|
||||
}
|
||||
|
||||
this.bufferStartedTimestamp = new Date().getTime();
|
||||
this.timeout();
|
||||
|
||||
this.bufferingTimer = setTimeout(() => {
|
||||
this.countBufferingEvent();
|
||||
}, MIN_BUFFER_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,32 +8,6 @@ import LatencyCompensator from './latencyCompensator.js';
|
||||
|
||||
const VIDEO_ID = 'video';
|
||||
|
||||
const EVENTS = [
|
||||
'loadstart',
|
||||
'progress',
|
||||
'suspend',
|
||||
'abort',
|
||||
'error',
|
||||
'emptied',
|
||||
'stalled',
|
||||
'loadedmetadata',
|
||||
'loadeddata',
|
||||
'canplay',
|
||||
'canplaythrough',
|
||||
'playing',
|
||||
'waiting',
|
||||
'seeking',
|
||||
'seeked',
|
||||
'ended',
|
||||
'durationchange',
|
||||
'timeupdate',
|
||||
'play',
|
||||
'pause',
|
||||
'ratechange',
|
||||
'resize',
|
||||
'volumechange',
|
||||
];
|
||||
|
||||
// Video setup
|
||||
const VIDEO_SRC = {
|
||||
src: URL_STREAM,
|
||||
@ -108,6 +82,7 @@ class OwncastPlayer {
|
||||
this.appPlayerReadyCallback = null;
|
||||
this.appPlayerPlayingCallback = null;
|
||||
this.appPlayerEndedCallback = null;
|
||||
this.hasStartedPlayback = false;
|
||||
|
||||
// bind all the things because safari
|
||||
this.startPlayer = this.startPlayer.bind(this);
|
||||
@ -236,7 +211,10 @@ class OwncastPlayer {
|
||||
this.appPlayerPlayingCallback();
|
||||
}
|
||||
|
||||
this.latencyCompensator.enable();
|
||||
if (!this.hasStartedPlayback) {
|
||||
this.latencyCompensator.enable();
|
||||
this.hasStartedPlayback = true;
|
||||
}
|
||||
|
||||
setInterval(() => {
|
||||
this.collectPlaybackMetrics();
|
||||
|
Loading…
x
Reference in New Issue
Block a user