Skip to content

Commit

Permalink
feat: Add liveSync configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Jun 14, 2023
1 parent 7209695 commit 8640c44
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 2 deletions.
3 changes: 3 additions & 0 deletions demo/common/message_ids.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@ shakaDemo.MessageIds = {
LCEVC_LOG_LEVEL: 'DEMO_LCEVC_LOG_LEVEL',
LCEVC_SECTION_HEADER: 'DEMO_LCEVC_SECTION_HEADER',
LIVE_SEGMENTS_DELAY: 'DEMO_LIVE_SEGMENTS_DELAY',
LIVE_SYNC: 'DEMO_LIVE_SYNC',
LIVE_SYNC_MAX_LATENCY: 'DEMO_LIVE_SYNC_MAX_LATENCY',
LIVE_SYNC_PLAYBACK_RATE: 'DEMO_LIVE_SYNC_PLAYBACK_RATE',
LOG_LEVEL: 'DEMO_LOG_LEVEL',
LOG_LEVEL_DEBUG: 'DEMO_LOG_LEVEL_DEBUG',
LOG_LEVEL_INFO: 'DEMO_LOG_LEVEL_INFO',
Expand Down
12 changes: 11 additions & 1 deletion demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,17 @@ shakaDemo.Config = class {
.addNumberInput_(MessageIds.MAX_DISABLED_TIME,
'streaming.maxDisabledTime')
.addNumberInput_(MessageIds.SEGMENT_PREFETCH_LIMIT,
'streaming.segmentPrefetchLimit');
'streaming.segmentPrefetchLimit')
.addBoolInput_(MessageIds.LIVE_SYNC,
'streaming.liveSync')
.addNumberInput_(MessageIds.LIVE_SYNC_MAX_LATENCY,
'streaming.liveSyncMaxLatency',
/* canBeDecimal= */ true,
/* canBeZero= */ false)
.addNumberInput_(MessageIds.LIVE_SYNC_PLAYBACK_RATE,
'streaming.liveSyncPlaybackRate',
/* canBeDecimal= */ true,
/* canBeZero= */ false);

if (!shakaDemoMain.getNativeControlsEnabled()) {
this.addBoolInput_(MessageIds.ALWAYS_STREAM_TEXT,
Expand Down
3 changes: 3 additions & 0 deletions demo/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@
"DEMO_LIVE": "Live",
"DEMO_LIVE_SEARCH": "Live",
"DEMO_LIVE_SEGMENTS_DELAY": "Live segments delay",
"DEMO_LIVE_SYNC": "Live sync",
"DEMO_LIVE_SYNC_MAX_LATENCY":"Max latency for live sync",
"DEMO_LIVE_SYNC_PLAYBACK_RATE":"Playback rate for live sync",
"DEMO_LOG_LEVEL": "Log Level",
"DEMO_LOG_LEVEL_DEBUG": "Debug",
"DEMO_LOG_LEVEL_INFO": "Info",
Expand Down
12 changes: 12 additions & 0 deletions demo/locales/source.json
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,18 @@
"description": "The name of a configuration value.",
"message": "Live segments delay"
},
"DEMO_LIVE_SYNC": {
"description": "The name of a configuration value.",
"message": "Live sync"
},
"DEMO_LIVE_SYNC_MAX_LATENCY": {
"description": "The name of a configuration value.",
"message": "Max latency for live sync"
},
"DEMO_LIVE_SYNC_PLAYBACK_RATE": {
"description": "The name of a configuration value.",
"message": "Playback rate for live sync"
},
"DEMO_LOG_LEVEL": {
"description": "The name of a configuration value.",
"message": "Log Level"
Expand Down
17 changes: 16 additions & 1 deletion externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1078,7 +1078,10 @@ shaka.extern.ManifestConfiguration;
* observeQualityChanges: boolean,
* maxDisabledTime: number,
* parsePrftBox: boolean,
* segmentPrefetchLimit: number
* segmentPrefetchLimit: number,
* liveSync: boolean,
* liveSyncMaxLatency: number,
* liveSyncPlaybackRate: number
* }}
*
* @description
Expand Down Expand Up @@ -1193,6 +1196,18 @@ shaka.extern.ManifestConfiguration;
* ahead of playhead in parallel.
* If <code>0</code>, the segments will be fetched sequentially.
* Defaults to <code>0</code>.
* @property {boolean} liveSync
* Enable the live stream sync against the live edge by changing the playback
* rate. Defaults to <code>false</code>.
* Note: on some SmartTVs, if this is activated, it may not work or the sound
* may be lost when activated.
* @property {number} liveSyncMaxLatency
* Maximum acceptable latency, in seconds. Effective only if liveSync is
* true. Defaults to <code>1</code>.
* @property {number} liveSyncPlaybackRate
* Playback rate used for latency chasing. It is recommended to use a value
* between 1 and 2. Effective only if liveSync is true. Defaults to
* <code>1.2</code>.
* @exportDoc
*/
shaka.extern.StreamingConfiguration;
Expand Down
53 changes: 53 additions & 0 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2275,6 +2275,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
'arbitrary language initially');
}

if (this.isLive() && this.config_.streaming.liveSync) {
const onTimeUpdate = () => this.onTimeUpdate_();
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
}

this.fullyLoaded_ = true;

// Wait for the 'loadedmetadata' event to measure load() latency.
Expand Down Expand Up @@ -2627,6 +2632,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
fullyLoaded.reject(abortedError);
return Promise.resolve(); // Abort complete.
}).chain(() => {
if (this.isLive() && this.config_.streaming.liveSync) {
const onTimeUpdate = () => this.onTimeUpdate_();
this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
}

this.fullyLoaded_ = true;
});
}
Expand Down Expand Up @@ -5691,6 +5701,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
}
}

/**
* Callback for liveSync
*
* @private
*/
onTimeUpdate_() {
// If the live stream has reached its end, do not sync.
if (!this.isLive()) {
return;
}
const seekRange = this.seekRange();
if (!Number.isFinite(seekRange.end)) {
return;
}
const currentTime = this.video_.currentTime;
if (currentTime > seekRange.start) {
// Bad stream?
return;
}
const liveSyncMaxLatency = this.config_.streaming.liveSyncMaxLatency;
const liveSyncPlaybackRate = this.config_.streaming.liveSyncPlaybackRate;
const playbackRate = this.video_.playbackRate;
const latency = seekRange.end - this.video_.currentTime;
let offset = 0;
// HLS in SRC= doesn't update every time the seekrange, so we need calculate
// a safe offset
if (this.loadMode_ = shaka.Player.LoadMode.SRC_EQUALS) {
const buffered = this.video_.buffered;
if (buffered.length > 0) {
const bufferedEnd = buffered.end(buffered.length - 1);
offset = Math.max(liveSyncPlaybackRate, bufferedEnd - seekRange.end);
}
}

if ((latency - offset) > liveSyncMaxLatency) {
if (playbackRate != liveSyncPlaybackRate) {
this.trickPlay(liveSyncPlaybackRate);
}
} else if (playbackRate !== 1 && playbackRate !== 0) {
this.cancelTrickPlay();
}
}

/**
* Callback from Playhead.
*
Expand Down
3 changes: 3 additions & 0 deletions lib/util/player_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,9 @@ shaka.util.PlayerConfiguration = class {
// When low latency streaming is enabled, segmentPrefetchLimit will
// default to 2 if not specified.
segmentPrefetchLimit: 0,
liveSync: false,
liveSyncMaxLatency: 1,
liveSyncPlaybackRate: 1.2,
};

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

0 comments on commit 8640c44

Please sign in to comment.