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
package/lib/util/xml_utils.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
goog.provide('shaka.util.XmlUtils');
|
|
8
8
|
|
|
9
|
+
goog.require('goog.asserts');
|
|
9
10
|
goog.require('shaka.log');
|
|
10
11
|
goog.require('shaka.util.StringUtils');
|
|
11
12
|
|
|
@@ -312,29 +313,46 @@ shaka.util.XmlUtils = class {
|
|
|
312
313
|
|
|
313
314
|
|
|
314
315
|
/**
|
|
315
|
-
* Parse a string and return the resulting root element if
|
|
316
|
-
*
|
|
316
|
+
* Parse a string and return the resulting root element if it was valid XML.
|
|
317
|
+
*
|
|
317
318
|
* @param {string} xmlString
|
|
318
319
|
* @param {string} expectedRootElemName
|
|
319
320
|
* @return {Element}
|
|
320
321
|
*/
|
|
321
322
|
static parseXmlString(xmlString, expectedRootElemName) {
|
|
322
323
|
const parser = new DOMParser();
|
|
323
|
-
let rootElem = null;
|
|
324
324
|
let xml = null;
|
|
325
325
|
try {
|
|
326
326
|
xml = parser.parseFromString(xmlString, 'text/xml');
|
|
327
|
-
} catch (exception) {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
327
|
+
} catch (exception) {
|
|
328
|
+
shaka.log.error('XML parsing exception:', exception);
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// According to MDN, parseFromString never returns null.
|
|
333
|
+
goog.asserts.assert(xml, 'Parsed XML document cannot be null!');
|
|
334
|
+
|
|
335
|
+
// Check for empty documents.
|
|
336
|
+
const rootElem = xml.documentElement;
|
|
337
|
+
if (!rootElem) {
|
|
338
|
+
shaka.log.error('XML document was empty!');
|
|
339
|
+
return null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Check for parser errors.
|
|
343
|
+
const parserErrorElements = rootElem.getElementsByTagName('parsererror');
|
|
344
|
+
if (parserErrorElements.length) {
|
|
345
|
+
shaka.log.error('XML parser error found:', parserErrorElements[0]);
|
|
346
|
+
return null;
|
|
334
347
|
}
|
|
335
|
-
|
|
348
|
+
|
|
349
|
+
// The top-level element in the loaded XML should have the name we expect.
|
|
350
|
+
if (xml.documentElement.tagName != expectedRootElemName) {
|
|
351
|
+
shaka.log.error(
|
|
352
|
+
`XML tag name does not match expected "${expectedRootElemName}":`,
|
|
353
|
+
xml.documentElement.tagName);
|
|
336
354
|
return null;
|
|
337
|
-
}
|
|
355
|
+
}
|
|
338
356
|
|
|
339
357
|
return rootElem;
|
|
340
358
|
}
|
|
@@ -352,6 +370,7 @@ shaka.util.XmlUtils = class {
|
|
|
352
370
|
const string = shaka.util.StringUtils.fromUTF8(data);
|
|
353
371
|
return shaka.util.XmlUtils.parseXmlString(string, expectedRootElemName);
|
|
354
372
|
} catch (exception) {
|
|
373
|
+
shaka.log.error('parseXmlString threw!', exception);
|
|
355
374
|
return null;
|
|
356
375
|
}
|
|
357
376
|
}
|
package/package.json
CHANGED
package/roadmap.md
CHANGED
|
@@ -8,49 +8,51 @@ CHANGELOG.md.
|
|
|
8
8
|
The goals of future milestones are fluid until we begin that development cycle,
|
|
9
9
|
so the exact milestone for future features is not pre-determined.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- HLS performance improvements
|
|
13
|
-
https://github.com/google/shaka-player/issues/3363
|
|
14
|
-
- HLS interstitial support
|
|
15
|
-
https://github.com/google/shaka-player/issues/3364
|
|
16
|
-
- Integration with Google Video ML/Cloud AI
|
|
11
|
+
Priority features up next:
|
|
17
12
|
- Support containerless formats
|
|
18
13
|
https://github.com/google/shaka-player/issues/2337
|
|
14
|
+
- Preload API
|
|
15
|
+
https://github.com/google/shaka-player/issues/880
|
|
16
|
+
- Codec-switching and order preference
|
|
17
|
+
https://github.com/google/shaka-player/issues/1528
|
|
18
|
+
https://github.com/google/shaka-player/issues/2179
|
|
19
|
+
|
|
20
|
+
Candidate features for future release cycles:
|
|
21
|
+
- HLS interstitial support
|
|
22
|
+
https://github.com/google/shaka-player/issues/3364
|
|
19
23
|
- Support ID3 tags in mp4
|
|
20
24
|
https://github.com/google/shaka-player/issues/3351
|
|
21
25
|
- MSE support for FairPlay
|
|
22
26
|
https://github.com/google/shaka-player/issues/3346
|
|
23
|
-
- Codec-switching and order preference
|
|
24
|
-
https://github.com/google/shaka-player/issues/1528
|
|
25
|
-
https://github.com/google/shaka-player/issues/2179
|
|
26
|
-
- Configurable key-system priority
|
|
27
|
-
https://github.com/google/shaka-player/issues/3002
|
|
28
|
-
- Preload API
|
|
29
|
-
https://github.com/google/shaka-player/issues/880
|
|
30
|
-
- Thumbnail tracks
|
|
31
|
-
DASH: https://github.com/google/shaka-player/issues/559
|
|
32
|
-
HLS: https://github.com/google/shaka-player/issues/2429
|
|
33
27
|
- Smaller, more modular binary
|
|
34
28
|
- Official TypeScript defs, generated from source
|
|
35
29
|
https://github.com/google/shaka-player/issues/1030
|
|
30
|
+
- Shaka Analytics Component and an integration with BigQuery Analytics
|
|
31
|
+
https://github.com/google/shaka-player/issues/3359
|
|
32
|
+
- Integration with Google Video ML/Cloud AI
|
|
36
33
|
|
|
37
|
-
v3.3 - 2021 Q3
|
|
34
|
+
v3.3 - 2021 Q3 or Q4
|
|
38
35
|
- Background fetch for offline storage
|
|
39
36
|
https://github.com/google/shaka-player/issues/879
|
|
40
|
-
-
|
|
41
|
-
https://github.com/google/shaka-player/issues/
|
|
37
|
+
- HLS performance improvements
|
|
38
|
+
https://github.com/google/shaka-player/issues/3363
|
|
42
39
|
|
|
43
|
-
|
|
40
|
+
=====
|
|
41
|
+
|
|
42
|
+
v3.2 - 2021 Q3
|
|
44
43
|
- MediaCapabilities to make performance-based decisions
|
|
45
44
|
https://github.com/google/shaka-player/issues/1391
|
|
45
|
+
- Configurable key-system priority
|
|
46
|
+
https://github.com/google/shaka-player/issues/3002
|
|
46
47
|
|
|
47
48
|
v3.1 - 2021 Q2
|
|
48
49
|
- Low-latency live (LL-HLS, LL-DASH)
|
|
49
50
|
https://github.com/google/shaka-player/issues/1525
|
|
50
51
|
- Own CEA parser, use mux.js only for TS
|
|
51
52
|
https://github.com/google/shaka-player/issues/2648
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
- Thumbnail tracks
|
|
54
|
+
DASH: https://github.com/google/shaka-player/issues/559
|
|
55
|
+
HLS: https://github.com/google/shaka-player/issues/2429
|
|
54
56
|
|
|
55
57
|
v3.0 - 2020 Q2
|
|
56
58
|
- Code health improvements
|
|
@@ -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.Aria');
|
|
35
36
|
goog.require('shaka.polyfill.EncryptionScheme');
|
|
36
37
|
goog.require('shaka.polyfill.Fullscreen');
|
|
37
38
|
goog.require('shaka.polyfill.MathRound');
|
|
@@ -297,7 +297,8 @@ describe('CastProxy', () => {
|
|
|
297
297
|
'timeupdate', Util.spyFunc(proxyListener));
|
|
298
298
|
|
|
299
299
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
300
|
-
const fakeEvent = new FakeEvent(
|
|
300
|
+
const fakeEvent = new FakeEvent(
|
|
301
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
301
302
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
302
303
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
303
304
|
type: 'timeupdate',
|
|
@@ -315,7 +316,8 @@ describe('CastProxy', () => {
|
|
|
315
316
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
316
317
|
|
|
317
318
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
318
|
-
const fakeEvent = new FakeEvent(
|
|
319
|
+
const fakeEvent = new FakeEvent(
|
|
320
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
319
321
|
mockVideo.on['timeupdate'](fakeEvent);
|
|
320
322
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
321
323
|
});
|
|
@@ -332,7 +334,8 @@ describe('CastProxy', () => {
|
|
|
332
334
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
333
335
|
|
|
334
336
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
335
|
-
const fakeEvent = new FakeEvent(
|
|
337
|
+
const fakeEvent = new FakeEvent(
|
|
338
|
+
'timeupdate', (new Map()).set('detail', 8675309));
|
|
336
339
|
mockSender.onRemoteEvent('video', fakeEvent);
|
|
337
340
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
338
341
|
type: 'timeupdate',
|
|
@@ -446,7 +449,8 @@ describe('CastProxy', () => {
|
|
|
446
449
|
'buffering', Util.spyFunc(proxyListener));
|
|
447
450
|
|
|
448
451
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
449
|
-
const fakeEvent = new FakeEvent(
|
|
452
|
+
const fakeEvent = new FakeEvent(
|
|
453
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
450
454
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
451
455
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
452
456
|
type: 'buffering',
|
|
@@ -464,7 +468,8 @@ describe('CastProxy', () => {
|
|
|
464
468
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
465
469
|
|
|
466
470
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
467
|
-
const fakeEvent = new FakeEvent(
|
|
471
|
+
const fakeEvent = new FakeEvent(
|
|
472
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
468
473
|
mockPlayer.listeners['buffering'](fakeEvent);
|
|
469
474
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
470
475
|
});
|
|
@@ -481,7 +486,8 @@ describe('CastProxy', () => {
|
|
|
481
486
|
mockSender.hasRemoteProperties.and.returnValue(true);
|
|
482
487
|
|
|
483
488
|
expect(proxyListener).not.toHaveBeenCalled();
|
|
484
|
-
const fakeEvent = new FakeEvent(
|
|
489
|
+
const fakeEvent = new FakeEvent(
|
|
490
|
+
'buffering', (new Map()).set('detail', 8675309));
|
|
485
491
|
mockSender.onRemoteEvent('player', fakeEvent);
|
|
486
492
|
expect(proxyListener).toHaveBeenCalledWith(jasmine.objectContaining({
|
|
487
493
|
type: 'buffering',
|
|
@@ -27,6 +27,7 @@ describe('CastUtils', () => {
|
|
|
27
27
|
'getAdManager', // Handled specially
|
|
28
28
|
'getSharedConfiguration', // Handled specially
|
|
29
29
|
'getNetworkingEngine', // Handled specially
|
|
30
|
+
'getDrmEngine', // Handled specially
|
|
30
31
|
'getMediaElement', // Handled specially
|
|
31
32
|
'setMaxHardwareResolution',
|
|
32
33
|
'destroy', // Should use CastProxy.destroy instead
|
|
@@ -122,7 +123,7 @@ describe('CastUtils', () => {
|
|
|
122
123
|
expect(typeof deserialized).toBe('object');
|
|
123
124
|
|
|
124
125
|
// The object can be used to construct a FakeEvent.
|
|
125
|
-
const fakeEvent =
|
|
126
|
+
const fakeEvent = FakeEvent.fromRealEvent(deserialized);
|
|
126
127
|
|
|
127
128
|
// The fake event has the same type and properties as the original.
|
|
128
129
|
const asObj = /** @type {!Object} */ (fakeEvent);
|
|
@@ -19,39 +19,39 @@ describe('Cea708Service', () => {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Hide window (2 bytes), with a bitmap provided to indicate all windows.
|
|
22
|
-
* @type {!Array
|
|
22
|
+
* @type {!Array<number>}
|
|
23
23
|
*/
|
|
24
24
|
const hideWindow = [0x8a, 0xff];
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
27
|
* Define window (7 bytes), defines window #0 to be a visible window
|
|
28
28
|
* with 32 rows and 32 columns. (We specify 31 for each since decoder adds 1).
|
|
29
|
-
* @type {!Array
|
|
29
|
+
* @type {!Array<number>}
|
|
30
30
|
*/
|
|
31
31
|
const defineWindow = [
|
|
32
32
|
0x98, 0x38, 0x00, 0x00, 0x1f, 0x1f, 0x00,
|
|
33
33
|
];
|
|
34
34
|
|
|
35
|
-
/** @type {
|
|
35
|
+
/** @type {number} */
|
|
36
36
|
const startTime = 1;
|
|
37
37
|
|
|
38
|
-
/** @type {
|
|
38
|
+
/** @type {number} */
|
|
39
39
|
const endTime = 2;
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
42
|
* We arbitrarily pick service 1 for all of these tests.
|
|
43
|
-
* @type {
|
|
43
|
+
* @type {number}
|
|
44
44
|
*/
|
|
45
45
|
const serviceNumber = 1;
|
|
46
46
|
|
|
47
|
-
/** @type {
|
|
47
|
+
/** @type {string} */
|
|
48
48
|
const stream = `svc${serviceNumber}`;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Takes in a array of bytes and a presentation timestamp (in seconds),
|
|
52
52
|
* and converts it into a CEA-708 DTVCC Packet.
|
|
53
|
-
* @param {!Array
|
|
54
|
-
* @param {
|
|
53
|
+
* @param {!Array<number>} bytes
|
|
54
|
+
* @param {number} pts
|
|
55
55
|
*/
|
|
56
56
|
const createCea708PacketFromBytes = (bytes, pts) => {
|
|
57
57
|
const cea708Bytes = bytes.map((code, i) => {
|
|
@@ -15,22 +15,22 @@ describe('Cea708Window', () => {
|
|
|
15
15
|
/** @type {!shaka.cea.Cea708Window} */
|
|
16
16
|
let window;
|
|
17
17
|
|
|
18
|
-
/** @type {
|
|
18
|
+
/** @type {number} */
|
|
19
19
|
const serviceNumber = 1; // We will arbitrarily pick service 1 for all tests.
|
|
20
20
|
|
|
21
|
-
/** @type {
|
|
21
|
+
/** @type {string} */
|
|
22
22
|
const stream = `svc${serviceNumber}`;
|
|
23
23
|
|
|
24
|
-
/** @type {
|
|
24
|
+
/** @type {number} */
|
|
25
25
|
const rowCount = 10;
|
|
26
26
|
|
|
27
|
-
/** @type {
|
|
27
|
+
/** @type {number} */
|
|
28
28
|
const colCount = 32;
|
|
29
29
|
|
|
30
|
-
/** @type {
|
|
30
|
+
/** @type {number} */
|
|
31
31
|
const startTime = 1;
|
|
32
32
|
|
|
33
|
-
/** @type {
|
|
33
|
+
/** @type {number} */
|
|
34
34
|
const endTime = 2;
|
|
35
35
|
|
|
36
36
|
beforeEach(() => {
|
|
@@ -11,10 +11,10 @@ describe('DtvccPacketBuilder', () => {
|
|
|
11
11
|
/** @type {!shaka.cea.DtvccPacketBuilder} */
|
|
12
12
|
let dtvccPacketBuilder;
|
|
13
13
|
|
|
14
|
-
/** @type {
|
|
14
|
+
/** @type {number} */
|
|
15
15
|
const DTVCC_PACKET_START = shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_START;
|
|
16
16
|
|
|
17
|
-
/** @type {
|
|
17
|
+
/** @type {number} */
|
|
18
18
|
const DTVCC_PACKET_DATA = shaka.cea.DtvccPacketBuilder.DTVCC_PACKET_DATA;
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
@@ -36,8 +36,16 @@ describe('DashParser Manifest', () => {
|
|
|
36
36
|
/** @type {!ArrayBuffer} */
|
|
37
37
|
let mp4Index;
|
|
38
38
|
|
|
39
|
-
/** @type {
|
|
39
|
+
/** @type {string} */
|
|
40
40
|
const thumbnailScheme = 'http://dashif.org/guidelines/thumbnail_tile';
|
|
41
|
+
/**
|
|
42
|
+
* CICP scheme. parameter must be one of the following: "ColourPrimaries",
|
|
43
|
+
* "TransferCharacteristics", or "MatrixCoefficients".
|
|
44
|
+
*
|
|
45
|
+
* @param {string} parameter
|
|
46
|
+
* @return {string}
|
|
47
|
+
*/
|
|
48
|
+
const cicpScheme = (parameter) => `urn:mpeg:mpegB:cicp:${parameter}`;
|
|
41
49
|
|
|
42
50
|
beforeAll(async () => {
|
|
43
51
|
mp4Index = await shaka.test.Util.fetch(mp4IndexSegmentUri);
|
|
@@ -1790,6 +1798,48 @@ describe('DashParser Manifest', () => {
|
|
|
1790
1798
|
expect(manifest.presentationTimeline).toBeTruthy();
|
|
1791
1799
|
});
|
|
1792
1800
|
|
|
1801
|
+
it('Invokes manifestPreprocessor in config', async () => {
|
|
1802
|
+
const manifestText = [
|
|
1803
|
+
'<MPD minBufferTime="PT75S">',
|
|
1804
|
+
' <Period id="1" duration="PT30S">',
|
|
1805
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1806
|
+
' <Representation id="video-sd" width="640" height="480">',
|
|
1807
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1808
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1809
|
+
' </Representation>',
|
|
1810
|
+
' </AdaptationSet>',
|
|
1811
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1812
|
+
' <Representation id="audio-en">',
|
|
1813
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1814
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1815
|
+
' </Representation>',
|
|
1816
|
+
' </AdaptationSet>',
|
|
1817
|
+
' <AdaptationSet mimeType="text/vtt" lang="de">',
|
|
1818
|
+
' <Representation>',
|
|
1819
|
+
' <BaseURL>http://example.com/de.vtt</BaseURL>',
|
|
1820
|
+
' </Representation>',
|
|
1821
|
+
' </AdaptationSet>',
|
|
1822
|
+
' </Period>',
|
|
1823
|
+
'</MPD>',
|
|
1824
|
+
].join('\n');
|
|
1825
|
+
|
|
1826
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1827
|
+
const config = shaka.util.PlayerConfiguration.createDefault().manifest;
|
|
1828
|
+
config.dash.manifestPreprocessor = (mpd) => {
|
|
1829
|
+
const selector = 'AdaptationSet[mimeType="text/vtt"';
|
|
1830
|
+
const vttElements = mpd.querySelectorAll(selector);
|
|
1831
|
+
for (const element of vttElements) {
|
|
1832
|
+
element.parentNode.removeChild(element);
|
|
1833
|
+
}
|
|
1834
|
+
};
|
|
1835
|
+
parser.configure(config);
|
|
1836
|
+
|
|
1837
|
+
/** @type {shaka.extern.Manifest} */
|
|
1838
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1839
|
+
const stream = manifest.textStreams[0];
|
|
1840
|
+
expect(stream).toBeUndefined();
|
|
1841
|
+
});
|
|
1842
|
+
|
|
1793
1843
|
it('converts Accessibility element to "kind"', async () => {
|
|
1794
1844
|
const manifestText = [
|
|
1795
1845
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -1854,7 +1904,8 @@ describe('DashParser Manifest', () => {
|
|
|
1854
1904
|
expect(textStream.forced).toBe(true);
|
|
1855
1905
|
});
|
|
1856
1906
|
|
|
1857
|
-
it('supports HDR signaling', async () => {
|
|
1907
|
+
it('supports HDR signaling via profiles', async () => {
|
|
1908
|
+
// (DASH-IF IOP v4.3 10.3.3.)
|
|
1858
1909
|
const hdrProfile =
|
|
1859
1910
|
'http://dashif.org/guidelines/dash-if-uhd#hevc-hdr-pq10';
|
|
1860
1911
|
const manifestText = [
|
|
@@ -1885,6 +1936,110 @@ describe('DashParser Manifest', () => {
|
|
|
1885
1936
|
expect(stream.hdr).toBe('PQ');
|
|
1886
1937
|
});
|
|
1887
1938
|
|
|
1939
|
+
it('supports HDR signaling via SupplementalProperty', async () => {
|
|
1940
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1941
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1942
|
+
const pq = 16;
|
|
1943
|
+
const manifestText = [
|
|
1944
|
+
'<MPD minBufferTime="PT75S">',
|
|
1945
|
+
' <Period id="1" duration="PT30S">',
|
|
1946
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1947
|
+
` <SupplementalProperty schemeIdUri="${hdrScheme}" value="${pq}" />`,
|
|
1948
|
+
' <Representation codecs="hvc1.2.4.L150.90">',
|
|
1949
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1950
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1951
|
+
' </Representation>',
|
|
1952
|
+
' </AdaptationSet>',
|
|
1953
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1954
|
+
' <Representation id="audio-en">',
|
|
1955
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1956
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1957
|
+
' </Representation>',
|
|
1958
|
+
' </AdaptationSet>',
|
|
1959
|
+
' </Period>',
|
|
1960
|
+
'</MPD>',
|
|
1961
|
+
].join('\n');
|
|
1962
|
+
|
|
1963
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1964
|
+
|
|
1965
|
+
/** @type {shaka.extern.Manifest} */
|
|
1966
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
1967
|
+
expect(manifest.variants.length).toBe(1);
|
|
1968
|
+
const stream = manifest.variants[0].video;
|
|
1969
|
+
expect(stream.hdr).toBe('PQ');
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
it('supports HDR signaling via EssentialProperty', async () => {
|
|
1973
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
1974
|
+
const hdrScheme = cicpScheme('TransferCharacteristics');
|
|
1975
|
+
const hlg = 18;
|
|
1976
|
+
const manifestText = [
|
|
1977
|
+
'<MPD minBufferTime="PT75S">',
|
|
1978
|
+
' <Period id="1" duration="PT30S">',
|
|
1979
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
1980
|
+
` <EssentialProperty schemeIdUri="${hdrScheme}" value="${hlg}" />`,
|
|
1981
|
+
' <Representation codecs="hvc1.2.4.L153.B0">',
|
|
1982
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
1983
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1984
|
+
' </Representation>',
|
|
1985
|
+
' </AdaptationSet>',
|
|
1986
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
1987
|
+
' <Representation id="audio-en">',
|
|
1988
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
1989
|
+
' <SegmentBase indexRange="100-200" />',
|
|
1990
|
+
' </Representation>',
|
|
1991
|
+
' </AdaptationSet>',
|
|
1992
|
+
' </Period>',
|
|
1993
|
+
'</MPD>',
|
|
1994
|
+
].join('\n');
|
|
1995
|
+
|
|
1996
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
1997
|
+
|
|
1998
|
+
/** @type {shaka.extern.Manifest} */
|
|
1999
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2000
|
+
expect(manifest.variants.length).toBe(1);
|
|
2001
|
+
const stream = manifest.variants[0].video;
|
|
2002
|
+
expect(stream.hdr).toBe('HLG');
|
|
2003
|
+
});
|
|
2004
|
+
|
|
2005
|
+
it('supports SDR signalling via EssentialProperty', async () => {
|
|
2006
|
+
// (DASH-IF IOP v4.3 6.2.5.1.)
|
|
2007
|
+
const scheme = cicpScheme('TransferCharacteristics');
|
|
2008
|
+
const sdrValues = [1, 6, 13, 14, 15];
|
|
2009
|
+
const manifestPromises = [];
|
|
2010
|
+
for (const value of sdrValues) {
|
|
2011
|
+
const manifestText = [
|
|
2012
|
+
'<MPD minBufferTime="PT75S">',
|
|
2013
|
+
' <Period id="1" duration="PT30S">',
|
|
2014
|
+
' <AdaptationSet id="2" mimeType="video/mp4">',
|
|
2015
|
+
` <EssentialProperty schemeIdUri="${scheme}" value="${value}" />`,
|
|
2016
|
+
' <Representation codecs="avc1.640028">',
|
|
2017
|
+
' <BaseURL>v-sd.mp4</BaseURL>',
|
|
2018
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2019
|
+
' </Representation>',
|
|
2020
|
+
' </AdaptationSet>',
|
|
2021
|
+
' <AdaptationSet id="3" mimeType="audio/mp4">',
|
|
2022
|
+
' <Representation id="audio-en">',
|
|
2023
|
+
' <BaseURL>a-en.mp4</BaseURL>',
|
|
2024
|
+
' <SegmentBase indexRange="100-200" />',
|
|
2025
|
+
' </Representation>',
|
|
2026
|
+
' </AdaptationSet>',
|
|
2027
|
+
' </Period>',
|
|
2028
|
+
'</MPD>',
|
|
2029
|
+
].join('\n');
|
|
2030
|
+
|
|
2031
|
+
fakeNetEngine.setResponseText('dummy://foo', manifestText);
|
|
2032
|
+
|
|
2033
|
+
manifestPromises.push(parser.start('dummy://foo', playerInterface));
|
|
2034
|
+
}
|
|
2035
|
+
const manifests = await Promise.all(manifestPromises);
|
|
2036
|
+
for (const manifest of manifests) {
|
|
2037
|
+
expect(manifest.variants.length).toBe(1);
|
|
2038
|
+
const stream = manifest.variants[0].video;
|
|
2039
|
+
expect(stream.hdr).toBe('SDR');
|
|
2040
|
+
}
|
|
2041
|
+
});
|
|
2042
|
+
|
|
1888
2043
|
it('Does not error when image adaptation sets are present', async () => {
|
|
1889
2044
|
const manifestText = [
|
|
1890
2045
|
'<MPD minBufferTime="PT75S">',
|
|
@@ -2108,4 +2263,77 @@ describe('DashParser Manifest', () => {
|
|
|
2108
2263
|
expect(uri).not.toContain('/p2/');
|
|
2109
2264
|
}
|
|
2110
2265
|
});
|
|
2266
|
+
|
|
2267
|
+
/**
|
|
2268
|
+
* @param {!Array.<number>} periods Start time of multiple periods
|
|
2269
|
+
* @return {string}
|
|
2270
|
+
*/
|
|
2271
|
+
function buildManifestWithPeriodStartTime(periods) {
|
|
2272
|
+
const mpdTemplate = [
|
|
2273
|
+
`<MPD type="dynamic"`,
|
|
2274
|
+
'availabilityStartTime="1970-01-01T00:00:00Z"',
|
|
2275
|
+
'timeShiftBufferDepth="PT10H">',
|
|
2276
|
+
' %(periods)s',
|
|
2277
|
+
'</MPD>',
|
|
2278
|
+
].join('\n');
|
|
2279
|
+
const periodTemplate = (id, period, duration) => {
|
|
2280
|
+
return [
|
|
2281
|
+
` <Period id="${id}" start="PT${period}S">`,
|
|
2282
|
+
' <AdaptationSet mimeType="video/mp4" lang="en" group="1">',
|
|
2283
|
+
' <SegmentTemplate startNumber="1" media="l-$Number$.mp4">',
|
|
2284
|
+
' <SegmentTimeline>',
|
|
2285
|
+
` <S t="0" d="${duration}" />`,
|
|
2286
|
+
' </SegmentTimeline>',
|
|
2287
|
+
' </SegmentTemplate>',
|
|
2288
|
+
' <Representation id="1"/>',
|
|
2289
|
+
' </AdaptationSet>',
|
|
2290
|
+
' </Period>',
|
|
2291
|
+
].join('\n');
|
|
2292
|
+
};
|
|
2293
|
+
const periodXmls = periods.map((period, i) => {
|
|
2294
|
+
const duration = i+1 === periods.length ? 10 : periods[i+1] - period;
|
|
2295
|
+
// Period start time as ID here. If we use index then there will be
|
|
2296
|
+
// periods with same period ID and different start time which are invalid.
|
|
2297
|
+
return periodTemplate(period, period, duration);
|
|
2298
|
+
});
|
|
2299
|
+
return sprintf(mpdTemplate, {
|
|
2300
|
+
periods: periodXmls.join('\n'),
|
|
2301
|
+
});
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
// Bug description: Inconsistent period start time in the manifests due
|
|
2305
|
+
// to failover triggered in backend servers
|
|
2306
|
+
|
|
2307
|
+
// When one of the servers is down, the manifest will be served by other
|
|
2308
|
+
// redundant servers. The period start time might become out of sync
|
|
2309
|
+
// during the switch-over/recovery.
|
|
2310
|
+
|
|
2311
|
+
it('skip periods that are earlier than max period start time', async () => {
|
|
2312
|
+
const sources = [
|
|
2313
|
+
buildManifestWithPeriodStartTime([5, 15]),
|
|
2314
|
+
buildManifestWithPeriodStartTime([6, 15]), // simulate out-of-sync of +1s
|
|
2315
|
+
buildManifestWithPeriodStartTime([4, 15]), // simulate out-of-sync of -1s
|
|
2316
|
+
];
|
|
2317
|
+
const segments = [];
|
|
2318
|
+
|
|
2319
|
+
for (const source of sources) {
|
|
2320
|
+
fakeNetEngine.setResponseText('dummy://foo', source);
|
|
2321
|
+
/** @type {shaka.extern.Manifest} */
|
|
2322
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2323
|
+
const manifest = await parser.start('dummy://foo', playerInterface);
|
|
2324
|
+
const video = manifest.variants[0].video;
|
|
2325
|
+
// eslint-disable-next-line no-await-in-loop
|
|
2326
|
+
await video.createSegmentIndex();
|
|
2327
|
+
goog.asserts.assert(video.segmentIndex, 'Null segmentIndex!');
|
|
2328
|
+
segments.push(Array.from(video.segmentIndex));
|
|
2329
|
+
}
|
|
2330
|
+
|
|
2331
|
+
// Expect identical segments
|
|
2332
|
+
expect(segments[0].length).toBe(2);
|
|
2333
|
+
expect(segments[1].length).toBe(2);
|
|
2334
|
+
expect(segments[0][0].startTime).toBe(5);
|
|
2335
|
+
expect(segments[1][0].startTime).toBe(5);
|
|
2336
|
+
expect(segments[0][1].startTime).toBe(15);
|
|
2337
|
+
expect(segments[1][1].startTime).toBe(15);
|
|
2338
|
+
});
|
|
2111
2339
|
});
|