shaka-player 4.7.5 → 4.7.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.
Files changed (37) hide show
  1. package/AUTHORS +1 -0
  2. package/CONTRIBUTORS +1 -0
  3. package/dist/locales.js +32 -32
  4. package/dist/shaka-player.compiled.d.ts +8 -1
  5. package/dist/shaka-player.compiled.debug.d.ts +8 -1
  6. package/dist/shaka-player.compiled.debug.externs.js +6 -1
  7. package/dist/shaka-player.compiled.debug.js +52 -49
  8. package/dist/shaka-player.compiled.debug.map +3 -3
  9. package/dist/shaka-player.compiled.externs.js +6 -1
  10. package/dist/shaka-player.compiled.js +1078 -1076
  11. package/dist/shaka-player.compiled.map +3 -3
  12. package/dist/shaka-player.ui.d.ts +20 -2
  13. package/dist/shaka-player.ui.debug.d.ts +20 -2
  14. package/dist/shaka-player.ui.debug.externs.js +7 -1
  15. package/dist/shaka-player.ui.debug.js +59 -58
  16. package/dist/shaka-player.ui.debug.map +3 -3
  17. package/dist/shaka-player.ui.externs.js +7 -1
  18. package/dist/shaka-player.ui.js +1201 -1199
  19. package/dist/shaka-player.ui.map +3 -3
  20. package/externs/shaka/abr_manager.js +9 -0
  21. package/lib/abr/simple_abr_manager.js +11 -0
  22. package/lib/media/drm_engine.js +9 -0
  23. package/lib/media/segment_prefetch.js +4 -0
  24. package/lib/media/streaming_engine.js +6 -8
  25. package/lib/net/networking_engine.js +1 -1
  26. package/lib/offline/storage.js +4 -0
  27. package/lib/player.js +31 -7
  28. package/lib/polyfill/media_capabilities.js +9 -2
  29. package/lib/util/periods.js +4 -0
  30. package/lib/util/platform.js +18 -0
  31. package/lib/util/stream_utils.js +14 -0
  32. package/lib/util/ts_parser.js +7 -4
  33. package/package.json +1 -1
  34. package/ui/controls.js +3 -0
  35. package/ui/externs/ui.js +24 -0
  36. package/ui/pip_button.js +5 -21
  37. package/ui/play_button.js +3 -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
@@ -542,6 +542,15 @@ shaka.media.DrmEngine = class {
542
542
  }
543
543
  }
544
544
 
