shaka-player 3.2.2 → 3.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/locales.js +30 -30
- package/dist/shaka-player.compiled.d.ts +21 -2
- package/dist/shaka-player.compiled.debug.d.ts +21 -2
- package/dist/shaka-player.compiled.debug.externs.js +22 -1
- package/dist/shaka-player.compiled.debug.js +205 -202
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +22 -1
- package/dist/shaka-player.compiled.js +727 -723
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +21 -2
- package/dist/shaka-player.ui.debug.d.ts +21 -2
- package/dist/shaka-player.ui.debug.externs.js +22 -1
- package/dist/shaka-player.ui.debug.js +221 -218
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +22 -1
- package/dist/shaka-player.ui.js +829 -825
- package/dist/shaka-player.ui.map +4 -4
- package/externs/shaka/player.js +8 -0
- package/externs/shaka/text.js +10 -0
- package/lib/dash/dash_parser.js +4 -2
- package/lib/hls/hls_parser.js +30 -0
- package/lib/media/segment_index.js +2 -1
- package/lib/media/segment_reference.js +19 -1
- package/lib/media/streaming_engine.js +5 -2
- package/lib/player.js +20 -13
- package/lib/polyfill/media_capabilities.js +30 -15
- package/lib/text/cue.js +6 -0
- package/lib/text/ttml_text_parser.js +84 -58
- package/lib/text/ui_text_displayer.js +17 -11
- package/lib/util/stream_utils.js +86 -10
- package/lib/util/xml_utils.js +19 -0
- package/package.json +2 -2
- package/test/dash/dash_parser_manifest_unit.js +33 -1
- package/test/media/drm_engine_unit.js +3 -3
- package/test/media/streaming_engine_integration.js +2 -1
- package/test/media/streaming_engine_unit.js +145 -0
- package/test/player_integration.js +41 -8
- package/test/player_src_equals_integration.js +41 -8
- package/test/player_unit.js +19 -9
- package/test/test/assets/chapters.vtt +2 -2
- package/test/test/assets/chapters2.vtt +10 -0
- package/test/test/util/ttml_utils.js +81 -0
- package/test/text/mp4_ttml_parser_unit.js +11 -18
- package/test/text/ttml_text_parser_unit.js +495 -228
- package/test/util/stream_utils_unit.js +36 -30
- package/ui/less/buttons.less +0 -4
- package/ui/text_selection.js +1 -0
- package/externs/media_capabilities.js +0 -16
package/externs/shaka/player.js
CHANGED
|
@@ -1082,6 +1082,8 @@ shaka.extern.LanguageRole;
|
|
|
1082
1082
|
|
|
1083
1083
|
/**
|
|
1084
1084
|
* @typedef {{
|
|
1085
|
+
* imageHeight: number,
|
|
1086
|
+
* imageWidth: number,
|
|
1085
1087
|
* height: number,
|
|
1086
1088
|
* positionX: number,
|
|
1087
1089
|
* positionY: number,
|
|
@@ -1091,6 +1093,12 @@ shaka.extern.LanguageRole;
|
|
|
1091
1093
|
* width: number
|
|
1092
1094
|
* }}
|
|
1093
1095
|
*
|
|
1096
|
+
* @property {number} imageHeight
|
|
1097
|
+
* The image height in px. The image height could be different to height if
|
|
1098
|
+
* the layout is different to 1x1.
|
|
1099
|
+
* @property {number} imageWidth
|
|
1100
|
+
* The image width in px. The image width could be different to width if
|
|
1101
|
+
* the layout is different to 1x1.
|
|
1094
1102
|
* @property {number} height
|
|
1095
1103
|
* The thumbnail height in px.
|
|
1096
1104
|
* @property {number} positionX
|
package/externs/shaka/text.js
CHANGED
|
@@ -349,6 +349,16 @@ shaka.extern.Cue = class {
|
|
|
349
349
|
*/
|
|
350
350
|
this.nestedCues;
|
|
351
351
|
|
|
352
|
+
/**
|
|
353
|
+
* If true, this represents a container element that is "above" the main
|
|
354
|
+
* cues. For example, the <body> and <div> tags that contain the <p> tags
|
|
355
|
+
* in a TTML file. This controls the flow of the final cues; any nested cues
|
|
356
|
+
* within an "isContainer" cue will be laid out as separate lines.
|
|
357
|
+
* @type {boolean}
|
|
358
|
+
* @exportDoc
|
|
359
|
+
*/
|
|
360
|
+
this.isContainer;
|
|
361
|
+
|
|
352
362
|
/**
|
|
353
363
|
* Whether or not the cue only acts as a line break between two nested cues.
|
|
354
364
|
* Should only appear in nested cues.
|
package/lib/dash/dash_parser.js
CHANGED
|
@@ -1220,8 +1220,10 @@ shaka.dash.DashParser = class {
|
|
|
1220
1220
|
|
|
1221
1221
|
let forced = false;
|
|
1222
1222
|
if (isText) {
|
|
1223
|
-
// See: https://github.com/google/shaka-player/issues/2122
|
|
1224
|
-
|
|
1223
|
+
// See: https://github.com/google/shaka-player/issues/2122 and
|
|
1224
|
+
// https://github.com/Dash-Industry-Forum/DASH-IF-IOP/issues/165
|
|
1225
|
+
forced = roles.includes('forced_subtitle') ||
|
|
1226
|
+
roles.includes('forced-subtitle');
|
|
1225
1227
|
}
|
|
1226
1228
|
|
|
1227
1229
|
let tilesLayout;
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -1280,6 +1280,30 @@ shaka.hls.HlsParser = class {
|
|
|
1280
1280
|
if (this.uriToStreamInfosMap_.has(verbatimImagePlaylistUri)) {
|
|
1281
1281
|
return this.uriToStreamInfosMap_.get(verbatimImagePlaylistUri);
|
|
1282
1282
|
}
|
|
1283
|
+
|
|
1284
|
+
// Parse misc attributes.
|
|
1285
|
+
const resolution = tag.getAttributeValue('RESOLUTION');
|
|
1286
|
+
if (resolution) {
|
|
1287
|
+
// The RESOLUTION tag represents the resolution of a single thumbnail, not
|
|
1288
|
+
// of the entire sheet at once (like we expect in the output).
|
|
1289
|
+
// So multiply by the layout size.
|
|
1290
|
+
|
|
1291
|
+
const reference = streamInfo.stream.segmentIndex.get(0);
|
|
1292
|
+
const layout = reference.getTilesLayout();
|
|
1293
|
+
if (layout) {
|
|
1294
|
+
streamInfo.stream.width =
|
|
1295
|
+
Number(resolution.split('x')[0]) * Number(layout.split('x')[0]);
|
|
1296
|
+
streamInfo.stream.height =
|
|
1297
|
+
Number(resolution.split('x')[1]) * Number(layout.split('x')[1]);
|
|
1298
|
+
// TODO: What happens if there are multiple grids, with different
|
|
1299
|
+
// layout sizes, inside this image stream?
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
const bandwidth = tag.getAttributeValue('BANDWIDTH');
|
|
1303
|
+
if (bandwidth) {
|
|
1304
|
+
streamInfo.stream.bandwidth = Number(bandwidth);
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1283
1307
|
this.uriToStreamInfosMap_.set(verbatimImagePlaylistUri, streamInfo);
|
|
1284
1308
|
return streamInfo;
|
|
1285
1309
|
}
|
|
@@ -1811,6 +1835,7 @@ shaka.hls.HlsParser = class {
|
|
|
1811
1835
|
}
|
|
1812
1836
|
|
|
1813
1837
|
let tilesLayout = '';
|
|
1838
|
+
let tileDuration = null;
|
|
1814
1839
|
if (type == shaka.util.ManifestParserUtils.ContentType.IMAGE) {
|
|
1815
1840
|
// By default in HLS the tilesLayout is 1x1
|
|
1816
1841
|
tilesLayout = '1x1';
|
|
@@ -1818,6 +1843,10 @@ shaka.hls.HlsParser = class {
|
|
|
1818
1843
|
shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-TILES');
|
|
1819
1844
|
if (tilesTag) {
|
|
1820
1845
|
tilesLayout = tilesTag.getRequiredAttrValue('LAYOUT');
|
|
1846
|
+
const duration = tilesTag.getAttributeValue('DURATION');
|
|
1847
|
+
if (duration) {
|
|
1848
|
+
tileDuration = Number(duration);
|
|
1849
|
+
}
|
|
1821
1850
|
}
|
|
1822
1851
|
}
|
|
1823
1852
|
|
|
@@ -1833,6 +1862,7 @@ shaka.hls.HlsParser = class {
|
|
|
1833
1862
|
/* appendWindowEnd= */ Infinity,
|
|
1834
1863
|
partialSegmentRefs,
|
|
1835
1864
|
tilesLayout,
|
|
1865
|
+
tileDuration,
|
|
1836
1866
|
);
|
|
1837
1867
|
}
|
|
1838
1868
|
|
|
@@ -347,7 +347,8 @@ shaka.media.SegmentIndex = class {
|
|
|
347
347
|
lastReference.appendWindowStart,
|
|
348
348
|
lastReference.appendWindowEnd,
|
|
349
349
|
lastReference.partialReferences,
|
|
350
|
-
lastReference.tilesLayout
|
|
350
|
+
lastReference.tilesLayout,
|
|
351
|
+
lastReference.tileDuration);
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
|
|
@@ -140,11 +140,15 @@ shaka.media.SegmentReference = class {
|
|
|
140
140
|
* The value is a grid-item-dimension consisting of two positive decimal
|
|
141
141
|
* integers in the format: column-x-row ('4x3'). It describes the
|
|
142
142
|
* arrangement of Images in a Grid. The minimum valid LAYOUT is '1x1'.
|
|
143
|
+
* @param {?number=} tileDuration
|
|
144
|
+
* The explicit duration of an individual tile within the tiles grid.
|
|
145
|
+
* If not provided, the duration should be automatically calculated based on
|
|
146
|
+
* the duration of the reference.
|
|
143
147
|
*/
|
|
144
148
|
constructor(
|
|
145
149
|
startTime, endTime, uris, startByte, endByte, initSegmentReference,
|
|
146
150
|
timestampOffset, appendWindowStart, appendWindowEnd,
|
|
147
|
-
partialReferences = [], tilesLayout = '') {
|
|
151
|
+
partialReferences = [], tilesLayout = '', tileDuration = null) {
|
|
148
152
|
// A preload hinted Partial Segment has the same startTime and endTime.
|
|
149
153
|
goog.asserts.assert(startTime <= endTime,
|
|
150
154
|
'startTime must be less than or equal to endTime');
|
|
@@ -191,6 +195,9 @@ shaka.media.SegmentReference = class {
|
|
|
191
195
|
|
|
192
196
|
/** @type {?string} */
|
|
193
197
|
this.tilesLayout = tilesLayout;
|
|
198
|
+
|
|
199
|
+
/** @type {?number} */
|
|
200
|
+
this.tileDuration = tileDuration;
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
/**
|
|
@@ -275,6 +282,17 @@ shaka.media.SegmentReference = class {
|
|
|
275
282
|
getTilesLayout() {
|
|
276
283
|
return this.tilesLayout;
|
|
277
284
|
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Returns the segment's explicit tile duration.
|
|
288
|
+
* Only defined in image segments.
|
|
289
|
+
*
|
|
290
|
+
* @return {?number}
|
|
291
|
+
* @export
|
|
292
|
+
*/
|
|
293
|
+
getTileDuration() {
|
|
294
|
+
return this.tileDuration;
|
|
295
|
+
}
|
|
278
296
|
};
|
|
279
297
|
|
|
280
298
|
|
|
@@ -623,7 +623,7 @@ shaka.media.StreamingEngine = class {
|
|
|
623
623
|
|
|
624
624
|
// Don't clear the buffer unless something is buffered. This extra
|
|
625
625
|
// check prevents extra, useless calls to clear the buffer.
|
|
626
|
-
if (somethingBuffered) {
|
|
626
|
+
if (somethingBuffered || mediaState.performingUpdate) {
|
|
627
627
|
this.forceClearBuffer_(mediaState);
|
|
628
628
|
streamCleared = true;
|
|
629
629
|
}
|
|
@@ -977,7 +977,10 @@ shaka.media.StreamingEngine = class {
|
|
|
977
977
|
unscaledBufferingGoal * this.bufferingGoalScale_;
|
|
978
978
|
|
|
979
979
|
// Check if we've buffered to the end of the presentation.
|
|
980
|
-
|
|
980
|
+
const timeUntilEnd =
|
|
981
|
+
this.manifest_.presentationTimeline.getDuration() - timeNeeded;
|
|
982
|
+
const oneMicrosecond = 1e-6;
|
|
983
|
+
if (timeUntilEnd < oneMicrosecond) {
|
|
981
984
|
// We shouldn't rebuffer if the playhead is close to the end of the
|
|
982
985
|
// presentation.
|
|
983
986
|
shaka.log.debug(logPrefix, 'buffered to end of presentation');
|
package/lib/player.js
CHANGED
|
@@ -3454,7 +3454,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3454
3454
|
const height = fullImageHeight / rows;
|
|
3455
3455
|
const totalImages = columns * rows;
|
|
3456
3456
|
const segmentDuration = reference.trueEndTime - reference.startTime;
|
|
3457
|
-
const thumbnailDuration =
|
|
3457
|
+
const thumbnailDuration =
|
|
3458
|
+
reference.getTileDuration() || (segmentDuration / totalImages);
|
|
3458
3459
|
let thumbnailTime = reference.startTime;
|
|
3459
3460
|
let positionX = 0;
|
|
3460
3461
|
let positionY = 0;
|
|
@@ -3477,6 +3478,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3477
3478
|
positionY = Math.floor(thumbnailPosition / columns) * height;
|
|
3478
3479
|
}
|
|
3479
3480
|
return {
|
|
3481
|
+
imageHeight: fullImageHeight,
|
|
3482
|
+
imageWidth: fullImageWidth,
|
|
3480
3483
|
height: height,
|
|
3481
3484
|
positionX: positionX,
|
|
3482
3485
|
positionY: positionY,
|
|
@@ -3878,20 +3881,24 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
3878
3881
|
const LanguageUtils = shaka.util.LanguageUtils;
|
|
3879
3882
|
const inputlanguage = LanguageUtils.normalize(language);
|
|
3880
3883
|
const chaptersTracks = this.getChaptersTracks_();
|
|
3881
|
-
const
|
|
3882
|
-
.
|
|
3883
|
-
if (!
|
|
3884
|
+
const chaptersTracksWithLanguage = chaptersTracks
|
|
3885
|
+
.filter((t) => LanguageUtils.normalize(t.language) == inputlanguage);
|
|
3886
|
+
if (!chaptersTracksWithLanguage || !chaptersTracksWithLanguage.length) {
|
|
3884
3887
|
return [];
|
|
3885
3888
|
}
|
|
3886
3889
|
const chapters = [];
|
|
3887
|
-
for (const
|
|
3888
|
-
|
|
3889
|
-
|
|
3890
|
-
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3890
|
+
for (const chaptersTrack of chaptersTracksWithLanguage) {
|
|
3891
|
+
if (chaptersTrack && chaptersTrack.cues) {
|
|
3892
|
+
for (const cue of chaptersTrack.cues) {
|
|
3893
|
+
/** @type {shaka.extern.Chapter} */
|
|
3894
|
+
const chapter = {
|
|
3895
|
+
title: cue.text,
|
|
3896
|
+
startTime: cue.startTime,
|
|
3897
|
+
endTime: cue.endTime,
|
|
3898
|
+
};
|
|
3899
|
+
chapters.push(chapter);
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3895
3902
|
}
|
|
3896
3903
|
return chapters;
|
|
3897
3904
|
}
|
|
@@ -6331,7 +6338,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6331
6338
|
* @define {string} A version number taken from git at compile time.
|
|
6332
6339
|
* @export
|
|
6333
6340
|
*/
|
|
6334
|
-
shaka.Player.version = 'v3.2.
|
|
6341
|
+
shaka.Player.version = 'v3.2.3-uncompiled';
|
|
6335
6342
|
|
|
6336
6343
|
// Initialize the deprecation system using the version string we just set
|
|
6337
6344
|
// on the player.
|
|
@@ -24,8 +24,6 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
24
24
|
* @export
|
|
25
25
|
*/
|
|
26
26
|
static install() {
|
|
27
|
-
shaka.log.debug('MediaCapabilities: install');
|
|
28
|
-
|
|
29
27
|
// Since MediaCapabilities is not fully supported on Chromecast yet, we
|
|
30
28
|
// should always install polyfill for Chromecast.
|
|
31
29
|
// TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
|
|
@@ -40,21 +38,28 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
40
38
|
// TODO: re-evaluate MediaCapabilities in the future versions of PS5
|
|
41
39
|
// Browsers.
|
|
42
40
|
if (!shaka.util.Platform.isChromecast() &&
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
shaka.log.
|
|
41
|
+
!shaka.util.Platform.isApple() &&
|
|
42
|
+
!shaka.util.Platform.isPS5() &&
|
|
43
|
+
navigator.mediaCapabilities) {
|
|
44
|
+
shaka.log.info(
|
|
47
45
|
'MediaCapabilities: Native mediaCapabilities support found.');
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
shaka.log.info('MediaCapabilities: install');
|
|
50
|
+
|
|
51
|
+
if (!navigator.mediaCapabilities) {
|
|
52
|
+
navigator.mediaCapabilities = /** @type {!MediaCapabilities} */ ({});
|
|
54
53
|
}
|
|
55
|
-
|
|
56
|
-
// MediaCapabilities
|
|
57
|
-
|
|
54
|
+
|
|
55
|
+
// Keep the patched MediaCapabilities object from being garbage-collected in
|
|
56
|
+
// Safari.
|
|
57
|
+
// See https://github.com/google/shaka-player/issues/3696#issuecomment-1009472718
|
|
58
|
+
shaka.polyfill.MediaCapabilities.originalMcap =
|
|
59
|
+
navigator.mediaCapabilities;
|
|
60
|
+
|
|
61
|
+
navigator.mediaCapabilities.decodingInfo =
|
|
62
|
+
shaka.polyfill.MediaCapabilities.decodingInfo_;
|
|
58
63
|
}
|
|
59
64
|
|
|
60
65
|
/**
|
|
@@ -182,6 +187,16 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
182
187
|
}
|
|
183
188
|
};
|
|
184
189
|
|
|
190
|
+
/**
|
|
191
|
+
* A copy of the MediaCapabilities instance, to prevent Safari from
|
|
192
|
+
* garbage-collecting the polyfilled method on it. We make it public and export
|
|
193
|
+
* it to ensure that it is not stripped out by the compiler.
|
|
194
|
+
*
|
|
195
|
+
* @type {MediaCapabilities}
|
|
196
|
+
* @export
|
|
197
|
+
*/
|
|
198
|
+
shaka.polyfill.MediaCapabilities.originalMcap = null;
|
|
199
|
+
|
|
185
200
|
// Install at a lower priority than MediaSource polyfill, so that we have
|
|
186
201
|
// MediaSource available first.
|
|
187
202
|
shaka.polyfill.register(shaka.polyfill.MediaCapabilities.install, -1);
|
package/lib/text/cue.js
CHANGED
|
@@ -86,16 +86,17 @@ shaka.text.TtmlTextParser = class {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// Get the framerate, subFrameRate and frameRateMultiplier if applicable.
|
|
89
|
-
const frameRate = XmlUtils.
|
|
90
|
-
const subFrameRate = XmlUtils.
|
|
89
|
+
const frameRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRate');
|
|
90
|
+
const subFrameRate = XmlUtils.getAttributeNSList(
|
|
91
|
+
tt, ttpNs, 'subFrameRate');
|
|
91
92
|
const frameRateMultiplier =
|
|
92
|
-
XmlUtils.
|
|
93
|
-
const tickRate = XmlUtils.
|
|
93
|
+
XmlUtils.getAttributeNSList(tt, ttpNs, 'frameRateMultiplier');
|
|
94
|
+
const tickRate = XmlUtils.getAttributeNSList(tt, ttpNs, 'tickRate');
|
|
94
95
|
|
|
95
|
-
const cellResolution = XmlUtils.
|
|
96
|
+
const cellResolution = XmlUtils.getAttributeNSList(
|
|
96
97
|
tt, ttpNs, 'cellResolution');
|
|
97
98
|
const spaceStyle = tt.getAttribute('xml:space') || 'default';
|
|
98
|
-
const extent = XmlUtils.
|
|
99
|
+
const extent = XmlUtils.getAttributeNSList(tt, ttsNs, 'extent');
|
|
99
100
|
|
|
100
101
|
if (spaceStyle != 'default' && spaceStyle != 'preserve') {
|
|
101
102
|
throw new shaka.util.Error(
|
|
@@ -147,28 +148,15 @@ shaka.text.TtmlTextParser = class {
|
|
|
147
148
|
shaka.util.Error.Code.INVALID_TEXT_CUE,
|
|
148
149
|
'<span> can only be inside <p> in TTML');
|
|
149
150
|
}
|
|
151
|
+
}
|
|
150
152
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
if (cue) {
|
|
159
|
-
cues.push(cue);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
} else {
|
|
163
|
-
// Only used for parsing the background image
|
|
164
|
-
const cue = TtmlTextParser.parseCue_(
|
|
165
|
-
div, time.periodStart, rateInfo, metadataElements, styles,
|
|
166
|
-
regionElements, cueRegions, whitespaceTrim,
|
|
167
|
-
/* isNested= */ false, cellResolutionInfo);
|
|
168
|
-
if (cue) {
|
|
169
|
-
cues.push(cue);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
153
|
+
const cue = TtmlTextParser.parseCue_(
|
|
154
|
+
body, time.periodStart, rateInfo, metadataElements, styles,
|
|
155
|
+
regionElements, cueRegions, whitespaceTrim,
|
|
156
|
+
cellResolutionInfo, /* parentCueElement= */ null,
|
|
157
|
+
/* isContent= */ false);
|
|
158
|
+
if (cue) {
|
|
159
|
+
cues.push(cue);
|
|
172
160
|
}
|
|
173
161
|
}
|
|
174
162
|
|
|
@@ -186,18 +174,19 @@ shaka.text.TtmlTextParser = class {
|
|
|
186
174
|
* @param {!Array.<!Element>} regionElements
|
|
187
175
|
* @param {!Array.<!shaka.text.CueRegion>} cueRegions
|
|
188
176
|
* @param {boolean} whitespaceTrim
|
|
189
|
-
* @param {boolean} isNested
|
|
190
177
|
* @param {?{columns: number, rows: number}} cellResolution
|
|
178
|
+
* @param {?Element} parentCueElement
|
|
179
|
+
* @param {boolean} isContent
|
|
191
180
|
* @return {shaka.text.Cue}
|
|
192
181
|
* @private
|
|
193
182
|
*/
|
|
194
183
|
static parseCue_(
|
|
195
184
|
cueNode, offset, rateInfo, metadataElements, styles, regionElements,
|
|
196
|
-
cueRegions, whitespaceTrim,
|
|
185
|
+
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
|
|
197
186
|
/** @type {Element} */
|
|
198
187
|
let cueElement;
|
|
199
188
|
/** @type {Element} */
|
|
200
|
-
let parentElement = /** @type {Element} */(cueNode.parentNode);
|
|
189
|
+
let parentElement = /** @type {Element} */ (cueNode.parentNode);
|
|
201
190
|
|
|
202
191
|
if (cueNode.nodeType == Node.COMMENT_NODE) {
|
|
203
192
|
// The comments do not contain information that interests us here.
|
|
@@ -205,6 +194,12 @@ shaka.text.TtmlTextParser = class {
|
|
|
205
194
|
}
|
|
206
195
|
|
|
207
196
|
if (cueNode.nodeType == Node.TEXT_NODE) {
|
|
197
|
+
if (!isContent) {
|
|
198
|
+
// Ignore text elements outside the content. For example, whitespace
|
|
199
|
+
// on the same lexical level as the <p> elements, in a document with
|
|
200
|
+
// xml:space="preserve", should not be renderer.
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
208
203
|
// This should generate an "anonymous span" according to the TTML spec.
|
|
209
204
|
// So pretend the element was a <span>. parentElement was set above, so
|
|
210
205
|
// we should still be able to correctly traverse up for timing
|
|
@@ -219,6 +214,21 @@ shaka.text.TtmlTextParser = class {
|
|
|
219
214
|
}
|
|
220
215
|
goog.asserts.assert(cueElement, 'cueElement should be non-null!');
|
|
221
216
|
|
|
217
|
+
let imageElement = null;
|
|
218
|
+
for (const nameSpace of shaka.text.TtmlTextParser.smpteNsList_) {
|
|
219
|
+
imageElement = shaka.text.TtmlTextParser.getElementsFromCollection_(
|
|
220
|
+
cueElement, 'backgroundImage', metadataElements, '#',
|
|
221
|
+
nameSpace)[0];
|
|
222
|
+
if (imageElement) {
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const parentIsContent = isContent;
|
|
228
|
+
if (cueNode.nodeName == 'p' || imageElement) {
|
|
229
|
+
isContent = true;
|
|
230
|
+
}
|
|
231
|
+
|
|
222
232
|
const spaceStyle = cueElement.getAttribute('xml:space') ||
|
|
223
233
|
(whitespaceTrim ? 'default' : 'preserve');
|
|
224
234
|
|
|
@@ -243,8 +253,9 @@ shaka.text.TtmlTextParser = class {
|
|
|
243
253
|
regionElements,
|
|
244
254
|
cueRegions,
|
|
245
255
|
localWhitespaceTrim,
|
|
246
|
-
/* isNested= */ true,
|
|
247
256
|
cellResolution,
|
|
257
|
+
cueElement,
|
|
258
|
+
isContent,
|
|
248
259
|
);
|
|
249
260
|
|
|
250
261
|
// This node may or may not generate a nested cue.
|
|
@@ -254,6 +265,8 @@ shaka.text.TtmlTextParser = class {
|
|
|
254
265
|
}
|
|
255
266
|
}
|
|
256
267
|
|
|
268
|
+
const isNested = /** @type {boolean} */ (parentCueElement != null);
|
|
269
|
+
|
|
257
270
|
// In this regex, "\S" means "non-whitespace character".
|
|
258
271
|
const hasTextContent = /\S/.test(cueElement.textContent);
|
|
259
272
|
const hasTimeAttributes =
|
|
@@ -334,6 +347,13 @@ shaka.text.TtmlTextParser = class {
|
|
|
334
347
|
const cue = new shaka.text.Cue(start, end, payload);
|
|
335
348
|
cue.nestedCues = nestedCues;
|
|
336
349
|
|
|
350
|
+
if (!isContent) {
|
|
351
|
+
// If this is not a <p> element or a <div> with images, and it has no
|
|
352
|
+
// parent that was a <p> element, then it's part of the outer containers
|
|
353
|
+
// (e.g. the <body> or a normal <div> element within it).
|
|
354
|
+
cue.isContainer = true;
|
|
355
|
+
}
|
|
356
|
+
|
|
337
357
|
if (cellResolution) {
|
|
338
358
|
cue.cellResolution = cellResolution;
|
|
339
359
|
}
|
|
@@ -341,31 +361,32 @@ shaka.text.TtmlTextParser = class {
|
|
|
341
361
|
// Get other properties if available.
|
|
342
362
|
const regionElement = shaka.text.TtmlTextParser.getElementsFromCollection_(
|
|
343
363
|
cueElement, 'region', regionElements, /* prefix= */ '')[0];
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
imageElement = shaka.text.TtmlTextParser.getElementsFromCollection_(
|
|
352
|
-
cueElement, 'backgroundImage', metadataElements, '#',
|
|
353
|
-
nameSpace)[0];
|
|
354
|
-
if (imageElement) {
|
|
355
|
-
break;
|
|
364
|
+
// Do not actually apply that region unless it is non-inherited, though.
|
|
365
|
+
// This makes it so that, if a parent element has a region, the children
|
|
366
|
+
// don't also all independently apply the positioning of that region.
|
|
367
|
+
if (cueElement.hasAttribute('region')) {
|
|
368
|
+
if (regionElement && regionElement.getAttribute('xml:id')) {
|
|
369
|
+
const regionId = regionElement.getAttribute('xml:id');
|
|
370
|
+
cue.region = cueRegions.filter((region) => region.id == regionId)[0];
|
|
356
371
|
}
|
|
357
372
|
}
|
|
358
373
|
|
|
359
|
-
|
|
374
|
+
let regionElementForStyle = regionElement;
|
|
375
|
+
if (parentCueElement && isNested && !cueElement.getAttribute('region') &&
|
|
376
|
+
!cueElement.getAttribute('style')) {
|
|
377
|
+
regionElementForStyle =
|
|
378
|
+
shaka.text.TtmlTextParser.getElementsFromCollection_(
|
|
379
|
+
parentCueElement, 'region', regionElements, /* prefix= */ '')[0];
|
|
380
|
+
}
|
|
360
381
|
|
|
361
382
|
shaka.text.TtmlTextParser.addStyle_(
|
|
362
383
|
cue,
|
|
363
384
|
cueElement,
|
|
364
|
-
|
|
385
|
+
regionElementForStyle,
|
|
365
386
|
imageElement,
|
|
366
387
|
styles,
|
|
367
|
-
isNested,
|
|
368
|
-
isLeaf);
|
|
388
|
+
/** isNested= */ parentIsContent, // "nested in a <div>" doesn't count.
|
|
389
|
+
/** isLeaf= */ (nestedCues.length == 0));
|
|
369
390
|
|
|
370
391
|
return cue;
|
|
371
392
|
}
|
|
@@ -505,14 +526,13 @@ shaka.text.TtmlTextParser = class {
|
|
|
505
526
|
}
|
|
506
527
|
|
|
507
528
|
const align = TtmlTextParser.getStyleAttribute_(
|
|
508
|
-
cueElement, region, styles, 'textAlign',
|
|
529
|
+
cueElement, region, styles, 'textAlign', true);
|
|
509
530
|
if (align) {
|
|
510
531
|
cue.positionAlign = TtmlTextParser.textAlignToPositionAlign_[align];
|
|
511
532
|
cue.lineAlign = TtmlTextParser.textAlignToLineAlign_[align];
|
|
512
533
|
|
|
513
534
|
goog.asserts.assert(align.toUpperCase() in Cue.textAlign,
|
|
514
|
-
align.toUpperCase() +
|
|
515
|
-
' Should be in Cue.textAlign values!');
|
|
535
|
+
align.toUpperCase() + ' Should be in Cue.textAlign values!');
|
|
516
536
|
|
|
517
537
|
cue.textAlign = Cue.textAlign[align.toUpperCase()];
|
|
518
538
|
} else {
|
|
@@ -521,7 +541,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
521
541
|
}
|
|
522
542
|
|
|
523
543
|
const displayAlign = TtmlTextParser.getStyleAttribute_(
|
|
524
|
-
cueElement, region, styles, 'displayAlign',
|
|
544
|
+
cueElement, region, styles, 'displayAlign', true);
|
|
525
545
|
if (displayAlign) {
|
|
526
546
|
goog.asserts.assert(displayAlign.toUpperCase() in Cue.displayAlign,
|
|
527
547
|
displayAlign.toUpperCase() +
|
|
@@ -753,7 +773,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
753
773
|
return null;
|
|
754
774
|
}
|
|
755
775
|
|
|
756
|
-
const attr = XmlUtils.
|
|
776
|
+
const attr = XmlUtils.getAttributeNSList(region, ttsNs, attribute);
|
|
757
777
|
if (attr) {
|
|
758
778
|
return attr;
|
|
759
779
|
}
|
|
@@ -778,7 +798,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
778
798
|
|
|
779
799
|
// Styling on elements should take precedence
|
|
780
800
|
// over the main styling attributes
|
|
781
|
-
const elementAttribute = XmlUtils.
|
|
801
|
+
const elementAttribute = XmlUtils.getAttributeNSList(
|
|
782
802
|
cueElement,
|
|
783
803
|
ttsNs,
|
|
784
804
|
attribute);
|
|
@@ -821,7 +841,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
821
841
|
|
|
822
842
|
if (!styleAttributeValue) {
|
|
823
843
|
// Fall back to tts namespace.
|
|
824
|
-
styleAttributeValue = XmlUtils.
|
|
844
|
+
styleAttributeValue = XmlUtils.getAttributeNSList(
|
|
825
845
|
inheritedStyles[i],
|
|
826
846
|
ttsNs,
|
|
827
847
|
attribute);
|
|
@@ -1276,20 +1296,26 @@ shaka.text.TtmlTextParser.textAlignToPositionAlign_ = {
|
|
|
1276
1296
|
* document, not just "ttp:", so we use this with getAttributeNS() to ensure
|
|
1277
1297
|
* that we support arbitrary namespace names.
|
|
1278
1298
|
*
|
|
1279
|
-
* @const {string}
|
|
1299
|
+
* @const {!Array.<string>}
|
|
1280
1300
|
* @private
|
|
1281
1301
|
*/
|
|
1282
|
-
shaka.text.TtmlTextParser.parameterNs_ =
|
|
1302
|
+
shaka.text.TtmlTextParser.parameterNs_ = [
|
|
1303
|
+
'http://www.w3.org/ns/ttml#parameter',
|
|
1304
|
+
'http://www.w3.org/2006/10/ttaf1#parameter',
|
|
1305
|
+
];
|
|
1283
1306
|
|
|
1284
1307
|
/**
|
|
1285
1308
|
* The namespace URL for TTML styles. Can be assigned any name in the TTML
|
|
1286
1309
|
* document, not just "tts:", so we use this with getAttributeNS() to ensure
|
|
1287
1310
|
* that we support arbitrary namespace names.
|
|
1288
1311
|
*
|
|
1289
|
-
* @const {string}
|
|
1312
|
+
* @const {!Array.<string>}
|
|
1290
1313
|
* @private
|
|
1291
1314
|
*/
|
|
1292
|
-
shaka.text.TtmlTextParser.styleNs_ =
|
|
1315
|
+
shaka.text.TtmlTextParser.styleNs_ = [
|
|
1316
|
+
'http://www.w3.org/ns/ttml#styling',
|
|
1317
|
+
'http://www.w3.org/2006/10/ttaf1#styling',
|
|
1318
|
+
];
|
|
1293
1319
|
|
|
1294
1320
|
/**
|
|
1295
1321
|
* The namespace URL for EBU TTML styles. Can be assigned any name in the TTML
|