shaka-player 4.8.4 → 4.8.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.
@@ -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
+ };
package/externs/ima.js CHANGED
@@ -204,6 +204,8 @@ google.ima.AdDisplayContainer = class {
204
204
  constructor(adContainer, video) {}
205
205
 
206
206
  initialize() {}
207
+
208
+ destroy() {}
207
209
  };
208
210
 
209
211
 
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
  */
@@ -63,16 +63,17 @@ shaka.ads.ClientSideAdManager = class {
63
63
 
64
64
  google.ima.settings.setDisableCustomPlaybackForIOS10Plus(true);
65
65
 
66
- const adDisplayContainer = new google.ima.AdDisplayContainer(
66
+ /** @private {!google.ima.AdDisplayContainer} */
67
+ this.adDisplayContainer_ = new google.ima.AdDisplayContainer(
67
68
  this.adContainer_,
68
69
  this.video_);
69
70
 
70
71
  // TODO: IMA: Must be done as the result of a user action on mobile
71
- adDisplayContainer.initialize();
72
+ this.adDisplayContainer_.initialize();
72
73
 
73
74
  // IMA: This instance should be re-used for the entire lifecycle of
74
75
  // the page.
75
- this.adsLoader_ = new google.ima.AdsLoader(adDisplayContainer);
76
+ this.adsLoader_ = new google.ima.AdsLoader(this.adDisplayContainer_);
76
77
 
77
78
  this.adsLoader_.getSettings().setPlayerType('shaka-player');
78
79
  this.adsLoader_.getSettings().setPlayerVersion(shaka.Player.version);
@@ -176,6 +177,7 @@ shaka.ads.ClientSideAdManager = class {
176
177
  this.imaAdsManager_.destroy();
177
178
  }
178
179
  this.adsLoader_.destroy();
180
+ this.adDisplayContainer_.destroy();
179
181
  }
180
182
 
181
183
  /**
@@ -928,13 +928,12 @@ shaka.hls.HlsParser = class {
928
928
  if (!playlist.segments.length) {
929
929
  return defaultBasicInfo;
930
930
  }
931
- const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
932
- const middleSegment = playlist.segments[middleSegmentIdx];
933
- const middleSegmentUris = shaka.hls.Utils.constructSegmentUris(
931
+ const firstSegment = playlist.segments[0];
932
+ const firstSegmentUris = shaka.hls.Utils.constructSegmentUris(
934
933
  getUris(),
935
- playlist.segments[0].verbatimSegmentUri);
936
- const middleSegmentUri = middleSegmentUris[0];
937
- const parsedUri = new goog.Uri(middleSegmentUri);
934
+ firstSegment.verbatimSegmentUri);
935
+ const firstSegmentUri = firstSegmentUris[0];
936
+ const parsedUri = new goog.Uri(firstSegmentUri);
938
937
  const extension = parsedUri.getPath().split('.').pop();
939
938
  const rawMimeType = HlsParser.RAW_FORMATS_TO_MIME_TYPES_[extension];
940
939
  if (rawMimeType) {
@@ -946,7 +945,7 @@ shaka.hls.HlsParser = class {
946
945
 
947
946
  let initData = null;
948
947
  const initSegmentRef = this.getInitSegmentReference_(
949
- playlist, middleSegment.tags, getUris);
948
+ playlist, firstSegment.tags, getUris);
950
949
  this.mapTagToInitSegmentRefMap_.clear();
951
950
  if (initSegmentRef) {
952
951
  const initSegmentRequest = shaka.util.Networking.createSegmentRequest(
@@ -961,8 +960,17 @@ shaka.hls.HlsParser = class {
961
960
  initData = initResponse.data;
962
961
  }
963
962
 
964
- const segmentRequest = shaka.net.NetworkingEngine.makeRequest(
965
- middleSegmentUris, this.config_.retryParameters);
963
+ let startByte = 0;
964
+ let endByte = null;
965
+ const byterangeTag = shaka.hls.Utils.getFirstTagWithName(
966
+ firstSegment.tags, 'EXT-X-BYTERANGE');
967
+ if (byterangeTag) {
968
+ [startByte, endByte] = this.parseByteRange_(
969
+ /* previousReference= */ null, byterangeTag.value);
970
+ }
971
+
972
+ const segmentRequest = shaka.util.Networking.createSegmentRequest(
973
+ firstSegmentUris, startByte, endByte, this.config_.retryParameters);
966
974
  const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
967
975
  const response = await this.makeNetworkRequest_(
968
976
  segmentRequest, requestType, {type});
@@ -980,7 +988,8 @@ shaka.hls.HlsParser = class {
980
988
  return basicInfo;
981
989
  }
