From b244def2f420907991541b3ce5353733d1b1e5d5 Mon Sep 17 00:00:00 2001 From: Timothy Drews Date: Tue, 3 Feb 2015 11:20:42 -0800 Subject: [PATCH] Fix-up startNumber behavior. * Revert 5c7fca90b6dd12cda65dae3b44291cb1c379fb5f, which intended to allow startNumber to be 0, and maintain the previous behavior, which requires startNumber to begin at 1 (this appears to be in-line with the MPD spec). * Correctly compute the $Time$ placeholder. * Rename firstSegmentNumber to startNumber to clarify code. Closes #10 Change-Id: I9b565edc9e23b9ad7c6bc62829d6b0ab565008a4 --- lib/dash/mpd_parser.js | 11 ++-- lib/dash/mpd_processor.js | 38 +++++++------- spec/mpd_processor_spec.js | 105 +++++++++++++++++++++++++++++++++++-- spec/mpd_spec.js | 17 ++++++ 4 files changed, 141 insertions(+), 30 deletions(-) diff --git a/lib/dash/mpd_parser.js b/lib/dash/mpd_parser.js index ab63a60d82..f5c01459a3 100644 --- a/lib/dash/mpd_parser.js +++ b/lib/dash/mpd_parser.js @@ -427,7 +427,7 @@ shaka.dash.mpd.SegmentList = function() { this.segmentDuration = null; /** @type {number} */ - this.firstSegmentNumber = 1; + this.startNumber = 1; /** @type {shaka.dash.mpd.Initialization} */ this.initialization = null; @@ -475,7 +475,7 @@ shaka.dash.mpd.SegmentTemplate = function() { this.segmentDuration = null; /** @type {number} */ - this.firstSegmentNumber = 1; + this.startNumber = 1; /** @type {?string} */ this.mediaUrlTemplate = null; @@ -957,7 +957,7 @@ shaka.dash.mpd.SegmentList.prototype.parse = function(parent, elem) { this.segmentDuration = mpd.parseAttr_(elem, 'duration', mpd.parseNonNegativeInt_); - this.firstSegmentNumber = + this.startNumber = mpd.parseAttr_(elem, 'startNumber', mpd.parsePositiveInt_) || 1; // Parse simple children @@ -1000,9 +1000,8 @@ shaka.dash.mpd.SegmentTemplate.prototype.parse = function(parent, elem) { this.segmentDuration = mpd.parseAttr_(elem, 'duration', mpd.parseNonNegativeInt_); - // The startNumber attribute defaults to 1 but may be set to 0 explicitly. - this.firstSegmentNumber = - mpd.parseAttr_(elem, 'startNumber', mpd.parseNonNegativeInt_) || 1; + this.startNumber = + mpd.parseAttr_(elem, 'startNumber', mpd.parsePositiveInt_) || 1; this.mediaUrlTemplate = mpd.parseAttr_(elem, 'media', mpd.parseString_); this.indexUrlTemplate = mpd.parseAttr_(elem, 'index', mpd.parseString_); diff --git a/lib/dash/mpd_processor.js b/lib/dash/mpd_processor.js index 716f018e6d..e9b6bb725c 100644 --- a/lib/dash/mpd_processor.js +++ b/lib/dash/mpd_processor.js @@ -162,7 +162,7 @@ shaka.dash.MpdProcessor.prototype.calculateDurations_ = function(mpd) { * @private */ shaka.dash.MpdProcessor.prototype.calculatePeriodDuration_ = function(period) { - if (period.duration) { + if (period.duration != null) { return; } @@ -322,7 +322,7 @@ shaka.dash.MpdProcessor.prototype.processSegmentTemplates_ = function(mpd) { 'Ignoring segment timeline because an explicit segment index ' + 'URL was provided for the SegmentTemplate.'); } - if (segmentTemplate.duration) { + if (segmentTemplate.segmentDuration) { shaka.log.warning( 'Ignoring segment duration because an explicit segment index ' + 'URL was provided for the SegmentTemplate.'); @@ -346,7 +346,7 @@ shaka.dash.MpdProcessor.prototype.processSegmentTemplates_ = function(mpd) { --k; } } else if (segmentTemplate.segmentDuration) { - if (period.duration) { + if (period.duration != null) { this.generateSegmentListFromDuration_( representation, period.duration); @@ -456,7 +456,7 @@ shaka.dash.MpdProcessor.prototype.generateSegmentListFromTimeline_ = function( // lengths. segmentList.timescale = segmentTemplate.timescale; segmentList.presentationTimeOffset = segmentTemplate.presentationTimeOffset; - segmentList.firstSegmentNumber = segmentTemplate.firstSegmentNumber; + segmentList.startNumber = segmentTemplate.startNumber; segmentList.initialization = this.generateInitialization_(representation); segmentList.segmentUrls = []; @@ -509,7 +509,7 @@ shaka.dash.MpdProcessor.prototype.generateSegmentListFromTimeline_ = function( var filledUrlTemplate = this.fillUrlTemplate_( segmentTemplate.mediaUrlTemplate, representation.id, - segmentNumber - 1 + segmentTemplate.firstSegmentNumber, + (segmentNumber - 1) + segmentTemplate.startNumber, representation.bandwidth, startTime); @@ -561,27 +561,29 @@ shaka.dash.MpdProcessor.prototype.generateSegmentListFromDuration_ = function( var segmentList = new shaka.dash.mpd.SegmentList(); - // Note: do not copy |segmentDuration| since the segments may have different - // lengths. segmentList.timescale = segmentTemplate.timescale; segmentList.presentationTimeOffset = segmentTemplate.presentationTimeOffset; - segmentList.firstSegmentNumber = segmentTemplate.firstSegmentNumber; + segmentList.segmentDuration = segmentTemplate.segmentDuration; + segmentList.startNumber = segmentTemplate.startNumber; segmentList.initialization = this.generateInitialization_(representation); segmentList.segmentUrls = []; - // The current segment number. - var segmentNumber = 1; - var startTime = 0; + var numSegments = + Math.floor(periodDuration / segmentTemplate.segmentDuration); + + for (var segmentNumber = 1; segmentNumber <= numSegments; ++segmentNumber) { + var time = + ((segmentNumber - 1) + (segmentTemplate.startNumber - 1)) * + segmentTemplate.segmentDuration; - while ((startTime / segmentList.timescale) < periodDuration) { // Generate the media URL. shaka.asserts.assert(segmentTemplate.mediaUrlTemplate); var filledUrlTemplate = this.fillUrlTemplate_( segmentTemplate.mediaUrlTemplate, representation.id, - segmentNumber - 1 + segmentTemplate.firstSegmentNumber, + segmentNumber - 1 + segmentTemplate.startNumber, representation.bandwidth, - startTime); + time); if (!filledUrlTemplate) { // An error has already been logged. @@ -595,13 +597,10 @@ shaka.dash.MpdProcessor.prototype.generateSegmentListFromDuration_ = function( // Create the SegmentUrl. var segmentUrl = new shaka.dash.mpd.SegmentUrl(); segmentUrl.mediaUrl = mediaUrl; - segmentUrl.startTime = startTime; + segmentUrl.startTime = time; segmentUrl.duration = segmentTemplate.segmentDuration; segmentList.segmentUrls.push(segmentUrl); - - ++segmentNumber; - startTime += segmentTemplate.segmentDuration; } representation.segmentList = segmentList; @@ -1017,7 +1016,8 @@ shaka.dash.MpdProcessor.prototype.createSegmentMetadataInfo_ = function( */ shaka.dash.MpdProcessor.prototype.createSegmentIndex_ = function(segmentList) { var timescale = segmentList.timescale; - var firstSegmentNumber = segmentList.firstSegmentNumber; + var presentationTimeOffset = segmentList.presentationTimeOffset; + var startNumber = segmentList.startNumber; var segmentDuration = segmentList.segmentDuration; /** @type {!Array.} */ diff --git a/spec/mpd_processor_spec.js b/spec/mpd_processor_spec.js index bb93308c13..fd538bf372 100644 --- a/spec/mpd_processor_spec.js +++ b/spec/mpd_processor_spec.js @@ -189,7 +189,7 @@ describe('MpdProcessor', function() { m.periods.push(p); }); - it('generates a SegmentBase from a SegmentTemplate', function() { + it('creates SegmentBase from SegmentTemplate', function() { st.mediaUrlTemplate = 'http://example.com/$Bandwidth$-media.mp4'; st.indexUrlTemplate = 'http://example.com/$Bandwidth$-index.sidx'; st.initializationUrlTemplate = 'http://example.com/$Bandwidth$-init.mp4'; @@ -243,7 +243,7 @@ describe('MpdProcessor', function() { expect(i2.range).toBeNull(); }); - it('generates a SegmentList from a SegmentTemplate', function() { + it('creates SegmentList from SegmentTemplate+SegmentTimeline', function() { var tp1 = new mpd.SegmentTimePoint(); tp1.duration = 10; tp1.repeat = 1; @@ -259,7 +259,7 @@ describe('MpdProcessor', function() { st.timescale = 9000; st.presentationTimeOffset = 0; st.segmentDuration = null; - st.firstSegmentNumber = 1; + st.startNumber = 1; st.mediaUrlTemplate = '$Number$-$Time$-$Bandwidth$-media.mp4'; st.initializationUrlTemplate = '$Bandwidth$-init.mp4'; @@ -281,7 +281,7 @@ describe('MpdProcessor', function() { expect(sl1.timescale).toBe(9000); expect(sl1.presentationTimeOffset).toBe(0); expect(sl1.segmentDuration).toBe(null); - expect(sl1.firstSegmentNumber).toBe(1); + expect(sl1.startNumber).toBe(1); expect(sl1.initialization).toBeTruthy(); expect(sl1.initialization.url).toBeTruthy(); @@ -319,7 +319,7 @@ describe('MpdProcessor', function() { expect(sl2.timescale).toBe(9000); expect(sl2.presentationTimeOffset).toBe(0); expect(sl2.segmentDuration).toBe(null); - expect(sl2.firstSegmentNumber).toBe(1); + expect(sl2.startNumber).toBe(1); expect(sl2.initialization).toBeTruthy(); expect(sl2.initialization.url).toBeTruthy(); @@ -349,6 +349,101 @@ describe('MpdProcessor', function() { expect(sl2.segmentUrls[2].startTime).toBe(20); expect(sl2.segmentUrls[2].duration).toBe(20); }); + + it('creates SegmentList from SegmentTemplate+segmentDuration', function() { + p.duration = 30; + + st.timescale = 9000; + st.presentationTimeOffset = 0; + st.segmentDuration = 10; + st.startNumber = 5; // Ensure startNumber > 1 works. + st.mediaUrlTemplate = '$Number$-$Time$-$Bandwidth$-media.mp4'; + st.initializationUrlTemplate = '$Bandwidth$-init.mp4'; + + r1.bandwidth = 250000; + r1.baseUrl = new goog.Uri('http://example.com/'); + + r2.bandwidth = 500000; + r2.baseUrl = new goog.Uri('http://example.com/'); + + processor.processSegmentTemplates_(m); + + // Check |r1|. + expect(r1.segmentBase).toBeNull(); + expect(r1.segmentList).toBeTruthy(); + + var sl1 = r1.segmentList; + expect(sl1.timescale).toBe(9000); + expect(sl1.presentationTimeOffset).toBe(0); + expect(sl1.segmentDuration).toBe(10); + expect(sl1.startNumber).toBe(5); + + expect(sl1.initialization).toBeTruthy(); + expect(sl1.initialization.url).toBeTruthy(); + expect(sl1.initialization.url.toString()) + .toBe('http://example.com/250000-init.mp4'); + + expect(sl1.segmentUrls.length).toBe(3); + + expect(sl1.segmentUrls[0].mediaUrl).toBeTruthy(); + expect(sl1.segmentUrls[0].mediaUrl.toString()) + .toBe('http://example.com/5-40-250000-media.mp4'); + expect(sl1.segmentUrls[0].mediaRange).toBeNull(); + expect(sl1.segmentUrls[0].startTime).toBe(40); + expect(sl1.segmentUrls[0].duration).toBe(10); + + expect(sl1.segmentUrls[1].mediaUrl).toBeTruthy(); + expect(sl1.segmentUrls[1].mediaUrl.toString()) + .toBe('http://example.com/6-50-250000-media.mp4'); + expect(sl1.segmentUrls[1].mediaRange).toBeNull(); + expect(sl1.segmentUrls[1].startTime).toBe(50); + expect(sl1.segmentUrls[1].duration).toBe(10); + + expect(sl1.segmentUrls[2].mediaUrl).toBeTruthy(); + expect(sl1.segmentUrls[2].mediaUrl.toString()) + .toBe('http://example.com/7-60-250000-media.mp4'); + expect(sl1.segmentUrls[2].mediaRange).toBeNull(); + expect(sl1.segmentUrls[2].startTime).toBe(60); + expect(sl1.segmentUrls[2].duration).toBe(10); + + // Check |r2|. + expect(r2.segmentBase).toBeNull(); + expect(r2.segmentList).toBeTruthy(); + + var sl2 = r2.segmentList; + expect(sl2.timescale).toBe(9000); + expect(sl2.presentationTimeOffset).toBe(0); + expect(sl2.segmentDuration).toBe(10); + expect(sl2.startNumber).toBe(5); + + expect(sl2.initialization).toBeTruthy(); + expect(sl2.initialization.url).toBeTruthy(); + expect(sl2.initialization.url.toString()) + .toBe('http://example.com/500000-init.mp4'); + + expect(sl2.segmentUrls.length).toBe(3); + + expect(sl2.segmentUrls[0].mediaUrl).toBeTruthy(); + expect(sl2.segmentUrls[0].mediaUrl.toString()) + .toBe('http://example.com/5-40-500000-media.mp4'); + expect(sl2.segmentUrls[0].mediaRange).toBeNull(); + expect(sl2.segmentUrls[0].startTime).toBe(40); + expect(sl2.segmentUrls[0].duration).toBe(10); + + expect(sl2.segmentUrls[1].mediaUrl).toBeTruthy(); + expect(sl2.segmentUrls[1].mediaUrl.toString()) + .toBe('http://example.com/6-50-500000-media.mp4'); + expect(sl2.segmentUrls[1].mediaRange).toBeNull(); + expect(sl2.segmentUrls[1].startTime).toBe(50); + expect(sl2.segmentUrls[1].duration).toBe(10); + + expect(sl2.segmentUrls[2].mediaUrl).toBeTruthy(); + expect(sl2.segmentUrls[2].mediaUrl.toString()) + .toBe('http://example.com/7-60-500000-media.mp4'); + expect(sl2.segmentUrls[2].mediaRange).toBeNull(); + expect(sl2.segmentUrls[2].startTime).toBe(60); + expect(sl2.segmentUrls[2].duration).toBe(10); + }); }); describe('fillUrlTemplate_', function() { diff --git a/spec/mpd_spec.js b/spec/mpd_spec.js index bd550ed6a7..e83198fd56 100644 --- a/spec/mpd_spec.js +++ b/spec/mpd_spec.js @@ -785,5 +785,22 @@ describe('mpd', function() { var mpd = shaka.dash.mpd.parseMpd(source, ''); expect(mpd).toBeTruthy(); }); + + it('defaults startNumber to 1', function() { + var source = [ + '', + ' ', + ' ', + ' ', + ' ', + ' ', + ''].join('\n'); + + var mpd = shaka.dash.mpd.parseMpd(source, ''); + var period = mpd.periods[0]; + var adaptationSet = period.adaptationSets[0]; + var segmentTemplate = adaptationSet.segmentTemplate; + expect(segmentTemplate.startNumber).toBe(1); + }); });