shaka-player 4.8.5 → 4.8.7

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.
@@ -0,0 +1,14 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Google Cast namespace definitions, shared by other externs
9
+ * files.
10
+ * @externs
11
+ */
12
+
13
+ /** @const */
14
+ var cast = {};
@@ -15,10 +15,6 @@
15
15
  var __onGCastApiAvailable;
16
16
 
17
17
 
18
- /** @const */
19
- var cast = {};
20
-
21
-
22
18
  /** @const */
23
19
  cast.receiver = {};
24
20
 
@@ -0,0 +1,35 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Externs for the limited subset of the Cast Application
9
+ * Framework (Receiver SDK v3) that we use in our test infrastructure.
10
+ *
11
+ * @externs
12
+ */
13
+
14
+ /** @const */
15
+ cast.framework = {};
16
+
17
+ /**
18
+ * @typedef {{
19
+ * statusText: string,
20
+ * disableIdleTimeout: boolean,
21
+ * skipPlayersLoad: boolean
22
+ * }}
23
+ */
24
+ cast.framework.CastReceiverOptions;
25
+
26
+ cast.framework.CastReceiverContext = class {
27
+ /** @return {!cast.framework.CastReceiverContext} */
28
+ static getInstance() {}
29
+
30
+ /**
31
+ * @param {!cast.framework.CastReceiverOptions} options
32
+ * @return {!cast.framework.CastReceiverContext}
33
+ */
34
+ start(options) {}
35
+ };
File without changes
@@ -5,8 +5,8 @@
5
5
  */
6
6
 
7
7
  /**
8
- * @fileoverview Externs for HTMLMediaElement related to casting which were
9
- * missing in the Closure compiler.
8
+ * @fileoverview Externs for HTMLMediaElement related to remote playback which
9
+ * were missing in the Closure compiler.
10
10
  *
11
11
  * @externs
12
12
  */
@@ -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
@@ -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;
@@ -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) {
@@ -26,8 +26,10 @@ shaka.media.IClosedCaptionParser = class {
26
26
  * Initialize the caption parser. This should be called whenever new init
27
27
  * segment arrives.
28
28
  * @param {BufferSource} initSegment
29
+ * @param {boolean=} adaptation True if we just automatically switched active
30
+ * variant(s).
29
31
  */
30
- init(initSegment) {}
32
+ init(initSegment, adaptation = false) {}
31
33
 
32
34
  /**
33
35
  * Parses embedded CEA closed captions and interacts with the underlying
@@ -89,12 +91,16 @@ shaka.media.ClosedCaptionParser = class {
89
91
  /**
90
92
  * @override
91
93
  */
92
- init(initSegment) {
94
+ init(initSegment, adaptation = false) {
93
95
  shaka.log.debug('Passing new init segment to CEA parser');
94
- // Reset underlying decoder when new init segment arrives
95
- // to clear stored pts values.
96
- this.reset();
97
- this.ceaParser_.init(initSegment);
96
+ if (!adaptation) {
97
+ // Reset underlying decoder when new init segment arrives
98
+ // to clear stored pts values.
99
+ // This is necessary when a new Period comes in DASH or a discontinuity
100
+ // in HLS.
101
+ this.reset();
102
+ this.ceaParser_.init(initSegment);
103
+ }
98
104
  }
99
105
 
100
106
  /**
@@ -964,7 +964,7 @@ shaka.media.MediaSourceEngine = class {
964
964
  // If it is the init segment for closed captions, initialize the closed
965
965
  // caption parser.
966
966
  if (!reference) {
967
- this.captionParser_.init(data);
967
+ this.captionParser_.init(data, adaptation);
968
968
  } else {
969
969
  const closedCaptions = this.captionParser_.parseFrom(data);
970
970
  if (closedCaptions.length) {
@@ -167,8 +167,10 @@ shaka.net.HttpFetchPlugin = class {
167
167
  // is long enough, or if a whole segment is downloaded, call
168
168
  // progressUpdated().
169
169
  if (currentTime - lastTime > 100 || readObj.done) {
170
+ const numBytesRemaining =
171
+ readObj.done ? 0 : contentLength - loaded;
170
172
  progressUpdated(currentTime - lastTime, loaded - lastLoaded,
171
- contentLength - loaded);
173
+ numBytesRemaining);
172
174
  lastLoaded = loaded;
173
175
  lastTime = currentTime;
174
176
  }
@@ -67,6 +67,9 @@ shaka.net.HttpXHRPlugin = class {
67
67
  const xhrResponse = xhr.response;
68
68
 
69
69
  try {
70
+ const currentTime = Date.now();
71
+ progressUpdated(currentTime - lastTime, event.loaded - lastLoaded,
72
+ /* numBytesRemaining= */ 0);
70
73
  const response = shaka.net.HttpPluginUtils.makeResponse(headers,
71
74
  xhrResponse, xhr.status, uri, xhr.responseURL, requestType);
72
75
  resolve(response);
@@ -97,8 +100,10 @@ shaka.net.HttpXHRPlugin = class {
97
100
  // progressUpdated().
98
101
  if (currentTime - lastTime > 100 ||
99
102
  (event.lengthComputable && event.loaded == event.total)) {
103
+ const numBytesRemaining =
104
+ xhr.readyState == 4 ? 0 : event.total - event.loaded;
100
105
  progressUpdated(currentTime - lastTime, event.loaded - lastLoaded,
101
- event.total - event.loaded);
106
+ numBytesRemaining);
102
107
  lastLoaded = event.loaded;
103
108
  lastTime = currentTime;
104
109
  }
package/lib/player.js CHANGED
@@ -2454,42 +2454,49 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2454
2454
  disabledUntilTime: 0,
2455
2455
  primary: false,
2456
2456
  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
- },
2457
+ video: null,
2486
2458
  bandwidth: 100,
2487
2459
  allowedByApplication: true,
2488
2460
  allowedByKeySystem: true,
2489
2461
  decodingInfos: [],
2490
2462
  };
