shaka-player 4.1.3 → 4.1.5

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 (85) hide show
  1. package/.github/workflows/build-and-test.yaml +31 -16
  2. package/.github/workflows/custom-actions/set-commit-status/action.yaml +1 -5
  3. package/.github/workflows/deflake.yaml +28 -0
  4. package/.github/workflows/selenium-lab-tests.yaml +168 -54
  5. package/AUTHORS +2 -0
  6. package/CHANGELOG.md +29 -0
  7. package/CONTRIBUTORS +2 -0
  8. package/demo/common/message_ids.js +1 -0
  9. package/demo/config.js +3 -1
  10. package/demo/locales/en.json +1 -0
  11. package/demo/locales/source.json +4 -0
  12. package/dist/controls.css +1 -1
  13. package/dist/controls.css.map +1 -1
  14. package/dist/demo.compiled.debug.js +6 -6
  15. package/dist/demo.compiled.debug.map +2 -2
  16. package/dist/demo.compiled.js +6 -6
  17. package/dist/demo.compiled.map +2 -2
  18. package/dist/deps.js +1 -1
  19. package/dist/locales.js +31 -31
  20. package/dist/receiver.compiled.debug.js +3 -3
  21. package/dist/receiver.compiled.debug.map +2 -2
  22. package/dist/receiver.compiled.js +3 -3
  23. package/dist/receiver.compiled.map +2 -2
  24. package/dist/shaka-player.compiled.d.ts +2 -2
  25. package/dist/shaka-player.compiled.debug.d.ts +2 -2
  26. package/dist/shaka-player.compiled.debug.externs.js +24 -24
  27. package/dist/shaka-player.compiled.debug.js +209 -200
  28. package/dist/shaka-player.compiled.debug.map +4 -4
  29. package/dist/shaka-player.compiled.externs.js +24 -24
  30. package/dist/shaka-player.compiled.js +736 -733
  31. package/dist/shaka-player.compiled.map +4 -4
  32. package/dist/shaka-player.ui.d.ts +2 -2
  33. package/dist/shaka-player.ui.debug.d.ts +2 -2
  34. package/dist/shaka-player.ui.debug.externs.js +24 -24
  35. package/dist/shaka-player.ui.debug.js +263 -254
  36. package/dist/shaka-player.ui.debug.map +4 -4
  37. package/dist/shaka-player.ui.externs.js +24 -24
  38. package/dist/shaka-player.ui.js +900 -896
  39. package/dist/shaka-player.ui.map +4 -4
  40. package/docs/jsdoc-template/publish.js +33 -0
  41. package/docs/jsdoc-template/tmpl/container.tmpl +4 -1
  42. package/docs/jsdoc.conf.json +1 -0
  43. package/externs/shaka/manifest_parser.js +4 -1
  44. package/externs/shaka/player.js +7 -1
  45. package/lib/ads/client_side_ad_manager.js +7 -0
  46. package/lib/cast/cast_sender.js +39 -14
  47. package/lib/cea/cea608_memory.js +4 -0
  48. package/lib/hls/hls_parser.js +30 -8
  49. package/lib/media/drm_engine.js +52 -1
  50. package/lib/media/media_source_engine.js +43 -15
  51. package/lib/media/streaming_engine.js +79 -14
  52. package/lib/offline/storage.js +1 -0
  53. package/lib/player.js +29 -5
  54. package/lib/text/ttml_text_parser.js +6 -0
  55. package/lib/text/ui_text_displayer.js +8 -0
  56. package/lib/util/platform.js +24 -0
  57. package/lib/util/player_configuration.js +5 -0
  58. package/lib/util/pssh.js +2 -1
  59. package/lib/util/stream_utils.js +2 -1
  60. package/package.json +1 -1
  61. package/test/cea/cea608_memory_unit.js +88 -0
  62. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  63. package/test/dash/dash_parser_live_unit.js +1 -0
  64. package/test/dash/dash_parser_manifest_unit.js +1 -0
  65. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  66. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  67. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  68. package/test/hls/hls_live_unit.js +169 -247
  69. package/test/hls/hls_parser_unit.js +60 -2
  70. package/test/media/drm_engine_unit.js +90 -0
  71. package/test/media/media_source_engine_integration.js +36 -0
  72. package/test/media/media_source_engine_unit.js +118 -1
  73. package/test/media/streaming_engine_integration.js +9 -7
  74. package/test/media/streaming_engine_unit.js +29 -0
  75. package/test/offline/offline_integration.js +3 -3
  76. package/test/player_integration.js +2 -2
  77. package/test/player_src_equals_integration.js +2 -0
  78. package/test/player_unit.js +39 -1
  79. package/test/test/assets/small.mp4 +0 -0
  80. package/test/test/util/dash_parser_util.js +2 -0
  81. package/test/test/util/ttml_utils.js +0 -2
  82. package/test/text/ttml_text_parser_unit.js +46 -0
  83. package/test/ui/text_displayer_layout_unit.js +17 -2
  84. package/ui/controls.js +0 -7
  85. package/ui/less/ad_controls.less +4 -0
