shaka-player 4.11.13 → 4.12.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 (71) hide show
  1. package/README.md +47 -0
  2. package/dist/deps.js +8 -8
  3. package/dist/locales.js +35 -35
  4. package/dist/shaka-player.compiled.d.ts +36 -13
  5. package/dist/shaka-player.compiled.debug.d.ts +36 -13
  6. package/dist/shaka-player.compiled.debug.externs.js +46 -11
  7. package/dist/shaka-player.compiled.debug.js +543 -517
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +46 -11
  10. package/dist/shaka-player.compiled.js +1348 -1331
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +37 -14
  13. package/dist/shaka-player.ui.debug.d.ts +37 -14
  14. package/dist/shaka-player.ui.debug.externs.js +46 -11
  15. package/dist/shaka-player.ui.debug.js +629 -598
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +46 -11
  18. package/dist/shaka-player.ui.js +1624 -1603
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/ads.js +9 -0
  21. package/externs/shaka/manifest.js +26 -1
  22. package/externs/shaka/offline.js +8 -2
  23. package/externs/shaka/player.js +80 -28
  24. package/externs/shaka/text.js +9 -0
  25. package/lib/ads/ad_manager.js +6 -0
  26. package/lib/ads/ad_utils.js +1 -0
  27. package/lib/ads/client_side_ad.js +8 -0
  28. package/lib/ads/interstitial_ad.js +25 -4
  29. package/lib/ads/interstitial_ad_manager.js +67 -8
  30. package/lib/ads/media_tailor_ad.js +8 -0
  31. package/lib/ads/server_side_ad.js +8 -0
  32. package/lib/dash/content_protection.js +31 -0
  33. package/lib/dash/dash_parser.js +22 -12
  34. package/lib/hls/hls_parser.js +63 -49
  35. package/lib/media/adaptation_set.js +11 -17
  36. package/lib/media/adaptation_set_criteria.js +4 -9
  37. package/lib/media/drm_engine.js +3 -1
  38. package/lib/media/manifest_filterer.js +2 -1
  39. package/lib/media/media_source_engine.js +286 -20
  40. package/lib/media/playhead.js +3 -1
  41. package/lib/media/preload_manager.js +10 -7
  42. package/lib/media/segment_prefetch.js +32 -13
  43. package/lib/media/segment_reference.js +3 -3
  44. package/lib/media/streaming_engine.js +87 -261
  45. package/lib/mss/mss_parser.js +1 -0
  46. package/lib/offline/download_info.js +5 -1
  47. package/lib/offline/indexeddb/storage_mechanism.js +4 -3
  48. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  49. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  50. package/lib/offline/manifest_converter.js +4 -0
  51. package/lib/offline/storage.js +277 -5
  52. package/lib/player.js +224 -78
  53. package/lib/text/simple_text_displayer.js +7 -0
  54. package/lib/text/stub_text_displayer.js +7 -0
  55. package/lib/text/ui_text_displayer.js +12 -0
  56. package/lib/util/dom_utils.js +14 -0
  57. package/lib/util/error.js +5 -0
  58. package/lib/util/periods.js +2 -0
  59. package/lib/util/platform.js +15 -0
  60. package/lib/util/player_configuration.js +11 -4
  61. package/lib/util/stream_utils.js +20 -1
  62. package/package.json +1 -1
  63. package/ui/ad_statistics_button.js +12 -0
  64. package/ui/controls.js +31 -7
  65. package/ui/externs/ui.js +8 -1
  66. package/ui/fullscreen_button.js +9 -0
  67. package/ui/locales/be.json +49 -0
  68. package/ui/mute_button.js +42 -11
  69. package/ui/skip_ad_button.js +16 -11
  70. package/ui/statistics_button.js +12 -0
  71. package/ui/ui.js +10 -5
@@ -1607,6 +1607,7 @@ shaka.util.PeriodCombiner = class {
1607
1607
  closedCaptions: null,
1608
1608
  external: false,
1609
1609
  fastSwitching: false,
1610
+ isAudioMuxedInVideo: false,
1610
1611
  };
