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.
Files changed (143) hide show
  1. package/.github/workflows/build-and-test.yaml +7 -0
  2. package/.github/workflows/sync-labels.yaml +1 -1
  3. package/.github/workflows/update-issues.yaml +1 -1
  4. package/AUTHORS +4 -0
  5. package/CHANGELOG.md +114 -74
  6. package/CONTRIBUTORS +4 -0
  7. package/README.md +14 -0
  8. package/conditional/dummy_cast_proxy.js +120 -0
  9. package/demo/common/assets.js +58 -0
  10. package/demo/common/message_ids.js +22 -1
  11. package/demo/config.js +89 -5
  12. package/demo/demo.less +7 -1
  13. package/demo/index.html +8 -1
  14. package/demo/locales/en.json +21 -1
  15. package/demo/locales/source.json +82 -2
  16. package/demo/main.js +57 -1
  17. package/demo/search.js +2 -0
  18. package/demo/visualizer.js +337 -0
  19. package/demo/visualizer_button.js +103 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +107 -88
  23. package/dist/demo.compiled.debug.map +4 -4
  24. package/dist/demo.compiled.js +107 -88
  25. package/dist/demo.compiled.map +4 -4
  26. package/dist/demo.css +1 -1
  27. package/dist/demo.css.map +1 -1
  28. package/dist/deps.js +21 -13
  29. package/dist/locales.js +31 -31
  30. package/dist/receiver.compiled.debug.js +75 -77
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +75 -77
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +181 -27
  35. package/dist/shaka-player.compiled.debug.d.ts +181 -27
  36. package/dist/shaka-player.compiled.debug.externs.js +169 -31
  37. package/dist/shaka-player.compiled.debug.js +510 -450
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +169 -31
  40. package/dist/shaka-player.compiled.js +912 -874
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +181 -27
  43. package/dist/shaka-player.ui.debug.d.ts +181 -27
  44. package/dist/shaka-player.ui.debug.externs.js +169 -31
  45. package/dist/shaka-player.ui.debug.js +533 -471
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +169 -31
  48. package/dist/shaka-player.ui.js +1073 -1036
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/lcevc-architecture.png +0 -0
  51. package/docs/design/lcevc-demo.png +0 -0
  52. package/docs/design/lcevc-integration.md +72 -0
  53. package/docs/tutorials/fairplay.md +7 -0
  54. package/docs/tutorials/faq.md +1 -1
  55. package/docs/tutorials/ui.md +7 -7
  56. package/docs/tutorials/upgrade.md +6 -0
  57. package/externs/ima.js +13 -1
  58. package/externs/lcevc.js +81 -0
  59. package/externs/mux.js +1 -42
  60. package/externs/shaka/ads.js +11 -1
  61. package/externs/shaka/manifest_parser.js +5 -1
  62. package/externs/shaka/mp4_parser.js +3 -0
  63. package/externs/shaka/player.js +178 -15
  64. package/lib/abr/simple_abr_manager.js +7 -0
  65. package/lib/ads/ad_manager.js +0 -8
  66. package/lib/ads/client_side_ad.js +15 -0
  67. package/lib/ads/client_side_ad_manager.js +21 -8
  68. package/lib/ads/server_side_ad.js +15 -0
  69. package/lib/ads/server_side_ad_manager.js +3 -3
  70. package/lib/cast/cast_utils.js +2 -0
  71. package/lib/cea/dummy_cea_parser.js +28 -0
  72. package/lib/cea/i_cea_parser.js +14 -2
  73. package/lib/cea/mp4_cea_parser.js +108 -5
  74. package/lib/config/auto_show_text.js +33 -0
  75. package/lib/dash/dash_parser.js +1 -1
  76. package/lib/hls/hls_parser.js +808 -384
  77. package/lib/lcevc/lcevc_dil.js +152 -0
  78. package/lib/media/closed_caption_parser.js +9 -6
  79. package/lib/media/drm_engine.js +14 -6
  80. package/lib/media/media_source_capabilities.js +36 -0
  81. package/lib/media/media_source_engine.js +113 -28
  82. package/lib/media/presentation_timeline.js +26 -11
  83. package/lib/media/segment_reference.js +49 -1
  84. package/lib/media/streaming_engine.js +142 -12
  85. package/lib/media/transmuxer.js +78 -21
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +456 -77
  88. package/lib/polyfill/media_capabilities.js +9 -5
  89. package/lib/polyfill/patchedmediakeys_apple.js +63 -4
  90. package/lib/text/vtt_text_parser.js +35 -1
  91. package/lib/util/dom_utils.js +0 -1
  92. package/lib/util/error.js +17 -0
  93. package/lib/util/fake_event.js +2 -0
  94. package/lib/util/id3_utils.js +300 -0
  95. package/lib/util/mime_utils.js +3 -6
  96. package/lib/util/mp4_box_parsers.js +39 -5
  97. package/lib/util/mp4_parser.js +35 -0
  98. package/lib/util/player_configuration.js +23 -1
  99. package/lib/util/stream_utils.js +5 -3
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +7 -4
  103. package/roadmap.md +5 -3
  104. package/test/ads/ad_manager_unit.js +182 -29
  105. package/test/cast/cast_utils_unit.js +3 -1
  106. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  107. package/test/dash/dash_parser_live_unit.js +1 -0
  108. package/test/dash/dash_parser_manifest_unit.js +93 -1
  109. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  110. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  111. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  112. package/test/demo/demo_unit.js +2 -1
  113. package/test/hls/hls_live_unit.js +253 -19
  114. package/test/hls/hls_parser_unit.js +631 -12
  115. package/test/media/closed_caption_parser_unit.js +2 -1
  116. package/test/media/drm_engine_integration.js +3 -1
  117. package/test/media/media_source_engine_integration.js +71 -6
  118. package/test/media/media_source_engine_unit.js +10 -5
  119. package/test/media/streaming_engine_integration.js +4 -1
  120. package/test/media/streaming_engine_unit.js +144 -1
  121. package/test/media/transmuxer_integration.js +21 -10
  122. package/test/player_integration.js +224 -82
  123. package/test/player_unit.js +5 -2
  124. package/test/polyfill/media_capabilities_unit.js +21 -23
  125. package/test/polyfill/patchedmediakeys_apple_unit.js +130 -0
  126. package/test/test/assets/id3-metadata.aac +0 -0
  127. package/test/test/assets/id3-metadata.ts +0 -0
  128. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  129. package/test/test/assets/thumbnails.vtt +10 -0
  130. package/test/test/util/dash_parser_util.js +2 -0
  131. package/test/test/util/fake_ad.js +21 -0
  132. package/test/test/util/fake_media_source_engine.js +7 -0
  133. package/test/test/util/id3_generator.js +100 -0
  134. package/test/test/util/manifest_generator.js +3 -6
  135. package/test/test/util/simple_fakes.js +4 -0
  136. package/test/test/util/stream_generator.js +38 -0
  137. package/test/test/util/test_scheme.js +43 -14
  138. package/test/text/vtt_text_parser_unit.js +11 -0
  139. package/test/util/id3_utils_unit.js +116 -0
  140. package/test/util/ts_parser_unit.js +77 -0
  141. package/third_party/closure-uri/uri.js +1 -1
  142. package/ui/less/containers.less +7 -0
  143. 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 = null;
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
- if (!this.initData) {
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
- * sequenceMode: boolean
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
- sequenceMode: false,
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
- sequenceMode: false,
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
+ '&quot;Test &amp; 1&quot;',
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) && this.keyMap_[key];
801
+ var values = this.keyMap_.hasOwnProperty(key) ? this.keyMap_[key] : null;
802
802
  if (!values) {
803
803
  this.keyMap_[key] = (values = []);
804
804
  }
@@ -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
- shaka.ui.Overlay.setupUIandAutoLoad_(container, video);
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_(container, currentVideo);
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