2491
- variant.video.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2492
- variant.video.mimeType, variant.video.codecs));
2463
+ const stream = {
2464
+ id: 0,
2465
+ originalId: null,
2466
+ groupId: null,
2467
+ createSegmentIndex: () => Promise.resolve(),
2468
+ segmentIndex: null,
2469
+ mimeType: mimeType ? shaka.util.MimeUtils.getBasicType(mimeType) : '',
2470
+ codecs: mimeType ? shaka.util.MimeUtils.getCodecs(mimeType) : '',
2471
+ encrypted: true,
2472
+ drmInfos: [], // Filled in by DrmEngine config.
2473
+ keyIds: new Set(),
2474
+ language: 'und',
2475
+ originalLanguage: null,
2476
+ label: null,
2477
+ type: ContentType.VIDEO,
2478
+ primary: false,
2479
+ trickModeVideo: null,
2480
+ emsgSchemeIdUris: null,
2481
+ roles: [],
2482
+ forced: false,
2483
+ channelsCount: null,
2484
+ audioSamplingRate: null,
2485
+ spatialAudio: false,
2486
+ closedCaptions: null,
2487
+ accessibilityPurpose: null,
2488
+ external: false,
2489
+ fastSwitching: false,
2490
+ fullMimeTypes: new Set(),
2491
+ };
2492
+ stream.fullMimeTypes.add(shaka.util.MimeUtils.getFullType(
2493
+ stream.mimeType, stream.codecs));
2494
+ if (mimeType.startsWith('audio/')) {
2495
+ stream.type = ContentType.AUDIO;
2496
+ variant.audio = stream;
2497
+ } else {
2498
+ variant.video = stream;
2499
+ }
2493
2500
 
2494
2501
  this.drmEngine_.setSrcEquals(/* srcEquals= */ true);
2495
2502
  await this.drmEngine_.initForPlayback(
@@ -2708,7 +2715,15 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2708
2715
  fullyLoaded.reject(this.videoErrorToShakaError_());
2709
2716
  });
2710
2717
 
2711
- await fullyLoaded;
2718
+ const timeout = new Promise((resolve, reject) => {
2719
+ const timer = new shaka.util.Timer(reject);
2720
+ timer.tickAfter(this.config_.streaming.loadTimeout);
2721
+ });
2722
+
2723
+ await Promise.race([
2724
+ fullyLoaded,
2725
+ timeout,
2726
+ ]);
2712
2727
 
2713
2728
  const isLive = this.isLive();
2714
2729
 
@@ -3026,10 +3041,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3026
3041
 
3027
3042
  /** @type {shaka.net.NetworkingEngine.OnResponse} */
3028
3043
  const onResponse_ = (type, response, context) => {
3029
- const bytesDownloaded = response.data.byteLength;
3030
- const stats = getStats();
3031
- if (stats) {
3032
- stats.addBytesDownloaded(bytesDownloaded);
3044
+ if (response.data) {
3045
+ const bytesDownloaded = response.data.byteLength;
3046
+ const stats = getStats();
3047
+ if (stats) {
3048
+ stats.addBytesDownloaded(bytesDownloaded);
3049
+ }
3033
3050
  }
3034
3051
  };
3035
3052
 
@@ -7217,7 +7234,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7217
7234
  * @export
7218
7235
  */
7219
7236
  // eslint-disable-next-line no-useless-concat
7220
- shaka.Player.version = 'v4.8.5' + '-uncompiled'; // x-release-please-version
7237
+ shaka.Player.version = 'v4.8.7' + '-uncompiled'; // x-release-please-version
7221
7238
 
7222
7239
  // Initialize the deprecation system using the version string we just set
7223
7240
  // 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;
@@ -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
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.5",
4
+ "version": "4.8.7",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -39,7 +39,7 @@
39
39
  "jimp": "^0.16.1",
40
40
  "js-yaml": "^4.1.0",
41
41
  "jsdoc": "github:joeyparrish/jsdoc#2ca85bb6",
42
- "karma": "^6.3.16",
42
+ "karma": "github:joeyparrish/karma#shaka-fixes",
43
43
  "karma-coverage": "^2.2.0",
44
44
  "karma-jasmine": "^4.0.1",
45
45
  "karma-jasmine-ajax": "^0.1.13",
package/support.html CHANGED
@@ -38,8 +38,10 @@
38
38
  }
39
39
 
40
40
  </style>
41
+ <script src="dist/cast-boot.js"></script>
41
42
  <script src="dist/shaka-player.compiled.js"></script>
42
43
  <script>
44
+
43
45
  function whenLoaded(fn) {
44
46
  // IE 9 fires DOMContentLoaded, and enters the "interactive"
45
47
  // readyState, before document.body has been initialized, so wait
@@ -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
  });