shaka-player 3.3.4 → 4.0.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 (201) hide show
  1. package/.github/config.yml +1 -0
  2. package/.github/workflows/build-and-test.yaml +0 -1
  3. package/.github/workflows/demo-version-index.yaml +8 -0
  4. package/.github/workflows/release-please.yaml +19 -3
  5. package/.github/workflows/selenium-lab-tests.yaml +0 -1
  6. package/.github/workflows/sync-labels.yaml +33 -0
  7. package/.github/workflows/update-issues.yaml +29 -0
  8. package/.github/workflows/validate-pr-title.yaml +35 -0
  9. package/AUTHORS +1 -0
  10. package/CHANGELOG.md +209 -28
  11. package/CONTRIBUTORS +1 -0
  12. package/README.md +10 -7
  13. package/app-engine/demo-version-index/README.md +3 -3
  14. package/app-engine/demo-version-index/app.yaml +27 -1
  15. package/app-engine/demo-version-index/generate.py +164 -0
  16. package/app-engine/demo-version-index/main.py +4 -155
  17. package/app-engine/demo-version-index/requirements.txt +0 -1
  18. package/build/README.md +2 -2
  19. package/build/compiler.py +1 -1
  20. package/build/generateTsDefs.py +0 -2
  21. package/build/misspellings.txt +1 -0
  22. package/build/shaka-lab.yaml +0 -2
  23. package/build/test.py +7 -1
  24. package/build/types/core +0 -1
  25. package/build/types/polyfill +0 -3
  26. package/demo/common/asset.js +17 -1
  27. package/demo/common/assets.js +29 -0
  28. package/demo/common/message_ids.js +6 -3
  29. package/demo/config.js +10 -11
  30. package/demo/custom.js +39 -0
  31. package/demo/demo_utils.js +0 -1
  32. package/demo/locales/en.json +6 -3
  33. package/demo/locales/source.json +22 -10
  34. package/demo/main.js +9 -6
  35. package/demo/search.js +2 -1
  36. package/dist/controls.css +1 -1
  37. package/dist/demo.compiled.debug.js +71 -64
  38. package/dist/demo.compiled.debug.map +3 -3
  39. package/dist/demo.compiled.js +71 -64
  40. package/dist/demo.compiled.map +3 -3
  41. package/dist/demo.css +1 -1
  42. package/dist/demo.css.map +1 -1
  43. package/dist/deps.js +16 -20
  44. package/dist/locales.js +33 -31
  45. package/dist/receiver.compiled.debug.js +32 -28
  46. package/dist/receiver.compiled.debug.map +3 -3
  47. package/dist/receiver.compiled.js +32 -28
  48. package/dist/receiver.compiled.map +3 -3
  49. package/dist/shaka-player.compiled.d.ts +78 -82
  50. package/dist/shaka-player.compiled.debug.d.ts +78 -82
  51. package/dist/shaka-player.compiled.debug.externs.js +127 -126
  52. package/dist/shaka-player.compiled.debug.js +471 -523
  53. package/dist/shaka-player.compiled.debug.map +4 -4
  54. package/dist/shaka-player.compiled.externs.js +127 -126
  55. package/dist/shaka-player.compiled.js +844 -858
  56. package/dist/shaka-player.compiled.map +4 -4
  57. package/dist/shaka-player.ui.d.ts +80 -82
  58. package/dist/shaka-player.ui.debug.d.ts +80 -82
  59. package/dist/shaka-player.ui.debug.externs.js +135 -126
  60. package/dist/shaka-player.ui.debug.js +531 -581
  61. package/dist/shaka-player.ui.debug.map +4 -4
  62. package/dist/shaka-player.ui.externs.js +135 -126
  63. package/dist/shaka-player.ui.js +991 -1001
  64. package/dist/shaka-player.ui.map +4 -4
  65. package/docs/tutorials/config.md +0 -2
  66. package/docs/tutorials/drm-config.md +5 -0
  67. package/docs/tutorials/fairplay.md +7 -22
  68. package/docs/tutorials/faq.md +0 -11
  69. package/docs/tutorials/index.json +2 -8
  70. package/docs/tutorials/plugins.md +1 -3
  71. package/docs/tutorials/upgrade.md +85 -11
  72. package/externs/polyfill.js +15 -0
  73. package/externs/shaka/manifest.js +5 -1
  74. package/externs/shaka/net.js +12 -0
  75. package/externs/shaka/offline.js +5 -1
  76. package/externs/shaka/player.js +39 -21
  77. package/externs/shaka/text.js +12 -12
  78. package/externs/sourcebuffer.js +15 -0
  79. package/karma.conf.js +4 -0
  80. package/lib/ads/client_side_ad.js +13 -2
  81. package/lib/ads/client_side_ad_manager.js +22 -4
  82. package/lib/cast/cast_utils.js +0 -1
  83. package/lib/dash/content_protection.js +85 -5
  84. package/lib/dash/dash_parser.js +3 -3
  85. package/lib/hls/hls_parser.js +391 -747
  86. package/lib/hls/manifest_text_parser.js +2 -3
  87. package/lib/media/drm_engine.js +102 -37
  88. package/lib/media/gap_jumping_controller.js +10 -45
  89. package/lib/media/media_source_engine.js +157 -31
  90. package/lib/media/playhead.js +2 -5
  91. package/lib/media/presentation_timeline.js +1 -2
  92. package/lib/media/segment_index.js +13 -46
  93. package/lib/media/segment_reference.js +47 -1
  94. package/lib/media/streaming_engine.js +28 -11
  95. package/lib/media/time_ranges_utils.js +4 -9
  96. package/lib/net/networking_engine.js +3 -0
  97. package/lib/offline/indexeddb/storage_mechanism.js +4 -2
  98. package/lib/offline/indexeddb/v1_storage_cell.js +1 -0
  99. package/lib/offline/indexeddb/v2_storage_cell.js +1 -0
  100. package/lib/offline/manifest_converter.js +1 -0
  101. package/lib/offline/storage.js +100 -132
  102. package/lib/player.js +181 -307
  103. package/lib/polyfill/media_capabilities.js +12 -6
  104. package/lib/polyfill/mediasource.js +0 -28
  105. package/lib/polyfill/patchedmediakeys_nop.js +2 -0
  106. package/lib/polyfill/patchedmediakeys_webkit.js +2 -0
  107. package/lib/text/cue.js +0 -6
  108. package/lib/text/lrc_text_parser.js +8 -0
  109. package/lib/text/mp4_ttml_parser.js +8 -0
  110. package/lib/text/mp4_vtt_parser.js +8 -0
  111. package/lib/text/sbv_text_parser.js +8 -0
  112. package/lib/text/simple_text_displayer.js +1 -7
  113. package/lib/text/srt_text_parser.js +8 -0
  114. package/lib/text/ssa_text_parser.js +8 -0
  115. package/lib/text/text_engine.js +19 -3
  116. package/lib/text/ttml_text_parser.js +8 -0
  117. package/lib/text/ui_text_displayer.js +1 -7
  118. package/lib/text/vtt_text_parser.js +27 -5
  119. package/lib/text/web_vtt_generator.js +1 -7
  120. package/lib/util/config_utils.js +25 -0
  121. package/lib/util/error.js +16 -13
  122. package/lib/util/fairplay_utils.js +109 -0
  123. package/lib/util/functional.js +0 -42
  124. package/lib/util/manifest_parser_utils.js +1 -1
  125. package/lib/util/mime_utils.js +50 -1
  126. package/lib/util/periods.js +5 -4
  127. package/lib/util/platform.js +45 -0
  128. package/lib/util/player_configuration.js +26 -20
  129. package/lib/util/pssh.js +23 -4
  130. package/lib/util/stream_utils.js +96 -24
  131. package/lib/util/uint8array_utils.js +0 -16
  132. package/lib/util/xml_utils.js +1 -1
  133. package/package.json +4 -1
  134. package/roadmap.md +19 -19
  135. package/shaka-player.uncompiled.js +0 -3
  136. package/test/cast/cast_utils_unit.js +1 -0
  137. package/test/dash/dash_parser_content_protection_unit.js +44 -0
  138. package/test/dash/dash_parser_manifest_unit.js +0 -24
  139. package/test/dash/dash_parser_segment_template_unit.js +3 -4
  140. package/test/demo/demo_unit.js +2 -1
  141. package/test/hls/hls_live_unit.js +121 -220
  142. package/test/hls/hls_parser_unit.js +515 -432
  143. package/test/hls/manifest_text_parser_unit.js +19 -0
  144. package/test/media/drm_engine_unit.js +86 -120
  145. package/test/media/media_source_engine_integration.js +26 -3
  146. package/test/media/media_source_engine_unit.js +7 -6
  147. package/test/media/playhead_unit.js +27 -191
  148. package/test/media/segment_index_unit.js +15 -25
  149. package/test/media/streaming_engine_integration.js +4 -39
  150. package/test/media/streaming_engine_unit.js +3 -2
  151. package/test/media/time_ranges_utils_unit.js +0 -5
  152. package/test/offline/indexeddb_storage_unit.js +1 -1
  153. package/test/offline/manifest_convert_unit.js +34 -4
  154. package/test/offline/offline_manifest_parser_unit.js +1 -0
  155. package/test/offline/storage_compatibility_unit.js +1 -1
  156. package/test/offline/storage_integration.js +5 -4
  157. package/test/player_external.js +7 -7
  158. package/test/player_integration.js +22 -10
  159. package/test/player_src_equals_integration.js +81 -83
  160. package/test/player_unit.js +124 -1
  161. package/test/test/boot.js +9 -0
  162. package/test/test/externs/filters.js +4 -0
  163. package/test/test/util/fake_media_source_engine.js +11 -4
  164. package/test/test/util/manifest_generator.js +3 -1
  165. package/test/test/util/manifest_parser_util.js +4 -1
  166. package/test/test/util/offline_utils.js +1 -0
  167. package/test/test/util/simple_fakes.js +31 -2
  168. package/test/test/util/streaming_engine_util.js +1 -0
  169. package/test/test/util/test_scheme.js +24 -1
  170. package/test/test/util/util.js +4 -4
  171. package/test/text/cue_integration.js +2 -2
  172. package/test/text/lrc_text_parser_unit.js +6 -7
  173. package/test/text/mp4_ttml_parser_unit.js +6 -4
  174. package/test/text/mp4_vtt_parser_unit.js +6 -5
  175. package/test/text/sbv_text_parser_unit.js +5 -6
  176. package/test/text/srt_text_parser_unit.js +5 -6
  177. package/test/text/ssa_text_parser_unit.js +17 -29
  178. package/test/text/text_engine_unit.js +45 -5
  179. package/test/text/ttml_text_parser_unit.js +77 -77
  180. package/test/text/vtt_text_parser_unit.js +116 -59
  181. package/test/ui/ui_integration.js +0 -16
  182. package/test/ui/ui_unit.js +27 -0
  183. package/test/util/cmcd_manager_unit.js +3 -0
  184. package/test/util/mime_utils_unit.js +54 -0
  185. package/test/util/periods_unit.js +79 -0
  186. package/test/util/stream_utils_unit.js +23 -7
  187. package/ui/controls.js +65 -40
  188. package/ui/fullscreen_button.js +26 -6
  189. package/ui/less/containers.less +3 -3
  190. package/ui/locales/en.json +1 -0
  191. package/ui/locales/source.json +5 -0
  192. package/ui/resolution_selection.js +31 -33
  193. package/ui/ui.js +0 -17
  194. package/docs/tutorials/gsoc-ideas-list.md +0 -3
  195. package/docs/tutorials/upgrade-v2-5.md +0 -441
  196. package/docs/tutorials/upgrade-v3.md +0 -38
  197. package/lib/polyfill/mathround.js +0 -49
  198. package/lib/polyfill/patchedmediakeys_apple.js +0 -744
  199. package/lib/polyfill/patchedmediakeys_ms.js +0 -698
  200. package/lib/util/mutex.js +0 -52
  201. package/test/util/functional_unit.js +0 -82