@@ -320,8 +320,14 @@ shaka.media.StreamingEngine = class {
320
320
  * @param {number=} safeMargin
321
321
  * @param {boolean=} force
322
322
  * If true, reload the variant even if it did not change.
323
+ * @param {boolean=} adaptation
324
+ * If true, update the media state to indicate MediaSourceEngine should
325
+ * reset the timestamp offset to ensure the new track segments are correctly
326
+ * placed on the timeline.
323
327
  */
324
- switchVariant(variant, clearBuffer = false, safeMargin = 0, force = false) {
328
+ switchVariant(
329
+ variant, clearBuffer = false, safeMargin = 0, force = false,
330
+ adaptation = false) {
325
331
  this.currentVariant_ = variant;
326
332
 
327
333
  if (!this.startupComplete_) {
@@ -332,12 +338,14 @@ shaka.media.StreamingEngine = class {
332
338
  if (variant.video) {
333
339
  this.switchInternal_(
334
340
  variant.video, /* clearBuffer= */ clearBuffer,
335
- /* safeMargin= */ safeMargin, /* force= */ force);
341
+ /* safeMargin= */ safeMargin, /* force= */ force,
342
+ /* adaptation= */ adaptation);
336
343
  }
337
344
  if (variant.audio) {
338
345
  this.switchInternal_(
339
346
  variant.audio, /* clearBuffer= */ clearBuffer,
340
- /* safeMargin= */ safeMargin, /* force= */ force);
347
+ /* safeMargin= */ safeMargin, /* force= */ force,
348
+ /* adaptation= */ adaptation);
341
349
  }
342
350
  }
343
351
 
@@ -383,9 +391,13 @@ shaka.media.StreamingEngine = class {
383
391
  * @param {number} safeMargin
384
392
  * @param {boolean} force
385
393
  * If true, reload the text stream even if it did not change.
394
+ * @param {boolean=} adaptation
395
+ * If true, update the media state to indicate MediaSourceEngine should
396
+ * reset the timestamp offset to ensure the new track segments are correctly
397
+ * placed on the timeline.
386
398
  * @private
387
399
  */
388
- switchInternal_(stream, clearBuffer, safeMargin, force) {
400
+ switchInternal_(stream, clearBuffer, safeMargin, force, adaptation) {
389
401
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
390
402
  const type = /** @type {!ContentType} */(stream.type);
391
403
  const mediaState = this.mediaStates_.get(type);
@@ -440,6 +452,7 @@ shaka.media.StreamingEngine = class {
440
452
 
441
453
  mediaState.stream = stream;
442
454
  mediaState.segmentIterator = null;
455
+ mediaState.adaptation = !!adaptation;
443
456
 
444
457
  const streamTag = shaka.media.StreamingEngine.logPrefix_(mediaState);
445
458
  shaka.log.debug('switch: switching to Stream ' + streamTag);
@@ -769,12 +782,11 @@ shaka.media.StreamingEngine = class {
769
782
  const mediaSourceEngine = this.playerInterface_.mediaSourceEngine;
770
783
  const forceTransmuxTS = this.config_.forceTransmuxTS;
771
784
 
772
-
773
785
  await mediaSourceEngine.init(streamsByType, forceTransmuxTS,
774
786
  this.manifest_.sequenceMode);
775
787
  this.destroyer_.ensureNotDestroyed();
776
788
 
777
- this.setDuration_();
789
+ this.updateDuration();
778
790
 
779
791
  for (const type of streamsByType.keys()) {
780
792
  const stream = streamsByType.get(type);
@@ -824,9 +836,8 @@ shaka.media.StreamingEngine = class {
824
836
 
825
837
  /**
826
838
  * Sets the MediaSource's duration.
827
- * @private
828
839
  */
829
- setDuration_() {
840
+ updateDuration() {
830
841
  const duration = this.manifest_.presentationTimeline.getDuration();
831
842
  if (duration < Infinity) {
832
843
  this.playerInterface_.mediaSourceEngine.setDuration(duration);
@@ -1001,7 +1012,11 @@ shaka.media.StreamingEngine = class {
1001
1012
  const timeUntilEnd =
1002
1013
  this.manifest_.presentationTimeline.getDuration() - timeNeeded;
1003
1014
  const oneMicrosecond = 1e-6;
1004
- if (timeUntilEnd < oneMicrosecond) {
1015
+
1016
+ const bufferEnd =
1017
+ this.playerInterface_.mediaSourceEngine.bufferEnd(mediaState.type);
1018
+
1019
+ if (timeUntilEnd < oneMicrosecond && !!bufferEnd) {
1005
1020
  // We shouldn't rebuffer if the playhead is close to the end of the
1006
1021
  // presentation.
1007
1022
  shaka.log.debug(logPrefix, 'buffered to end of presentation');
@@ -1031,8 +1046,6 @@ shaka.media.StreamingEngine = class {
1031
1046
  return this.config_.updateIntervalSeconds / 2;
1032
1047
  }
1033
1048
 
1034
- const bufferEnd =
1035
- this.playerInterface_.mediaSourceEngine.bufferEnd(mediaState.type);
1036
1049
  const reference = this.getSegmentReferenceNeeded_(
1037
1050
  mediaState, presentationTime, bufferEnd);
1038
1051
  if (!reference) {
@@ -1235,7 +1248,16 @@ shaka.media.StreamingEngine = class {
1235
1248
  // Enable MP4 low latency streaming with ReadableStream chunked data.
1236
1249
  if (this.config_.lowLatencyMode && isReadableStreamSupported && isMP4) {
1237
1250
  let remaining = new Uint8Array(0);
1251
+ let processingResult = false;
1252
+ let callbackCalled = false;
1238
1253
  const streamDataCallback = async (data) => {
1254
+ if (processingResult) {
1255
+ // If the fallback result processing was triggered, don't also
1256
+ // append the buffer here. In theory this should never happen,
1257
+ // but it does on some older TVs.
1258
+ return;
1259
+ }
1260
+ callbackCalled = true;
1239
1261
  this.destroyer_.ensureNotDestroyed();
1240
1262
  if (this.fatalError_) {
1241
1263
  return;
@@ -1266,7 +1288,33 @@ shaka.media.StreamingEngine = class {
1266
1288
  }
1267
1289
  };
1268
1290
 
1269
- await this.fetch_(mediaState, reference, streamDataCallback);
1291
+ const result =
1292
+ await this.fetch_(mediaState, reference, streamDataCallback);
1293
+ if (!callbackCalled) {
1294
+ // In some environments, we might be forced to use network plugins
1295
+ // that don't support streamDataCallback. In those cases, as a
1296
+ // fallback, append the buffer here.
1297
+ processingResult = true;
1298
+ this.destroyer_.ensureNotDestroyed();
1299
+ if (this.fatalError_) {
1300
+ return;
1301
+ }
1302
+
1303
+ // If the text stream gets switched between fetch_() and append_(),
1304
+ // the new text parser is initialized, but the new init segment is
1305
+ // not fetched yet. That would cause an error in
1306
+ // TextParser.parseMedia().
1307
+ // See http://b/168253400
1308
+ if (mediaState.waitingToClearBuffer) {
1309
+ shaka.log.info(logPrefix, 'waitingToClearBuffer, skip append');
1310
+ mediaState.performingUpdate = false;
1311
+ this.scheduleUpdate_(mediaState, 0);
1312
+ return;
1313
+ }
1314
+
1315
+ await this.append_(
1316
+ mediaState, presentationTime, stream, reference, result);
1317
+ }
1270
1318
  } else {
1271
1319
  if (this.config_.lowLatencyMode && !isReadableStreamSupported) {
1272
1320
  shaka.log.warning('Low latency streaming mode is enabled, but ' +
@@ -1554,6 +1602,8 @@ shaka.media.StreamingEngine = class {
1554
1602
  shaka.log.v1(logPrefix, 'appending init segment');
1555
1603
  const hasClosedCaptions = mediaState.stream.closedCaptions &&
1556
1604
  mediaState.stream.closedCaptions.size > 0;
1605
+ await this.playerInterface_.beforeAppendSegment(
1606
+ mediaState.type, initSegment);
1557
1607
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1558
1608
  mediaState.type, initSegment, /* reference= */ null,
1559
1609
  hasClosedCaptions);
@@ -1604,14 +1654,21 @@ shaka.media.StreamingEngine = class {
1604
1654
  shaka.log.v1(logPrefix, 'appending media segment at',
1605
1655
  (reference.syncTime == null ? 'unknown' : reference.syncTime));
1606
1656
 
1657
+ // 'seeked' or 'adaptation' triggered logic applies only to this
1658
+ // appendBuffer() call.
1607
1659
  const seeked = mediaState.seeked;
1608
1660
  mediaState.seeked = false;
1661
+ const adaptation = mediaState.adaptation;
1662
+ mediaState.adaptation = false;
1663
+
1664
+ await this.playerInterface_.beforeAppendSegment(mediaState.type, segment);
1609
1665
  await this.playerInterface_.mediaSourceEngine.appendBuffer(
1610
1666
  mediaState.type,
1611
1667
  segment,
1612
1668
  reference,
1613
1669
  hasClosedCaptions,
1614
- seeked);
1670
+ seeked,
1671
+ adaptation);
1615
1672
  this.destroyer_.ensureNotDestroyed();
1616
1673
  shaka.log.v2(logPrefix, 'appended media segment');
1617
1674
  }
@@ -1988,7 +2045,9 @@ shaka.media.StreamingEngine = class {
1988
2045
  * onEvent: function(!Event),
1989
2046
  * onManifestUpdate: function(),
1990
2047
  * onSegmentAppended: function(),
1991
- * onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference)
2048
+ * onInitSegmentAppended: function(!number,!shaka.media.InitSegmentReference),
2049
+ * beforeAppendSegment: function(
2050
+ * shaka.util.ManifestParserUtils.ContentType,!BufferSource):Promise
1992
2051
  * }}
1993
2052
  *
1994
2053
  * @property {function():number} getPresentationTime
@@ -2016,6 +2075,9 @@ shaka.media.StreamingEngine = class {
2016
2075
  * @property
2017
2076
  * {function(!number, !shaka.media.InitSegmentReference)} onInitSegmentAppended
2018
2077
  * Called when an init segment is appended to a MediaSource.
2078
+ * @property {!function(shaka.util.ManifestParserUtils.ContentType,
2079
+ * !BufferSource):Promise} beforeAppendSegment
2080
+ * A function called just before appending to the source buffer.
2019
2081
  */
2020
2082
  shaka.media.StreamingEngine.PlayerInterface;
2021
2083
 
@@ -2039,6 +2101,7 @@ shaka.media.StreamingEngine.PlayerInterface;
2039
2101
  * clearBufferSafeMargin: number,
2040
2102
  * clearingBuffer: boolean,
2041
2103
  * seeked: boolean,
2104
+ * adaptation: boolean,
2042
2105
  * recovering: boolean,
2043
2106
  * hasError: boolean,
2044
2107
  * operation: shaka.net.NetworkingEngine.PendingRequest
@@ -2085,6 +2148,8 @@ shaka.media.StreamingEngine.PlayerInterface;
2085
2148
  * True indicates that the buffer is being cleared.
2086
2149
  * @property {boolean} seeked
2087
2150
  * True indicates that the presentation just seeked.
2151
+ * @property {boolean} adaptation
2152
+ * True indicates that the presentation just automatically switched variants.
2088
2153
  * @property {boolean} recovering
2089
2154
  * True indicates that the last segment was not appended because it could not
2090
2155
  * fit in the buffer.
@@ -1169,6 +1169,7 @@ shaka.offline.Storage = class {
1169
1169
  isLowLatencyMode: () => false,
1170
1170
  isAutoLowLatencyMode: () => false,
1171
1171
  enableLowLatencyMode: () => {},
1172
+ updateDuration: () => {},
1172
1173
  };
1173
1174
 
1174
1175
  parser.configure(config.manifest);
package/lib/player.js CHANGED
@@ -1770,6 +1770,11 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
1770
1770
  enableLowLatencyMode: () => {
1771
1771
  this.configure('streaming.lowLatencyMode', true);
1772
1772
  },
1773
+ updateDuration: () => {
1774
+ if (this.streamingEngine_) {
1775
+ this.streamingEngine_.updateDuration();
1776
+ }
1777
+ },
1773
1778
  };
1774
1779
 
1775
1780
  const startTime = Date.now() / 1000;
@@ -2945,6 +2950,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2945
2950
  this.qualityObserver_.addMediaQualityChange(mediaQuality, position);
2946
2951
  }
2947
2952
  },
2953
+ beforeAppendSegment: (contentType, segment) => {
2954
+ return this.drmEngine_.parseInbandPssh(contentType, segment);
2955
+ },
2948
2956
  };
2949
2957
 
2950
2958
  return new shaka.media.StreamingEngine(this.manifest_, playerInterface);
@@ -3831,8 +3839,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
3831
3839
  return;
3832
3840
  }
3833
3841
 
3834
- this.switchVariant_(variant, /* fromAdaptation= */ false, clearBuffer,
3835
- safeMargin);
3842
+ this.switchVariant_(
3843
+ variant, /* fromAdaptation= */ false, clearBuffer, safeMargin);
3836
3844
 
3837
3845
  // Workaround for
3838
3846
  // https://github.com/shaka-project/shaka-player/issues/1299
@@ -4452,6 +4460,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4452
4460
  this.stats_.setBandwidthEstimate(estimate);
4453
4461
  }
4454
4462
 
4463
+ if (this.loadMode_ == shaka.Player.LoadMode.SRC_EQUALS) {
4464
+ this.stats_.setResolution(
4465
+ /* width= */ element.videoWidth || NaN,
4466
+ /* height= */ element.videoHeight || NaN);
4467
+ }
4468
+
4455
4469
  return this.stats_.getBlob();
4456
4470
  }
4457
4471
 
@@ -5363,7 +5377,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5363
5377
  const currentVariant = this.streamingEngine_.getCurrentVariant();
5364
5378
  if (variant == currentVariant) {
5365
5379
  shaka.log.debug('Variant already selected.');
5366
- // If you want to clear the buffer, we force to reselect the same variant
5380
+ // If you want to clear the buffer, we force to reselect the same variant.
5381
+ // We don't need to reset the timestampOffset since it's the same variant,
5382
+ // so 'adaptation' isn't passed here.
5367
5383
  if (clearBuffer) {
5368
5384
  this.streamingEngine_.switchVariant(variant, clearBuffer, safeMargin,
5369
5385
  /* force= */ true);
@@ -5373,7 +5389,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5373
5389
 
5374
5390
  // Add entries to the history.
5375
5391
  this.addVariantToSwitchHistory_(variant, fromAdaptation);
5376
- this.streamingEngine_.switchVariant(variant, clearBuffer, safeMargin);
5392
+ this.streamingEngine_.switchVariant(
5393
+ variant, clearBuffer, safeMargin, /* force= */ undefined,
5394
+ /* adaptation= */ fromAdaptation);
5377
5395
  let oldTrack = null;
5378
5396
  if (currentVariant) {
5379
5397
  oldTrack = shaka.util.StreamUtils.variantToTrack(currentVariant);
@@ -5618,6 +5636,12 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
5618
5636
  return false;
5619
5637
  }
5620
5638
 
5639
+ if (!navigator.onLine) {
5640
+ // Don't restrict variants if we're completely offline, or else we end up
5641
+ // rapidly restricting all of them.
5642
+ return false;
5643
+ }
5644
+
5621
5645
  let maxDisabledTime = this.config_.streaming.maxDisabledTime;
5622
5646
  if (maxDisabledTime == 0) {
5623
5647
  if (error.code == shaka.util.Error.Code.SEGMENT_MISSING) {
@@ -6551,7 +6575,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6551
6575
  * @define {string} A version number taken from git at compile time.
6552
6576
  * @export
6553
6577
  */
6554
- shaka.Player.version = 'v4.1.3-uncompiled';
6578
+ shaka.Player.version = 'v4.1.5-uncompiled';
6555
6579
 
6556
6580
  // Initialize the deprecation system using the version string we just set
6557
6581
  // on the player.
@@ -164,6 +164,12 @@ shaka.text.TtmlTextParser = class {
164
164
  cellResolutionInfo, /* parentCueElement= */ null,
165
165
  /* isContent= */ false);
166
166
  if (cue) {
167
+ // According to the TTML spec, backgrounds default to transparent.
168
+ // So default the background of the top-level element to transparent.
169
+ // Nested elements may override that background color already.
170
+ if (!cue.backgroundColor) {
171
+ cue.backgroundColor = 'transparent';
172
+ }
167
173
  cues.push(cue);
168
174
  }
169
175
  }
@@ -493,6 +493,14 @@ shaka.text.UITextDisplayer = class {
493
493
  style.backgroundRepeat = 'no-repeat';
494
494
  style.backgroundSize = 'contain';
495
495
  style.backgroundPosition = 'center';
496
+
497
+ // Quoting https://www.w3.org/TR/ttml-imsc1.2/:
498
+ // "The width and height (in pixels) of the image resource referenced by
499
+ // smpte:backgroundImage SHALL be equal to the width and height expressed
500
+ // by the tts:extent attribute of the region in which the div element is
501
+ // presented".
502
+ style.width = '100%';
503
+ style.height = '100%';
496
504
  } else {
497
505
  // If we have both text and nested cues, then style everything; otherwise
498
506
  // place the text in its own <span> so the background doesn't fill the
@@ -173,6 +173,30 @@ shaka.util.Platform = class {
173
173
  shaka.util.Platform.userAgentContains_('Android');
174
174
  }
175
175
 
176
+ /**
177
+ * Returns a major version number for Chrome, or Chromium-based browsers.
178
+ *
179
+ * For example:
180
+ * - Chrome 106.0.5249.61 returns 106.
181
+ * - Edge 106.0.1370.34 returns 106 (since this is based on Chromium).
182
+ * - Safari returns null (since this is independent of Chromium).
183
+ *
184
+ * @return {?number} A major version number or null if not Chromium-based.
185
+ */
186
+ static chromeVersion() {
187
+ if (!shaka.util.Platform.userAgentContains_('Chrome')) {
188
+ return null;
189
+ }
190
+
191
+ // Looking for something like "Chrome/106.0.0.0".
192
+ const match = navigator.userAgent.match(/Chrome\/(\d+)/);
193
+ if (match) {
194
+ return parseInt(match[1], /* base= */ 10);
195
+ }
196
+
197
+ return null;
198
+ }
199
+
176
200
  /**
177
201
  * Check if the current platform is Google Chrome.
178
202
  *
@@ -75,6 +75,10 @@ shaka.util.PlayerConfiguration = class {
75
75
  updateExpirationTime: 1,
76
76
  preferredKeySystems: [],
77
77
  keySystemsMapping: {},
78
+ // The Xbox One browser does not detect DRM key changes signalled by a
79
+ // change in the PSSH in media segments. We need to parse PSSH from media
80
+ // segments to detect key changes.
81
+ parseInbandPsshEnabled: shaka.util.Platform.isXboxOne(),
78
82
  };
79
83
 
80
84
  const manifest = {
@@ -321,6 +325,7 @@ shaka.util.PlayerConfiguration = class {
321
325
  */
322
326
  static mergeConfigObjects(destination, updates, template) {
323
327
  const overrides = {
328
+ '.drm.keySystemsMapping': '',
324
329
  '.drm.servers': '',
325
330
  '.drm.clearKeys': '',
326
331
  '.drm.advanced': {
package/lib/util/pssh.js CHANGED
@@ -42,11 +42,12 @@ shaka.util.Pssh = class {
42
42
 
43
43
  new shaka.util.Mp4Parser()
44
44
  .box('moov', shaka.util.Mp4Parser.children)
45
+ .box('moof', shaka.util.Mp4Parser.children)
45
46
  .fullBox('pssh', (box) => this.parsePsshBox_(box))
46
47
  .parse(psshBox);
47
48
 
48
49
  if (this.data.length == 0) {
49
- shaka.log.warning('No pssh box found!');
50
+ shaka.log.v2('No pssh box found!');
50
51
  }
51
52
  }
52
53
 
@@ -474,7 +474,8 @@ shaka.util.StreamUtils = class {
474
474
  if (shaka.util.Platform.isXboxOne() && video &&
475
475
  ((video.width && video.width > 1920) ||
476
476
  (video.height && video.height > 1080)) &&
477
- video.codecs.includes('avc1.')) {
477
+ (video.codecs.includes('avc1.') ||
478
+ video.codecs.includes('avc3.'))) {
478
479
  shaka.log.debug('Dropping variant - not compatible with platform',
479
480
  shaka.util.StreamUtils.getVariantSummaryString_(variant));
480
481
  return false;
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.1.3",
4
+ "version": "4.1.5",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -295,5 +295,93 @@ describe('Cea608Memory', () => {
295
295
  const caption = memory.forceEmit(startTime, endTime);
296
296
  expect(caption).toEqual(expectedCaption);
297
297
  });
298
+
299
+ it('does not move rows if source row index is negative', () => {
300
+ const startTime = 1;
301
+ const endTime = 2;
302
+ const text = 'test';
303
+
304
+ // Add the text to the buffer, each character on separate rows.
305
+ // At this point, the memory looks like:
306
+ // [1]: t
307
+ // [2]: e
308
+ // [3]: s
309
+ // [4]: t
310
+ for (const c of text) {
311
+ memory.addChar(CharSet.BASIC_NORTH_AMERICAN,
312
+ c.charCodeAt(0));
313
+ memory.setRow(memory.getRow() + 1); // increment row
314
+ }
315
+
316
+ const srcRowIdx = -1;
317
+ const dstRowIdx = 2;
318
+ const rowsToMove = 3;
319
+ memory.moveRows(dstRowIdx, srcRowIdx, rowsToMove);
320
+
321
+ // Expected text is 't\ne\ns\nt'
322
+ const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
323
+ topLevelCue.nestedCues = [
324
+ CeaUtils.createDefaultCue(startTime, endTime, 't'),
325
+ CeaUtils.createLineBreakCue(startTime, endTime),
326
+ CeaUtils.createDefaultCue(startTime, endTime, 'e'),
327
+ CeaUtils.createLineBreakCue(startTime, endTime),
328
+ CeaUtils.createDefaultCue(startTime, endTime, 's'),
329
+ CeaUtils.createLineBreakCue(startTime, endTime),
330
+ CeaUtils.createDefaultCue(startTime, endTime, 't'),
331
+ ];
332
+
333
+ const expectedCaption = {
334
+ stream,
335
+ cue: topLevelCue,
336
+ };
337
+
338
+ // Force out the new memory.
339
+ const caption = memory.forceEmit(startTime, endTime);
340
+ expect(caption).toEqual(expectedCaption);
341
+ });
342
+
343
+ it('does not move rows if destination row index is negative', () => {
344
+ const startTime = 1;
345
+ const endTime = 2;
346
+ const text = 'test';
347
+
348
+ // Add the text to the buffer, each character on separate rows.
349
+ // At this point, the memory looks like:
350
+ // [1]: t
351
+ // [2]: e
352
+ // [3]: s
353
+ // [4]: t
354
+ for (const c of text) {
355
+ memory.addChar(CharSet.BASIC_NORTH_AMERICAN,
356
+ c.charCodeAt(0));
357
+ memory.setRow(memory.getRow() + 1); // increment row
358
+ }
359
+
360
+ const srcRowIdx = 1;
361
+ const dstRowIdx = -2;
362
+ const rowsToMove = 3;
363
+ memory.moveRows(dstRowIdx, srcRowIdx, rowsToMove);
364
+
365
+ // Expected text is 't\ne\ns\nt'
366
+ const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
367
+ topLevelCue.nestedCues = [
368
+ CeaUtils.createDefaultCue(startTime, endTime, 't'),
369
+ CeaUtils.createLineBreakCue(startTime, endTime),
370
+ CeaUtils.createDefaultCue(startTime, endTime, 'e'),
371
+ CeaUtils.createLineBreakCue(startTime, endTime),
372
+ CeaUtils.createDefaultCue(startTime, endTime, 's'),
373
+ CeaUtils.createLineBreakCue(startTime, endTime),
374
+ CeaUtils.createDefaultCue(startTime, endTime, 't'),
375
+ ];
376
+
377
+ const expectedCaption = {
378
+ stream,
379
+ cue: topLevelCue,
380
+ };
381
+
382
+ // Force out the new memory.
383
+ const caption = memory.forceEmit(startTime, endTime);
384
+ expect(caption).toEqual(expectedCaption);
385
+ });
298
386
  });
299
387
  });
@@ -43,6 +43,7 @@ describe('DashParser ContentProtection', () => {
43
43
  isLowLatencyMode: () => false,
44
44
  isAutoLowLatencyMode: () => false,
45
45
  enableLowLatencyMode: () => {},
46
+ updateDuration: () => {},
46
47
  };
47
48
 
48
49
  const actual = await dashParser.start(
@@ -36,6 +36,7 @@ describe('DashParser Live', () => {
36
36
  isLowLatencyMode: () => false,
37
37
  isAutoLowLatencyMode: () => false,
38
38
  enableLowLatencyMode: () => {},
39
+ updateDuration: () => {},
39
40
  };
40
41
  });
41
42
 
@@ -52,6 +52,7 @@ describe('DashParser Manifest', () => {
52
52
  isLowLatencyMode: () => false,
53
53
  isAutoLowLatencyMode: () => false,
54
54
  enableLowLatencyMode: () => {},
55
+ updateDuration: () => {},
55
56
  };
56
57
  });
57
58
 
@@ -38,6 +38,7 @@ describe('DashParser SegmentBase', () => {
38
38
  isLowLatencyMode: () => false,
39
39
  isAutoLowLatencyMode: () => false,
40
40
  enableLowLatencyMode: () => {},
41
+ updateDuration: () => {},
41
42
  };
42
43
  });
43
44
 
@@ -348,6 +348,7 @@ describe('DashParser SegmentList', () => {
348
348
  isLowLatencyMode: () => false,
349
349
  isAutoLowLatencyMode: () => false,
350
350
  enableLowLatencyMode: () => {},
351
+ updateDuration: () => {},
351
352
  };
352
353
  const manifest = await dashParser.start('dummy://foo', playerInterface);
353
354
  const stream = manifest.variants[0].video;
@@ -47,6 +47,7 @@ describe('DashParser SegmentTemplate', () => {
47
47
  isLowLatencyMode: () => false,
48
48
  isAutoLowLatencyMode: () => false,
49
49
  enableLowLatencyMode: () => {},
50
+ updateDuration: () => {},
50
51
  };
51
52
  });
52
53