Skip to content

Commit

Permalink
feat(DASH): Add initial support to urn:mpeg:dash:ssr:2023
Browse files Browse the repository at this point in the history
  • Loading branch information
avelad committed Oct 23, 2023
1 parent 85e14dd commit 343038e
Show file tree
Hide file tree
Showing 17 changed files with 139 additions and 77 deletions.
5 changes: 4 additions & 1 deletion externs/shaka/manifest.js
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,8 @@ shaka.extern.FetchCryptoKeysFunction;
* (!Array.<shaka.extern.Stream>|!Array.<shaka.extern.StreamDB>|
* undefined),
* mssPrivateData: (shaka.extern.MssPrivateData|undefined),
* external: boolean
* external: boolean,
* fastSwitching: boolean
* }}
*
* @description
Expand Down Expand Up @@ -519,6 +520,8 @@ shaka.extern.FetchCryptoKeysFunction;
* @property {boolean} external
* Indicate if the stream was added externally.
* Eg: external text tracks.
* @property {boolean} fastSwitching
* Indicate if the stream should be used for fast switching.
*
* @exportDoc
*/
Expand Down
5 changes: 4 additions & 1 deletion externs/shaka/offline.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ shaka.extern.ManifestDB;
* spatialAudio: boolean,
* closedCaptions: Map.<string, string>,
* tilesLayout: (string|undefined),
* external: boolean
* external: boolean,
* fastSwitching: boolean
* }}
*
* @property {number} id
Expand Down Expand Up @@ -208,6 +209,8 @@ shaka.extern.ManifestDB;
* @property {boolean} external
* Indicate if the stream was added externally.
* Eg: external text tracks.
* @property {boolean} fastSwitching
* Indicate if the stream should be used for fast switching.
*/
shaka.extern.StreamDB;

Expand Down
71 changes: 39 additions & 32 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -828,37 +828,17 @@ shaka.dash.DashParser = class {
}

const normalAdaptationSets = adaptationSets
.filter((as) => { return !as.trickModeFor && !as.isSegmentSequence; });

const segmentSequenceAdaptationSets = adaptationSets
.filter((as) => { return !as.trickModeFor && as.isSegmentSequence; });

let playbackAdaptationSets = normalAdaptationSets;
if (segmentSequenceAdaptationSets.length &&
segmentSequenceAdaptationSets.length == normalAdaptationSets.length) {
let sameStreamLengths = true;
for (let i = 0; i < segmentSequenceAdaptationSets.length; i++) {
const segmentSequenceStreams = segmentSequenceAdaptationSets[i].streams;
const normalStreams = normalAdaptationSets[i].streams;
if (segmentSequenceStreams.length != normalStreams.length) {
sameStreamLengths = false;
break;
}
}
if (sameStreamLengths) {
playbackAdaptationSets = segmentSequenceAdaptationSets;
}
}
.filter((as) => { return !as.trickModeFor; });

const trickModeAdaptationSets = adaptationSets
.filter((as) => { return as.trickModeFor; });