545
+ /**
546
+ * Returns true if the manifest has init data.
547
+ *
548
+ * @return {boolean}
549
+ */
550
+ hasManifestInitData() {
551
+ return !!this.manifestInitData_;
552
+ }
553
+
545
554
  /**
546
555
  * Sets the server certificate based on the current DrmInfo.
547
556
  *
@@ -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
  }
@@ -400,6 +400,10 @@ shaka.offline.Storage = class {
400
400
  downloader, config, activeHandle.cell, manifest, drmEngine);
401
401
  this.ensureNotDestroyed_();
402
402
 
403
+ this.setManifestDrmFields_(manifest, manifestDB, drmEngine, config);
404
+ await activeHandle.cell.updateManifest(manifestId, manifestDB);
405
+ this.ensureNotDestroyed_();
406
+
403
407
  const offlineUri = shaka.offline.OfflineUri.manifest(
404
408
  activeHandle.path.mechanism, activeHandle.path.cell, manifestId);
405
409
 
package/lib/player.js CHANGED
@@ -1858,6 +1858,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1858
1858
  'Please use an AbrManager with setCmsdManager function.');
1859
1859
  this.abrManager_.setCmsdManager = () => {};
1860
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
+ }
1861
1867
  this.abrManager_.configure(this.config_.abr);
1862
1868
  }
1863
1869
 
@@ -2915,8 +2921,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2915
2921
  onError: (error) => this.onError_(error),
2916
2922
  onEvent: (event) => this.dispatchEvent(event),
2917
2923
  onManifestUpdate: () => this.onManifestUpdate_(),
2918
- onSegmentAppended: (start, end, contentType) => {
2919
- 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
+ }
2920
2931
  },
2921
2932
  onInitSegmentAppended: (position, initSegment) => {
2922
2933
  const mediaQuality = initSegment.getMediaQuality();
@@ -4605,13 +4616,16 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4605
4616
  // variant yet because we set the load mode before we select the first
4606
4617
  // variant to stream.
4607
4618
  const variant = this.streamingEngine_.getCurrentVariant();
4619
+ const textStream = this.streamingEngine_.getCurrentTextStream();
4608
4620
 
4609
4621
  if (variant) {
4610
4622
  const rate = this.playRateController_ ?
4611
4623
  this.playRateController_.getRealRate() : 1;
4612
4624
  const variantBandwidth = rate * variant.bandwidth;
4613
- // TODO: Should include text bandwidth if it enabled.
4614
- const currentStreamBandwidth = variantBandwidth;
4625
+ let currentStreamBandwidth = variantBandwidth;
4626
+ if (textStream && textStream.bandwidth) {
4627
+ currentStreamBandwidth += (rate * textStream.bandwidth);
4628
+ }
4615
4629
  this.stats_.setCurrentStreamBandwidth(currentStreamBandwidth);
4616
4630
  }
4617
4631
 
@@ -4677,6 +4691,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4677
4691
  shaka.util.Error.Code.CONTENT_NOT_LOADED);
4678
4692
  }
4679
4693
 
4694
+ if (kind != 'subtitles' && kind != 'captions') {
4695
+ shaka.log.alwaysWarn(
4696
+ 'Using a kind value different of `subtitles` or `captions` can ' +
4697
+ 'cause unwanted issues.');
4698
+ }
4699
+
4680
4700
  if (!mimeType) {
4681
4701
  mimeType = await this.getTextMimetype_(uri);
4682
4702
  }
@@ -6480,6 +6500,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6480
6500
  const restrictedStatuses = shaka.Player.restrictedStatuses_;
6481
6501
  let tracksChanged = false;
6482
6502
 
6503
+ goog.asserts.assert(this.drmEngine_, 'drmEngine should be non-null here.');
6504
+
6483
6505
  // Only filter tracks for keys if we have some key statuses to look at.
6484
6506
  if (keyIds.length) {
6485
6507
  for (const variant of this.manifest_.variants) {
@@ -6495,8 +6517,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
6495
6517
 
6496
6518
  for (const keyId of stream.keyIds) {
6497
6519
  const keyStatus = keyStatusMap[isGlobalStatus ? '00' : keyId];
6498
- variant.allowedByKeySystem = variant.allowedByKeySystem &&
6499
- !!keyStatus && !restrictedStatuses.includes(keyStatus);
6520
+ if (keyStatus || this.drmEngine_.hasManifestInitData()) {
6521
+ variant.allowedByKeySystem = variant.allowedByKeySystem &&
6522
+ !!keyStatus && !restrictedStatuses.includes(keyStatus);
6523
+ }
6500
6524
  }
6501
6525
  }
6502
6526
 
@@ -6900,7 +6924,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6900
6924
  * @export
6901
6925
  */
6902
6926
  // eslint-disable-next-line no-useless-concat
6903
- shaka.Player.version = 'v4.7.5' + '-uncompiled'; // x-release-please-version
6927
+ shaka.Player.version = 'v4.7.7' + '-uncompiled'; // x-release-please-version
6904
6928
 
6905
6929
  // Initialize the deprecation system using the version string we just set
6906
6930
  // on the player.
@@ -141,8 +141,15 @@ shaka.polyfill.MediaCapabilities = class {
141
141
  }
142
142
 
143
143
  if (audioConfig) {
144
- const contentType = audioConfig.contentType;
145
- const isSupported = Capabilities.isTypeSupported(contentType);
144
+ let isSupported;
145
+ if (shaka.util.Platform.isChromecast() &&
146
+ audioConfig.spatialRendering) {
147
+ const extendedType =
148
+ audioConfig.contentType + '; spatialRendering=true';
149
+ isSupported = Capabilities.isTypeSupported(extendedType);
150
+ } else {
151
+ isSupported = Capabilities.isTypeSupported(audioConfig.contentType);
152
+ }
146
153
  if (!isSupported) {
147
154
  return res;
148
155
  }
@@ -1031,6 +1031,10 @@ shaka.util.PeriodCombiner = class {
1031
1031
  continue;
1032
1032
  }
1033
1033
 
1034
+ if (outputStream.fastSwitching != stream.fastSwitching) {
1035
+ continue;
1036
+ }
1037
+
1034
1038
  if (!best || isBetterMatch(outputStream, best, stream)) {
1035
1039
  best = stream;
1036
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(
@@ -446,10 +446,11 @@ shaka.util.TsParser = class {
446
446
  *
447
447
  * @param {shaka.extern.MPEG_PES} pes
448
448
  * @param {?shaka.extern.VideoNalu=} lastNalu
449
+ * @param {?number=} lastState
449
450
  * @return {!Array.<shaka.extern.VideoNalu>}
450
451
  * @export
451
452
  */
452
- parseNalus(pes, lastNalu) {
453
+ parseNalus(pes, lastNalu, lastState) {
453
454
  const timescale = shaka.util.TsParser.Timescale;
454
455
  const time = pes.pts ? pes.pts / timescale : null;
455
456
  const data = pes.data;
@@ -465,7 +466,7 @@ shaka.util.TsParser = class {
465
466
  // precede each NALU. A start code is 2 or 3 0x00 bytes followed with a
466
467
  // 0x01 byte. e.g. 0x000001 or 0x00000001.
467
468
  // More info in: https://stackoverflow.com/questions/24884827/possible-locations-for-sequence-picture-parameter-sets-for-h-264-stream/24890903#24890903
468
- let numZeros = 0;
469
+ let numZeros = lastState || 0;
469
470
 
470
471
  /** @type {!Array.<shaka.extern.VideoNalu>} */
471
472
  const nalus = [];
@@ -490,7 +491,7 @@ shaka.util.TsParser = class {
490
491
  const startCodeSize = numZeros > 3 ? 3 : numZeros;
491
492
  const lastByteToKeep = i - startCodeSize;
492
493
  // Optimization
493
- if (lastByteToKeep != 0) {
494
+ if (lastState && lastByteToKeep != 0) {
494
495
  const prevData = data.subarray(0, lastByteToKeep);
495
496
  lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
496
497
  lastNalu.data, prevData);
@@ -572,6 +573,7 @@ shaka.util.TsParser = class {
572
573
  lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
573
574
  lastNalu.fullData, data);
574
575
  }
576
+ lastState = Math.min(3, numZeros);
575
577
  return nalus;
576
578
  }
577
579
 
@@ -676,8 +678,9 @@ shaka.util.TsParser = class {
676
678
  }
677
679
  if (naluProcessing) {
678
680
  let lastNalu;
681
+ let lastState;
679
682
  for (const pes of this.videoPes_) {
680
- pes.nalus = this.parseNalus(pes, lastNalu);
683
+ pes.nalus = this.parseNalus(pes, lastNalu, lastState);
681
684
  if (pes.nalus.length) {
682
685
  lastNalu = pes.nalus[pes.nalus.length - 1];
683
686
  }
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.5",
4
+ "version": "4.7.7",
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/externs/ui.js CHANGED
@@ -486,3 +486,27 @@ shaka.extern.IUISeekBar.Factory = class {
486
486
  */
487
487
  create(rootElement, controls) {}
488
488
  };
489
+
490
+ /**
491
+ * @interface
492
+ * @exportDoc
493
+ */
494
+ shaka.extern.IUIPlayButton = class {
495
+ /**
496
+ * @param {!HTMLElement} parent
497
+ * @param {!shaka.ui.Controls} controls
498
+ */
499
+ constructor(parent, controls) {
500
+ /**
501
+ * @protected {!HTMLButtonElement}
502
+ * @exportDoc
503
+ */
504
+ this.button;
505
+ }
506
+
507
+ /** @return {boolean} */
508
+ isPaused() {}
509
+
510
+ /** @return {boolean} */
511
+ isEnded() {}
512
+ };
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()) {
package/ui/play_button.js CHANGED
@@ -16,6 +16,7 @@ goog.requireType('shaka.ui.Controls');
16
16
 
17
17
  /**
18
18
  * @extends {shaka.ui.Element}
19
+ * @implements {shaka.extern.IUIPlayButton}
19
20
  * @export
20
21
  */
21
22
  shaka.ui.PlayButton = class extends shaka.ui.Element {
@@ -95,6 +96,7 @@ shaka.ui.PlayButton = class extends shaka.ui.Element {
95
96
  /**
96
97
  * @return {boolean}
97
98
  * @protected
99
+ * @override
98
100
  */
99
101
  isPaused() {
100
102
  if (this.ad && this.ad.isLinear()) {
@@ -107,6 +109,7 @@ shaka.ui.PlayButton = class extends shaka.ui.Element {
107
109
  /**
108
110
  * @return {boolean}
109
111
  * @protected
112
+ * @override
110
113
  */
111
114
  isEnded() {
112
115
  if (this.ad && this.ad.isLinear()) {