shaka-player 4.7.4 → 4.7.6

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 (36) hide show
  1. package/dist/locales.js +31 -31
  2. package/dist/shaka-player.compiled.d.ts +7 -0
  3. package/dist/shaka-player.compiled.debug.d.ts +7 -0
  4. package/dist/shaka-player.compiled.debug.externs.js +4 -0
  5. package/dist/shaka-player.compiled.debug.js +117 -114
  6. package/dist/shaka-player.compiled.debug.map +3 -3
  7. package/dist/shaka-player.compiled.externs.js +4 -0
  8. package/dist/shaka-player.compiled.js +1032 -1030
  9. package/dist/shaka-player.compiled.map +3 -3
  10. package/dist/shaka-player.ui.d.ts +7 -0
  11. package/dist/shaka-player.ui.debug.d.ts +7 -0
  12. package/dist/shaka-player.ui.debug.externs.js +4 -0
  13. package/dist/shaka-player.ui.debug.js +140 -138
  14. package/dist/shaka-player.ui.debug.map +3 -3
  15. package/dist/shaka-player.ui.externs.js +4 -0
  16. package/dist/shaka-player.ui.js +1202 -1200
  17. package/dist/shaka-player.ui.map +3 -3
  18. package/externs/shaka/abr_manager.js +9 -0
  19. package/lib/abr/simple_abr_manager.js +11 -0
  20. package/lib/cast/cast_proxy.js +18 -0
  21. package/lib/hls/hls_utils.js +5 -5
  22. package/lib/media/media_source_engine.js +1 -1
  23. package/lib/media/play_rate_controller.js +1 -0
  24. package/lib/media/segment_prefetch.js +4 -0
  25. package/lib/media/streaming_engine.js +6 -8
  26. package/lib/net/networking_engine.js +1 -1
  27. package/lib/player.js +48 -5
  28. package/lib/text/ttml_text_parser.js +5 -0
  29. package/lib/util/language_utils.js +7 -8
  30. package/lib/util/periods.js +7 -1
  31. package/lib/util/platform.js +18 -0
  32. package/lib/util/stream_utils.js +14 -0
  33. package/package.json +1 -1
  34. package/ui/controls.js +3 -0
  35. package/ui/pip_button.js +5 -21
  36. package/ui/playback_rate_selection.js +4 -0
@@ -96,6 +96,15 @@ shaka.extern.AbrManager = class {
96
96
  */
97
97
  segmentDownloaded(deltaTimeMs, numBytes, allowSwitch) {}
98
98
 
99
+ /**
100
+ * Notifies the ABR that it is a time to suggest new streams. This is used by
101
+ * the Player when it finishes adding the last partial segment of a fast
102
+ * switching stream.
103
+ *
104
+ * @exportDoc
105
+ */
106
+ trySuggestStreams() {}
107
+
99
108
  /**
100
109
  * Gets an estimate of the current bandwidth in bit/sec. This is used by the
101
110
  * Player to generate stats.
@@ -320,6 +320,17 @@ shaka.abr.SimpleAbrManager = class {
320
320
  }
321
321
 
322
322
 
323
+ /**
324
+ * @override
325
+ * @export
326
+ */
327
+ trySuggestStreams() {
328
+ if ((this.lastTimeChosenMs_ != null) && this.enabled_) {
329
+ this.suggestStreams_();
330
+ }
331
+ }
332
+
333
+
323
334
  /**
324
335
  * @override
325
336
  * @export
@@ -496,6 +496,16 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
496
496
  startTime = this.sender_.get('video', 'currentTime');
497
497
  }
498
498
 
499
+ let activeTextTrack;
500
+ const textTracks = this.sender_.get('player', 'getTextTracks')();
501
+
502
+ if (textTracks && textTracks.length) {
503
+ activeTextTrack = textTracks.find((t) => t.active);
504
+ }
505
+
506
+ const isTextTrackVisible =
507
+ this.sender_.get('player', 'isTextTrackVisible')();
508
+
499
509
  // Now load the manifest, if present.
500
510
  if (assetUri) {
501
511
  // Don't autoplay the content until we finish setting up initial state.
@@ -527,6 +537,14 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
527
537
  /** @type {Object} */(this.localPlayer_)[setter](value);
528
538
  }