// Attach trick mode tracks to playback tracks.
// Attach trick mode tracks to normal tracks.
for (const trickModeSet of trickModeAdaptationSets) {
const targetIds = trickModeSet.trickModeFor.split(' ');
for (const playbackSet of playbackAdaptationSets) {
if (targetIds.includes(playbackSet.id)) {
for (const stream of playbackSet.streams) {
for (const normalSet of normalAdaptationSets) {
if (targetIds.includes(normalSet.id)) {
for (const stream of normalSet.streams) {
// There may be multiple trick mode streams, but we do not
// currently support that. Just choose one.
// TODO: https://github.com/shaka-project/shaka-player/issues/1528
Expand All @@ -872,19 +852,19 @@ shaka.dash.DashParser = class {

const audioStreams = this.getStreamsFromSets_(
this.config_.disableAudio,
playbackAdaptationSets,
normalAdaptationSets,
ContentType.AUDIO);
const videoStreams = this.getStreamsFromSets_(
this.config_.disableVideo,
playbackAdaptationSets,
normalAdaptationSets,
ContentType.VIDEO);
const textStreams = this.getStreamsFromSets_(
this.config_.disableText,
playbackAdaptationSets,
normalAdaptationSets,
ContentType.TEXT);
const imageStreams = this.getStreamsFromSets_(
this.config_.disableThumbnails,
playbackAdaptationSets,
normalAdaptationSets,
ContentType.IMAGE);

if (videoStreams.length === 0 && audioStreams.length === 0) {
Expand All @@ -905,8 +885,10 @@ shaka.dash.DashParser = class {
}

/**
* Gets the streams from the given sets or returns an empty array if disabled
* or no streams are found.
* Gets the streams from the given sets, performing a filter if it finds
* fast switching streams. Returns an empty array if disabled or no streams
* are found.
*
* @param {boolean} disabled
* @param {!Array.<!shaka.dash.DashParser.AdaptationInfo>} adaptationSets
* @param {string} contentType
Expand All @@ -917,14 +899,37 @@ shaka.dash.DashParser = class {
return [];
}

return adaptationSets.reduce((all, part) => {
const streams = adaptationSets.reduce((all, part) => {
if (part.contentType != contentType) {
return all;
}

all.push(...part.streams);
return all;
}, []);

const fastSwitchingStreams = streams.filter((s) => s.fastSwitching);
if (!fastSwitchingStreams.length) {
return streams;
}
return streams.filter((stream) => {
if (stream.fastSwitching) {
return true;
}
return !fastSwitchingStreams.find((fastStream) => {
return fastStream.width == stream.width &&
fastStream.height == stream.height &&
fastStream.frameRate == stream.frameRate &&
fastStream.language == stream.language &&
fastStream.label == stream.label &&
fastStream.mimeType == stream.mimeType &&
fastStream.codecs == stream.codecs &&
fastStream.hdr == stream.hdr &&
fastStream.channelsCount == stream.channelsCount &&
fastStream.audioSamplingRate == stream.audioSamplingRate &&
fastStream.spatialAudio == stream.spatialAudio;
});
});
}

/**
Expand Down Expand Up @@ -1184,6 +1189,7 @@ shaka.dash.DashParser = class {
closedCaptions, representation, accessibilityPurpose);
if (parsedRepresentation) {
parsedRepresentation.hdr = parsedRepresentation.hdr || videoRange;
parsedRepresentation.fastSwitching = isSegmentSequence;
}
return parsedRepresentation;
}).filter((s) => !!s);
Expand Down Expand Up @@ -1498,6 +1504,7 @@ shaka.dash.DashParser = class {
matchedStreams: [],
accessibilityPurpose,
external: false,
fastSwitching: false,
};
}

Expand Down
1 change: 1 addition & 0 deletions lib/hls/hls_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -2354,6 +2354,7 @@ shaka.hls.HlsParser = class {
tilesLayout: undefined,
accessibilityPurpose: null,
external: false,
fastSwitching: false,
};
}

Expand Down
1 change: 1 addition & 0 deletions lib/mss/mss_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -546,6 +546,7 @@ shaka.mss.MssParser = class {
},
accessibilityPurpose: null,
external: false,
fastSwitching: false,
};

// This is specifically for text tracks.
Expand Down
1 change: 1 addition & 0 deletions lib/offline/indexeddb/v1_storage_cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ shaka.offline.indexeddb.V1StorageCell = class
closedCaptions: null,
tilesLayout: undefined,
external: false,
fastSwitching: false,
};
}

Expand Down
1 change: 1 addition & 0 deletions lib/offline/indexeddb/v2_storage_cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ shaka.offline.indexeddb.V2StorageCell = class
closedCaptions: null,
tilesLayout: undefined,
external: false,
fastSwitching: false,
};
}

Expand Down
1 change: 1 addition & 0 deletions lib/offline/manifest_converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ shaka.offline.ManifestConverter = class {
tilesLayout: streamDB.tilesLayout,
accessibilityPurpose: null,
external: streamDB.external,
fastSwitching: streamDB.fastSwitching,
};

return stream;
Expand Down
1 change: 1 addition & 0 deletions lib/offline/storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -1327,6 +1327,7 @@ shaka.offline.Storage = class {
closedCaptions: stream.closedCaptions,
tilesLayout: stream.tilesLayout,
external: stream.external,
fastSwitching: stream.fastSwitching,
};

const startTime =
Expand Down
4 changes: 4 additions & 0 deletions lib/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -2446,6 +2446,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
closedCaptions: null,
accessibilityPurpose: null,
external: false,
fastSwitching: false,
},
bandwidth: 100,
allowedByApplication: true,
Expand Down Expand Up @@ -4965,6 +4966,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
closedCaptions: null,
accessibilityPurpose: null,
external: true,
fastSwitching: false,
};

const fullMimeType = shaka.util.MimeUtils.getFullType(
Expand Down Expand Up @@ -5120,6 +5122,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
tilesLayout: '1x1',
accessibilityPurpose: null,
external: true,
fastSwitching: false,
};

this.manifest_.imageStreams.push(stream);
Expand Down Expand Up @@ -5542,6 +5545,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
closedCaptions: null,
accessibilityPurpose: null,
external: false,
fastSwitching: false,
};
manifest.textStreams.push(textStream);
closedCaptionsSet.add(id);
Expand Down
2 changes: 2 additions & 0 deletions lib/util/periods.js
Original file line number Diff line number Diff line change
Expand Up @@ -1544,6 +1544,7 @@ shaka.util.PeriodCombiner = class {
spatialAudio: false,
closedCaptions: null,
external: false,
fastSwitching: false,
};
}

Expand Down Expand Up @@ -1584,6 +1585,7 @@ shaka.util.PeriodCombiner = class {
closedCaptions: null,
accessibilityPurpose: null,
external: false,
fastSwitching: false,
};
}

Expand Down
111 changes: 68 additions & 43 deletions test/dash/dash_parser_manifest_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -2765,51 +2765,76 @@ describe('DashParser Manifest', () => {
});
});

describe('parses urn:mpeg:dash:ssr:2023', () => {
it('prioritize over normal AdaptationSet when has the same number of streams', async () => { // eslint-disable-line max-len
const manifestText = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet id="1" mimeType="video/mp4">',
' <Representation bandwidth="1" codecs="avc1.4d401f">',
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4"',
' initialization="init.mp4" timescale="50" duration="100">',
' <SegmentTimeline>',
' <S t="0" d="100"/>',
' <S d="100"/>',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' <AdaptationSet id="2" mimeType="video/mp4">',
' <EssentialProperty value="video_primary" ',
' schemeIdUri="urn:mpeg:dash:ssr:2023" />',
' <Representation bandwidth="1" codecs="avc1.4d401f">',
' <SegmentTemplate startNumber="1"',
' media="l-$Number$-p$SubNumber$.mp4"',
' initialization="init.mp4" timescale="50" duration="100">',
' <SegmentTimeline>',
' <S t="0" d="100" k="4"/>',
' <S d="100" k="4"/>',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');
it('prioritize stream with urn:mpeg:dash:ssr:2023 over normal stream', async () => { // eslint-disable-line max-len
const manifestText = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet id="1" mimeType="video/mp4">',
' <Representation bandwidth="1" codecs="avc1.4d401f"',
' height="720" width="1280">',
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4"',
' initialization="init.mp4" timescale="50" duration="100">',
' <SegmentTimeline>',
' <S t="0" d="100"/>',
' <S d="100"/>',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' <Representation bandwidth="2" codecs="avc1.4d401f"',
' height="1080" width="1920">',
' <SegmentTemplate startNumber="1" media="l2-$Number$.mp4"',
' initialization="init.mp4" timescale="50" duration="100">',
' <SegmentTimeline>',
' <S t="0" d="100"/>',
' <S d="100"/>',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' <AdaptationSet id="2" mimeType="video/mp4">',
' <EssentialProperty value="video_primary" ',
' schemeIdUri="urn:mpeg:dash:ssr:2023" />',
' <Representation bandwidth="1" codecs="avc1.4d401f"',
' height="720" width="1280">',
' <SegmentTemplate startNumber="1"',
' media="l-$Number$-p$SubNumber$.mp4"',
' initialization="init.mp4" timescale="50" duration="100">',
' <SegmentTimeline>',
' <S t="0" d="100" k="4"/>',
' <S d="100" k="4"/>',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>',
].join('\n');

fakeNetEngine.setResponseText('dummy://foo', manifestText);
/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);
expect(manifest.variants.length).toBe(1);
fakeNetEngine.setResponseText('dummy://foo', manifestText);
/** @type {shaka.extern.Manifest} */
const manifest = await parser.start('dummy://foo', playerInterface);
expect(manifest.variants.length).toBe(2);

const stream = manifest.variants[0].video;
await stream.createSegmentIndex();
goog.asserts.assert(stream.segmentIndex != null, 'Null segmentIndex!');
const normalStream = manifest.variants[0].video;
expect(normalStream.width).toBe(1920);
expect(normalStream.height).toBe(1080);
expect(normalStream.fastSwitching).toBe(false);
await normalStream.createSegmentIndex();
goog.asserts.assert(normalStream.segmentIndex != null,
'Null segmentIndex!');

const firstNormalReference = normalStream.segmentIndex.get(0);
expect(firstNormalReference.partialReferences.length).toBe(0);

const stream = manifest.variants[1].video;
expect(stream.width).toBe(1280);
expect(stream.height).toBe(720);
expect(stream.fastSwitching).toBe(true);
await stream.createSegmentIndex();
goog.asserts.assert(stream.segmentIndex != null,
'Null segmentIndex!');

const firstReference = stream.segmentIndex.get(0);
expect(firstReference.partialReferences.length).toBe(4);
});
const firstReference = stream.segmentIndex.get(0);
expect(firstReference.partialReferences.length).toBe(4);
});
});
1 change: 1 addition & 0 deletions test/media/adaptation_set_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ describe('AdaptationSet', () => {
accessibilityPurpose: null,
external: false,
groupId: null,
fastSwitching: false,
};
}
});
Expand Down
Loading

0 comments on commit 343038e

Please sign in to comment.