shaka-player 4.2.3 → 4.3.0
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/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +5 -0
- package/CHANGELOG.md +85 -39
- package/CONTRIBUTORS +5 -0
- package/README.md +14 -0
- package/demo/common/assets.js +58 -0
- package/demo/common/message_ids.js +22 -1
- package/demo/config.js +89 -5
- package/demo/demo.less +7 -1
- package/demo/index.html +8 -1
- package/demo/locales/en.json +21 -1
- package/demo/locales/source.json +82 -2
- package/demo/main.js +57 -1
- package/demo/search.js +2 -0
- package/demo/visualizer.js +337 -0
- package/demo/visualizer_button.js +103 -0
- package/dist/controls.css +1 -1
- package/dist/controls.css.map +1 -1
- package/dist/demo.compiled.debug.js +83 -58
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +83 -58
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +18 -11
- package/dist/locales.js +32 -32
- package/dist/receiver.compiled.debug.js +23 -19
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +23 -19
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.d.ts +178 -27
- package/dist/shaka-player.compiled.debug.externs.js +163 -32
- package/dist/shaka-player.compiled.debug.js +488 -422
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +163 -32
- package/dist/shaka-player.compiled.js +895 -857
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +178 -27
- package/dist/shaka-player.ui.debug.d.ts +178 -27
- package/dist/shaka-player.ui.debug.externs.js +163 -32
- package/dist/shaka-player.ui.debug.js +519 -450
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +163 -32
- package/dist/shaka-player.ui.js +1053 -1015
- package/dist/shaka-player.ui.map +4 -4
- package/docs/design/lcevc-architecture.png +0 -0
- package/docs/design/lcevc-demo.png +0 -0
- package/docs/design/lcevc-integration.md +72 -0
- package/docs/tutorials/fairplay.md +7 -0
- package/docs/tutorials/faq.md +1 -1
- package/docs/tutorials/ui.md +7 -7
- package/docs/tutorials/upgrade.md +6 -0
- package/externs/ima.js +13 -1
- package/externs/lcevc.js +81 -0
- package/externs/mux.js +4 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/player.js +178 -15
- package/lib/abr/simple_abr_manager.js +7 -0
- package/lib/ads/ad_manager.js +0 -8
- package/lib/ads/client_side_ad.js +15 -0
- package/lib/ads/client_side_ad_manager.js +21 -8
- package/lib/ads/server_side_ad.js +15 -0
- package/lib/ads/server_side_ad_manager.js +3 -3
- package/lib/cast/cast_proxy.js +2 -1
- package/lib/cast/cast_receiver.js +19 -5
- package/lib/cast/cast_sender.js +2 -1
- package/lib/cast/cast_utils.js +18 -6
- package/lib/cea/dummy_cea_parser.js +28 -0
- package/lib/cea/i_cea_parser.js +14 -2
- package/lib/cea/mp4_cea_parser.js +87 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +799 -376
- package/lib/lcevc/lcevc_dil.js +152 -0
- package/lib/media/closed_caption_parser.js +9 -6
- package/lib/media/drm_engine.js +14 -6
- package/lib/media/media_source_engine.js +112 -26
- package/lib/media/playhead.js +23 -1
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +145 -24
- package/lib/media/transmuxer.js +79 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +477 -85
- package/lib/polyfill/patchedmediakeys_apple.js +55 -1
- package/lib/text/ui_text_displayer.js +5 -5
- package/lib/text/vtt_text_parser.js +35 -1
- package/lib/util/dom_utils.js +0 -1
- package/lib/util/error.js +17 -0
- package/lib/util/fake_event.js +2 -0
- package/lib/util/id3_utils.js +300 -0
- package/lib/util/manifest_parser_utils.js +4 -0
- package/lib/util/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +15 -5
- package/lib/util/platform.js +9 -1
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +4 -1
- package/roadmap.md +27 -7
- package/test/ads/ad_manager_unit.js +180 -29
- package/test/cast/cast_receiver_integration.js +97 -16
- package/test/cast/cast_receiver_unit.js +17 -12
- package/test/cast/cast_utils_unit.js +4 -1
- package/test/dash/dash_parser_content_protection_unit.js +1 -0
- package/test/dash/dash_parser_live_unit.js +1 -0
- package/test/dash/dash_parser_manifest_unit.js +93 -1
- package/test/dash/dash_parser_segment_base_unit.js +1 -0
- package/test/dash/dash_parser_segment_list_unit.js +1 -0
- package/test/dash/dash_parser_segment_template_unit.js +1 -0
- package/test/demo/demo_unit.js +2 -1
- package/test/hls/hls_live_unit.js +253 -19
- package/test/hls/hls_parser_unit.js +500 -12
- package/test/media/closed_caption_parser_unit.js +2 -1
- package/test/media/drm_engine_integration.js +3 -1
- package/test/media/media_source_engine_integration.js +102 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/playhead_unit.js +41 -0
- package/test/media/streaming_engine_integration.js +17 -1
- package/test/media/streaming_engine_unit.js +180 -24
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +92 -2
- package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
- package/test/test/assets/id3-metadata.aac +0 -0
- package/test/test/assets/id3-metadata.ts +0 -0
- package/test/test/assets/thumbnails-sprites.vtt +10 -0
- package/test/test/assets/thumbnails.vtt +10 -0
- package/test/test/util/dash_parser_util.js +2 -0
- package/test/test/util/fake_ad.js +21 -0
- package/test/test/util/fake_media_source_engine.js +7 -0
- package/test/test/util/id3_generator.js +100 -0
- package/test/test/util/manifest_generator.js +3 -6
- package/test/test/util/simple_fakes.js +7 -0
- package/test/test/util/stream_generator.js +38 -0
- package/test/test/util/test_scheme.js +43 -14
- package/test/text/vtt_text_parser_unit.js +11 -0
- package/test/ui/text_displayer_layout_unit.js +14 -0
- package/test/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/ui/controls.js +4 -1
- package/ui/less/containers.less +7 -0
- package/ui/settings_menu.js +11 -2
- package/ui/ui.js +43 -4
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @summary
|
|
9
|
+
* A helper class used to generate ID3 metadata.
|
|
10
|
+
*/
|
|
11
|
+
shaka.test.Id3Generator = class {
|
|
12
|
+
/**
|
|
13
|
+
* Generate an ID3 from a frames.
|
|
14
|
+
*
|
|
15
|
+
* @param {!Uint8Array} frames
|
|
16
|
+
* @param {boolean=} extendedHeader
|
|
17
|
+
* @return {!Uint8Array}
|
|
18
|
+
*/
|
|
19
|
+
static generateId3(frames, extendedHeader = false) {
|
|
20
|
+
const Id3Generator = shaka.test.Id3Generator;
|
|
21
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
22
|
+
let result = Uint8ArrayUtils.concat(
|
|
23
|
+
Id3Generator.stringToInts_('ID3'),
|
|
24
|
+
new Uint8Array([
|
|
25
|
+
0x03, 0x00, // version 3.0 of ID3v2 (aka ID3v.2.3.0)
|
|
26
|
+
0x40, // flags. include an extended header
|
|
27
|
+
0x00, 0x00, 0x00, 0x00, // size. set later
|
|
28
|
+
// extended header
|
|
29
|
+
0x00, 0x00, 0x00, 0x06, // extended header size. no CRC
|
|
30
|
+
0x00, 0x00, // extended flags
|
|
31
|
+
0x00, 0x00, 0x00, 0x02, // size of padding
|
|
32
|
+
]),
|
|
33
|
+
frames);
|
|
34
|
+
if (!extendedHeader) {
|
|
35
|
+
result = Uint8ArrayUtils.concat(
|
|
36
|
+
Id3Generator.stringToInts_('ID3'),
|
|
37
|
+
new Uint8Array([
|
|
38
|
+
0x03, 0x00, // version 3.0 of ID3v2 (aka ID3v.2.3.0)
|
|
39
|
+
0x00, // flags
|
|
40
|
+
0x00, 0x00, 0x00, 0x00, // size. set later
|
|
41
|
+
]),
|
|
42
|
+
frames);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// size is stored as a sequence of four 7-bit integers with the
|
|
46
|
+
// high bit of each byte set to zero
|
|
47
|
+
const size = result.length - 10;
|
|
48
|
+
|
|
49
|
+
result[6] = (size >>> 21) & 0x7f;
|
|
50
|
+
result[7] = (size >>> 14) & 0x7f;
|
|
51
|
+
result[8] = (size >>> 7) & 0x7f;
|
|
52
|
+
result[9] = size & 0x7f;
|
|
53
|
+
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Generate an ID3 frame from a type and value.
|
|
59
|
+
*
|
|
60
|
+
* @param {string} type
|
|
61
|
+
* @param {!Uint8Array} value
|
|
62
|
+
* @return {!Uint8Array}
|
|
63
|
+
*/
|
|
64
|
+
static generateId3Frame(type, value) {
|
|
65
|
+
goog.asserts.assert(type, 'type must be non-null');
|
|
66
|
+
goog.asserts.assert(type.length == 4, 'type must contain 4 characters');
|
|
67
|
+
const Id3Generator = shaka.test.Id3Generator;
|
|
68
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
69
|
+
const result = Uint8ArrayUtils.concat(
|
|
70
|
+
Id3Generator.stringToInts_(type),
|
|
71
|
+
new Uint8Array([
|
|
72
|
+
0x00, 0x00, 0x00, 0x00, // size
|
|
73
|
+
0xe0, 0x00, // flags
|
|
74
|
+
]),
|
|
75
|
+
value);
|
|
76
|
+
|
|
77
|
+
// set the size
|
|
78
|
+
const size = result.length - 10;
|
|
79
|
+
|
|
80
|
+
result[4] = (size >>> 21) & 0x7f;
|
|
81
|
+
result[5] = (size >>> 14) & 0x7f;
|
|
82
|
+
result[6] = (size >>> 7) & 0x7f;
|
|
83
|
+
result[7] = size & 0x7f;
|
|
84
|
+
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {string} string
|
|
90
|
+
* @return {!Uint8Array}
|
|
91
|
+
* @private
|
|
92
|
+
*/
|
|
93
|
+
static stringToInts_(string) {
|
|
94
|
+
const result = [];
|
|
95
|
+
for (let i = 0; i < string.length; i++) {
|
|
96
|
+
result[i] = string.charCodeAt(i);
|
|
97
|
+
}
|
|
98
|
+
return new Uint8Array(result);
|
|
99
|
+
}
|
|
100
|
+
};
|
|
@@ -393,8 +393,8 @@ shaka.test.ManifestGenerator.DrmInfo = class {
|
|
|
393
393
|
this.videoRobustness = '';
|
|
394
394
|
/** @type {Uint8Array} */
|
|
395
395
|
this.serverCertificate = null;
|
|
396
|
-
/** @type {Array.<shaka.extern.InitDataOverride>} */
|
|
397
|
-
this.initData =
|
|
396
|
+
/** @type {!Array.<shaka.extern.InitDataOverride>} */
|
|
397
|
+
this.initData = [];
|
|
398
398
|
/** @type {Set.<string>} */
|
|
399
399
|
this.keyIds = new Set();
|
|
400
400
|
/** @type {string} */
|
|
@@ -422,10 +422,7 @@ shaka.test.ManifestGenerator.DrmInfo = class {
|
|
|
422
422
|
* @param {!Uint8Array} buffer
|
|
423
423
|
*/
|
|
424
424
|
addInitData(type, buffer) {
|
|
425
|
-
|
|
426
|
-
this.initData = [];
|
|
427
|
-
}
|
|
428
|
-
this.initData.push({initData: buffer, initDataType: type});
|
|
425
|
+
this.initData.push({initData: buffer, initDataType: type, keyId: null});
|
|
429
426
|
}
|
|
430
427
|
|
|
431
428
|
/**
|
|
@@ -339,6 +339,9 @@ shaka.test.FakePlayhead = class {
|
|
|
339
339
|
/** @private {number} */
|
|
340
340
|
this.stallsDetected_ = 0;
|
|
341
341
|
|
|
342
|
+
/** @type {!jasmine.Spy} */
|
|
343
|
+
this.ready = jasmine.createSpy('ready');
|
|
344
|
+
|
|
342
345
|
/** @type {!jasmine.Spy} */
|
|
343
346
|
this.setStartTime = jasmine.createSpy('setStartTime')
|
|
344
347
|
.and.callFake((value) => {
|
|
@@ -509,5 +512,9 @@ shaka.test.FakeTransmuxer = class {
|
|
|
509
512
|
/** @type {!jasmine.Spy} */
|
|
510
513
|
this.transmux =
|
|
511
514
|
jasmine.createSpy('transmux').and.returnValue(Promise.resolve(output));
|
|
515
|
+
|
|
516
|
+
/** @type {!jasmine.Spy} */
|
|
517
|
+
this.getOrginalMimeType =
|
|
518
|
+
jasmine.createSpy('getOrginalMimeType').and.returnValue('mimeType');
|
|
512
519
|
}
|
|
513
520
|
};
|
|
@@ -83,6 +83,44 @@ shaka.test.TSVodStreamGenerator = class {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
+
/**
|
|
87
|
+
* @summary
|
|
88
|
+
* Simulates an HLS, video-on-demand, aac stream. The StreamGenerator assumes
|
|
89
|
+
* the stream contains a single segment.
|
|
90
|
+
*
|
|
91
|
+
* @implements {shaka.test.IStreamGenerator}
|
|
92
|
+
*/
|
|
93
|
+
shaka.test.AACVodStreamGenerator = class {
|
|
94
|
+
/** @param {string} segmentUri The URI of the segment. */
|
|
95
|
+
constructor(segmentUri) {
|
|
96
|
+
/** @private {string} */
|
|
97
|
+
this.segmentUri_ = segmentUri;
|
|
98
|
+
|
|
99
|
+
/** @private {ArrayBuffer} */
|
|
100
|
+
this.segment_ = null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @override */
|
|
104
|
+
async init() {
|
|
105
|
+
const segment = await shaka.test.Util.fetch(this.segmentUri_);
|
|
106
|
+
this.segment_ = segment;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** @override */
|
|
110
|
+
getInitSegment(time) {
|
|
111
|
+
goog.asserts.assert(false, 'getInitSegment not implemented for HLS VOD.');
|
|
112
|
+
return new ArrayBuffer(0);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/** @override */
|
|
116
|
+
getSegment(position, wallClockTime) {
|
|
117
|
+
goog.asserts.assert(
|
|
118
|
+
this.segment_,
|
|
119
|
+
'init() must be called before getSegment().');
|
|
120
|
+
return this.segment_;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
|
|
86
124
|
/**
|
|
87
125
|
* @summary
|
|
88
126
|
* Simulates a DASH, video-on-demand, MP4 stream. The StreamGenerator loops a
|
|
@@ -52,7 +52,8 @@ let ExtraMetadataType;
|
|
|
52
52
|
* duration: number,
|
|
53
53
|
* licenseServers: (!Object.<string, string>|undefined),
|
|
54
54
|
* licenseRequestHeaders: (!Object.<string, string>|undefined),
|
|
55
|
-
*
|
|
55
|
+
* customizeStream: (function(shaka.test.ManifestGenerator.Stream)|undefined),
|
|
56
|
+
* sequenceMode: (boolean|undefined)
|
|
56
57
|
* }}
|
|
57
58
|
*/
|
|
58
59
|
let MetadataType;
|
|
@@ -174,6 +175,9 @@ shaka.test.TestScheme = class {
|
|
|
174
175
|
if (metadata.segmentUri.includes('.ts')) {
|
|
175
176
|
return new shaka.test.TSVodStreamGenerator(metadata.segmentUri);
|
|
176
177
|
}
|
|
178
|
+
if (metadata.segmentUri.includes('.aac')) {
|
|
179
|
+
return new shaka.test.AACVodStreamGenerator(metadata.segmentUri);
|
|
180
|
+
}
|
|
177
181
|
return new shaka.test.Mp4VodStreamGenerator(
|
|
178
182
|
metadata.initSegmentUri, metadata.mdhdOffset, metadata.segmentUri,
|
|
179
183
|
metadata.tfdtOffset, metadata.segmentDuration);
|
|
@@ -243,6 +247,10 @@ shaka.test.TestScheme = class {
|
|
|
243
247
|
});
|
|
244
248
|
}
|
|
245
249
|
}
|
|
250
|
+
|
|
251
|
+
if (data.customizeStream) {
|
|
252
|
+
data.customizeStream(stream);
|
|
253
|
+
}
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
/**
|
|
@@ -279,7 +287,7 @@ shaka.test.TestScheme = class {
|
|
|
279
287
|
|
|
280
288
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
281
289
|
manifest.presentationTimeline.setDuration(data.duration);
|
|
282
|
-
manifest.sequenceMode = data.sequenceMode;
|
|
290
|
+
manifest.sequenceMode = data.sequenceMode || false;
|
|
283
291
|
|
|
284
292
|
const videoResolutions = data.videoResolutions || [undefined];
|
|
285
293
|
const audioLanguages = data.audioLanguages ||
|
|
@@ -513,7 +521,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
513
521
|
audio: sintelAudioSegment,
|
|
514
522
|
text: vttSegment,
|
|
515
523
|
duration: 30,
|
|
516
|
-
sequenceMode: false,
|
|
517
524
|
},
|
|
518
525
|
|
|
519
526
|
// Like 'sintel', but flagged as sequence mode.
|
|
@@ -530,7 +537,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
530
537
|
video: sintelVideoSegment,
|
|
531
538
|
audio: sintelAudioSegment,
|
|
532
539
|
duration: 300,
|
|
533
|
-
sequenceMode: false,
|
|
534
540
|
},
|
|
535
541
|
|
|
536
542
|
// Like 'sintel' above, but with languages and delayed setup.
|
|
@@ -547,7 +553,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
547
553
|
language: 'fa', // Necessary to repro #1696
|
|
548
554
|
}),
|
|
549
555
|
duration: 30,
|
|
550
|
-
sequenceMode: false,
|
|
551
556
|
},
|
|
552
557
|
|
|
553
558
|
'sintel_multi_lingual_multi_res': {
|
|
@@ -561,20 +566,17 @@ shaka.test.TestScheme.DATA = {
|
|
|
561
566
|
audioLanguages: ['en', 'es'],
|
|
562
567
|
textLanguages: ['zh', 'fr'],
|
|
563
568
|
duration: 30,
|
|
564
|
-
sequenceMode: false,
|
|
565
569
|
},
|
|
566
570
|
|
|
567
571
|
'sintel_audio_only': {
|
|
568
572
|
audio: sintelAudioSegment,
|
|
569
573
|
duration: 30,
|
|
570
|
-
sequenceMode: false,
|
|
571
574
|
},
|
|
572
575
|
|
|
573
576
|
'sintel_no_text': {
|
|
574
577
|
video: sintelVideoSegment,
|
|
575
578
|
audio: sintelAudioSegment,
|
|
576
579
|
duration: 30,
|
|
577
|
-
sequenceMode: false,
|
|
578
580
|
},
|
|
579
581
|
|
|
580
582
|
// https://github.com/shaka-project/shaka-player/issues/2553
|
|
@@ -583,7 +585,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
583
585
|
text: vttSegment,
|
|
584
586
|
textLanguages: ['de', 'de'], // one of these is the "forced subs" track
|
|
585
587
|
duration: 30,
|
|
586
|
-
sequenceMode: false,
|
|
587
588
|
},
|
|
588
589
|
|
|
589
590
|
'sintel-enc': {
|
|
@@ -592,7 +593,19 @@ shaka.test.TestScheme.DATA = {
|
|
|
592
593
|
text: vttSegment,
|
|
593
594
|
licenseServers: widevineDrmServers,
|
|
594
595
|
duration: 30,
|
|
595
|
-
|
|
596
|
+
},
|
|
597
|
+
|
|
598
|
+
// Equivalent to what you get with HLS METHOD=SAMPLE-AES, KEYFORMAT=identity.
|
|
599
|
+
// Requires explicit clear keys or license server configuration.
|
|
600
|
+
'sintel-hls-clearkey': {
|
|
601
|
+
video: sintelEncryptedVideo,
|
|
602
|
+
audio: sintelEncryptedAudio,
|
|
603
|
+
duration: 30,
|
|
604
|
+
sequenceMode: true,
|
|
605
|
+
customizeStream: (stream) => {
|
|
606
|
+
stream.encrypted = true;
|
|
607
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
608
|
+
},
|
|
596
609
|
},
|
|
597
610
|
|
|
598
611
|
'multidrm': {
|
|
@@ -602,7 +615,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
602
615
|
licenseServers: axinomDrmServers,
|
|
603
616
|
licenseRequestHeaders: axinomDrmHeaders,
|
|
604
617
|
duration: 30,
|
|
605
|
-
sequenceMode: false,
|
|
606
618
|
},
|
|
607
619
|
|
|
608
620
|
'multidrm_no_init_data': {
|
|
@@ -615,7 +627,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
615
627
|
licenseServers: axinomDrmServers,
|
|
616
628
|
licenseRequestHeaders: axinomDrmHeaders,
|
|
617
629
|
duration: 30,
|
|
618
|
-
sequenceMode: false,
|
|
619
630
|
},
|
|
620
631
|
|
|
621
632
|
'cea-708_ts': {
|
|
@@ -629,7 +640,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
629
640
|
mimeType: 'application/cea-608',
|
|
630
641
|
},
|
|
631
642
|
duration: 30,
|
|
632
|
-
sequenceMode: false,
|
|
633
643
|
},
|
|
634
644
|
|
|
635
645
|
'cea-708_mp4': {
|
|
@@ -644,7 +654,26 @@ shaka.test.TestScheme.DATA = {
|
|
|
644
654
|
closedCaptions: new Map([['CC1', 'en']]),
|
|
645
655
|
},
|
|
646
656
|
duration: 30,
|
|
647
|
-
|
|
657
|
+
},
|
|
658
|
+
|
|
659
|
+
'id3-metadata_ts': {
|
|
660
|
+
audio: {
|
|
661
|
+
segmentUri: '/base/test/test/assets/id3-metadata.ts',
|
|
662
|
+
mimeType: 'video/mp2t',
|
|
663
|
+
codecs: 'mp4a.40.5',
|
|
664
|
+
segmentDuration: 4.99,
|
|
665
|
+
},
|
|
666
|
+
duration: 4.99,
|
|
667
|
+
},
|
|
668
|
+
|
|
669
|
+
'id3-metadata_aac': {
|
|
670
|
+
audio: {
|
|
671
|
+
segmentUri: '/base/test/test/assets/id3-metadata.aac',
|
|
672
|
+
mimeType: 'audio/aac',
|
|
673
|
+
codecs: '',
|
|
674
|
+
segmentDuration: 9.98458,
|
|
675
|
+
},
|
|
676
|
+
duration: 9.98458,
|
|
648
677
|
},
|
|
649
678
|
};
|
|
650
679
|
|
|
@@ -849,6 +849,17 @@ describe('VttTextParser', () => {
|
|
|
849
849
|
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
|
850
850
|
});
|
|
851
851
|
|
|
852
|
+
it('support escaped html payload', () => {
|
|
853
|
+
verifyHelper(
|
|
854
|
+
[
|
|
855
|
+
{startTime: 20.1, endTime: 40.505, payload: '"Test & 1"'},
|
|
856
|
+
],
|
|
857
|
+
'WEBVTT\n\n' +
|
|
858
|
+
'00:00:20.100 --> 00:00:40.505\n' +
|
|
859
|
+
'"Test & 1"',
|
|
860
|
+
{periodStart: 0, segmentStart: 0, segmentEnd: 0, vttOffset: 0});
|
|
861
|
+
});
|
|
862
|
+
|
|
852
863
|
it('supports specific style blocks', () => {
|
|
853
864
|
verifyHelper(
|
|
854
865
|
[
|
|
@@ -444,6 +444,20 @@ filterDescribe('TextDisplayer layout', supportsScreenshots, () => {
|
|
|
444
444
|
|
|
445
445
|
await checkScreenshot(prefix, 'deeply-nested-cues');
|
|
446
446
|
});
|
|
447
|
+
|
|
448
|
+
// Regression test for #4567, in which "end" line alignment for VTT was
|
|
449
|
+
// inverted.
|
|
450
|
+
it('line alignment', async () => {
|
|
451
|
+
const cue = new shaka.text.Cue(0, 1, 'Captain\'s log, stardate 41636.9');
|
|
452
|
+
|
|
453
|
+
cue.line = 70;
|
|
454
|
+
cue.lineInterpretation = shaka.text.Cue.lineInterpretation.PERCENTAGE;
|
|
455
|
+
cue.lineAlign = shaka.text.Cue.lineAlign.END;
|
|
456
|
+
|
|
457
|
+
textDisplayer.append([cue]);
|
|
458
|
+
|
|
459
|
+
await checkScreenshot(prefix, 'line-alignment');
|
|
460
|
+
});
|
|
447
461
|
}
|
|
448
462
|
|
|
449
463
|
/** @param {!HTMLElement} element */
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
describe('Id3Utils', () => {
|
|
8
|
+
const Id3Utils = shaka.util.Id3Utils;
|
|
9
|
+
const Id3Generator = shaka.test.Id3Generator;
|
|
10
|
+
const BufferUtils = shaka.util.BufferUtils;
|
|
11
|
+
|
|
12
|
+
it('no valid data produces empty output', () => {
|
|
13
|
+
expect(Id3Utils.getID3Frames(new Uint8Array([]))).toEqual([]);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('parse a TXXX frame', () => {
|
|
17
|
+
const txxxValue = new Uint8Array([3, 65, 0, 83, 104, 97, 107, 97]);
|
|
18
|
+
const txxxFrame = Id3Generator.generateId3Frame('TXXX', txxxValue);
|
|
19
|
+
const txxxID3 = Id3Generator.generateId3(txxxFrame);
|
|
20
|
+
const expectedID3 = [
|
|
21
|
+
{
|
|
22
|
+
key: 'TXXX',
|
|
23
|
+
description: 'A',
|
|
24
|
+
data: 'Shaka',
|
|
25
|
+
},
|
|
26
|
+
];
|
|
27
|
+
expect(Id3Utils.getID3Frames(txxxID3)).toEqual(expectedID3);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('parse a TXXX frame with extended header', () => {
|
|
31
|
+
const txxxValue = new Uint8Array([3, 65, 0, 83, 104, 97, 107, 97]);
|
|
32
|
+
const txxxFrame = Id3Generator.generateId3Frame('TXXX', txxxValue);
|
|
33
|
+
const txxxID3 = Id3Generator.generateId3(txxxFrame, true);
|
|
34
|
+
const expectedID3 = [
|
|
35
|
+
{
|
|
36
|
+
key: 'TXXX',
|
|
37
|
+
description: 'A',
|
|
38
|
+
data: 'Shaka',
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
expect(Id3Utils.getID3Frames(txxxID3)).toEqual(expectedID3);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('parse a TCOP frame', () => {
|
|
45
|
+
const tcopValue = new Uint8Array(
|
|
46
|
+
[3, 83, 104, 97, 107, 97, 32, 50, 48, 49, 54]);
|
|
47
|
+
const tcopFrame = Id3Generator.generateId3Frame('TCOP', tcopValue);
|
|
48
|
+
const tcopID3 = Id3Generator.generateId3(tcopFrame);
|
|
49
|
+
const expectedID3 = [
|
|
50
|
+
{
|
|
51
|
+
key: 'TCOP',
|
|
52
|
+
description: '',
|
|
53
|
+
data: 'Shaka 2016',
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
expect(Id3Utils.getID3Frames(tcopID3)).toEqual(expectedID3);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('parse a WXXX frame', () => {
|
|
60
|
+
const wxxxValue = new Uint8Array(
|
|
61
|
+
[3, 65, 0, 103, 111, 111, 103, 108, 101, 46, 99, 111, 109]);
|
|
62
|
+
const wxxxFrame = Id3Generator.generateId3Frame('WXXX', wxxxValue);
|
|
63
|
+
const wxxxID3 = Id3Generator.generateId3(wxxxFrame);
|
|
64
|
+
const expectedID3 = [
|
|
65
|
+
{
|
|
66
|
+
key: 'WXXX',
|
|
67
|
+
description: 'A',
|
|
68
|
+
data: 'google.com',
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
expect(Id3Utils.getID3Frames(wxxxID3)).toEqual(expectedID3);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('parse a WCOP frame', () => {
|
|
75
|
+
const wcopValue = new Uint8Array(
|
|
76
|
+
[103, 111, 111, 103, 108, 101, 46, 99, 111, 109]);
|
|
77
|
+
const wcopFrame = Id3Generator.generateId3Frame('WCOP', wcopValue);
|
|
78
|
+
const wcopID3 = Id3Generator.generateId3(wcopFrame);
|
|
79
|
+
const expectedID3 = [
|
|
80
|
+
{
|
|
81
|
+
key: 'WCOP',
|
|
82
|
+
description: '',
|
|
83
|
+
data: 'google.com',
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
expect(Id3Utils.getID3Frames(wcopID3)).toEqual(expectedID3);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('parse a PRIV frame', () => {
|
|
90
|
+
const privValue = new Uint8Array([65, 0, 83, 104, 97, 107]);
|
|
91
|
+
const privFrame = Id3Generator.generateId3Frame('PRIV', privValue);
|
|
92
|
+
const privID3 = Id3Generator.generateId3(privFrame);
|
|
93
|
+
const expectedID3 = [
|
|
94
|
+
{
|
|
95
|
+
key: 'PRIV',
|
|
96
|
+
description: 'A',
|
|
97
|
+
data: BufferUtils.toArrayBuffer(new Uint8Array([83, 104, 97, 107])),
|
|
98
|
+
},
|
|
99
|
+
];
|
|
100
|
+
expect(Id3Utils.getID3Frames(privID3)).toEqual(expectedID3);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('parse an unknown frame', () => {
|
|
104
|
+
const unknownValue = new Uint8Array([83, 104, 97, 107]);
|
|
105
|
+
const unknownFrame = Id3Generator.generateId3Frame('XXXX', unknownValue);
|
|
106
|
+
const unknownID3 = Id3Generator.generateId3(unknownFrame);
|
|
107
|
+
const expectedID3 = [
|
|
108
|
+
{
|
|
109
|
+
key: 'XXXX',
|
|
110
|
+
description: '',
|
|
111
|
+
data: BufferUtils.toArrayBuffer(new Uint8Array([83, 104, 97, 107])),
|
|
112
|
+
},
|
|
113
|
+
];
|
|
114
|
+
expect(Id3Utils.getID3Frames(unknownID3)).toEqual(expectedID3);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/*! @license
|
|
2
|
+
* Shaka Player
|
|
3
|
+
* Copyright 2016 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
describe('TsParser', () => {
|
|
8
|
+
const Util = shaka.test.Util;
|
|
9
|
+
const BufferUtils = shaka.util.BufferUtils;
|
|
10
|
+
|
|
11
|
+
it('probes a TS segment', async () => {
|
|
12
|
+
const responses = await Promise.all([
|
|
13
|
+
Util.fetch('/base/test/test/assets/video.ts'),
|
|
14
|
+
Util.fetch('/base/test/test/assets/audio.ts'),
|
|
15
|
+
]);
|
|
16
|
+
const videoSegment = BufferUtils.toUint8(responses[0]);
|
|
17
|
+
const audioSegment = BufferUtils.toUint8(responses[1]);
|
|
18
|
+
expect(shaka.util.TsParser.probe(videoSegment)).toBeTruthy();
|
|
19
|
+
expect(shaka.util.TsParser.probe(audioSegment)).toBeTruthy();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('probes a non TS segment', async () => {
|
|
23
|
+
const responses = await Promise.all([
|
|
24
|
+
Util.fetch('/base/test/test/assets/small.mp4'),
|
|
25
|
+
]);
|
|
26
|
+
const nonTsSegment = BufferUtils.toUint8(responses[0]);
|
|
27
|
+
expect(shaka.util.TsParser.probe(nonTsSegment)).toBeFalsy();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('parses a TS segment', async () => {
|
|
31
|
+
const responses = await Promise.all([
|
|
32
|
+
Util.fetch('/base/test/test/assets/video.ts'),
|
|
33
|
+
Util.fetch('/base/test/test/assets/audio.ts'),
|
|
34
|
+
]);
|
|
35
|
+
const videoSegment = BufferUtils.toUint8(responses[0]);
|
|
36
|
+
const audioSegment = BufferUtils.toUint8(responses[1]);
|
|
37
|
+
expect(new shaka.util.TsParser().parse(videoSegment)).toBeDefined();
|
|
38
|
+
expect(new shaka.util.TsParser().parse(audioSegment)).toBeDefined();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('parses a TS segment with metadata', async () => {
|
|
42
|
+
const responses = await Promise.all([
|
|
43
|
+
Util.fetch('/base/test/test/assets/id3-metadata.ts'),
|
|
44
|
+
]);
|
|
45
|
+
const tsSegment = BufferUtils.toUint8(responses[0]);
|
|
46
|
+
const metadata = new shaka.util.TsParser().parse(tsSegment)
|
|
47
|
+
.getMetadata();
|
|
48
|
+
expect(metadata).toBeTruthy();
|
|
49
|
+
expect(metadata.length).toBe(2);
|
|
50
|
+
const firstMetadata = metadata[0];
|
|
51
|
+
expect(firstMetadata.frames.length).toBe(2);
|
|
52
|
+
const secondMetadata = metadata[1];
|
|
53
|
+
expect(secondMetadata.frames.length).toBe(2);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('get the start time from a TS segment', async () => {
|
|
57
|
+
const responses = await Promise.all([
|
|
58
|
+
Util.fetch('/base/test/test/assets/id3-metadata.ts'),
|
|
59
|
+
]);
|
|
60
|
+
const tsSegment = BufferUtils.toUint8(responses[0]);
|
|
61
|
+
const starttime = new shaka.util.TsParser().parse(tsSegment)
|
|
62
|
+
.getStartTime();
|
|
63
|
+
expect(starttime.audio).toBeCloseTo(90019.586, 3);
|
|
64
|
+
expect(starttime.video).toBe(null);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('get the codecs from a TS segment', async () => {
|
|
68
|
+
const responses = await Promise.all([
|
|
69
|
+
Util.fetch('/base/test/test/assets/id3-metadata.ts'),
|
|
70
|
+
]);
|
|
71
|
+
const tsSegment = BufferUtils.toUint8(responses[0]);
|
|
72
|
+
const codecs = new shaka.util.TsParser().parse(tsSegment)
|
|
73
|
+
.getCodecs();
|
|
74
|
+
expect(codecs.audio).toBe('aac');
|
|
75
|
+
expect(codecs.video).toBe('');
|
|
76
|
+
});
|
|
77
|
+
});
|
package/ui/controls.js
CHANGED
|
@@ -1024,7 +1024,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
1024
1024
|
if (!this.video_ ||
|
|
1025
1025
|
this.video_.readyState == 0 ||
|
|
1026
1026
|
this.castProxy_.isCasting() ||
|
|
1027
|
-
!this.config_.enableFullscreenOnRotation
|
|
1027
|
+
!this.config_.enableFullscreenOnRotation ||
|
|
1028
|
+
!this.isFullScreenSupported()) {
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1028
1031
|
|
|
1029
1032
|
if (screen.orientation.type.includes('landscape') &&
|
|
1030
1033
|
!document.fullscreenElement) {
|
package/ui/less/containers.less
CHANGED
|
@@ -98,6 +98,13 @@
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/* A container for all canvas for LCEVC decoding
|
|
102
|
+
* Sits inside .shaka-video-container, on top of (Z axis) .shaka-video, and
|
|
103
|
+
* below (Y axis) .shaka-play-button-container. */
|
|
104
|
+
.shaka-canvas-container {
|
|
105
|
+
.overlay-child();
|
|
106
|
+
}
|
|
107
|
+
|
|
101
108
|
/* Container for controls positioned at the bottom of the video container:
|
|
102
109
|
* controls button panel and the seek bar. */
|
|
103
110
|
.shaka-bottom-controls {
|
package/ui/settings_menu.js
CHANGED
|
@@ -12,6 +12,7 @@ goog.require('shaka.ui.Enums');
|
|
|
12
12
|
goog.require('shaka.ui.Utils');
|
|
13
13
|
goog.require('shaka.util.Dom');
|
|
14
14
|
goog.require('shaka.util.FakeEvent');
|
|
15
|
+
goog.require('shaka.util.Iterables');
|
|
15
16
|
goog.requireType('shaka.ui.Controls');
|
|
16
17
|
|
|
17
18
|
|
|
@@ -116,8 +117,16 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
|
|
|
116
117
|
this.eventManager.listen(this.backButton, 'click', () => {
|
|
117
118
|
shaka.ui.Utils.setDisplay(this.parent, true);
|
|
118
119
|
|
|
119
|
-
|
|
120
|
-
(
|
|
120
|
+
const isDisplayed =
|
|
121
|
+
(element) => element.classList.contains('shaka-hidden') == false;
|
|
122
|
+
|
|
123
|
+
const Iterables = shaka.util.Iterables;
|
|
124
|
+
if (Iterables.some(this.parent.childNodes, isDisplayed)) {
|
|
125
|
+
// Focus on the first visible child of the overflow menu
|
|
126
|
+
const visibleElements =
|
|
127
|
+
Iterables.filter(this.parent.childNodes, isDisplayed);
|
|
128
|
+
/** @type {!HTMLElement} */ (visibleElements[0]).focus();
|
|
129
|
+
}
|
|
121
130
|
|
|
122
131
|
// Make sure controls are displayed
|
|
123
132
|
this.controls.computeOpacity();
|