Skip to content

Commit

Permalink
Redefining Text Parsing Plugin System
Browse files Browse the repository at this point in the history
The text parsers were all stateless. This caused problems with MP4
VTT as the timescale is needed later on for other boxes. This changes
parsers to carry state.

How time is referenced with the text parsers is not clear and has
caused confusion. In v2.0.1, we introduced the useRelativeCueTimestamps
option to control the behavior of our WebVTT parser. We decided in #480
(comment) that we would remove this option in v2.1.0. All WebVTT
timestamps in v2.1.0 will be relative to the segment time. This change
creates a new time context interface that will be used to help limit
the confusion around how time is communicated.

Closes #726

Change-Id: I67409608c35d2d5abb8b8b25529859cb37f8f0a8
  • Loading branch information
vaage committed Apr 5, 2017
1 parent f5cabea commit 9a86d04
Show file tree
Hide file tree
Showing 20 changed files with 512 additions and 358 deletions.
4 changes: 0 additions & 4 deletions externs/shaka/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,6 @@ shakaExtern.ManifestConfiguration;
* bufferingGoal: number,
* bufferBehind: number,
* ignoreTextStreamFailures: boolean,
* useRelativeCueTimestamps: boolean,
* startAtSegmentBoundary: boolean,
* smallGapLimit: number,
* jumpLargeGaps: boolean
Expand All @@ -497,9 +496,6 @@ shakaExtern.ManifestConfiguration;
* @property {boolean} ignoreTextStreamFailures
* If true, the player will ignore text stream failures and proceed to play
* other streams.
* @property {boolean} useRelativeCueTimestamps
* If true, WebVTT cue timestamps will be treated as relative to the start
* time of the VTT segment. Defaults to false.
* @property {boolean} startAtSegmentBoundary
* If true, adjust the start time backwards so it is at the start of a
* segment. This affects both explicit start times and calculated start time
Expand Down
61 changes: 58 additions & 3 deletions externs/shaka/text.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,66 @@
/** @externs */



/**
* An interface for plugins that parse text tracks.
*
* @interface
* @exportDoc
*/
shakaExtern.TextParser = function() {};


/**
* A collection of time offsets used to adjust text cue times.
*
* @typedef {{
* periodStart : number,
* segmentStart : number,
* segmentEnd : number
* }}
*
* @property {number} periodStart
* The absolute start time of the period in seconds.
* @property {number} segmentStart
* The absolute start time of the segment in seconds.
* @property {number} segmentEnd
* The absolute end time of the segment in seconds.
*
* @exportDoc
*/
shakaExtern.TextParser.TimeContext;


/**
* Parse an initialization segment. Some formats do not have init
* segments so this won't always be called.
*
* @param {!ArrayBuffer} data
* The data that makes up the init segment.
*
* @exportDoc
*/

shakaExtern.TextParser.prototype.parseInit;


/**
* Parses a text buffer into an array of cues.
* Parse a media segment and return the cues that make up the segment.
*
* @param {!ArrayBuffer} data
* The next section of buffer.
* @param {shakaExtern.TextParser.TimeContext} timeContext
* The time information that should be used to adjust the times values
* for each cue.
* @return {!Array.<!TextTrackCue>}
*
* @typedef {function(ArrayBuffer, number, ?number,
* ?number, boolean):!Array.<!TextTrackCue>}
* @exportDoc
*/
shakaExtern.TextParser.prototype.parseMedia;


