Skip to content

Commit

Permalink
Added Dash manifest parser.
Browse files Browse the repository at this point in the history
b/25851171

Change-Id: I4fbd6410d579899b749b50c8819fa8c17ac80658
  • Loading branch information
TheModMaker committed Jan 13, 2016
1 parent dea1021 commit 54fa88f
Show file tree
Hide file tree
Showing 17 changed files with 4,426 additions and 13 deletions.
909 changes: 909 additions & 0 deletions lib/dash/dash_parser.js

Large diffs are not rendered by default.

202 changes: 195 additions & 7 deletions lib/dash/mpd_utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

goog.provide('shaka.dash.MpdUtils');

goog.require('goog.Uri');
goog.require('shaka.asserts');
goog.require('shaka.log');
goog.require('shaka.util.XmlUtils');
Expand All @@ -36,6 +37,49 @@ goog.require('shaka.util.XmlUtils');
shaka.dash.MpdUtils.GAP_OVERLAP_WARN_THRESHOLD = 1.0 / 32.0;


/**
* @typedef {{
* start: number,
* end: number
* }}
*
* @description
* Defines a time range of a media segment. Times are in seconds.
*
* @property {number} start
* The start time of the range.
* @property {number} end
* The end time (exclusive) of the range.
*/
shaka.dash.MpdUtils.TimeRange;


/**
* @typedef {{
* timescale: number,
* segmentDuration: ?number,
* startNumber: number,
* presentationTimeOffset: number,
* timeline: Array.<shaka.dash.MpdUtils.TimeRange>
* }}
*
* @description
* Contains common information between SegmentList and SegmentTemplate items.
*
* @property {number} timescale
* The time-scale of the representation.
* @property {?number} segmentDuration
* The duration of the segments in seconds, if given.
* @property {number} startNumber
* The start number of the segments; 1 or greater.
* @property {number} presentationTimeOffset
* The presentationTimeOffset of the representation, in seconds.
* @property {Array.<shaka.dash.MpdUtils.TimeRange>} timeline
* The timeline of the representation, if given. Times in seconds.
*/
shaka.dash.MpdUtils.SegmentInfo;


/**
* Fills a SegmentTemplate URI template. This function does not validate the
* resulting URI.
Expand Down Expand Up @@ -99,13 +143,14 @@ shaka.dash.MpdUtils.fillUriTemplate = function(


/**
* Expands a SegmentTimeline into an array-based timeline.
* Expands a SegmentTimeline into an array-based timeline. The results are in
* seconds.
*
* @param {!Node} segmentTimeline
* @param {number} timescale
* @param {number} periodDuration The Period's duration in seconds.
* POSITIVE_INFINITY indicates that the Period continues indefinitely.
* @return {!Array.<{start: number, end: number}>}
* @return {!Array.<shaka.dash.MpdUtils.TimeRange>}
*/
shaka.dash.MpdUtils.createTimeline = function(
segmentTimeline, timescale, periodDuration) {
Expand All @@ -120,7 +165,7 @@ shaka.dash.MpdUtils.createTimeline = function(

var timePoints = XmlUtils.findChildren(segmentTimeline, 'S');

/** @type {!Array.<{start: number, end: number}>} */
/** @type {!Array.<shaka.dash.MpdUtils.TimeRange>} */
var timeline = [];
var lastEndTime = 0;

Expand Down Expand Up @@ -173,15 +218,15 @@ shaka.dash.MpdUtils.createTimeline = function(
'ignoring the last "S" element.',
timePoint);
return timeline;
} else if (startTime >= periodDuration) {
} else if (startTime / timescale >= periodDuration) {
shaka.log.warning(
'The last "S" element cannot have a negative repeat',
'if its start time exceeds the Period\'s duration:',
'igoring the last "S" element.',
timePoint);
return timeline;
}
repeat = Math.ceil((periodDuration - startTime) / d) - 1;
repeat = Math.ceil((periodDuration * timescale - startTime) / d) - 1;
}
}

Expand All @@ -204,12 +249,13 @@ shaka.dash.MpdUtils.createTimeline = function(
timePoint);
}

timeline[timeline.length - 1].end = startTime;
timeline[timeline.length - 1].end = startTime / timescale;
}

