shaka-player 4.2.5 → 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/.github/workflows/sync-labels.yaml +1 -1
- package/.github/workflows/update-issues.yaml +1 -1
- package/AUTHORS +4 -0
- package/CHANGELOG.md +114 -74
- package/CONTRIBUTORS +4 -0
- package/README.md +14 -0
- package/conditional/dummy_cast_proxy.js +120 -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 +107 -88
- package/dist/demo.compiled.debug.map +4 -4
- package/dist/demo.compiled.js +107 -88
- package/dist/demo.compiled.map +4 -4
- package/dist/demo.css +1 -1
- package/dist/demo.css.map +1 -1
- package/dist/deps.js +21 -13
- package/dist/locales.js +31 -31
- package/dist/receiver.compiled.debug.js +75 -77
- package/dist/receiver.compiled.debug.map +3 -3
- package/dist/receiver.compiled.js +75 -77
- package/dist/receiver.compiled.map +3 -3
- package/dist/shaka-player.compiled.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.d.ts +181 -27
- package/dist/shaka-player.compiled.debug.externs.js +169 -31
- package/dist/shaka-player.compiled.debug.js +510 -450
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +169 -31
- package/dist/shaka-player.compiled.js +912 -874
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +181 -27
- package/dist/shaka-player.ui.debug.d.ts +181 -27
- package/dist/shaka-player.ui.debug.externs.js +169 -31
- package/dist/shaka-player.ui.debug.js +533 -471
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +169 -31
- package/dist/shaka-player.ui.js +1073 -1036
- 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 +1 -42
- package/externs/shaka/ads.js +11 -1
- package/externs/shaka/manifest_parser.js +5 -1
- package/externs/shaka/mp4_parser.js +3 -0
- 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_utils.js +2 -0
- 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 +108 -5
- package/lib/config/auto_show_text.js +33 -0
- package/lib/dash/dash_parser.js +1 -1
- package/lib/hls/hls_parser.js +808 -384
- 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_capabilities.js +36 -0
- package/lib/media/media_source_engine.js +113 -28
- package/lib/media/presentation_timeline.js +26 -11
- package/lib/media/segment_reference.js +49 -1
- package/lib/media/streaming_engine.js +142 -12
- package/lib/media/transmuxer.js +78 -21
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +456 -77
- package/lib/polyfill/media_capabilities.js +9 -5
- package/lib/polyfill/patchedmediakeys_apple.js +63 -4
- 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/mime_utils.js +3 -6
- package/lib/util/mp4_box_parsers.js +39 -5
- package/lib/util/mp4_parser.js +35 -0
- package/lib/util/player_configuration.js +23 -1
- package/lib/util/stream_utils.js +5 -3
- package/lib/util/ts_parser.js +499 -0
- package/maintained-branches.md +2 -2
- package/package.json +7 -4
- package/roadmap.md +5 -3
- package/test/ads/ad_manager_unit.js +182 -29
- package/test/cast/cast_utils_unit.js +3 -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 +631 -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 +71 -6
- package/test/media/media_source_engine_unit.js +10 -5
- package/test/media/streaming_engine_integration.js +4 -1
- package/test/media/streaming_engine_unit.js +144 -1
- package/test/media/transmuxer_integration.js +21 -10
- package/test/player_integration.js +224 -82
- package/test/player_unit.js +5 -2
- package/test/polyfill/media_capabilities_unit.js +21 -23
- package/test/polyfill/patchedmediakeys_apple_unit.js +130 -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 +4 -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/util/id3_utils_unit.js +116 -0
- package/test/util/ts_parser_unit.js +77 -0
- package/third_party/closure-uri/uri.js +1 -1
- package/ui/less/containers.less +7 -0
- 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
|
/**
|
|
@@ -512,5 +512,9 @@ shaka.test.FakeTransmuxer = class {
|
|
|
512
512
|
/** @type {!jasmine.Spy} */
|
|
513
513
|
this.transmux =
|
|
514
514
|
jasmine.createSpy('transmux').and.returnValue(Promise.resolve(output));
|
|
515
|
+
|
|
516
|
+
/** @type {!jasmine.Spy} */
|
|
517
|
+
this.getOrginalMimeType =
|
|
518
|
+
jasmine.createSpy('getOrginalMimeType').and.returnValue('mimeType');
|
|
515
519
|
}
|
|
516
520
|
};
|
|
@@ -287,6 +287,44 @@ shaka.test.TSVodStreamGenerator = class {
|
|
|
287
287
|
}
|
|
288
288
|
};
|
|
289
289
|
|
|
290
|
+
/**
|
|
291
|
+
* @summary
|
|
292
|
+
* Simulates an HLS, video-on-demand, aac stream. The StreamGenerator assumes
|
|
293
|
+
* the stream contains a single segment.
|
|
294
|
+
*
|
|
295
|
+
* @implements {shaka.test.IStreamGenerator}
|
|
296
|
+
*/
|
|
297
|
+
shaka.test.AACVodStreamGenerator = class {
|
|
298
|
+
/** @param {string} segmentUri The URI of the segment. */
|
|
299
|
+
constructor(segmentUri) {
|
|
300
|
+
/** @private {string} */
|
|
301
|
+
this.segmentUri_ = segmentUri;
|
|
302
|
+
|
|
303
|
+
/** @private {ArrayBuffer} */
|
|
304
|
+
this.segment_ = null;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** @override */
|
|
308
|
+
async init() {
|
|
309
|
+
const segment = await shaka.test.Util.fetch(this.segmentUri_);
|
|
310
|
+
this.segment_ = segment;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** @override */
|
|
314
|
+
getInitSegment(time) {
|
|
315
|
+
goog.asserts.assert(false, 'getInitSegment not implemented for HLS VOD.');
|
|
316
|
+
return new ArrayBuffer(0);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** @override */
|
|
320
|
+
getSegment(position, wallClockTime) {
|
|
321
|
+
goog.asserts.assert(
|
|
322
|
+
this.segment_,
|
|
323
|
+
'init() must be called before getSegment().');
|
|
324
|
+
return this.segment_;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
|
|
290
328
|
/**
|
|
291
329
|
* @summary
|
|
292
330
|
* 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;
|
|
@@ -175,6 +176,9 @@ shaka.test.TestScheme = class {
|
|
|
175
176
|
return new shaka.test.TSVodStreamGenerator(
|
|
176
177
|
metadata.segmentUri, metadata.segmentDuration);
|
|
177
178
|
}
|
|
179
|
+
if (metadata.segmentUri.includes('.aac')) {
|
|
180
|
+
return new shaka.test.AACVodStreamGenerator(metadata.segmentUri);
|
|
181
|
+
}
|
|
178
182
|
return new shaka.test.Mp4VodStreamGenerator(
|
|
179
183
|
metadata.initSegmentUri, metadata.mdhdOffset, metadata.segmentUri,
|
|
180
184
|
metadata.tfdtOffset, metadata.segmentDuration);
|
|
@@ -244,6 +248,10 @@ shaka.test.TestScheme = class {
|
|
|
244
248
|
});
|
|
245
249
|
}
|
|
246
250
|
}
|
|
251
|
+
|
|
252
|
+
if (data.customizeStream) {
|
|
253
|
+
data.customizeStream(stream);
|
|
254
|
+
}
|
|
247
255
|
}
|
|
248
256
|
|
|
249
257
|
/**
|
|
@@ -280,7 +288,7 @@ shaka.test.TestScheme = class {
|
|
|
280
288
|
|
|
281
289
|
const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
|
|
282
290
|
manifest.presentationTimeline.setDuration(data.duration);
|
|
283
|
-
manifest.sequenceMode = data.sequenceMode;
|
|
291
|
+
manifest.sequenceMode = data.sequenceMode || false;
|
|
284
292
|
|
|
285
293
|
const videoResolutions = data.videoResolutions || [undefined];
|
|
286
294
|
const audioLanguages = data.audioLanguages ||
|
|
@@ -514,7 +522,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
514
522
|
audio: sintelAudioSegment,
|
|
515
523
|
text: vttSegment,
|
|
516
524
|
duration: 30,
|
|
517
|
-
sequenceMode: false,
|
|
518
525
|
},
|
|
519
526
|
|
|
520
527
|
// Like 'sintel', but flagged as sequence mode.
|
|
@@ -531,7 +538,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
531
538
|
video: sintelVideoSegment,
|
|
532
539
|
audio: sintelAudioSegment,
|
|
533
540
|
duration: 300,
|
|
534
|
-
sequenceMode: false,
|
|
535
541
|
},
|
|
536
542
|
|
|
537
543
|
// Like 'sintel' above, but with languages and delayed setup.
|
|
@@ -548,7 +554,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
548
554
|
language: 'fa', // Necessary to repro #1696
|
|
549
555
|
}),
|
|
550
556
|
duration: 30,
|
|
551
|
-
sequenceMode: false,
|
|
552
557
|
},
|
|
553
558
|
|
|
554
559
|
'sintel_multi_lingual_multi_res': {
|
|
@@ -562,20 +567,17 @@ shaka.test.TestScheme.DATA = {
|
|
|
562
567
|
audioLanguages: ['en', 'es'],
|
|
563
568
|
textLanguages: ['zh', 'fr'],
|
|
564
569
|
duration: 30,
|
|
565
|
-
sequenceMode: false,
|
|
566
570
|
},
|
|
567
571
|
|
|
568
572
|
'sintel_audio_only': {
|
|
569
573
|
audio: sintelAudioSegment,
|
|
570
574
|
duration: 30,
|
|
571
|
-
sequenceMode: false,
|
|
572
575
|
},
|
|
573
576
|
|
|
574
577
|
'sintel_no_text': {
|
|
575
578
|
video: sintelVideoSegment,
|
|
576
579
|
audio: sintelAudioSegment,
|
|
577
580
|
duration: 30,
|
|
578
|
-
sequenceMode: false,
|
|
579
581
|
},
|
|
580
582
|
|
|
581
583
|
// https://github.com/shaka-project/shaka-player/issues/2553
|
|
@@ -584,7 +586,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
584
586
|
text: vttSegment,
|
|
585
587
|
textLanguages: ['de', 'de'], // one of these is the "forced subs" track
|
|
586
588
|
duration: 30,
|
|
587
|
-
sequenceMode: false,
|
|
588
589
|
},
|
|
589
590
|
|
|
590
591
|
'sintel-enc': {
|
|
@@ -593,7 +594,19 @@ shaka.test.TestScheme.DATA = {
|
|
|
593
594
|
text: vttSegment,
|
|
594
595
|
licenseServers: widevineDrmServers,
|
|
595
596
|
duration: 30,
|
|
596
|
-
|
|
597
|
+
},
|
|
598
|
+
|
|
599
|
+
// Equivalent to what you get with HLS METHOD=SAMPLE-AES, KEYFORMAT=identity.
|
|
600
|
+
// Requires explicit clear keys or license server configuration.
|
|
601
|
+
'sintel-hls-clearkey': {
|
|
602
|
+
video: sintelEncryptedVideo,
|
|
603
|
+
audio: sintelEncryptedAudio,
|
|
604
|
+
duration: 30,
|
|
605
|
+
sequenceMode: true,
|
|
606
|
+
customizeStream: (stream) => {
|
|
607
|
+
stream.encrypted = true;
|
|
608
|
+
stream.addDrmInfo('org.w3.clearkey');
|
|
609
|
+
},
|
|
597
610
|
},
|
|
598
611
|
|
|
599
612
|
'multidrm': {
|
|
@@ -603,7 +616,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
603
616
|
licenseServers: axinomDrmServers,
|
|
604
617
|
licenseRequestHeaders: axinomDrmHeaders,
|
|
605
618
|
duration: 30,
|
|
606
|
-
sequenceMode: false,
|
|
607
619
|
},
|
|
608
620
|
|
|
609
621
|
'multidrm_no_init_data': {
|
|
@@ -616,7 +628,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
616
628
|
licenseServers: axinomDrmServers,
|
|
617
629
|
licenseRequestHeaders: axinomDrmHeaders,
|
|
618
630
|
duration: 30,
|
|
619
|
-
sequenceMode: false,
|
|
620
631
|
},
|
|
621
632
|
|
|
622
633
|
'cea-708_ts': {
|
|
@@ -630,7 +641,6 @@ shaka.test.TestScheme.DATA = {
|
|
|
630
641
|
mimeType: 'application/cea-608',
|
|
631
642
|
},
|
|
632
643
|
duration: 30,
|
|
633
|
-
sequenceMode: false,
|
|
634
644
|
},
|
|
635
645
|
|
|
636
646
|
'cea-708_mp4': {
|
|
@@ -645,7 +655,26 @@ shaka.test.TestScheme.DATA = {
|
|
|
645
655
|
closedCaptions: new Map([['CC1', 'en']]),
|
|
646
656
|
},
|
|
647
657
|
duration: 30,
|
|
648
|
-
|
|
658
|
+
},
|
|
659
|
+
|
|
660
|
+
'id3-metadata_ts': {
|
|
661
|
+
audio: {
|
|
662
|
+
segmentUri: '/base/test/test/assets/id3-metadata.ts',
|
|
663
|
+
mimeType: 'video/mp2t',
|
|
664
|
+
codecs: 'mp4a.40.5',
|
|
665
|
+
segmentDuration: 5,
|
|
666
|
+
},
|
|
667
|
+
duration: 4.99,
|
|
668
|
+
},
|
|
669
|
+
|
|
670
|
+
'id3-metadata_aac': {
|
|
671
|
+
audio: {
|
|
672
|
+
segmentUri: '/base/test/test/assets/id3-metadata.aac',
|
|
673
|
+
mimeType: 'audio/aac',
|
|
674
|
+
codecs: '',
|
|
675
|
+
segmentDuration: 9.98458,
|
|
676
|
+
},
|
|
677
|
+
duration: 9.98458,
|
|
649
678
|
},
|
|
650
679
|
};
|
|
651
680
|
|
|
@@ -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
|
[
|
|
@@ -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
|
+
});
|
|
@@ -798,7 +798,7 @@ goog.Uri.QueryData.prototype.add = function(key, value) {
|
|
|
798
798
|
// Invalidate the cache.
|
|
799
799
|
this.encodedQuery_ = null;
|
|
800
800
|
|
|
801
|
-
var values = this.keyMap_.hasOwnProperty(key)
|
|
801
|
+
var values = this.keyMap_.hasOwnProperty(key) ? this.keyMap_[key] : null;
|
|
802
802
|
if (!values) {
|
|
803
803
|
this.keyMap_[key] = (values = []);
|
|
804
804
|
}
|
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/ui.js
CHANGED
|
@@ -290,6 +290,12 @@ shaka.ui.Overlay = class {
|
|
|
290
290
|
const videos = document.querySelectorAll(
|
|
291
291
|
'[data-shaka-player]');
|
|
292
292
|
|
|
293
|
+
// Look for elements marked 'data-shaka-player-canvas'
|
|
294
|
+
// on the page. These will be used to create our default
|
|
295
|
+
// UI.
|
|
296
|
+
const canvases = document.querySelectorAll(
|
|
297
|
+
'[data-shaka-player-canvas]');
|
|
298
|
+
|
|
293
299
|
if (!videos.length && !containers.length) {
|
|
294
300
|
// No elements have been tagged with shaka attributes.
|
|
295
301
|
} else if (videos.length && !containers.length) {
|
|
@@ -307,8 +313,21 @@ shaka.ui.Overlay = class {
|
|
|
307
313
|
const videoParent = video.parentElement;
|
|
308
314
|
videoParent.replaceChild(container, video);
|
|
309
315
|
container.appendChild(video);
|
|
310
|
-
|
|
311
|
-
|
|
316
|
+
let currentCanvas = null;
|
|
317
|
+
for (const canvas of canvases) {
|
|
318
|
+
goog.asserts.assert(canvas.tagName.toLowerCase() == 'canvas',
|
|
319
|
+
'Should be a canvas element!');
|
|
320
|
+
if (canvas.parentElement == container) {
|
|
321
|
+
currentCanvas = canvas;
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (!currentCanvas) {
|
|
326
|
+
currentCanvas = document.createElement('canvas');
|
|
327
|
+
currentCanvas.classList.add('shaka-canvas-container');
|
|
328
|
+
container.appendChild(currentCanvas);
|
|
329
|
+
}
|
|
330
|
+
shaka.ui.Overlay.setupUIandAutoLoad_(container, video, currentCanvas);
|
|
312
331
|
}
|
|
313
332
|
} else {
|
|
314
333
|
for (const container of containers) {
|
|
@@ -336,9 +355,25 @@ shaka.ui.Overlay = class {
|
|
|
336
355
|
container.appendChild(currentVideo);
|
|
337
356
|
}
|
|
338
357
|
|
|
358
|
+
let currentCanvas = null;
|
|
359
|
+
for (const canvas of canvases) {
|
|
360
|
+
goog.asserts.assert(canvas.tagName.toLowerCase() == 'canvas',
|
|
361
|
+
'Should be a canvas element!');
|
|
362
|
+
if (canvas.parentElement == container) {
|
|
363
|
+
currentCanvas = canvas;
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if (!currentCanvas) {
|
|
368
|
+
currentCanvas = document.createElement('canvas');
|
|
369
|
+
currentCanvas.classList.add('shaka-canvas-container');
|
|
370
|
+
container.appendChild(currentCanvas);
|
|
371
|
+
}
|
|
372
|
+
|
|
339
373
|
try {
|
|
340
374
|
// eslint-disable-next-line no-await-in-loop
|
|
341
|
-
await shaka.ui.Overlay.setupUIandAutoLoad_(
|
|
375
|
+
await shaka.ui.Overlay.setupUIandAutoLoad_(
|
|
376
|
+
container, currentVideo, currentCanvas);
|
|
342
377
|
} catch (e) {
|
|
343
378
|
// This can fail if, for example, not every player file has loaded.
|
|
344
379
|
// Ad-block is a likely cause for this sort of failure.
|
|
@@ -377,9 +412,10 @@ shaka.ui.Overlay = class {
|
|
|
377
412
|
/**
|
|
378
413
|
* @param {!Element} container
|
|
379
414
|
* @param {!Element} video
|
|
415
|
+
* @param {!Element} canvas
|
|
380
416
|
* @private
|
|
381
417
|
*/
|
|
382
|
-
static async setupUIandAutoLoad_(container, video) {
|
|
418
|
+
static async setupUIandAutoLoad_(container, video, canvas) {
|
|
383
419
|
// Create the UI
|
|
384
420
|
const player = new shaka.Player(
|
|
385
421
|
shaka.util.Dom.asHTMLMediaElement(video));
|
|
@@ -387,6 +423,9 @@ shaka.ui.Overlay = class {
|
|
|
387
423
|
shaka.util.Dom.asHTMLElement(container),
|
|
388
424
|
shaka.util.Dom.asHTMLMediaElement(video));
|
|
389
425
|
|
|
426
|
+
// Attach Canvas used for LCEVC Decoding
|
|
427
|
+
player.attachCanvas(/** @type {HTMLCanvasElement} */(canvas));
|
|
428
|
+
|
|
390
429
|
// Get and configure cast app id.
|
|
391
430
|
let castAppId = '';
|
|
392
431
|
|