/**
* @typedef {function(new:shakaExtern.TextParser)}
*/
shakaExtern.TextParserPlugin;
11 changes: 2 additions & 9 deletions lib/media/media_source_engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ shaka.media.MediaSourceEngine = function(video, mediaSource, textTrack) {
/** @private {shaka.media.TextEngine} */
this.textEngine_ = null;

/** @private {boolean} */
this.useRelativeCueTimestamps_ = false;

/**
* @private {!Object.<string,
* !Array.<shaka.media.MediaSourceEngine.Operation>>}
Expand Down Expand Up @@ -233,16 +230,13 @@ shaka.media.MediaSourceEngine.prototype.destroy = function() {
* For example: { 'audio': 'audio/webm; codecs="vorbis"',
* 'video': 'video/webm; codecs="vp9"', 'text': 'text/vtt' }.
* All types given must be supported.
* @param {boolean} useRelativeCueTimestamps
*
* @throws InvalidAccessError if blank MIME types are given
* @throws NotSupportedError if unsupported MIME types are given
* @throws QuotaExceededError if the browser can't support that many buffers
*/
shaka.media.MediaSourceEngine.prototype.init =
function(typeConfig, useRelativeCueTimestamps) {
shaka.media.MediaSourceEngine.prototype.init = function(typeConfig) {
var ContentType = shaka.util.ManifestParserUtils.ContentType;
this.useRelativeCueTimestamps_ = useRelativeCueTimestamps;

for (var contentType in typeConfig) {
var mimeType = typeConfig[contentType];
Expand Down Expand Up @@ -271,8 +265,7 @@ shaka.media.MediaSourceEngine.prototype.init =
*/
shaka.media.MediaSourceEngine.prototype.reinitText = function(mimeType) {
if (!this.textEngine_) {
this.textEngine_ = new shaka.media.TextEngine(
this.textTrack_, this.useRelativeCueTimestamps_);
this.textEngine_ = new shaka.media.TextEngine(this.textTrack_);
}
this.textEngine_.initParser(mimeType);
};
Expand Down
84 changes: 47 additions & 37 deletions lib/media/mp4_ttml_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,61 +23,71 @@ goog.require('shaka.util.Error');
goog.require('shaka.util.Mp4Parser');



/**
* @namespace
* @summary Extracts a TTML segment from an MP4 file and invokes the TTML parser
* to parse it.
* @param {ArrayBuffer} data
* @param {number} offset
* @param {?number} segmentStartTime
* @param {?number} segmentEndTime
* @param {boolean} useRelativeCueTimestamps Only used by the VTT parser
* @return {!Array.<!TextTrackCue>}
* @export
* @struct
* @constructor
* @implements {shakaExtern.TextParser}
*/
shaka.media.Mp4TtmlParser =
function(data, offset, segmentStartTime,
segmentEndTime, useRelativeCueTimestamps) {
shaka.media.Mp4TtmlParser = function() {
/**
* @type {!shakaExtern.TextParser}
* @private
*/
this.parser_ = new shaka.media.TtmlTextParser();
};


/** @override **/
shaka.media.Mp4TtmlParser.prototype.parseInit = function(data) {
var Mp4Parser = shaka.util.Mp4Parser;

var sawMDAT = false;
var sawSTPP = false;
var payload = [];

var parser = new shaka.util.Mp4Parser()
new Mp4Parser()
.box('moov', Mp4Parser.children)
.box('trak', Mp4Parser.children)
.box('mdia', Mp4Parser.children)
.box('minf', Mp4Parser.children)
.box('moov', Mp4Parser.children)
.box('stbl', Mp4Parser.children)
.fullBox('stsd', Mp4Parser.sampleDescription)
.box('trak', Mp4Parser.children)
.box('mdat', Mp4Parser.allData(function(data) {
sawMDAT = true;
payload = shaka.media.TtmlTextParser(
data.buffer,
offset,
segmentStartTime,
segmentEndTime,
false);
}))
.box('stpp', function(box) {
sawSTPP = true;
});
}).parse(data);

if (data) {
parser.parse(data);
if (!sawSTPP) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_MP4_TTML);
}
};


/** @override **/
shaka.media.Mp4TtmlParser.prototype.parseMedia = function(data, time) {
var Mp4Parser = shaka.util.Mp4Parser;

var sawMDAT = false;
var payload = [];

new Mp4Parser()
.box('mdat', Mp4Parser.allData(function(data) {
sawMDAT = true;
payload = this.parser_.parseMedia(data.buffer, time);
}.bind(this))).parse(data);

if (sawMDAT || sawSTPP) {
return payload;
if (!sawMDAT) {
throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_MP4_TTML);
}

throw new shaka.util.Error(
shaka.util.Error.Severity.CRITICAL,
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_MP4_TTML);
return payload;
};


shaka.media.TextEngine.registerParser(
'application/mp4; codecs="stpp"', shaka.media.Mp4TtmlParser);
'application/mp4; codecs="stpp"',
shaka.media.Mp4TtmlParser);
Loading

0 comments on commit 9a86d04

Please sign in to comment.