Skip to content

Commit

Permalink
Fit segments to Period for live content.
Browse files Browse the repository at this point in the history
We usually fit segment references to the Period for VOD content.  This
ensures that a gap in the manifest at the end of the Period doesn't
cause problems.  We don't do this for live because we may get more
segments.  However, for multi-Period live, we should still fit the
references for all Periods except the last one.

This also removes the error about duplicate Representation IDs for
VOD content since the problem only occurs for live.

Issue #694
b/35849085

Change-Id: Ib195dc51982ca58b8d5613b37817216980b8d366
  • Loading branch information
TheModMaker committed Mar 9, 2017
1 parent 21c6420 commit d510795
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 13 deletions.
11 changes: 8 additions & 3 deletions lib/dash/dash_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,8 @@ shaka.dash.DashParser.Context;
* @typedef {{
* start: number,
* duration: ?number,
* node: !Element
* node: !Element,
* isLastPeriod: boolean
* }}
*
* @description
Expand All @@ -212,6 +213,8 @@ shaka.dash.DashParser.Context;
* will be non-null for all periods except the last.
* @property {!Element} node
* The XML Node for the Period.
* @property {boolean} isLastPeriod
* Whether this Period is the last one in the manifest.
*/
shaka.dash.DashParser.PeriodInfo;

Expand Down Expand Up @@ -465,6 +468,7 @@ shaka.dash.DashParser.prototype.parseManifest_ =

