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