@@ -996,6 +996,85 @@ describe('PeriodCombiner', () => {
996
996
  expect(audio2.originalId).toBe('2,4');
997
997
  });
998
998
 
999
+ it('Matches streams with related codecs', async () => {
1000
+ const stream1 = makeVideoStream(1080);
1001
+ stream1.originalId = '1';
1002
+ stream1.bandwidth = 120000;
1003
+ stream1.codecs = 'hvc1.1.4.L126.B0';
1004
+
1005
+ const stream2 = makeVideoStream(1080);
1006
+ stream2.originalId = '2';
1007
+ stream2.bandwidth = 120000;
1008
+ stream2.codecs = 'hev1.2.4.L123.B0';
1009
+
1010
+ const stream3 = makeVideoStream(1080);
1011
+ stream3.originalId = '3';
1012
+ stream3.bandwidth = 120000;
1013
+ stream3.codecs = 'dvhe.05.01';
1014
+
1015
+ const stream4 = makeVideoStream(1080);
1016
+ stream4.originalId = '4';
1017
+ stream4.bandwidth = 120000;
1018
+ stream4.codecs = 'dvh1.05.01';
1019
+
1020
+ const stream5 = makeVideoStream(1080);
1021
+ stream5.originalId = '5';
1022
+ stream5.bandwidth = 120000;
1023
+ stream5.codecs = 'avc1.42001f';
1024
+
1025
+ const stream6 = makeVideoStream(1080);
1026
+ stream6.originalId = '6';
1027
+ stream6.bandwidth = 120000;
1028
+ stream6.codecs = 'avc3.42001f';
1029
+
1030
+ const stream7 = makeVideoStream(1080);
1031
+ stream7.originalId = '7';
1032
+ stream7.bandwidth = 120000;
1033
+ stream7.codecs = 'vp09.00.10.08';
1034
+
1035
+ const stream8 = makeVideoStream(1080);
1036
+ stream8.originalId = '8';
1037
+ stream8.bandwidth = 120000;
1038
+ stream8.codecs = 'vp09.01.20.08.01';
1039
+
1040
+ /** @type {!Array.<shaka.util.PeriodCombiner.Period>} */
1041
+ const periods = [
1042
+ {
1043
+ id: '0',
1044
+ videoStreams: [
1045
+ stream1, stream3, stream5, stream7,
1046
+ ],
1047
+ audioStreams: [],
1048
+ textStreams: [],
1049
+ imageStreams: [],
1050
+ },
1051
+ {
1052
+ id: '1',
1053
+ videoStreams: [
1054
+ stream2, stream4, stream6, stream8,
1055
+ ],
1056
+ audioStreams: [],
1057
+ textStreams: [],
1058
+ imageStreams: [],
1059
+ },
1060
+ ];
1061
+
1062
+ await combiner.combinePeriods(periods, /* isDynamic= */ true);
1063
+ const variants = combiner.getVariants();
1064
+ expect(variants.length).toBe(4);
1065
+ // We can use the originalId field to see what each track is composed of.
1066
+ const video1 = variants[0].video;
1067
+ expect(video1.originalId).toBe('1,2');
1068
+
1069
+ const video2 = variants[1].video;
1070
+ expect(video2.originalId).toBe('3,4');
1071
+
1072
+ const video3 = variants[2].video;
1073
+ expect(video3.originalId).toBe('5,6');
1074
+
1075
+ const video4 = variants[3].video;
1076
+ expect(video4.originalId).toBe('7,8');
1077
+ });
999
1078
 
