Files
shaka-player/lib/media/mp4_vtt_parser.js
T
2016-07-23 14:42:38 -07:00

199 lines
5.7 KiB
JavaScript

/**
* @license
* Copyright 2016 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
goog.provide('shaka.media.Mp4VttParser');
goog.require('goog.asserts');
goog.require('shaka.media.TextEngine');
goog.require('shaka.media.VttTextParser');
goog.require('shaka.util.DataViewReader');
goog.require('shaka.util.Error');
goog.require('shaka.util.Mp4Parser');
goog.require('shaka.util.StringUtils');
goog.require('shaka.util.TextParser');
/**
* Extracts vtt segment from an mp4 file and
* does the mapping to VttCue objects.
* @param {ArrayBuffer} data
* @param {?number} segmentStartTime
* @param {?number} segmentEndTime
* @return {!Array.<!TextTrackCue>}
*/
shaka.media.Mp4VttParser = function(data, segmentStartTime, segmentEndTime) {
var reader = new shaka.util.DataViewReader(
new DataView(data),
shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
var boxSize = shaka.util.Mp4Parser.findBox(
shaka.util.Mp4Parser.BOX_TYPE_MDAT, reader);
if (boxSize != shaka.util.Mp4Parser.BOX_NOT_FOUND) {
// mdat box found, parse the content
// valid media segment should have start and end time
goog.asserts.assert(
segmentStartTime != null, 'start time should not be null');
goog.asserts.assert(segmentEndTime != null, 'end time should not be null');
return shaka.media.Mp4VttParser.parseData_(
reader.readBytes(boxSize - 8).buffer, segmentStartTime, segmentEndTime);
}
var wvttBoxSize = shaka.util.Mp4Parser.findSampleDescriptionBox(
data, shaka.media.Mp4VttParser.BOX_TYPE_WVTT);
if (wvttBoxSize != shaka.util.Mp4Parser.BOX_NOT_FOUND) {
// a valid vtt init segment, no actual subtitles yet
return [];
} else {
throw new shaka.util.Error(
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_MP4_VTT);
}
};
/**
* Parses the content of the mdat mp4 box into
* VttCue objects.
* @param {ArrayBuffer} data
* @param {number} segmentStartTime
* @param {number} segmentEndTime
* @return {!Array.<!TextTrackCue>}
* @private
*/
shaka.media.Mp4VttParser.parseData_ = function(
data, segmentStartTime, segmentEndTime) {
var reader = new shaka.util.DataViewReader(
new DataView(data),
shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
var result = [];
// VttCues are represented as vttc boxes. Each box corresponds
// to a cue.
while (reader.hasMoreData()) {
var boxSize = shaka.util.Mp4Parser.findBox(
shaka.media.Mp4VttParser.BOX_TYPE_VTTC, reader);
if (boxSize == shaka.util.Mp4Parser.BOX_NOT_FOUND) {
// No more cues
break;
}
var cue = shaka.media.Mp4VttParser.parseCue_(
reader.readBytes(boxSize - 8).buffer, segmentStartTime, segmentEndTime);
result.push(cue);
}
return result;
};
/**
* Parses a vttc box into a cue.
*
* @param {ArrayBuffer} data
* @param {number} segmentStartTime
* @param {number} segmentEndTime
* @return {TextTrackCue}
* @private
*/
shaka.media.Mp4VttParser.parseCue_ = function(
data, segmentStartTime, segmentEndTime) {
var reader = new shaka.util.DataViewReader(
new DataView(data),
shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
var cue = null;
var payload;
var settings;
var id;
while (reader.hasMoreData()) {
var startPosition = reader.getPosition();
var size = reader.readUint32();
var type = reader.readUint32();
var content = shaka.util.StringUtils.fromBytesAutoDetect(
reader.readBytes(size - 8).buffer);
if (size == 1) {
size = reader.readUint64();
} else if (size == 0) {
size = reader.getLength() - startPosition;
}
switch (type) {
case shaka.media.Mp4VttParser.BOX_TYPE_PAYL:
payload = content;
break;
case shaka.media.Mp4VttParser.BOX_TYPE_IDEN:
id = content;
break;
case shaka.media.Mp4VttParser.BOX_TYPE_STTG:
settings = content;
break;
default:
break;
}
}
// payload box is mandatory
if (!payload) {
throw new shaka.util.Error(
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_MP4_VTT);
} else if (window.VTTCue) {
cue = new VTTCue(segmentStartTime, segmentEndTime, payload);
if (id)
cue.id = id;
if (settings) {
var parser = new shaka.util.TextParser(settings);
var word = parser.readWord();
while (word) {
if (!shaka.media.VttTextParser.parseSetting(cue, word)) {
throw new shaka.util.Error(
shaka.util.Error.Category.TEXT,
shaka.util.Error.Code.INVALID_TEXT_SETTINGS);
}
parser.skipWhitespace();
word = parser.readWord();
}
}
} else {
cue = new TextTrackCue(
Number(segmentStartTime), Number(segmentEndTime), payload);
}
return cue;
};
/** @const {number} */
shaka.media.Mp4VttParser.BOX_TYPE_WVTT = 0x77767474;
/** @const {number} */
shaka.media.Mp4VttParser.BOX_TYPE_VTTC = 0x76747463;
/** @const {number} */
shaka.media.Mp4VttParser.BOX_TYPE_PAYL = 0x7061796C;
/** @const {number} */
shaka.media.Mp4VttParser.BOX_TYPE_IDEN = 0x6964656F;
/** @const {number} */
shaka.media.Mp4VttParser.BOX_TYPE_STTG = 0x73747467;
shaka.media.TextEngine.registerParser(
'application/mp4; codecs="wvtt"', shaka.media.Mp4VttParser);