529
539
 
540
+ this.localPlayer_.setTextTrackVisibility(isTextTrackVisible);
541
+ if (activeTextTrack) {
542
+ this.localPlayer_.selectTextLanguage(
543
+ activeTextTrack.language,
544
+ activeTextTrack.roles,
545
+ activeTextTrack.forced);
546
+ }
547
+
530
548
  // Restore the original autoplay setting.
531
549
  this.localVideo_.autoplay = autoplay;
532
550
  if (assetUri) {
@@ -47,12 +47,12 @@ shaka.hls.Utils = class {
47
47
  * @return {?shaka.hls.Tag}
48
48
  */
49
49
  static getFirstTagWithName(tags, name) {
50
- const tagsWithName = shaka.hls.Utils.filterTagsByName(tags, name);
51
- if (!tagsWithName.length) {
52
- return null;
50
+ for (const tag of tags) {
51
+ if (tag.name === name) {
52
+ return tag;
53
+ }
53
54
  }
54
-
55
- return tagsWithName[0];
55
+ return null;
56
56
  }
57
57
 
58
58
  /**
@@ -1805,7 +1805,7 @@ shaka.media.MediaSourceEngine = class {
1805
1805
 
1806
1806
  this.destroyer_.ensureNotDestroyed();
1807
1807
 
1808
- this.eventManager_.listenOnce(this.video_, 'canplay', () => {
1808
+ this.eventManager_.listenOnce(this.video_, 'canplaythrough', () => {
1809
1809
  this.destroyer_.ensureNotDestroyed();
1810
1810
 
1811
1811
  this.video_.autoplay = previousAutoPlayState;
@@ -41,6 +41,7 @@ shaka.media.PlayRateController = class {
41
41
 
42
42
  /** @override */
43
43
  release() {
44
+ this.set(this.getDefaultRate());
44
45
  if (this.timer_) {
45
46
  this.timer_.stop();
46
47
  this.timer_ = null;
@@ -97,6 +97,10 @@ shaka.media.SegmentPrefetch = class {
97
97
  this.segmentPrefetchMap_.set(reference, segmentPrefetchOperation);
98
98
  }
99
99
  this.prefetchPosTime_ = reference.startTime;
100
+ if (this.stream_.fastSwitching && reference.isPartial() &&
101
+ reference.isLastPartial()) {
102
+ break;
103
+ }
100
104
  reference = iterator.next().value;
101
105
  }
102
106
  }
@@ -121,7 +121,7 @@ shaka.media.StreamingEngine = class {
121
121
  this.destroyer_ = new shaka.util.Destroyer(() => this.doDestroy_());
122
122
 
123
123
  /** @private {number} */
124
- this.lastMediaSourceReset_ = 0;
124
+ this.lastMediaSourceReset_ = Date.now() / 1000;
125
125
  }
126
126
 
127
127
  /** @override */
@@ -1488,8 +1488,7 @@ shaka.media.StreamingEngine = class {
1488
1488
  JSON.stringify(buffered));
1489
1489
 
1490
1490
  if (!mediaState.waitingToClearBuffer) {
1491
- this.playerInterface_.onSegmentAppended(
1492
- reference.startTime, reference.endTime, mediaState.type);
1491
+ this.playerInterface_.onSegmentAppended(reference, mediaState.stream);
1493
1492
  }
1494
1493
 
1495
1494
  // Update right away.
@@ -2526,8 +2525,8 @@ shaka.media.StreamingEngine = class {
2526
2525
  * onError: function(!shaka.util.Error),
2527
2526
  * onEvent: function(!Event),
2528
2527
  * onManifestUpdate: function(),
2529
- * onSegmentAppended: function(number, number,
2530
- * !shaka.util.ManifestParserUtils.ContentType),
2528
+ * onSegmentAppended: function(!shaka.media.SegmentReference,
2529
+ * !shaka.extern.Stream),
2531
2530
  * onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference),
2532
2531
  * beforeAppendSegment: function(
2533
2532
  * shaka.util.ManifestParserUtils.ContentType,!BufferSource):Promise,
@@ -2552,10 +2551,9 @@ shaka.media.StreamingEngine = class {
2552
2551
  * Called when an event occurs that should be sent to the app.
2553
2552
  * @property {function()} onManifestUpdate
2554
2553
  * Called when an embedded 'emsg' box should trigger a manifest update.
2555
- * @property {function(number, number,
2556
- * !shaka.util.ManifestParserUtils.ContentType)} onSegmentAppended
2554
+ * @property {function(!shaka.media.SegmentReference,
2555
+ * !shaka.extern.Stream)} onSegmentAppended
2557
2556
  * Called after a segment is successfully appended to a MediaSource.
2558
- * The parameters are the start and end time.
2559
2557
  * @property
2560
2558
  * {function(!number, !shaka.media.InitSegmentReference)} onInitSegmentAppended
2561
2559
  * Called when an init segment is appended to a MediaSource.
@@ -700,7 +700,7 @@ shaka.net.NetworkingEngine = class extends shaka.util.FakeEventTarget {
700
700
  const segment = context.segment;
701
701
  const stream = context.stream;
702
702
  if (segment && stream && stream.fastSwitching) {
703
- if (segment.isPartial() && !segment.isLastPartial()) {
703
+ if (segment.isPartial()) {
704
704
  return false;
705
705
  }
706
706
  }
package/lib/player.js CHANGED
@@ -550,6 +550,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
550
550
  */
551
551
  this.loadEventManager_ = new shaka.util.EventManager();
552
552
 
553
+ /**
554
+ * For listeners scoped to the lifetime of the loaded content.
555
+ * @private {shaka.util.EventManager}
556
+ */
557
+ this.trickPlayEventManager_ = new shaka.util.EventManager();
558
+
553
559
  /** @private {shaka.net.NetworkingEngine} */
554
560
  this.networkingEngine_ = null;
555
561
 
@@ -848,6 +854,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
848
854
  this.loadEventManager_.release();
849
855
  this.loadEventManager_ = null;
850
856
  }
857
+ if (this.trickPlayEventManager_) {
858
+ this.trickPlayEventManager_.release();
859
+ this.trickPlayEventManager_ = null;
860
+ }
851
861
 
852
862
  this.abrManagerFactory_ = null;
853
863
  this.config_ = null;
@@ -1166,6 +1176,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1166
1176
  // element.
1167
1177
  if (this.video_) {
1168
1178
  this.loadEventManager_.removeAll();
1179
+ this.trickPlayEventManager_.removeAll();
1169
1180
  }
1170
1181
 
1171
1182
  // Stop the variant checker timer
@@ -1847,6 +1858,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1847
1858
  'Please use an AbrManager with setCmsdManager function.');
1848
1859
  this.abrManager_.setCmsdManager = () => {};
1849
1860
  }
1861
+ if (typeof this.abrManager_.trySuggestStreams != 'function') {
1862
+ shaka.Deprecate.deprecateFeature(5,
1863
+ 'AbrManager',
1864
+ 'Please use an AbrManager with trySuggestStreams function.');
1865
+ this.abrManager_.trySuggestStreams = () => {};
1866
+ }
1850
1867
  this.abrManager_.configure(this.config_.abr);
1851
1868
  }
1852
1869
 
@@ -2904,8 +2921,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2904
2921
  onError: (error) => this.onError_(error),
2905
2922
  onEvent: (event) => this.dispatchEvent(event),
2906
2923
  onManifestUpdate: () => this.onManifestUpdate_(),
2907
- onSegmentAppended: (start, end, contentType) => {
2908
- this.onSegmentAppended_(start, end, contentType);
2924
+ onSegmentAppended: (reference, stream) => {
2925
+ this.onSegmentAppended_(
2926
+ reference.startTime, reference.endTime, stream.type);
2927
+ if (this.abrManager_ && stream.fastSwitching &&
2928
+ reference.isPartial() && reference.isLastPartial()) {
2929
+ this.abrManager_.trySuggestStreams();
2930
+ }
2909
2931
  },
2910
2932
  onInitSegmentAppended: (position, initSegment) => {
2911
2933
  const mediaQuality = initSegment.getMediaQuality();
@@ -3569,6 +3591,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3569
3591
  shaka.log.alwaysWarn('A trick play rate of 0 is unsupported!');
3570
3592
  return;
3571
3593
  }
3594
+ this.trickPlayEventManager_.removeAll();
3572
3595
 
3573
3596
  if (this.video_.paused) {
3574
3597
  // Our fast forward is implemented with playbackRate and needs the video
@@ -3582,6 +3605,22 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3582
3605
  this.abrManager_.playbackRateChanged(rate);
3583
3606
  this.streamingEngine_.setTrickPlay(Math.abs(rate) > 1);
3584
3607
  }
3608
+ if (this.isLive()) {
3609
+ this.trickPlayEventManager_.listen(this.video_, 'timeupdate', () => {
3610
+ const currentTime = this.video_.currentTime;
3611
+ const seekRange = this.seekRange();
3612
+ const safeSeekOffset = this.config_.streaming.safeSeekOffset;
3613
+ if (rate > 0) {
3614
+ if (currentTime >= seekRange.end) {
3615
+ this.cancelTrickPlay();
3616
+ }
3617
+ } else {
3618
+ if (currentTime <= (seekRange.start + safeSeekOffset)) {
3619
+ this.cancelTrickPlay();
3620
+ }
3621
+ }
3622
+ });
3623
+ }
3585
3624
  }
3586
3625
 
3587
3626
  /**
@@ -3601,6 +3640,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3601
3640
  this.abrManager_.playbackRateChanged(defaultPlaybackRate);
3602
3641
  this.streamingEngine_.setTrickPlay(false);
3603
3642
  }
3643
+ this.trickPlayEventManager_.removeAll();
3604
3644
  }
3605
3645
 
3606
3646
  /**
@@ -4576,13 +4616,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4576
4616
  // variant yet because we set the load mode before we select the first
4577
4617
  // variant to stream.
4578
4618
  const variant = this.streamingEngine_.getCurrentVariant();
4619
+ const textStream = this.streamingEngine_.getCurrentTextStream();
4579
4620
 
4580
4621
  if (variant) {
4581
4622
  const rate = this.playRateController_ ?
4582
4623
  this.playRateController_.getRealRate() : 1;
4583
4624
  const variantBandwidth = rate * variant.bandwidth;
4584
- // TODO: Should include text bandwidth if it enabled.
4585
- const currentStreamBandwidth = variantBandwidth;
4625
+ let currentStreamBandwidth = variantBandwidth;
4626
+ if (textStream && textStream.bandwidth) {
4627
+ currentStreamBandwidth += (rate * textStream.bandwidth);
4628
+ }
4586
4629
  this.stats_.setCurrentStreamBandwidth(currentStreamBandwidth);
4587
4630
  }
4588
4631
 
@@ -6871,7 +6914,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6871
6914
  * @export
6872
6915
  */
6873
6916
  // eslint-disable-next-line no-useless-concat
6874
- shaka.Player.version = 'v4.7.4' + '-uncompiled'; // x-release-please-version
6917
+ shaka.Player.version = 'v4.7.6' + '-uncompiled'; // x-release-please-version
6875
6918
 
6876
6919
  // Initialize the deprecation system using the version string we just set
6877
6920
  // on the player.
@@ -491,6 +491,11 @@ shaka.text.TtmlTextParser = class {
491
491
  region.viewportAnchorX = region.viewportAnchorX * 100 /
492
492
  globalWidth;
493
493
  }
494
+ } else if (!extent) {
495
+ region.width = 100 - region.viewportAnchorX;
496
+ region.widthUnits = shaka.text.CueRegion.units.PERCENTAGE;
497
+ region.height = 100 - region.viewportAnchorY;
498
+ region.heightUnits = shaka.text.CueRegion.units.PERCENTAGE;
494
499
  }
495
500
 
496
501
  region.viewportAnchorUnits = percentage || globalWidth != null ?
@@ -192,25 +192,24 @@ shaka.util.LanguageUtils = class {
192
192
  const LanguageUtils = shaka.util.LanguageUtils;
193
193
 
194
194
  const privateusePrefix = 'x-';
195
- const components = locale.split(`-${privateusePrefix}`);
196
- const languageAndRegion = components[0].split('-');
195
+ const [languageRegion = '', privateuseSuffix = ''] =
196
+ locale.split(`-${privateusePrefix}`);
197
+ const [languageCode = '', regionCode = ''] = languageRegion.split('-');
197
198
 
198
199
  // We are only going to use the language, the region and the "privateuse" part (as per https://datatracker.ietf.org/doc/html/rfc5646).
199
200
  // Anything else is thrown away.
200
- let language = languageAndRegion[0] || '';
201
- let region = languageAndRegion[1] || '';
202
- const privateuse = components[1] ?
203
- `${privateusePrefix}${components[1]}` : '';
201
+ const privateuse = privateuseSuffix ?
202
+ `${privateusePrefix}${privateuseSuffix}` : '';
204
203
 
205
204
  // Convert the language to lower case. It is standard for the language code
206
205
  // to be in lower case, but it will also make the map look-up easier.
207
- language = language.toLowerCase();
206
+ let language = languageCode.toLowerCase();
208
207
  language = LanguageUtils.isoMap_.get(language) || language;
209
208
 
210
209
  // Convert the region to upper case. It is standard for the region to be in
211
210
  // upper case. If there is no upper code, then it will be an empty string
212
211
  // and this will be a no-op.
213
- region = region.toUpperCase();
212
+ const region = regionCode.toUpperCase();
214
213
 
215
214
  return `${region ? `${language}-${region}` : language}${
216
215
  privateuse ? `-${privateuse}` : ''}`;
@@ -724,7 +724,9 @@ shaka.util.PeriodCombiner = class {
724
724
  // Add the codec of this stream to the output stream's codecs.
725
725
  const codecs = new Set(outputStream.codecs.split(','));
726
726
  for (const codec of match.codecs.split(',')) {
727
- codecs.add(codec);
727
+ if (codec) {
728
+ codecs.add(codec);
729
+ }
728
730
  }
729
731
  const PeriodCombiner = shaka.util.PeriodCombiner;
730
732
  outputStream.codecs = PeriodCombiner.filterDuplicateCodecs_(
@@ -1029,6 +1031,10 @@ shaka.util.PeriodCombiner = class {
1029
1031
  continue;
1030
1032
  }
1031
1033
 
1034
+ if (outputStream.fastSwitching != stream.fastSwitching) {
1035
+ continue;
1036
+ }
1037
+
1032
1038
  if (!best || isBetterMatch(outputStream, best, stream)) {
1033
1039
  best = stream;
1034
1040
  }
@@ -220,6 +220,15 @@ shaka.util.Platform = class {
220
220
  !shaka.util.Platform.isLegacyEdge();
221
221
  }
222
222
 
223
+ /**
224
+ * Check if the current platform is Firefox.
225
+ *
226
+ * @return {boolean}
227
+ */
228
+ static isFirefox() {
229
+ return shaka.util.Platform.userAgentContains_('Firefox');
230
+ }
231
+
223
232
  /**
224
233
  * Check if the current platform is from Apple.
225
234
  *
@@ -422,6 +431,15 @@ shaka.util.Platform = class {
422
431
  return navigator.platform.toLowerCase().includes('windows');
423
432
  }
424
433
 
434
+ /**
435
+ * Return true if the platform is a Android, regardless of the browser.
436
+ *
437
+ * @return {boolean}
438
+ */
439
+ static isAndroid() {
440
+ return shaka.util.Platform.userAgentContains_('Android');
441
+ }
442
+
425
443
  /**
426
444
  * Check if the user agent contains a key. This is the best way we know of
427
445
  * right now to detect platforms. If there is a better way, please send a
@@ -371,6 +371,9 @@ shaka.util.StreamUtils = class {
371
371
  const StreamUtils = shaka.util.StreamUtils;
372
372
  const isXboxOne = shaka.util.Platform.isXboxOne();
373
373
 
374
+ const isFirefoxAndroid = shaka.util.Platform.isFirefox() &&
375
+ shaka.util.Platform.isAndroid();
376
+
374
377
  manifest.variants = manifest.variants.filter((variant) => {
375
378
  // See: https://github.com/shaka-project/shaka-player/issues/3860
376
379
  const video = variant.video;
@@ -426,6 +429,17 @@ shaka.util.StreamUtils = class {
426
429
 
427
430
  const audio = variant.audio;
428
431
 
432
+ // See: https://github.com/shaka-project/shaka-player/issues/6111
433
+ // It seems that Firefox Android reports that it supports
434
+ // Opus + Widevine, but it is not actually supported.
435
+ // It makes sense to drop early.
436
+ if (isFirefoxAndroid && audio && audio.encrypted &&
437
+ audio.codecs.toLowerCase().includes('opus')) {
438
+ shaka.log.debug('Dropping variant - not compatible with platform',
439
+ StreamUtils.getVariantSummaryString_(variant));
440
+ return false;
441
+ }
442
+
429
443
  if (audio) {
430
444
  const codecs = StreamUtils.getCorrectAudioCodecs_(audio.codecs);
431
445
  const fullType = MimeUtils.getFullOrConvertedType(
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.7.4",
4
+ "version": "4.7.6",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
package/ui/controls.js CHANGED
@@ -650,6 +650,9 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
650
650
  * @export
651
651
  */
652
652
  isPiPAllowed() {
653
+ if (this.castProxy_.isCasting()) {
654
+ return false;
655
+ }
653
656
  if ('documentPictureInPicture' in window &&
654
657
  this.config_.preferDocumentPictureInPicture) {
655
658
  const video = /** @type {HTMLVideoElement} */(this.localVideo_);
package/ui/pip_button.js CHANGED
@@ -98,8 +98,8 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
98
98
  this.onLeavePictureInPicture_();
99
99
  });
100
100
 
101
- this.eventManager.listen(this.controls, 'caststatuschanged', (e) => {
102
- this.onCastStatusChange_(e);
101
+ this.eventManager.listen(this.controls, 'caststatuschanged', () => {
102
+ this.onTracksChanged_();
103
103
  });
104
104
 
105
105
  this.eventManager.listen(this.player, 'trackschanged', () => {
@@ -164,25 +164,6 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
164
164
  this.localization.resolve(currentPipState);
165
165
  }
166
166
 
167
- /**
168
- * @param {Event} e
169
- * @private
170
- */
171
- onCastStatusChange_(e) {
172
- const isCasting = e['newStatus'];
173
-
174
- if (isCasting) {
175
- // Picture-in-picture is not applicable if we're casting
176
- if (this.controls.isPiPAllowed()) {
177
- shaka.ui.Utils.setDisplay(this.pipButton_, false);
178
- }
179
- } else {
180
- if (this.controls.isPiPAllowed()) {
181
- shaka.ui.Utils.setDisplay(this.pipButton_, true);
182
- }
183
- }
184
- }
185
-
186
167
 
187
168
  /**
188
169
  * Display the picture-in-picture button only when the content contains video.
@@ -194,6 +175,9 @@ shaka.ui.PipButton = class extends shaka.ui.Element {
194
175
  async onTracksChanged_() {
195
176
  if (!this.controls.isPiPAllowed()) {
196
177
  shaka.ui.Utils.setDisplay(this.pipButton_, false);
178
+ if (this.controls.isPiPEnabled()) {
179
+ await this.controls.togglePiP();
180
+ }
197
181
  } else if (this.player && this.player.isAudioOnly()) {
198
182
  shaka.ui.Utils.setDisplay(this.pipButton_, false);
199
183
  if (this.controls.isPiPEnabled()) {
@@ -44,6 +44,10 @@ shaka.ui.PlaybackRateSelection = class extends shaka.ui.SettingsMenu {
44
44
  this.updateLocalizedStrings_();
45
45
  });
46
46
 
47
+ this.eventManager.listen(this.player, 'loaded', () => {
48
+ this.updatePlaybackRateSelection_(this.player.getPlaybackRate());
49
+ });
50
+
47
51
  this.eventManager.listen(this.player, 'ratechange', () => {
48
52
  this.updatePlaybackRateSelection_(this.player.getPlaybackRate());
49
53
  });