1000
1079
  it('Matches streams with most roles in common', async () => {
1001
1080
  const makeAudioStreamWithRoles = (roles) => {
@@ -619,11 +619,20 @@ describe('StreamUtils', () => {
619
619
  stream.mimeType = 'image/png';
620
620
  });
621
621
  manifest.addImageStream(3, (stream) => {
622
- stream.mimeType = 'image/jpeg';
622
+ stream.mimeType = 'image/jpg';
623
623
  });
624
624
  manifest.addImageStream(4, (stream) => {
625
+ stream.mimeType = 'image/jpeg';
626
+ });
627
+ manifest.addImageStream(5, (stream) => {
625
628
  stream.mimeType = 'image/bogus';
626
629
  });
630
+ manifest.addImageStream(6, (stream) => {
631
+ stream.mimeType = 'image/avif';
632
+ });
633
+ manifest.addImageStream(7, (stream) => {
634
+ stream.mimeType = 'image/webp';
635
+ });
627
636
  });
628
637
 
629
638
  const noVariant = null;
@@ -631,12 +640,19 @@ describe('StreamUtils', () => {
631
640
  fakeDrmEngine, noVariant, manifest);
632
641
 
633
642
  // Covers a regression in which we would remove streams with codecs.
634
- // The last two streams should be removed because their full MIME types
635
- // are bogus.
636
- expect(manifest.imageStreams.length).toBe(3);
637
- expect(manifest.imageStreams[0].id).toBe(1);
638
- expect(manifest.imageStreams[1].id).toBe(2);
639
- expect(manifest.imageStreams[2].id).toBe(3);
643
+ // The first 4 streams should be there because they are always supported.
644
+ // The 5th stream should be removed because the MIME type is bogus.
645
+ // The 6th and 7th streams may be there, based on platform support.
646
+ expect(manifest.imageStreams).toContain(
647
+ jasmine.objectContaining({id: 1}));
648
+ expect(manifest.imageStreams).toContain(
649
+ jasmine.objectContaining({id: 2}));
650
+ expect(manifest.imageStreams).toContain(
651
+ jasmine.objectContaining({id: 3}));
652
+ expect(manifest.imageStreams).toContain(
653
+ jasmine.objectContaining({id: 4}));
654
+ expect(manifest.imageStreams).not.toContain(
655
+ jasmine.objectContaining({id: 5}));
640
656
  });