1611
1612
  }
1612
1613
 
@@ -1649,6 +1650,7 @@ shaka.util.PeriodCombiner = class {
1649
1650
  external: false,
1650
1651
  fastSwitching: false,
1651
1652
  fullMimeTypes: new Set(),
1653
+ isAudioMuxedInVideo: false,
1652
1654
  };
1653
1655
  }
1654
1656
 
@@ -489,6 +489,21 @@ shaka.util.Platform = class {
489
489
  return navigator.platform.toLowerCase().includes('mac');
490
490
  }
491
491
 
492
+ /**
493
+ * Return true if the platform is a VisionOS.
494
+ *
495
+ * @return {boolean}
496
+ */
497
+ static isVisionOS() {
498
+ if (!shaka.util.Platform.isMac()) {
499
+ return false;
500
+ }
501
+ if (!('xr' in navigator)) {
502
+ return false;
503
+ }
504
+ return true;
505
+ }
506
+
492
507
  /**
493
508
  * Return true if the platform is a Windows, regardless of the browser.
494
509
  *
@@ -118,6 +118,7 @@ shaka.util.PlayerConfiguration = class {
118
118
  disableVideo: false,
119
119
  disableText: false,
120
120
  disableThumbnails: false,
121
+ disableIFrames: false,
121
122
  defaultPresentationDelay: 0,
122
123
  segmentRelativeVttTiming: false,
123
124
  raiseFatalErrorOnManifestUpdateRequestFailure: false,
@@ -125,7 +126,7 @@ shaka.util.PlayerConfiguration = class {
125
126
  dash: {
126
127
  clockSyncUri: '',
127
128
  ignoreDrmInfo: false,
128
- disableXlinkProcessing: false,
129
+ disableXlinkProcessing: true,
129
130
  xlinkFailGracefully: false,
130
131
  ignoreMinBufferTime: false,
131
132
  autoCorrectDrift: true,
@@ -144,13 +145,14 @@ shaka.util.PlayerConfiguration = class {
144
145
  'com.microsoft.playready',
145
146
  'urn:uuid:79f0049a-4098-8642-ab92-e65be0885f95':
146
147
  'com.microsoft.playready',
148
+ 'urn:uuid:94ce86fb-07ff-4f43-adb8-93d2fa968ca2':
149
+ 'com.apple.fps',
147
150
  },
148
151
  manifestPreprocessor:
149
152
  shaka.util.PlayerConfiguration.defaultManifestPreprocessor,
150
153
  manifestPreprocessorTXml:
151
154
  shaka.util.PlayerConfiguration.defaultManifestPreprocessorTXml,
152
155
  sequenceMode: false,
153
- enableAudioGroups: false,
154
156
  multiTypeVariantsAllowed,
155
157
  useStreamOnceInPeriodFlattening: false,
156
158
  updatePeriod: -1,
@@ -170,6 +172,7 @@ shaka.util.PlayerConfiguration = class {
170
172
  ignoreManifestTimestampsInSegmentsMode: false,
171
173
  disableCodecGuessing: false,
172
174
  disableClosedCaptionsDetection: false,
175
+ updatePeriod: -1,
173
176
  allowLowLatencyByteRangeOptimization: true,
174
177
  },
175
178
  mss: {
@@ -214,6 +217,7 @@ shaka.util.PlayerConfiguration = class {
214
217
  // Offset by 5 seconds since Chromecast takes a few seconds to start
215
218
  // playing after a seek, even when buffered.
216
219
  safeSeekOffset: 5,
220
+ safeSeekEndOffset: 0,
217
221
  stallEnabled: true,
218
222
  stallThreshold: 1 /* seconds */,
219
223
  stallSkip: 0.1 /* seconds */,
