shaka-player 4.3.0 → 4.3.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/.github/workflows/build-and-test.yaml +20 -0
- package/AUTHORS +2 -0
- package/CHANGELOG.md +44 -0
- package/CONTRIBUTORS +2 -0
- package/README.md +1 -1
- package/conditional/dummy_cast_proxy.js +120 -0
- package/dist/demo.compiled.debug.js +104 -110
- package/dist/demo.compiled.debug.map +3 -3
- package/dist/demo.compiled.js +104 -110
- package/dist/demo.compiled.map +3 -3
- package/dist/deps.js +8 -6
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +54 -60
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +54 -60
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +19 -2
- package/dist/shaka-player.compiled.debug.d.ts +19 -2
- package/dist/shaka-player.compiled.debug.externs.js +102 -80
- package/dist/shaka-player.compiled.debug.js +657 -652
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +102 -80
- package/dist/shaka-player.compiled.js +904 -901
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +19 -2
- package/dist/shaka-player.ui.debug.d.ts +19 -2
- package/dist/shaka-player.ui.debug.externs.js +102 -80
- package/dist/shaka-player.ui.debug.js +703 -698
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +102 -80
- package/dist/shaka-player.ui.js +1029 -1026
- package/dist/shaka-player.ui.map +4 -4
- package/docs/tutorials/welcome.md +2 -2
- package/externs/shaka/mp4_parser.js +3 -0
- package/externs/shaka/player.js +2 -1
- package/lib/ads/ad_manager.js +2 -2
- package/lib/cea/cea708_window.js +2 -2
- package/lib/cea/mp4_cea_parser.js +37 -16
- package/lib/dash/segment_template.js +1 -1
- package/lib/hls/hls_parser.js +49 -23
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +28 -7
- package/lib/media/segment_reference.js +3 -0
- package/lib/media/streaming_engine.js +33 -10
- package/lib/media/transmuxer.js +6 -4
- package/lib/player.js +10 -6
- package/lib/polyfill/abort_controller.js +143 -0
- package/lib/polyfill/media_capabilities.js +65 -11
- package/lib/polyfill/patchedmediakeys_apple.js +33 -28
- package/lib/text/ttml_text_parser.js +32 -7
- package/lib/text/ui_text_displayer.js +17 -0
- package/lib/util/mp4_box_parsers.js +24 -0
- package/lib/util/mp4_parser.js +35 -0
- package/lib/util/player_configuration.js +10 -3
- package/lib/util/stream_utils.js +35 -13
- package/package.json +4 -4
- package/shaka-player.uncompiled.js +1 -0
- package/test/ads/ad_manager_unit.js +3 -1
- package/test/dash/dash_parser_manifest_unit.js +1 -0
- package/test/hls/hls_parser_unit.js +132 -0
- package/test/media/drm_engine_unit.js +11 -1
- package/test/media/media_source_engine_integration.js +78 -2
- package/test/player_integration.js +10 -3
- package/test/polyfill/media_capabilities_unit.js +259 -0
- package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
- package/test/test/boot.js +29 -11
- package/test/test/util/manifest_parser_util.js +4 -3
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +6 -5
- package/test/test/util/util.js +3 -2
- package/test/text/mp4_ttml_parser_unit.js +6 -4
- package/test/text/ttml_text_parser_unit.js +119 -86
- package/test/text/ui_text_displayer_unit.js +76 -0
- package/test/util/stream_utils_unit.js +18 -0
- package/test/util/xml_utils_unit.js +6 -1
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/controls.js +49 -36
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
goog.provide('shaka.polyfill.AbortController');
|
|
8
|
+
|
|
9
|
+
goog.require('shaka.polyfill');
|
|
10
|
+
goog.require('shaka.util.FakeEvent');
|
|
11
|
+
goog.require('shaka.util.FakeEventTarget');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @summary A polyfill for systems that do not implement AbortController.
|
|
15
|
+
* This is used both with the fetch API for HTTP requests and inside the HLS
|
|
16
|
+
* parser.
|
|
17
|
+
* @export
|
|
18
|
+
* @extends AbortController
|
|
19
|
+
*/
|
|
20
|
+
shaka.polyfill.AbortController = class {
|
|
21
|
+
/**
|
|
22
|
+
* Install the polyfill if needed.
|
|
23
|
+
* @export
|
|
24
|
+
*/
|
|
25
|
+
static install() {
|
|
26
|
+
if (window.AbortController) {
|
|
27
|
+
// Not needed.
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
window.AbortController = shaka.polyfill.AbortController;
|
|
32
|
+
window.AbortSignal = shaka.polyfill.AbortController.AbortSignal;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** */
|
|
36
|
+
constructor() {
|
|
37
|
+
/** @private {!shaka.polyfill.AbortController.AbortSignal} */
|
|
38
|
+
this.signal_ = new shaka.polyfill.AbortController.AbortSignal();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @override
|
|
43
|
+
* @suppress {const|duplicate} Since the original is defined as "const", we
|
|
44
|
+
* need this suppression to override it.
|
|
45
|
+
*/
|
|
46
|
+
get signal() {
|
|
47
|
+
return this.signal_;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {*=} reason
|
|
52
|
+
* @override
|
|
53
|
+
*/
|
|
54
|
+
abort(reason) {
|
|
55
|
+
this.signal_.doAbort_(reason);
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @summary A polyfill for AbortSignal, part of the AbortController API.
|
|
61
|
+
* @implements {AbortSignal}
|
|
62
|
+
*/
|
|
63
|
+
shaka.polyfill.AbortController.AbortSignal =
|
|
64
|
+
class extends shaka.util.FakeEventTarget {
|
|
65
|
+
/** */
|
|
66
|
+
constructor() {
|
|
67
|
+
super();
|
|
68
|
+
|
|
69
|
+
/** @private {boolean} */
|
|
70
|
+
this.aborted_ = false;
|
|
71
|
+
|
|
72
|
+
/** @private {*} */
|
|
73
|
+
this.reason_ = undefined;
|
|
74
|
+
|
|
75
|
+
/** @type {?function(!Event)} */
|
|
76
|
+
this.onabort = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** @override */
|
|
80
|
+
get aborted() {
|
|
81
|
+
return this.aborted_;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** @return {*} */
|
|
85
|
+
get reason() {
|
|
86
|
+
return this.reason_;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* @param {*} reason
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
doAbort_(reason) {
|
|
94
|
+
if (this.aborted_) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this.aborted_ = true;
|
|
99
|
+
this.reason_ = reason;
|
|
100
|
+
if (this.reason_ === undefined) {
|
|
101
|
+
// This is equivalent to a native implementation.
|
|
102
|
+
this.reason_ = new DOMException(
|
|
103
|
+
'signal is aborted without reason', 'AbortError');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// According to MDN:
|
|
107
|
+
// "Event type - A generic Event with no added properties."
|
|
108
|
+
const event = new shaka.util.FakeEvent('abort');
|
|
109
|
+
if (this.onabort) {
|
|
110
|
+
this.onabort(event);
|
|
111
|
+
}
|
|
112
|
+
this.dispatchEvent(event);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* @param {*=} reason
|
|
118
|
+
* @return {!AbortSignal}
|
|
119
|
+
*/
|
|
120
|
+
static abort(reason) {
|
|
121
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
122
|
+
signal.doAbort_(reason);
|
|
123
|
+
return signal;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {number} timeMs
|
|
128
|
+
* @return {!AbortSignal}
|
|
129
|
+
*/
|
|
130
|
+
static timeout(timeMs) {
|
|
131
|
+
const signal = new shaka.polyfill.AbortController.AbortSignal();
|
|
132
|
+
|
|
133
|
+
window.setTimeout(() => {
|
|
134
|
+
// This is equivalent to a native implementation.
|
|
135
|
+
signal.doAbort_(new DOMException('signal timed out', 'TimeoutError'));
|
|
136
|
+
}, timeMs);
|
|
137
|
+
|
|
138
|
+
return signal;
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
shaka.polyfill.register(shaka.polyfill.AbortController.install);
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
goog.provide('shaka.polyfill.MediaCapabilities');
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.log');
|
|
10
|
+
goog.require('shaka.media.Capabilities');
|
|
10
11
|
goog.require('shaka.polyfill');
|
|
12
|
+
goog.require('shaka.util.Error');
|
|
11
13
|
goog.require('shaka.util.Platform');
|
|
12
14
|
|
|
13
15
|
|
|
@@ -96,37 +98,51 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
96
98
|
return res;
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
const videoConfig = mediaDecodingConfig['video'];
|
|
102
|
+
const audioConfig = mediaDecodingConfig['audio'];
|
|
103
|
+
|
|
104
|
+
const Capabilities = shaka.media.Capabilities;
|
|
105
|
+
|
|
99
106
|
if (mediaDecodingConfig.type == 'media-source') {
|
|
100
107
|
if (!shaka.util.Platform.supportsMediaSource()) {
|
|
101
108
|
return res;
|
|
102
109
|
}
|
|
103
|
-
// Use '
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
110
|
+
// Use 'shaka.media.Capabilities.isTypeSupported'to check if
|
|
111
|
+
// the stream is supported.
|
|
112
|
+
// Cast platforms will additionally check canDisplayType(), which
|
|
113
|
+
// accepts extended MIME type parameters.
|
|
114
|
+
// See: https://github.com/shaka-project/shaka-player/issues/4726
|
|
115
|
+
if (videoConfig) {
|
|
116
|
+
let isSupported;
|
|
117
|
+
if (shaka.util.Platform.isChromecast()) {
|
|
118
|
+
isSupported =
|
|
119
|
+
shaka.polyfill.MediaCapabilities.canCastDisplayType_(videoConfig);
|
|
120
|
+
} else {
|
|
121
|
+
isSupported = Capabilities.isTypeSupported(videoConfig.contentType);
|
|
122
|
+
}
|
|
107
123
|
if (!isSupported) {
|
|
108
124
|
return res;
|
|
109
125
|
}
|
|
110
126
|
}
|
|
111
127
|
|
|
112
|
-
if (
|
|
113
|
-
const contentType =
|
|
114
|
-
const isSupported =
|
|
128
|
+
if (audioConfig) {
|
|
129
|
+
const contentType = audioConfig.contentType;
|
|
130
|
+
const isSupported = Capabilities.isTypeSupported(contentType);
|
|
115
131
|
if (!isSupported) {
|
|
116
132
|
return res;
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
} else if (mediaDecodingConfig.type == 'file') {
|
|
120
|
-
if (
|
|
121
|
-
const contentType =
|
|
136
|
+
if (videoConfig) {
|
|
137
|
+
const contentType = videoConfig.contentType;
|
|
122
138
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
123
139
|
if (!isSupported) {
|
|
124
140
|
return res;
|
|
125
141
|
}
|
|
126
142
|
}
|
|
127
143
|
|
|
128
|
-
if (
|
|
129
|
-
const contentType =
|
|
144
|
+
if (audioConfig) {
|
|
145
|
+
const contentType = audioConfig.contentType;
|
|
130
146
|
const isSupported = shaka.util.Platform.supportsMediaType(contentType);
|
|
131
147
|
if (!isSupported) {
|
|
132
148
|
return res;
|
|
@@ -201,6 +217,44 @@ shaka.polyfill.MediaCapabilities = class {
|
|
|
201
217
|
|
|
202
218
|
return res;
|
|
203
219
|
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Checks if the given media parameters of the video or audio streams are
|
|
223
|
+
* supported by the Cast platform.
|
|
224
|
+
* @param {!VideoConfiguration} videoConfig The 'video' field of the
|
|
225
|
+
* MediaDecodingConfiguration.
|
|
226
|
+
* @return {boolean} `true` when the stream can be displayed on a Cast device.
|
|
227
|
+
* @private
|
|
228
|
+
*/
|
|
229
|
+
static canCastDisplayType_(videoConfig) {
|
|
230
|
+
if (!(window.cast)) {
|
|
231
|
+
shaka.log.error('Expected cast namespace to be available!');
|
|
232
|
+
throw new shaka.util.Error(
|
|
233
|
+
shaka.util.Error.Severity.CRITICAL,
|
|
234
|
+
shaka.util.Error.Category.CAST,
|
|
235
|
+
shaka.util.Error.Code.CAST_API_UNAVAILABLE);
|
|
236
|
+
} else if (!(cast.__platform__ && cast.__platform__.canDisplayType)) {
|
|
237
|
+
shaka.log.warning('Expected cast APIs to be available! Falling back to ' +
|
|
238
|
+
'shaka.media.Capabilities.isTypeSupported() for type support.');
|
|
239
|
+
return shaka.media.Capabilities.isTypeSupported(videoConfig.contentType);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
let displayType = videoConfig.contentType;
|
|
243
|
+
if (videoConfig.width && videoConfig.height) {
|
|
244
|
+
displayType +=
|
|
245
|
+
`; width=${videoConfig.width}; height=${videoConfig.height}`;
|
|
246
|
+
}
|
|
247
|
+
if (videoConfig.framerate) {
|
|
248
|
+
displayType += `; framerate=${videoConfig.framerate}`;
|
|
249
|
+
}
|
|
250
|
+
if (videoConfig.transferFunction === 'pq') {
|
|
251
|
+
// A "PQ" transfer function indicates this is an HDR-capable stream;
|
|
252
|
+
// "smpte2084" is the published standard. We need to inform the platform
|
|
253
|
+
// this query is specifically for HDR.
|
|
254
|
+
displayType += '; eotf=smpte2084';
|
|
255
|
+
}
|
|
256
|
+
return cast.__platform__.canDisplayType(displayType);
|
|
257
|
+
}
|
|
204
258
|
};
|
|
205
259
|
|
|
206
260
|
/**
|
|
@@ -27,38 +27,39 @@ goog.require('shaka.util.StringUtils');
|
|
|
27
27
|
shaka.polyfill.PatchedMediaKeysApple = class {
|
|
28
28
|
/**
|
|
29
29
|
* Installs the polyfill if needed.
|
|
30
|
-
* @param {boolean} enableUninstall enables uninstalling the polyfill
|
|
30
|
+
* @param {boolean=} enableUninstall enables uninstalling the polyfill
|
|
31
31
|
* @export
|
|
32
32
|
*/
|
|
33
33
|
static install(enableUninstall = false) {
|
|
34
|
+
// Alias
|
|
35
|
+
const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
|
|
36
|
+
|
|
34
37
|
if (!window.HTMLVideoElement || !window.WebKitMediaKeys) {
|
|
35
38
|
// No HTML5 video or no prefixed EME.
|
|
36
39
|
return;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
if (enableUninstall) {
|
|
40
|
-
|
|
41
|
-
|
|
43
|
+
PatchedMediaKeysApple.enableUninstall = true;
|
|
44
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys =
|
|
42
45
|
/** @type {!Object} */ (
|
|
43
46
|
Object.getOwnPropertyDescriptor(
|
|
44
47
|
// eslint-disable-next-line no-restricted-syntax
|
|
45
48
|
HTMLMediaElement.prototype, 'mediaKeys',
|
|
46
49
|
)
|
|
47
50
|
);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
.prototype.setMediaKeys;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
51
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys =
|
|
52
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
53
|
+
HTMLMediaElement.prototype.setMediaKeys;
|
|
54
|
+
PatchedMediaKeysApple.originalWindowMediaKeys = window.MediaKeys;
|
|
55
|
+
PatchedMediaKeysApple.originalWindowMediaKeySystemAccess =
|
|
56
|
+
window.MediaKeySystemAccess;
|
|
57
|
+
PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess =
|
|
58
|
+
navigator.requestMediaKeySystemAccess;
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
shaka.log.info('Using Apple-prefixed EME');
|
|
58
62
|
|
|
59
|
-
// Alias
|
|
60
|
-
const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
|
|
61
|
-
|
|
62
63
|
// Delete mediaKeys to work around strict mode compatibility issues.
|
|
63
64
|
// eslint-disable-next-line no-restricted-syntax
|
|
64
65
|
delete HTMLMediaElement.prototype['mediaKeys'];
|
|
@@ -83,32 +84,36 @@ shaka.polyfill.PatchedMediaKeysApple = class {
|
|
|
83
84
|
* @export
|
|
84
85
|
*/
|
|
85
86
|
static uninstall() {
|
|
86
|
-
|
|
87
|
+
// Alias
|
|
88
|
+
const PatchedMediaKeysApple = shaka.polyfill.PatchedMediaKeysApple;
|
|
89
|
+
|
|
90
|
+
if (!PatchedMediaKeysApple.enableUninstall) {
|
|
87
91
|
return;
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
shaka.log.info('Un-installing Apple-prefixed EME');
|
|
91
95
|
|
|
92
|
-
|
|
96
|
+
PatchedMediaKeysApple.enableUninstall = false;
|
|
93
97
|
Object.defineProperty(
|
|
94
98
|
// eslint-disable-next-line no-restricted-syntax
|
|
95
99
|
HTMLMediaElement.prototype,
|
|
96
100
|
'mediaKeys',
|
|
97
|
-
|
|
101
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys,
|
|
98
102
|
);
|
|
99
103
|
// eslint-disable-next-line no-restricted-syntax
|
|
100
|
-
HTMLMediaElement.prototype.setMediaKeys =
|
|
101
|
-
.originalHTMLMediaElementPrototypeSetMediaKeys;
|
|
102
|
-
window.MediaKeys =
|
|
103
|
-
window.MediaKeySystemAccess =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
104
|
+
HTMLMediaElement.prototype.setMediaKeys =
|
|
105
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys;
|
|
106
|
+
window.MediaKeys = PatchedMediaKeysApple.originalWindowMediaKeys;
|
|
107
|
+
window.MediaKeySystemAccess =
|
|
108
|
+
PatchedMediaKeysApple.originalWindowMediaKeySystemAccess;
|
|
109
|
+
navigator.requestMediaKeySystemAccess =
|
|
110
|
+
PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess;
|
|
111
|
+
|
|
112
|
+
PatchedMediaKeysApple.originalWindowMediaKeys = null;
|
|
113
|
+
PatchedMediaKeysApple.originalWindowMediaKeySystemAccess = null;
|
|
114
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeSetMediaKeys = null;
|
|
115
|
+
PatchedMediaKeysApple.originalNavigatorRequestMediaKeySystemAccess = null;
|
|
116
|
+
PatchedMediaKeysApple.originalHTMLMediaElementPrototypeMediaKeys = null;
|
|
112
117
|
|
|
113
118
|
window.shakaMediaKeysPolyfill = false;
|
|
114
119
|
}
|
|
@@ -136,7 +136,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
136
136
|
}
|
|
137
137
|
|
|
138
138
|
const cue = TtmlTextParser.parseCue_(
|
|
139
|
-
body, time
|
|
139
|
+
body, time, rateInfo, metadataElements, styles,
|
|
140
140
|
regionElements, cueRegions, whitespaceTrim,
|
|
141
141
|
cellResolutionInfo, /* parentCueElement= */ null,
|
|
142
142
|
/* isContent= */ false);
|
|
@@ -157,7 +157,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
157
157
|
* Parses a TTML node into a Cue.
|
|
158
158
|
*
|
|
159
159
|
* @param {!Node} cueNode
|
|
160
|
-
* @param {
|
|
160
|
+
* @param {shaka.extern.TextParser.TimeContext} timeContext
|
|
161
161
|
* @param {!shaka.text.TtmlTextParser.RateInfo_} rateInfo
|
|
162
162
|
* @param {!Array.<!Element>} metadataElements
|
|
163
163
|
* @param {!Array.<!Element>} styles
|
|
@@ -171,7 +171,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
171
171
|
* @private
|
|
172
172
|
*/
|
|
173
173
|
static parseCue_(
|
|
174
|
-
cueNode,
|
|
174
|
+
cueNode, timeContext, rateInfo, metadataElements, styles, regionElements,
|
|
175
175
|
cueRegions, whitespaceTrim, cellResolution, parentCueElement, isContent) {
|
|
176
176
|
/** @type {Element} */
|
|
177
177
|
let cueElement;
|
|
@@ -236,7 +236,7 @@ shaka.text.TtmlTextParser = class {
|
|
|
236
236
|
for (const childNode of cueElement.childNodes) {
|
|
237
237
|
const nestedCue = shaka.text.TtmlTextParser.parseCue_(
|
|
238
238
|
childNode,
|
|
239
|
-
|
|
239
|
+
timeContext,
|
|
240
240
|
rateInfo,
|
|
241
241
|
metadataElements,
|
|
242
242
|
styles,
|
|
@@ -293,15 +293,20 @@ shaka.text.TtmlTextParser = class {
|
|
|
293
293
|
if (start == null) {
|
|
294
294
|
start = 0;
|
|
295
295
|
}
|
|
296
|
-
start +=
|
|
296
|
+
start += timeContext.periodStart;
|
|
297
297
|
|
|
298
298
|
// If end is null, that means the duration is effectively infinite.
|
|
299
299
|
if (end == null) {
|
|
300
300
|
end = Infinity;
|
|
301
301
|
} else {
|
|
302
|
-
end +=
|
|
302
|
+
end += timeContext.periodStart;
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
+
// Clip times to segment boundaries.
|
|
306
|
+
// https://github.com/shaka-project/shaka-player/issues/4631
|
|
307
|
+
start = Math.max(start, timeContext.segmentStart);
|
|
308
|
+
end = Math.min(end, timeContext.segmentEnd);
|
|
309
|
+
|
|
305
310
|
if (!hasTimeAttributes && nestedCues.length > 0) {
|
|
306
311
|
// If no time is defined for this cue, base the timing information on
|
|
307
312
|
// the time of the nested cues. In the case of multiple nested cues with
|
|
@@ -569,8 +574,28 @@ shaka.text.TtmlTextParser = class {
|
|
|
569
574
|
|
|
570
575
|
const fontFamily = TtmlTextParser.getStyleAttribute_(
|
|
571
576
|
cueElement, region, styles, 'fontFamily', shouldInheritRegionStyles);
|
|
577
|
+
// See https://github.com/sandflow/imscJS/blob/1.1.3/src/main/js/html.js#L1384
|
|
572
578
|
if (fontFamily) {
|
|
573
|
-
|
|
579
|
+
switch (fontFamily) {
|
|
580
|
+
case 'monospaceSerif':
|
|
581
|
+
cue.fontFamily = 'Courier New,Liberation Mono,Courier,monospace';
|
|
582
|
+
break;
|
|
583
|
+
case 'proportionalSansSerif':
|
|
584
|
+
cue.fontFamily = 'Arial,Helvetica,Liberation Sans,sans-serif';
|
|
585
|
+
break;
|
|
586
|
+
case 'sansSerif':
|
|
587
|
+
cue.fontFamily = 'sans-serif';
|
|
588
|
+
break;
|
|
589
|
+
case 'monospaceSansSerif':
|
|
590
|
+
cue.fontFamily = 'Consolas,monospace';
|
|
591
|
+
break;
|
|
592
|
+
case 'proportionalSerif':
|
|
593
|
+
cue.fontFamily = 'serif';
|
|
594
|
+
break;
|
|
595
|
+
default:
|
|
596
|
+
cue.fontFamily = fontFamily;
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
574
599
|
}
|
|
575
600
|
|
|
576
601
|
const fontWeight = TtmlTextParser.getStyleAttribute_(
|
|
@@ -199,6 +199,19 @@ shaka.text.UITextDisplayer = class {
|
|
|
199
199
|
this.isTextVisible_ = on;
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
/**
|
|
203
|
+
* @private
|
|
204
|
+
*/
|
|
205
|
+
isElementUnderTextContainer_(elemToCheck) {
|
|
206
|
+
while (elemToCheck != null) {
|
|
207
|
+
if (elemToCheck == this.textContainer_) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
elemToCheck = elemToCheck.parentElement;
|
|
211
|
+
}
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
202
215
|
/**
|
|
203
216
|
* @param {!Array.<!shaka.extern.Cue>} cues
|
|
204
217
|
* @param {!HTMLElement} container
|
|
@@ -260,6 +273,9 @@ shaka.text.UITextDisplayer = class {
|
|
|
260
273
|
cueRegistry = this.currentCuesMap_.get(cue);
|
|
261
274
|
wrapper = cueRegistry.wrapper;
|
|
262
275
|
updateDOM = true;
|
|
276
|
+
} else if (!this.isElementUnderTextContainer_(wrapper)) {
|
|
277
|
+
// We found that the wrapper needs to be in the DOM
|
|
278
|
+
updateDOM = true;
|
|
263
279
|
}
|
|
264
280
|
}
|
|
265
281
|
|
|
@@ -276,6 +292,7 @@ shaka.text.UITextDisplayer = class {
|
|
|
276
292
|
const topCue = parents.pop();
|
|
277
293
|
goog.asserts.assert(topCue == cue, 'Parent cues should be kept in order');
|
|
278
294
|
}
|
|
295
|
+
|
|
279
296
|
if (updateDOM) {
|
|
280
297
|
for (const element of toUproot) {
|
|
281
298
|
// NOTE: Because we uproot shared region elements, too, we might hit an
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
goog.provide('shaka.util.Mp4BoxParsers');
|
|
8
8
|
|
|
9
9
|
goog.require('shaka.util.DataViewReader');
|
|
10
|
+
goog.require('shaka.util.Mp4Parser');
|
|
10
11
|
|
|
11
12
|
shaka.util.Mp4BoxParsers = class {
|
|
12
13
|
/**
|
|
@@ -188,6 +189,17 @@ shaka.util.Mp4BoxParsers = class {
|
|
|
188
189
|
trackId,
|
|
189
190
|
};
|
|
190
191
|
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Parses a FRMA box.
|
|
195
|
+
* @param {!shaka.util.DataViewReader} reader
|
|
196
|
+
* @return {!shaka.util.ParsedFRMABox}
|
|
197
|
+
*/
|
|
198
|
+
static parseFRMA(reader) {
|
|
199
|
+
const fourcc = reader.readUint32();
|
|
200
|
+
const codec = shaka.util.Mp4Parser.typeToString(fourcc);
|
|
201
|
+
return {codec};
|
|
202
|
+
}
|
|
191
203
|
};
|
|
192
204
|
|
|
193
205
|
|
|
@@ -306,3 +318,15 @@ shaka.util.ParsedTRUNSample;
|
|
|
306
318
|
* @exportDoc
|
|
307
319
|
*/
|
|
308
320
|
shaka.util.ParsedTKHDBox;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* @typedef {{
|
|
324
|
+
* codec: string
|
|
325
|
+
* }}
|
|
326
|
+
*
|
|
327
|
+
* @property {string} codec
|
|
328
|
+
* A fourcc for a codec
|
|
329
|
+
*
|
|
330
|
+
* @exportDoc
|
|
331
|
+
*/
|
|
332
|
+
shaka.util.ParsedFRMABox;
|
package/lib/util/mp4_parser.js
CHANGED
|
@@ -172,6 +172,7 @@ shaka.util.Mp4Parser = class {
|
|
|
172
172
|
|
|
173
173
|
/** @type {shaka.extern.ParsedBox} */
|
|
174
174
|
const box = {
|
|
175
|
+
name,
|
|
175
176
|
parser: this,
|
|
176
177
|
partialOkay: partialOkay || false,
|
|
177
178
|
version,
|
|
@@ -236,6 +237,40 @@ shaka.util.Mp4Parser = class {
|
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
|
|
240
|
+
/**
|
|
241
|
+
* A callback that tells the Mp4 parser to treat the body of a box as a visual
|
|
242
|
+
* sample entry. A visual sample entry has some fixed-sized fields
|
|
243
|
+
* describing the video codec parameters, followed by an arbitrary number of
|
|
244
|
+
* appended children. Each child is a box.
|
|
245
|
+
*
|
|
246
|
+
* @param {!shaka.extern.ParsedBox} box
|
|
247
|
+
* @export
|
|
248
|
+
*/
|
|
249
|
+
static visualSampleEntry(box) {
|
|
250
|
+
// The "reader" starts at the payload, so we need to add the header to the
|
|
251
|
+
// start position. The header size varies.
|
|
252
|
+
const headerSize = shaka.util.Mp4Parser.headerSize(box);
|
|
253
|
+
|
|
254
|
+
// Skip 6 reserved bytes.
|
|
255
|
+
// Skip 2-byte data reference index.
|
|
256
|
+
// Skip 16 more reserved bytes.
|
|
257
|
+
// Skip 4 bytes for width/height.
|
|
258
|
+
// Skip 8 bytes for horizontal/vertical resolution.
|
|
259
|
+
// Skip 4 more reserved bytes (0)
|
|
260
|
+
// Skip 2-byte frame count.
|
|
261
|
+
// Skip 32-byte compressor name (length byte, then name, then 0-padding).
|
|
262
|
+
// Skip 2-byte depth.
|
|
263
|
+
// Skip 2 more reserved bytes (0xff)
|
|
264
|
+
// 78 bytes total.
|
|
265
|
+
// See also https://github.com/shaka-project/shaka-packager/blob/d5ca6e84/packager/media/formats/mp4/box_definitions.cc#L1544
|
|
266
|
+
box.reader.skip(78);
|
|
267
|
+
|
|
268
|
+
while (box.reader.hasMoreData() && !box.parser.done_) {
|
|
269
|
+
box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
|
|
239
274
|
/**
|
|
240
275
|
* Create a callback that tells the Mp4 parser to treat the body of a box as a
|
|
241
276
|
* binary blob and to parse the body's contents using the provided callback.
|
|
@@ -12,6 +12,7 @@ goog.require('shaka.config.AutoShowText');
|
|
|
12
12
|
goog.require('shaka.log');
|
|
13
13
|
goog.require('shaka.net.NetworkingEngine');
|
|
14
14
|
goog.require('shaka.util.ConfigUtils');
|
|
15
|
+
goog.require('shaka.util.FairPlayUtils');
|
|
15
16
|
goog.require('shaka.util.LanguageUtils');
|
|
16
17
|
goog.require('shaka.util.ManifestParserUtils');
|
|
17
18
|
goog.require('shaka.util.Platform');
|
|
@@ -68,9 +69,15 @@ shaka.util.PlayerConfiguration = class {
|
|
|
68
69
|
advanced: {}, // key is arbitrary key system ID, value is a record type
|
|
69
70
|
delayLicenseRequestUntilPlayed: false,
|
|
70
71
|
initDataTransform: (initData, initDataType, drmInfo) => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
const keySystem = drmInfo.keySystem;
|
|
73
|
+
if (keySystem == 'com.apple.fps.1_0' && initDataType == 'skd') {
|
|
74
|
+
const cert = drmInfo.serverCertificate;
|
|
75
|
+
const contentId =
|
|
76
|
+
shaka.util.FairPlayUtils.defaultGetContentId(initData);
|
|
77
|
+
initData = shaka.util.FairPlayUtils.initDataTransform(
|
|
78
|
+
initData, contentId, cert);
|
|
79
|
+
}
|
|
80
|
+
return initData;
|
|
74
81
|
},
|
|
75
82
|
logLicenseExchange: false,
|
|
76
83
|
updateExpirationTime: 1,
|