Skip to content

Commit

Permalink
feat: add presentationTime as an attribute on each SegmentList segment (
Browse files Browse the repository at this point in the history
#142)

Now that presentationTime is included on segment objects, and is already
adjusted by the presentationTimeOffset, the presentationTimeOffset
attribute has been removed from the output to make the segment output
more understandable (without any qualifications of whether offset times
are already accounted for).

BREAKING CHANGE: the presentationTimeOffset attribute has been removed from output
  • Loading branch information
gesinger authored Aug 10, 2021
1 parent fd85fd6 commit 478abb0
Show file tree
Hide file tree
Showing 11 changed files with 215 additions and 61 deletions.
11 changes: 10 additions & 1 deletion src/segment/segmentList.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ const SegmentURLToSegmentObject = (attributes, segmentUrl) => {
export const segmentsFromList = (attributes, segmentTimeline) => {
const {
duration,
segmentUrls = []
segmentUrls = [],
periodStart
} = attributes;

// Per spec (5.3.9.2.1) no way to determine segment duration OR
Expand All @@ -75,10 +76,18 @@ export const segmentsFromList = (attributes, segmentTimeline) => {
const segments = segmentTimeInfo.map((segmentTime, index) => {
if (segmentUrlMap[index]) {
const segment = segmentUrlMap[index];
// See DASH spec section 5.3.9.2.2
// - if timescale isn't present on any level, default to 1.
const timescale = attributes.timescale || 1;
// - if presentationTimeOffset isn't present on any level, default to 0
const presentationTimeOffset = attributes.presentationTimeOffset || 0;

segment.timeline = segmentTime.timeline;
segment.duration = segmentTime.duration;
segment.number = segmentTime.number;
segment.presentationTime =
periodStart + ((segmentTime.time - presentationTimeOffset) / timescale);

return segment;
}
// Since we're mapping we should get rid of any blank segments (in case
Expand Down
7 changes: 1 addition & 6 deletions src/segment/segmentTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,10 @@ export const segmentsFromTemplate = (attributes, segmentTimeline) => {
const timescale = attributes.timescale || 1;
// - if presentationTimeOffset isn't present on any level, default to 0
const presentationTimeOffset = attributes.presentationTimeOffset || 0;
// presentationTimeOffset has already been adjusted by the timescale
const presentationTime =
// Even if the @t attribute is not specified for the segment, segment.time is
// calculated in mpd-parser prior to this, so it's assumed to be available.
attributes.periodStart + (segment.time / timescale) - presentationTimeOffset;
attributes.periodStart + ((segment.time - presentationTimeOffset) / timescale);

const map = {
uri,
Expand All @@ -178,10 +177,6 @@ export const segmentsFromTemplate = (attributes, segmentTimeline) => {
presentationTime
};

if (attributes.presentationTimeOffset) {
map.presentationTimeOffset = attributes.presentationTimeOffset;
}

return map;
});
};
6 changes: 0 additions & 6 deletions src/toPlaylists.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,6 @@ export const generateSegments = ({ attributes, segmentInfo }) => {
if (segmentInfo.template) {
segmentsFn = segmentsFromTemplate;
segmentAttributes = merge(attributes, segmentInfo.template);

if (segmentInfo.template.presentationTimeOffset) {
segmentAttributes.presentationTimeOffset =
segmentInfo.template.presentationTimeOffset / segmentInfo.template.timescale;
}

} else if (segmentInfo.base) {
segmentsFn = segmentsFromBase;
segmentAttributes = merge(attributes, segmentInfo.base);
Expand Down
8 changes: 8 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ 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 multiperiodSegmentList from './manifests/multiperiod-segment-list.mpd';
import multiperiodDynamic from './manifests/multiperiod-dynamic.mpd';
import audioOnly from './manifests/audio-only.mpd';
import {
Expand All @@ -41,6 +42,9 @@ import {
import {
parsedManifest as multiperiodSegmentTemplateManifest
} from './manifests/multiperiod-segment-template.js';
import {
parsedManifest as multiperiodSegmentListManifest
} from './manifests/multiperiod-segment-list.js';
import {
parsedManifest as multiperiodDynamicManifest
} from './manifests/multiperiod-dynamic.js';
Expand Down Expand Up @@ -101,6 +105,10 @@ QUnit.test('has parse', function(assert) {
name: 'multiperiod_segment_template',
input: multiperiodSegmentTemplate,
expected: multiperiodSegmentTemplateManifest
}, {
name: 'multiperiod_segment_list',
input: multiperiodSegmentList,
expected: multiperiodSegmentListManifest
}, {
name: 'multiperiod_dynamic',
input: multiperiodDynamic,
Expand Down
29 changes: 8 additions & 21 deletions test/manifests/multiperiod-dynamic.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ export const parsedManifest = {
},
presentationTime: 19.938462809458052,
number: 14,
presentationTimeOffset: 4.85297052154195,
discontinuity: true
},
{
Expand All @@ -218,8 +217,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_audio128_2/init3.m4f'
},
presentationTime: 21.099460541884355,
number: 15,
presentationTimeOffset: 4.85297052154195
number: 15
},
{
uri: 'https://example.com/default_audio128_2/segment16.m4f',
Expand Down Expand Up @@ -475,7 +473,6 @@ export const parsedManifest = {
},
presentationTime: 19.938462809458052,
number: 14,
presentationTimeOffset: 4.85297052154195,
discontinuity: true
},
{
Expand All @@ -488,8 +485,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_audio96_2/init3.m4f'
},
presentationTime: 21.099460541884355,
number: 15,
presentationTimeOffset: 4.85297052154195
number: 15
},
{
uri: 'https://example.com/default_audio96_2/segment16.m4f',
Expand Down Expand Up @@ -751,7 +747,6 @@ export const parsedManifest = {
},
presentationTime: 19.953266664333334,
number: 13,
presentationTimeOffset: 4.838166666666667,
discontinuity: true
},
{
Expand All @@ -764,8 +759,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video2000_0_1280x720/init3.m4f'
},
presentationTime: 20.920899997666666,
number: 14,
presentationTimeOffset: 4.838166666666667
number: 14
},
{
uri: 'https://example.com/default_video2000_0_1280x720/segment15.m4f',
Expand All @@ -777,8 +771,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video2000_0_1280x720/init3.m4f'
},
presentationTime: 22.856166664333333,
number: 15,
presentationTimeOffset: 4.838166666666667
number: 15
},
{
uri: 'https://example.com/default_video2000_0_1280x720/segment16.m4f',
Expand Down Expand Up @@ -1028,7 +1021,6 @@ export const parsedManifest = {
},
presentationTime: 19.953266664333334,
number: 13,
presentationTimeOffset: 4.838166666666667,
discontinuity: true
},
{
Expand All @@ -1041,8 +1033,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video1200_1_960x540/init3.m4f'
},
presentationTime: 20.920899997666666,
number: 14,
presentationTimeOffset: 4.838166666666667
number: 14
},
{
uri: 'https://example.com/default_video1200_1_960x540/segment15.m4f',
Expand All @@ -1054,8 +1045,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video1200_1_960x540/init3.m4f'
},
presentationTime: 22.856166664333333,
number: 15,
presentationTimeOffset: 4.838166666666667
number: 15
},
{
uri: 'https://example.com/default_video1200_1_960x540/segment16.m4f',
Expand Down Expand Up @@ -1305,7 +1295,6 @@ export const parsedManifest = {
},
presentationTime: 19.953266664333334,
number: 13,
presentationTimeOffset: 4.838166666666667,
discontinuity: true
},
{
Expand All @@ -1318,8 +1307,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video900_1_640x360/init3.m4f'
},
presentationTime: 20.920899997666666,
number: 14,
presentationTimeOffset: 4.838166666666667
number: 14
},
{
uri: 'https://example.com/default_video900_1_640x360/segment15.m4f',
Expand All @@ -1331,8 +1319,7 @@ export const parsedManifest = {
resolvedUri: 'https://example.com/default_video900_1_640x360/init3.m4f'
},
presentationTime: 22.856166664333333,
number: 15,
presentationTimeOffset: 4.838166666666667
number: 15
},
{
uri: 'https://example.com/default_video900_1_640x360/segment16.m4f',
Expand Down
87 changes: 87 additions & 0 deletions test/manifests/multiperiod-segment-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
export const parsedManifest = {
allowCache: true,
discontinuityStarts: [],
duration: 12,
endList: true,
mediaGroups: {
'AUDIO': {},
'CLOSED-CAPTIONS': {},
'SUBTITLES': {},
'VIDEO': {}
},
playlists: [
{
attributes: {
'AUDIO': 'audio',
'BANDWIDTH': 449000,
'CODECS': 'avc1.420015',
'NAME': '482',
'PROGRAM-ID': 1,
'RESOLUTION': {
height: 270,
width: 482
},
'SUBTITLES': 'subs'
},
endList: true,
mediaSequence: 1,
targetDuration: 3,
resolvedUri: '',
segments: [
{
duration: 3,
map: {
uri: '',
resolvedUri: 'https://www.example.com/base'
},
resolvedUri: 'https://www.example.com/low/segment-1.ts',
timeline: 0,
presentationTime: 0,
uri: 'low/segment-1.ts',
number: 1
},
{
duration: 3,
map: {
uri: '',
resolvedUri: 'https://www.example.com/base'
},
resolvedUri: 'https://www.example.com/low/segment-2.ts',
timeline: 0,
presentationTime: 3,
uri: 'low/segment-2.ts',
number: 2
},
{
discontinuity: true,
duration: 3,
map: {
uri: '',
resolvedUri: 'https://www.example.com/base'
},
resolvedUri: 'https://www.example.com/low/segment-1.ts',
timeline: 1,
presentationTime: 6,
uri: 'low/segment-1.ts',
number: 1
},
{
duration: 3,
map: {
uri: '',
resolvedUri: 'https://www.example.com/base'
},
resolvedUri: 'https://www.example.com/low/segment-2.ts',
timeline: 1,
presentationTime: 9,
uri: 'low/segment-2.ts',
number: 2
}
],
timeline: 0,
uri: ''
}
],
segments: [],
uri: ''
};
33 changes: 33 additions & 0 deletions test/manifests/multiperiod-segment-list.mpd
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<MPD
xmlns="urn:mpeg:dash:schema:mpd:2011"
profiles="urn:mpeg:dash:profile:isoff-live:2011"
type="static"
mediaPresentationDuration="PT12S"
minBufferTime="PT3.000S">
<BaseURL>https://www.example.com/base</BaseURL>
<Period duration="PT6S">
<AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1">
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" >
</Representation>
<SegmentList timescale="1000" duration="3000">
<RepresentationIndex sourceURL="representation-index-low"/>
<SegmentURL media="low/segment-1.ts"/>
<SegmentURL media="low/segment-2.ts"/>
</SegmentList>
</AdaptationSet>
</Period>
<Period>
<AdaptationSet mimeType="video/mp4" scanType="progressive" segmentAlignment="true" startWithSAP="1">
<Role schemeIdUri="urn:mpeg:dash:role:2011" value="main"></Role>
<Representation bandwidth="449000" codecs="avc1.420015" frameRate="2997/125" height="270" id="482" width="482" >
</Representation>
<SegmentList timescale="1000" duration="3000">
<RepresentationIndex sourceURL="representation-index-low"/>
<SegmentURL media="low/segment-1.ts"/>
<SegmentURL media="low/segment-2.ts"/>
</SegmentList>
</AdaptationSet>
</Period>
</MPD>
Loading

0 comments on commit 478abb0

Please sign in to comment.