641
657
 
642
658
  it('filters transport streams', async () => {
package/ui/controls.js CHANGED
@@ -9,7 +9,6 @@ goog.provide('shaka.ui.Controls');
9
9
  goog.provide('shaka.ui.ControlsPanel');
10
10
 
11
11
  goog.require('goog.asserts');
12
- goog.require('shaka.Deprecate');
13
12
  goog.require('shaka.ads.AdManager');
14
13
  goog.require('shaka.cast.CastProxy');
15
14
  goog.require('shaka.log');
@@ -556,33 +555,74 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
556
555
  this.hideSettingsMenusTimer_.tickNow();
557
556
  }
558
557
 
558
+ /**
559
+ * @return {boolean}
560
+ * @export
561
+ */
562
+ isFullScreenSupported() {
563
+ if (document.fullscreenEnabled) {
564
+ return true;
565
+ }
566
+ const video = /** @type {HTMLVideoElement} */(this.localVideo_);
567
+ if (video.webkitSupportsFullscreen) {
568
+ return true;
569
+ }
570
+ return false;
571
+ }
572
+
573
+ /**
574
+ * @return {boolean}
575
+ * @export
576
+ */
577
+ isFullScreenEnabled() {
578
+ if (document.fullscreenEnabled) {
579
+ return !!document.fullscreenElement;
580
+ }
581
+ const video = /** @type {HTMLVideoElement} */(this.localVideo_);
582
+ if (video.webkitSupportsFullscreen) {
583
+ return video.webkitDisplayingFullscreen;
584
+ }
585
+ return false;
586
+ }
587
+
559
588
  /** @export */
560
589
  async toggleFullScreen() {
561
- if (document.fullscreenElement) {
562
- if (screen.orientation) {
563
- screen.orientation.unlock();
564
- }
565
- await document.exitFullscreen();
566
- } else {
567
- // If we are in PiP mode, leave PiP mode first.
568
- try {
569
- if (document.pictureInPictureElement) {
570
- await document.exitPictureInPicture();
590
+ if (document.fullscreenEnabled) {
591
+ if (document.fullscreenElement) {
592
+ if (screen.orientation) {
593
+ screen.orientation.unlock();
571
594
  }
572
- await this.videoContainer_.requestFullscreen({navigationUI: 'hide'});
573
- if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
574
- try {
575
- // Locking to 'landscape' should let it be either
576
- // 'landscape-primary' or 'landscape-secondary' as appropriate.
577
- await screen.orientation.lock('landscape');
578
- } catch (error) {
579
- // If screen.orientation.lock does not work on a device, it will
580
- // be rejected with an error. Suppress that error.
595
+ await document.exitFullscreen();
596
+ } else {
597
+ // If we are in PiP mode, leave PiP mode first.
598
+ try {
599
+ if (document.pictureInPictureElement) {
600
+ await document.exitPictureInPicture();
601
+ }
602
+ await this.videoContainer_.requestFullscreen({navigationUI: 'hide'});
603
+ if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
604
+ try {
605
+ // Locking to 'landscape' should let it be either
606
+ // 'landscape-primary' or 'landscape-secondary' as appropriate.
607
+ await screen.orientation.lock('landscape');
608
+ } catch (error) {
609
+ // If screen.orientation.lock does not work on a device, it will
610
+ // be rejected with an error. Suppress that error.
611
+ }
581
612
  }
613
+ } catch (error) {
614
+ this.dispatchEvent(new shaka.util.FakeEvent(
615
+ 'error', (new Map()).set('detail', error)));
616
+ }
617
+ }
618
+ } else {
619
+ const video = /** @type {HTMLVideoElement} */(this.localVideo_);
620
+ if (video.webkitSupportsFullscreen) {
621
+ if (video.webkitDisplayingFullscreen) {
622
+ video.webkitExitFullscreen();
623
+ } else {
624
+ video.webkitEnterFullscreen();
582
625
  }
583
- } catch (error) {
584
- this.dispatchEvent(new shaka.util.FakeEvent(
585
- 'error', (new Map()).set('detail', error)));
586
626
  }
587
627
  }
588
628
  }
@@ -702,7 +742,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
702
742
  });
703
743
 
704
744
  this.eventManager_.listen(this.controlsContainer_, 'dblclick', () => {
705
- if (this.config_.doubleClickForFullscreen && document.fullscreenEnabled) {
745
+ if (this.config_.doubleClickForFullscreen &&
746
+ this.isFullScreenSupported()) {
706
747
  this.toggleFullScreen();
707
748
  }
708
749
  });
@@ -826,22 +867,6 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
826
867
  const factory =
827
868
  shaka.ui.ControlsPanel.elementNamesToFactories_.get(name);
828
869
  const element = factory.create(this.controlsButtonPanel_, this);
829
-
830
- if (typeof element.release != 'function') {
831
- shaka.Deprecate.deprecateFeature(4,
832
- 'shaka.extern.IUIElement',
833
- 'Please update UI elements to have a release() method.');
834
-
835
- // This cast works around compiler strictness about the IUIElement
836
- // type being "@struct" (as ES6 classes are by default).
837
- const moddableElement = /** @type {Object} */(element);
838
- moddableElement['release'] = () => {
839
- if (moddableElement['destroy']) {
840
- moddableElement['destroy']();
841
- }
842
- };
843
- }
844
-
845
870
  this.elements_.push(element);
846
871
  } else {
847
872
  shaka.log.alwaysWarn('Unrecognized control panel element requested:',
@@ -28,16 +28,16 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
28
28
  constructor(parent, controls) {
29
29
  super(parent, controls);
30
30
 
31
+ /** @private {HTMLMediaElement} */
32
+ this.localVideo_ = this.controls.getLocalVideo();
33
+
31
34
  /** @private {!HTMLButtonElement} */
32
35
  this.button_ = shaka.util.Dom.createButton();
33
36
  this.button_.classList.add('shaka-fullscreen-button');
34
37
  this.button_.classList.add('material-icons-round');
35
38
  this.button_.classList.add('shaka-tooltip');
36
39
 
37
- // Don't show the button if fullscreen is not supported
38
- if (!document.fullscreenEnabled) {
39
- this.button_.classList.add('shaka-hidden');
40
- }
40
+ this.checkSupport_();
41
41
 
42
42
  this.button_.textContent = shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
43
43
  this.parent.appendChild(this.button_);
@@ -61,6 +61,26 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
61
61
  this.updateIcon_();
62
62
  this.updateAriaLabel_();
63
63
  });
64
+
65
+ this.eventManager.listen(this.localVideo_, 'loadedmetadata', () => {
66
+ this.checkSupport_();
67
+ });
68
+
69
+ this.eventManager.listen(this.localVideo_, 'loadeddata', () => {
70
+ this.checkSupport_();
71
+ });
72
+ }
73
+
74
+ /**
75
+ * @private
76
+ */
77
+ checkSupport_() {
78
+ // Don't show the button if fullscreen is not supported
79
+ if (!this.controls.isFullScreenSupported()) {
80
+ this.button_.classList.add('shaka-hidden');
81
+ } else {
82
+ this.button_.classList.remove('shaka-hidden');
83
+ }
64
84
  }
65
85
 
66
86
  /**
@@ -68,7 +88,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
68
88
  */
69
89
  updateAriaLabel_() {
70
90
  const LocIds = shaka.ui.Locales.Ids;
71
- const label = document.fullscreenElement ?
91
+ const label = this.controls.isFullScreenEnabled() ?
72
92
  LocIds.EXIT_FULL_SCREEN : LocIds.FULL_SCREEN;
73
93
 
74
94
  this.button_.ariaLabel = this.localization.resolve(label);
@@ -79,7 +99,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
79
99
  */
80
100
  updateIcon_() {
81
101
  this.button_.textContent =
82
- document.fullscreenElement ?
102
+ this.controls.isFullScreenEnabled() ?
83
103
  shaka.ui.Enums.MaterialDesignIcons.EXIT_FULLSCREEN :
84
104
  shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
85
105
  }
@@ -337,8 +337,8 @@
337
337
 
338
338
  .shaka-spinner {
339
339
  /* This uses the same trickery as the big play button define
340
- * the spinner's width and height. See .shaka-play-button
341
- * for the detailed explanation. */
340
+ the spinner's width and height. See .shaka-play-button
341
+ for the detailed explanation. */
342
342
 
343
343
  /* For the padding thing to work, spinner div needs to be an
344
344
  overlay-parent and spinner svg - an overlay child. */
@@ -351,6 +351,6 @@
351
351
  height: 0;
352
352
 
353
353
  /* Add a bit of a white shadow to keep our black spinner visible
354
- * on a black background. */
354
+ on a black background. */
355
355
  filter: drop-shadow(0 0 2px rgba(255 255 255 / 50%));
356
356
  }
@@ -27,6 +27,7 @@
27
27
  "PICTURE_IN_PICTURE": "Picture-in-Picture",
28
28
  "PLAY": "Play",
29
29
  "PLAYBACK_RATE": "Playback speed",
30
+ "QUALITY": "Quality",
30
31
  "REPLAY": "Replay",
31
32
  "RESOLUTION": "Resolution",
32
33
  "REWIND": "Rewind",
@@ -113,6 +113,11 @@
113
113
  "message": "Playback speed",
114
114
  "description": "Label for a button used to navigate to a submenu to choose the playback speed in the video player."
115
115
  },
116
+ "QUALITY": {
117
+ "description": "Label for a button used to open a submenu to choose audio quality in the video player.",
118
+ "meaning": "Audio quality",
119
+ "message": "Quality"
120
+ },
116
121
  "REPLAY": {
117
122
  "description": "Label for a button used to replay the video in a video player.",
118
123
  "message": "Replay"
@@ -61,9 +61,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
61
61
  });
62
62
 
63
63
  this.updateResolutionSelection_();
64
-
65
- // Set up all the strings in the user's preferred language.
66
- this.updateLocalizedStrings_();
67
64
  }