/** @type {shaka.dash.DashParser.Context} */
var context = {
// Don't base on updatePeriod_ since emsg boxes can cause manifest updates.
dynamic: mpdType != 'static',
presentationTimeline: presentationTimeline,
period: null,
Expand Down Expand Up @@ -567,7 +571,8 @@ shaka.dash.DashParser.prototype.parsePeriods_ = function(
var info = {
start: start,
duration: periodDuration,
node: elem
node: elem,
isLastPeriod: periodDuration == null || i == periodNodes.length - 1
};
var period = this.parsePeriod_(context, baseUris, info);
periods.push(period);
Expand Down Expand Up @@ -662,7 +667,7 @@ shaka.dash.DashParser.prototype.parsePeriod_ = function(
.map(function(as) { return as.representationIds; })
.reduce(Functional.collapseArrays, []);
var uniqueRepIds = representationIds.filter(Functional.isNotDuplicate);
if (representationIds.length != uniqueRepIds.length) {
if (context.dynamic && representationIds.length != uniqueRepIds.length) {
throw new shaka.util.Error(
shaka.util.Error.Category.MANIFEST,
shaka.util.Error.Code.DASH_DUPLICATE_REPRESENTATION_ID);
Expand Down
6 changes: 3 additions & 3 deletions lib/dash/mpd_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,12 @@ shaka.dash.MpdUtils.createTimeline = function(
* contracts the last SegmentReference so it ends at the end of its Period for
* VOD presentations.
*
* @param {boolean} dynamic
* @param {boolean} fitLast
* @param {?number} periodDuration
* @param {!Array.<!shaka.media.SegmentReference>} references
*/
shaka.dash.MpdUtils.fitSegmentReferences = function(
dynamic, periodDuration, references) {
fitLast, periodDuration, references) {
if (references.length == 0)
return;

Expand All @@ -321,7 +321,7 @@ shaka.dash.MpdUtils.fitSegmentReferences = function(
firstReference.startByte, firstReference.endByte);
}

if (dynamic)
if (!fitLast)
return;
goog.asserts.assert(periodDuration != null,
'Period duration must be known for static content!');
Expand Down
3 changes: 2 additions & 1 deletion lib/dash/segment_base.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ shaka.dash.SegmentBase.createSegmentIndexFromUris = function(
context, requestInitSegment, init, uris,
startByte, endByte, containerType, presentationTimeOffset) {
var presentationTimeline = context.presentationTimeline;
var fitLast = !context.dynamic || !context.periodInfo.isLastPeriod;
var periodStartTime = context.periodInfo.start;
var periodDuration = context.periodInfo.duration;

Expand Down Expand Up @@ -154,7 +155,7 @@ shaka.dash.SegmentBase.createSegmentIndexFromUris = function(
}

shaka.dash.MpdUtils.fitSegmentReferences(
context.dynamic, periodDuration, references);
fitLast, periodDuration, references);
presentationTimeline.notifySegments(periodStartTime, references);

// Since containers are never updated, we don't need to store the
Expand Down
5 changes: 3 additions & 2 deletions lib/dash/segment_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ shaka.dash.SegmentList.createStream = function(context, segmentIndexMap) {
context.periodInfo.duration, info.startNumber,
context.representation.baseUris, info);
shaka.dash.MpdUtils.fitSegmentReferences(
context.dynamic, context.periodInfo.duration, references);
!context.dynamic || !context.periodInfo.isLastPeriod,
context.periodInfo.duration, references);
if (segmentIndex) {
segmentIndex.merge(references);
var start = context.presentationTimeline.getSegmentAvailabilityStart();
Expand All @@ -74,7 +75,7 @@ shaka.dash.SegmentList.createStream = function(context, segmentIndexMap) {
context.presentationTimeline.notifySegments(
context.periodInfo.start, references);
segmentIndex = new shaka.media.SegmentIndex(references);
if (id)
if (id && context.dynamic)
segmentIndexMap[id] = segmentIndex;
}

Expand Down
5 changes: 3 additions & 2 deletions lib/dash/segment_template.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ shaka.dash.SegmentTemplate.createStream = function(

var references = SegmentTemplate.createFromTimeline_(context, info);
shaka.dash.MpdUtils.fitSegmentReferences(
context.dynamic, context.periodInfo.duration, references);
!context.dynamic || !context.periodInfo.isLastPeriod,
context.periodInfo.duration, references);
if (segmentIndex) {
segmentIndex.merge(references);
var start = context.presentationTimeline.getSegmentAvailabilityStart();
Expand All @@ -87,7 +88,7 @@ shaka.dash.SegmentTemplate.createStream = function(
context.presentationTimeline.notifySegments(
context.periodInfo.start, references);
segmentIndex = new shaka.media.SegmentIndex(references);
if (id)
if (id && context.dynamic)
segmentIndexMap[id] = segmentIndex;
}

Expand Down
54 changes: 52 additions & 2 deletions test/dash/dash_parser_manifest_unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -675,9 +675,9 @@ describe('DashParser Manifest', function() {
Dash.testFails(done, source, error);
});

it('duplicate Representation ids', function(done) {
it('duplicate Representation ids with live', function(done) {
var source = [
'<MPD minBufferTime="PT75S">',
'<MPD minBufferTime="PT75S" type="dynamic">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4">',
' <Representation id="1" bandwidth="1">',
Expand Down Expand Up @@ -817,6 +817,56 @@ describe('DashParser Manifest', function() {
.then(done);
});

it('ignores duplicate Representation IDs for VOD', function(done) {
var source = [
'<MPD minBufferTime="PT75S">',
' <Period id="1" duration="PT30S">',
' <AdaptationSet mimeType="video/mp4">',
' <Representation id="1" bandwidth="1">',
' <SegmentTemplate media="1.mp4">',
' <SegmentTimeline>',
' <S t="0" d="30" />',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' <AdaptationSet mimeType="video/mp4">',
' <Representation id="1" bandwidth="1">',
' <SegmentTemplate media="2.mp4">',
' <SegmentTimeline>',
' <S t="0" d="30" />',
' </SegmentTimeline>',
' </SegmentTemplate>',
' </Representation>',
' </AdaptationSet>',
' </Period>',
'</MPD>'
].join('\n');

// See https://goo.gl/BAM3mi
// The old error was that with SegmentTimeline, duplicate Representation IDs
// would use the same segment index, so they would have the same references.
// This test proves that duplicate Representation IDs are allowed for VOD
// and that error doesn't occur.
fakeNetEngine.setResponseMapAsText({'dummy://foo': source});
parser.start('dummy://foo', playerInterface)
.then(function(manifest) {
expect(manifest.periods.length).toBe(1);
expect(manifest.periods[0].variants.length).toBe(2);

var variant1 = manifest.periods[0].variants[0];
var variant2 = manifest.periods[0].variants[1];
expect(variant1.video).toBeTruthy();
expect(variant2.video).toBeTruthy();
expect(variant1.video.getSegmentReference(1).getUris())
.toEqual(['dummy://foo/1.mp4']);
expect(variant2.video.getSegmentReference(1).getUris())
.toEqual(['dummy://foo/2.mp4']);
})
.catch(fail)
.then(done);
});

/**
* @param {string} manifestText
* @param {Uint8Array} emsgUpdate
Expand Down

0 comments on commit d510795

Please sign in to comment.