shaka-player 4.2.3 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (147) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +5 -0
  4. package/CHANGELOG.md +85 -39
  5. package/CONTRIBUTORS +5 -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 +18 -11
  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 +178 -27
  33. package/dist/shaka-player.compiled.debug.d.ts +178 -27
  34. package/dist/shaka-player.compiled.debug.externs.js +163 -32
  35. package/dist/shaka-player.compiled.debug.js +488 -422
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +163 -32
  38. package/dist/shaka-player.compiled.js +895 -857
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +178 -27
  41. package/dist/shaka-player.ui.debug.d.ts +178 -27
  42. package/dist/shaka-player.ui.debug.externs.js +163 -32
  43. package/dist/shaka-player.ui.debug.js +519 -450
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +163 -32
  46. package/dist/shaka-player.ui.js +1053 -1015
  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 +4 -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_proxy.js +2 -1
  68. package/lib/cast/cast_receiver.js +19 -5
  69. package/lib/cast/cast_sender.js +2 -1
  70. package/lib/cast/cast_utils.js +18 -6
  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 +87 -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 +799 -376
  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_engine.js +112 -26
  81. package/lib/media/playhead.js +23 -1
  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 +145 -24
  85. package/lib/media/transmuxer.js +79 -18
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +477 -85
  88. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  89. package/lib/text/ui_text_displayer.js +5 -5
  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/manifest_parser_utils.js +4 -0
  96. package/lib/util/mime_utils.js +3 -6
  97. package/lib/util/mp4_box_parsers.js +15 -5
  98. package/lib/util/platform.js +9 -1
  99. package/lib/util/player_configuration.js +23 -1
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +4 -1
  103. package/roadmap.md +27 -7
  104. package/test/ads/ad_manager_unit.js +180 -29
  105. package/test/cast/cast_receiver_integration.js +97 -16
  106. package/test/cast/cast_receiver_unit.js +17 -12
  107. package/test/cast/cast_utils_unit.js +4 -1
  108. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  109. package/test/dash/dash_parser_live_unit.js +1 -0
  110. package/test/dash/dash_parser_manifest_unit.js +93 -1
  111. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  112. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  113. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  114. package/test/demo/demo_unit.js +2 -1
  115. package/test/hls/hls_live_unit.js +253 -19
  116. package/test/hls/hls_parser_unit.js +500 -12
  117. package/test/media/closed_caption_parser_unit.js +2 -1
  118. package/test/media/drm_engine_integration.js +3 -1
  119. package/test/media/media_source_engine_integration.js +102 -6
  120. package/test/media/media_source_engine_unit.js +10 -5
  121. package/test/media/playhead_unit.js +41 -0
  122. package/test/media/streaming_engine_integration.js +17 -1
  123. package/test/media/streaming_engine_unit.js +180 -24
  124. package/test/media/transmuxer_integration.js +21 -10
  125. package/test/player_integration.js +224 -82
  126. package/test/player_unit.js +92 -2
  127. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  128. package/test/test/assets/id3-metadata.aac +0 -0
  129. package/test/test/assets/id3-metadata.ts +0 -0
  130. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  131. package/test/test/assets/thumbnails.vtt +10 -0
  132. package/test/test/util/dash_parser_util.js +2 -0
  133. package/test/test/util/fake_ad.js +21 -0
  134. package/test/test/util/fake_media_source_engine.js +7 -0
  135. package/test/test/util/id3_generator.js +100 -0
  136. package/test/test/util/manifest_generator.js +3 -6
  137. package/test/test/util/simple_fakes.js +7 -0
  138. package/test/test/util/stream_generator.js +38 -0
  139. package/test/test/util/test_scheme.js +43 -14
  140. package/test/text/vtt_text_parser_unit.js +11 -0
  141. package/test/ui/text_displayer_layout_unit.js +14 -0
  142. package/test/util/id3_utils_unit.js +116 -0
  143. package/test/util/ts_parser_unit.js +77 -0
  144. package/ui/controls.js +4 -1
  145. package/ui/less/containers.less +7 -0
  146. package/ui/settings_menu.js +11 -2
  147. 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
  /**
@@ -339,6 +339,9 @@ shaka.test.FakePlayhead = class {
339
339
  /** @private {number} */
340
340
  this.stallsDetected_ = 0;
341
341
 
342
+ /** @type {!jasmine.Spy} */
343
+ this.ready = jasmine.createSpy('ready');
344
+
342
345
  /** @type {!jasmine.Spy} */