68
65
 
69
66
 
@@ -72,27 +69,6 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
72
69
  /** @type {!Array.<shaka.extern.Track>} */
73
70
  let tracks = this.player.getVariantTracks();
74
71
 
75
- // Hide resolution menu and button for audio-only content and src= content
76
- // without resolution information.
77
- // TODO: for audio-only content, this should be a bitrate selection menu
78
- // instead.
79
- if (tracks.length && !tracks[0].height) {
80
- shaka.ui.Utils.setDisplay(this.menu, false);
81
- shaka.ui.Utils.setDisplay(this.button, false);
82
- return;
83
- }
84
- // Otherwise, restore it.
85
- shaka.ui.Utils.setDisplay(this.button, true);
86
-
87
- tracks.sort((t1, t2) => {
88
- // We have already screened for audio-only content, but the compiler
89
- // doesn't know that.
90
- goog.asserts.assert(t1.height != null, 'Null height');
91
- goog.asserts.assert(t2.height != null, 'Null height');
92
-
93
- return t2.height - t1.height;
94
- });
95
-
96
72
  // If there is a selected variant track, then we filter out any tracks in
97
73
  // a different language. Then we use those remaining tracks to display the
98
74
  // available resolutions.
@@ -104,14 +80,31 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
104
80
  track.channelsCount == selectedTrack.channelsCount);
