shaka-player 3.1.3 → 3.2.2
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 +175 -3
- package/CONTRIBUTORS +3 -0
- package/README.md +4 -4
- package/build/generateExterns.js +1 -1
- package/build/types/polyfill +1 -0
- package/demo/common/asset.js +9 -7
- package/demo/common/assets.js +11 -2
- package/demo/common/message_ids.js +2 -1
- package/demo/config.js +5 -2
- package/demo/index.html +1 -1
- package/demo/locales/en.json +2 -1
- package/demo/locales/source.json +8 -4
- package/demo/main.js +2 -1
- package/dist/controls.css +2 -2
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +15 -15
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +15 -15
- package/dist/demo.compiled.map +2 -2
- package/dist/deps.js +22 -21
- package/dist/locales.js +36 -36
- package/dist/receiver.compiled.debug.js +9 -9
- package/dist/receiver.compiled.debug.map +2 -2
- package/dist/receiver.compiled.js +9 -9
- package/dist/receiver.compiled.map +2 -2
- package/dist/shaka-player.compiled.d.ts +58 -10
- package/dist/shaka-player.compiled.debug.d.ts +58 -10
- package/dist/shaka-player.compiled.debug.externs.js +777 -717
- package/dist/shaka-player.compiled.debug.js +639 -607
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +777 -717
- package/dist/shaka-player.compiled.js +826 -819
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +58 -10
- package/dist/shaka-player.ui.debug.d.ts +58 -10
- package/dist/shaka-player.ui.debug.externs.js +529 -469
- package/dist/shaka-player.ui.debug.js +676 -645
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +529 -469
- package/dist/shaka-player.ui.js +963 -955
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/fairplay.md +1 -1
- package/docs/tutorials/ui.md +5 -5
- package/externs/aria.js +6 -0
- package/externs/shaka/ads.js +24 -1
- package/externs/shaka/manifest.js +6 -1
- package/externs/shaka/offline.js +5 -0
- package/externs/shaka/player.js +53 -9
- package/externs/shaka/text.js +10 -10
- package/externs/texttrack.js +3 -0
- package/karma.conf.js +1 -0
- package/lib/ads/ad_manager.js +16 -15
- package/lib/ads/client_side_ad_manager.js +52 -88
- package/lib/ads/server_side_ad_manager.js +26 -12
- package/lib/cast/cast_proxy.js +11 -2
- package/lib/cast/cast_receiver.js +2 -1
- package/lib/cast/cast_sender.js +2 -2
- package/lib/cast/cast_utils.js +6 -1
- package/lib/cea/cea608_data_channel.js +56 -56
- package/lib/cea/cea608_memory.js +33 -33
- package/lib/cea/cea708_service.js +43 -43
- package/lib/cea/cea708_window.js +38 -38
- package/lib/cea/cea_decoder.js +13 -14
- package/lib/cea/cea_utils.js +23 -23
- package/lib/cea/dtvcc_packet_builder.js +7 -7
- package/lib/cea/i_caption_decoder.js +2 -3
- package/lib/cea/i_cea_parser.js +2 -2
- package/lib/cea/mp4_cea_parser.js +6 -6
- package/lib/dash/dash_parser.js +98 -2
- package/lib/dash/segment_list.js +1 -1
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +159 -29
- package/lib/media/closed_caption_parser.js +1 -2
- package/lib/media/drm_engine.js +169 -192
- package/lib/media/gap_jumping_controller.js +4 -2
- package/lib/media/media_source_engine.js +1 -2
- package/lib/media/segment_index.js +3 -1
- package/lib/media/segment_reference.js +19 -2
- package/lib/media/streaming_engine.js +8 -7
- package/lib/net/networking_engine.js +3 -1
- package/lib/offline/download_manager.js +1 -2
- package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
- package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
- package/lib/offline/manifest_converter.js +3 -1
- package/lib/offline/storage.js +12 -14
- package/lib/player.js +437 -167
- package/lib/polyfill/aria.js +82 -0
- 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/routing/walker.js +2 -3
- package/lib/text/mp4_ttml_parser.js +3 -0
- package/lib/text/srt_text_parser.js +4 -4
- package/lib/text/text_engine.js +16 -4
- package/lib/text/ttml_text_parser.js +5 -0
- package/lib/text/ui_text_displayer.js +85 -37
- package/lib/text/vtt_text_parser.js +2 -2
- package/lib/text/web_vtt_generator.js +55 -12
- package/lib/util/error.js +6 -0
- package/lib/util/fake_event.js +53 -7
- package/lib/util/manifest_parser_utils.js +1 -0
- package/lib/util/mp4_box_parsers.js +20 -20
- package/lib/util/multi_map.js +8 -0
- package/lib/util/periods.js +4 -1
- package/lib/util/platform.js +13 -1
- package/lib/util/player_configuration.js +13 -1
- package/lib/util/stream_utils.js +153 -85
- package/lib/util/xml_utils.js +31 -12
- package/package.json +1 -1
- package/roadmap.md +24 -22
- package/shaka-player.uncompiled.js +1 -0
- package/test/cast/cast_proxy_unit.js +12 -6
- package/test/cast/cast_utils_unit.js +2 -1
- package/test/cea/cea608_memory_unit.js +1 -1
- package/test/cea/cea708_service_unit.js +8 -8
- package/test/cea/cea708_window_unit.js +6 -6
- package/test/cea/cea_decoder_unit.js +1 -1
- package/test/cea/dtvcc_packet_builder_unit.js +2 -2
- package/test/dash/dash_parser_manifest_unit.js +230 -2
- package/test/hls/hls_parser_unit.js +255 -1
- package/test/media/closed_caption_parser_unit.js +6 -1
- package/test/media/drm_engine_integration.js +86 -12
- package/test/media/drm_engine_unit.js +353 -450
- package/test/offline/manifest_convert_unit.js +2 -0
- package/test/offline/storage_integration.js +9 -19
- package/test/player_integration.js +90 -0
- package/test/player_src_equals_integration.js +52 -0
- package/test/player_unit.js +11 -33
- 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/assets/chapters.srt +11 -0
- package/test/test/assets/chapters.vtt +10 -0
- package/test/test/util/cea_utils.js +12 -12
- package/test/test/util/fake_ad_manager.js +8 -1
- package/test/test/util/manifest_generator.js +2 -0
- package/test/test/util/manifest_parser_util.js +3 -1
- package/test/text/ttml_text_parser_unit.js +9 -0
- package/test/text/ui_text_displayer_unit.js +29 -17
- package/test/text/web_vtt_generator_unit.js +81 -7
- 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 +153 -10
- package/ui/airplay_button.js +1 -1
- package/ui/audio_language_selection.js +2 -5
- package/ui/cast_button.js +6 -9
- package/ui/constants.js +0 -3
- package/ui/controls.js +4 -6
- package/ui/fast_forward_button.js +2 -3
- package/ui/fullscreen_button.js +1 -3
- package/ui/language_utils.js +1 -1
- package/ui/less/containers.less +3 -7
- package/ui/locales/ru.json +6 -6
- package/ui/localization.js +28 -51
- package/ui/loop_button.js +1 -3
- package/ui/mute_button.js +1 -3
- package/ui/overflow_menu.js +2 -3
- package/ui/pip_button.js +7 -10
- package/ui/playback_rate_selection.js +3 -6
- package/ui/presentation_time.js +1 -3
- package/ui/resolution_selection.js +4 -7
- package/ui/rewind_button.js +2 -3
- package/ui/seek_bar.js +2 -4
- package/ui/spacer.js +1 -1
- package/ui/text_selection.js +8 -11
- package/ui/ui_utils.js +1 -1
- package/ui/volume_bar.js +1 -3
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.polyfill.Aria');
|
|
8
|
+
|
|
9
|
+
goog.require('shaka.log');
|
|
10
|
+
goog.require('shaka.polyfill');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @summary A polyfill to add support for the ARIAMixin interface mixin, for
|
|
14
|
+
* browsers that do not implement it (e.g. Firefox).
|
|
15
|
+
* Note that IE also does not support ARIAMixin, but this polyfill does not work
|
|
16
|
+
* for that platform, as it relies on getters and setters.
|
|
17
|
+
* @see https://w3c.github.io/aria/#ARIAMixin
|
|
18
|
+
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element
|
|
19
|
+
* @export
|
|
20
|
+
*/
|
|
21
|
+
shaka.polyfill.Aria = class {
|
|
22
|
+
/**
|
|
23
|
+
* Install the polyfill if needed.
|
|
24
|
+
* @export
|
|
25
|
+
*/
|
|
26
|
+
static install() {
|
|
27
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
28
|
+
if (Object.getOwnPropertyDescriptor(Element.prototype, 'ariaHidden')) {
|
|
29
|
+
shaka.log.info('Using native ARIAMixin interface.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
shaka.log.info('ARIAMixin interface not detected. Installing polyfill.');
|
|
33
|
+
|
|
34
|
+
// Define a list of all of the ARIAMixin properties that we have externs
|
|
35
|
+
// for.
|
|
36
|
+
const attributes = [
|
|
37
|
+
'ariaHidden',
|
|
38
|
+
'ariaLabel',
|
|
39
|
+
'ariaPressed',
|
|
40
|
+
'ariaSelected',
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
// Add each attribute, one by one.
|
|
44
|
+
for (const attribute of attributes) {
|
|
45
|
+
shaka.polyfill.Aria.addARIAMixinAttribute_(attribute);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Adds an attribute with the given name.
|
|
51
|
+
* @param {string} name The name of the attribute, in camelCase.
|
|
52
|
+
* @private
|
|
53
|
+
*/
|
|
54
|
+
static addARIAMixinAttribute_(name) {
|
|
55
|
+
const baseName = name.toLowerCase().replace(/^aria/, '');
|
|
56
|
+
// NOTE: All the attributes listed in the method above begin with "aria".
|
|
57
|
+
// However, to add extra protection against the possibility of XSS attacks
|
|
58
|
+
// through this method, this enforces "aria-" at the beginning of the
|
|
59
|
+
// snake-case name, even if somehow "aria" were missing from the input.
|
|
60
|
+
const snakeCaseName = `aria-${baseName}`;
|
|
61
|
+
|
|
62
|
+
/* eslint-disable no-restricted-syntax */
|
|
63
|
+
Object.defineProperty(Element.prototype, name, {
|
|
64
|
+
get() {
|
|
65
|
+
const element = /** @type {!Element} */ (this);
|
|
66
|
+
return element.getAttribute(snakeCaseName);
|
|
67
|
+
},
|
|
68
|
+
set(value) {
|
|
69
|
+
const element = /** @type {!Element} */ (this);
|
|
70
|
+
if (value == null || value == undefined) {
|
|
71
|
+
element.removeAttribute(snakeCaseName);
|
|
72
|
+
} else {
|
|
73
|
+
element.setAttribute(snakeCaseName, value);
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
/* eslint-enable no-restricted-syntax */
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
shaka.polyfill.register(shaka.polyfill.Aria.install);
|
|
@@ -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);
|
package/lib/routing/walker.js
CHANGED
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.routing.Walker');
|
|
8
|
-
goog.provide('shaka.routing.Walker.Implementation');
|
|
9
8
|
|
|
10
9
|
goog.require('goog.asserts');
|
|
11
10
|
goog.require('shaka.routing.Node');
|
|
@@ -81,7 +80,7 @@ shaka.routing.Walker = class {
|
|
|
81
80
|
* new work is added (and this is not null) it can be resolved. The only
|
|
82
81
|
* time when this should be non-null is when we are waiting for more work.
|
|
83
82
|
*
|
|
84
|
-
* @private {shaka.util.PublicPromise}
|
|
83
|
+
* @private {?shaka.util.PublicPromise}
|
|
85
84
|
*/
|
|
86
85
|
this.waitForWork_ = null;
|
|
87
86
|
|
|
@@ -91,7 +90,7 @@ shaka.routing.Walker = class {
|
|
|
91
90
|
/** @private {?shaka.routing.Walker.ActiveRoute_} */
|
|
92
91
|
this.currentRoute_ = null;
|
|
93
92
|
|
|
94
|
-
/** @private {shaka.util.AbortableOperation} */
|
|
93
|
+
/** @private {?shaka.util.AbortableOperation} */
|
|
95
94
|
this.currentStep_ = null;
|
|
96
95
|
|
|
97
96
|
/**
|
|
@@ -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());
|
|
@@ -57,8 +57,8 @@ shaka.text.SrtTextParser = class {
|
|
|
57
57
|
/**
|
|
58
58
|
* Convert a SRT format to WebVTT
|
|
59
59
|
*
|
|
60
|
-
* @param {
|
|
61
|
-
* @return {
|
|
60
|
+
* @param {string} data
|
|
61
|
+
* @return {string}
|
|
62
62
|
* @export
|
|
63
63
|
*/
|
|
64
64
|
static srt2webvtt(data) {
|
|
@@ -87,8 +87,8 @@ shaka.text.SrtTextParser = class {
|
|
|
87
87
|
/**
|
|
88
88
|
* Convert a SRT cue into WebVTT cue
|
|
89
89
|
*
|
|
90
|
-
* @param {
|
|
91
|
-
* @return {
|
|
90
|
+
* @param {string} caption
|
|
91
|
+
* @return {string}
|
|
92
92
|
* @private
|
|
93
93
|
*/
|
|
94
94
|
static convertSrtCue_(caption) {
|
package/lib/text/text_engine.js
CHANGED
|
@@ -12,7 +12,7 @@ goog.require('shaka.util.BufferUtils');
|
|
|
12
12
|
goog.require('shaka.util.Functional');
|
|
13
13
|
goog.require('shaka.util.IDestroyable');
|
|
14
14
|
goog.require('shaka.util.MimeUtils');
|
|
15
|
-
goog.requireType('shaka.cea.ICaptionDecoder
|
|
15
|
+
goog.requireType('shaka.cea.ICaptionDecoder');
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
// TODO: revisit this when Closure Compiler supports partially-exported classes.
|
|
@@ -24,7 +24,7 @@ goog.requireType('shaka.cea.ICaptionDecoder.ClosedCaption');
|
|
|
24
24
|
shaka.text.TextEngine = class {
|
|
25
25
|
/** @param {shaka.extern.TextDisplayer} displayer */
|
|
26
26
|
constructor(displayer) {
|
|
27
|
-
/** @private {shaka.extern.TextParser} */
|
|
27
|
+
/** @private {?shaka.extern.TextParser} */
|
|
28
28
|
this.parser_ = null;
|
|
29
29
|
|
|
30
30
|
/** @private {shaka.extern.TextDisplayer} */
|
|
@@ -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
|
};
|
|
@@ -464,8 +464,8 @@ shaka.text.VttTextParser = class {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
/**
|
|
467
|
-
* @param {
|
|
468
|
-
* @param {
|
|
467
|
+
* @param {string} value
|
|
468
|
+
* @param {string} defaultValue
|
|
469
469
|
* @private
|
|
470
470
|
*/
|
|
471
471
|
static getOrDefault_(value, defaultValue) {
|
|
@@ -17,9 +17,10 @@ goog.require('shaka.text.Cue');
|
|
|
17
17
|
shaka.text.WebVttGenerator = class {
|
|
18
18
|
/**
|
|
19
19
|
* @param {!Array.<!shaka.text.Cue>} cues
|
|
20
|
+
* @param {!Array.<!shaka.extern.AdCuePoint>} adCuePoints
|
|
20
21
|
* @return {string}
|
|
21
22
|
*/
|
|
22
|
-
static convert(cues) {
|
|
23
|
+
static convert(cues, adCuePoints) {
|
|
23
24
|
// Flatten nested cue payloads recursively. If a cue has nested cues,
|
|
24
25
|
// their contents should be combined and replace the payload of the parent.
|
|
25
26
|
const flattenPayload = (cue) => {
|
|
@@ -64,6 +65,25 @@ shaka.text.WebVttGenerator = class {
|
|
|
64
65
|
}
|
|
65
66
|
};
|
|
66
67
|
|
|
68
|
+
const webvttTimeString = (time) => {
|
|
69
|
+
let newTime = time;
|
|
70
|
+
for (const adCuePoint of adCuePoints) {
|
|
71
|
+
if (adCuePoint.end && adCuePoint.start < time) {
|
|
72
|
+
const offset = adCuePoint.end - adCuePoint.start;
|
|
73
|
+
newTime += offset;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
const hours = Math.floor(newTime / 3600);
|
|
77
|
+
const minutes = Math.floor(newTime / 60 % 60);
|
|
78
|
+
const seconds = Math.floor(newTime % 60);
|
|
79
|
+
const milliseconds = Math.floor(newTime * 1000 % 1000);
|
|
80
|
+
return (hours < 10 ? '0' : '') + hours + ':' +
|
|
81
|
+
(minutes < 10 ? '0' : '') + minutes + ':' +
|
|
82
|
+
(seconds < 10 ? '0' : '') + seconds + '.' +
|
|
83
|
+
(milliseconds < 100 ? (milliseconds < 10 ? '00' : '0') : '') +
|
|
84
|
+
milliseconds;
|
|
85
|
+
};
|
|
86
|
+
|
|
67
87
|
// We don't want to modify the array or objects passed in, since we don't
|
|
68
88
|
// technically own them. So we build a new array and replace certain items
|
|
69
89
|
// in it if they need to be flattened.
|
|
@@ -80,19 +100,42 @@ shaka.text.WebVttGenerator = class {
|
|
|
80
100
|
|
|
81
101
|
let webvttString = 'WEBVTT\n\n';
|
|
82
102
|
for (const cue of flattenedCues) {
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
(
|
|
92
|
-
|
|
103
|
+
const webvttSettings = (cue) => {
|
|
104
|
+
const settings = [];
|
|
105
|
+
const Cue = shaka.text.Cue;
|
|
106
|
+
switch (cue.textAlign) {
|
|
107
|
+
case Cue.textAlign.LEFT:
|
|
108
|
+
settings.push('align:left');
|
|
109
|
+
break;
|
|
110
|
+
case Cue.textAlign.RIGHT:
|
|
111
|
+
settings.push('align:right');
|
|
112
|
+
break;
|
|
113
|
+
case Cue.textAlign.CENTER:
|
|
114
|
+
settings.push('align:middle');
|
|
115
|
+
break;
|
|
116
|
+
case Cue.textAlign.START:
|
|
117
|
+
settings.push('align:start');
|
|
118
|
+
break;
|
|
119
|
+
case Cue.textAlign.END:
|
|
120
|
+
settings.push('align:end');
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
switch (cue.writingMode) {
|
|
124
|
+
case Cue.writingMode.VERTICAL_LEFT_TO_RIGHT:
|
|
125
|
+
settings.push('vertical:lr');
|
|
126
|
+
break;
|
|
127
|
+
case Cue.writingMode.VERTICAL_RIGHT_TO_LEFT:
|
|
128
|
+
settings.push('vertical:rl');
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (settings.length) {
|
|
133
|
+
return ' ' + settings.join(' ');
|
|
134
|
+
}
|
|
135
|
+
return '';
|
|
93
136
|
};
|
|
94
137
|
webvttString += webvttTimeString(cue.startTime) + ' --> ' +
|
|
95
|
-
webvttTimeString(cue.endTime) + '\n';
|
|
138
|
+
webvttTimeString(cue.endTime) + webvttSettings(cue) + '\n';
|
|
96
139
|
webvttString += cue.payload + '\n\n';
|
|
97
140
|
}
|
|
98
141
|
return webvttString;
|
package/lib/util/error.js
CHANGED
|
@@ -814,6 +814,12 @@ shaka.util.Error.Code = {
|
|
|
814
814
|
*/
|
|
815
815
|
'INIT_DATA_TRANSFORM_ERROR': 6016,
|
|
816
816
|
|
|
817
|
+
/**
|
|
818
|
+
* The server certificate request failed.
|
|
819
|
+
* <br> error.data[0] is a shaka.util.Error from the networking engine.
|
|
820
|
+
*/
|
|
821
|
+
'SERVER_CERTIFICATE_REQUEST_FAILED': 6017,
|
|
822
|
+
|
|
817
823
|
|
|
818
824
|
/**
|
|
819
825
|
* The call to Player.load() was interrupted by a call to Player.unload()
|