shaka-player 4.3.0 → 4.3.1
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 +7 -0
- package/AUTHORS +1 -0
- package/CHANGELOG.md +33 -0
- package/CONTRIBUTORS +1 -0
- 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 +28 -28
- 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 +632 -628
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +102 -80
- package/dist/shaka-player.compiled.js +903 -900
- 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 +681 -678
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +102 -80
- package/dist/shaka-player.ui.js +1033 -1031
- 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/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 +37 -19
- package/lib/media/content_workarounds.js +12 -0
- package/lib/media/media_source_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +12 -7
- package/lib/media/transmuxer.js +6 -4
- package/lib/player.js +5 -5
- 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/polyfill/media_capabilities_unit.js +259 -0
- package/test/polyfill/patchedmediakeys_apple_unit.js +6 -0
- package/test/test/util/stream_generator.js +212 -8
- package/test/test/util/test_scheme.js +4 -3
- 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 +45 -36
|
@@ -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,
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -8,6 +8,7 @@ goog.provide('shaka.util.StreamUtils');
|
|
|
8
8
|
|
|
9
9
|
goog.require('goog.asserts');
|
|
10
10
|
goog.require('shaka.log');
|
|
11
|
+
goog.require('shaka.media.Capabilities');
|
|
11
12
|
goog.require('shaka.text.TextEngine');
|
|
12
13
|
goog.require('shaka.util.Functional');
|
|
13
14
|
goog.require('shaka.util.LanguageUtils');
|
|
@@ -437,25 +438,31 @@ shaka.util.StreamUtils = class {
|
|
|
437
438
|
// See: https://github.com/shaka-project/shaka-player/issues/3860
|
|
438
439
|
const video = variant.video;
|
|
439
440
|
const ContentType = shaka.util.ManifestParserUtils.ContentType;
|
|
441
|
+
const Capabilities = shaka.media.Capabilities;
|
|
440
442
|
if (video) {
|
|
441
|
-
let videoCodecs =
|
|
443
|
+
let videoCodecs =
|
|
444
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
|
|
442
445
|
// For multiplexed streams. Here we must check the audio of the
|
|
443
446
|
// stream to see if it is compatible.
|
|
444
447
|
if (video.codecs.includes(',')) {
|
|
445
448
|
const allCodecs = video.codecs.split(',');
|
|
446
449
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
447
450
|
ContentType.VIDEO, allCodecs);
|
|
448
|
-
|
|
451
|
+
videoCodecs =
|
|
452
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
453
|
+
let audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
449
454
|
ContentType.AUDIO, allCodecs);
|
|
455
|
+
audioCodecs =
|
|
456
|
+
shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
|
|
450
457
|
const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
451
458
|
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
452
|
-
if (!
|
|
459
|
+
if (!Capabilities.isTypeSupported(audioFullType)) {
|
|
453
460
|
return false;
|
|
454
461
|
}
|
|
455
462
|
}
|
|
456
463
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
457
464
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
458
|
-
if (!
|
|
465
|
+
if (!Capabilities.isTypeSupported(fullType)) {
|
|
459
466
|
return false;
|
|
460
467
|
}
|
|
461
468
|
}
|
|
@@ -465,7 +472,7 @@ shaka.util.StreamUtils = class {
|
|
|
465
472
|
shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
|
|
466
473
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
467
474
|
audio.mimeType, codecs, ContentType.AUDIO);
|
|
468
|
-
if (!
|
|
475
|
+
if (!Capabilities.isTypeSupported(fullType)) {
|
|
469
476
|
return false;
|
|
470
477
|
}
|
|
471
478
|
}
|
|
@@ -566,7 +573,8 @@ shaka.util.StreamUtils = class {
|
|
|
566
573
|
const allCodecs = video.codecs.split(',');
|
|
567
574
|
videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
568
575
|
ContentType.VIDEO, allCodecs);
|
|
569
|
-
videoCodecs =
|
|
576
|
+
videoCodecs =
|
|
577
|
+
shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
570
578
|
const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
|
|
571
579
|
ContentType.AUDIO, allCodecs);
|
|
572
580
|
|
|
@@ -580,7 +588,7 @@ shaka.util.StreamUtils = class {
|
|
|
580
588
|
spatialRendering: false,
|
|
581
589
|
};
|
|
582
590
|
}
|
|
583
|
-
videoCodecs = shaka.util.StreamUtils.
|
|
591
|
+
videoCodecs = shaka.util.StreamUtils.getCorrectVideoCodecs_(videoCodecs);
|
|
584
592
|
const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
|
|
585
593
|
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
586
594
|
// VideoConfiguration
|
|
@@ -749,15 +757,29 @@ shaka.util.StreamUtils = class {
|
|
|
749
757
|
|
|
750
758
|
|
|
751
759
|
/**
|
|
752
|
-
*
|
|
753
|
-
*
|
|
754
|
-
* mediaCapabilities enabled.
|
|
755
|
-
*
|
|
760
|
+
* Generates the correct video codec for MediaDecodingConfiguration and
|
|
761
|
+
* for MediaSource.isTypeSupported.
|
|
756
762
|
* @param {string} codec
|
|
757
763
|
* @return {string}
|
|
764
|
+
* @private
|
|
758
765
|
*/
|
|
759
|
-
static
|
|
760
|
-
if (codec
|
|
766
|
+
static getCorrectVideoCodecs_(codec) {
|
|
767
|
+
if (codec.includes('avc1')) {
|
|
768
|
+
// Convert avc1 codec string from RFC-4281 to RFC-6381 for
|
|
769
|
+
// MediaSource.isTypeSupported
|
|
770
|
+
// Example, convert avc1.66.30 to avc1.42001e (0x42 == 66 and 0x1e == 30)
|
|
771
|
+
const avcdata = codec.split('.');
|
|
772
|
+
if (avcdata.length == 3) {
|
|
773
|
+
let result = avcdata.shift() + '.';
|
|
774
|
+
result += parseInt(avcdata.shift(), 10).toString(16);
|
|
775
|
+
result +=
|
|
776
|
+
('000' + parseInt(avcdata.shift(), 10).toString(16)).slice(-4);
|
|
777
|
+
return result;
|
|
778
|
+
}
|
|
779
|
+
} else if (codec == 'vp9') {
|
|
780
|
+
// MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate
|
|
781
|
+
// vp9 codec strings into 'vp09...', to allow such content to play with
|
|
782
|
+
// mediaCapabilities enabled.
|
|
761
783
|
// This means profile 0, level 4.1, 8-bit color. This supports 1080p @
|
|
762
784
|
// 60Hz. See https://en.wikipedia.org/wiki/VP9#Levels
|
|
763
785
|
//
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shaka-player",
|
|
3
3
|
"description": "DASH/EME video player library",
|
|
4
|
-
"version": "4.3.
|
|
4
|
+
"version": "4.3.1",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -28,9 +28,9 @@
|
|
|
28
28
|
"eslint-plugin-shaka-rules": "file:./build/eslint-plugin-shaka-rules",
|
|
29
29
|
"esprima": "^4.0.1",
|
|
30
30
|
"fontfaceonload": "^1.0.2",
|
|
31
|
-
"google-closure-compiler-java": "^
|
|
32
|
-
"google-closure-deps": "
|
|
33
|
-
"google-closure-library": "^
|
|
31
|
+
"google-closure-compiler-java": "^20221102.0.0",
|
|
32
|
+
"google-closure-deps": "^20221102.0.0",
|
|
33
|
+
"google-closure-library": "^20221102.0.0",
|
|
34
34
|
"htmlhint": "^1.1.3",
|
|
35
35
|
"jasmine-ajax": "^4.0.0",
|
|
36
36
|
"jimp": "^0.16.1",
|
|
@@ -32,6 +32,7 @@ goog.require('shaka.offline.OfflineManifestParser');
|
|
|
32
32
|
goog.require('shaka.offline.OfflineScheme');
|
|
33
33
|
goog.require('shaka.offline.Storage');
|
|
34
34
|
goog.require('shaka.offline.indexeddb.StorageMechanism');
|
|
35
|
+
goog.require('shaka.polyfill.AbortController');
|
|
35
36
|
goog.require('shaka.polyfill.Aria');
|
|
36
37
|
goog.require('shaka.polyfill.EncryptionScheme');
|
|
37
38
|
goog.require('shaka.polyfill.Fullscreen');
|
|
@@ -115,7 +115,9 @@ describe('Ad manager', () => {
|
|
|
115
115
|
startedEventB = /** @type {!google.ima.AdEvent} */ (
|
|
116
116
|
new shaka.util.FakeEvent(google.ima.AdEvent.Type.STARTED));
|
|
117
117
|
startedEventB.getAd = () => {
|
|
118
|
-
return {
|
|
118
|
+
return {
|
|
119
|
+
isLinear: () => true,
|
|
120
|
+
};
|
|
119
121
|
};
|
|
120
122
|
}
|
|
121
123
|
makeMocks();
|
|
@@ -2198,6 +2198,7 @@ describe('DashParser Manifest', () => {
|
|
|
2198
2198
|
/** @type {shaka.extern.Manifest} */
|
|
2199
2199
|
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2200
2200
|
expect(manifest.imageStreams.length).toBe(1);
|
|
2201
|
+
expect(manifest.presentationTimeline.getMaxSegmentDuration()).toBe(1);
|
|
2201
2202
|
const imageStream = manifest.imageStreams[0];
|
|
2202
2203
|
expect(imageStream.width).toBe(1024);
|
|
2203
2204
|
expect(imageStream.height).toBe(1152);
|