982
990
  } else if (extension == 'mp4' || extension == 'cmfv' ||
983
- extension == 'm4s' || contentMimeType == 'video/mp4' ||
991
+ extension == 'm4s' || extension == 'fmp4' ||
992
+ contentMimeType == 'video/mp4' ||
984
993
  contentMimeType == 'audio/mp4' ||
985
994
  contentMimeType == 'video/iso.segment') {
986
995
  const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMp4(
@@ -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
  /**
@@ -280,7 +280,7 @@ shaka.media.MediaSourceEngine = class {
280
280
  if (contentType == ContentType.VIDEO) {
281
281
  codecs = StreamUtils.getCorrectVideoCodecs(codecs);
282
282
  } else if (contentType == ContentType.AUDIO) {
283
- codecs = StreamUtils.getCorrectAudioCodecs(codecs);
283
+ codecs = StreamUtils.getCorrectAudioCodecs(codecs, mimeType);
284
284
  }
285
285
  const extendedMimeType = MimeUtils.getExtendedType(
286
286
  stream, mimeType, codecs);
@@ -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
@@ -3026,10 +3026,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3026
3026
 
3027
3027
  /** @type {shaka.net.NetworkingEngine.OnResponse} */
3028
3028
  const onResponse_ = (type, response, context) => {
3029
- const bytesDownloaded = response.data.byteLength;
3030
- const stats = getStats();
3031
- if (stats) {
3032
- stats.addBytesDownloaded(bytesDownloaded);
3029
+ if (response.data) {
3030
+ const bytesDownloaded = response.data.byteLength;
3031
+ const stats = getStats();
3032
+ if (stats) {
3033
+ stats.addBytesDownloaded(bytesDownloaded);
3034
+ }
3033
3035
  }
3034
3036
  };
3035
3037
 
@@ -7217,7 +7219,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7217
7219
  * @export
7218
7220
  */
7219
7221
  // eslint-disable-next-line no-useless-concat
7220
- shaka.Player.version = 'v4.8.4' + '-uncompiled'; // x-release-please-version
7222
+ shaka.Player.version = 'v4.8.6' + '-uncompiled'; // x-release-please-version
7221
7223
 
7222
7224
  // Initialize the deprecation system using the version string we just set
7223
7225
  // on the player.
@@ -144,13 +144,17 @@ shaka.transmuxer.TsTransmuxer = class {
144
144
  convertCodecs(contentType, mimeType) {
145
145
  if (this.isTsContainer_(mimeType)) {
146
146
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
147
+ const StreamUtils = shaka.util.StreamUtils;
147
148
  // The replace it's necessary because Firefox(the only browser that
148
149
  // supports MP3 in MP4) only support the MP3 codec with the mp3 string.
149
150
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.34"') -> false
150
151
  // MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') -> true
151
152
  const codecs = shaka.util.MimeUtils.getCodecs(mimeType)
152
153
  .replace('mp4a.40.34', 'mp3').split(',')
153
- .map(shaka.util.StreamUtils.getCorrectVideoCodecs).join(',');
154
+ .map((codecs) => {
155
+ return StreamUtils.getCorrectAudioCodecs(codecs, 'audio/mp4');
156
+ })
157
+ .map(StreamUtils.getCorrectVideoCodecs).join(',');
154
158
  if (contentType == ContentType.AUDIO) {
155
159
  return `audio/mp4; codecs="${codecs}"`;
156
160
  }
@@ -198,8 +198,8 @@ shaka.util.Platform = class {
198
198
  }
199
199
 
200
200
  /**
201
- * Check if the current platform is a Google Chromecast with Android
202
- * (i.e. Chromecast with GoogleTV).
201
+ * Check if the current platform is a Google Chromecast with Fuchsia
202
+ * (i.e. Google Nest Hub).
203
203
  *
204
204
  * @return {boolean}
205
205
  */
@@ -594,7 +594,8 @@ shaka.util.Platform = class {
594
594
  return false;
595
595
  }
596
596
  // Older chromecasts without GoogleTV seem to not support SMOOTH properly.
597
- if (Platform.isChromecast() && !Platform.isAndroidCastDevice()) {
597
+ if (Platform.isChromecast() && !Platform.isAndroidCastDevice() &&
598
+ !Platform.isFuchsiaCastDevice()) {
598
599
  return false;
599
600
  }
600
601
  // See: https://chromium-review.googlesource.com/c/chromium/src/+/4577759
@@ -429,7 +429,8 @@ shaka.util.StreamUtils = class {
429
429
  videoCodecs = StreamUtils.getCorrectVideoCodecs(videoCodecs);
430
430
  let audioCodecs = ManifestParserUtils.guessCodecs(
431
431
  ContentType.AUDIO, allCodecs);
432
- audioCodecs = StreamUtils.getCorrectAudioCodecs(audioCodecs);
432
+ audioCodecs = StreamUtils.getCorrectAudioCodecs(
433
+ audioCodecs, video.mimeType);
433
434
 
434
435
  const audioFullType = MimeUtils.getFullOrConvertedType(
435
436
  video.mimeType, audioCodecs, ContentType.AUDIO);
@@ -465,7 +466,8 @@ shaka.util.StreamUtils = class {
465
466
  }
466
467
 
467
468
  if (audio) {
468
- const codecs = StreamUtils.getCorrectAudioCodecs(audio.codecs);
469
+ const codecs = StreamUtils.getCorrectAudioCodecs(
470
+ audio.codecs, audio.mimeType);
469
471
  const fullType = MimeUtils.getFullOrConvertedType(
470
472
  audio.mimeType, codecs, ContentType.AUDIO);
471
473
 
@@ -754,17 +756,18 @@ shaka.util.StreamUtils = class {
754
756
  // We ignore the multiplexed audio when there is normal audio also.
755
757
  if (videoCodecs.includes(',') && !audio) {
756
758
  const allCodecs = videoCodecs.split(',');
759
+ const baseMimeType = MimeUtils.getBasicType(fullMimeType);
757
760
 
758
761
  videoCodecs = ManifestParserUtils.guessCodecs(
759
762
  ContentType.VIDEO, allCodecs);
760
763
 
761
764
  let audioCodecs = ManifestParserUtils.guessCodecs(
762
765
  ContentType.AUDIO, allCodecs);
763
- audioCodecs = StreamUtils.getCorrectAudioCodecs(audioCodecs);
766
+ audioCodecs = StreamUtils.getCorrectAudioCodecs(
767
+ audioCodecs, baseMimeType);
764
768
 
765
769
  const audioFullType = MimeUtils.getFullOrConvertedType(
766
- MimeUtils.getBasicType(fullMimeType), audioCodecs,
767
- ContentType.AUDIO);
770
+ baseMimeType, audioCodecs, ContentType.AUDIO);
768
771
 
769
772
  audioConfigs.push({
770
773
  contentType: audioFullType,
@@ -815,10 +818,11 @@ shaka.util.StreamUtils = class {
815
818
  }
816
819
  if (audio) {
817
820
  for (const fullMimeType of audio.fullMimeTypes) {
818
- const codecs = StreamUtils.getCorrectAudioCodecs(MimeUtils.getCodecs(
819
- fullMimeType));
821
+ const baseMimeType = MimeUtils.getBasicType(fullMimeType);
822
+ const codecs = StreamUtils.getCorrectAudioCodecs(
823
+ MimeUtils.getCodecs(fullMimeType), baseMimeType);
820
824
  const fullType = MimeUtils.getFullOrConvertedType(
821
- MimeUtils.getBasicType(fullMimeType), codecs, ContentType.AUDIO);
825
+ baseMimeType, codecs, ContentType.AUDIO);
822
826
 
823
827
  // AudioConfiguration
824
828
  audioConfigs.push({
@@ -975,9 +979,10 @@ shaka.util.StreamUtils = class {
975
979
  * Generates the correct audio codec for MediaDecodingConfiguration and
976
980
  * for MediaSource.isTypeSupported.
977
981
  * @param {string} codecs
982
+ * @param {string} mimeType
978
983
  * @return {string}
979
984
  */
980
- static getCorrectAudioCodecs(codecs) {
985
+ static getCorrectAudioCodecs(codecs, mimeType) {
981
986
  // According to RFC 6381 section 3.3, 'fLaC' is actually the correct
982
987
  // codec string. We still need to map it to 'flac', as some browsers
983
988
  // currently don't support 'fLaC', while 'flac' is supported by most
@@ -992,8 +997,16 @@ shaka.util.StreamUtils = class {
992
997
  }
993
998
 
994
999
  // The same is true for 'Opus'.
995
- if (codecs === 'Opus') {
996
- return 'opus';
1000
+ if (codecs.toLowerCase() === 'opus') {
1001
+ if (!shaka.util.Platform.isSafari()) {
1002
+ return 'opus';
1003
+ } else {
1004
+ if (shaka.util.MimeUtils.getContainerType(mimeType) == 'mp4') {
1005
+ return 'Opus';
1006
+ } else {
1007
+ return 'opus';
1008
+ }
1009
+ }
997
1010
  }
998
1011
 
999
1012
  return codecs;
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.4",
4
+ "version": "4.8.6",
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