@@ -231,10 +235,8 @@ shaka.util.PlayerConfiguration = class {
231
235
  minBytesForProgressEvents: minBytes,
232
236
  preferNativeHls: false,
233
237
  updateIntervalSeconds: 1,
234
- dispatchAllEmsgBoxes: false,
235
238
  observeQualityChanges: false,
236
239
  maxDisabledTime: 30,
237
- parsePrftBox: false,
238
240
  // When low latency streaming is enabled, segmentPrefetchLimit will
239
241
  // default to 2 if not specified.
240
242
  segmentPrefetchLimit: 0,
@@ -399,6 +401,7 @@ shaka.util.PlayerConfiguration = class {
399
401
  [cue, uri],
400
402
  undefined);
401
403
  },
404
+ dispatchAllEmsgBoxes: false,
402
405
  };
403
406
 
404
407
  let customPlayheadTracker = false;
@@ -414,6 +417,8 @@ shaka.util.PlayerConfiguration = class {
414
417
  customPlayheadTracker,
415
418
  skipPlayDetection,
416
419
  supportsMultipleMediaElements,
420
+ disableHLSInterstitial: false,
421
+ disableDASHInterstitial: false,
417
422
  };
418
423
 
419
424
  const textDisplayer = {
@@ -443,6 +448,7 @@ shaka.util.PlayerConfiguration = class {
443
448
  preferredVideoLabel: '',
444
449
  preferredVideoCodecs: [],
445
450
  preferredAudioCodecs: [],
451
+ preferredTextFormats: [],
446
452
  preferForcedSubs: false,
447
453
  preferSpatialAudio: false,
448
454
  preferredDecodingAttributes: [],
@@ -468,6 +474,7 @@ shaka.util.PlayerConfiguration = class {
468
474
  cmsd: cmsd,
469
475
  lcevc: lcevc,
470
476
  ads: ads,
477
+ ignoreHardwareResolution: false,
471
478
  };
472
479
 
473
480
  // Add this callback so that we can reference the preferred audio language
@@ -33,12 +33,31 @@ shaka.util.StreamUtils = class {
33
33
  * @param {!Array.<string>} preferredVideoCodecs
34
34
  * @param {!Array.<string>} preferredAudioCodecs
35
35
  * @param {!Array.<string>} preferredDecodingAttributes
36
+ * @param {!Array.<string>} preferredTextFormats
36
37
  */
37
38
  static chooseCodecsAndFilterManifest(manifest, preferredVideoCodecs,
38
- preferredAudioCodecs, preferredDecodingAttributes) {
39
+ preferredAudioCodecs, preferredDecodingAttributes, preferredTextFormats) {
39
40
  const StreamUtils = shaka.util.StreamUtils;
40
41
  const MimeUtils = shaka.util.MimeUtils;
41
42
 
43
+ if (preferredTextFormats.length) {
44
+ let subset = manifest.textStreams;
45
+ for (const textFormat of preferredTextFormats) {
46
+ const filtered = subset.filter((textStream) => {
47
+ if (textStream.codecs.startsWith(textFormat) ||
48
+ textStream.mimeType.startsWith(textFormat)) {
49
+ return true;
50
+ }
51
+ return false;
52
+ });
53
+ if (filtered.length) {
54
+ subset = filtered;
55
+ break;
56
+ }
57
+ }
58
+ manifest.textStreams = subset;
59
+ }
60
+
42
61
  let variants = manifest.variants;
43
62
  // To start, choose the codecs based on configured preferences if available.
44
63
  if (preferredVideoCodecs.length || preferredAudioCodecs.length) {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.11.13",
4
+ "version": "4.12.1",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -189,6 +189,18 @@ shaka.ui.AdStatisticsButton = class extends shaka.ui.Element {
189
189
 
190
190
  /** @private */
191
191
  loadContainer_() {
192
+ const closeElement = shaka.util.Dom.createHTMLElement('div');
193
+ closeElement.classList.add('shaka-no-propagation');
194
+ closeElement.classList.add('shaka-statistics-close');
195
+ const icon = shaka.util.Dom.createHTMLElement('i');
196
+ icon.classList.add('material-icons-round');
197
+ icon.textContent =
198
+ shaka.ui.Enums.MaterialDesignIcons.CLOSE;
199
+ closeElement.appendChild(icon);
200
+ this.container_.appendChild(closeElement);
201
+ this.eventManager.listen(icon, 'click', () => {
202
+ this.onClick_();
203
+ });
192
204
  for (const name of this.controls.getConfig().adStatisticsList) {
193
205
  if (name in this.currentStats_) {
194
206
  this.container_.appendChild(this.generateComponent_(name));
package/ui/controls.js CHANGED
@@ -29,6 +29,7 @@ goog.require('shaka.util.EventManager');
29
29
  goog.require('shaka.util.FakeEvent');
30
30
  goog.require('shaka.util.FakeEventTarget');
31
31
  goog.require('shaka.util.IDestroyable');
32
+ goog.require('shaka.util.Platform');
32
33
  goog.require('shaka.util.Timer');
33
34
 
34
35
  goog.requireType('shaka.Player');
@@ -203,6 +204,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
203
204
  shaka.ui.Localization.LOCALE_CHANGED, (e) => {
204
205
  const locale = e['locales'][0];
205
206
  this.adManager_.setLocale(locale);
207
+ this.videoContainer_.setAttribute('lang', locale);
206
208
  });
207
209
 
208
210
  this.adManager_.initInterstitial(
@@ -604,16 +606,38 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
604
606
 
605
607
  /**
606
608
  * @return {boolean}
607
- * @export
609
+ * @private
608
610
  */
609
- isFullScreenSupported() {
610
- if (document.fullscreenEnabled) {
611
- return true;
611
+ shouldUseDocumentFullscreen_() {
612
+ if (!document.fullscreenEnabled) {
613
+ return false;
612
614
  }
615
+ // When the preferVideoFullScreenInVisionOS configuration value applies,
616
+ // we avoid using document fullscreen, even if it is available.
613
617
  const video = /** @type {HTMLVideoElement} */(this.localVideo_);
614
618
  if (video.webkitSupportsFullscreen) {
619
+ if (this.config_.preferVideoFullScreenInVisionOS &&
620
+ shaka.util.Platform.isVisionOS()) {
621
+ return false;
622
+ }
623
+ }
624
+ return true;
625
+ }
626
+
627
+ /**
628
+ * @return {boolean}
629
+ * @export
630
+ */
631
+ isFullScreenSupported() {
632
+ if (this.shouldUseDocumentFullscreen_()) {
615
633
  return true;
616
634
  }
635
+ if (!this.ad_ || !this.ad_.isUsingAnotherMediaElement()) {
636
+ const video = /** @type {HTMLVideoElement} */(this.localVideo_);
637
+ if (video.webkitSupportsFullscreen) {
638
+ return true;
639
+ }
640
+ }
617
641
  return false;
618
642
  }
619
643
 
@@ -622,7 +646,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
622
646
  * @export
623
647
  */
624
648
  isFullScreenEnabled() {
625
- if (document.fullscreenEnabled) {
649
+ if (this.shouldUseDocumentFullscreen_()) {
626
650
  return !!document.fullscreenElement;
627
651
  }
628
652
  const video = /** @type {HTMLVideoElement} */(this.localVideo_);
@@ -635,7 +659,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
635
659
  /** @private */
636
660
  async enterFullScreen_() {
637
661
  try {
638
- if (document.fullscreenEnabled) {
662
+ if (this.shouldUseDocumentFullscreen_()) {
639
663
  if (document.pictureInPictureElement) {
640
664
  await document.exitPictureInPicture();
641
665
  }
@@ -666,7 +690,7 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
666
690
 
667
691
  /** @private */
668
692
  async exitFullScreen_() {
669
- if (document.fullscreenEnabled) {
693
+ if (this.shouldUseDocumentFullscreen_()) {
670
694
  if (screen.orientation) {
671
695
  screen.orientation.unlock();
672
696
  }
package/ui/externs/ui.js CHANGED
@@ -97,7 +97,8 @@ shaka.extern.UIVolumeBarColors;
97
97
  * refreshTickInSeconds: number,
98
98
  * displayInVrMode: boolean,
99
99
  * defaultVrProjectionMode: string,
100
- * setupMediaSession: boolean
100
+ * setupMediaSession: boolean,
101
+ * preferVideoFullScreenInVisionOS: boolean
101
102
  * }}
102
103
  *
103
104
  * @property {!Array.<string>} controlPanelElements
@@ -254,6 +255,12 @@ shaka.extern.UIVolumeBarColors;
254
255
  * the ID3 APIC and TIT2 as image and title in Media Session, and ID3 APIC
255
256
  * will also be used to change video poster.
256
257
  * Defaults to true.
258
+ * @property {boolean} preferVideoFullScreenInVisionOS
259
+ * If true, we will use the fullscreen API of the video element itself if it
260
+ * is available in Vision OS. This is useful to be able to access 3D
261
+ * experiences that are only allowed with the fullscreen of the video element
262
+ * itself.
263
+ * Defaults to false.
257
264
  * @exportDoc
258
265
  */
259
266
  shaka.extern.UIConfiguration;
@@ -7,6 +7,7 @@
7
7
 
8
8
  goog.provide('shaka.ui.FullscreenButton');
9
9
 
10
+ goog.require('shaka.ads.Utils');
10
11
  goog.require('shaka.ui.Controls');
11
12
  goog.require('shaka.ui.Element');
12
13
  goog.require('shaka.ui.Enums');
@@ -69,6 +70,14 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
69
70
  this.eventManager.listen(this.localVideo_, 'loadeddata', () => {
70
71
  this.checkSupport_();
71
72
  });
73
+
74
+ this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_STARTED, () => {
75
+ this.checkSupport_();
76
+ });
77
+
78
+ this.eventManager.listen(this.adManager, shaka.ads.Utils.AD_STOPPED, () => {
79
+ this.checkSupport_();
80
+ });
72
81
  }
73
82
 
74
83
  /**
@@ -0,0 +1,49 @@
1
+ {
2
+ "AD_DURATION": "Працягласць рэкламы",
3
+ "AD_PROGRESS": "Рэклама [AD_ON] з [NUM_ADS]",
4
+ "AD_STATISTICS": "Статыстыка аб'яўленняў",
5
+ "AD_TIME": "Рэклама: [AD_TIME]",
6
+ "AIRPLAY": "AirPlay",
7
+ "AUTO_QUALITY": "Аўта",
8
+ "BACK": "Назад",
9
+ "CAPTIONS": "Субцітры",
10
+ "CAST": "Трансляваць...",
11
+ "CHAPTERS": "Раздзелы",
12
+ "DOWNLOAD_VIDEO_FRAME": "Захаваць кадр відэа",
13
+ "ENTER_LOOP_MODE": "Зацыкліць цяперашняе відэа",
14
+ "ENTER_PICTURE_IN_PICTURE": "Перайсці ў рэжым \"Выява ў выяве\"",
15
+ "EXIT_FULL_SCREEN": "Звычайны рэжым",
16
+ "EXIT_LOOP_MODE": "Адмяніць зацыкліванне цяперашняга відэа",
17
+ "EXIT_PICTURE_IN_PICTURE": "Выйсці з рэжыма \"Выява ў выяве\"",
18
+ "FAST_FORWARD": "Перамотка наперад",
19
+ "FULL_SCREEN": "Поўнаэкраны рэжым",
20
+ "LANGUAGE": "Мова",
21
+ "LIVE": "Ужывую",
22
+ "LOOP": "Зацыкліць",
23
+ "MORE_SETTINGS": "Дадатковыя налады",
24
+ "MULTIPLE_LANGUAGES": "Несколько моваў",
25
+ "MUTE": "Адключыць гук",
26
+ "NOT_APPLICABLE": "Не ўжываецца",
27
+ "OFF": "Выкл.",
28
+ "ON": "Укл.",
29
+ "PAUSE": "Паўза",
30
+ "PICTURE_IN_PICTURE": "Выява ў выяве",
31
+ "PLAY": "Прайграць",
32
+ "PLAYBACK_RATE": "Хуткасць прайгравання",
33
+ "QUALITY": "Якасць",
34
+ "RECENTER_VR": "Цэнтраваць зноў",
35
+ "REPLAY": "Паўтарыць",
36
+ "RESOLUTION": "Раздзяляльнасць",
37
+ "REWIND": "Перамотка назад",
38
+ "SEEK": "Перамотка",
39
+ "SKIP_AD": "Прапусціць",
40
+ "SKIP_TO_LIVE": "Прапусціць і перайсці да жывога эфіра",
41
+ "STATISTICS": "Статыстыка",
42
+ "SUBTITLE_FORCED": "Субцітры прымусова",
43
+ "SURROUND": "Аб'ёмны гук",
44
+ "TOGGLE_STEREOSCOPIC": "Уключыць або адключыць стэрэаскапічны рэжым",
45
+ "UNDETERMINED_LANGUAGE": "Не пазначана",
46
+ "UNMUTE": "Уключыць гук",
47
+ "UNRECOGNIZED_LANGUAGE": "Не распазнана",
48
+ "VOLUME": "Гучнасць"
49
+ }
package/ui/mute_button.js CHANGED
@@ -8,11 +8,13 @@
8
8
  goog.provide('shaka.ui.MuteButton');
9
9
 
10
10
  goog.require('shaka.ads.Utils');
11
+ goog.require('shaka.ui.ContextMenu');
11
12
  goog.require('shaka.ui.Controls');
12
13
  goog.require('shaka.ui.Element');
13
14
  goog.require('shaka.ui.Enums');
14
15
  goog.require('shaka.ui.Locales');
15
16
  goog.require('shaka.ui.Localization');
17
+ goog.require('shaka.ui.OverflowMenu');
16
18
  goog.require('shaka.util.Dom');
17
19
 
18
20
 
@@ -29,23 +31,44 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
29
31
  constructor(parent, controls) {
30
32
  super(parent, controls);
31
33
 
34
+ const LocIds = shaka.ui.Locales.Ids;
32
35
  /** @private {!HTMLButtonElement} */
33
36
  this.button_ = shaka.util.Dom.createButton();
34
37
  this.button_.classList.add('shaka-mute-button');
35
- this.button_.classList.add('material-icons-round');
36
38
  this.button_.classList.add('shaka-tooltip');
39
+
40
+ /** @private {!HTMLElement} */
41
+ this.icon_ = shaka.util.Dom.createHTMLElement('i');
42
+ this.icon_.classList.add('material-icons-round');
43
+ this.icon_.textContent = shaka.ui.Enums.MaterialDesignIcons.MUTE;
44
+ this.button_.appendChild(this.icon_);
45
+
46
+ const label = shaka.util.Dom.createHTMLElement('label');
47
+ label.classList.add('shaka-overflow-button-label');
48
+ label.classList.add('shaka-overflow-menu-only');
49
+ this.nameSpan_ = shaka.util.Dom.createHTMLElement('span');
50
+ this.nameSpan_.textContent = this.localization.resolve(LocIds.MUTE);
51
+ label.appendChild(this.nameSpan_);
52
+
53
+ /** @private {!HTMLElement} */
54
+ this.currentState_ = shaka.util.Dom.createHTMLElement('span');
55
+ this.currentState_.classList.add('shaka-current-selection-span');
56
+ label.appendChild(this.currentState_);
57
+
58
+ this.button_.appendChild(label);
59
+
37
60
  this.parent.appendChild(this.button_);
38
- this.updateAriaLabel_();
61
+ this.updateLocalizedStrings_();
39
62
  this.updateIcon_();
40
63
 
41
64
  this.eventManager.listen(
42
65
  this.localization, shaka.ui.Localization.LOCALE_UPDATED, () => {
43
- this.updateAriaLabel_();
66
+ this.updateLocalizedStrings_();
44
67
  });
45
68
 
46
69
  this.eventManager.listen(
47
70
  this.localization, shaka.ui.Localization.LOCALE_CHANGED, () => {
48
- this.updateAriaLabel_();
71
+ this.updateLocalizedStrings_();
49
72
  });
50
73
 
51
74
  this.eventManager.listen(this.button_, 'click', () => {
@@ -57,19 +80,19 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
57
80
  });
58
81
 
59
82
  this.eventManager.listen(this.video, 'volumechange', () => {
60
- this.updateAriaLabel_();
83
+ this.updateLocalizedStrings_();
61
84
  this.updateIcon_();
62
85
  });
63
86
 
64
87
  this.eventManager.listen(this.adManager,
65
88
  shaka.ads.Utils.AD_VOLUME_CHANGED, () => {
66
- this.updateAriaLabel_();
89
+ this.updateLocalizedStrings_();
67
90
  this.updateIcon_();
68
91
  });
69
92
 
70
93
  this.eventManager.listen(this.adManager,
71
94
  shaka.ads.Utils.AD_MUTED, () => {
72
- this.updateAriaLabel_();
95
+ this.updateLocalizedStrings_();
73
96
  this.updateIcon_();
74
97
  });
75
98
 
@@ -80,7 +103,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
80
103
  // the label and icon code depends on this.ad being correctly
81
104
  // updated at the time it runs.
82
105
  this.ad = null;
83
- this.updateAriaLabel_();
106
+ this.updateLocalizedStrings_();
84
107
  this.updateIcon_();
85
108
  });
86
109
  }
@@ -88,7 +111,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
88
111
  /**
89
112
  * @private
90
113
  */
91
- updateAriaLabel_() {
114
+ updateLocalizedStrings_() {
92
115
  const LocIds = shaka.ui.Locales.Ids;
93
116
  let label;
94
117
  if (this.ad) {
@@ -98,6 +121,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
98
121
  }
99
122
 
100
123
  this.button_.ariaLabel = this.localization.resolve(label);
124
+ this.nameSpan_.textContent = this.localization.resolve(label);
101
125
  }
102
126
 
103
127
  /**
@@ -111,7 +135,7 @@ shaka.ui.MuteButton = class extends shaka.ui.Element {
111
135
  } else {
112
136
  icon = this.video.muted ? Icons.UNMUTE : Icons.MUTE;
113
137
  }
114
- this.button_.textContent = icon;
138
+ this.icon_.textContent = icon;
115
139
  }
116
140
  };
117
141
 
@@ -127,4 +151,11 @@ shaka.ui.MuteButton.Factory = class {
127
151
  }
128
152
  };
129
153
 
130
- shaka.ui.Controls.registerElement('mute', new shaka.ui.MuteButton.Factory());
154
+ shaka.ui.OverflowMenu.registerElement(
155
+ 'mute', new shaka.ui.MuteButton.Factory());
156
+
157
+ shaka.ui.Controls.registerElement(
158
+ 'mute', new shaka.ui.MuteButton.Factory());
159
+
160
+ shaka.ui.ContextMenu.registerElement(
161
+ 'mute', new shaka.ui.MuteButton.Factory());
@@ -142,17 +142,20 @@ shaka.ui.SkipAdButton = class extends shaka.ui.Element {
142
142
  onTimerTick_() {
143
143
  goog.asserts.assert(this.ad != null,
144
144
  'this.ad should exist at this point');
145
-
146
- const secondsLeft = Math.round(this.ad.getTimeUntilSkippable());
147
- if (secondsLeft > 0) {
148
- this.counter_.textContent = secondsLeft;
145
+ if (this.ad.isSkippable()) {
146
+ const secondsLeft = Math.round(this.ad.getTimeUntilSkippable());
147
+ if (secondsLeft > 0) {
148
+ this.counter_.textContent = secondsLeft;
149
+ } else {
150
+ // The ad should now be skippable. OnSkipStateChanged() is
151
+ // listening for a SKIP_STATE_CHANGED event and will take care
152
+ // of the button. Here we just stop the timer and hide the counter.
153
+ // NOTE: onSkipStateChanged_() also hides the counter.
154
+ this.timer_.stop();
155
+ shaka.ui.Utils.setDisplay(this.counter_, false);
156
+ }
149
157
  } else {
150
- // The ad should now be skippable. OnSkipStateChanged() is
151
- // listening for a SKIP_STATE_CHANGED event and will take care
152
- // of the button. Here we just stop the timer and hide the counter.
153
- // NOTE: onSkipStateChanged_() also hides the counter.
154
- this.timer_.stop();
155
- shaka.ui.Utils.setDisplay(this.counter_, false);
158
+ this.reset_();
156
159
  }
157
160
  }
158
161
 
@@ -161,7 +164,9 @@ shaka.ui.SkipAdButton = class extends shaka.ui.Element {
161
164
  */
162
165
  onSkipStateChanged_() {
163
166
  // Double-check that the ad is now skippable
164
- if (this.ad.canSkipNow()) {
167
+ if (!this.ad.isSkippable()) {
168
+ this.reset_();
169
+ } else if (this.ad.canSkipNow()) {
165
170
  this.button_.disabled = false;
166
171
  this.timer_.stop();
167
172
  shaka.ui.Utils.setDisplay(this.counter_, false);
@@ -239,6 +239,18 @@ shaka.ui.StatisticsButton = class extends shaka.ui.Element {
239
239
 
240
240
  /** @private */
241
241
  loadContainer_() {
242
+ const closeElement = shaka.util.Dom.createHTMLElement('div');
243
+ closeElement.classList.add('shaka-no-propagation');
244
+ closeElement.classList.add('shaka-statistics-close');
245
+ const icon = shaka.util.Dom.createHTMLElement('i');
246
+ icon.classList.add('material-icons-round');
247
+ icon.textContent =
248
+ shaka.ui.Enums.MaterialDesignIcons.CLOSE;
249
+ closeElement.appendChild(icon);
250
+ this.container_.appendChild(closeElement);
251
+ this.eventManager.listen(icon, 'click', () => {
252
+ this.onClick_();
253
+ });
242
254
  for (const name of this.controls.getConfig().statisticsList) {
243
255
  if (name in this.currentStats_ && !this.skippedStats_.includes(name)) {
244
256
  const element = this.generateComponent_(name);
package/ui/ui.js CHANGED
@@ -273,6 +273,7 @@ shaka.ui.Overlay = class {
273
273
  displayInVrMode: false,
274
274
  defaultVrProjectionMode: 'equirectangular',
275
275
  setupMediaSession: true,
276
+ preferVideoFullScreenInVisionOS: false,
276
277
  };
277
278
 
278
279
  // eslint-disable-next-line no-restricted-syntax
@@ -487,19 +488,23 @@ shaka.ui.Overlay = class {
487
488
  // <source src='foo.m2u8'/>
488
489
  // </video>
489
490
  // It should not be specified on both.
491
+ const urls = [];
490
492
  const src = video.getAttribute('src');
491
493
  if (src) {
492
- const sourceElem = document.createElement('source');
493
- sourceElem.setAttribute('src', src);
494
- video.appendChild(sourceElem);
494
+ urls.push(src);
495
495
  video.removeAttribute('src');
496
496
  }
497
497
 
498
+ for (const source of video.getElementsByTagName('source')) {
499
+ urls.push(/** @type {!HTMLSourceElement} */ (source).src);
500
+ video.removeChild(source);
501
+ }
502
+
498
503
  await player.attach(shaka.util.Dom.asHTMLMediaElement(video));
499
504
 
500
- for (const elem of video.querySelectorAll('source')) {
505
+ for (const url of urls) {
501
506
  try { // eslint-disable-next-line no-await-in-loop
502
- await ui.getControls().getPlayer().load(elem.getAttribute('src'));
507
+ await ui.getControls().getPlayer().load(url);
503
508
  break;
504
509
  } catch (e) {
505
510
  shaka.log.error('Error auto-loading asset', e);