shaka-player 4.8.6 → 4.8.8

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.
@@ -1230,7 +1230,8 @@ shaka.extern.ManifestConfiguration;
1230
1230
  * vodDynamicPlaybackRate: boolean,
1231
1231
  * vodDynamicPlaybackRateLowBufferRate: number,
1232
1232
  * vodDynamicPlaybackRateBufferRatio: number,
1233
- * infiniteLiveStreamDuration: boolean
1233
+ * infiniteLiveStreamDuration: boolean,
1234
+ * loadTimeout: number
1234
1235
  * }}
1235
1236
  *
1236
1237
  * @description
@@ -1390,7 +1391,7 @@ shaka.extern.ManifestConfiguration;
1390
1391
  * @property {number} liveSyncMinPlaybackRate
1391
1392
  * Minimum playback rate used for latency chasing. It is recommended to use a
1392
1393
  * value between 0 and 1. Effective only if liveSync is true. Defaults to
1393
- * <code>1</code>.
1394
+ * <code>0.95</code>.
1394
1395
  * @property {boolean} liveSyncPanicMode
1395
1396
  * If <code>true</code>, panic mode for live sync is enabled. When enabled,
1396
1397
  * will set the playback rate to the <code>liveSyncMinPlaybackRate</code>
@@ -1421,6 +1422,10 @@ shaka.extern.ManifestConfiguration;
1421
1422
  * If <code>true</code>, the media source live duration
1422
1423
  * set as a<code>Infinity</code>
1423
1424
  * Defaults to <code> false </code>.
1425
+ * @property {number} loadTimeout
1426
+ * The maximum timeout to reject the load when using src= in case the content
1427
+ * does not work correctly. Measured in seconds.
1428
+ * Defaults to <code> 30 </code>.
1424
1429
  * @exportDoc
1425
1430
  */
1426
1431
  shaka.extern.StreamingConfiguration;
