mirror of
https://github.com/owncast/owncast.git
synced 2024-11-25 14:20:54 +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 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 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_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 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.
|
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.playbackRate = 1.0;
|
||||||
this.lastJumpOccurred = null;
|
this.lastJumpOccurred = null;
|
||||||
this.startupTime = new Date();
|
this.startupTime = new Date();
|
||||||
|
|
||||||
this.player.on('playing', this.handlePlaying.bind(this));
|
this.player.on('playing', this.handlePlaying.bind(this));
|
||||||
this.player.on('error', this.handleError.bind(this));
|
this.player.on('error', this.handleError.bind(this));
|
||||||
this.player.on('waiting', this.handleBuffering.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('ended', this.handleEnded.bind(this));
|
||||||
this.player.on('canplaythrough', this.handlePlaying.bind(this));
|
this.player.on('canplaythrough', this.handlePlaying.bind(this));
|
||||||
this.player.on('canplay', 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.
|
// This is run on a timer to check if we should be compensating for latency.
|
||||||
check() {
|
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) {
|
if (new Date().getTime() - this.startupTime.getTime() < STARTUP_WAIT_TIME) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -87,34 +92,40 @@ class LatencyCompensator {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
console.log('not enabled...');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tech = this.player.tech({ IWillNotUseThisInPlugins: true });
|
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) {
|
if (!tech || !tech.vhs) {
|
||||||
return;
|
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 {
|
try {
|
||||||
// Check the player buffers to make sure there's enough playable content
|
// Check the player buffers to make sure there's enough playable content
|
||||||
// that we can safely play.
|
// that we can safely play.
|
||||||
if (tech.vhs.stats.buffered.length === 0) {
|
if (tech.vhs.stats.buffered.length === 0) {
|
||||||
console.log('timeout due to zero buffers');
|
|
||||||
this.timeout();
|
this.timeout();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let totalBuffered = 0;
|
|
||||||
|
|
||||||
tech.vhs.stats.buffered.forEach((buffer) => {
|
tech.vhs.stats.buffered.forEach((buffer) => {
|
||||||
totalBuffered += buffer.end - buffer.start;
|
totalBuffered += buffer.end - buffer.start;
|
||||||
});
|
});
|
||||||
console.log('buffered', totalBuffered);
|
console.log('buffered', totalBuffered);
|
||||||
|
|
||||||
if (totalBuffered < 18) {
|
|
||||||
this.timeout();
|
|
||||||
}
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
|
||||||
// Determine how much of the current playlist's bandwidth requirements
|
// Determine how much of the current playlist's bandwidth requirements
|
||||||
|
@ -125,18 +136,22 @@ class LatencyCompensator {
|
||||||
const playerBandwidth = tech.vhs.systemBandwidth;
|
const playerBandwidth = tech.vhs.systemBandwidth;
|
||||||
const bandwidthRatio = playerBandwidth / currentPlaylistBandwidth;
|
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 {
|
try {
|
||||||
const segment = getCurrentlyPlayingSegment(tech);
|
const segment = getCurrentlyPlayingSegment(tech);
|
||||||
if (!segment) {
|
if (!segment) {
|
||||||
return;
|
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.
|
// How far away from live edge do we start the compensator.
|
||||||
const maxLatencyThreshold = Math.min(
|
const maxLatencyThreshold = Math.min(
|
||||||
MAX_LATENCY,
|
MAX_LATENCY,
|
||||||
|
@ -168,6 +183,13 @@ class LatencyCompensator {
|
||||||
proposedPlaybackRate = this.playbackRate + MAX_SPEEDUP_RAMP;
|
proposedPlaybackRate = this.playbackRate + MAX_SPEEDUP_RAMP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'proposedPlaybackRate',
|
||||||
|
proposedPlaybackRate,
|
||||||
|
'previous',
|
||||||
|
this.playbackRate
|
||||||
|
);
|
||||||
|
|
||||||
// Limit to 3 decimal places of precision.
|
// Limit to 3 decimal places of precision.
|
||||||
proposedPlaybackRate =
|
proposedPlaybackRate =
|
||||||
Math.round(proposedPlaybackRate * Math.pow(10, 3)) / Math.pow(10, 3);
|
Math.round(proposedPlaybackRate * Math.pow(10, 3)) / Math.pow(10, 3);
|
||||||
|
@ -352,6 +374,8 @@ class LatencyCompensator {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (this.bufferingCounter > 0) {
|
if (this.bufferingCounter > 0) {
|
||||||
this.bufferingCounter--;
|
this.bufferingCounter--;
|
||||||
|
// Allow a time jump after a long buffer if applicable.
|
||||||
|
this.lastJumpOccurred = null;
|
||||||
}
|
}
|
||||||
}, BUFFERING_AMNESTY_DURATION);
|
}, BUFFERING_AMNESTY_DURATION);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue