Skip to content

Commit

Permalink
fix: generate proper number of segments for multiperiod content that …
Browse files Browse the repository at this point in the history
…uses segment template (#138)
  • Loading branch information
gesinger authored Jul 26, 2021
1 parent f118a8b commit 39109d0
Show file tree
Hide file tree
Showing 8 changed files with 381 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/inheritAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,10 @@ export const toAdaptationSets = (mpdAttributes, mpdBaseUrls) => (period, index)
periodIndex,
periodStart: period.attributes.start
});

if (typeof period.attributes.duration === 'number') {
periodAttributes.periodDuration = period.attributes.duration;
}
const adaptationSets = findChildren(period.node, 'AdaptationSet');
const periodSegmentInfo = getSegmentInformation(period.node);

Expand Down
23 changes: 17 additions & 6 deletions src/segment/durationTimeParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,21 @@ export const segmentRange = {
const {
duration,
timescale = 1,
sourceDuration
sourceDuration,
periodDuration
} = attributes;
const endNumber = parseEndNumber(attributes.endNumber);
const segmentDuration = duration / timescale;

return {
start: 0,
end: typeof endNumber === 'number' ? endNumber : Math.ceil(sourceDuration / (duration / timescale))
};
if (typeof endNumber === 'number') {
return { start: 0, end: endNumber };
}

if (typeof periodDuration === 'number') {
return { start: 0, end: periodDuration / segmentDuration };
}

return { start: 0, end: sourceDuration / segmentDuration };
},

/**
Expand Down Expand Up @@ -139,6 +146,7 @@ export const parseByDuration = (attributes) => {
type,
duration,
timescale = 1,
periodDuration,
sourceDuration
} = attributes;

Expand All @@ -147,9 +155,12 @@ export const parseByDuration = (attributes) => {

if (type === 'static') {
const index = segments.length - 1;
// section is either a period or the full source
const sectionDuration =
typeof periodDuration === 'number' ? periodDuration : sourceDuration;

// final segment may be less than full segment duration
segments[index].duration = sourceDuration - (duration / timescale * index);
segments[index].duration = sectionDuration - (duration / timescale * index);
}

return segments;
Expand Down
8 changes: 8 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import locationTemplate from './manifests/location.mpd';
import locationsTemplate from './manifests/locations.mpd';
import multiperiod from './manifests/multiperiod.mpd';
import webmsegments from './manifests/webmsegments.mpd';
import multiperiodSegmentTemplate from './manifests/multiperiod-segment-template.mpd';
import multiperiodDynamic from './manifests/multiperiod-dynamic.mpd';
import audioOnly from './manifests/audio-only.mpd';
import {
Expand All @@ -37,6 +38,9 @@ import {
import {
parsedManifest as webmsegmentsManifest
} from './manifests/webmsegments.js';
import {
parsedManifest as multiperiodSegmentTemplateManifest
} from './manifests/multiperiod-segment-template.js';
import {
parsedManifest as multiperiodDynamicManifest
} from './manifests/multiperiod-dynamic.js';
Expand Down Expand Up @@ -93,6 +97,10 @@ QUnit.test('has parse', function(assert) {
name: 'webmsegments',
input: webmsegments,
expected: webmsegmentsManifest
}, {
name: 'multiperiod_segment_template',
input: multiperiodSegmentTemplate,
expected: multiperiodSegmentTemplateManifest
}, {
name: 'multiperiod_dynamic',
input: multiperiodDynamic,
Expand Down
10 changes: 7 additions & 3 deletions test/inheritAttributes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ QUnit.test('end to end - basic multiperiod', function(assert) {
mediaPresentationDuration: 60,
mimeType: 'video/mp4',
periodIndex: 0,
periodDuration: 30,
// inferred start
periodStart: 0,
role: {
Expand Down Expand Up @@ -1166,6 +1167,7 @@ QUnit.test(
mediaPresentationDuration: 30,
mimeType: 'video/mp6',
periodIndex: 0,
periodDuration: 280.414,
periodStart: 0,
role: {
value: 'main'
Expand All @@ -1191,6 +1193,7 @@ QUnit.test(
mediaPresentationDuration: 30,
mimeType: 'video/mp4',
periodIndex: 0,
periodDuration: 280.414,
periodStart: 0,
height: 545,
role: {
Expand Down Expand Up @@ -1219,6 +1222,7 @@ QUnit.test(
mediaPresentationDuration: 30,
mimeType: 'text/vtt',
periodIndex: 0,
periodDuration: 280.414,
periodStart: 0,
role: {},
sourceDuration: 30,
Expand Down Expand Up @@ -1250,7 +1254,7 @@ QUnit.test(
const actual = inheritAttributes(stringToMpdXml(`
<MPD mediaPresentationDuration= "PT30S" >
<BaseURL>https://www.example.com/base/</BaseURL>
<Period duration= "PT0H4M40.414S" >
<Period>
<AdaptationSet mimeType= "video/mp4" >
<Role value= "main" ></Role>
<SegmentBase indexRange= "1212" indexRangeExact= "true" >
Expand Down Expand Up @@ -1370,7 +1374,7 @@ QUnit.test(
const actual = toPlaylists(inheritAttributes(stringToMpdXml(`
<MPD mediaPresentationDuration= "PT30S" >
<BaseURL>https://www.example.com/base</BaseURL>
<Period duration= "PT0H4M40.414S" >
<Period>
<AdaptationSet
mimeType= "video/mp4"
segmentAlignment= "true"
Expand Down Expand Up @@ -1807,7 +1811,7 @@ QUnit.test('Test to check use of either Segment Template or Segment List when bo
const actual = toPlaylists(inheritAttributes(stringToMpdXml(`
<MPD mediaPresentationDuration= "PT30S" >
<BaseURL>https://www.example.com/base</BaseURL>
<Period duration= "PT0H4M40.414S" >
<Period>
<AdaptationSet
mimeType= "video/mp4"
segmentAlignment= "true"
Expand Down
199 changes: 199 additions & 0 deletions test/manifests/multiperiod-segment-template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
export const parsedManifest = {
allowCache: true,
uri: '',
duration: 30,
discontinuityStarts: [],
segments: [],
endList: true,
mediaGroups: {
'AUDIO': {
audio: {
en: {
language: 'en',
autoselect: true,
default: true,
playlists: [
{
attributes: {
'NAME': '2',
'BANDWIDTH': 32000,
'CODECS': 'mp4a.40.2',
'PROGRAM-ID': 1
},
uri: '',
endList: true,
timeline: 1,
resolvedUri: '',
targetDuration: 5,
segments: [
{
uri: 'audio/segment_0.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_0.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 0
},
{
uri: 'audio/segment_1.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_1.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 1
},
{
uri: 'audio/segment_2.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_2.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 2
},
{
discontinuity: true,
uri: 'audio/segment_0.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_0.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 0
},
{
uri: 'audio/segment_1.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_1.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 1
},
{
uri: 'audio/segment_2.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/audio/segment_2.m4f',
map: {
uri: 'audio/init.m4f',
resolvedUri: 'https://www.example.com/audio/init.m4f'
},
number: 2
}
],
mediaSequence: 0
}
],
uri: ''
}
}
},
'VIDEO': {},
'CLOSED-CAPTIONS': {},
'SUBTITLES': {}
},
playlists: [
{
attributes: {
'NAME': '1',
'AUDIO': 'audio',
'SUBTITLES': 'subs',
'RESOLUTION': {
width: 480,
height: 200
},
'CODECS': 'avc1.4d001f',
'BANDWIDTH': 100000,
'PROGRAM-ID': 1
},
uri: '',
endList: true,
timeline: 1,
resolvedUri: '',
targetDuration: 5,
segments: [
{
uri: 'video/segment_0.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_0.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 0
},
{
uri: 'video/segment_1.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_1.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 1
},
{
uri: 'video/segment_2.m4f',
timeline: 1,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_2.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 2
},
{
discontinuity: true,
uri: 'video/segment_0.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_0.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 0
},
{
uri: 'video/segment_1.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_1.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 1
},
{
uri: 'video/segment_2.m4f',
timeline: 2,
duration: 5,
resolvedUri: 'https://www.example.com/video/segment_2.m4f',
map: {
uri: 'video/init.m4f',
resolvedUri: 'https://www.example.com/video/init.m4f'
},
number: 2
}
],
mediaSequence: 0
}
]
};
Loading

0 comments on commit 39109d0

Please sign in to comment.