@@ -506,7 +506,12 @@ shaka.dash.SegmentTemplate = class {
506
506
  // references were generated. Otherwise, the update would start creating
507
507
  // negative positions for segments in periods which begin in the future.
508
508
  let nextPosition = Math.max(minPosition, maxPosition + 1);
509
- segmentIndex.updateEvery(segmentDuration, () => {
509
+ let updateTime = segmentDuration;
510
+ // For low latency we need to evict very frequently.
511
+ if (context.representation.availabilityTimeOffset) {
512
+ updateTime = 0.1;
513
+ }
514
+ segmentIndex.updateEvery(updateTime, () => {
510
515
  // Evict any references outside the window.
511
516
  const availabilityStartTime =
512
517
  presentationTimeline.getSegmentAvailabilityStart();
@@ -741,6 +741,18 @@ shaka.hls.HlsParser = class {
741
741
  /** @type {!Array.<!shaka.extern.Stream>} */
742
742
  let imageStreams = [];
743
743
 
744
+ // This assert is our own sanity check.
745
+ goog.asserts.assert(this.presentationTimeline_ == null,
746
+ 'Presentation timeline created early!');
747
+ // We don't know if the presentation is VOD or live until we parse at least
748
+ // one media playlist, so make a VOD-style presentation timeline for now
749
+ // and change the type later if we discover this is live.
750
+ // Since the player will load the first variant chosen early in the process,
751
+ // there isn't a window during playback where the live-ness is unknown.
752
+ this.presentationTimeline_ = new shaka.media.PresentationTimeline(
753
+ /* presentationStartTime= */ null, /* delay= */ 0);
754
+ this.presentationTimeline_.setStatic(true);
755
+
744
756
  const getUris = () => {
745
757
  return [uri];
746
758
  };
@@ -843,18 +855,6 @@ shaka.hls.HlsParser = class {
843
855
  shaka.util.Error.Code.OPERATION_ABORTED);
844
856
  }
845
857
 
846
- // This assert is our own sanity check.
847
- goog.asserts.assert(this.presentationTimeline_ == null,
848
- 'Presentation timeline created early!');
849
- // We don't know if the presentation is VOD or live until we parse at least
850
- // one media playlist, so make a VOD-style presentation timeline for now
851
- // and change the type later if we discover this is live.
852
- // Since the player will load the first variant chosen early in the process,
853
- // there isn't a window during playback where the live-ness is unknown.
854
- this.presentationTimeline_ = new shaka.media.PresentationTimeline(
855
- /* presentationStartTime= */ null, /* delay= */ 0);
856
- this.presentationTimeline_.setStatic(true);
857
-
858
858
  // Single-variant streams aren't lazy-loaded, so for them we already have
859
859
  // enough info here to determine the presentation type and duration.
860
860
  if (playlist.type == shaka.hls.PlaylistType.MEDIA) {
@@ -13,6 +13,7 @@ goog.require('shaka.media.TimeRangesUtils');
13
13
  goog.require('shaka.util.EventManager');
14
14
  goog.require('shaka.util.FakeEvent');
15
15
  goog.require('shaka.util.IReleasable');
16
+ goog.require('shaka.util.Platform');
16
17
  goog.require('shaka.util.Timer');
17
18
 
18
19
 
@@ -217,7 +218,14 @@ shaka.media.GapJumpingController = class {
217
218
 
218
219
  // StreamingEngine can buffer past the seek end, but still don't allow
219
220
  // seeking past it.
220
- const jumpTo = buffered.start(gapIndex);
221
+ let jumpTo = buffered.start(gapIndex);
222
+ // Workaround for Xbox with Legacy Edge. On this platform video element
223
+ // often rounds value we want to set as currentTime and we are not able
224
+ // to jump over the gap.
225
+ if (shaka.util.Platform.isLegacyEdge() ||
226
+ shaka.util.Platform.isXboxOne()) {
227
+ jumpTo = Math.ceil((jumpTo + 0.01) * 100) / 100;
228
+ }
221
229
  const seekEnd = this.timeline_.getSeekRangeEnd();
222
230
  if (jumpTo >= seekEnd) {
223
231
  return;
package/lib/player.js CHANGED
@@ -2369,7 +2369,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2369
2369
  this.config_.streaming.vodDynamicPlaybackRate) {
2370
2370
  const onTimeUpdate = () => this.onTimeUpdate_();
2371
2371
  this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2372
- } else if (!isLive) {
2372
+ }
2373
+ if (!isLive) {
2373
2374
  const onVideoProgress = () => this.onVideoProgress_();
2374
2375
  this.loadEventManager_.listen(
2375
2376
  mediaElement, 'timeupdate', onVideoProgress);
@@ -2454,42 +2455,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2454
2455
  disabledUntilTime: 0,
2455
2456
  primary: false,
2456
2457
  audio: null,
2457
- video: {
2458
- id: 0,
2459
- originalId: null,
2460
- groupId: null,
2461
- createSegmentIndex: () => Promise.resolve(),
2462
- segmentIndex: null,
2463
- mimeType: mimeType ? shaka.util.MimeUtils.getBasicType(mimeType) : '',
2464
- codecs: mimeType ? shaka.util.MimeUtils.getCodecs(mimeType) : '',
2465
- encrypted: true,
2466
- drmInfos: [], // Filled in by DrmEngine config.
2467
- keyIds: new Set(),
2468
- language: 'und',
2469
- originalLanguage: null,
2470
- label: null,
2471
- type: ContentType.VIDEO,
2472
- primary: false,
2473
- trickModeVideo: null,
2474
- emsgSchemeIdUris: null,
2475
- roles: [],
2476
- forced: false,
2477
- channelsCount: null,
2478
- audioSamplingRate: null,
2479
- spatialAudio: false,
2480
- closedCaptions: null,
2481
- accessibilityPurpose: null,
2482
- external: false,
2483
- fastSwitching: false,
2484
- fullMimeTypes: new Set(),
2485
- },
2458
+ video: null,
2486
2459
  bandwidth: 100,
2487
2460
  allowedByApplication: true,
2488
2461
  allowedByKeySystem: true,
2489
2462
  decodingInfos: [],
2490
2463
  };
2491
- variant.video.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2492
- variant.video.mimeType, variant.video.codecs));
2464
+ const stream = {
2465
+ id: 0,
2466
+ originalId: null,
2467
+ groupId: null,
2468
+ createSegmentIndex: () => Promise.resolve(),
2469
+ segmentIndex: null,
2470
+ mimeType: mimeType ? shaka.util.MimeUtils.getBasicType(mimeType) : '',
2471
+ codecs: mimeType ? shaka.util.MimeUtils.getCodecs(mimeType) : '',
2472
+ encrypted: true,
2473
+ drmInfos: [], // Filled in by DrmEngine config.
2474
+ keyIds: new Set(),
2475
+ language: 'und',
2476
+ originalLanguage: null,
2477
+ label: null,
2478
+ type: ContentType.VIDEO,
2479
+ primary: false,
2480
+ trickModeVideo: null,
2481
+ emsgSchemeIdUris: null,
2482
+ roles: [],
2483
+ forced: false,
2484
+ channelsCount: null,
2485
+ audioSamplingRate: null,
2486
+ spatialAudio: false,
2487
+ closedCaptions: null,
2488
+ accessibilityPurpose: null,
2489
+ external: false,
2490
+ fastSwitching: false,
2491
+ fullMimeTypes: new Set(),
2492
+ };
2493
+ stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2494
+ stream.mimeType, stream.codecs));
2495
+ if (mimeType.startsWith('audio/')) {
2496
+ stream.type = ContentType.AUDIO;
2497
+ variant.audio = stream;
2498
+ } else {
2499
+ variant.video = stream;
2500
+ }
2493
2501
 
2494
2502
  this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
2495
2503
  await this.drmEngine_.initForPlayback(
@@ -2708,7 +2716,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2708
2716
  fullyLoaded.reject(this.videoErrorToShakaError_());
2709
2717
  });
2710
2718
 
2711
- await fullyLoaded;
2719
+ const timeout = new Promise((resolve, reject) => {
2720
+ const timer = new shaka.util.Timer(reject);
2721
+ timer.tickAfter(this.config_.streaming.loadTimeout);
2722
+ });
2723
+
2724
+ await Promise.race([
2725
+ fullyLoaded,
2726
+ timeout,
2727
+ ]);
2712
2728
 
2713
2729
  const isLive = this.isLive();
2714
2730
 
@@ -2717,7 +2733,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2717
2733
  this.config_.streaming.vodDynamicPlaybackRate) {
2718
2734
  const onTimeUpdate = () => this.onTimeUpdate_();
2719
2735
  this.loadEventManager_.listen(mediaElement, 'timeupdate', onTimeUpdate);
2720
- } else if (!isLive) {
2736
+ }
2737
+ if (!isLive) {
2721
2738
  const onVideoProgress = () => this.onVideoProgress_();
2722
2739
  this.loadEventManager_.listen(
2723
2740
  mediaElement, 'timeupdate', onVideoProgress);
@@ -6512,8 +6529,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6512
6529
  shaka.log.debug('switch_');
6513
6530
  goog.asserts.assert(this.config_.abr.enabled,
6514
6531
  'AbrManager should not call switch while disabled!');
6515
- goog.asserts.assert(this.manifest_, 'We need a manifest to switch ' +
6516
- 'variants.');
6532
+
6533
+ if (!this.manifest_) {
6534
+ // It could come from a preload manager operation.
6535
+ return;
6536
+ }
6517
6537
 
6518
6538
  if (!this.streamingEngine_) {
6519
6539
  // There's no way to change it.
@@ -7219,7 +7239,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7219
7239
  * @export
7220
7240
  */
7221
7241
  // eslint-disable-next-line no-useless-concat
7222
- shaka.Player.version = 'v4.8.6' + '-uncompiled'; // x-release-please-version
7242
+ shaka.Player.version = 'v4.8.8' + '-uncompiled'; // x-release-please-version
7223
7243
 
7224
7244
  // Initialize the deprecation system using the version string we just set
7225
7245
  // on the player.
@@ -25,10 +25,8 @@ shaka.polyfill.MediaCapabilities = class {
25
25
  * @export
26
26
  */
27
27
  static install() {
28
- // Since MediaCapabilities implementation is buggy on the Chromecast
29
- // platform (see https://github.com/shaka-project/shaka-player/issues/4569),
30
- // we should always install polyfills on all Chromecast models.
31
- // TODO: re-evaluate MediaCapabilities in the future versions of Chromecast.
28
+ // We can enable MediaCapabilities in Android and Fuchsia devices, but not
29
+ // in Linux devices because the implementation is buggy.
32
30
  // Since MediaCapabilities implementation is buggy in Apple browsers, we
33
31
  // should always install polyfill for Apple browsers.
34
32
  // See: https://github.com/shaka-project/shaka-player/issues/3530
@@ -50,12 +48,16 @@ shaka.polyfill.MediaCapabilities = class {
50
48
  // Since MediaCapabilities implementation is buggy in Hisense browsers, we
51
49
  // should always install polyfill for Hisense browsers.
52
50
  let canUseNativeMCap = true;
51
+ if (shaka.util.Platform.isChromecast() &&
52
+ !shaka.util.Platform.isAndroidCastDevice() &&
53
+ !shaka.util.Platform.isFuchsiaCastDevice()) {
54
+ canUseNativeMCap = false;
55
+ }
53
56
  if (shaka.util.Platform.isApple() ||
54
57
  shaka.util.Platform.isPS5() ||
55
58
  shaka.util.Platform.isPS4() ||
56
59
  shaka.util.Platform.isWebOS() ||
57
60
  shaka.util.Platform.isTizen() ||
58
- shaka.util.Platform.isChromecast() ||
59
61
  shaka.util.Platform.isEOS() ||
60
62
  shaka.util.Platform.isHisense()) {
61
63
  canUseNativeMCap = false;
@@ -0,0 +1,62 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.polyfill.PatchedMediaKeysCert');
8
+
9
+ goog.require('shaka.log');
10
+ goog.require('shaka.polyfill');
11
+ goog.require('shaka.util.Platform');
12
+
13
+
14
+ /**
15
+ * @summary A polyfill to fix setServerCertificate implementation on
16
+ * older platforms which claim to support modern EME.
17
+ * @export
18
+ */
19
+ shaka.polyfill.PatchedMediaKeysCert = class {
20
+ /**
21
+ * Installs the polyfill if needed.
22
+ * @export
23
+ */
24
+ static install() {
25
+ if (!window.MediaKeys) {
26
+ // No MediaKeys available
27
+ return;
28
+ }
29
+ // eslint-disable-next-line no-restricted-syntax
30
+ if (MediaKeys.prototype.setServerCertificate &&
31
+ !shaka.polyfill.PatchedMediaKeysCert.hasInvalidImplementation_()) {
32
+ // setServerCertificate is there and userAgent seems to be valid.
33
+ return;
34
+ }
35
+
36
+ shaka.log.info('Patching MediaKeys.setServerCertificate');
37
+ // eslint-disable-next-line no-restricted-syntax
38
+ MediaKeys.prototype.setServerCertificate =
39
+ shaka.polyfill.PatchedMediaKeysCert.setServerCertificate_;
40
+ }
41
+
42
+ /**
43
+ * @param {!BufferSource} certificate
44
+ * @return {!Promise<boolean>}
45
+ * @private
46
+ */
47
+ static setServerCertificate_(certificate) {
48
+ shaka.log.debug('PatchedMediaKeysCert.setServerCertificate');
49
+ return Promise.resolve(false);
50
+ }
51
+
52
+ /**
53
+ * @return {boolean}
54
+ * @private
55
+ */
56
+ static hasInvalidImplementation_() {
57
+ return shaka.util.Platform.isTizen3() || shaka.util.Platform.isTizen4() ||
58
+ shaka.util.Platform.isTizen5_0() || shaka.util.Platform.isWebOS3();
59
+ }
60
+ };
61
+
62
+ shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysCert.install);
@@ -108,6 +108,33 @@ shaka.util.Platform = class {
108
108
  return shaka.util.Platform.userAgentContains_('Tizen');
109
109
  }
110
110
 
111
+ /**
112
+ * Check if the current platform is a Tizen 6 TV.
113
+ *
114
+ * @return {boolean}
115
+ */
116
+ static isTizen6() {
117
+ return shaka.util.Platform.userAgentContains_('Tizen 6');
118
+ }
119
+
120
+ /**
121
+ * Check if the current platform is a Tizen 5.0 TV.
122
+ *
123
+ * @return {boolean}
124
+ */
125
+ static isTizen5_0() {
126
+ return shaka.util.Platform.userAgentContains_('Tizen 5.0');
127
+ }
128
+
129
+ /**
130
+ * Check if the current platform is a Tizen 5 TV.
131
+ *
132
+ * @return {boolean}
133
+ */
134
+ static isTizen5() {
135
+ return shaka.util.Platform.userAgentContains_('Tizen 5');
136
+ }
137
+
111
138
  /**
112
139
  * Check if the current platform is a Tizen 4 TV.
113
140
  *
@@ -588,8 +615,8 @@ shaka.util.Platform = class {
588
615
  */
589
616
  static supportsSmoothCodecSwitching() {
590
617
  const Platform = shaka.util.Platform;
591
- if (Platform.isTizen2() || Platform.isTizen3() ||
592
- Platform.isTizen4() || Platform.isWebOS3() ||
618
+ if (Platform.isTizen2() || Platform.isTizen3() || Platform.isTizen4() ||
619
+ Platform.isTizen5() || Platform.isTizen6() || Platform.isWebOS3() ||
593
620
  Platform.isWebOS4() || Platform.isWebOS5()) {
594
621
  return false;
595
622
  }
@@ -239,7 +239,7 @@ shaka.util.PlayerConfiguration = class {
239
239
  liveSyncMaxLatency: 1,
240
240
  liveSyncPlaybackRate: 1.1,
241
241
  liveSyncMinLatency: 0,
242
- liveSyncMinPlaybackRate: 1,
242
+ liveSyncMinPlaybackRate: 0.95,
243
243
  liveSyncPanicMode: false,
244
244
  liveSyncPanicThreshold: 60,
245
245
  allowMediaSourceRecoveries: true,
@@ -248,6 +248,7 @@ shaka.util.PlayerConfiguration = class {
248
248
  vodDynamicPlaybackRateLowBufferRate: 0.95,
249
249
  vodDynamicPlaybackRateBufferRatio: 0.5,
250
250
  infiniteLiveStreamDuration: false,
251
+ loadTimeout: 30,
251
252
  };
252
253
 
253
254
  // WebOS, Tizen, Chromecast and Hisense have long hardware pipelines
@@ -548,7 +548,7 @@ shaka.util.StreamUtils = class {
548
548
  if (!a) {
549
549
  return b;
550
550
  } else {
551
- const res = shaka.util.ObjectUtils.cloneObject(a);
551
+ const res = shaka.util.ObjectUtils.shallowCloneObject(a);
552
552
  res.supported = a.supported && b.supported;
553
553
  res.powerEfficient = a.powerEfficient && b.powerEfficient;
554
554
  res.smooth = a.smooth && b.smooth;
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.8.6",
4
+ "version": "4.8.8",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -70,11 +70,12 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
70
70
  this.container.appendChild(this.bar);
71
71
  this.parent.appendChild(this.container);
72
72
 
73
- this.eventManager.listen(this.bar, 'mousedown', () => {
73
+ this.eventManager.listen(this.bar, 'mousedown', (e) => {
74
74
  if (this.controls.isOpaque()) {
75
75
  this.isChanging_ = true;
76
76
  this.onChangeStart();
77
77
  }
78
+ e.stopPropagation();
78
79
  });
79
80
 
80
81
  this.eventManager.listen(this.bar, 'touchstart', (e) => {
@@ -83,6 +84,7 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
83
84
  this.setBarValueForTouch_(e);
84
85
  this.onChangeStart();
85
86
  }
87
+ e.stopPropagation();
86
88
  });
87
89
 
88
90
  this.eventManager.listen(this.bar, 'input', () => {
@@ -94,6 +96,7 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
94
96
  this.setBarValueForTouch_(e);
95
97
  this.onChange();
96
98
  }
99
+ e.stopPropagation();
97
100
  });
98
101
 
99
102
  this.eventManager.listen(this.bar, 'touchend', (e) => {
@@ -102,6 +105,7 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
102
105
  this.setBarValueForTouch_(e);
103
106
  this.onChangeEnd();
104
107
  }
108
+ e.stopPropagation();
105
109
  });
106
110
 
107
111
  this.eventManager.listen(this.bar, 'touchcancel', (e) => {
@@ -110,13 +114,15 @@ shaka.ui.RangeElement = class extends shaka.ui.Element {
110
114
  this.setBarValueForTouch_(e);
111
115
  this.onChangeEnd();
112
116
  }
117
+ e.stopPropagation();
113
118
  });
114
119
 
115
- this.eventManager.listen(this.bar, 'mouseup', () => {
120
+ this.eventManager.listen(this.bar, 'mouseup', (e) => {
116
121
  if (this.isChanging_) {
117
122
  this.isChanging_ = false;
118
123
  this.onChangeEnd();
119
124
  }
125
+ e.stopPropagation();
120
126
  });
121
127
 
122
128
  this.eventManager.listen(this.bar, 'blur', () => {
package/ui/vr_webgl.js CHANGED
@@ -245,7 +245,9 @@ shaka.ui.VRWebgl = class {
245
245
  }).tickNow().tickEvery(1 / frameRate);
246
246
  });
247
247
  this.eventManager_.listen(this.video_, 'pause', () => {
248
- this.activeTimer_.stop();
248
+ if (this.activeTimer_) {
249
+ this.activeTimer_.stop();
250
+ }
249
251
  this.activeTimer_ = null;
250
252
  this.renderGL_();
251
253
  });