import QUnit from 'qunit'; import { constructTemplateUrl, parseTemplateInfo, segmentsFromTemplate } from '../../src/segment/segmentTemplate'; QUnit.module('segmentTemplate - constructTemplateUrl'); QUnit.test('does not change url with no identifiers', function(assert) { const url = 'path/to/segment.mp4'; assert.equal(constructTemplateUrl(url, {}), url, 'no change'); }); QUnit.test('replaces each identifier individually', function(assert) { const values = { RepresentationID: 'Rep1', Bandwidth: 1000, Number: 2, Time: 2000 }; const cases = [ { url: '/$RepresentationID$/segment.mp4', expected: '/Rep1/segment.mp4' }, { url: '/$Bandwidth$/segment.mp4', expected: '/1000/segment.mp4' }, { url: '/$Number$/segment.mp4', expected: '/2/segment.mp4' }, { url: '/$Time$/segment.mp4', expected: '/2000/segment.mp4' }, { url: '/$$/segment.mp4', expected: '/$/segment.mp4' } ]; cases.forEach(test => { assert.equal(constructTemplateUrl(test.url, values), test.expected, `constructs ${test.url}`); }); }); QUnit.test('replaces multiple identifiers in url', function(assert) { assert.equal( constructTemplateUrl( '$$$Time$$$$$/$RepresentationID$/$Bandwidth$/$Number$-$Time$-segment-$Number$.mp4', { RepresentationID: 'Rep1', Bandwidth: 1000, Number: 2, Time: 2000 } ), '$2000$$/Rep1/1000/2-2000-segment-2.mp4', 'correctly replaces multiple identifiers in single url' ); }); QUnit.test('does not replace unknown identifiers', function(assert) { assert.equal( constructTemplateUrl( '/$UNKNOWN$/$RepresentationID$/$UNKOWN2$/$Number$.mp4', { RepresentationID: 'Rep1', Number: 1 } ), '/$UNKNOWN$/Rep1/$UNKOWN2$/1.mp4', 'ignores unknown identifiers' ); }); QUnit.test('honors padding format tag', function(assert) { assert.equal( constructTemplateUrl( '/$Number%03d$/segment.mp4', { Number: 7 } ), '/007/segment.mp4', 'correctly adds padding when format tag present' ); }); QUnit.test('does not add padding when value is longer than width', function(assert) { assert.equal( constructTemplateUrl( '/$Bandwidth%06d$/segment.mp4', { Bandwidth: 999999999 } ), '/999999999/segment.mp4', 'no padding when value longer than format width' ); }); QUnit.test('does not use padding format tag for $RepresentationID$', function(assert) { assert.equal( constructTemplateUrl( '/$RepresentationID%09d$/$Number%03d$/segment.mp4', { RepresentationID: 'Rep1', Number: 7 } ), '/Rep1/007/segment.mp4', 'ignores format tag for $RepresentationID$' ); }); QUnit.module('segmentTemplate - parseTemplateInfo'); QUnit.test( 'one media segment when no @duration attribute or SegmentTimeline element', function(assert) { const attributes = { startNumber: 3, timescale: 1000, sourceDuration: 42, periodIndex: 1 }; assert.deepEqual( parseTemplateInfo(attributes, void 0), [ { number: 3, duration: 42, time: 0, timeline: 1 }], 'creates segment list of one media segment when no @duration attribute or timeline' ); } ); QUnit.test('uses @duration attribute when present', function(assert) { const attributes = { startNumber: 0, timescale: 1000, sourceDuration: 16, duration: 6000, periodIndex: 1 }; assert.deepEqual( parseTemplateInfo(attributes, []), [ { number: 0, duration: 6, timeline: 1, time: 0 }, { number: 1, duration: 6, timeline: 1, time: 6000 }, { number: 2, duration: 4, timeline: 1, time: 12000 } ], 'correctly parses segment durations and start times with @duration attribute' ); }); QUnit.test('parseByDuration allows non zero startNumber', function(assert) { const attributes = { startNumber: 101, timescale: 1000, sourceDuration: 16, duration: 6000, periodIndex: 1 }; assert.deepEqual( parseTemplateInfo(attributes, []), [ { number: 101, duration: 6, timeline: 1, time: 0 }, { number: 102, duration: 6, timeline: 1, time: 6000 }, { number: 103, duration: 4, timeline: 1, time: 12000 } ], 'allows non zero startNumber' ); }); QUnit.test('parseByDuration defaults 1 for startNumber and timescale', function(assert) { const attributes = { sourceDuration: 11, duration: '4', periodIndex: 1 }; assert.deepEqual( parseTemplateInfo(attributes, []), [ { number: 1, duration: 4, timeline: 1, time: 0 }, { number: 2, duration: 4, timeline: 1, time: 4 }, { number: 3, duration: 3, timeline: 1, time: 8 } ], 'uses default startNumber and timescale value of 1' ); }); QUnit.test('parseByDuration uses endNumber and has correct duration', function(assert) { const attributes = { sourceDuration: 11, duration: '4', periodIndex: 1, endNumber: '2' }; assert.deepEqual( parseTemplateInfo(attributes, []), [ { number: 1, duration: 4, timeline: 1, time: 0 }, { number: 2, duration: 7, timeline: 1, time: 4 } ], 'uses default startNumber and timescale value of 1' ); }); QUnit.test('uses SegmentTimeline info when no @duration attribute', function(assert) { const attributes = { startNumber: 0, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 6000 }, { d: 2000 }, { d: 3000 }, { d: 5000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 0, timeline: 1 }, { number: 1, duration: 2, time: 6000, timeline: 1 }, { number: 2, duration: 3, time: 8000, timeline: 1 }, { number: 3, duration: 5, time: 11000, timeline: 1 } ], 'correctly calculates segment durations and start times with SegmentTimeline' ); }); QUnit.test('parseByTimeline allows non zero startNumber', function(assert) { const attributes = { startNumber: 101, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 6000 }, { d: 2000 }, { d: 3000 }, { d: 5000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 101, duration: 6, time: 0, timeline: 1 }, { number: 102, duration: 2, time: 6000, timeline: 1 }, { number: 103, duration: 3, time: 8000, timeline: 1 }, { number: 104, duration: 5, time: 11000, timeline: 1 } ], 'allows non zero startNumber' ); }); QUnit.test('parseByTimeline defaults 1 for startNumber and timescale', function(assert) { const attributes = { sourceDuration: 11, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 4 }, { d: 2 }, { d: 3 }, { d: 2 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 1, duration: 4, time: 0, timeline: 1 }, { number: 2, duration: 2, time: 4, timeline: 1 }, { number: 3, duration: 3, time: 6, timeline: 1 }, { number: 4, duration: 2, time: 9, timeline: 1 } ], 'defaults to 1 for startNumber and timescale' ); }); QUnit.test('defaults SegmentTimeline.S@t to 0 for first segment', function(assert) { const attributes = { startNumber: 0, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { d: 6000 }, { d: 2000 }, { d: 3000 }, { d: 5000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 0, timeline: 1 }, { number: 1, duration: 2, time: 6000, timeline: 1 }, { number: 2, duration: 3, time: 8000, timeline: 1 }, { number: 3, duration: 5, time: 11000, timeline: 1 } ], 'uses default value of 0' ); }); QUnit.test('allows non zero starting SegmentTimeline.S@t value', function(assert) { const attributes = { startNumber: 0, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 42000, d: 6000 }, { d: 2000 }, { d: 3000 }, { d: 5000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 42000, timeline: 1 }, { number: 1, duration: 2, time: 48000, timeline: 1 }, { number: 2, duration: 3, time: 50000, timeline: 1 }, { number: 3, duration: 5, time: 53000, timeline: 1 } ], 'allows non zero SegmentTimeline.S@t start value' ); }); QUnit.test('honors @r repeat attribute for SegmentTimeline.S', function(assert) { const attributes = { startNumber: 0, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 6000 }, { d: 1000, r: 3 }, { d: 5000 }, { d: 1000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 0, timeline: 1 }, { number: 1, duration: 1, time: 6000, timeline: 1 }, { number: 2, duration: 1, time: 7000, timeline: 1 }, { number: 3, duration: 1, time: 8000, timeline: 1 }, { number: 4, duration: 1, time: 9000, timeline: 1 }, { number: 5, duration: 5, time: 10000, timeline: 1 }, { number: 6, duration: 1, time: 15000, timeline: 1 } ], 'correctly uses @r repeat attribute' ); }); QUnit.test('correctly handles negative @r repeat value', function(assert) { const attributes = { startNumber: 0, sourceDuration: 16, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 6000 }, { d: 1000, r: -1 }, { t: 10000, d: 5000 }, { d: 1000 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 0, timeline: 1 }, { number: 1, duration: 1, time: 6000, timeline: 1 }, { number: 2, duration: 1, time: 7000, timeline: 1 }, { number: 3, duration: 1, time: 8000, timeline: 1 }, { number: 4, duration: 1, time: 9000, timeline: 1 }, { number: 5, duration: 5, time: 10000, timeline: 1 }, { number: 6, duration: 1, time: 15000, timeline: 1 } ], 'correctly uses negative @r repeat attribute' ); }); QUnit.test('correctly handles negative @r repeat value for last S', function(assert) { const attributes = { startNumber: 0, sourceDuration: 15, timescale: 1000, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 6000 }, { d: 3000, r: -1 } ]; assert.deepEqual( parseTemplateInfo(attributes, segmentTimeline), [ { number: 0, duration: 6, time: 0, timeline: 1 }, { number: 1, duration: 3, time: 6000, timeline: 1 }, { number: 2, duration: 3, time: 9000, timeline: 1 }, { number: 3, duration: 3, time: 12000, timeline: 1 } ], 'correctly uses negative @r repeat attribute for last S' ); }); QUnit.skip( 'detects discontinuity when @t time is greater than expected start time', function(assert) { } ); QUnit.module('segmentTemplate - type ="dynamic"'); QUnit.test('correctly handles duration', function(assert) { const basicAttributes = { baseUrl: 'http://www.example.com/', type: 'dynamic', media: 'n-$Number$.m4s', minimumUpdatePeriod: 0, timescale: 1, NOW: 10000, clientOffset: 0, availabilityStartTime: 0, startNumber: 1, duration: 2, periodIndex: 1 }; assert.deepEqual( segmentsFromTemplate(basicAttributes, []), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 5, resolvedUri: 'http://www.example.com/n-5.m4s', timeline: 1, uri: 'n-5.m4s' }], 'segments correctly with basic settings' ); assert.deepEqual( segmentsFromTemplate(Object.assign({}, basicAttributes, { startNumber: 10 }), []), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 10, resolvedUri: 'http://www.example.com/n-10.m4s', timeline: 1, uri: 'n-10.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 11, resolvedUri: 'http://www.example.com/n-11.m4s', timeline: 1, uri: 'n-11.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 12, resolvedUri: 'http://www.example.com/n-12.m4s', timeline: 1, uri: 'n-12.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 13, resolvedUri: 'http://www.example.com/n-13.m4s', timeline: 1, uri: 'n-13.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 14, resolvedUri: 'http://www.example.com/n-14.m4s', timeline: 1, uri: 'n-14.m4s' }], 'segments adjusted correctly based on @startNumber' ); assert.deepEqual( segmentsFromTemplate(Object.assign( {}, basicAttributes, { availabilityStartTime: 4 } ), []), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }], 'segments correct with @availabilityStartTime set' ); assert.deepEqual( segmentsFromTemplate(Object.assign( {}, basicAttributes, { availabilityStartTime: 2, start: 4 } ), []), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }], 'segments correct with @availabilityStartTime and @start set' ); assert.deepEqual( segmentsFromTemplate(Object.assign( {}, basicAttributes, { timeShiftBufferDepth: 4 }, [] )), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 5, resolvedUri: 'http://www.example.com/n-5.m4s', timeline: 1, uri: 'n-5.m4s' }], 'segments correct with @timeShiftBufferDepth set' ); assert.deepEqual( segmentsFromTemplate(Object.assign( {}, basicAttributes, { clientOffset: -2000 }, [] )), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }], 'segments correct with given clientOffset' ); assert.deepEqual( segmentsFromTemplate(Object.assign( {}, basicAttributes, { endNumber: '4' }, [] )), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }], 'segments correct with endNumber' ); }); QUnit.test('correctly handles duration with segmentTimeline', function(assert) { const basicAttributes = { baseUrl: 'http://www.example.com/', type: 'dynamic', media: 'n-$Number$.m4s', minimumUpdatePeriod: 2, timescale: 1, NOW: 8000, clientOffset: 0, availabilityStartTime: 0, startNumber: 1, periodIndex: 1 }; const segmentTimeline = [ { t: 0, d: 2, r: 1 }, { d: 2, r: -1 } ]; assert.deepEqual( segmentsFromTemplate(basicAttributes, segmentTimeline), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 5, resolvedUri: 'http://www.example.com/n-5.m4s', timeline: 1, uri: 'n-5.m4s' }], 'segments should fill until current time when r = -1 and @minimumUpdatePeriod > 0' ); assert.deepEqual( segmentsFromTemplate(Object.assign({}, basicAttributes, {clientOffset: -2000}), segmentTimeline), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }], 'segments should fill correctly when taking client offset into account' ); const segmentTimelineShifted = [ { t: 2, d: 2, r: 1 }, { d: 2, r: -1 } ]; assert.deepEqual( segmentsFromTemplate(basicAttributes, segmentTimelineShifted), [{ duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 2, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }], 'segments take into account different time value for first segment' ); assert.deepEqual( segmentsFromTemplate( Object.assign({}, basicAttributes, {timescale: 2 }), segmentTimelineShifted ), [{ duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 1, resolvedUri: 'http://www.example.com/n-1.m4s', timeline: 1, uri: 'n-1.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 2, resolvedUri: 'http://www.example.com/n-2.m4s', timeline: 1, uri: 'n-2.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 3, resolvedUri: 'http://www.example.com/n-3.m4s', timeline: 1, uri: 'n-3.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 4, resolvedUri: 'http://www.example.com/n-4.m4s', timeline: 1, uri: 'n-4.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 5, resolvedUri: 'http://www.example.com/n-5.m4s', timeline: 1, uri: 'n-5.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 6, resolvedUri: 'http://www.example.com/n-6.m4s', timeline: 1, uri: 'n-6.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 7, resolvedUri: 'http://www.example.com/n-7.m4s', timeline: 1, uri: 'n-7.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 8, resolvedUri: 'http://www.example.com/n-8.m4s', timeline: 1, uri: 'n-8.m4s' }, { duration: 1, map: { resolvedUri: 'http://www.example.com/', uri: '' }, number: 9, resolvedUri: 'http://www.example.com/n-9.m4s', timeline: 1, uri: 'n-9.m4s' }], 'segments take into account different time value for first segment with timescale' ); }); QUnit.module('segmentTemplate - segmentsFromTemplate'); QUnit.test('constructs simple segment list and resolves uris', function(assert) { const attributes = { startNumber: 0, duration: 6000, sourceDuration: 16, timescale: 1000, bandwidth: 100, id: 'Rep1', initialization: { sourceURL: '$RepresentationID$/$Bandwidth$/init.mp4' }, media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4', periodIndex: 1, baseUrl: 'https://example.com/' }; const segments = [ { duration: 6, map: { resolvedUri: 'https://example.com/Rep1/100/init.mp4', uri: 'Rep1/100/init.mp4' }, resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4', timeline: 1, uri: 'Rep1/100/000-00000.mp4', number: 0 }, { duration: 6, map: { resolvedUri: 'https://example.com/Rep1/100/init.mp4', uri: 'Rep1/100/init.mp4' }, resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4', timeline: 1, uri: 'Rep1/100/001-06000.mp4', number: 1 }, { duration: 4, map: { resolvedUri: 'https://example.com/Rep1/100/init.mp4', uri: 'Rep1/100/init.mp4' }, resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4', timeline: 1, uri: 'Rep1/100/002-12000.mp4', number: 2 } ]; assert.deepEqual( segmentsFromTemplate(attributes, void 0), segments, 'creates segments from template' ); }); QUnit.test('constructs simple segment list and with node', function(assert) { const attributes = { startNumber: 0, duration: 6000, sourceDuration: 16, timescale: 1000, bandwidth: 100, id: 'Rep1', initialization: { sourceURL: 'init.mp4', range: '121-125' }, media: '$RepresentationID$/$Bandwidth$/$Number%03d$-$Time%05d$.mp4', periodIndex: 1, baseUrl: 'https://example.com/' }; const segments = [ { duration: 6, map: { resolvedUri: 'https://example.com/init.mp4', uri: 'init.mp4', byterange: { length: 5, offset: 121 } }, resolvedUri: 'https://example.com/Rep1/100/000-00000.mp4', timeline: 1, uri: 'Rep1/100/000-00000.mp4', number: 0 }, { duration: 6, map: { resolvedUri: 'https://example.com/init.mp4', uri: 'init.mp4', byterange: { length: 5, offset: 121 } }, resolvedUri: 'https://example.com/Rep1/100/001-06000.mp4', timeline: 1, uri: 'Rep1/100/001-06000.mp4', number: 1 }, { duration: 4, map: { resolvedUri: 'https://example.com/init.mp4', uri: 'init.mp4', byterange: { length: 5, offset: 121 } }, resolvedUri: 'https://example.com/Rep1/100/002-12000.mp4', timeline: 1, uri: 'Rep1/100/002-12000.mp4', number: 2 } ]; assert.deepEqual( segmentsFromTemplate(attributes, void 0), segments, 'creates segments from template' ); });