mirror of
https://github.com/shaka-project/shaka-player.git
synced 2026-06-18 16:36:56 +03:00
Separate text parsing and display logic.
Closes #796. Closes #923. Change-Id: Ifc2017b40a0fb570103f0fed7bc130aa24819e9f
This commit is contained in:
+3
-1
@@ -1,6 +1,8 @@
|
||||
# All standard text parsing plugins.
|
||||
# All files related to text parsing and displaying.
|
||||
|
||||
+../../lib/text/cue.js
|
||||
+../../lib/text/mp4_ttml_parser.js
|
||||
+../../lib/text/mp4_vtt_parser.js
|
||||
+../../lib/text/simple_text_displayer.js
|
||||
+../../lib/text/ttml_text_parser.js
|
||||
+../../lib/text/vtt_text_parser.js
|
||||
|
||||
@@ -606,7 +606,8 @@ shakaExtern.AbrConfiguration;
|
||||
* preferredTextLanguage: string,
|
||||
* restrictions: shakaExtern.Restrictions,
|
||||
* playRangeStart: number,
|
||||
* playRangeEnd: number
|
||||
* playRangeEnd: number,
|
||||
* textDisplayFactory: shakaExtern.TextDisplayer.Factory
|
||||
* }}
|
||||
*
|
||||
* @property {shakaExtern.DrmConfiguration} drm
|
||||
@@ -637,6 +638,8 @@ shakaExtern.AbrConfiguration;
|
||||
* @property {number} playRangeEnd
|
||||
* Optional playback and seek end time in seconds. Defaults to the end of
|
||||
* the presentation if not provided.
|
||||
* @property {shakaExtern.TextDisplayer.Factory} textDisplayFactory
|
||||
* A factory to construct text displayer.
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.PlayerConfiguration;
|
||||
|
||||
+64
-1
@@ -70,7 +70,7 @@ shakaExtern.TextParser.prototype.parseInit = function(data) {};
|
||||
* @param {shakaExtern.TextParser.TimeContext} timeContext
|
||||
* The time information that should be used to adjust the times values
|
||||
* for each cue.
|
||||
* @return {!Array.<!TextTrackCue>}
|
||||
* @return {!Array.<!shaka.text.Cue>}
|
||||
*
|
||||
* @exportDoc
|
||||
*/
|
||||
@@ -81,3 +81,66 @@ shakaExtern.TextParser.prototype.parseMedia = function(data, timeContext) {};
|
||||
* @typedef {function(new:shakaExtern.TextParser)}
|
||||
*/
|
||||
shakaExtern.TextParserPlugin;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An interface for plugins that display text.
|
||||
*
|
||||
* @interface
|
||||
* @extends {shaka.util.IDestroyable}
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Append given text cues to the list of cues to be displayed.
|
||||
*
|
||||
* @param {!Array.<!shaka.text.Cue>} cues
|
||||
* Text cues to be appended.
|
||||
*
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer.prototype.append = function(cues) {};
|
||||
|
||||
|
||||
/**
|
||||
* Remove cues in a given time range.
|
||||
*
|
||||
* @param {number} start
|
||||
* @param {number} end
|
||||
* @return {boolean}
|
||||
*
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer.prototype.remove = function(start, end) {};
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if text is currently visible.
|
||||
*
|
||||
* @return {boolean}
|
||||
*
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer.prototype.isTextVisible = function() {};
|
||||
|
||||
|
||||
/**
|
||||
* Set text visibility.
|
||||
*
|
||||
* @param {boolean} on
|
||||
*
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer.prototype.setTextVisibility = function(on) {};
|
||||
|
||||
|
||||
/**
|
||||
* A factory for creating a TextDisplayer.
|
||||
*
|
||||
* @typedef {function(new:shakaExtern.TextDisplayer)}
|
||||
* @exportDoc
|
||||
*/
|
||||
shakaExtern.TextDisplayer.Factory;
|
||||
|
||||
@@ -40,13 +40,13 @@ goog.require('shaka.util.PublicPromise');
|
||||
* when MediaSource operations fail.
|
||||
* @param {MediaSource} mediaSource The MediaSource, which must be in the
|
||||
* 'open' state.
|
||||
* @param {TextTrack} textTrack The TextTrack to use for subtitles/captions.
|
||||
* @param {shakaExtern.TextDisplayer} textDisplayer
|
||||
*
|
||||
* @struct
|
||||
* @constructor
|
||||
* @implements {shaka.util.IDestroyable}
|
||||
*/
|
||||
shaka.media.MediaSourceEngine = function(video, mediaSource, textTrack) {
|
||||
shaka.media.MediaSourceEngine = function(video, mediaSource, textDisplayer) {
|
||||
goog.asserts.assert(mediaSource.readyState == 'open',
|
||||
'The MediaSource should be in the \'open\' state.');
|
||||
|
||||
@@ -56,8 +56,8 @@ shaka.media.MediaSourceEngine = function(video, mediaSource, textTrack) {
|
||||
/** @private {MediaSource} */
|
||||
this.mediaSource_ = mediaSource;
|
||||
|
||||
/** @private {TextTrack} */
|
||||
this.textTrack_ = textTrack;
|
||||
/** @private {shakaExtern.TextDisplayer} */
|
||||
this.textDisplayer_ = textDisplayer;
|
||||
|
||||
/** @private {!Object.<shaka.util.ManifestParserUtils.ContentType,
|
||||
SourceBuffer>} */
|
||||
@@ -204,8 +204,8 @@ shaka.media.MediaSourceEngine.prototype.destroy = function() {
|
||||
this.eventManager_ = null;
|
||||
this.video_ = null;
|
||||
this.mediaSource_ = null;
|
||||
this.textTrack_ = null;
|
||||
this.textEngine_ = null;
|
||||
this.textDisplayer_ = null;
|
||||
this.sourceBuffers_ = {};
|
||||
if (!COMPILED) {
|
||||
for (var contentType in this.queues_) {
|
||||
@@ -265,7 +265,7 @@ shaka.media.MediaSourceEngine.prototype.init = function(typeConfig) {
|
||||
*/
|
||||
shaka.media.MediaSourceEngine.prototype.reinitText = function(mimeType) {
|
||||
if (!this.textEngine_) {
|
||||
this.textEngine_ = new shaka.text.TextEngine(this.textTrack_);
|
||||
this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
|
||||
}
|
||||
this.textEngine_.initParser(mimeType);
|
||||
};
|
||||
|
||||
+15
-43
@@ -28,6 +28,7 @@ goog.require('shaka.media.PlayheadObserver');
|
||||
goog.require('shaka.media.SegmentReference');
|
||||
goog.require('shaka.media.StreamingEngine');
|
||||
goog.require('shaka.net.NetworkingEngine');
|
||||
goog.require('shaka.text.SimpleTextDisplayer');
|
||||
goog.require('shaka.util.CancelableChain');
|
||||
goog.require('shaka.util.ConfigUtils');
|
||||
goog.require('shaka.util.Error');
|
||||
@@ -67,8 +68,8 @@ shaka.Player = function(video, opt_dependencyInjector) {
|
||||
/** @private {HTMLMediaElement} */
|
||||
this.video_ = video;
|
||||
|
||||
/** @private {TextTrack} */
|
||||
this.textTrack_ = null;
|
||||
/** @private {shakaExtern.TextDisplayer} */
|
||||
this.textDisplayer_ = null;
|
||||
|
||||
/** @private {shaka.util.EventManager} */
|
||||
this.eventManager_ = new shaka.util.EventManager();
|
||||
@@ -207,16 +208,16 @@ shaka.Player.prototype.destroy = function() {
|
||||
this.unloadChain_,
|
||||
this.destroyStreaming_(),
|
||||
this.eventManager_ ? this.eventManager_.destroy() : null,
|
||||
this.networkingEngine_ ? this.networkingEngine_.destroy() : null
|
||||
this.networkingEngine_ ? this.networkingEngine_.destroy() : null,
|
||||
this.textDisplayer_ ? this.textDisplayer_.destroy() : null
|
||||
]);
|
||||
|
||||
this.video_ = null;
|
||||
this.textTrack_ = null;
|
||||
this.textDisplayer_ = null;
|
||||
this.eventManager_ = null;
|
||||
this.abrManager_ = null;
|
||||
this.networkingEngine_ = null;
|
||||
this.config_ = null;
|
||||
|
||||
return p;
|
||||
}.bind(this));
|
||||
};
|
||||
@@ -494,6 +495,8 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime,
|
||||
this.abrManager_ = new abrManagerFactory();
|
||||
this.abrManager_.configure(this.config_.abr);
|
||||
|
||||
this.textDisplayer_ = new this.config_.textDisplayFactory();
|
||||
|
||||
goog.asserts.assert(this.networkingEngine_, 'Must not be destroyed');
|
||||
return shaka.media.ManifestParser.getFactory(
|
||||
manifestUri,
|
||||
@@ -561,7 +564,6 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime,
|
||||
this.drmEngine_.configure(this.config_.drm);
|
||||
return this.drmEngine_.init(manifest, false /* isOffline */);
|
||||
}.bind(this)).then(function() {
|
||||
|
||||
// Re-filter the manifest after DRM has been initialized.
|
||||
this.manifest_.periods.forEach(this.filterPeriod_.bind(this));
|
||||
|
||||
@@ -604,7 +606,6 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime,
|
||||
]);
|
||||
}.bind(this)).then(function() {
|
||||
this.abrManager_.init(this.switch_.bind(this));
|
||||
|
||||
// MediaSource is open, so create the Playhead, MediaSourceEngine, and
|
||||
// StreamingEngine.
|
||||
var startTime = opt_startTime || this.config_.playRangeStart;
|
||||
@@ -618,7 +619,6 @@ shaka.Player.prototype.load = function(manifestUri, opt_startTime,
|
||||
// If the content is multi-codec and the browser can play more than one of
|
||||
// them, choose codecs now before we initialize streaming.
|
||||
this.chooseCodecsAndFilterManifest_();
|
||||
|
||||
return this.streamingEngine_.init();
|
||||
}.bind(this)).then(function() {
|
||||
if (this.config_.streaming.startAtSegmentBoundary) {
|
||||
@@ -821,7 +821,7 @@ shaka.Player.prototype.createMediaSource = function() {
|
||||
*/
|
||||
shaka.Player.prototype.createMediaSourceEngine = function() {
|
||||
return new shaka.media.MediaSourceEngine(
|
||||
this.video_, this.mediaSource_, this.textTrack_);
|
||||
this.video_, this.mediaSource_, this.textDisplayer_);
|
||||
};
|
||||
|
||||
|
||||
@@ -1432,7 +1432,7 @@ shaka.Player.prototype.selectTextLanguage = function(language, opt_role) {
|
||||
* @export
|
||||
*/
|
||||
shaka.Player.prototype.isTextTrackVisible = function() {
|
||||
return this.textTrack_.mode == 'showing';
|
||||
return this.textDisplayer_.isTextVisible();
|
||||
};
|
||||
|
||||
|
||||
@@ -1443,7 +1443,7 @@ shaka.Player.prototype.isTextTrackVisible = function() {
|
||||
* @export
|
||||
*/
|
||||
shaka.Player.prototype.setTextTrackVisibility = function(on) {
|
||||
this.textTrack_.mode = on ? 'showing' : 'hidden';
|
||||
this.textDisplayer_.setTextVisibility(on);
|
||||
this.onTextTrackVisibility_();
|
||||
};
|
||||
|
||||
@@ -1662,30 +1662,6 @@ shaka.Player.prototype.initialize_ = function() {
|
||||
// Start the (potentially slow) process of opening MediaSource now.
|
||||
this.mediaSourceOpen_ = this.createMediaSource();
|
||||
|
||||
// If the video element has TextTracks, disable them. If we see one that
|
||||
// was created by a previous instance of Shaka Player, reuse it.
|
||||
for (var i = 0; i < this.video_.textTracks.length; ++i) {
|
||||
var track = this.video_.textTracks[i];
|
||||
track.mode = 'disabled';
|
||||
|
||||
if (track.label == shaka.Player.TextTrackLabel_) {
|
||||
this.textTrack_ = track;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.textTrack_) {
|
||||
// As far as I can tell, there is no observable difference between setting
|
||||
// kind to 'subtitles' or 'captions' when creating the TextTrack object.
|
||||
// The individual text tracks from the manifest will still have their own
|
||||
// kinds which can be displayed in the app's UI.
|
||||
this.textTrack_ = this.video_.addTextTrack(
|
||||
'subtitles', shaka.Player.TextTrackLabel_);
|
||||
}
|
||||
this.textTrack_.mode = 'hidden';
|
||||
|
||||
// TODO: test that in all cases, the built-in CC controls in the video element
|
||||
// are toggling our TextTrack.
|
||||
|
||||
// Listen for video errors.
|
||||
this.eventManager_.listen(this.video_, 'error',
|
||||
this.onVideoError_.bind(this));
|
||||
@@ -1831,13 +1807,6 @@ shaka.Player.prototype.resetStreaming_ = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @const {string}
|
||||
* @private
|
||||
*/
|
||||
shaka.Player.TextTrackLabel_ = 'Shaka Player TextTrack';
|
||||
|
||||
|
||||
/**
|
||||
* @return {!Object}
|
||||
* @private
|
||||
@@ -1904,6 +1873,9 @@ shaka.Player.prototype.defaultConfig_ = function() {
|
||||
jumpLargeGaps: false
|
||||
},
|
||||
abrFactory: shaka.abr.SimpleAbrManager,
|
||||
textDisplayFactory: function(videoElement) {
|
||||
return new shaka.text.SimpleTextDisplayer(videoElement);
|
||||
}.bind(null, this.video_),
|
||||
abr: {
|
||||
enabled: true,
|
||||
// This is a relatively safe default, since 3G cell connections
|
||||
@@ -2355,7 +2327,7 @@ shaka.Player.prototype.chooseStreamsAndSwitch_ = function(period) {
|
||||
languageMatches[ContentType.TEXT] &&
|
||||
chosen[ContentType.TEXT].language !=
|
||||
chosen[ContentType.AUDIO].language) {
|
||||
this.textTrack_.mode = 'showing';
|
||||
this.textDisplayer_.setTextVisibility(true);
|
||||
this.onTextTrackVisibility_();
|
||||
}
|
||||
}
|
||||
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/**
|
||||
* @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.text.Cue');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a Cue object.
|
||||
*
|
||||
* @param {number} startTime
|
||||
* @param {number} endTime
|
||||
* @param {!string} payload
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue = function(startTime, endTime, payload) {
|
||||
var Cue = shaka.text.Cue;
|
||||
|
||||
/**
|
||||
* The start time of the cue in seconds and fractions of a second.
|
||||
* @type {number}
|
||||
*/
|
||||
this.startTime = startTime;
|
||||
|
||||
/**
|
||||
* The end time of the cue in seconds and fractions of a second.
|
||||
* @type {number}
|
||||
*/
|
||||
this.endTime = endTime;
|
||||
|
||||
/**
|
||||
* The text payload of the cue.
|
||||
* @type {!string}
|
||||
*/
|
||||
this.payload = payload;
|
||||
|
||||
/**
|
||||
* The indent (in percent) of the cue box in the direction defined by the
|
||||
* writing direction.
|
||||
* @type {?number}
|
||||
*/
|
||||
this.position = null;
|
||||
|
||||
/**
|
||||
* Position alignment of the cue.
|
||||
* @type {shaka.text.Cue.positionAlign}
|
||||
*/
|
||||
this.positionAlign = Cue.positionAlign.AUTO;
|
||||
|
||||
/**
|
||||
* Size of the cue box (in percents).
|
||||
* @type {number}
|
||||
*/
|
||||
this.size = 100;
|
||||
|
||||
/**
|
||||
* Alignment of the text inside the cue box.
|
||||
* @type {shaka.text.Cue.textAlign}
|
||||
*/
|
||||
this.textAlign = Cue.textAlign.CENTER;
|
||||
|
||||
/**
|
||||
* Text writing direction of the cue.
|
||||
* (Vertical growing left, vertical growing right or horizontal).
|
||||
* NOTE: Horizontal right-to-left text is handled by setting
|
||||
* cue.textAlign to 'end'.
|
||||
* @type {shaka.text.Cue.writingDirection}
|
||||
*/
|
||||
this.writingDirection = Cue.writingDirection.HORIZONTAL;
|
||||
|
||||
/**
|
||||
* The way to interpret line field. (Either as an integer line number or
|
||||
* percentage from the display box).
|
||||
* @type {shaka.text.Cue.lineInterpretation}
|
||||
*/
|
||||
this.lineInterpretation = Cue.lineInterpretation.LINE_NUMBER;
|
||||
|
||||
/**
|
||||
* The offset from the display box in either number of lines or
|
||||
* percentage depending on the value of lineInterpretation.
|
||||
* @type {?number}
|
||||
*/
|
||||
this.line = null;
|
||||
|
||||
/**
|
||||
* Line alignment of the cue box.
|
||||
* @type {shaka.text.Cue.lineAlign}
|
||||
*/
|
||||
this.lineAlign = Cue.lineAlign.CENTER;
|
||||
|
||||
/**
|
||||
* Vertical alignments of the cues within their extents.
|
||||
* @type {shaka.text.Cue.displayAlign}
|
||||
*/
|
||||
this.displayAlign = Cue.displayAlign.BEFORE;
|
||||
|
||||
/**
|
||||
* Text color represented by any string that would be
|
||||
* accepted in CSS.
|
||||
* E. g. '#FFFFFF' or 'white'.
|
||||
* @type {!string}
|
||||
*/
|
||||
this.color = '';
|
||||
|
||||
/**
|
||||
* Text background color represented by any string that would be
|
||||
* accepted in CSS.
|
||||
* E. g. '#FFFFFF' or 'white'.
|
||||
* @type {!string}
|
||||
*/
|
||||
this.backgroundColor = '';
|
||||
|
||||
/**
|
||||
* Text font size in pixels.
|
||||
* @type {?number}
|
||||
*/
|
||||
this.fontSize = null;
|
||||
|
||||
/**
|
||||
* Text font weight. Either normal or bold.
|
||||
* @type {shaka.text.Cue.fontWeight}
|
||||
*/
|
||||
this.fontWeight = Cue.fontWeight.NORMAL;
|
||||
|
||||
/**
|
||||
* Text font family.
|
||||
* @type {!string}
|
||||
*/
|
||||
this.fontFamily = '';
|
||||
|
||||
/**
|
||||
* Whether or not line wrapping should be applied
|
||||
* to the cue.
|
||||
* @type {boolean}
|
||||
*/
|
||||
this.wrapLine = true;
|
||||
|
||||
/**
|
||||
* Id of the cue.
|
||||
* @type {!string}
|
||||
*/
|
||||
this.id = '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.positionAlign = {
|
||||
LEFT: 'line-left',
|
||||
RIGHT: 'line-right',
|
||||
CENTER: 'center',
|
||||
AUTO: 'auto'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.textAlign = {
|
||||
LEFT: 'left',
|
||||
RIGHT: 'right',
|
||||
CENTER: 'center',
|
||||
START: 'start',
|
||||
END: 'end'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.displayAlign = {
|
||||
BEFORE: 'before',
|
||||
CENTER: 'center',
|
||||
AFTER: 'after'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.writingDirection = {
|
||||
HORIZONTAL: 0,
|
||||
VERTICAL_LEFT: 1,
|
||||
VERTICAL_RIGHT: 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {number}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.lineInterpretation = {
|
||||
LINE_NUMBER: 0,
|
||||
PERCENTAGE: 1
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @enum {string}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.lineAlign = {
|
||||
CENTER: 'center',
|
||||
START: 'start',
|
||||
END: 'end'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* In CSS font weight can be a number, where 400 is normal
|
||||
* and 700 is bold. Use these values for the enum for consistency.
|
||||
* @enum {number}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.Cue.fontWeight = {
|
||||
NORMAL: 400,
|
||||
BOLD: 700
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ goog.provide('shaka.text.Mp4VttParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.log');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.text.VttTextParser');
|
||||
goog.require('shaka.util.DataViewReader');
|
||||
@@ -110,7 +111,7 @@ shaka.text.Mp4VttParser.prototype.parseMedia = function(data, time) {
|
||||
var presentations = [];
|
||||
/** @type {!Array.<ArrayBuffer>} */
|
||||
var payloads = [];
|
||||
/** @type {!Array.<TextTrackCue>} */
|
||||
/** @type {!Array.<shaka.text.Cue>} */
|
||||
var cues = [];
|
||||
|
||||
var sawTFDT = false;
|
||||
@@ -267,7 +268,7 @@ shaka.text.Mp4VttParser.parseTRUN_ = function(version, flags, reader) {
|
||||
* @param {!ArrayBuffer} data
|
||||
* @param {number} startTime
|
||||
* @param {number} endTime
|
||||
* @return {TextTrackCue}
|
||||
* @return {shaka.text.Cue}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.Mp4VttParser.parseVTTC_ = function(data, startTime, endTime) {
|
||||
@@ -307,7 +308,7 @@ shaka.text.Mp4VttParser.parseVTTC_ = function(data, startTime, endTime) {
|
||||
* @param {?string} settings
|
||||
* @param {number} startTime
|
||||
* @param {number} endTime
|
||||
* @return {TextTrackCue}
|
||||
* @return {!shaka.text.Cue}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.Mp4VttParser.assembleCue_ = function(payload,
|
||||
@@ -315,16 +316,16 @@ shaka.text.Mp4VttParser.assembleCue_ = function(payload,
|
||||
settings,
|
||||
startTime,
|
||||
endTime) {
|
||||
var cue = shaka.text.TextEngine.makeCue(
|
||||
var cue = new shaka.text.Cue(
|
||||
startTime,
|
||||
endTime,
|
||||
payload);
|
||||
|
||||
if (cue && id) {
|
||||
if (id) {
|
||||
cue.id = id;
|
||||
}
|
||||
|
||||
if (cue && settings) {
|
||||
if (settings) {
|
||||
var parser = new shaka.util.TextParser(settings);
|
||||
|
||||
var word = parser.readWord();
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* @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.text.SimpleTextDisplayer');
|
||||
|
||||
goog.require('shaka.log');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This defines the default text displayer plugin. An instance of this
|
||||
* class is used when no custom displayer is given.
|
||||
* </p>
|
||||
* <p>
|
||||
* This class simply converts shaka.text.Cue objects to
|
||||
* TextTrackCues and feeds them to the browser.
|
||||
* </p>
|
||||
*
|
||||
* @param {HTMLMediaElement} video
|
||||
* @constructor
|
||||
* @struct
|
||||
* @implements {shakaExtern.TextDisplayer}
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer = function(video) {
|
||||
/** @private {TextTrack} */
|
||||
this.textTrack_ = null;
|
||||
|
||||
// TODO: test that in all cases, the built-in CC controls in the video element
|
||||
// are toggling our TextTrack.
|
||||
|
||||
// If the video element has TextTracks, disable them. If we see one that
|
||||
// was created by a previous instance of Shaka Player, reuse it.
|
||||
for (var i = 0; i < video.textTracks.length; ++i) {
|
||||
var track = video.textTracks[i];
|
||||
track.mode = 'disabled';
|
||||
|
||||
if (track.label == shaka.text.SimpleTextDisplayer.TextTrackLabel_) {
|
||||
this.textTrack_ = track;
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.textTrack_) {
|
||||
// As far as I can tell, there is no observable difference between setting
|
||||
// kind to 'subtitles' or 'captions' when creating the TextTrack object.
|
||||
// The individual text tracks from the manifest will still have their own
|
||||
// kinds which can be displayed in the app's UI.
|
||||
this.textTrack_ = video.addTextTrack(
|
||||
'subtitles', shaka.text.SimpleTextDisplayer.TextTrackLabel_);
|
||||
}
|
||||
this.textTrack_.mode = 'hidden';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.remove = function(start, end) {
|
||||
// Check that the displayer hasn't been destroyed.
|
||||
if (!this.textTrack_) return false;
|
||||
|
||||
this.removeWhere_(function(cue) {
|
||||
if (cue.startTime >= end || cue.endTime <= start) {
|
||||
// Outside the remove range. Hang on to it.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.append = function(cues) {
|
||||
var textTrackCues = [];
|
||||
for (var i = 0; i < cues.length; i++) {
|
||||
var cue = this.convertToTextTrackCue_(cues[i]);
|
||||
if (cue)
|
||||
textTrackCues.push(cue);
|
||||
}
|
||||
|
||||
textTrackCues.forEach(function(cue) {
|
||||
this.textTrack_.addCue(cue);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.destroy = function() {
|
||||
if (this.textTrack_) {
|
||||
this.removeWhere_(function(cue) { return true; });
|
||||
}
|
||||
|
||||
this.textTrack_ = null;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.isTextVisible = function() {
|
||||
return this.textTrack_.mode == 'showing';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @override
|
||||
* @export
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.setTextVisibility = function(on) {
|
||||
this.textTrack_.mode = on ? 'showing' : 'hidden';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} shakaCue
|
||||
* @return {TextTrackCue}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.convertToTextTrackCue_ =
|
||||
function(shakaCue) {
|
||||
if (shakaCue.startTime >= shakaCue.endTime) {
|
||||
// IE/Edge will throw in this case.
|
||||
// See issue #501
|
||||
shaka.log.warning('Invalid cue times: ' + shakaCue.startTime +
|
||||
' - ' + shakaCue.endTime);
|
||||
return null;
|
||||
}
|
||||
|
||||
var Cue = shaka.text.Cue;
|
||||
var vttCue = new VTTCue(shakaCue.startTime,
|
||||
shakaCue.endTime,
|
||||
shakaCue.payload);
|
||||
|
||||
// NOTE: positionAlign and lineAlign settings are not supported by Chrome
|
||||
// at the moment, so setting them will have no effect.
|
||||
// The bug on chromium to implement them:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=633690
|
||||
|
||||
vttCue.lineAlign = shakaCue.lineAlign;
|
||||
vttCue.positionAlign = shakaCue.positionAlign;
|
||||
vttCue.size = shakaCue.size;
|
||||
vttCue.align = shakaCue.textAlign;
|
||||
|
||||
if (shakaCue.textAlign == 'center' && vttCue.align != 'center') {
|
||||
// Workaround for a Chrome bug http://crbug.com/663797
|
||||
// Chrome does not support align = 'center'
|
||||
vttCue.position = 'auto';
|
||||
vttCue.align = 'middle';
|
||||
}
|
||||
|
||||
if (shakaCue.writingDirection == Cue.writingDirection.VERTICAL_LEFT)
|
||||
vttCue.vertical = 'lr';
|
||||
else if (shakaCue.writingDirection == Cue.writingDirection.VERTICAL_RIGHT)
|
||||
vttCue.vertical = 'rl';
|
||||
|
||||
// snapToLines flag is true by default
|
||||
if (shakaCue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE)
|
||||
vttCue.snapToLines = false;
|
||||
|
||||
if (shakaCue.line)
|
||||
vttCue.line = shakaCue.line;
|
||||
|
||||
if (shakaCue.position)
|
||||
vttCue.position = shakaCue.position;
|
||||
|
||||
return vttCue;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove all cues for which the matching function returns true.
|
||||
*
|
||||
* @param {function(!TextTrackCue):boolean} predicate
|
||||
* @private
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.prototype.removeWhere_ = function(predicate) {
|
||||
var cues = this.textTrack_.cues;
|
||||
var removeMe = [];
|
||||
|
||||
// Remove these in another loop to avoid mutating the TextTrackCueList
|
||||
// while iterating over it. This allows us to avoid making assumptions
|
||||
// about whether or not this.textTrack_.remove() will alter that list.
|
||||
for (var i = 0; i < cues.length; ++i) {
|
||||
if (predicate(cues[i])) {
|
||||
removeMe.push(cues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < removeMe.length; ++i) {
|
||||
this.textTrack_.removeCue(removeMe[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @const {string}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.SimpleTextDisplayer.TextTrackLabel_ = 'Shaka Player TextTrack';
|
||||
+46
-97
@@ -26,17 +26,17 @@ goog.require('shaka.util.IDestroyable');
|
||||
/**
|
||||
* Manages text parsers and cues.
|
||||
*
|
||||
* @param {shakaExtern.TextDisplayer} displayer
|
||||
* @struct
|
||||
* @constructor
|
||||
* @param {TextTrack} track
|
||||
* @implements {shaka.util.IDestroyable}
|
||||
*/
|
||||
shaka.text.TextEngine = function(track) {
|
||||
shaka.text.TextEngine = function(displayer) {
|
||||
/** @private {shakaExtern.TextParser} */
|
||||
this.parser_ = null;
|
||||
|
||||
/** @private {TextTrack} */
|
||||
this.track_ = track;
|
||||
/** @private {shakaExtern.TextDisplayer} */
|
||||
this.displayer_ = displayer;
|
||||
|
||||
/** @private {number} */
|
||||
this.timestampOffset_ = 0;
|
||||
@@ -93,37 +93,21 @@ shaka.text.TextEngine.isTypeSupported = function(mimeType) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a cue using the best platform-specific interface available.
|
||||
*
|
||||
* @param {number} startTime
|
||||
* @param {number} endTime
|
||||
* @param {string} payload
|
||||
* @return {TextTrackCue} or null if the parameters were invalid.
|
||||
* @export
|
||||
*/
|
||||
shaka.text.TextEngine.makeCue = function(startTime, endTime, payload) {
|
||||
if (startTime >= endTime) {
|
||||
// IE/Edge will throw in this case.
|
||||
// See issue #501
|
||||
shaka.log.warning('Invalid cue times: ' + startTime + ' - ' + endTime);
|
||||
return null;
|
||||
}
|
||||
/** @override */
|
||||
shaka.text.TextEngine.prototype.destroy = function() {
|
||||
this.parser_ = null;
|
||||
this.displayer_ = null;
|
||||
|
||||
return new VTTCue(startTime, endTime, payload);
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
|
||||
/** @override */
|
||||
shaka.text.TextEngine.prototype.destroy = function() {
|
||||
if (this.track_) {
|
||||
this.removeWhere_(function(cue) { return true; });
|
||||
}
|
||||
|
||||
this.parser_ = null;
|
||||
this.track_ = null;
|
||||
|
||||
return Promise.resolve();
|
||||
/**
|
||||
* @param {shakaExtern.TextDisplayer} displayer
|
||||
* @export
|
||||
*/
|
||||
shaka.text.TextEngine.prototype.setDisplayer = function(displayer) {
|
||||
this.displayer_ = displayer;
|
||||
};
|
||||
|
||||
|
||||
@@ -155,7 +139,7 @@ shaka.text.TextEngine.prototype.appendBuffer =
|
||||
// Start the operation asynchronously to avoid blocking the caller.
|
||||
return Promise.resolve().then(function() {
|
||||
// Check that TextEngine hasn't been destroyed.
|
||||
if (!this.track_) return;
|
||||
if (!this.parser_ || !this.displayer_) return;
|
||||
|
||||
if (startTime == null || endTime == null) {
|
||||
this.parser_.parseInit(buffer);
|
||||
@@ -170,12 +154,12 @@ shaka.text.TextEngine.prototype.appendBuffer =
|
||||
};
|
||||
|
||||
// Parse the buffer and add the new cues.
|
||||
var cues = this.parser_.parseMedia(buffer, time);
|
||||
var allCues = this.parser_.parseMedia(buffer, time);
|
||||
var cuesToAppend = allCues.filter(function(cue) {
|
||||
return cue.startTime < this.appendWindowEnd_;
|
||||
}.bind(this));
|
||||
|
||||
for (var i = 0; i < cues.length; ++i) {
|
||||
if (cues[i].startTime >= this.appendWindowEnd_) break;
|
||||
this.track_.addCue(cues[i]);
|
||||
}
|
||||
this.displayer_.append(cuesToAppend);
|
||||
|
||||
// NOTE: We update the buffered range from the start and end times passed
|
||||
// down from the segment reference, not with the start and end times of the
|
||||
@@ -202,42 +186,31 @@ shaka.text.TextEngine.prototype.appendBuffer =
|
||||
shaka.text.TextEngine.prototype.remove = function(start, end) {
|
||||
// Start the operation asynchronously to avoid blocking the caller.
|
||||
return Promise.resolve().then(function() {
|
||||
// Check that TextEngine hasn't been destroyed.
|
||||
if (!this.track_) return;
|
||||
|
||||
this.removeWhere_(function(cue) {
|
||||
if (cue.startTime >= end || cue.endTime <= start) {
|
||||
// Outside the remove range. Hang on to it.
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (this.bufferStart_ == null) {
|
||||
goog.asserts.assert(this.bufferEnd_ == null,
|
||||
'end must be null if start is null');
|
||||
} else {
|
||||
goog.asserts.assert(this.bufferEnd_ != null,
|
||||
'end must be non-null if start is non-null');
|
||||
|
||||
// Update buffered range.
|
||||
if (end <= this.bufferStart_ || start >= this.bufferEnd_) {
|
||||
// No intersection. Nothing was removed.
|
||||
} else if (start <= this.bufferStart_ && end >= this.bufferEnd_) {
|
||||
// We wiped out everything.
|
||||
goog.asserts.assert(
|
||||
this.track_.cues.length == 0, 'should be no cues left');
|
||||
this.bufferStart_ = this.bufferEnd_ = null;
|
||||
} else if (start <= this.bufferStart_ && end < this.bufferEnd_) {
|
||||
// We removed from the beginning of the range.
|
||||
this.bufferStart_ = end;
|
||||
} else if (start > this.bufferStart_ && end >= this.bufferEnd_) {
|
||||
// We removed from the end of the range.
|
||||
this.bufferEnd_ = start;
|
||||
if (this.displayer_ && this.displayer_.remove(start, end)) {
|
||||
if (this.bufferStart_ == null) {
|
||||
goog.asserts.assert(this.bufferEnd_ == null,
|
||||
'end must be null if start is null');
|
||||
} else {
|
||||
// We removed from the middle? StreamingEngine isn't supposed to.
|
||||
goog.asserts.assert(
|
||||
false, 'removal from the middle is not supported by TextEngine');
|
||||
goog.asserts.assert(this.bufferEnd_ != null,
|
||||
'end must be non-null if start is non-null');
|
||||
|
||||
// Update buffered range.
|
||||
if (end <= this.bufferStart_ || start >= this.bufferEnd_) {
|
||||
// No intersection. Nothing was removed.
|
||||
} else if (start <= this.bufferStart_ && end >= this.bufferEnd_) {
|
||||
// We wiped out everything.
|
||||
this.bufferStart_ = this.bufferEnd_ = null;
|
||||
} else if (start <= this.bufferStart_ && end < this.bufferEnd_) {
|
||||
// We removed from the beginning of the range.
|
||||
this.bufferStart_ = end;
|
||||
} else if (start > this.bufferStart_ && end >= this.bufferEnd_) {
|
||||
// We removed from the end of the range.
|
||||
this.bufferEnd_ = start;
|
||||
} else {
|
||||
// We removed from the middle? StreamingEngine isn't supposed to.
|
||||
goog.asserts.assert(
|
||||
false, 'removal from the middle is not supported by TextEngine');
|
||||
}
|
||||
}
|
||||
}
|
||||
}.bind(this));
|
||||
@@ -299,31 +272,6 @@ shaka.text.TextEngine.prototype.bufferedAheadOf = function(t) {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove all cues for which the matching function returns true.
|
||||
*
|
||||
* @param {function(!TextTrackCue):boolean} predicate
|
||||
* @private
|
||||
*/
|
||||
shaka.text.TextEngine.prototype.removeWhere_ = function(predicate) {
|
||||
var cues = this.track_.cues;
|
||||
var removeMe = [];
|
||||
|
||||
// Remove these in another loop to avoid mutating the TextTrackCueList
|
||||
// while iterating over it. This allows us to avoid making assumptions
|
||||
// about whether or not this.track_.remove() will alter that list.
|
||||
for (var i = 0; i < cues.length; ++i) {
|
||||
if (predicate(cues[i])) {
|
||||
removeMe.push(cues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < removeMe.length; ++i) {
|
||||
this.track_.removeCue(removeMe[i]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param {Function} parser
|
||||
@@ -350,3 +298,4 @@ shaka.text.TextEngine.TextParserWrapper_.prototype.parseMedia = function(
|
||||
time.segmentStart,
|
||||
time.segmentEnd);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
goog.provide('shaka.text.TtmlTextParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.util.Error');
|
||||
goog.require('shaka.util.StringUtils');
|
||||
@@ -271,7 +272,7 @@ shaka.text.TtmlTextParser.addNewLines_ = function(element, whitespaceTrim) {
|
||||
* @param {!Array.<!Element>} styles
|
||||
* @param {!Array.<!Element>} regions
|
||||
* @param {boolean} whitespaceTrim
|
||||
* @return {TextTrackCue}
|
||||
* @return {shaka.text.Cue}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.TtmlTextParser.parseCue_ = function(
|
||||
@@ -310,9 +311,7 @@ shaka.text.TtmlTextParser.parseCue_ = function(
|
||||
start += offset;
|
||||
end += offset;
|
||||
|
||||
var cue = shaka.text.TextEngine.makeCue(start, end, payload);
|
||||
if (!cue)
|
||||
return null;
|
||||
var cue = new shaka.text.Cue(start, end, payload);
|
||||
|
||||
// Get other properties if available
|
||||
var region = shaka.text.TtmlTextParser.getElementFromCollection_(
|
||||
@@ -326,7 +325,7 @@ shaka.text.TtmlTextParser.parseCue_ = function(
|
||||
/**
|
||||
* Adds applicable style properties to a cue.
|
||||
*
|
||||
* @param {!TextTrackCue} cue
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {!Element} cueElement
|
||||
* @param {Element} region
|
||||
* @param {!Array.<!Element>} styles
|
||||
@@ -335,8 +334,9 @@ shaka.text.TtmlTextParser.parseCue_ = function(
|
||||
shaka.text.TtmlTextParser.addStyle_ = function(
|
||||
cue, cueElement, region, styles) {
|
||||
var TtmlTextParser = shaka.text.TtmlTextParser;
|
||||
var results = null;
|
||||
var Cue = shaka.text.Cue;
|
||||
|
||||
var results = null;
|
||||
|
||||
var extent = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:extent');
|
||||
@@ -351,53 +351,77 @@ shaka.text.TtmlTextParser.addStyle_ = function(
|
||||
|
||||
var writingMode = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:writingMode');
|
||||
var isVerticalText = true;
|
||||
if (writingMode == 'tb' || writingMode == 'tblr')
|
||||
cue.vertical = 'lr';
|
||||
cue.writingDirection = Cue.writingDirection.VERTICAL_LEFT;
|
||||
else if (writingMode == 'tbrl')
|
||||
cue.vertical = 'rl';
|
||||
else
|
||||
isVerticalText = false;
|
||||
cue.writingDirection = Cue.writingDirection.VERTICAL_RIGHT;
|
||||
|
||||
var origin = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:origin');
|
||||
if (origin) {
|
||||
results = TtmlTextParser.percentValues_.exec(origin);
|
||||
if (results != null) {
|
||||
// for vertical text use first coordinate of tts:origin
|
||||
// to represent line of the cue and second - for position.
|
||||
// Otherwise (horizontal), use them the other way around.
|
||||
if (isVerticalText) {
|
||||
cue.position = Number(results[2]);
|
||||
cue.line = Number(results[1]);
|
||||
} else {
|
||||
// for horizontal text use first coordinate of tts:origin
|
||||
// to represent position of the cue and second - for line.
|
||||
// Otherwise (vertical), use them the other way around.
|
||||
if (cue.writingDirection == Cue.writingDirection.HORIZONTAL) {
|
||||
cue.position = Number(results[1]);
|
||||
cue.line = Number(results[2]);
|
||||
} else {
|
||||
cue.position = Number(results[2]);
|
||||
cue.line = Number(results[1]);
|
||||
}
|
||||
// A boolean indicating whether the line is an integer
|
||||
// number of lines (using the line dimensions of the first
|
||||
// line of the cue), or whether it is a percentage of the
|
||||
// dimension of the video. The flag is set to true when lines
|
||||
// are counted, and false otherwise.
|
||||
cue.snapToLines = false;
|
||||
|
||||
cue.lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
||||
}
|
||||
}
|
||||
|
||||
var align = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:textAlign');
|
||||
if (align) {
|
||||
cue.align = align;
|
||||
if (align == 'center') {
|
||||
if (cue.align != 'center') {
|
||||
// Workaround for a Chrome bug http://crbug.com/663797
|
||||
// Chrome does not support align = 'center'
|
||||
cue.align = 'middle';
|
||||
}
|
||||
cue.position = 'auto';
|
||||
}
|
||||
cue.positionAlign = TtmlTextParser.textAlignToPositionAlign_[align];
|
||||
cue.lineAlign = TtmlTextParser.textAlignToLineAlign_[align];
|
||||
|
||||
goog.asserts.assert(align.toUpperCase() in Cue.textAlign,
|
||||
align.toUpperCase() +
|
||||
' Should be in Cue.textAlign values!');
|
||||
|
||||
cue.textAlign = Cue.textAlign[align.toUpperCase()];
|
||||
}
|
||||
|
||||
var displayAlign = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:displayAlign');
|
||||
if (displayAlign) {
|
||||
goog.asserts.assert(displayAlign.toUpperCase() in Cue.displayAlign,
|
||||
displayAlign.toUpperCase() +
|
||||
' Should be in Cue.displayAlign values!');
|
||||
cue.displayAlign = Cue.displayAlign[displayAlign.toUpperCase()];
|
||||
}
|
||||
|
||||
var color = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:color');
|
||||
if (color)
|
||||
cue.color = color;
|
||||
|
||||
var backgroundColor = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:backgroundColor');
|
||||
if (backgroundColor)
|
||||
cue.backgroundColor = backgroundColor;
|
||||
|
||||
var fontFamily = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:fontFamily');
|
||||
if (fontFamily)
|
||||
cue.fontFamily = fontFamily;
|
||||
|
||||
var fontWeight = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:fontWeight');
|
||||
if (fontWeight && fontWeight == 'bold')
|
||||
cue.fontWeight = Cue.fontWeight.BOLD;
|
||||
|
||||
var wrapOption = TtmlTextParser.getStyleAttribute_(
|
||||
cueElement, region, styles, 'tts:wrapOption');
|
||||
if (wrapOption && wrapOption == 'noWrap')
|
||||
cue.wrapLine = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
+92
-32
@@ -19,6 +19,7 @@ goog.provide('shaka.text.VttTextParser');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('shaka.log');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.util.Error');
|
||||
goog.require('shaka.util.StringUtils');
|
||||
@@ -103,7 +104,7 @@ shaka.text.VttTextParser.prototype.parseMedia = function(data, time) {
|
||||
*
|
||||
* @param {!Array.<string>} text
|
||||
* @param {number} timeOffset
|
||||
* @return {?TextTrackCue}
|
||||
* @return {shaka.text.Cue}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.VttTextParser.parseCue_ = function(text, timeOffset) {
|
||||
@@ -141,9 +142,7 @@ shaka.text.VttTextParser.parseCue_ = function(text, timeOffset) {
|
||||
// Get the payload.
|
||||
var payload = text.slice(1).join('\n').trim();
|
||||
|
||||
var cue = shaka.text.TextEngine.makeCue(start, end, payload);
|
||||
if (!cue)
|
||||
return null;
|
||||
var cue = new shaka.text.Cue(start, end, payload);
|
||||
|
||||
// Parse optional settings.
|
||||
parser.skipWhitespace();
|
||||
@@ -167,52 +166,113 @@ shaka.text.VttTextParser.parseCue_ = function(text, timeOffset) {
|
||||
/**
|
||||
* Parses a WebVTT setting from the given word.
|
||||
*
|
||||
* @param {!TextTrackCue} cue
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {string} word
|
||||
* @return {boolean} True on success.
|
||||
*/
|
||||
shaka.text.VttTextParser.parseSetting = function(cue, word) {
|
||||
// NOTE: positionAlign and lineAlign settings are not supported by Chrome
|
||||
// at the moment, so setting them will have no effect.
|
||||
// The bug on chromium to implement them:
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=633690
|
||||
|
||||
var VttTextParser = shaka.text.VttTextParser;
|
||||
var results = null;
|
||||
if ((results = /^align:(start|middle|center|end|left|right)$/.exec(word))) {
|
||||
cue.align = results[1];
|
||||
if (results[1] == 'center' && cue.align != 'center') {
|
||||
// Workaround for a Chrome bug http://crbug.com/663797
|
||||
// Chrome does not support align = 'center'
|
||||
cue.position = 'auto';
|
||||
cue.align = 'middle';
|
||||
}
|
||||
VttTextParser.setTextAlign_(cue, results[1]);
|
||||
} else if ((results = /^vertical:(lr|rl)$/.exec(word))) {
|
||||
cue.vertical = results[1];
|
||||
VttTextParser.setVerticalWritingDirection_(cue, results[1]);
|
||||
} else if ((results = /^size:(\d{1,2}|100)%$/.exec(word))) {
|
||||
cue.size = Number(results[1]);
|
||||
}
|
||||
// There was a disagreement between a working draft and an editor draft of
|
||||
// the WebVTT spec. According to the former, optional position alignment
|
||||
// options are 'start', 'end' and 'center'. According to the latter -
|
||||
// 'line-left', 'center' and 'line-right'.
|
||||
// We are going to support both options for now.
|
||||
else if ((results =
|
||||
/^position:(\d{1,2}|100)%(?:,(line-left|line-right|center|start|end))?$/
|
||||
.exec(word))) {
|
||||
cue.position = Number(results[1]);
|
||||
if (results[2])
|
||||
cue.positionAlign = results[2];
|
||||
} else if ((results =
|
||||
if (results[2]) {
|
||||
VttTextParser.setPositionAlign_(cue, results[2]);
|
||||
}
|
||||
} else {
|
||||
return VttTextParser.parsedLineValueAndInterpretation_(cue, word);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {!string} align
|
||||
* @private
|
||||
*/
|
||||
shaka.text.VttTextParser.setTextAlign_ = function(cue, align) {
|
||||
var Cue = shaka.text.Cue;
|
||||
if (align == 'middle') {
|
||||
cue.textAlign = Cue.textAlign.CENTER;
|
||||
} else {
|
||||
goog.asserts.assert(align.toUpperCase() in Cue.textAlign,
|
||||
align.toUpperCase() +
|
||||
' Should be in Cue.textAlign values!');
|
||||
|
||||
cue.textAlign = Cue.textAlign[align.toUpperCase()];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {!string} align
|
||||
* @private
|
||||
*/
|
||||
shaka.text.VttTextParser.setPositionAlign_ = function(cue, align) {
|
||||
var Cue = shaka.text.Cue;
|
||||
if (align == 'line-left' || align == 'start')
|
||||
cue.positionAlign = Cue.positionAlign.LEFT;
|
||||
else if (align == 'line-right' || align == 'end')
|
||||
cue.positionAlign = Cue.positionAlign.RIGHT;
|
||||
else
|
||||
cue.positionAlign = Cue.positionAlign.CENTER;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {!string} value
|
||||
* @private
|
||||
*/
|
||||
shaka.text.VttTextParser.setVerticalWritingDirection_ = function(cue, value) {
|
||||
var Cue = shaka.text.Cue;
|
||||
if (value == 'lr')
|
||||
cue.writingDirection = Cue.writingDirection.VERTICAL_LEFT;
|
||||
else
|
||||
cue.writingDirection = Cue.writingDirection.VERTICAL_RIGHT;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {!shaka.text.Cue} cue
|
||||
* @param {!string} word
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
shaka.text.VttTextParser.parsedLineValueAndInterpretation_ =
|
||||
function(cue, word) {
|
||||
var Cue = shaka.text.Cue;
|
||||
var results = null;
|
||||
if ((results =
|
||||
/^line:(\d{1,2}|100)%(?:,(start|end|center))?$/.exec(word))) {
|
||||
cue.snapToLines = false;
|
||||
cue.lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
||||
cue.line = Number(results[1]);
|
||||
if (results[2])
|
||||
cue.lineAlign = results[2];
|
||||
if (results[2]) {
|
||||
goog.asserts.assert(results[2].toUpperCase() in Cue.lineAlign,
|
||||
results[2].toUpperCase() +
|
||||
' Should be in Cue.lineAlign values!');
|
||||
cue.lineAlign = Cue.lineAlign[results[2].toUpperCase()];
|
||||
}
|
||||
} else if ((results = /^line:(-?\d+)(?:,(start|end|center))?$/.exec(word))) {
|
||||
cue.snapToLines = true;
|
||||
cue.lineInterpretation = Cue.lineInterpretation.LINE_NUMBER;
|
||||
cue.line = Number(results[1]);
|
||||
if (results[2])
|
||||
cue.lineAlign = results[2];
|
||||
if (results[2]) {
|
||||
goog.asserts.assert(results[2].toUpperCase() in Cue.lineAlign,
|
||||
results[2].toUpperCase() +
|
||||
' Should be in Cue.lineAlign values!');
|
||||
cue.lineAlign = Cue.lineAlign[results[2].toUpperCase()];
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,8 +48,10 @@ goog.require('shaka.polyfill.VTTCue');
|
||||
goog.require('shaka.polyfill.VideoPlayPromise');
|
||||
goog.require('shaka.polyfill.VideoPlaybackQuality');
|
||||
goog.require('shaka.polyfill.installAll');
|
||||
goog.require('shaka.text.Cue');
|
||||
goog.require('shaka.text.Mp4TtmlParser');
|
||||
goog.require('shaka.text.Mp4VttParser');
|
||||
goog.require('shaka.text.SimpleTextDisplayer');
|
||||
goog.require('shaka.text.TextEngine');
|
||||
goog.require('shaka.text.TtmlTextParser');
|
||||
goog.require('shaka.text.VttTextParser');
|
||||
|
||||
@@ -978,7 +978,8 @@ describe('MediaSourceEngine', function() {
|
||||
expect(mockTextEngine).toBeFalsy();
|
||||
mockTextEngine = jasmine.createSpyObj('TextEngine', [
|
||||
'initParser', 'destroy', 'appendBuffer', 'remove', 'setTimestampOffset',
|
||||
'setAppendWindowEnd', 'bufferStart', 'bufferEnd', 'bufferedAheadOf'
|
||||
'setAppendWindowEnd', 'bufferStart', 'bufferEnd', 'bufferedAheadOf',
|
||||
'setDisplayer'
|
||||
]);
|
||||
|
||||
var resolve = Promise.resolve.bind(Promise);
|
||||
|
||||
@@ -162,7 +162,7 @@ describe('Player', function() {
|
||||
});
|
||||
|
||||
describe('plays', function() {
|
||||
it('while external text tracks', function(done) {
|
||||
it('with external text tracks', function(done) {
|
||||
player.load('test:sintel_no_text_compiled').then(function() {
|
||||
// For some reason, using path-absolute URLs (i.e. without the hostname)
|
||||
// like this doesn't work on Safari. So manually resolve the URL.
|
||||
|
||||
+11
-1
@@ -53,6 +53,11 @@ describe('Player', function() {
|
||||
var playhead;
|
||||
/** @type {!shaka.test.FakePlayheadObserver} */
|
||||
var playheadObserver;
|
||||
/** @type {!shaka.test.FakeTextDisplayer} */
|
||||
var textDisplayer;
|
||||
/** @type {function():shakaExtern.TextDisplayer} */
|
||||
var textDisplayFactory;
|
||||
|
||||
var mediaSourceEngine;
|
||||
|
||||
/** @type {!shaka.test.FakeVideo} */
|
||||
@@ -90,6 +95,9 @@ describe('Player', function() {
|
||||
abrManager = new shaka.test.FakeAbrManager();
|
||||
abrFactory = function() { return abrManager; };
|
||||
|
||||
textDisplayer = new shaka.test.FakeTextDisplayer();
|
||||
textDisplayFactory = function() { return textDisplayer; };
|
||||
|
||||
function dependencyInjector(player) {
|
||||
networkingEngine =
|
||||
new shaka.test.FakeNetworkingEngine({}, new ArrayBuffer(0));
|
||||
@@ -121,7 +129,8 @@ describe('Player', function() {
|
||||
player.configure({
|
||||
// Ensures we don't get a warning about missing preference.
|
||||
preferredAudioLanguage: 'en',
|
||||
abrFactory: abrFactory
|
||||
abrFactory: abrFactory,
|
||||
textDisplayFactory: textDisplayFactory
|
||||
});
|
||||
|
||||
onError = jasmine.createSpy('error event');
|
||||
@@ -156,6 +165,7 @@ describe('Player', function() {
|
||||
expect(playheadObserver.destroy).toHaveBeenCalled();
|
||||
expect(mediaSourceEngine.destroy).toHaveBeenCalled();
|
||||
expect(streamingEngine.destroy).toHaveBeenCalled();
|
||||
expect(textDisplayer.destroy).toHaveBeenCalled();
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -372,4 +372,3 @@ shaka.test.FakeMediaSourceEngine.prototype.toIndex_ = function(type, ts) {
|
||||
shaka.test.FakeMediaSourceEngine.prototype.toTime_ = function(type, i) {
|
||||
return this.drift_ + (i * this.segmentData[type].segmentDuration);
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ goog.provide('shaka.test.FakePlayhead');
|
||||
goog.provide('shaka.test.FakePlayheadObserver');
|
||||
goog.provide('shaka.test.FakePresentationTimeline');
|
||||
goog.provide('shaka.test.FakeStreamingEngine');
|
||||
goog.provide('shaka.test.FakeTextDisplayer');
|
||||
goog.provide('shaka.test.FakeTextTrack');
|
||||
goog.provide('shaka.test.FakeVideo');
|
||||
|
||||
|
||||
@@ -321,8 +323,7 @@ shaka.test.FakeVideo = function(opt_currentTime) {
|
||||
};
|
||||
video.setMediaKeys.and.returnValue(Promise.resolve());
|
||||
video.addTextTrack.and.callFake(function(kind, id) {
|
||||
// TODO: mock TextTrack, if/when Player starts directly accessing it.
|
||||
var track = {};
|
||||
var track = new shaka.test.FakeTextTrack();
|
||||
video.textTracks.push(track);
|
||||
return track;
|
||||
});
|
||||
@@ -538,3 +539,82 @@ shaka.test.FakePlayheadObserver.prototype.setRebufferingGoal;
|
||||
|
||||
/** @type {jasmine.Spy} */
|
||||
shaka.test.FakePlayheadObserver.prototype.addTimelineRegion;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a text track.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @extends {TextTrack}
|
||||
* @return {!Object}
|
||||
*/
|
||||
shaka.test.FakeTextTrack = function() {
|
||||
var track = {
|
||||
addCue: jasmine.createSpy('addCue'),
|
||||
removeCue: jasmine.createSpy('removeCue'),
|
||||
cues: []
|
||||
};
|
||||
track.addCue.and.callFake(function(cue) {
|
||||
track.cues.push(cue);
|
||||
});
|
||||
track.removeCue.and.callFake(function(cue) {
|
||||
var idx = track.cues.indexOf(cue);
|
||||
expect(idx).not.toBeLessThan(0);
|
||||
track.cues.splice(idx, 1);
|
||||
});
|
||||
return track;
|
||||
};
|
||||
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
shaka.test.FakeTextTrack.prototype.addCue;
|
||||
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
shaka.test.FakeTextTrack.prototype.removeCue;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a text track.
|
||||
*
|
||||
* @constructor
|
||||
* @struct
|
||||
* @extends {shaka.text.SimpleTextDisplayer}
|
||||
* @return {!Object}
|
||||
*/
|
||||
shaka.test.FakeTextDisplayer = function() {
|
||||
var displayer = {
|
||||
append: jasmine.createSpy('append'),
|
||||
remove: jasmine.createSpy('remove').and.returnValue(true),
|
||||
destroy:
|
||||
jasmine.createSpy('destroy').and.returnValue(Promise.resolve()),
|
||||
isTextVisible: jasmine.createSpy('isTextVisible'),
|
||||
setTextVisibility: jasmine.createSpy('setTextVisibility'),
|
||||
textVisible: false
|
||||
};
|
||||
|
||||
displayer.isTextVisible.and.callFake(function() {
|
||||
return displayer.textVisible;
|
||||
});
|
||||
|
||||
displayer.setTextVisibility.and.callFake(function(on) {
|
||||
displayer.textVisible = on;
|
||||
});
|
||||
|
||||
return displayer;
|
||||
};
|
||||
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
shaka.test.FakeTextDisplayer.prototype.remove;
|
||||
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
shaka.test.FakeTextDisplayer.prototype.append;
|
||||
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
shaka.test.FakeTextDisplayer.prototype.destroy;
|
||||
|
||||
@@ -23,17 +23,6 @@ describe('Cue', function() {
|
||||
// The scenarios under test are not specific to WebVTT, but WebVTT is used to
|
||||
// exercise the platform's native cues and ensure that no errors occur.
|
||||
|
||||
it('skips zero-duration cues', function() {
|
||||
// These cannot be constructed on IE/Edge.
|
||||
// See issue #501
|
||||
var cues = parseVtt(
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:20.000\n' +
|
||||
'Test',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
expect(cues.length).toBe(0);
|
||||
});
|
||||
|
||||
it('handles offsets', function() {
|
||||
// Offsets must be handled early.
|
||||
// See issue #502
|
||||
@@ -63,7 +52,7 @@ describe('Cue', function() {
|
||||
/**
|
||||
* @param {string} text
|
||||
* @param {!shakaExtern.TextParser.TimeContext} time
|
||||
* @return {!Array.<TextTrackCue>}
|
||||
* @return {!Array.<shaka.text.Cue>}
|
||||
*/
|
||||
function parseVtt(text, time) {
|
||||
var data = shaka.util.StringUtils.toUTF8(text);
|
||||
|
||||
@@ -76,9 +76,17 @@ describe('Mp4vttParser', function() {
|
||||
it('parses media segment', function() {
|
||||
var cues =
|
||||
[
|
||||
{start: 111.8, end: 115.8, text: 'It has shed much innocent blood.\n'},
|
||||
{start: 118, end: 120, text:
|
||||
'You\'re a fool for traveling alone,\nso completely unprepared.\n'}
|
||||
{
|
||||
start: 111.8,
|
||||
end: 115.8,
|
||||
payload: 'It has shed much innocent blood.\n'
|
||||
},
|
||||
{
|
||||
start: 118,
|
||||
end: 120,
|
||||
payload:
|
||||
'You\'re a fool for traveling alone,\nso completely unprepared.\n'
|
||||
}
|
||||
];
|
||||
|
||||
var parser = new shaka.text.Mp4VttParser();
|
||||
@@ -89,13 +97,25 @@ describe('Mp4vttParser', function() {
|
||||
});
|
||||
|
||||
it('parses media segment containing settings', function() {
|
||||
var Cue = shaka.text.Cue;
|
||||
var cues =
|
||||
[
|
||||
{start: 111.8, end: 115.8, text: 'It has shed much innocent blood.\n',
|
||||
align: 'right', size: 50, position: 10},
|
||||
{start: 118, end: 120, text:
|
||||
{
|
||||
start: 111.8,
|
||||
end: 115.8,
|
||||
payload: 'It has shed much innocent blood.\n',
|
||||
align: 'right',
|
||||
size: 50,
|
||||
position: 10
|
||||
},
|
||||
{
|
||||
start: 118,
|
||||
end: 120,
|
||||
payload:
|
||||
'You\'re a fool for traveling alone,\nso completely unprepared.\n',
|
||||
vertical: 'lr', line: 1}
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT,
|
||||
line: 1
|
||||
}
|
||||
];
|
||||
|
||||
var parser = new shaka.text.Mp4VttParser();
|
||||
@@ -108,9 +128,17 @@ describe('Mp4vttParser', function() {
|
||||
it('accounts for offset', function() {
|
||||
var cues =
|
||||
[
|
||||
{start: 121.8, end: 125.8, text: 'It has shed much innocent blood.\n'},
|
||||
{start: 128, end: 130, text:
|
||||
'You\'re a fool for traveling alone,\nso completely unprepared.\n'}
|
||||
{
|
||||
start: 121.8,
|
||||
end: 125.8,
|
||||
payload: 'It has shed much innocent blood.\n'
|
||||
},
|
||||
{
|
||||
start: 128,
|
||||
end: 130,
|
||||
payload:
|
||||
'You\'re a fool for traveling alone,\nso completely unprepared.\n'
|
||||
}
|
||||
];
|
||||
|
||||
var parser = new shaka.text.Mp4VttParser();
|
||||
@@ -140,14 +168,14 @@ describe('Mp4vttParser', function() {
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
expect(actual[i].startTime).toBe(expected[i].start);
|
||||
expect(actual[i].endTime).toBe(expected[i].end);
|
||||
expect(actual[i].text).toBe(expected[i].text);
|
||||
expect(actual[i].payload).toBe(expected[i].payload);
|
||||
|
||||
if (expected[i].line)
|
||||
expect(actual[i].line).toBe(expected[i].line);
|
||||
if (expected[i].vertical)
|
||||
expect(actual[i].vertical).toBe(expected[i].vertical);
|
||||
if (expected[i].align)
|
||||
expect(actual[i].align).toBe(expected[i].align);
|
||||
if (expected[i].writingDirection)
|
||||
expect(actual[i].writingDirection).toBe(expected[i].writingDirection);
|
||||
if (expected[i].textAlign)
|
||||
expect(actual[i].textAlign).toBe(expected[i].textAlign);
|
||||
if (expected[i].size)
|
||||
expect(actual[i].size).toBe(expected[i].size);
|
||||
if (expected[i].position)
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* @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.
|
||||
*/
|
||||
|
||||
|
||||
describe('SimpleTextDisplayer', function() {
|
||||
/** @const */
|
||||
var originalVTTCue = window.VTTCue;
|
||||
/** @const */
|
||||
var Cue = shaka.text.Cue;
|
||||
/** @const */
|
||||
var SimpleTextDisplayer = shaka.text.SimpleTextDisplayer;
|
||||
/** @type {!shaka.test.FakeVideo} */
|
||||
var video;
|
||||
/** @type {!shaka.test.FakeTextTrack} */
|
||||
var mockTrack;
|
||||
/** @type {!shaka.text.SimpleTextDisplayer} */
|
||||
var displayer;
|
||||
/** @type {shaka.text.Cue} */
|
||||
var cue1;
|
||||
/** @type {shaka.text.Cue} */
|
||||
var cue2;
|
||||
/** @type {shaka.text.Cue} */
|
||||
var cue3;
|
||||
|
||||
beforeEach(function() {
|
||||
video = new shaka.test.FakeVideo();
|
||||
displayer = new SimpleTextDisplayer(video);
|
||||
|
||||
expect(video.textTracks.length).toBe(1);
|
||||
mockTrack = /** @type {!shaka.test.FakeTextTrack} */ (video.textTracks[0]);
|
||||
expect(mockTrack).toBeTruthy();
|
||||
|
||||
window.VTTCue = function(start, end, text) {
|
||||
this.startTime = start;
|
||||
this.endTime = end;
|
||||
this.text = text;
|
||||
};
|
||||
});
|
||||
|
||||
afterAll(function() {
|
||||
window.VTTCue = originalVTTCue;
|
||||
});
|
||||
|
||||
describe('remove', function() {
|
||||
it('removes cues which overlap the range', function() {
|
||||
cue1 = new shaka.text.Cue(0, 1, 'Test');
|
||||
cue2 = new shaka.text.Cue(1, 2, 'Test');
|
||||
cue3 = new shaka.text.Cue(2, 3, 'Test');
|
||||
displayer.append([cue1, cue2, cue3]);
|
||||
|
||||
displayer.remove(0, 1);
|
||||
expect(mockTrack.removeCue.calls.count()).toBe(1);
|
||||
expect(mockTrack.removeCue).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({startTime: 0, endTime: 1}));
|
||||
mockTrack.removeCue.calls.reset();
|
||||
|
||||
displayer.remove(0.5, 1.001);
|
||||
expect(mockTrack.removeCue.calls.count()).toBe(1);
|
||||
expect(mockTrack.removeCue).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({startTime: 1, endTime: 2}));
|
||||
mockTrack.removeCue.calls.reset();
|
||||
|
||||
displayer.remove(3, 5);
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
mockTrack.removeCue.calls.reset();
|
||||
|
||||
displayer.remove(2.9999, Infinity);
|
||||
expect(mockTrack.removeCue.calls.count()).toBe(1);
|
||||
expect(mockTrack.removeCue).toHaveBeenCalledWith(
|
||||
jasmine.objectContaining({startTime: 2, endTime: 3}));
|
||||
mockTrack.removeCue.calls.reset();
|
||||
});
|
||||
|
||||
it('does nothing when nothing is buffered', function() {
|
||||
displayer.remove(0, 1);
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('convertToTextTrackCue', function() {
|
||||
it('converts shaka.text.Cues to VttCues', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
],
|
||||
[
|
||||
new shaka.text.Cue(20, 40, 'Test')
|
||||
]);
|
||||
|
||||
cue1 = new shaka.text.Cue(20, 40, 'Test');
|
||||
cue1.positionAlign = Cue.positionAlign.LEFT;
|
||||
cue1.lineAlign = Cue.lineAlign.START;
|
||||
cue1.size = 80;
|
||||
cue1.textAlign = Cue.textAlign.LEFT;
|
||||
cue1.writingDirection = Cue.writingDirection.VERTICAL_LEFT;
|
||||
cue1.lineInterpretation = Cue.lineInterpretation.LINE_NUMBER;
|
||||
cue1.line = 5;
|
||||
cue1.position = 10;
|
||||
|
||||
cue2 = new shaka.text.Cue(20, 40, 'Test');
|
||||
cue2.positionAlign = Cue.positionAlign.RIGHT;
|
||||
cue2.lineAlign = Cue.lineAlign.END;
|
||||
cue2.textAlign = Cue.textAlign.RIGHT;
|
||||
cue2.writingDirection = Cue.writingDirection.VERTICAL_RIGHT;
|
||||
cue2.lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
|
||||
cue2.line = 5;
|
||||
|
||||
cue3 = new shaka.text.Cue(20, 40, 'Test');
|
||||
cue3.positionAlign = Cue.positionAlign.CENTER;
|
||||
cue3.lineAlign = Cue.lineAlign.CENTER;
|
||||
cue3.textAlign = Cue.textAlign.START;
|
||||
cue3.writingDirection = Cue.writingDirection.HORIZONTAL;
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
lineAlign: 'start',
|
||||
positionAlign: 'line-left',
|
||||
size: 80,
|
||||
align: 'left',
|
||||
vertical: 'lr',
|
||||
snapToLines: true,
|
||||
line: 5,
|
||||
position: 10
|
||||
},
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
lineAlign: 'end',
|
||||
positionAlign: 'line-right',
|
||||
align: 'right',
|
||||
vertical: 'rl',
|
||||
snapToLines: false,
|
||||
line: 5
|
||||
},
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
lineAlign: 'center',
|
||||
positionAlign: 'center',
|
||||
align: 'start',
|
||||
vertical: undefined
|
||||
}
|
||||
],
|
||||
[cue1, cue2, cue3]);
|
||||
});
|
||||
|
||||
it('uses a workaround for browsers not supporting align=center',
|
||||
function() {
|
||||
window.VTTCue = function(start, end, text) {
|
||||
var align = 'middle';
|
||||
Object.defineProperty(this, 'align', {
|
||||
get: function() { return align; },
|
||||
set: function(newValue) {
|
||||
if (newValue != 'center') align = newValue;
|
||||
}
|
||||
});
|
||||
this.startTime = start;
|
||||
this.endTime = end;
|
||||
this.text = text;
|
||||
};
|
||||
|
||||
cue1 = new shaka.text.Cue(20, 40, 'Test');
|
||||
cue1.textAlign = Cue.textAlign.CENTER;
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
align: 'middle'
|
||||
}
|
||||
],
|
||||
[cue1]);
|
||||
});
|
||||
|
||||
it('ignores cues with startTime >= endTime', function() {
|
||||
cue1 = new shaka.text.Cue(60, 40, 'Test');
|
||||
cue2 = new shaka.text.Cue(40, 40, 'Test');
|
||||
displayer.append([cue1, cue2]);
|
||||
expect(mockTrack.addCue).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function createFakeCue(startTime, endTime) {
|
||||
return { startTime: startTime, endTime: endTime };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!Array} vttCues
|
||||
* @param {!Array.<!shaka.text.Cue>} shakaCues
|
||||
*/
|
||||
function verifyHelper(vttCues, shakaCues) {
|
||||
mockTrack.addCue.calls.reset();
|
||||
displayer.append(shakaCues);
|
||||
var result = mockTrack.addCue.calls.allArgs().reduce(
|
||||
shaka.util.Functional.collapseArrays, []);
|
||||
expect(result).toBeTruthy();
|
||||
expect(result.length).toBe(vttCues.length);
|
||||
for (var i = 0; i < vttCues.length; i++) {
|
||||
expect(result[i].startTime).toBe(vttCues[i].start);
|
||||
expect(result[i].endTime).toBe(vttCues[i].end);
|
||||
expect(result[i].text).toBe(vttCues[i].text);
|
||||
|
||||
if (vttCues[i].id)
|
||||
expect(result[i].id).toBe(vttCues[i].id);
|
||||
if (vttCues[i].vertical)
|
||||
expect(result[i].vertical).toBe(vttCues[i].vertical);
|
||||
if (vttCues[i].line)
|
||||
expect(result[i].line).toBe(vttCues[i].line);
|
||||
if (vttCues[i].align)
|
||||
expect(result[i].align).toBe(vttCues[i].align);
|
||||
if (vttCues[i].size)
|
||||
expect(result[i].size).toBe(vttCues[i].size);
|
||||
if (vttCues[i].position)
|
||||
expect(result[i].position).toBe(vttCues[i].position);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -25,13 +25,18 @@ describe('TextEngine', function() {
|
||||
|
||||
/** @type {!Function} */
|
||||
var mockParserPlugIn;
|
||||
|
||||
/** @type {!shaka.test.FakeTextDisplayer} */
|
||||
var mockDisplayer;
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
var mockParseInit;
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
var mockParseMedia;
|
||||
|
||||
/** @type {!shaka.text.TextEngine} */
|
||||
var textEngine;
|
||||
var mockTrack;
|
||||
|
||||
beforeEach(function() {
|
||||
mockParseInit = jasmine.createSpy('mockParseInit');
|
||||
@@ -42,9 +47,10 @@ describe('TextEngine', function() {
|
||||
parseMedia: mockParseMedia
|
||||
};
|
||||
};
|
||||
mockTrack = createMockTrack();
|
||||
|
||||
mockDisplayer = new shaka.test.FakeTextDisplayer();
|
||||
TextEngine.registerParser(dummyMimeType, mockParserPlugIn);
|
||||
textEngine = new TextEngine(mockTrack);
|
||||
textEngine = new TextEngine(mockDisplayer);
|
||||
textEngine.initParser(dummyMimeType);
|
||||
});
|
||||
|
||||
@@ -67,48 +73,32 @@ describe('TextEngine', function() {
|
||||
it('works asynchronously', function(done) {
|
||||
mockParseMedia.and.returnValue([1, 2, 3]);
|
||||
textEngine.appendBuffer(dummyData, 0, 3).catch(fail).then(done);
|
||||
expect(mockTrack.addCue).not.toHaveBeenCalled();
|
||||
expect(mockDisplayer.append).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('considers empty cues buffered', function(done) {
|
||||
mockParseMedia.and.returnValue([]);
|
||||
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
expect(mockParseMedia).toHaveBeenCalledWith(
|
||||
dummyData, {periodStart: 0, segmentStart: 0, segmentEnd: 3});
|
||||
expect(mockTrack.addCue).not.toHaveBeenCalled();
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
|
||||
expect(textEngine.bufferStart()).toBe(0);
|
||||
expect(textEngine.bufferEnd()).toBe(3);
|
||||
|
||||
mockTrack.addCue.calls.reset();
|
||||
mockParseInit.calls.reset();
|
||||
mockParseMedia.calls.reset();
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
it('adds cues to the track', function(done) {
|
||||
mockParseMedia.and.returnValue([1, 2, 3]);
|
||||
it('calls displayer.append()', function(done) {
|
||||
var cue1 = createFakeCue(1, 2);
|
||||
var cue2 = createFakeCue(2, 3);
|
||||
var cue3 = createFakeCue(3, 4);
|
||||
var cue4 = createFakeCue(4, 5);
|
||||
mockParseMedia.and.returnValue([cue1, cue2]);
|
||||
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
expect(mockParseMedia).toHaveBeenCalledWith(
|
||||
dummyData, {periodStart: 0, segmentStart: 0, segmentEnd: 3 });
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(1);
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(2);
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(3);
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith([cue1, cue2]);
|
||||
|
||||
mockTrack.addCue.calls.reset();
|
||||
expect(mockDisplayer.remove).not.toHaveBeenCalled();
|
||||
|
||||
mockDisplayer.append.calls.reset();
|
||||
mockParseMedia.calls.reset();
|
||||
|
||||
mockParseMedia.and.returnValue([4, 5]);
|
||||
mockParseMedia.and.returnValue([cue3, cue4]);
|
||||
return textEngine.appendBuffer(dummyData, 3, 5);
|
||||
}).then(function() {
|
||||
expect(mockParseMedia).toHaveBeenCalledWith(
|
||||
dummyData, {periodStart: 0, segmentStart: 3, segmentEnd: 5 });
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(4);
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(5);
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith([cue3, cue4]);
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
@@ -134,37 +124,15 @@ describe('TextEngine', function() {
|
||||
it('works asynchronously', function(done) {
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
var p = textEngine.remove(0, 1);
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
expect(mockDisplayer.remove).not.toHaveBeenCalled();
|
||||
return p;
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
it('removes cues which overlap the range', function(done) {
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
return textEngine.remove(0, 1);
|
||||
}).then(function() {
|
||||
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue1]]);
|
||||
|
||||
mockTrack.removeCue.calls.reset();
|
||||
return textEngine.remove(0.5, 1.001);
|
||||
}).then(function() {
|
||||
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue2]]);
|
||||
|
||||
mockTrack.removeCue.calls.reset();
|
||||
return textEngine.remove(3, 5);
|
||||
}).then(function() {
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
|
||||
mockTrack.removeCue.calls.reset();
|
||||
return textEngine.remove(2.9999, Infinity);
|
||||
}).then(function() {
|
||||
expect(mockTrack.removeCue.calls.allArgs()).toEqual([[cue3]]);
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
it('does nothing when nothing is buffered', function(done) {
|
||||
it('calls displayer.remove()', function(done) {
|
||||
textEngine.remove(0, 1).then(function() {
|
||||
expect(mockTrack.removeCue).not.toHaveBeenCalled();
|
||||
expect(mockDisplayer.remove).toHaveBeenCalledWith(0, 1);
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
@@ -189,18 +157,25 @@ describe('TextEngine', function() {
|
||||
expect(mockParseMedia).toHaveBeenCalledWith(
|
||||
dummyData,
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 3});
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(2, 3));
|
||||
|
||||
mockTrack.addCue.calls.reset();
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith(
|
||||
[
|
||||
createFakeCue(0, 1),
|
||||
createFakeCue(2, 3)
|
||||
]);
|
||||
|
||||
mockDisplayer.append.calls.reset();
|
||||
textEngine.setTimestampOffset(4);
|
||||
return textEngine.appendBuffer(dummyData, 0, 3);
|
||||
}).then(function() {
|
||||
expect(mockParseMedia).toHaveBeenCalledWith(
|
||||
dummyData,
|
||||
{periodStart: 4, segmentStart: 0, segmentEnd: 3});
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(4, 5));
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(6, 7));
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith(
|
||||
[
|
||||
createFakeCue(4, 5),
|
||||
createFakeCue(6, 7)
|
||||
]);
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
});
|
||||
@@ -308,16 +283,22 @@ describe('TextEngine', function() {
|
||||
it('limits appended cues', function(done) {
|
||||
textEngine.setAppendWindowEnd(1.9);
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(1, 2));
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith(
|
||||
[
|
||||
createFakeCue(0, 1),
|
||||
createFakeCue(1, 2)
|
||||
]);
|
||||
|
||||
mockTrack.addCue.calls.reset();
|
||||
mockDisplayer.append.calls.reset();
|
||||
textEngine.setAppendWindowEnd(2.1);
|
||||
return textEngine.appendBuffer(dummyData, 0, 3);
|
||||
}).then(function() {
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(0, 1));
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(1, 2));
|
||||
expect(mockTrack.addCue).toHaveBeenCalledWith(createFakeCue(2, 3));
|
||||
expect(mockDisplayer.append).toHaveBeenCalledWith(
|
||||
[
|
||||
createFakeCue(0, 1),
|
||||
createFakeCue(1, 2),
|
||||
createFakeCue(2, 3)
|
||||
]);
|
||||
}).catch(fail).then(done);
|
||||
});
|
||||
|
||||
@@ -360,7 +341,7 @@ describe('TextEngine', function() {
|
||||
describe('stateless parser', function() {
|
||||
describe('converted to stateful parser', function() {
|
||||
it('parses init segment', function(done) {
|
||||
var textEngine = new TextEngine(createMockTrack());
|
||||
var textEngine = new TextEngine(mockDisplayer);
|
||||
textEngine.initParser(dummyMimeType);
|
||||
textEngine.appendBuffer(dummyData, null, null).then(function() {
|
||||
expect(mockParser).toHaveBeenCalledWith(dummyData, 0, null, null);
|
||||
@@ -368,7 +349,7 @@ describe('TextEngine', function() {
|
||||
});
|
||||
|
||||
it('parses media segment', function(done) {
|
||||
var textEngine = new TextEngine(createMockTrack());
|
||||
var textEngine = new TextEngine(mockDisplayer);
|
||||
textEngine.initParser(dummyMimeType);
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
expect(mockParser).toHaveBeenCalledWith(dummyData, 0, 0, 3);
|
||||
@@ -376,7 +357,7 @@ describe('TextEngine', function() {
|
||||
});
|
||||
|
||||
it('parses media segment with time offset', function(done) {
|
||||
var textEngine = new TextEngine(createMockTrack());
|
||||
var textEngine = new TextEngine(mockDisplayer);
|
||||
textEngine.initParser(dummyMimeType);
|
||||
textEngine.setTimestampOffset(3);
|
||||
textEngine.appendBuffer(dummyData, 0, 3).then(function() {
|
||||
@@ -387,23 +368,6 @@ describe('TextEngine', function() {
|
||||
});
|
||||
});
|
||||
|
||||
function createMockTrack() {
|
||||
var track = {
|
||||
addCue: jasmine.createSpy('addCue'),
|
||||
removeCue: jasmine.createSpy('removeCue'),
|
||||
cues: []
|
||||
};
|
||||
track.addCue.and.callFake(function(cue) {
|
||||
track.cues.push(cue);
|
||||
});
|
||||
track.removeCue.and.callFake(function(cue) {
|
||||
var idx = track.cues.indexOf(cue);
|
||||
expect(idx).not.toBeLessThan(0);
|
||||
track.cues.splice(idx, 1);
|
||||
});
|
||||
return track;
|
||||
}
|
||||
|
||||
function createFakeCue(startTime, endTime) {
|
||||
return { startTime: startTime, endTime: endTime };
|
||||
}
|
||||
|
||||
@@ -17,19 +17,7 @@
|
||||
|
||||
describe('TtmlTextParser', function() {
|
||||
/** @const */
|
||||
var originalVTTCue = window.VTTCue;
|
||||
|
||||
afterAll(function() {
|
||||
window.VTTCue = originalVTTCue;
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
window.VTTCue = function(start, end, text) {
|
||||
this.startTime = start;
|
||||
this.endTime = end;
|
||||
this.text = text;
|
||||
};
|
||||
});
|
||||
var Cue = shaka.text.Cue;
|
||||
|
||||
it('supports no cues', function() {
|
||||
verifyHelper([],
|
||||
@@ -55,21 +43,21 @@ describe('TtmlTextParser', function() {
|
||||
// When xml:space="default", ignore whitespace outside tags.
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.03, end: 62.05, text: 'A B C'}
|
||||
{start: 62.03, end: 62.05, payload: 'A B C'}
|
||||
],
|
||||
'<tt xml:space="default">' + ttBody + '</tt>',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
// When xml:space="preserve", take them into account.
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.03, end: 62.05, text: '\n A B C \n '}
|
||||
{start: 62.03, end: 62.05, payload: '\n A B C \n '}
|
||||
],
|
||||
'<tt xml:space="preserve">' + ttBody + '</tt>',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
// The default value for xml:space is "default".
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.03, end: 62.05, text: 'A B C'}
|
||||
{start: 62.03, end: 62.05, payload: 'A B C'}
|
||||
],
|
||||
'<tt>' + ttBody + '</tt>',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
@@ -93,7 +81,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports colon formatted time', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test'}
|
||||
{start: 62.05, end: 3723.2, payload: 'Test'}
|
||||
],
|
||||
'<tt><body><p begin="01:02.05" ' +
|
||||
'end="01:02:03.200">Test</p></body></tt>',
|
||||
@@ -103,7 +91,7 @@ describe('TtmlTextParser', function() {
|
||||
it('accounts for offset', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 69.05, end: 3730.2, text: 'Test'}
|
||||
{start: 69.05, end: 3730.2, payload: 'Test'}
|
||||
],
|
||||
'<tt><body><p begin="01:02.05" ' +
|
||||
'end="01:02:03.200">Test</p></body></tt>',
|
||||
@@ -113,7 +101,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time in 0.00h 0.00m 0.00s format', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 3567.03, end: 5402.3, text: 'Test'}
|
||||
{start: 3567.03, end: 5402.3, payload: 'Test'}
|
||||
],
|
||||
'<tt><body><p begin="59.45m30ms" ' +
|
||||
'end="1.5h2.3s">Test</p></body></tt>',
|
||||
@@ -123,7 +111,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time with frame rate', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 615.5, end: 663, text: 'Test'}
|
||||
{start: 615.5, end: 663, payload: 'Test'}
|
||||
],
|
||||
'<tt xmlns:ttp="ttml#parameter" ' +
|
||||
'ttp:frameRate="30"> ' +
|
||||
@@ -137,7 +125,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time with frame rate multiplier', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 615.5, end: 663, text: 'Test'}
|
||||
{start: 615.5, end: 663, payload: 'Test'}
|
||||
],
|
||||
'<tt xmlns:ttp="ttml#parameter" ' +
|
||||
'ttp:frameRate="60" ' +
|
||||
@@ -152,7 +140,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time with subframes', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 615.517, end: 663, text: 'Test'}
|
||||
{start: 615.517, end: 663, payload: 'Test'}
|
||||
],
|
||||
'<tt xmlns:ttp="ttml#parameter" ' +
|
||||
'ttp:frameRate="30" ' +
|
||||
@@ -167,7 +155,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time in frame format', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 2.5, end: 10.01, text: 'Test'}
|
||||
{start: 2.5, end: 10.01, payload: 'Test'}
|
||||
],
|
||||
'<tt xmlns:ttp="ttml#parameter" ' +
|
||||
'ttp:frameRate="60" ' +
|
||||
@@ -182,7 +170,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time in tick format', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 5, end: 6.02, text: 'Test'}
|
||||
{start: 5, end: 6.02, payload: 'Test'}
|
||||
],
|
||||
'<tt xmlns:ttp="ttml#parameter" ' +
|
||||
'ttp:frameRate="60" ' +
|
||||
@@ -197,7 +185,7 @@ describe('TtmlTextParser', function() {
|
||||
it('supports time with duration', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 67.05, text: 'Test'}
|
||||
{start: 62.05, end: 67.05, payload: 'Test'}
|
||||
],
|
||||
'<tt><body><p begin="01:02.05" ' +
|
||||
'dur="5s">Test</p></body></tt>',
|
||||
@@ -207,7 +195,12 @@ describe('TtmlTextParser', function() {
|
||||
it('parses alignment from textAlign attribute of a region', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', lineAlign: 'start'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
lineAlign: Cue.textAlign.START
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -223,7 +216,12 @@ describe('TtmlTextParser', function() {
|
||||
it('parses alignment from <style> block with id on region', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', lineAlign: 'end'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
lineAlign: Cue.textAlign.END
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<styling>' +
|
||||
@@ -242,7 +240,12 @@ describe('TtmlTextParser', function() {
|
||||
it('parses alignment from <style> block with id on p', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', lineAlign: 'end'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
lineAlign: Cue.textAlign.END
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<styling>' +
|
||||
@@ -261,7 +264,8 @@ describe('TtmlTextParser', function() {
|
||||
it('supports size setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', size: 50}
|
||||
{
|
||||
start: 62.05, end: 3723.2, payload: 'Test', size: 50}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -278,7 +282,13 @@ describe('TtmlTextParser', function() {
|
||||
function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 50, line: 16}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 50,
|
||||
line: 16
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -291,7 +301,13 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 50, line: 16}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 50,
|
||||
line: 16
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -305,7 +321,13 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 50, line: 16}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 50,
|
||||
line: 16
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -323,7 +345,13 @@ describe('TtmlTextParser', function() {
|
||||
function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 16, line: 50}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 16,
|
||||
line: 50
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -337,7 +365,13 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 16, line: 50}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 16,
|
||||
line: 50
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -351,7 +385,13 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', position: 16, line: 50}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
position: 16,
|
||||
line: 50
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -365,10 +405,15 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
});
|
||||
|
||||
it('supports vertical setting', function() {
|
||||
it('supports writingDirection setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', vertical: 'lr'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -382,7 +427,12 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', vertical: 'rl'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
writingDirection: Cue.writingDirection.VERTICAL_RIGHT
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -396,7 +446,12 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', vertical: 'lr'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<layout>' +
|
||||
@@ -413,7 +468,7 @@ describe('TtmlTextParser', function() {
|
||||
it('disregards empty divs and ps', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test'}
|
||||
{start: 62.05, end: 3723.2, payload: 'Test'}
|
||||
],
|
||||
'<tt>' +
|
||||
'<body>' +
|
||||
@@ -426,7 +481,7 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test'}
|
||||
{start: 62.05, end: 3723.2, payload: 'Test'}
|
||||
],
|
||||
'<tt>' +
|
||||
'<body>' +
|
||||
@@ -453,14 +508,14 @@ describe('TtmlTextParser', function() {
|
||||
it('inserts newline characters into <br> tags', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Line1\nLine2'}
|
||||
{start: 62.05, end: 3723.2, payload: 'Line1\nLine2'}
|
||||
],
|
||||
'<tt><body><p begin="01:02.05" ' +
|
||||
'end="01:02:03.200">Line1<br/>Line2</p></body></tt>',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Line1\nLine2'}
|
||||
{start: 62.05, end: 3723.2, payload: 'Line1\nLine2'}
|
||||
],
|
||||
'<tt><body><p begin="01:02.05" ' +
|
||||
'end="01:02:03.200"><span>Line1<br/>Line2</span></p></body></tt>',
|
||||
@@ -470,8 +525,14 @@ describe('TtmlTextParser', function() {
|
||||
it('parses cue alignment from textAlign attribute', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', lineAlign: 'start',
|
||||
align: 'left', positionAlign: 'line-left'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
lineAlign: Cue.lineAlign.START,
|
||||
textAlign: Cue.textAlign.LEFT,
|
||||
positionAlign: Cue.positionAlign.LEFT
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<styling>' +
|
||||
@@ -487,29 +548,49 @@ describe('TtmlTextParser', function() {
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
});
|
||||
|
||||
|
||||
it('uses a workaround for browsers not supporting align=center', function() {
|
||||
|
||||
window.VTTCue = function(start, end, text) {
|
||||
var align = 'middle';
|
||||
Object.defineProperty(this, 'align', {
|
||||
get: function() { return align; },
|
||||
set: function(newValue) { if (newValue != 'center') align = newValue; }
|
||||
});
|
||||
this.startTime = start;
|
||||
this.endTime = end;
|
||||
this.text = text;
|
||||
};
|
||||
|
||||
|
||||
it('parses text style information', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 62.05, end: 3723.2, text: 'Test', lineAlign: 'center',
|
||||
align: 'middle', position: 'auto', positionAlign: 'center'}
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
color: 'red',
|
||||
backgroundColor: 'blue',
|
||||
fontWeight: Cue.fontWeight.BOLD,
|
||||
fontFamily: 'Times New Roman'
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<styling>' +
|
||||
'<style xml:id="s1" tts:textAlign="center"/>' +
|
||||
'<style xml:id="s1" tts:color="red" ' +
|
||||
'tts:backgroundColor="blue" ' +
|
||||
'tts:fontWeight="bold" ' +
|
||||
'tts:fontFamily="Times New Roman"/>' +
|
||||
'</styling>' +
|
||||
'<layout xmlns:tts="ttml#styling">' +
|
||||
'<region xml:id="subtitleArea" />' +
|
||||
'</layout>' +
|
||||
'<body region="subtitleArea">' +
|
||||
'<p begin="01:02.05" end="01:02:03.200" style="s1">Test</p>' +
|
||||
'</body>' +
|
||||
'</tt>',
|
||||
{periodStart: 0, segmentStart: 0, segmentEnd: 0 });
|
||||
});
|
||||
|
||||
it('parses wrapping option', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{
|
||||
start: 62.05,
|
||||
end: 3723.2,
|
||||
payload: 'Test',
|
||||
wrapLine: false
|
||||
}
|
||||
],
|
||||
'<tt xmlns:tts="ttml#styling">' +
|
||||
'<styling>' +
|
||||
'<style xml:id="s1" tts:wrapOption="noWrap"/>' +
|
||||
'</styling>' +
|
||||
'<layout xmlns:tts="ttml#styling">' +
|
||||
'<region xml:id="subtitleArea" />' +
|
||||
@@ -535,24 +616,42 @@ describe('TtmlTextParser', function() {
|
||||
for (var i = 0; i < cues.length; i++) {
|
||||
expect(result[i].startTime).toBeCloseTo(cues[i].start, 3);
|
||||
expect(result[i].endTime).toBeCloseTo(cues[i].end, 3);
|
||||
expect(result[i].text).toBe(cues[i].text);
|
||||
expect(result[i].payload).toBe(cues[i].payload);
|
||||
|
||||
// Workaround a bug in the compiler's externs.
|
||||
// TODO: Remove when compiler is updated.
|
||||
if (cues[i].align)
|
||||
expect(/** @type {?} */ (result[i]).align).toBe(cues[i].align);
|
||||
if (cues[i].textAlign)
|
||||
expect(/** @type {?} */ (result[i]).textAlign).toBe(cues[i].textAlign);
|
||||
if (cues[i].lineAlign)
|
||||
expect(result[i].lineAlign).toBe(cues[i].lineAlign);
|
||||
expect(/** @type {?} */ (result[i]).lineAlign).toBe(cues[i].lineAlign);
|
||||
if (cues[i].positionAlign)
|
||||
expect(result[i].positionAlign).toBe(cues[i].positionAlign);
|
||||
expect(/** @type {?} */ (result[i]).positionAlign)
|
||||
.toBe(cues[i].positionAlign);
|
||||
if (cues[i].size)
|
||||
expect(/** @type {?} */ (result[i]).size).toBe(cues[i].size);
|
||||
if (cues[i].line)
|
||||
expect(result[i].line).toBe(cues[i].line);
|
||||
expect(/** @type {?} */ (result[i]).line).toBe(cues[i].line);
|
||||
if (cues[i].position)
|
||||
expect(/** @type {?} */ (result[i]).position).toBe(cues[i].position);
|
||||
if (cues[i].vertical)
|
||||
expect(result[i].vertical).toBe(cues[i].vertical);
|
||||
expect(/** @type {?} */ (result[i]).writingDirection)
|
||||
.toBe(cues[i].writingDirection);
|
||||
if (cues[i].vertical)
|
||||
expect(/** @type {?} */ (result[i]).lineInterpretation)
|
||||
.toBe(cues[i].lineInterpretation);
|
||||
if (cues[i].color)
|
||||
expect(/** @type {?} */ (result[i]).color).toBe(cues[i].color);
|
||||
if (cues[i].backgroundColor)
|
||||
expect(/** @type {?} */ (result[i]).backgroundColor)
|
||||
.toBe(cues[i].backgroundColor);
|
||||
if (cues[i].fontWeight)
|
||||
expect(/** @type {?} */ (result[i]).fontWeight)
|
||||
.toBe(cues[i].fontWeight);
|
||||
if (cues[i].fontFamily)
|
||||
expect(/** @type {?} */ (result[i]).fontFamily)
|
||||
.toBe(cues[i].fontFamily);
|
||||
if (cues[i].wrapLine)
|
||||
expect(/** @type {?} */ (result[i]).wrapLine).toBe(cues[i].wrapLine);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,28 +16,19 @@
|
||||
*/
|
||||
|
||||
describe('VttTextParser', function() {
|
||||
/** @const */
|
||||
var originalVTTCue = window.VTTCue;
|
||||
|
||||
/** @type {!jasmine.Spy} */
|
||||
var logWarningSpy;
|
||||
|
||||
/** @const */
|
||||
var Cue = shaka.text.Cue;
|
||||
|
||||
beforeAll(function() {
|
||||
logWarningSpy = jasmine.createSpy('shaka.log.warning');
|
||||
shaka.log.warning = shaka.test.Util.spyFunc(logWarningSpy);
|
||||
});
|
||||
|
||||
afterAll(function() {
|
||||
window.VTTCue = originalVTTCue;
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
logWarningSpy.calls.reset();
|
||||
window.VTTCue = function(start, end, text) {
|
||||
this.startTime = start;
|
||||
this.endTime = end;
|
||||
this.text = text;
|
||||
};
|
||||
});
|
||||
|
||||
it('supports no cues', function() {
|
||||
@@ -71,7 +62,7 @@ describe('VttTextParser', function() {
|
||||
it('handles a blank line at the end of the file', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -82,7 +73,7 @@ describe('VttTextParser', function() {
|
||||
it('handles no blank line at the end of the file', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -94,7 +85,7 @@ describe('VttTextParser', function() {
|
||||
it('handles no newline after the final text payload', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -105,7 +96,7 @@ describe('VttTextParser', function() {
|
||||
it('ignores offset', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -116,8 +107,8 @@ describe('VttTextParser', function() {
|
||||
it('supports cues with no settings', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', id: '1'},
|
||||
{start: 40, end: 50, text: 'Test2', id: '2'}
|
||||
{start: 20, end: 40, payload: 'Test', id: '1'},
|
||||
{start: 40, end: 50, payload: 'Test2', id: '2'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'1\n' +
|
||||
@@ -132,8 +123,8 @@ describe('VttTextParser', function() {
|
||||
it('supports cues with no ID', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'},
|
||||
{start: 40, end: 50, text: 'Test2'}
|
||||
{start: 20, end: 40, payload: 'Test'},
|
||||
{start: 40, end: 50, payload: 'Test2'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -146,8 +137,8 @@ describe('VttTextParser', function() {
|
||||
it('supports comments within cues', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'},
|
||||
{start: 40, end: 50, text: 'Test2'}
|
||||
{start: 20, end: 40, payload: 'Test'},
|
||||
{start: 40, end: 50, payload: 'Test2'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000\n' +
|
||||
@@ -162,7 +153,7 @@ describe('VttTextParser', function() {
|
||||
it('supports non-integer timecodes', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20.1, end: 40.505, text: 'Test'}
|
||||
{start: 20.1, end: 40.505, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.100 --> 00:00:40.505\n' +
|
||||
@@ -173,7 +164,7 @@ describe('VttTextParser', function() {
|
||||
it('supports large timecodes', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 108000, text: 'Test'}
|
||||
{start: 20, end: 108000, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 30:00:00.000\n' +
|
||||
@@ -220,8 +211,18 @@ describe('VttTextParser', function() {
|
||||
it('supports vertical setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', vertical: 'rl'},
|
||||
{start: 40, end: 50, text: 'Test2', vertical: 'lr'}
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
payload: 'Test',
|
||||
writingDirection: Cue.writingDirection.VERTICAL_RIGHT
|
||||
},
|
||||
{
|
||||
start: 40,
|
||||
end: 50,
|
||||
payload: 'Test2',
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 vertical:rl\n' +
|
||||
@@ -234,8 +235,8 @@ describe('VttTextParser', function() {
|
||||
it('supports line setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', line: 0},
|
||||
{start: 40, end: 50, text: 'Test2', line: -1}
|
||||
{start: 20, end: 40, payload: 'Test', line: 0},
|
||||
{start: 40, end: 50, payload: 'Test2', line: -1}
|
||||
] ,
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 line:0\n' +
|
||||
@@ -248,8 +249,8 @@ describe('VttTextParser', function() {
|
||||
it('supports line setting with optional part', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', line: 10},
|
||||
{start: 40, end: 50, text: 'Test2', line: -1}
|
||||
{start: 20, end: 40, payload: 'Test', line: 10},
|
||||
{start: 40, end: 50, payload: 'Test2', line: -1}
|
||||
] ,
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 line:10%,start\n' +
|
||||
@@ -262,7 +263,7 @@ describe('VttTextParser', function() {
|
||||
it('supports position setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test2', position: 45}
|
||||
{start: 20, end: 40, payload: 'Test2', position: 45}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 position:45%\n' +
|
||||
@@ -273,8 +274,8 @@ describe('VttTextParser', function() {
|
||||
it('supports position setting with optional part', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', position: 45},
|
||||
{start: 20, end: 40, text: 'Test2', position: 45}
|
||||
{start: 20, end: 40, payload: 'Test', position: 45},
|
||||
{start: 20, end: 40, payload: 'Test2', position: 45}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 position:45%,line-left\n' +
|
||||
@@ -287,7 +288,7 @@ describe('VttTextParser', function() {
|
||||
it('supports size setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', size: 56}
|
||||
{start: 20, end: 40, payload: 'Test', size: 56}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 size:56%\n' +
|
||||
@@ -298,7 +299,7 @@ describe('VttTextParser', function() {
|
||||
it('supports align setting', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test', align: 'center'}
|
||||
{start: 20, end: 40, payload: 'Test', align: 'center'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 align:center\n' +
|
||||
@@ -312,10 +313,10 @@ describe('VttTextParser', function() {
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
align: 'center',
|
||||
payload: 'Test',
|
||||
textAlign: Cue.textAlign.CENTER,
|
||||
size: 56,
|
||||
vertical: 'lr'
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
@@ -330,10 +331,10 @@ describe('VttTextParser', function() {
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
align: 'center',
|
||||
payload: 'Test',
|
||||
textAlign: Cue.textAlign.CENTER,
|
||||
size: 56,
|
||||
vertical: 'lr'
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
@@ -348,10 +349,10 @@ describe('VttTextParser', function() {
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
align: 'center',
|
||||
payload: 'Test',
|
||||
textAlign: Cue.textAlign.CENTER,
|
||||
size: 56,
|
||||
vertical: 'lr'
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
@@ -366,10 +367,10 @@ describe('VttTextParser', function() {
|
||||
{
|
||||
start: 20,
|
||||
end: 40,
|
||||
text: 'Test',
|
||||
payload: 'Test',
|
||||
align: 'center',
|
||||
size: 56,
|
||||
vertical: 'lr'
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
@@ -384,10 +385,10 @@ describe('VttTextParser', function() {
|
||||
{
|
||||
start: 40, // Note these are 20s off of the cue
|
||||
end: 60, // because using relative timestamps
|
||||
text: 'Test',
|
||||
payload: 'Test',
|
||||
align: 'center',
|
||||
size: 56,
|
||||
vertical: 'lr'
|
||||
writingDirection: Cue.writingDirection.VERTICAL_LEFT
|
||||
}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
@@ -401,7 +402,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 vertical:es\n' +
|
||||
@@ -410,7 +411,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 vertical:\n' +
|
||||
@@ -419,7 +420,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 vertical\n' +
|
||||
@@ -428,7 +429,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 line:-3%\n' +
|
||||
@@ -437,7 +438,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 line:45%%\n' +
|
||||
@@ -446,7 +447,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 align:10\n' +
|
||||
@@ -455,7 +456,7 @@ describe('VttTextParser', function() {
|
||||
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 20, end: 40, text: 'Test'}
|
||||
{start: 20, end: 40, payload: 'Test'}
|
||||
],
|
||||
'WEBVTT\n\n' +
|
||||
'00:00:20.000 --> 00:00:40.000 align:foo\n' +
|
||||
@@ -468,8 +469,8 @@ describe('VttTextParser', function() {
|
||||
it('respects X-TIMESTAMP-MAP header', function() {
|
||||
verifyHelper(
|
||||
[
|
||||
{start: 30, end: 50, text: 'Test'},
|
||||
{start: 50, end: 60, text: 'Test2'}
|
||||
{start: 30, end: 50, payload: 'Test'},
|
||||
{start: 50, end: 60, payload: 'Test2'}
|
||||
] ,
|
||||
// 900000 = 10 sec, so expect every timestamp to be 10
|
||||
// seconds ahead of what is specified.
|
||||
@@ -497,18 +498,18 @@ describe('VttTextParser', function() {
|
||||
for (var i = 0; i < cues.length; i++) {
|
||||
expect(result[i].startTime).toBe(cues[i].start);
|
||||
expect(result[i].endTime).toBe(cues[i].end);
|
||||
expect(result[i].text).toBe(cues[i].text);
|
||||
expect(result[i].payload).toBe(cues[i].payload);
|
||||
|
||||
// Workaround a bug in the compiler's externs.
|
||||
// TODO: Remove when compiler is updated.
|
||||
if (cues[i].id)
|
||||
expect(result[i].id).toBe(cues[i].id);
|
||||
if (cues[i].vertical)
|
||||
expect(result[i].vertical).toBe(cues[i].vertical);
|
||||
expect(result[i].writingDirection).toBe(cues[i].writingDirection);
|
||||
if (cues[i].line)
|
||||
expect(result[i].line).toBe(cues[i].line);
|
||||
if (cues[i].align)
|
||||
expect(/** @type {?} */ (result[i]).align).toBe(cues[i].align);
|
||||
if (cues[i].textAlign)
|
||||
expect(/** @type {?} */ (result[i]).textAlign).toBe(cues[i].textAlign);
|
||||
if (cues[i].size)
|
||||
expect(/** @type {?} */ (result[i]).size).toBe(cues[i].size);
|
||||
if (cues[i].position)
|
||||
|
||||
Reference in New Issue
Block a user