105
81
  }
106
82
 
107
- // Remove duplicate entries with the same height. This can happen if
108
- // we have multiple resolutions of audio. Pick an arbitrary one.
83
+ // Remove duplicate entries with the same resolution or quality depending
84
+ // on content type. Pick an arbitrary one.
109
85
  tracks = tracks.filter((track, idx) => {
110
- // Keep the first one with the same height.
111
- const otherIdx = tracks.findIndex((t) => t.height == track.height);
86
+ // Keep the first one with the same height or bandwidth.
87
+ const otherIdx = this.player.isAudioOnly() ?
88
+ tracks.findIndex((t) => t.bandwidth == track.bandwidth) :
89
+ tracks.findIndex((t) => t.height == track.height);
112
90
  return otherIdx == idx;
113
91
  });
114
92
 
93
+ // Sort the tracks by height or bandwidth depending on content type.
94
+ if (this.player.isAudioOnly()) {
95
+ tracks.sort((t1, t2) => {
96
+ goog.asserts.assert(t1.bandwidth != null, 'Null bandwidth');
97
+ goog.asserts.assert(t2.bandwidth != null, 'Null bandwidth');
98
+ return t2.bandwidth - t1.bandwidth;
99
+ });
100
+ } else {
101
+ tracks.sort((t1, t2) => {
102
+ goog.asserts.assert(t1.height != null, 'Null height');
103
+ goog.asserts.assert(t2.height != null, 'Null height');
104
+ return t2.height - t1.height;
105
+ });
106
+ }
107
+
115
108
  // Remove old shaka-resolutions
