Skip to content

Commit

Permalink
feat: vod dynamic playback rate buffer control (#6172)
Browse files Browse the repository at this point in the history
Dynamically update playback rate to keep the buffer full.

Related to #6131
  • Loading branch information
gkatsev committed Feb 2, 2024
1 parent 278c7bc commit 8fc292b
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 11 deletions.
14 changes: 11 additions & 3 deletions demo/config.js
Expand Up @@ -476,15 +476,23 @@ shakaDemo.Config = class {
/* canBeZero= */ true)
.addNumberInput_('Min playback rate for live sync',
'streaming.liveSyncMinPlaybackRate',
/* canBeDecimal= */ true,
/* canBeZero= */ false)
/* canBeDecimal= */ true)
.addBoolInput_('Live Sync Panic Mode', 'streaming.liveSyncPanicMode')
.addNumberInput_('Live Sync Panic Mode Threshold',
'streaming.liveSyncPanicThreshold')
.addBoolInput_('Allow Media Source recoveries',
'streaming.allowMediaSourceRecoveries')
.addNumberInput_('Minimum time between recoveries',
'streaming.minTimeBetweenRecoveries');
'streaming.minTimeBetweenRecoveries')
.addBoolInput_('VOD Dynamic Playback Rate Buffer Control',
'streaming.vodDynamicPlaybackRate')
.addNumberInput_('VOD Dynamic Playback Rate Low Buffer Rate',
'streaming.vodDynamicPlaybackRateLowBufferRate',
/* canBeDecimal= */ true)
.addNumberInput_('VOD Dynamic Playback Rate Buffer Ratio',
'streaming.vodDynamicPlaybackRateBufferRatio',
/* canBeDecimal= */ true);


if (!shakaDemoMain.getNativeControlsEnabled()) {
this.addBoolInput_('Always Stream Text', 'streaming.alwaysStreamText');
Expand Down
16 changes: 15 additions & 1 deletion externs/shaka/player.js
Expand Up @@ -1171,7 +1171,10 @@ shaka.extern.ManifestConfiguration;
* liveSyncPanicMode: boolean,
* liveSyncPanicThreshold: number,
* allowMediaSourceRecoveries: boolean,
* minTimeBetweenRecoveries: number
* minTimeBetweenRecoveries: number,
* vodDynamicPlaybackRate: boolean,
* vodDynamicPlaybackRateLowBufferRate: number,
* vodDynamicPlaybackRateBufferRatio: number
* }}
*
* @description
Expand Down Expand Up @@ -1338,6 +1341,17 @@ shaka.extern.ManifestConfiguration;
* The minimum time between recoveries when VIDEO_ERROR is reached, in
* seconds.
* Defaults to <code>5</code>.
* @property {boolean} vodDynamicPlaybackRate
* Adapt the playback rate of the player to keep the buffer full. Defaults to
* <code>false</code>.
* @property {number} vodDynamicPlaybackRateLowBufferRate
* Playback rate to use if the buffer is too small. Defaults to
* <code>0.95</code>.
* @property {number} vodDynamicPlaybackRateBufferRatio
* Ratio of the <code>bufferingGoal</code> as the low threshold for
* setting the playback rate to
* <code>vodDynamicPlaybackRateLowBufferRate</code>.
* Defaults to <code>0.5</code>.
* @exportDoc
*/
shaka.extern.StreamingConfiguration;
Expand Down
41 changes: 34 additions & 7 deletions lib/player.js
Expand Up @@ -2065,9 +2065,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

const isLive = this.isLive();

if (isLive && (this.config_.streaming.liveSync ||
if ((isLive && (this.config_.streaming.liveSync ||
this.manifest_.serviceDescription ||
this.config_.streaming.liveSyncPanicMode)) {
this.config_.streaming.liveSyncPanicMode)) ||
this.config_.streaming.vodDynamicPlaybackRate) {
const onTimeUpdate = () => this.onTimeUpdate_();
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
} else if (!isLive) {
Expand Down Expand Up @@ -2398,8 +2399,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {

const isLive = this.isLive();

if (isLive && (this.config_.streaming.liveSync ||
this.config_.streaming.liveSyncPanicMode)) {
if ((isLive && (this.config_.streaming.liveSync ||
this.config_.streaming.liveSyncPanicMode)) ||
this.config_.streaming.vodDynamicPlaybackRate) {
const onTimeUpdate = () => this.onTimeUpdate_();
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
} else if (!isLive) {
Expand Down Expand Up @@ -5672,15 +5674,41 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}

/**
* Callback for liveSync
* Callback for liveSync and vodDynamicPlaybackRate
*
* @private
*/
onTimeUpdate_() {
const playbackRate = this.video_.playbackRate;
const isLive = this.isLive();

if (this.config_.streaming.vodDynamicPlaybackRate && !isLive) {
const minPlaybackRate =
this.config_.streaming.vodDynamicPlaybackRateLowBufferRate;
const bufferFullness = this.getBufferFullness();
const bufferThreshold =
this.config_.streaming.vodDynamicPlaybackRateBufferRatio;

if (bufferFullness <= bufferThreshold) {
if (playbackRate != minPlaybackRate) {
shaka.log.debug('Buffer fullness ratio (' + bufferFullness + ') ' +
'is less than the vodDynamicPlaybackRateBufferRatio (' +
bufferThreshold + '). Updating playbackRate to ' + minPlaybackRate);
this.trickPlay(minPlaybackRate);
}
} else if (bufferFullness == 1) {
if (playbackRate !== this.playRateController_.getDefaultRate()) {
shaka.log.debug('Buffer is full. Cancel trick play.');
this.cancelTrickPlay();
}
}
}

// If the live stream has reached its end, do not sync.
if (!this.isLive()) {
if (!isLive) {
return;
}

const seekRange = this.seekRange();
if (!Number.isFinite(seekRange.end)) {
return;
Expand Down Expand Up @@ -5725,7 +5753,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}
}

const playbackRate = this.video_.playbackRate;
const latency = seekRange.end - this.video_.currentTime;
let offset = 0;
// In src= mode, the seek range isn't updated frequently enough, so we need
Expand Down
3 changes: 3 additions & 0 deletions lib/util/player_configuration.js
Expand Up @@ -235,6 +235,9 @@ shaka.util.PlayerConfiguration = class {
liveSyncPanicThreshold: 60,
allowMediaSourceRecoveries: true,
minTimeBetweenRecoveries: 5,
vodDynamicPlaybackRate: false,
vodDynamicPlaybackRateLowBufferRate: 0.95,
vodDynamicPlaybackRateBufferRatio: 0.5,
};

// WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
Expand Down

0 comments on commit 8fc292b

Please sign in to comment.