mirror of
https://github.com/owncast/owncast.git
synced 2024-11-22 04:40:37 +03:00
Allow latency compenstation if bandwidth is fast enough or there are enough segments buffered
This commit is contained in:
parent
6b909b2c47
commit
d4cbf07055
1 changed files with 39 additions and 15 deletions
|
@ -39,7 +39,7 @@ const HIGHEST_LATENCY_SEGMENT_LENGTH_MULTIPLIER = 2.6; // Segment length * this
|
|||
const LOWEST_LATENCY_SEGMENT_LENGTH_MULTIPLIER = 1.8; // Segment length * this value is when we stop compensating.
|
||||
const MIN_LATENCY = 4 * 1000; // The absolute lowest we'll continue compensation to be running at.
|
||||
const MAX_LATENCY = 15 * 1000; // The absolute highest we'll allow a target latency to be before we start compensating.
|
||||
const MAX_JUMP_LATENCY = 7 * 1000; // How much behind the max latency we need to be behind before we allow a jump.
|
||||
const MAX_JUMP_LATENCY = 5 * 1000; // How much behind the max latency we need to be behind before we allow a jump.
|
||||
const MAX_JUMP_FREQUENCY = 20 * 1000; // How often we'll allow a time jump.
|
||||
const STARTUP_WAIT_TIME = 10 * 1000; // The amount of time after we start up that we'll allow monitoring to occur.
|
||||
|
||||
|
@ -58,9 +58,11 @@ class LatencyCompensator {
|
|||
this.playbackRate = 1.0;
|
||||
this.lastJumpOccurred = null;
|
||||
this.startupTime = new Date();
|
||||
|
||||
this.player.on('playing', this.handlePlaying.bind(this));
|
||||
this.player.on('error', this.handleError.bind(this));
|
||||
this.player.on('waiting', this.handleBuffering.bind(this));
|
||||
this.player.on('stalled', this.handleBuffering.bind(this));
|
||||
this.player.on('ended', this.handleEnded.bind(this));
|
||||
this.player.on('canplaythrough', this.handlePlaying.bind(this));
|
||||
this.player.on('canplay', this.handlePlaying.bind(this));
|
||||
|
@ -68,6 +70,9 @@ class LatencyCompensator {
|
|||
|
||||
// This is run on a timer to check if we should be compensating for latency.
|
||||
check() {
|
||||
// We have an arbitrary delay at startup to allow the player to run
|
||||
// normally and hopefully get a bit of a buffer of segments before we
|
||||
// start messing with it.
|
||||
if (new Date().getTime() - this.startupTime.getTime() < STARTUP_WAIT_TIME) {
|
||||
return;
|
||||
}
|
||||
|
@ -87,34 +92,40 @@ class LatencyCompensator {
|
|||
}
|
||||
|
||||
if (!this.enabled) {
|
||||
console.log('not enabled...');
|
||||
return;
|
||||
}
|
||||
|
||||
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
||||
|
||||
// We need access to the internal tech of VHS to move forward.
|
||||
// If running under an Apple browser that uses CoreMedia (Safari)
|
||||
// we do not have access to this as the tech is internal to the OS.
|
||||
if (!tech || !tech.vhs) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Network state 2 means we're actively using the network.
|
||||
// We only want to attempt latency compensation if we're continuing to
|
||||
// download new segments.
|
||||
const networkState = this.player.networkState();
|
||||
if (networkState !== 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
let totalBuffered = 0;
|
||||
|
||||
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) {
|
||||
console.log('timeout due to zero buffers');
|
||||
this.timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
let totalBuffered = 0;
|
||||
|
||||
tech.vhs.stats.buffered.forEach((buffer) => {
|
||||
totalBuffered += buffer.end - buffer.start;
|
||||
});
|
||||
console.log('buffered', totalBuffered);
|
||||
|
||||
if (totalBuffered < 18) {
|
||||
this.timeout();
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
// Determine how much of the current playlist's bandwidth requirements
|
||||
|
@ -125,18 +136,22 @@ class LatencyCompensator {
|
|||
const playerBandwidth = tech.vhs.systemBandwidth;
|
||||
const bandwidthRatio = playerBandwidth / currentPlaylistBandwidth;
|
||||
|
||||
// If we don't think we have the bandwidth to play faster, then don't do it.
|
||||
if (bandwidthRatio < REQUIRED_BANDWIDTH_RATIO) {
|
||||
this.timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const segment = getCurrentlyPlayingSegment(tech);
|
||||
if (!segment) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're downloading media fast enough or we feel like we have a large
|
||||
// enough buffer then continue. Otherwise timeout for a bit.
|
||||
if (
|
||||
bandwidthRatio < REQUIRED_BANDWIDTH_RATIO &&
|
||||
totalBuffered < segment.duration * 6
|
||||
) {
|
||||
this.timeout();
|
||||
return;
|
||||
}
|
||||
|
||||
// How far away from live edge do we start the compensator.
|
||||
const maxLatencyThreshold = Math.min(
|
||||
MAX_LATENCY,
|
||||
|
@ -168,6 +183,13 @@ class LatencyCompensator {
|
|||
proposedPlaybackRate = this.playbackRate + MAX_SPEEDUP_RAMP;
|
||||
}
|
||||
|
||||
console.log(
|
||||
'proposedPlaybackRate',
|
||||
proposedPlaybackRate,
|
||||
'previous',
|
||||
this.playbackRate
|
||||
);
|
||||
|
||||
// Limit to 3 decimal places of precision.
|
||||
proposedPlaybackRate =
|
||||
Math.round(proposedPlaybackRate * Math.pow(10, 3)) / Math.pow(10, 3);
|
||||
|
@ -352,6 +374,8 @@ class LatencyCompensator {
|
|||
setTimeout(() => {
|
||||
if (this.bufferingCounter > 0) {
|
||||
this.bufferingCounter--;
|
||||
// Allow a time jump after a long buffer if applicable.
|
||||
this.lastJumpOccurred = null;
|
||||
}
|
||||
}, BUFFERING_AMNESTY_DURATION);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue