Skip to content

Commit 3cb40bf

Browse files
authored
feat(DASH): Add support for urn:mpeg:dash:ssr:2023 with SegmentTemplate $Number$ (#6745)
1 parent 9fcaf4d commit 3cb40bf

File tree

3 files changed

+117
-14
lines changed

3 files changed

+117
-14
lines changed

lib/dash/segment_template.js

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ shaka.dash.SegmentTemplate = class {
9696
generateSegmentIndex: () => {
9797
return SegmentTemplate.generateSegmentIndexFromDuration_(
9898
shallowCopyOfContext, info, segmentLimit, initSegmentReference,
99-
periodDurationMap, aesKey, lastSegmentNumber);
99+
periodDurationMap, aesKey, lastSegmentNumber,
100+
context.representation.segmentSequenceCadence);
100101
},
101102
};
102103
} else {
@@ -253,6 +254,14 @@ shaka.dash.SegmentTemplate = class {
253254
const index = MpdUtils.inheritAttribute(
254255
context, SegmentTemplate.fromInheritance_, 'index');
255256

257+
const k = MpdUtils.inheritAttribute(
258+
context, SegmentTemplate.fromInheritance_, 'k');
259+
260+
let numChunks = 0;
261+
if (k) {
262+
numChunks = parseInt(k, 10);
263+
}
264+
256265
return {
257266
segmentDuration: segmentInfo.segmentDuration,
258267
timescale: segmentInfo.timescale,
@@ -265,6 +274,7 @@ shaka.dash.SegmentTemplate = class {
265274
indexTemplate: index,
266275
mimeType: context.representation.mimeType,
267276
codecs: context.representation.codecs,
277+
numChunks: numChunks,
268278
};
269279
}
270280

@@ -358,12 +368,13 @@ shaka.dash.SegmentTemplate = class {
358368
* @param {!Object.<string, number>} periodDurationMap
359369
* @param {shaka.extern.aesKey|undefined} aesKey
360370
* @param {?number} lastSegmentNumber
371+
* @param {number} segmentSequenceCadence
361372
* @return {!Promise.<shaka.media.SegmentIndex>}
362373
* @private
363374
*/
364375
static generateSegmentIndexFromDuration_(
365376
context, info, segmentLimit, initSegmentReference, periodDurationMap,
366-
aesKey, lastSegmentNumber) {
377+
aesKey, lastSegmentNumber, segmentSequenceCadence) {
367378
goog.asserts.assert(info.mediaTemplate,
368379
'There should be a media template with duration');
369380

@@ -483,16 +494,6 @@ shaka.dash.SegmentTemplate = class {
483494
const segmentMediaTime = segmentPeriodTime +
484495
info.scaledPresentationTimeOffset;
485496

486-
const getUris = () => {
487-
let time = segmentMediaTime * timescale;
488-
if ('BigInt' in window && time > Number.MAX_SAFE_INTEGER) {
489-
time = BigInt(segmentMediaTime) * BigInt(timescale);
490-
}
491-
const mediaUri = MpdUtils.fillUriTemplate(
492-
template, id, position, /* subNumber= */ null, bandwidth, time);
493-
return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
494-
};
495-
496497
// Relative to the presentation.
497498
const segmentStart = segmentPeriodTime + periodStart;
498499
const trueSegmentEnd = segmentStart + segmentDuration;
@@ -505,6 +506,67 @@ shaka.dash.SegmentTemplate = class {
505506
goog.asserts.assert(segmentStart < segmentEnd,
506507
'Generated a segment outside of the period!');
507508

509+
const partialSegmentRefs = [];
510+
511+
const numChunks = info.numChunks;
512+
if (numChunks) {
513+
const partialDuration = (segmentEnd - segmentStart) / numChunks;
514+
515+
for (let i = 0; i < numChunks; i++) {
516+
const start = segmentStart + partialDuration * i;
517+
const end = start + partialDuration;
518+
const subNumber = i + 1;
519+
const getPartialUris = () => {
520+
let time = segmentMediaTime * timescale;
521+
if ('BigInt' in window && time > Number.MAX_SAFE_INTEGER) {
522+
time = BigInt(segmentMediaTime) * BigInt(timescale);
523+
}
524+
const mediaUri = MpdUtils.fillUriTemplate(
525+
template, id, position, subNumber, bandwidth, time);
526+
return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
527+
};
528+
const partial = new shaka.media.SegmentReference(
529+
start,
530+
end,
531+
getPartialUris,
532+
/* startByte= */ 0,
533+
/* endByte= */ null,
534+
initSegmentReference,
535+
timestampOffset,
536+
/* appendWindowStart= */ periodStart,
537+
/* appendWindowEnd= */ getPeriodEnd(),
538+
/* partialReferences= */ [],
539+
/* tilesLayout= */ '',
540+
/* tileDuration= */ null,
541+
/* syncTime= */ null,
542+
shaka.media.SegmentReference.Status.AVAILABLE,
543+
aesKey);
544+
partial.codecs = context.representation.codecs;
545+
partial.mimeType = context.representation.mimeType;
546+
if (segmentSequenceCadence == 0) {
547+
if (i > 0) {
548+
partial.markAsNonIndependent();
549+
}
550+
} else if ((i % segmentSequenceCadence) != 0) {
551+
partial.markAsNonIndependent();
552+
}
553+
partialSegmentRefs.push(partial);
554+
}
555+
}
556+
557+
const getUris = () => {
558+
if (numChunks) {
559+
return [];
560+
}
561+
let time = segmentMediaTime * timescale;
562+
if ('BigInt' in window && time > Number.MAX_SAFE_INTEGER) {
563+
time = BigInt(segmentMediaTime) * BigInt(timescale);
564+
}
565+
const mediaUri = MpdUtils.fillUriTemplate(
566+
template, id, position, /* subNumber= */ null, bandwidth, time);
567+
return ManifestParserUtils.resolveUris(getBaseUris(), [mediaUri]);
568+
};
569+
508570
const ref = new shaka.media.SegmentReference(
509571
segmentStart,
510572
segmentEnd,
@@ -515,7 +577,7 @@ shaka.dash.SegmentTemplate = class {
515577
timestampOffset,
516578
/* appendWindowStart= */ periodStart,
517579
/* appendWindowEnd= */ getPeriodEnd(),
518-
/* partialReferences= */ [],
580+
partialSegmentRefs,
519581
/* tilesLayout= */ '',
520582
/* tileDuration= */ null,
521583
/* syncTime= */ null,
@@ -1058,7 +1120,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
10581120
* mediaTemplate: ?string,
10591121
* indexTemplate: ?string,
10601122
* mimeType: string,
1061-
* codecs: string
1123+
* codecs: string,
1124+
* numChunks: number
10621125
* }}
10631126
*
10641127
* @description
@@ -1084,5 +1147,7 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
10841147
* The mimeType.
10851148
* @property {string} codecs
10861149
* The codecs.
1150+
* @property {number} numChunks
1151+
* The number of chunks in each segment.
10871152
*/
10881153
shaka.dash.SegmentTemplate.SegmentTemplateInfo;

test/dash/dash_parser_manifest_unit.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2878,6 +2878,43 @@ describe('DashParser Manifest', () => {
28782878
const fourthPartialReference = firstReference.partialReferences[3];
28792879
expect(fourthPartialReference.isIndependent()).toBeFalsy();
28802880
});
2881+
2882+
it('with SegmentTemplate and $Number$', async () => {
2883+
const manifestText = [
2884+
'<MPD minBufferTime="PT75S">',
2885+
' <Period id="1" duration="PT30S">',
2886+
' <AdaptationSet id="1" mimeType="video/mp4">',
2887+
' <Representation bandwidth="1" codecs="avc1.4d401f">',
2888+
' <SegmentTemplate startNumber="1" k="4"',
2889+
' media="l-$Number$-p$SubNumber$.mp4"',
2890+
' initialization="init.mp4" timescale="50" duration="100">',
2891+
' </SegmentTemplate>',
2892+
' <SegmentSequenceProperties>',
2893+
' <SAP type="1" cadence="1" />',
2894+
' </SegmentSequenceProperties>',
2895+
' </Representation>',
2896+
' </AdaptationSet>',
2897+
' </Period>',
2898+
'</MPD>',
2899+
].join('\n');
2900+
2901+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
2902+
/** @type {shaka.extern.Manifest} */
2903+
const manifest = await parser.start('dummy://foo', playerInterface);
2904+
const stream = manifest.variants[0].video;
2905+
await stream.createSegmentIndex();
2906+
goog.asserts.assert(stream.segmentIndex != null, 'Null segmentIndex!');
2907+
2908+
const firstReference = stream.segmentIndex.get(0);
2909+
const firstPartialReference = firstReference.partialReferences[0];
2910+
expect(firstPartialReference.isIndependent()).toBeTruthy();
2911+
const secondPartialReference = firstReference.partialReferences[1];
2912+
expect(secondPartialReference.isIndependent()).toBeTruthy();
2913+
const thirdPartialReference = firstReference.partialReferences[2];
2914+
expect(thirdPartialReference.isIndependent()).toBeTruthy();
2915+
const fourthPartialReference = firstReference.partialReferences[3];
2916+
expect(fourthPartialReference.isIndependent()).toBeTruthy();
2917+
});
28812918
});
28822919

28832920
describe('supports ContentSteering', () => {

test/dash/dash_parser_segment_template_unit.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -926,5 +926,6 @@ function makeTemplateInfo(timeline) {
926926
'indexTemplate': null,
927927
'mimeType': 'video/mp4',
928928
'codecs': 'avc1.42E01E',
929+
'numChunks': 0,
929930
};
930931
}

0 commit comments

Comments
 (0)