for (var j = 0; j <= repeat; ++j) {
var endTime = startTime + d;
timeline.push({start: startTime, end: endTime});
timeline.push(
{start: (startTime / timescale), end: (endTime / timescale)});

startTime = endTime;
lastEndTime = endTime;
Expand All @@ -218,3 +264,145 @@ shaka.dash.MpdUtils.createTimeline = function(

return timeline;
};


/**
* Resolves an array of relative URIs to the given base URIs. This will result
* in M*N number of URIs.
*
* @param {!Array.<string>} baseUris
* @param {!Array.<string>} relativeUris
* @return {!Array.<string>}
*/
shaka.dash.MpdUtils.resolveUris = function(baseUris, relativeUris) {
if (relativeUris.length == 0)
return baseUris;

var relativeAsGoog =
relativeUris.map(function(uri) { return new goog.Uri(uri); });
// Resolve each URI relative to each base URI, creating an Array of Arrays.
// Then flatten the Arrays into a single Array.
return baseUris.map(function(uri) { return new goog.Uri(uri); })
.map(function(base) { return relativeAsGoog.map(base.resolve.bind(base)); })
.reduce(function(all, part) { return all.concat(part); }, [])
.map(function(uri) { return uri.toString(); });
};


/**
* Parses common segment info for SegmentList and SegmentTemplate.
*
* @param {shaka.dash.DashParser.Context} context
* @param {string} type Either segmentList or segmentTemplate.
* @return {shaka.dash.MpdUtils.SegmentInfo}
*/
shaka.dash.MpdUtils.parseSegmentInfo = function(context, type) {
shaka.asserts.assert(
type === 'segmentList' || type === 'segmentTemplate',
'Must be either segmentList or segmentTemplate');
shaka.asserts.assert(
context.representation[type],
'There must be at least one element of type ' + type);
var MpdUtils = shaka.dash.MpdUtils;
var XmlUtils = shaka.util.XmlUtils;

var timescaleStr = MpdUtils.inheritAttribute(context, type, 'timescale');
var timescale = 1;
if (timescaleStr) {
timescale = XmlUtils.parsePositiveInt(timescaleStr) || 1;
}

var durationStr = MpdUtils.inheritAttribute(context, type, 'duration');
var segmentDuration = XmlUtils.parsePositiveInt(durationStr || '');
if (segmentDuration) {
segmentDuration /= timescale;
}

var startNumberStr = MpdUtils.inheritAttribute(context, type, 'startNumber');
var presentationTimeOffset =
MpdUtils.inheritAttribute(context, type, 'presentationTimeOffset');
var startNumber = XmlUtils.parseNonNegativeInt(startNumberStr || '');
if (startNumberStr == null || startNumber == null)
startNumber = 1;

var timelineNode = MpdUtils.inheritChild(context, type, 'SegmentTimeline');
/** @type {Array.<shaka.dash.MpdUtils.TimeRange>} */
var timeline = null;
if (timelineNode) {
timeline = MpdUtils.createTimeline(
timelineNode, timescale,
context.periodInfo.duration || Number.POSITIVE_INFINITY);
}

var pto = (Number(presentationTimeOffset) / timescale) || 0;
return {
timescale: timescale,
segmentDuration: segmentDuration,
startNumber: startNumber,
presentationTimeOffset: pto,
timeline: timeline
};
};


/**
* Searches the inheritance for a Segment* with the given attribute.
*
* @param {shaka.dash.DashParser.Context} context
* @param {string} type One of segmentBase, segmentList, or segmentTemplate
* which tells which inheritance to search for the attribute.
* @param {string} attribute
* @return {?string}
*/
shaka.dash.MpdUtils.inheritAttribute = function(context, type, attribute) {
shaka.asserts.assert(
type === 'segmentBase' || type === 'segmentList' ||
type === 'segmentTemplate',
'Must be one of: segmentBase, segmentList, or segmentTemplate');
shaka.asserts.assert(
context.representation[type],
'There must be at least one element of type ' + type);

/** @type {!Array.<!Node>} */
var nodes = [
context.representation[type],
context.adaptationSet[type],
context.period[type]
].filter(function(s) { return s != null; });

return nodes
.map(function(s) { return s.getAttribute(attribute); })
.reduce(function(all, part) { return all || part; });
};


/**
* Searches the inheritance for a Segment* with the given child.
*
* @param {shaka.dash.DashParser.Context} context
* @param {string} type One of segmentBase, segmentList, or segmentTemplate
* which tells which inheritance to search for the attribute.
* @param {string} child
* @return {Node}
*/
shaka.dash.MpdUtils.inheritChild = function(context, type, child) {
shaka.asserts.assert(
type === 'segmentBase' || type === 'segmentList' ||
type === 'segmentTemplate',
'Must be one of: segmentBase, segmentList, or segmentTemplate');
shaka.asserts.assert(
context.representation[type],
'There must be at least one element of type ' + type);

/** @type {!Array.<!Node>} */
var nodes = [
context.representation[type],
context.adaptationSet[type],
context.period[type]
].filter(function(s) { return s != null; });

var XmlUtils = shaka.util.XmlUtils;
return nodes
.map(function(s) { return XmlUtils.findChild(s, child); })
.reduce(function(all, part) { return all || part; });
};
Loading

0 comments on commit 54fa88f

Please sign in to comment.