343
346
  this.setStartTime = jasmine.createSpy('setStartTime')
344
347
  .and.callFake((value) => {
@@ -509,5 +512,9 @@ shaka.test.FakeTransmuxer = class {
509
512
  /** @type {!jasmine.Spy} */
510
513
  this.transmux =
511
514
  jasmine.createSpy('transmux').and.returnValue(Promise.resolve(output));
515
+
516
+ /** @type {!jasmine.Spy} */
517
+ this.getOrginalMimeType =
518
+ jasmine.createSpy('getOrginalMimeType').and.returnValue('mimeType');
512
519
  }
513
520
  };
@@ -83,6 +83,44 @@ shaka.test.TSVodStreamGenerator = class {
83
83
  }
84
84
  };
85
85
 
86
+ /**
87
+ * @summary
88
+ * Simulates an HLS, video-on-demand, aac stream. The StreamGenerator assumes
89
+ * the stream contains a single segment.
90
+ *
91
+ * @implements {shaka.test.IStreamGenerator}
92
+ */
93
+ shaka.test.AACVodStreamGenerator = class {
94
+ /** @param {string} segmentUri The URI of the segment. */
95
+ constructor(segmentUri) {
96
+ /** @private {string} */
97
+ this.segmentUri_ = segmentUri;
98
+
99
+ /** @private {ArrayBuffer} */
100
+ this.segment_ = null;
101
+ }
102
+
103
+ /** @override */
104
+ async init() {
105
+ const segment = await shaka.test.Util.fetch(this.segmentUri_);
106
+ this.segment_ = segment;
107
+ }
108
+
109
+ /** @override */
110
+ getInitSegment(time) {
111
+ goog.asserts.assert(false, 'getInitSegment not implemented for HLS VOD.');
112
+ return new ArrayBuffer(0);
113
+ }
114
+
115
+ /** @override */
116
+ getSegment(position, wallClockTime) {
117
+ goog.asserts.assert(
118
+ this.segment_,
119
+ 'init() must be called before getSegment().');
120
+ return this.segment_;
121
+ }
122
+ };
123
+
86
124
  /**
87
125
  * @summary
88
126
  * Simulates a DASH, video-on-demand, MP4 stream. The StreamGenerator loops a
@@ -52,7 +52,8 @@ let ExtraMetadataType;
52
52
  * duration: number,
53
53
  * licenseServers: (!Object.<string, string>|undefined),
54
54
  * licenseRequestHeaders: (!Object.<string, string>|undefined),
55
- * 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
+ });
package/ui/controls.js CHANGED
@@ -1024,7 +1024,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
1024
1024
  if (!this.video_ ||
1025
1025
  this.video_.readyState == 0 ||
1026
1026
  this.castProxy_.isCasting() ||
1027
- !this.config_.enableFullscreenOnRotation) { return; }
1027
+ !this.config_.enableFullscreenOnRotation ||
1028
+ !this.isFullScreenSupported()) {
1029
+ return;
1030
+ }
1028
1031
 
1029
1032
  if (screen.orientation.type.includes('landscape') &&
1030
1033
  !document.fullscreenElement) {
@@ -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 {
@@ -12,6 +12,7 @@ goog.require('shaka.ui.Enums');
12
12
  goog.require('shaka.ui.Utils');
13
13
  goog.require('shaka.util.Dom');
14
14
  goog.require('shaka.util.FakeEvent');
15
+ goog.require('shaka.util.Iterables');
15
16
  goog.requireType('shaka.ui.Controls');
16
17
 
17
18
 
@@ -116,8 +117,16 @@ shaka.ui.SettingsMenu = class extends shaka.ui.Element {
116
117
  this.eventManager.listen(this.backButton, 'click', () => {
117
118
  shaka.ui.Utils.setDisplay(this.parent, true);
118
119
 
119
- /** @type {!HTMLElement} */
120
- (this.parent.childNodes[0]).focus();
120
+ const isDisplayed =
121
+ (element) => element.classList.contains('shaka-hidden') == false;
122
+
123
+ const Iterables = shaka.util.Iterables;
124
+ if (Iterables.some(this.parent.childNodes, isDisplayed)) {
125
+ // Focus on the first visible child of the overflow menu
126
+ const visibleElements =
127
+ Iterables.filter(this.parent.childNodes, isDisplayed);
128
+ /** @type {!HTMLElement} */ (visibleElements[0]).focus();
129
+ }
121
130
 
122
131
  // Make sure controls are displayed
123
132
  this.controls.computeOpacity();