shaka-player 3.1.3 → 3.1.4
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/AUTHORS +3 -0
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTORS +3 -0
- package/build/generateExterns.js +1 -1
- package/demo/common/assets.js +1 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +1 -1
- package/dist/demo.compiled.js +1 -1
- package/dist/deps.js +1 -1
- package/dist/locales.js +35 -35
- package/dist/receiver.compiled.debug.js +1 -1
- package/dist/receiver.compiled.js +1 -1
- package/dist/shaka-player.compiled.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.d.ts +1 -1
- package/dist/shaka-player.compiled.debug.externs.js +1 -1
- package/dist/shaka-player.compiled.debug.js +187 -183
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +1 -1
- package/dist/shaka-player.compiled.js +799 -799
- package/dist/shaka-player.compiled.map +2 -2
- package/dist/shaka-player.ui.d.ts +1 -1
- package/dist/shaka-player.ui.debug.d.ts +1 -1
- package/dist/shaka-player.ui.debug.externs.js +3 -3
- package/dist/shaka-player.ui.debug.js +264 -260
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +3 -3
- package/dist/shaka-player.ui.js +896 -895
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/karma.conf.js +1 -0
- package/lib/ads/client_side_ad_manager.js +46 -86
- package/lib/ads/server_side_ad_manager.js +7 -10
- package/lib/cast/cast_proxy.js +3 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/dash/dash_parser.js +93 -2
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +30 -17
- package/lib/media/drm_engine.js +7 -0
- package/lib/media/gap_jumping_controller.js +4 -2
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/streaming_engine.js +2 -1
- package/lib/net/networking_engine.js +2 -1
- package/lib/player.js +22 -22
- package/lib/polyfill/media_capabilities.js +7 -1
- package/lib/polyfill/mediasource.js +6 -0
- package/lib/polyfill/orientation.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +4 -4
- package/lib/polyfill/patchedmediakeys_ms.js +4 -4
- package/lib/polyfill/patchedmediakeys_webkit.js +4 -4
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/text_engine.js +14 -2
- package/lib/text/ttml_text_parser.js +5 -0
- package/lib/text/ui_text_displayer.js +85 -37
- package/lib/util/fake_event.js +53 -7
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/stream_utils.js +13 -0
- package/package.json +1 -1
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_utils_unit.js +1 -1
- package/test/dash/dash_parser_manifest_unit.js +187 -1
- package/test/hls/hls_parser_unit.js +5 -1
- package/test/media/closed_caption_parser_unit.js +6 -1
- package/test/media/drm_engine_unit.js +43 -0
- package/test/player_integration.js +38 -0
- package/test/test/assets/3675/chunk-stream0-00001.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00002.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00003.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00004.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00005.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00006.m4s +0 -0
- package/test/test/assets/3675/chunk-stream0-00007.m4s +0 -0
- package/test/test/assets/3675/dash_0.mpd +30 -0
- package/test/test/assets/3675/dash_1.mpd +30 -0
- package/test/test/assets/3675/dash_2.mpd +30 -0
- package/test/test/assets/3675/dash_3.mpd +30 -0
- package/test/test/assets/3675/dash_4.mpd +30 -0
- package/test/test/assets/3675/dash_5.mpd +26 -0
- package/test/test/assets/3675/init-stream0.m4s +0 -0
- package/test/test/util/fake_ad_manager.js +1 -1
- package/test/text/ttml_text_parser_unit.js +9 -0
- package/test/text/ui_text_displayer_unit.js +29 -17
- package/test/ui/localization_unit.js +15 -14
- package/test/ui/text_displayer_layout_unit.js +13 -1
- package/test/util/stream_utils_unit.js +35 -0
- package/ui/cast_button.js +2 -3
- package/ui/controls.js +4 -6
- package/ui/less/containers.less +3 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +28 -51
- package/ui/pip_button.js +2 -3
|
@@ -217,8 +217,10 @@ shaka.media.GapJumpingController = class {
|
|
|
217
217
|
this.didFireLargeGap_ = true;
|
|
218
218
|
|
|
219
219
|
// Event firing is synchronous.
|
|
220
|
-
const
|
|
221
|
-
'
|
|
220
|
+
const data = new Map()
|
|
221
|
+
.set('currentTime', currentTime)
|
|
222
|
+
.set('gapSize', jumpSize);
|
|
223
|
+
const event = new shaka.util.FakeEvent('largegap', data);
|
|
222
224
|
event.cancelable = true;
|
|
223
225
|
this.onEvent_(event);
|
|
224
226
|
|
|
@@ -48,8 +48,7 @@ shaka.media.MediaSourceEngine = class {
|
|
|
48
48
|
* @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
|
|
49
49
|
* onMetadata
|
|
50
50
|
*/
|
|
51
|
-
constructor(video, closedCaptionParser, textDisplayer,
|
|
52
|
-
onMetadata) {
|
|
51
|
+
constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
|
|
53
52
|
/** @private {HTMLMediaElement} */
|
|
54
53
|
this.video_ = video;
|
|
55
54
|
|
|
@@ -1662,7 +1662,8 @@ shaka.media.StreamingEngine = class {
|
|
|
1662
1662
|
|
|
1663
1663
|
// Dispatch an event to notify the application about the emsg box.
|
|
1664
1664
|
const eventName = shaka.Player.EventName.Emsg;
|
|
1665
|
-
const
|
|
1665
|
+
const data = (new Map()).set('detail', emsg);
|
|
1666
|
+
const event = new shaka.util.FakeEvent(eventName, data);
|
|
1666
1667
|
this.playerInterface_.onEvent(event);
|
|
1667
1668
|
}
|
|
1668
1669
|
}
|
|
@@ -544,7 +544,8 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
|
|
|
544
544
|
}
|
|
545
545
|
|
|
546
546
|
if (error.severity == shaka.util.Error.Severity.RECOVERABLE) {
|
|
547
|
-
const
|
|
547
|
+
const data = (new Map()).set('error', error);
|
|
548
|
+
const event = new shaka.util.FakeEvent('retry', data);
|
|
548
549
|
this.dispatchEvent(event);
|
|
549
550
|
|
|
550
551
|
// Move to the next URI.
|
package/lib/player.js
CHANGED
|
@@ -596,7 +596,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
596
596
|
enterNode: (node, has, wants) => {
|
|
597
597
|
this.dispatchEvent(this.makeEvent_(
|
|
598
598
|
/* name= */ shaka.Player.EventName.OnStateChange,
|
|
599
|
-
/* data= */
|
|
599
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
600
600
|
|
|
601
601
|
const action = actions.get(node);
|
|
602
602
|
return action(has, wants);
|
|
@@ -627,7 +627,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
627
627
|
onIdle: (node) => {
|
|
628
628
|
this.dispatchEvent(this.makeEvent_(
|
|
629
629
|
/* name= */ shaka.Player.EventName.OnStateIdle,
|
|
630
|
-
/* data= */
|
|
630
|
+
/* data= */ (new Map()).set('state', node.name)));
|
|
631
631
|
},
|
|
632
632
|
};
|
|
633
633
|
|
|
@@ -647,7 +647,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
647
647
|
|
|
648
648
|
/**
|
|
649
649
|
* @param {!shaka.Player.EventName} name
|
|
650
|
-
* @param {Object
|
|
650
|
+
* @param {Map.<string, Object>=} data
|
|
651
651
|
* @return {!shaka.util.FakeEvent}
|
|
652
652
|
* @private
|
|
653
653
|
*/
|
|
@@ -1155,6 +1155,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
1155
1155
|
'mpg': 'video/mpeg',
|
|
1156
1156
|
'mpeg': 'video/mpeg',
|
|
1157
1157
|
'm3u8': 'application/x-mpegurl',
|
|
1158
|
+
'mpd': 'application/dash+xml',
|
|
1158
1159
|
'mp3': 'audio/mpeg',
|
|
1159
1160
|
'aac': 'audio/aac',
|
|
1160
1161
|
'flac': 'audio/flac',
|
|
@@ -2415,12 +2416,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2415
2416
|
goog.asserts.assert(!endTime || startTime <= endTime,
|
|
2416
2417
|
'Metadata start time should be less or equal to the end time!');
|
|
2417
2418
|
const eventName = shaka.Player.EventName.Metadata;
|
|
2418
|
-
const data =
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
};
|
|
2419
|
+
const data = new Map()
|
|
2420
|
+
.set('startTime', startTime)
|
|
2421
|
+
.set('endTime', endTime)
|
|
2422
|
+
.set('metadataType', metadataType)
|
|
2423
|
+
.set('payload', payload);
|
|
2424
2424
|
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
2425
2425
|
}
|
|
2426
2426
|
|
|
@@ -2482,7 +2482,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
2482
2482
|
this.abrManager_.segmentDownloaded(deltaTimeMs, bytesDownloaded);
|
|
2483
2483
|
}
|
|
2484
2484
|
};
|
|
2485
|
-
|
|
2486
2485
|
return new shaka.net.NetworkingEngine(onProgressUpdated_);
|
|
2487
2486
|
}
|
|
2488
2487
|
|
|
@@ -4816,7 +4815,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
4816
4815
|
// Surface the buffering event so that the app knows if/when we are
|
|
4817
4816
|
// buffering.
|
|
4818
4817
|
const eventName = shaka.Player.EventName.Buffering;
|
|
4819
|
-
|
|
4818
|
+
const data = (new Map()).set('buffering', isBuffering);
|
|
4819
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
4820
4820
|
}
|
|
4821
4821
|
|
|
4822
4822
|
/**
|
|
@@ -5198,10 +5198,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5198
5198
|
onVariantChanged_(from, to) {
|
|
5199
5199
|
// Delay the 'variantchanged' event so StreamingEngine has time to absorb
|
|
5200
5200
|
// the changes before the user tries to query it.
|
|
5201
|
-
const
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5201
|
+
const data = new Map()
|
|
5202
|
+
.set('oldTrack', from)
|
|
5203
|
+
.set('newTrack', to);
|
|
5204
|
+
const event = this.makeEvent_(shaka.Player.EventName.VariantChanged, data);
|
|
5205
5205
|
this.delayDispatchEvent_(event);
|
|
5206
5206
|
}
|
|
5207
5207
|
|
|
@@ -5224,10 +5224,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5224
5224
|
|
|
5225
5225
|
/** @private */
|
|
5226
5226
|
onAbrStatusChanged_() {
|
|
5227
|
-
const
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
this.delayDispatchEvent_(event);
|
|
5227
|
+
const data = (new Map()).set('newStatus', this.config_.abr.enabled);
|
|
5228
|
+
this.delayDispatchEvent_(this.makeEvent_(
|
|
5229
|
+
shaka.Player.EventName.AbrStatusChanged, data));
|
|
5231
5230
|
}
|
|
5232
5231
|
|
|
5233
5232
|
/**
|
|
@@ -5244,7 +5243,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5244
5243
|
}
|
|
5245
5244
|
|
|
5246
5245
|
const eventName = shaka.Player.EventName.Error;
|
|
5247
|
-
const event = this.makeEvent_(eventName,
|
|
5246
|
+
const event = this.makeEvent_(eventName, (new Map()).set('detail', error));
|
|
5248
5247
|
this.dispatchEvent(event);
|
|
5249
5248
|
if (event.defaultPrevented) {
|
|
5250
5249
|
error.handled = true;
|
|
@@ -5273,7 +5272,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
|
|
|
5273
5272
|
eventElement: region.eventElement,
|
|
5274
5273
|
};
|
|
5275
5274
|
|
|
5276
|
-
|
|
5275
|
+
const data = (new Map()).set('detail', clone);
|
|
5276
|
+
this.dispatchEvent(this.makeEvent_(eventName, data));
|
|
5277
5277
|
}
|
|
5278
5278
|
|
|
5279
5279
|
/**
|
|
@@ -6102,7 +6102,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
|
|
|
6102
6102
|
* @define {string} A version number taken from git at compile time.
|
|
6103
6103
|
* @export
|
|
6104
6104
|
*/
|
|
6105
|
-
shaka.Player.version = 'v3.1.
|
|
6105
|
+
shaka.Player.version = 'v3.1.4-uncompiled';
|
|
6106
6106
|
|
|
6107
6107
|
// Initialize the deprecation system using the version string we just set
|
|
6108
6108
|
// on the player.
|
|
@@ -34,8 +34,14 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
34
34
|
// See: https://github.com/google/shaka-player/issues/3530
|
|
35
35
|
// TODO: re-evaluate MediaCapabilities in the future versions of Apple
|
|
36
36
|
// Browsers.
|
|
37
|
+
// Since MediaCapabilities implementation is buggy in PS5 browsers, we
|
|
38
|
+
// should always install polyfill for PS5 browsers.
|
|
39
|
+
// See: https://github.com/google/shaka-player/issues/3582
|
|
40
|
+
// TODO: re-evaluate MediaCapabilities in the future versions of PS5
|
|
41
|
+
// Browsers.
|
|
37
42
|
if (!shaka.util.Platform.isChromecast() &&
|
|
38
43
|
!shaka.util.Platform.isApple() &&
|
|
44
|
+
!shaka.util.Platform.isPS5() &&
|
|
39
45
|
navigator.mediaCapabilities) {
|
|
40
46
|
shaka.log.debug(
|
|
41
47
|
'MediaCapabilities: Native mediaCapabilities support found.');
|
|
@@ -149,7 +155,7 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
149
155
|
sessionTypes: mediaCapkeySystemConfig.sessionTypes,
|
|
150
156
|
};
|
|
151
157
|
|
|
152
|
-
// Only add the audio video
|
|
158
|
+
// Only add the audio video capabilities if they have valid data.
|
|
153
159
|
// Otherwise the query will fail.
|
|
154
160
|
if (audioCapabilities.length) {
|
|
155
161
|
mediaKeySystemConfig.audioCapabilities = audioCapabilities;
|
|
@@ -208,6 +208,12 @@ shaka.polyfill.MediaSource = class {
|
|
|
208
208
|
static patchVp09_() {
|
|
209
209
|
const originalIsTypeSupported = MediaSource.isTypeSupported;
|
|
210
210
|
|
|
211
|
+
if (shaka.util.Platform.isWebOS()) {
|
|
212
|
+
// Don't do this on LG webOS as otherwise it is unable
|
|
213
|
+
// to play vp09 at all.
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
211
217
|
MediaSource.isTypeSupported = (mimeType) => {
|
|
212
218
|
// Split the MIME type into its various parameters.
|
|
213
219
|
const pieces = mimeType.split(/ *; */);
|
|
@@ -567,10 +567,10 @@ class extends shaka.util.FakeEventTarget {
|
|
|
567
567
|
|
|
568
568
|
const isNew = this.keyStatuses.getStatus() == undefined;
|
|
569
569
|
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
570
|
+
const data = new Map()
|
|
571
|
+
.set('messageType', isNew ? 'license-request' : 'license-renewal')
|
|
572
|
+
.set('message', shaka.util.BufferUtils.toArrayBuffer(event.message));
|
|
573
|
+
const event2 = new shaka.util.FakeEvent('message', data);
|
|
574
574
|
|
|
575
575
|
this.dispatchEvent(event2);
|
|
576
576
|
}
|
|
@@ -512,10 +512,10 @@ class extends shaka.util.FakeEventTarget {
|
|
|
512
512
|
|
|
513
513
|
const isNew = this.keyStatuses.getStatus() == undefined;
|
|
514
514
|
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
515
|
+
const data = new Map()
|
|
516
|
+
.set('messageType', isNew ? 'license-request' : 'license-renewal')
|
|
517
|
+
.set('message', shaka.util.BufferUtils.toArrayBuffer(event.message));
|
|
518
|
+
const event2 = new shaka.util.FakeEvent('message', data);
|
|
519
519
|
|
|
520
520
|
this.dispatchEvent(event2);
|
|
521
521
|
}
|
|
@@ -451,10 +451,10 @@ shaka.polyfill.PatchedMediaKeysWebkit.MediaKeys = class {
|
|
|
451
451
|
|
|
452
452
|
const isNew = session.keyStatuses.getStatus() == undefined;
|
|
453
453
|
|
|
454
|
-
const
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
454
|
+
const data = new Map()
|
|
455
|
+
.set('messageType', isNew ? 'licenserequest' : 'licenserenewal')
|
|
456
|
+
.set('message', event.message);
|
|
457
|
+
const event2 = new shaka.util.FakeEvent('message', data);
|
|
458
458
|
|
|
459
459
|
session.generated();
|
|
460
460
|
session.dispatchEvent(event2);
|
|
@@ -88,6 +88,9 @@ shaka.text.Mp4TtmlParser = class {
|
|
|
88
88
|
|
|
89
89
|
shaka.text.TextEngine.registerParser(
|
|
90
90
|
'application/mp4; codecs="stpp"', () => new shaka.text.Mp4TtmlParser());
|
|
91
|
+
shaka.text.TextEngine.registerParser(
|
|
92
|
+
'application/mp4; codecs="stpp.ttml"',
|
|
93
|
+
() => new shaka.text.Mp4TtmlParser());
|
|
91
94
|
shaka.text.TextEngine.registerParser(
|
|
92
95
|
'application/mp4; codecs="stpp.ttml.im1t"',
|
|
93
96
|
() => new shaka.text.Mp4TtmlParser());
|
package/lib/text/text_engine.js
CHANGED
|
@@ -345,6 +345,19 @@ shaka.text.TextEngine = class {
|
|
|
345
345
|
return cues;
|
|
346
346
|
}
|
|
347
347
|
|
|
348
|
+
/**
|
|
349
|
+
* @param {!shaka.text.Cue} cue the cue to apply the timestamp to recursively
|
|
350
|
+
* @param {number} videoTimestampOffset the timestamp offset of the video
|
|
351
|
+
* @private
|
|
352
|
+
*/
|
|
353
|
+
applyVideoTimestampOffsetRecursive_(cue, videoTimestampOffset) {
|
|
354
|
+
cue.startTime += videoTimestampOffset;
|
|
355
|
+
cue.endTime += videoTimestampOffset;
|
|
356
|
+
for (const nested of cue.nestedCues) {
|
|
357
|
+
this.applyVideoTimestampOffsetRecursive_(nested, videoTimestampOffset);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
348
361
|
/**
|
|
349
362
|
* Store the closed captions in the text engine, and append the cues to the
|
|
350
363
|
* text displayer. This is a side-channel used for embedded text only.
|
|
@@ -373,8 +386,7 @@ shaka.text.TextEngine = class {
|
|
|
373
386
|
|
|
374
387
|
// Adjust CEA captions with respect to the timestamp offset of the video
|
|
375
388
|
// stream in which they were embedded.
|
|
376
|
-
cue
|
|
377
|
-
cue.endTime += videoTimestampOffset;
|
|
389
|
+
this.applyVideoTimestampOffsetRecursive_(cue, videoTimestampOffset);
|
|
378
390
|
|
|
379
391
|
const keepThisCue =
|
|
380
392
|
cue.startTime >= this.appendWindowStart_ &&
|
|
@@ -199,6 +199,11 @@ shaka.text.TtmlTextParser = class {
|
|
|
199
199
|
/** @type {Element} */
|
|
200
200
|
let parentElement = /** @type {Element} */(cueNode.parentNode);
|
|
201
201
|
|
|
202
|
+
if (cueNode.nodeType == Node.COMMENT_NODE) {
|
|
203
|
+
// The comments do not contain information that interests us here.
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
|
|
202
207
|
if (cueNode.nodeType == Node.TEXT_NODE) {
|
|
203
208
|
// This should generate an "anonymous span" according to the TTML spec.
|
|
204
209
|
// So pretend the element was a <span>. parentElement was set above, so
|
|
@@ -167,9 +167,14 @@ shaka.text.UITextDisplayer = class {
|
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
// Remove the cues out of the time range.
|
|
170
|
+
const oldNumCues = this.cues_.length;
|
|
170
171
|
this.cues_ = this.cues_.filter(
|
|
171
172
|
(cue) => cue.startTime < start || cue.endTime >= end);
|
|
172
|
-
this
|
|
173
|
+
// If anything was actually removed in this process, force the captions to
|
|
174
|
+
// update. This makes sure that the currently-displayed cues will stop
|
|
175
|
+
// displaying if removed (say, due to the user changing languages).
|
|
176
|
+
const forceUpdate = oldNumCues > this.cues_.length;
|
|
177
|
+
this.updateCaptions_(forceUpdate);
|
|
173
178
|
|
|
174
179
|
return true;
|
|
175
180
|
}
|
|
@@ -192,13 +197,13 @@ shaka.text.UITextDisplayer = class {
|
|
|
192
197
|
}
|
|
193
198
|
|
|
194
199
|
/**
|
|
195
|
-
* @param {!Array.<!shaka.
|
|
200
|
+
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
196
201
|
* @param {!HTMLElement} container
|
|
197
202
|
* @param {number} currentTime
|
|
198
|
-
* @param {
|
|
203
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
199
204
|
* @private
|
|
200
205
|
*/
|
|
201
|
-
updateCuesRecursive_(cues, container, currentTime,
|
|
206
|
+
updateCuesRecursive_(cues, container, currentTime, parents) {
|
|
202
207
|
// Set to true if the cues have changed in some way, which will require
|
|
203
208
|
// DOM changes. E.g. if a cue was added or removed.
|
|
204
209
|
let updateDOM = false;
|
|
@@ -214,10 +219,12 @@ shaka.text.UITextDisplayer = class {
|
|
|
214
219
|
* The cues whose corresponding elements should be in the DOM.
|
|
215
220
|
* Some of these might be new, some might have been displayed beforehand.
|
|
216
221
|
* These will only be added if updateDOM is true.
|
|
217
|
-
* @type {!Array.<!shaka.
|
|
222
|
+
* @type {!Array.<!shaka.extern.Cue>}
|
|
218
223
|
*/
|
|
219
224
|
const toPlant = [];
|
|
220
225
|
for (const cue of cues) {
|
|
226
|
+
parents.push(cue);
|
|
227
|
+
|
|
221
228
|
let cueRegistry = this.currentCuesMap_.get(cue);
|
|
222
229
|
const shouldBeDisplayed =
|
|
223
230
|
cue.startTime <= currentTime && cue.endTime > currentTime;
|
|
@@ -241,7 +248,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
241
248
|
toPlant.push(cue);
|
|
242
249
|
if (!cueRegistry) {
|
|
243
250
|
// The cue has to be made!
|
|
244
|
-
this.createCue_(cue,
|
|
251
|
+
this.createCue_(cue, parents);
|
|
245
252
|
cueRegistry = this.currentCuesMap_.get(cue);
|
|
246
253
|
wrapper = cueRegistry.wrapper;
|
|
247
254
|
updateDOM = true;
|
|
@@ -255,8 +262,11 @@ shaka.text.UITextDisplayer = class {
|
|
|
255
262
|
// guaranteed that the children will neither need to be added nor removed.
|
|
256
263
|
if (cue.nestedCues.length > 0 && wrapper) {
|
|
257
264
|
this.updateCuesRecursive_(
|
|
258
|
-
cue.nestedCues, wrapper, currentTime,
|
|
265
|
+
cue.nestedCues, wrapper, currentTime, parents);
|
|
259
266
|
}
|
|
267
|
+
|
|
268
|
+
const topCue = parents.pop();
|
|
269
|
+
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
260
270
|
}
|
|
261
271
|
if (updateDOM) {
|
|
262
272
|
for (const cueElement of toUproot) {
|
|
@@ -304,7 +314,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
304
314
|
|
|
305
315
|
// Update the cues.
|
|
306
316
|
this.updateCuesRecursive_(
|
|
307
|
-
this.cues_, this.textContainer_, currentTime,
|
|
317
|
+
this.cues_, this.textContainer_, currentTime, /* parents= */ []);
|
|
308
318
|
|
|
309
319
|
if (goog.DEBUG) {
|
|
310
320
|
// Previously, we had an issue (#2076) where cues sometimes were not
|
|
@@ -327,10 +337,11 @@ shaka.text.UITextDisplayer = class {
|
|
|
327
337
|
* Creates the object for a cue.
|
|
328
338
|
*
|
|
329
339
|
* @param {!shaka.extern.Cue} cue
|
|
330
|
-
* @param {
|
|
340
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
331
341
|
* @private
|
|
332
342
|
*/
|
|
333
|
-
createCue_(cue,
|
|
343
|
+
createCue_(cue, parents) {
|
|
344
|
+
const isNested = parents.length > 1;
|
|
334
345
|
let type = isNested ? 'span' : 'div';
|
|
335
346
|
if (cue.lineBreak || cue.spacer) {
|
|
336
347
|
if (cue.spacer) {
|
|
@@ -344,7 +355,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
344
355
|
// Nested cues are inline elements. Top-level cues are block elements.
|
|
345
356
|
const cueElement = shaka.util.Dom.createHTMLElement(type);
|
|
346
357
|
if (type != 'br') {
|
|
347
|
-
this.setCaptionStyles_(cueElement, cue,
|
|
358
|
+
this.setCaptionStyles_(cueElement, cue, parents);
|
|
348
359
|
}
|
|
349
360
|
|
|
350
361
|
let wrapper = cueElement;
|
|
@@ -363,13 +374,16 @@ shaka.text.UITextDisplayer = class {
|
|
|
363
374
|
/**
|
|
364
375
|
* @param {!HTMLElement} cueElement
|
|
365
376
|
* @param {!shaka.extern.Cue} cue
|
|
366
|
-
* @param {
|
|
377
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
367
378
|
* @private
|
|
368
379
|
*/
|
|
369
|
-
setCaptionStyles_(cueElement, cue,
|
|
380
|
+
setCaptionStyles_(cueElement, cue, parents) {
|
|
370
381
|
const Cue = shaka.text.Cue;
|
|
371
|
-
|
|
382
|
+
const inherit =
|
|
383
|
+
(cb) => shaka.text.UITextDisplayer.inheritProperty_(parents, cb);
|
|
384
|
+
const style = cueElement.style;
|
|
372
385
|
const isLeaf = cue.nestedCues.length == 0;
|
|
386
|
+
const isNested = parents.length > 1;
|
|
373
387
|
|
|
374
388
|
// TODO: wrapLine is not yet supported. Lines always wrap.
|
|
375
389
|
|
|
@@ -388,16 +402,6 @@ shaka.text.UITextDisplayer = class {
|
|
|
388
402
|
const nonBreakingSpace = '\xa0';
|
|
389
403
|
return nonBreakingSpace.repeat(match.length);
|
|
390
404
|
});
|
|
391
|
-
if (isNested) {
|
|
392
|
-
cueElement.textContent = text;
|
|
393
|
-
} else if (text.length) {
|
|
394
|
-
// If a top-level cue has text, move to a <span> so the background is
|
|
395
|
-
// styled correctly.
|
|
396
|
-
const span = shaka.util.Dom.createHTMLElement('span');
|
|
397
|
-
span.textContent = text;
|
|
398
|
-
cueElement.appendChild(span);
|
|
399
|
-
style = span.style;
|
|
400
|
-
}
|
|
401
405
|
|
|
402
406
|
style.backgroundColor = cue.backgroundColor;
|
|
403
407
|
style.border = cue.border;
|
|
@@ -406,20 +410,40 @@ shaka.text.UITextDisplayer = class {
|
|
|
406
410
|
style.opacity = cue.opacity;
|
|
407
411
|
style.paddingLeft = shaka.text.UITextDisplayer.convertLengthValue_(
|
|
408
412
|
cue.linePadding, cue, this.videoContainer_);
|
|
409
|
-
style.paddingRight =
|
|
410
|
-
|
|
413
|
+
style.paddingRight =
|
|
414
|
+
shaka.text.UITextDisplayer.convertLengthValue_(
|
|
415
|
+
cue.linePadding, cue, this.videoContainer_);
|
|
411
416
|
|
|
412
417
|
if (cue.backgroundImage) {
|
|
413
418
|
style.backgroundImage = 'url(\'' + cue.backgroundImage + '\')';
|
|
414
419
|
style.backgroundRepeat = 'no-repeat';
|
|
415
420
|
style.backgroundSize = 'contain';
|
|
416
421
|
style.backgroundPosition = 'center';
|
|
422
|
+
} else {
|
|
423
|
+
// If we have both text and nested cues, then style everything; otherwise
|
|
424
|
+
// place the text in its own <span> so the background doesn't fill the
|
|
425
|
+
// whole region.
|
|
426
|
+
let elem;
|
|
427
|
+
if (cue.nestedCues.length) {
|
|
428
|
+
elem = cueElement;
|
|
429
|
+
} else {
|
|
430
|
+
elem = shaka.util.Dom.createHTMLElement('span');
|
|
431
|
+
cueElement.appendChild(elem);
|
|
432
|
+
}
|
|
417
433
|
|
|
418
|
-
if (cue.
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
434
|
+
if (cue.border) {
|
|
435
|
+
elem.style.border = cue.border;
|
|
436
|
+
}
|
|
437
|
+
const bgColor = inherit((c) => c.backgroundColor);
|
|
438
|
+
if (bgColor) {
|
|
439
|
+
elem.style.backgroundColor = bgColor;
|
|
440
|
+
} else if (text) {
|
|
441
|
+
// If there is no background, default to a semi-transparent black.
|
|
442
|
+
// Only do this for the text itself.
|
|
443
|
+
elem.style.backgroundColor = 'rgba(0, 0, 0, 0.8)';
|
|
444
|
+
}
|
|
445
|
+
if (text) {
|
|
446
|
+
elem.textContent = text;
|
|
423
447
|
}
|
|
424
448
|
}
|
|
425
449
|
|
|
@@ -472,20 +496,28 @@ shaka.text.UITextDisplayer = class {
|
|
|
472
496
|
// TODO: Implement lineAlignment of 'CENTER'.
|
|
473
497
|
if (cue.line) {
|
|
474
498
|
if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
|
|
499
|
+
// When setting absolute positioning, you need to set x/y/width/height
|
|
500
|
+
// so the element is positioned correctly. Set these as default and
|
|
501
|
+
// other settings will override them.
|
|
475
502
|
style.position = 'absolute';
|
|
503
|
+
style.left = '0';
|
|
504
|
+
style.top = '0';
|
|
476
505
|
if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
|
|
506
|
+
style.width = '100%';
|
|
477
507
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
478
508
|
style.top = cue.line + '%';
|
|
479
509
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
480
510
|
style.bottom = cue.line + '%';
|
|
481
511
|
}
|
|
482
512
|
} else if (cue.writingMode == Cue.writingMode.VERTICAL_LEFT_TO_RIGHT) {
|
|
513
|
+
style.height = '100%';
|
|
483
514
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
484
515
|
style.left = cue.line + '%';
|
|
485
516
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
486
517
|
style.right = cue.line + '%';
|
|
487
518
|
}
|
|
488
519
|
} else {
|
|
520
|
+
style.height = '100%';
|
|
489
521
|
if (cue.lineAlign == Cue.lineAlign.START) {
|
|
490
522
|
style.right = cue.line + '%';
|
|
491
523
|
} else if (cue.lineAlign == Cue.lineAlign.END) {
|
|
@@ -493,12 +525,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
493
525
|
}
|
|
494
526
|
}
|
|
495
527
|
}
|
|
496
|
-
} else if (cue.region && cue.region.id
|
|
497
|
-
((!isNested && !isLeaf) || (cue.backgroundImage))) {
|
|
498
|
-
// In text-base cues, regions are only applied to block container
|
|
499
|
-
// (!isNested && !isLeaf).
|
|
500
|
-
// In bitmap-based cues, region settings are used to specify the size and
|
|
501
|
-
// position of the backgroundImage.
|
|
528
|
+
} else if (cue.region && cue.region.id) {
|
|
502
529
|
const percentageUnit = shaka.text.CueRegion.units.PERCENTAGE;
|
|
503
530
|
const heightUnit = cue.region.heightUnits == percentageUnit ? '%' : 'px';
|
|
504
531
|
const widthUnit = cue.region.widthUnits == percentageUnit ? '%' : 'px';
|
|
@@ -631,4 +658,25 @@ shaka.text.UITextDisplayer = class {
|
|
|
631
658
|
|
|
632
659
|
return (containerHeight * value / cue.cellResolution.rows) + 'px';
|
|
633
660
|
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Inherits a property from the parent Cue elements. If the value is falsy,
|
|
664
|
+
* it is assumed to be inherited from the parent. This returns null if the
|
|
665
|
+
* value isn't found.
|
|
666
|
+
*
|
|
667
|
+
* @param {!Array.<!shaka.extern.Cue>} parents
|
|
668
|
+
* @param {function(!shaka.extern.Cue):?T} cb
|
|
669
|
+
* @return {?T}
|
|
670
|
+
* @template T
|
|
671
|
+
* @private
|
|
672
|
+
*/
|
|
673
|
+
static inheritProperty_(parents, cb) {
|
|
674
|
+
for (let i = parents.length - 1; i >= 0; i--) {
|
|
675
|
+
const val = cb(parents[i]);
|
|
676
|
+
if (val || val === 0) {
|
|
677
|
+
return val;
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return null;
|
|
681
|
+
}
|
|
634
682
|
};
|
package/lib/util/fake_event.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.FakeEvent');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
/**
|
|
11
13
|
* @summary Create an Event work-alike object based on the provided dictionary.
|
|
@@ -16,18 +18,62 @@ goog.provide('shaka.util.FakeEvent');
|
|
|
16
18
|
*/
|
|
17
19
|
shaka.util.FakeEvent = class {
|
|
18
20
|
/**
|
|
19
|
-
* @param {
|
|
20
|
-
* @
|
|
21
|
+
* @param {!Event} event
|
|
22
|
+
* @return {!shaka.util.FakeEvent}
|
|
21
23
|
*/
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
for (const key in
|
|
25
|
-
Object.defineProperty(
|
|
26
|
-
value:
|
|
24
|
+
static fromRealEvent(event) {
|
|
25
|
+
const fakeEvent = new shaka.util.FakeEvent(event.type);
|
|
26
|
+
for (const key in event) {
|
|
27
|
+
Object.defineProperty(fakeEvent, key, {
|
|
28
|
+
value: event[key],
|
|
27
29
|
writable: true,
|
|
28
30
|
enumerable: true,
|
|
29
31
|
});
|
|
30
32
|
}
|
|
33
|
+
return fakeEvent;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Allows us to tell the compiler that the dictionary "map" is actually a
|
|
38
|
+
* generic object, for backwards compatibility.
|
|
39
|
+
* @param {!Map.<string, Object>} dict
|
|
40
|
+
* @return {!Object}
|
|
41
|
+
* @suppress {invalidCasts}
|
|
42
|
+
* @private
|
|
43
|
+
*/
|
|
44
|
+
static recastDictAsObject_(dict) {
|
|
45
|
+
goog.asserts.assert(!(dict instanceof Map), 'dict should not be a map');
|
|
46
|
+
return /** @type {!Object} */ (dict);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @param {string} type
|
|
51
|
+
* @param {Map.<string, Object>=} dict
|
|
52
|
+
*/
|
|
53
|
+
constructor(type, dict) {
|
|
54
|
+
if (dict) {
|
|
55
|
+
if (dict instanceof Map) {
|
|
56
|
+
// Take properties from dict if present.
|
|
57
|
+
for (const key of dict.keys()) {
|
|
58
|
+
Object.defineProperty(this, key, {
|
|
59
|
+
value: dict.get(key),
|
|
60
|
+
writable: true,
|
|
61
|
+
enumerable: true,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
// For backwards compatibility with external apps that may make use of
|
|
66
|
+
// this public constructor, this should still accept generic objects.
|
|
67
|
+
const obj = shaka.util.FakeEvent.recastDictAsObject_(dict);
|
|
68
|
+
for (const key in obj) {
|
|
69
|
+
Object.defineProperty(this, key, {
|
|
70
|
+
value: obj[key],
|
|
71
|
+
writable: true,
|
|
72
|
+
enumerable: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
31
77
|
|
|
32
78
|
// The properties below cannot be set by the dict. They are all provided
|
|
33
79
|
// for compatibility with native events.
|
package/lib/util/periods.js
CHANGED
|
@@ -80,7 +80,10 @@ shaka.util.PeriodCombiner = class {
|
|
|
80
80
|
|
|
81
81
|
/** @return {!Array.<shaka.extern.Stream>} */
|
|
82
82
|
getTextStreams() {
|
|
83
|
-
|
|
83
|
+
// Return a copy of the array because makeTextStreamsForClosedCaptions
|
|
84
|
+
// may make changes to the contents of the array. Those changes should not
|
|
85
|
+
// propagate back to the PeriodCombiner.
|
|
86
|
+
return this.textStreams_.slice();
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
/** @return {!Array.<shaka.extern.Stream>} */
|