shaka-player 4.10.3 → 4.10.4

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.
@@ -647,12 +647,12 @@ shaka.ads.InterstitialAdManager = class {
647
647
  const dataAsJson =
648
648
  /** @type {!shaka.ads.InterstitialAdManager.AssetsList} */ (
649
649
  JSON.parse(data));
650
- for (const asset of dataAsJson.ASSETS) {
651
- if (asset.URI) {
650
+ for (const asset of dataAsJson['ASSETS']) {
651
+ if (asset['URI']) {
652
652
  interstitialsAd.push({
653
653
  startTime: interstitial.startTime,
654
654
  endTime: interstitial.endTime,
655
- uri: asset.URI,
655
+ uri: asset['URI'],
656
656
  isSkippable,
657
657
  canJump,
658
658
  resumeOffset,
@@ -1019,7 +1019,7 @@ shaka.hls.HlsParser = class {
1019
1019
  if (!segments.length) {
1020
1020
  return defaultBasicInfo;
1021
1021
  }
1022
- const segment = this.getAvailableSegment_(segments);
1022
+ const {segment, segmentIndex} = this.getAvailableSegment_(segments);
1023
1023
  const segmentUris = segment.getUris();
1024
1024
  const segmentUri = segmentUris[0];
1025
1025
  const parsedUri = new goog.Uri(segmentUri);
@@ -1044,6 +1044,10 @@ shaka.hls.HlsParser = class {
1044
1044
  const initResponse = await this.makeNetworkRequest_(
1045
1045
  initSegmentRequest, requestType, {type: initType});
1046
1046
  initData = initResponse.data;
1047
+ if (initSegmentRef.aesKey) {
1048
+ initData = await shaka.media.SegmentUtils.aesDecrypt(
1049
+ initData, initSegmentRef.aesKey, 0);
1050
+ }
1047
1051
 
1048
1052
  initMimeType = initResponse.headers['content-type'];
1049
1053
  if (initMimeType) {
@@ -1058,6 +1062,11 @@ shaka.hls.HlsParser = class {
1058
1062
  const type = shaka.net.NetworkingEngine.AdvancedRequestType.MEDIA_SEGMENT;
1059
1063
  const response = await this.makeNetworkRequest_(
1060
1064
  segmentRequest, requestType, {type});
1065
+ let data = response.data;
1066
+ if (segment.aesKey) {
1067
+ data = await shaka.media.SegmentUtils.aesDecrypt(
1068
+ data, segment.aesKey, segmentIndex);
1069
+ }
1061
1070
 
1062
1071
  let contentMimeType = response.headers['content-type'];
1063
1072
  if (contentMimeType) {
@@ -1085,9 +1094,9 @@ shaka.hls.HlsParser = class {
1085
1094
  ];
1086
1095
 
1087
1096
  if (shaka.util.TsParser.probe(
1088
- shaka.util.BufferUtils.toUint8(response.data))) {
1097
+ shaka.util.BufferUtils.toUint8(data))) {
1089
1098
  const basicInfo =
1090
- shaka.media.SegmentUtils.getBasicInfoFromTs(response.data);
1099
+ shaka.media.SegmentUtils.getBasicInfoFromTs(data);
1091
1100
  if (basicInfo) {
1092
1101
  return basicInfo;
1093
1102
  }
@@ -1095,7 +1104,7 @@ shaka.hls.HlsParser = class {
1095
1104
  validMp4MimeType.includes(contentMimeType) ||
1096
1105
  (initMimeType && validMp4MimeType.includes(initMimeType))) {
1097
1106
  const basicInfo = shaka.media.SegmentUtils.getBasicInfoFromMp4(
1098
- initData, response.data);
1107
+ initData, data);
1099
1108
  if (basicInfo) {
1100
1109
  return basicInfo;
1101
1110
  }
@@ -1579,7 +1588,7 @@ shaka.hls.HlsParser = class {
1579
1588
  videoLayout = videoLayout || 'CH-MONO';
1580
1589
 
1581
1590
  const streamInfos = this.createStreamInfosForVariantTags_(tags,
1582
- mediaTags, resolution, frameRate, bandwidth);
1591
+ mediaTags, resolution, frameRate);
1583
1592
 
1584
1593
  goog.asserts.assert(streamInfos.audio.length ||
1585
1594
  streamInfos.video.length, 'We should have created a stream!');
@@ -1607,12 +1616,10 @@ shaka.hls.HlsParser = class {
1607
1616
  * @param {!Array.<!shaka.hls.Tag>} mediaTags
1608
1617
  * @param {?string} resolution
1609
1618
  * @param {?string} frameRate
1610
- * @param {number} bandwidth
1611
1619
  * @return {!shaka.hls.HlsParser.StreamInfos}
1612
1620
  * @private
1613
1621
  */
1614
- createStreamInfosForVariantTags_(
1615
- tags, mediaTags, resolution, frameRate, bandwidth) {
1622
+ createStreamInfosForVariantTags_(tags, mediaTags, resolution, frameRate) {
1616
1623
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
1617
1624
 
1618
1625
  /** @type {shaka.hls.HlsParser.StreamInfos} */
@@ -1747,9 +1754,6 @@ shaka.hls.HlsParser = class {
1747
1754
  if (globalGroupId) {
1748
1755
  streamInfo.stream.groupId = globalGroupId;
1749
1756
  }
1750
- if (!streamInfos.length) {
1751
- streamInfo.stream.bandwidth = bandwidth;
1752
- }
1753
1757
  res[streamInfo.stream.type] = [streamInfo];
1754
1758
  }
1755
1759
  return res;
@@ -1971,6 +1975,12 @@ shaka.hls.HlsParser = class {
1971
1975
  videoStream.drmInfos = drmInfos;
1972
1976
  videoStream.keyIds = keyIds;
1973
1977
  }
1978
+ if (videoStream && !audioStream) {
1979
+ videoStream.bandwidth = bandwidth;
1980
+ }
1981
+ if (!videoStream && audioStream) {
1982
+ audioStream.bandwidth = bandwidth;
1983
+ }
1974
1984
  const audioDrmInfos = audioInfo ? audioInfo.stream.drmInfos : null;
1975
1985
  const videoDrmInfos = videoInfo ? videoInfo.stream.drmInfos : null;
1976
1986
  const videoStreamUri =
@@ -4224,7 +4234,7 @@ shaka.hls.HlsParser = class {
4224
4234
 
4225
4235
  /**
4226
4236
  * @param {!Array.<!shaka.media.SegmentReference>} segments
4227
- * @return {!shaka.media.SegmentReference}
4237
+ * @return {{segment: !shaka.media.SegmentReference, segmentIndex: number}}
4228
4238
  * @private
4229
4239
  */
4230
4240
  getAvailableSegment_(segments) {
@@ -4241,7 +4251,7 @@ shaka.hls.HlsParser = class {
4241
4251
  segmentIndex ++;
4242
4252
  segment = segments[segmentIndex];
4243
4253
  }
4244
- return segment;
4254
+ return {segment, segmentIndex};
4245
4255
  }
4246
4256
 
4247
4257
  /**
@@ -4257,7 +4267,7 @@ shaka.hls.HlsParser = class {
4257
4267
  const HlsParser = shaka.hls.HlsParser;
4258
4268
  const requestType = shaka.net.NetworkingEngine.RequestType.SEGMENT;
4259
4269
 
4260
- const segment = this.getAvailableSegment_(segments);
4270
+ const {segment} = this.getAvailableSegment_(segments);
4261
4271
 
4262
4272
  if (segment.status == shaka.media.SegmentReference.Status.MISSING) {
4263
4273
  return this.guessMimeTypeFallback_(contentType);
@@ -18,6 +18,7 @@ goog.require('shaka.media.VideoWrapper');
18
18
  goog.require('shaka.util.EventManager');
19
19
  goog.require('shaka.util.IReleasable');
20
20
  goog.require('shaka.util.MediaReadyState');
21
+ goog.require('shaka.util.Platform');
21
22
  goog.require('shaka.util.Timer');
22
23
  goog.requireType('shaka.media.PresentationTimeline');
23
24
 
@@ -420,12 +421,20 @@ shaka.media.MediaSourcePlayhead = class {
420
421
 
421
422
  const gapLimit = shaka.media.GapJumpingController.BROWSER_GAP_TOLERANCE;
422
423
  if (Math.abs(targetTime - currentTime) > gapLimit) {
423
- // You can only seek like this every so often. This is to prevent an
424
- // infinite loop on systems where changing currentTime takes a significant
425
- // amount of time (e.g. Chromecast).
426
- const time = Date.now() / 1000;
427
- if (!this.lastCorrectiveSeek_ || this.lastCorrectiveSeek_ < time - 1) {
428
- this.lastCorrectiveSeek_ = time;
424
+ let canCorrectiveSeek = false;
425
+ if (shaka.util.Platform.isSeekingSlow()) {
426
+ // You can only seek like this every so often. This is to prevent an
427
+ // infinite loop on systems where changing currentTime takes a
428
+ // significant amount of time (e.g. Chromecast).
429
+ const time = Date.now() / 1000;
430
+ if (!this.lastCorrectiveSeek_ || this.lastCorrectiveSeek_ < time - 1) {
431
+ this.lastCorrectiveSeek_ = time;
432
+ canCorrectiveSeek = true;
433
+ }
434
+ } else {
435
+ canCorrectiveSeek = true;
436
+ }
437
+ if (canCorrectiveSeek) {
429
438
  this.videoWrapper_.setTime(targetTime);
430
439
  return;
431
440
  }
@@ -534,6 +534,47 @@ shaka.media.SegmentUtils = class {
534
534
  .parse(data, /* partialOkay= */ true);
535
535
  return defaultKID;
536
536
  }
537
+
538
+ /**
539
+ * @param {!BufferSource} rawResult
540
+ * @param {shaka.extern.aesKey} aesKey
541
+ * @param {number} position
542
+ * @return {!Promise.<!BufferSource>}
543
+ */
544
+ static async aesDecrypt(rawResult, aesKey, position) {
545
+ const key = aesKey;
546
+ if (!key.cryptoKey) {
547
+ goog.asserts.assert(key.fetchKey, 'If AES cryptoKey was not ' +
548
+ 'preloaded, fetchKey function should be provided');
549
+ await key.fetchKey();
550
+ goog.asserts.assert(key.cryptoKey, 'AES cryptoKey should now be set');
551
+ }
552
+ let iv = key.iv;
553
+ if (!iv) {
554
+ iv = shaka.util.BufferUtils.toUint8(new ArrayBuffer(16));
555
+ let sequence = key.firstMediaSequenceNumber + position;
556
+ for (let i = iv.byteLength - 1; i >= 0; i--) {
557
+ iv[i] = sequence & 0xff;
558
+ sequence >>= 8;
559
+ }
560
+ }
561
+ let algorithm;
562
+ if (aesKey.blockCipherMode == 'CBC') {
563
+ algorithm = {
564
+ name: 'AES-CBC',
565
+ iv,
566
+ };
567
+ } else {
568
+ algorithm = {
569
+ name: 'AES-CTR',
570
+ counter: iv,
571
+ // NIST SP800-38A standard suggests that the counter should occupy half
572
+ // of the counter block
573
+ length: 64,
574
+ };
575
+ }
576
+ return window.crypto.subtle.decrypt(algorithm, key.cryptoKey, rawResult);
577
+ }
537
578
  };
538
579
 
539
580
 
@@ -18,9 +18,9 @@ goog.require('shaka.media.MediaSourceEngine');
18
18
  goog.require('shaka.media.SegmentIterator');
19
19
  goog.require('shaka.media.SegmentReference');
20
20
  goog.require('shaka.media.SegmentPrefetch');
21
+ goog.require('shaka.media.SegmentUtils');
21
22
  goog.require('shaka.net.Backoff');
22
23
  goog.require('shaka.net.NetworkingEngine');
23
- goog.require('shaka.util.BufferUtils');
24
24
  goog.require('shaka.util.DelayedTick');
25
25
  goog.require('shaka.util.Destroyer');
26
26
  goog.require('shaka.util.Error');
@@ -769,25 +769,16 @@ shaka.media.StreamingEngine = class {
769
769
  const mediaState = this.mediaStates_.get(type);
770
770
  const logPrefix = shaka.media.StreamingEngine.logPrefix_(mediaState);
771
771
 
772
- let segment = null;
773
- if (mediaState.segmentIterator) {
774
- segment = mediaState.segmentIterator.current();
775
- }
776
- if (mediaState.segmentPrefetch) {
777
- mediaState.segmentPrefetch.resetPosition();
778
- }
779
- if (mediaState.type === ContentType.AUDIO) {
780
- for (const prefetch of this.audioPrefetchMap_.values()) {
781
- prefetch.resetPosition();
772
+ if (!newTimeIsBuffered(type)) {
773
+ if (mediaState.segmentPrefetch) {
774
+ mediaState.segmentPrefetch.resetPosition();
775
+ }
776
+ if (mediaState.type === ContentType.AUDIO) {
777
+ for (const prefetch of this.audioPrefetchMap_.values()) {
778
+ prefetch.resetPosition();
779
+ }
782
780
  }
783
- }
784
- // Only reset the iterator if we seek outside the current segment.
785
- if (!segment || segment.startTime > presentationTime ||
786
- segment.endTime < presentationTime) {
787
781
  mediaState.segmentIterator = null;
788
- }
789
-
790
- if (!newTimeIsBuffered(type)) {
791
782
  const bufferEnd =
792
783
  this.playerInterface_.mediaSourceEngine.bufferEnd(type);
793
784
  const somethingBuffered = bufferEnd != null;
@@ -1400,7 +1391,7 @@ shaka.media.StreamingEngine = class {
1400
1391
 
1401
1392
  if (mediaState.segmentPrefetch && mediaState.segmentIterator &&
1402
1393
  !this.audioPrefetchMap_.has(mediaState.stream)) {
1403
- mediaState.segmentPrefetch.evict(presentationTime);
1394
+ mediaState.segmentPrefetch.evict(reference.startTime);
1404
1395
  mediaState.segmentPrefetch.prefetchSegmentsByTime(reference.startTime);
1405
1396
  }
1406
1397
 
@@ -1758,48 +1749,6 @@ shaka.media.StreamingEngine = class {
1758
1749
  }
1759
1750
  }
1760
1751
 
1761
- /**
1762
- * @param {!BufferSource} rawResult
1763
- * @param {shaka.extern.aesKey} aesKey
1764
- * @param {number} position
1765
- * @return {!Promise.<!BufferSource>} finalResult
1766
- * @private
1767
- */
1768
- async aesDecrypt_(rawResult, aesKey, position) {
1769
- const key = aesKey;
1770
- if (!key.cryptoKey) {
1771
- goog.asserts.assert(key.fetchKey, 'If AES cryptoKey was not ' +
1772
- 'preloaded, fetchKey function should be provided');
1773
- await key.fetchKey();
1774
- goog.asserts.assert(key.cryptoKey, 'AES cryptoKey should now be set');
1775
- }
1776
- let iv = key.iv;
1777
- if (!iv) {
1778
- iv = shaka.util.BufferUtils.toUint8(new ArrayBuffer(16));
1779
- let sequence = key.firstMediaSequenceNumber + position;
1780
- for (let i = iv.byteLength - 1; i >= 0; i--) {
1781
- iv[i] = sequence & 0xff;
1782
- sequence >>= 8;
1783
- }
1784
- }
1785
- let algorithm;
1786
- if (aesKey.blockCipherMode == 'CBC') {
1787
- algorithm = {
1788
- name: 'AES-CBC',
1789
- iv,
1790
- };
1791
- } else {
1792
- algorithm = {
1793
- name: 'AES-CTR',
1794
- counter: iv,
1795
- // NIST SP800-38A standard suggests that the counter should occupy half
1796
- // of the counter block
1797
- length: 64,
1798
- };
1799
- }
1800
- return window.crypto.subtle.decrypt(algorithm, key.cryptoKey, rawResult);
1801
- }
1802
-
1803
1752
 
1804
1753
  /**
1805
1754
  * Clear per-stream error states and retry any failed streams.
@@ -2576,7 +2525,8 @@ shaka.media.StreamingEngine = class {
2576
2525
  mediaState.operation = null;
2577
2526
  let result = response.data;
2578
2527
  if (reference.aesKey) {
2579
- result = await this.aesDecrypt_(result, reference.aesKey, position);
2528
+ result = await shaka.media.SegmentUtils.aesDecrypt(
2529
+ result, reference.aesKey, position);
2580
2530
  }
2581
2531
  return result;
2582
2532
  }
package/lib/player.js CHANGED
@@ -4862,13 +4862,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4862
4862
  */
4863
4863
  selectVariantTrack(track, clearBuffer = false, safeMargin = 0) {
4864
4864
  if (this.manifest_ && this.streamingEngine_) {
4865
- if (this.config_.abr.enabled) {
4866
- shaka.log.alwaysWarn('Changing tracks while abr manager is enabled ' +
4867
- 'will likely result in the selected track ' +
4868
- 'being overriden. Consider disabling abr before ' +
4869
- 'calling selectVariantTrack().');
4870
- }
4871
-
4872
4865
  const variant = this.manifest_.variants.find(
4873
4866
  (variant) => variant.id == track.id);
4874
4867
  if (!variant) {
@@ -4885,6 +4878,17 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4885
4878
  return;
4886
4879
  }
4887
4880
 
4881
+ const active = this.streamingEngine_.getCurrentVariant();
4882
+ if (this.config_.abr.enabled && (active.video != variant.video ||
4883
+ (active.audio && variant.audio &&
4884
+ active.audio.language == variant.audio.language &&
4885
+ active.audio.channelsCount == variant.audio.channelsCount))) {
4886
+ shaka.log.alwaysWarn('Changing tracks while abr manager is enabled ' +
4887
+ 'will likely result in the selected track ' +
4888
+ 'being overriden. Consider disabling abr before ' +
4889
+ 'calling selectVariantTrack().');
4890
+ }
4891
+
4888
4892
  this.switchVariant_(
4889
4893
  variant, /* fromAdaptation= */ false, clearBuffer, safeMargin);
4890
4894
 
@@ -7809,7 +7813,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7809
7813
  * @export
7810
7814
  */
7811
7815
  // eslint-disable-next-line no-useless-concat
7812
- shaka.Player.version = 'v4.10.3' + '-uncompiled'; // x-release-please-version
7816
+ shaka.Player.version = 'v4.10.4' + '-uncompiled'; // x-release-please-version
7813
7817
 
7814
7818
  // Initialize the deprecation system using the version string we just set
7815
7819
  // on the player.
@@ -645,6 +645,25 @@ shaka.util.Platform = class {
645
645
  return true;
646
646
  }
647
647
 
648
+ /**
649
+ * On some platforms, such as v1 Chromecasts, the act of seeking can take a
650
+ * significant amount of time.
651
+ *
652
+ * @return {boolean}
653
+ */
654
+ static isSeekingSlow() {
655
+ const Platform = shaka.util.Platform;
656
+ if (Platform.isChromecast()) {
657
+ if (Platform.isAndroidCastDevice()) {
658
+ // Android-based Chromecasts are new enough to not be a problem.
659
+ return false;
660
+ } else {
661
+ return true;
662
+ }
663
+ }
664
+ return false;
665
+ }
666
+
648
667
  /**
649
668
  * Returns true if MediaKeys is polyfilled
650
669
  *
@@ -108,7 +108,8 @@ shaka.util.StreamUtils = class {
108
108
  const currentCodec =
109
109
  MimeUtils.getNormalizedCodec(stream.codecs);
110
110
  if (previousCodec == currentCodec) {
111
- if (stream.bandwidth > previousStream.bandwidth) {
111
+ if (!stream.bandwidth || !previousStream.bandwidth ||
112
+ stream.bandwidth > previousStream.bandwidth) {
112
113
  validAudioStreams.push(stream);
113
114
  validAudioIds.push(stream.id);
114
115
  }
@@ -162,7 +163,8 @@ shaka.util.StreamUtils = class {
162
163
  const currentCodec =
163
164
  MimeUtils.getNormalizedCodec(stream.codecs);
164
165
  if (previousCodec == currentCodec) {
165
- if (stream.bandwidth > previousStream.bandwidth) {
166
+ if (!stream.bandwidth || !previousStream.bandwidth ||
167
+ stream.bandwidth > previousStream.bandwidth) {
166
168
  validVideoStreams.push(stream);
167
169
  validVideoIds.push(stream.id);
168
170
  }
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.10.3",
4
+ "version": "4.10.4",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [