shaka-player 4.2.4 → 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 +4 -0
- package/CHANGELOG.md +85 -54
- package/CONTRIBUTORS +4 -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 +16 -9
- 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 +173 -26
- package/dist/shaka-player.compiled.debug.d.ts +173 -26
- package/dist/shaka-player.compiled.debug.externs.js +161 -31
- package/dist/shaka-player.compiled.debug.js +464 -401
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +161 -31
- package/dist/shaka-player.compiled.js +897 -862
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +173 -26
- package/dist/shaka-player.ui.debug.d.ts +173 -26
- package/dist/shaka-player.ui.debug.externs.js +161 -31
- package/dist/shaka-player.ui.debug.js +488 -422
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +161 -31
- package/dist/shaka-player.ui.js +1053 -1018
- 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/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 +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 +790 -374
- 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 +106 -26
- 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 +73 -18
- package/lib/offline/storage.js +1 -0
- package/lib/player.js +454 -75
- 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/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 +5 -3
- package/test/ads/ad_manager_unit.js +180 -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 +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 +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/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 +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/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/less/containers.less +7 -0
- package/ui/ui.js +43 -4
|
@@ -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
|
};
|
|
@@ -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/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
|
|