Skip to content

Commit

Permalink
feat(DASH): Parse and use target latency (#6683)
Browse files Browse the repository at this point in the history
Related to #6193
Thanks to @gkatsev
  • Loading branch information
avelad authored May 29, 2024
1 parent c574be4 commit 9060ab0
Show file tree
Hide file tree
Showing 7 changed files with 84 additions and 26 deletions.
4 changes: 4 additions & 0 deletions demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,10 @@ shakaDemo.Config = class {
.addBoolInput_('Disable Video Prefetch',
'streaming.disableVideoPrefetch')
.addBoolInput_('Live Sync', 'streaming.liveSync')
.addNumberInput_('Target latency tolerance',
'streaming.liveSyncTargetLatencyTolerance',
/* canBeDecimal= */ true,
/* canBeZero= */ true)
.addNumberInput_('Max latency for live sync',
'streaming.liveSyncMaxLatency',
/* canBeDecimal= */ true,
Expand Down
3 changes: 3 additions & 0 deletions externs/shaka/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ shaka.extern.InitDataOverride;

/**
* @typedef {{
* targetLatency:?number,
* maxLatency: ?number,
* maxPlaybackRate: ?number,
* minLatency: ?number,
Expand All @@ -167,6 +168,8 @@ shaka.extern.InitDataOverride;
* minPlaybackRate to increase latency.
* More information {@link https://dashif.org/docs/CR-Low-Latency-Live-r8.pdf here}.
*
* @property {?number} targetLatency
* The target latency to aim for.
* @property {?number} maxLatency
* Maximum latency in seconds.
* @property {?number} maxPlaybackRate
Expand Down
4 changes: 4 additions & 0 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -1258,6 +1258,7 @@ shaka.extern.ManifestConfiguration;
* disableTextPrefetch: boolean,
* disableVideoPrefetch: boolean,
* liveSync: boolean,
* liveSyncTargetLatencyTolerance: number,
* liveSyncMaxLatency: number,
* liveSyncPlaybackRate: number,
* liveSyncMinLatency: number,
Expand Down Expand Up @@ -1422,6 +1423,9 @@ shaka.extern.ManifestConfiguration;
* 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} liveSyncTargetLatencyTolerance
* Latency tolerance for target latency, in seconds. Effective only if
* liveSync is true. Defaults to <code>0.5</code>.
* @property {number} liveSyncMaxLatency
* Maximum acceptable latency, in seconds. Effective only if liveSync is
* true. Defaults to <code>1</code>.
Expand Down
50 changes: 30 additions & 20 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -1143,29 +1143,39 @@ shaka.dash.DashParser = class {
const latencyNode = TXml.findChild(elem, 'Latency');
const playbackRateNode = TXml.findChild(elem, 'PlaybackRate');

if ((latencyNode && latencyNode.attributes['max']) || playbackRateNode) {
const maxLatency = latencyNode && latencyNode.attributes['max'] ?
parseInt(latencyNode.attributes['max'], 10) / 1000 :
null;
const maxPlaybackRate = playbackRateNode ?
parseFloat(playbackRateNode.attributes['max']) :
null;
const minLatency = latencyNode && latencyNode.attributes['min'] ?
parseInt(latencyNode.attributes['min'], 10) / 1000 :
null;
const minPlaybackRate = playbackRateNode ?
parseFloat(playbackRateNode.attributes['min']) :
null;
if (!latencyNode && !playbackRateNode) {
return null;
}

return {
maxLatency,
maxPlaybackRate,
minLatency,
minPlaybackRate,
};
const description = {};

if (latencyNode) {
if ('target' in latencyNode.attributes) {
description.targetLatency =
parseInt(latencyNode.attributes['target'], 10) / 1000;
}
if ('max' in latencyNode.attributes) {
description.maxLatency =
parseInt(latencyNode.attributes['max'], 10) / 1000;
}
if ('min' in latencyNode.attributes) {
description.minLatency =
parseInt(latencyNode.attributes['min'], 10) / 1000;
}
}

return null;
if (playbackRateNode) {
if ('max' in playbackRateNode.attributes) {
description.maxPlaybackRate =
parseFloat(playbackRateNode.attributes['max']);
}
if ('min' in playbackRateNode.attributes) {
description.minPlaybackRate =
parseFloat(playbackRateNode.attributes['min']);
}
}

return description;
}

/**
Expand Down
20 changes: 16 additions & 4 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -6220,8 +6220,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
// serviceDescription must override if it is defined in the MPD and
// liveSync configuration is not set.
if (this.manifest_ && this.manifest_.serviceDescription) {
liveSyncMaxLatency = this.manifest_.serviceDescription.maxLatency ||
this.config_.streaming.liveSyncMaxLatency;
liveSyncMaxLatency = this.config_.streaming.liveSyncMaxLatency;
if (this.manifest_.serviceDescription.targetLatency != null) {
liveSyncMaxLatency =
this.manifest_.serviceDescription.targetLatency +
this.config_.streaming.liveSyncTargetLatencyTolerance;
} else if (this.manifest_.serviceDescription.maxLatency != null) {
liveSyncMaxLatency = this.manifest_.serviceDescription.maxLatency;
}
liveSyncPlaybackRate =
this.manifest_.serviceDescription.maxPlaybackRate ||
this.config_.streaming.liveSyncPlaybackRate;
Expand All @@ -6237,8 +6243,14 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
// serviceDescription must override if it is defined in the MPD and
// liveSync configuration is not set.
if (this.manifest_ && this.manifest_.serviceDescription) {
liveSyncMinLatency = this.manifest_.serviceDescription.minLatency ||
this.config_.streaming.liveSyncMinLatency;
liveSyncMinLatency = this.config_.streaming.liveSyncMinLatency;
if (this.manifest_.serviceDescription.targetLatency != null) {
liveSyncMinLatency =
this.manifest_.serviceDescription.targetLatency -
this.config_.streaming.liveSyncTargetLatencyTolerance;
} else if (this.manifest_.serviceDescription.minLatency != null) {
liveSyncMinLatency = this.manifest_.serviceDescription.minLatency;
}
liveSyncMinPlaybackRate =
this.manifest_.serviceDescription.minPlaybackRate ||
this.config_.streaming.liveSyncMinPlaybackRate;
Expand Down
1 change: 1 addition & 0 deletions lib/util/player_configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ shaka.util.PlayerConfiguration = class {
disableTextPrefetch: false,
disableVideoPrefetch: false,
liveSync: false,
liveSyncTargetLatencyTolerance: 0.5,
liveSyncMaxLatency: 1,
liveSyncPlaybackRate: 1.1,
liveSyncMinLatency: 0,
Expand Down
28 changes: 26 additions & 2 deletions test/dash/dash_parser_manifest_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2614,7 +2614,7 @@ describe('DashParser Manifest', () => {
'<MPD minBufferTime="PT75S" type="dynamic"',
' availabilityStartTime="1970-01-01T00:00:00Z">',
' <ServiceDescription id="0">',
' <Latency max="2000" min="1000" referenceId="0" target="4000" />',
' <Latency max="4000" min="1000" referenceId="0" target="2000" />',
' <PlaybackRate max="1.10" min="0.95" />',
' </ServiceDescription>',
'</MPD>',
Expand All @@ -2625,11 +2625,35 @@ describe('DashParser Manifest', () => {
/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);

expect(manifest.serviceDescription.maxLatency).toBe(2);
expect(manifest.serviceDescription.targetLatency).toBe(2);
expect(manifest.serviceDescription.maxLatency).toBe(4);
expect(manifest.serviceDescription.maxPlaybackRate).toBe(1.1);
expect(manifest.serviceDescription.minLatency).toBe(1);
expect(manifest.serviceDescription.minPlaybackRate).toBe(0.95);
});

it('and excludes missing attributes', async () => {
const source = [
'<MPD minBufferTime="PT75S" type="dynamic"',
' availabilityStartTime="1970-01-01T00:00:00Z">',
' <ServiceDescription id="0">',
' <Latency referenceId="0" target="2000" />',
' <PlaybackRate max="1.10" min="0.95" />',
' </ServiceDescription>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', source);

/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);

expect(manifest.serviceDescription.targetLatency).toBe(2);
expect(manifest.serviceDescription.maxLatency).toBeUndefined();
expect(manifest.serviceDescription.maxPlaybackRate).toBe(1.1);
expect(manifest.serviceDescription.minLatency).toBeUndefined();
expect(manifest.serviceDescription.minPlaybackRate).toBe(0.95);
});
});

it('parses urn:mpeg:dash:chaining:2016', async () => {
Expand Down

0 comments on commit 9060ab0

Please sign in to comment.