116
109
  // 1. Save the back to menu button
117
110
  const backButton = shaka.ui.Utils.getFirstDescendantWithClassName(
@@ -133,7 +126,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
133
126
  () => this.onTrackSelected_(track));
134
127
 
135
128
  const span = shaka.util.Dom.createHTMLElement('span');
136
- span.textContent = track.height + 'p';
129
+ span.textContent = this.player.isAudioOnly() ?
130
+ Math.round(track.bandwidth / 1000) + ' kbits/s' : track.height + 'p';
137
131
  button.appendChild(span);
138
132
 
139
133
  if (!abrEnabled && track == selectedTrack) {
@@ -179,6 +173,8 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
179
173
  shaka.ui.Utils.focusOnTheChosenItem(this.menu);
180
174
  this.controls.dispatchEvent(
181
175
  new shaka.util.FakeEvent('resolutionselectionupdated'));
176
+
177
+ this.updateLocalizedStrings_();
182
178
  }
183
179
 
184
180
 
@@ -200,13 +196,15 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
200
196
  */
201
197
  updateLocalizedStrings_() {
202
198
  const LocIds = shaka.ui.Locales.Ids;
199
+ const locId = this.player.isAudioOnly() ?
200
+ LocIds.QUALITY : LocIds.RESOLUTION;
203
201
 
204
- this.button.ariaLabel = this.localization.resolve(LocIds.RESOLUTION);
205
- this.backButton.ariaLabel = this.localization.resolve(LocIds.RESOLUTION);
202
+ this.button.ariaLabel = this.localization.resolve(locId);
203
+ this.backButton.ariaLabel = this.localization.resolve(locId);
206
204
  this.backSpan.textContent =
207
- this.localization.resolve(LocIds.RESOLUTION);
205
+ this.localization.resolve(locId);
208
206
  this.nameSpan.textContent =
209
- this.localization.resolve(LocIds.RESOLUTION);
207
+ this.localization.resolve(locId);
210
208
  this.abrOnSpan_.textContent =
211
209
  this.localization.resolve(LocIds.AUTO_QUALITY);
212
210
 
package/ui/ui.js CHANGED
@@ -448,14 +448,6 @@ shaka.ui.Overlay.TrackLabelFormat = {
448
448
  'LABEL': 3,
449
449
  };
450
450
 
451
- /*
452
- * "shaka.ui.TrackLabelFormat" is deprecated and will be removed in v4.
453
- *
454
- * @deprecated
455
- * @enum {number}
456
- */
457
- shaka.ui.TrackLabelFormat = shaka.ui.Overlay.TrackLabelFormat;
458
-
459
451
  /**
460
452
  * Describes the possible reasons that the UI might fail to load.
461
453
  *
@@ -468,15 +460,6 @@ shaka.ui.Overlay.FailReasonCode = {
468
460
  };
469
461
 
470
462
 
471
- /**
472
- * "shaka.ui.FailReasonCode" is deprecated and will be removed in v4.
473
- *
474
- * @deprecated
475
- * @enum {number}
476
- */
477
- shaka.ui.FailReasonCode = shaka.ui.Overlay.FailReasonCode;
478
-
479
-
480
463
  if (document.readyState == 'complete') {
481
464
  // Don't fire this event synchronously. In a compiled bundle, the "shaka"
482
465
  // namespace might not be exported to the window until after this point.
@@ -1,3 +0,0 @@
1
- # Google Summer of Code Ideas List
2
-
3
- Please see the latest project ideas list [here](https://storage.googleapis.com/wvtemp/shaka/Shaka%20Player%20GSoC%20Projects%20List.pdf).