shaka-player 4.2.3 → 4.2.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 (82) hide show
  1. package/AUTHORS +2 -0
  2. package/CHANGELOG.md +39 -0
  3. package/CONTRIBUTORS +2 -0
  4. package/demo/index.html +1 -1
  5. package/dist/demo.compiled.debug.js +64 -64
  6. package/dist/demo.compiled.debug.map +2 -2
  7. package/dist/demo.compiled.js +64 -64
  8. package/dist/demo.compiled.map +2 -2
  9. package/dist/deps.js +5 -4
  10. package/dist/locales.js +32 -32
  11. package/dist/receiver.compiled.debug.js +12 -12
  12. package/dist/receiver.compiled.debug.map +2 -2
  13. package/dist/receiver.compiled.js +12 -12
  14. package/dist/receiver.compiled.map +2 -2
  15. package/dist/shaka-player.compiled.d.ts +15 -1
  16. package/dist/shaka-player.compiled.debug.d.ts +15 -1
  17. package/dist/shaka-player.compiled.debug.externs.js +95 -80
  18. package/dist/shaka-player.compiled.debug.js +583 -573
  19. package/dist/shaka-player.compiled.debug.map +4 -4
  20. package/dist/shaka-player.compiled.externs.js +95 -80
  21. package/dist/shaka-player.compiled.js +823 -820
  22. package/dist/shaka-player.compiled.map +4 -4
  23. package/dist/shaka-player.ui.d.ts +15 -1
  24. package/dist/shaka-player.ui.debug.d.ts +15 -1
  25. package/dist/shaka-player.ui.debug.externs.js +95 -80
  26. package/dist/shaka-player.ui.debug.js +612 -602
  27. package/dist/shaka-player.ui.debug.map +4 -4
  28. package/dist/shaka-player.ui.externs.js +95 -80
  29. package/dist/shaka-player.ui.js +963 -960
  30. package/dist/shaka-player.ui.map +4 -4
  31. package/docs/tutorials/ui.md +7 -7
  32. package/docs/tutorials/welcome.md +2 -2
  33. package/externs/mux.js +3 -0
  34. package/externs/shaka/player.js +2 -1
  35. package/lib/cast/cast_proxy.js +2 -1
  36. package/lib/cast/cast_receiver.js +19 -5
  37. package/lib/cast/cast_sender.js +2 -1
  38. package/lib/cast/cast_utils.js +16 -6
  39. package/lib/cea/cea708_window.js +2 -2
  40. package/lib/dash/segment_template.js +1 -1
  41. package/lib/hls/hls_parser.js +21 -4
  42. package/lib/media/content_workarounds.js +12 -0
  43. package/lib/media/media_source_engine.js +6 -0
  44. package/lib/media/playhead.js +23 -1
  45. package/lib/media/streaming_engine.js +3 -12
  46. package/lib/media/transmuxer.js +6 -0
  47. package/lib/player.js +24 -11
  48. package/lib/polyfill/abort_controller.js +143 -0
  49. package/lib/polyfill/media_capabilities.js +59 -9
  50. package/lib/text/ttml_text_parser.js +32 -7
  51. package/lib/text/ui_text_displayer.js +22 -5
  52. package/lib/util/manifest_parser_utils.js +4 -0
  53. package/lib/util/platform.js +9 -1
  54. package/lib/util/player_configuration.js +10 -3
  55. package/lib/util/stream_utils.js +30 -10
  56. package/package.json +1 -1
  57. package/roadmap.md +24 -6
  58. package/shaka-player.uncompiled.js +1 -0
  59. package/test/cast/cast_receiver_integration.js +97 -16
  60. package/test/cast/cast_receiver_unit.js +17 -12
  61. package/test/cast/cast_utils_unit.js +1 -0
  62. package/test/dash/dash_parser_manifest_unit.js +1 -0
  63. package/test/hls/hls_parser_unit.js +1 -0
  64. package/test/media/drm_engine_unit.js +11 -1
  65. package/test/media/media_source_engine_integration.js +108 -1
  66. package/test/media/playhead_unit.js +41 -0
  67. package/test/media/streaming_engine_integration.js +13 -0
  68. package/test/media/streaming_engine_unit.js +36 -23
  69. package/test/player_unit.js +87 -0
  70. package/test/polyfill/media_capabilities_unit.js +261 -0
  71. package/test/test/util/simple_fakes.js +3 -0
  72. package/test/test/util/stream_generator.js +212 -8
  73. package/test/test/util/test_scheme.js +3 -2
  74. package/test/test/util/util.js +3 -2
  75. package/test/text/mp4_ttml_parser_unit.js +6 -4
  76. package/test/text/ttml_text_parser_unit.js +119 -86
  77. package/test/text/ui_text_displayer_unit.js +76 -0
  78. package/test/ui/text_displayer_layout_unit.js +14 -0
  79. package/test/util/stream_utils_unit.js +18 -0
  80. package/test/util/xml_utils_unit.js +6 -1
  81. package/ui/controls.js +49 -37
  82. package/ui/settings_menu.js +11 -2
@@ -30,7 +30,7 @@ Set up controls with HTML data attributes:
30
30
  The data-shaka-player-cast-receiver-id tag allows you to provide a Cast Application ID that
31
31
  the cast button will cast to; the value provided here is the sample cast receiver. -->
32
32
  <div data-shaka-player-container style="max-width:40em"
33
- data-shaka-player-cast-receiver-id="07AEE832">
33
+ data-shaka-player-cast-receiver-id="A67E4E9A">
34
34
  <!-- The data-shaka-player tag will make the UI library use this video element.
35
35
  If no video is provided, the UI will automatically make one inside the container div. -->
36
36
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"></video>
@@ -111,7 +111,7 @@ set up a listener for the 'caststatuschanged' events.
111
111
  <!-- Add a data-shaka-player-cast-receiver-id tag to provide a Cast Application ID that
112
112
  the cast button will cast to; the value provided here is the sample cast receiver. -->
113
113
  <div data-shaka-player-container style="max-width:40em"
114
- data-shaka-player-cast-receiver-id="07AEE832">
114
+ data-shaka-player-cast-receiver-id="A67E4E9A">
115
115
  </div>
116
116
  ```
117
117
 
@@ -139,7 +139,7 @@ casting to an Android receiver app.
139
139
 
140
140
  ```html
141
141
  <div data-shaka-player-container style="max-width:40em"
142
- data-shaka-player-cast-receiver-id="E7271BEC"
142
+ data-shaka-player-cast-receiver-id="A67E4E9A"
143
143
  data-shaka-player-cast-android-receiver-compatible="true">
144
144
  <!-- The manifest url in the src attribute will be automatically loaded -->
145
145
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"
@@ -154,7 +154,7 @@ or a `<source>` tag inside it to enable auto loading of the specified content.
154
154
 
155
155
  ```html
156
156
  <div data-shaka-player-container style="max-width:40em"
157
- data-shaka-player-cast-receiver-id="07AEE832">
157
+ data-shaka-player-cast-receiver-id="A67E4E9A">
158
158
  <!-- The manifest url in the src attribute will be automatically loaded -->
159
159
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%"
160
160
  src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"></video>
@@ -165,7 +165,7 @@ or
165
165
 
166
166
  ```html
167
167
  <div data-shaka-player-container style="max-width:40em"
168
- data-shaka-player-cast-receiver-id="07AEE832">
168
+ data-shaka-player-cast-receiver-id="A67E4E9A">
169
169
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%">
170
170
  <!-- The manifest url in the src attribute will be auto loaded -->
171
171
  <source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
@@ -178,7 +178,7 @@ call to the first one fails.
178
178
 
179
179
  ```html
180
180
  <div data-shaka-player-container style="max-width:40em"
181
- data-shaka-player-cast-receiver-id="07AEE832">
181
+ data-shaka-player-cast-receiver-id="A67E4E9A">
182
182
  <video autoplay data-shaka-player id="video" style="width:100%;height:100%">
183
183
  <!-- Try this first -->
184
184
  <source src="https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd"/>
@@ -222,7 +222,7 @@ const video = controls.getVideo();
222
222
  // Receiver Compatability.
223
223
  ui.configure({
224
224
  // Set the castReceiverAppId
225
- 'castReceiverAppId': '07AEE832',
225
+ 'castReceiverAppId': 'A67E4E9A',
226
226
  // Enable casting to native Android Apps (e.g. Android TV Apps)
227
227
  'castAndroidReceiverCompatible': true,
228
228
  });
@@ -29,8 +29,8 @@ You can build Shaka on Linux, Windows, or Mac.
29
29
  To get the sources and compile the library, you will need:
30
30
  * {@link https://git-scm.com/downloads Git v1.9+}
31
31
  * {@link https://www.python.org/downloads/ Python v2.7 or v3.5+}
32
- * {@link https://java.com/en/download/ Java Runtime Environment v8+}
33
- * {@link https://nodejs.org/en/download/ NodeJS v10+}
32
+ * {@link https://java.com/en/download/ Java Runtime Environment v14+}
33
+ * {@link https://nodejs.org/en/download/ NodeJS v14+}
34
34
  * A local web server, such as {@link https://httpd.apache.org/ Apache}
35
35
  * _NOTE: A local web server is required because browsers place restrictions
36
36
  on applications from file:/// URLs._
package/externs/mux.js CHANGED
@@ -74,6 +74,9 @@ muxjs.mp4.Transmuxer = class {
74
74
 
75
75
  /** Remove all handlers and clean up. */
76
76
  dispose() {}
77
+
78
+ /** Reset captions. */
79
+ resetCaptions() {}
77
80
  };
78
81
 
79
82
 
@@ -131,7 +131,8 @@ shaka.extern.StateChange;
131
131
  * @property {number} manifestTimeSeconds
132
132
  * The amount of time it took to download and parse the manifest.
133
133
  * @property {number} drmTimeSeconds
134
- * The amount of time it took to download the first drm key.
134
+ * The amount of time it took to download the first drm key, and load that key
135
+ * into the drm system.
135
136
  * @property {number} playTime
136
137
  * The total time spent in a playing state in seconds.
137
138
  * @property {number} pauseTime
@@ -705,7 +705,8 @@ shaka.cast.CastProxy = class extends shaka.util.FakeEventTarget {
705
705
  // If we are casting, but the first update has not come in yet, use local
706
706
  // getters, but not local methods.
707
707
  if (this.sender_.isCasting() && !this.sender_.hasRemoteProperties()) {
708
- if (shaka.cast.CastUtils.PlayerGetterMethods[name]) {
708
+ if (shaka.cast.CastUtils.PlayerGetterMethods[name] ||
709
+ shaka.cast.CastUtils.LargePlayerGetterMethods[name]) {
709
710
  const value = /** @type {Object} */(this.localPlayer_)[name];
710
711
  goog.asserts.assert(typeof value == 'function',
711
712
  'only methods on Player');
@@ -518,6 +518,25 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
518
518
  update['video']['muted'] = systemVolume.muted;
519
519
  }
520
520
 
521
+ this.sendMessage_({
522
+ 'type': 'update',
523
+ 'update': update,
524
+ }, this.shakaBus_);
525
+
526
+ // Getters with large outputs each get sent in their own update message.
527
+ for (const name in shaka.cast.CastUtils.LargePlayerGetterMethods) {
528
+ const frequency = shaka.cast.CastUtils.LargePlayerGetterMethods[name];
529
+ if (this.updateNumber_ % frequency == 0) {
530
+ const update = {'player': {}};
531
+ update['player'][name] = /** @type {Object} */ (this.player_)[name]();
532
+
533
+ this.sendMessage_({
534
+ 'type': 'update',
535
+ 'update': update,
536
+ }, this.shakaBus_);
537
+ }
538
+ }
539
+
521
540
  // Only start progressing the update number once data is loaded,
522
541
  // just in case any of the "rarely changing" properties with less frequent
523
542
  // update messages changes significantly during the loading process.
@@ -525,11 +544,6 @@ shaka.cast.CastReceiver = class extends shaka.util.FakeEventTarget {
525
544
  this.updateNumber_ += 1;
526
545
  }
527
546
 
528
- this.sendMessage_({
529
- 'type': 'update',
530
- 'update': update,
531
- }, this.shakaBus_);
532
-
533
547
  this.maybeSendMediaInfoMessage_();
534
548
  }
535
549
 
@@ -370,7 +370,8 @@ shaka.cast.CastSender = class {
370
370
  return (...args) =>
371
371
  this.remoteAsyncCall_(targetName, property, ...args);
372
372
  }
373
- if (CastUtils.PlayerGetterMethods[property]) {
373
+ if (CastUtils.PlayerGetterMethods[property] ||
374
+ CastUtils.LargePlayerGetterMethods[property]) {
374
375
  return () => this.propertyGetter_(targetName, property);
375
376
  }
376
377
  }
@@ -307,9 +307,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
307
307
  'getAudioLanguagesAndRoles': 4,
308
308
  'getBufferFullness': 1,
309
309
  'getBufferedInfo': 2,
310
- // NOTE: The 'getSharedConfiguration' property is not proxied as it would
311
- // not be possible to share a reference.
312
- 'getConfiguration': 4,
313
310
  'getExpiration': 2,
314
311
  'getKeyStatuses': 2,
315
312
  // NOTE: The 'getManifest' property is not proxied, as it is very large.
@@ -318,9 +315,6 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
318
315
  'getPlaybackRate': 2,
319
316
  'getTextLanguages': 4,
320
317
  'getTextLanguagesAndRoles': 4,
321
- 'getTextTracks': 2,
322
- 'getStats': 5,
323
- 'getVariantTracks': 2,
324
318
  'getImageTracks': 2,
325
319
  'getThumbnails': 2,
326
320
  'isAudioOnly': 10,
@@ -334,6 +328,22 @@ shaka.cast.CastUtils.PlayerGetterMethods = {
334
328
  };
335
329
 
336
330
 
331
+ /**
332
+ * Player getter methods with data large enough to be sent in their own update
333
+ * messages, to reduce the size of each message. The format of this is
334
+ * identical to PlayerGetterMethods.
335
+ * @const {!Object.<string, number>}
336
+ */
337
+ shaka.cast.CastUtils.LargePlayerGetterMethods = {
338
+ // NOTE: The 'getSharedConfiguration' property is not proxied as it would
339
+ // not be possible to share a reference.
340
+ 'getConfiguration': 4,
341
+ 'getStats': 5,
342
+ 'getTextTracks': 2,
343
+ 'getVariantTracks': 2,
344
+ };
345
+
346
+
337
347
  /**
338
348
  * Player getter methods that are proxied while casting, but only when casting
339
349
  * a livestream.
@@ -420,7 +420,7 @@ shaka.cea.Cea708Window.TextJustification = {
420
420
  shaka.cea.Cea708Window.MAX_COLS = 42;
421
421
 
422
422
  /**
423
- * Maximum of 15 rows that can be indexed from 0 to 14.
423
+ * Maximum of 16 rows that can be indexed from 0 to 15.
424
424
  * @private @const {number}
425
425
  */
426
- shaka.cea.Cea708Window.MAX_ROWS = 15;
426
+ shaka.cea.Cea708Window.MAX_ROWS = 16;
@@ -67,7 +67,7 @@ shaka.dash.SegmentTemplate = class {
67
67
  },
68
68
  };
69
69
  } else if (info.segmentDuration) {
70
- if (!isUpdate) {
70
+ if (!isUpdate && context.adaptationSet.contentType !== 'image') {
71
71
  context.presentationTimeline.notifyMaxSegmentDuration(
72
72
  info.segmentDuration);
73
73
  context.presentationTimeline.notifyMinSegmentStartTime(
@@ -831,13 +831,19 @@ shaka.hls.HlsParser = class {
831
831
  shaka.util.ArrayUtils.remove(allCodecs, textCodecs);
832
832
  }
833
833
  if (audioGroupId) {
834
- const codecs = shaka.util.ManifestParserUtils.guessCodecs(
834
+ let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
835
835
  ContentType.AUDIO, allCodecs);
836
+ if (!codecs) {
837
+ codecs = this.config_.hls.defaultAudioCodec;
838
+ }
836
839
  this.groupIdToCodecsMap_.set(audioGroupId, codecs);
837
840
  }
838
841
  if (videoGroupId) {
839
- const codecs = shaka.util.ManifestParserUtils.guessCodecs(
842
+ let codecs = shaka.util.ManifestParserUtils.guessCodecsSafe(
840
843
  ContentType.VIDEO, allCodecs);
844
+ if (!codecs) {
845
+ codecs = this.config_.hls.defaultVideoCodec;
846
+ }
841
847
  this.groupIdToCodecsMap_.set(videoGroupId, codecs);
842
848
  }
843
849
  }
@@ -1043,7 +1049,7 @@ shaka.hls.HlsParser = class {
1043
1049
  shaka.log.debug('Guessing audio-only.');
1044
1050
  type = ContentType.AUDIO;
1045
1051
  ignoreStream = true;
1046
- } else if (res.video.length) {
1052
+ } else if (res.video.length && !res.audio.length) {
1047
1053
  // There are associated video streams. Assume this is audio.
1048
1054
  shaka.log.debug('Guessing audio-only.');
1049
1055
  type = ContentType.AUDIO;
@@ -1659,9 +1665,16 @@ shaka.hls.HlsParser = class {
1659
1665
  /** @type {!Map.<number, number>} */
1660
1666
  const mediaSequenceToStartTime = new Map();
1661
1667
 
1668
+ const prevLowLatencyMode = this.lowLatencyMode_;
1662
1669
  const segments = this.createSegments_(verbatimMediaPlaylistUri, playlist,
1663
1670
  type, mimeType, mediaSequenceToStartTime, mediaVariables, codecs);
1664
1671
 
1672
+ // This happens when autoLowLatencyMode is true, so we need set the
1673
+ // correct lowLatencyPresentationDelay_
1674
+ if (prevLowLatencyMode != this.lowLatencyMode_) {
1675
+ this.determinePresentationType_(playlist);
1676
+ }
1677
+
1665
1678
  const lastEndTime = segments[segments.length - 1].endTime;
1666
1679
  /** @type {!shaka.media.SegmentIndex} */
1667
1680
  const segmentIndex = new shaka.media.SegmentIndex(segments);
@@ -1790,8 +1803,11 @@ shaka.hls.HlsParser = class {
1790
1803
  shaka.util.Error.Code.HLS_AES_128_INVALID_KEY_LENGTH);
1791
1804
  }
1792
1805
 
1806
+ const algorithm = {
1807
+ name: 'AES-CBC',
1808
+ };
1793
1809
  keyInfo.cryptoKey = await window.crypto.subtle.importKey(
1794
- 'raw', keyResponse.data, 'AES-CBC', true, ['decrypt']);
1810
+ 'raw', keyResponse.data, algorithm, true, ['decrypt']);
1795
1811
  keyInfo.fetchKey = undefined; // No longer needed.
1796
1812
  };
1797
1813
 
@@ -2872,6 +2888,7 @@ shaka.hls.HlsParser.TEXT_EXTENSIONS_TO_MIME_TYPES_ = {
2872
2888
  'm4f': 'application/mp4',
2873
2889
  'cmft': 'application/mp4',
2874
2890
  'vtt': 'text/vtt',
2891
+ 'webvtt': 'txt/vtt',
2875
2892
  'ttml': 'application/ttml+xml',
2876
2893
  };
2877
2894
 
@@ -69,6 +69,18 @@ shaka.media.ContentWorkarounds = class {
69
69
  })
70
70
  .fullBox('encv', onEncryptionMetadataBox)
71
71
  .fullBox('enca', onEncryptionMetadataBox)
72
+ .fullBox('hev1', (box) => {
73
+ boxesToModify.push({
74
+ box,
75
+ newType: ContentWorkarounds.BOX_TYPE_ENCV_,
76
+ });
77
+ })
78
+ .fullBox('hvc1', (box) => {
79
+ boxesToModify.push({
80
+ box,
81
+ newType: ContentWorkarounds.BOX_TYPE_ENCV_,
82
+ });
83
+ })
72
84
  .fullBox('avc1', (box) => {
73
85
  boxesToModify.push({
74
86
  box,
@@ -545,6 +545,12 @@ shaka.media.MediaSourceEngine = class {
545
545
  }
546
546
 
547
547
  if (this.transmuxers_[contentType]) {
548
+ // When seeked we should reset the transmuxer captionstreams
549
+ // so it does not ignores the captions from previous segments
550
+ if (seeked) {
551
+ this.transmuxers_[contentType].resetCaptions();
552
+ }
553
+
548
554
  const transmuxedData =
549
555
  await this.transmuxers_[contentType].transmux(data);
550
556
  // For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
33
33
  * @interface
34
34
  */
35
35
  shaka.media.Playhead = class {
36
+ /**
37
+ * Called when the Player is ready to begin playback. Anything that depends
38
+ * on setStartTime() should be done here, not in the constructor.
39
+ *
40
+ * @see https://github.com/shaka-project/shaka-player/issues/4244
41
+ */
42
+ ready() {}
43
+
36
44
  /**
37
45
  * Set the start time. If the content has already started playback, this will
38
46
  * be ignored.
@@ -90,6 +98,14 @@ shaka.media.SrcEqualsPlayhead = class {
90
98
 
91
99
  /** @private {shaka.util.EventManager} */
92
100
  this.eventManager_ = new shaka.util.EventManager();
101
+ }
102
+
103
+ /** @override */
104
+ ready() {
105
+ goog.asserts.assert(
106
+ this.mediaElement_ != null,
107
+ 'Playhead should not be released before calling ready()',
108
+ );
93
109
 
94
110
  // We listen for the loaded-data-event so that we know when we can
95
111
  // interact with |currentTime|.
@@ -102,6 +118,7 @@ shaka.media.SrcEqualsPlayhead = class {
102
118
  this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
103
119
  this.started_ = true;
104
120
  });
121
+
105
122
  const currentTime = this.mediaElement_.currentTime;
106
123
  // Using the currentTime allows using a negative number in Live HLS
107
124
  const newTime = Math.max(0, currentTime + this.startTime_);
@@ -239,7 +256,12 @@ shaka.media.MediaSourcePlayhead = class {
239
256
  /** @type {shaka.util.Timer} */
240
257
  this.checkWindowTimer_ = new shaka.util.Timer(() => {
241
258
  this.onPollWindow_();
242
- }).tickEvery(/* seconds= */ 0.25);
259
+ });
260
+ }
261
+
262
+ /** @override */
263
+ ready() {
264
+ this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
243
265
  }
244
266
 
245
267
  /** @override */
@@ -1403,16 +1403,6 @@ shaka.media.StreamingEngine = class {
1403
1403
  this.mediaStates_.delete(ContentType.TEXT);
1404
1404
  } else if (error.code == shaka.util.Error.Code.QUOTA_EXCEEDED_ERROR) {
1405
1405
  this.handleQuotaExceeded_(mediaState, error);
1406
- } else if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS &&
1407
- error.data && error.data[1] == 404) {
1408
- // The segment could not be found, does not exist, or is not available.
1409
- // In any case just try again.
1410
- // The current segment is not available. Schedule another update to
1411
- // fetch the segment again.
1412
- shaka.log.v2(logPrefix, 'segment not available.');
1413
- mediaState.performingUpdate = false;
1414
- mediaState.updateTimer = null;
1415
- this.scheduleUpdate_(mediaState, 1);
1416
1406
  } else {
1417
1407
  shaka.log.error(logPrefix, 'failed fetch and append: code=' +
1418
1408
  error.code);
@@ -1455,9 +1445,10 @@ shaka.media.StreamingEngine = class {
1455
1445
 
1456
1446
  /**
1457
1447
  * Clear per-stream error states and retry any failed streams.
1448
+ * @param {number} delaySeconds
1458
1449
  * @return {boolean} False if unable to retry.
1459
1450
  */
1460
- retry() {
1451
+ retry(delaySeconds) {
1461
1452
  if (this.destroyer_.destroyed()) {
1462
1453
  shaka.log.error('Unable to retry after StreamingEngine is destroyed!');
1463
1454
  return false;
@@ -1474,7 +1465,7 @@ shaka.media.StreamingEngine = class {
1474
1465
  if (mediaState.hasError) {
1475
1466
  shaka.log.info(logPrefix, 'Retrying after failure...');
1476
1467
  mediaState.hasError = false;
1477
- this.scheduleUpdate_(mediaState, 0.1);
1468
+ this.scheduleUpdate_(mediaState, delaySeconds);
1478
1469
  }
1479
1470
  }
1480
1471
 
@@ -182,6 +182,12 @@ shaka.media.Transmuxer = class {
182
182
  return this.transmuxPromise_;
183
183
  }
184
184
 
185
+ /**
186
+ * Reset captions from Transport stream to MP4, using the mux.js library.
187
+ */
188
+ resetCaptions() {
189
+ this.muxTransmuxer_.resetCaptions();
190
+ }
185
191
 
186
192
  /**
187
193
  * Handles the 'data' event of the transmuxer.
package/lib/player.js CHANGED
@@ -2062,6 +2062,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2062
2062
  shaka.util.StreamUtils.variantToTrack(initialVariant));
2063
2063
  }
2064
2064
 
2065
+ this.playhead_.ready();
2066
+
2065
2067
  // Decide if text should be shown automatically.
2066
2068
  // similar to video/audio track, we would skip switch initial text track
2067
2069
  // if user already pick text track (via selectTextTrack api)
@@ -2399,6 +2401,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2399
2401
  HTMLMediaElement.HAVE_METADATA,
2400
2402
  this.loadEventManager_,
2401
2403
  () => {
2404
+ this.playhead_.ready();
2402
2405
  fullyLoaded.resolve();
2403
2406
  });
2404
2407
 
@@ -4841,12 +4844,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4841
4844
  * If the player has loaded content, and streaming seen an error, but the
4842
4845
  * could not resume streaming, this will return <code>false</code>.
4843
4846
  *
4847
+ * @param {number=} retryDelaySeconds
4844
4848
  * @return {boolean}
4845
4849
  * @export
4846
4850
  */
4847
- retryStreaming() {
4851
+ retryStreaming(retryDelaySeconds = 0.1) {
4848
4852
  return this.loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE ?
4849
- this.streamingEngine_.retry() :
4853
+ this.streamingEngine_.retry(retryDelaySeconds) :
4850
4854
  false;
4851
4855
  }
4852
4856
 
@@ -4942,17 +4946,26 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4942
4946
  * @private
4943
4947
  */
4944
4948
  defaultStreamingFailureCallback_(error) {
4945
- const retryErrorCodes = [
4946
- shaka.util.Error.Code.BAD_HTTP_STATUS,
4947
- shaka.util.Error.Code.HTTP_ERROR,
4948
- shaka.util.Error.Code.TIMEOUT,
4949
- ];
4949
+ // For live streams, we retry streaming automatically for certain errors.
4950
+ // For VOD streams, all streaming failures are fatal.
4951
+ if (!this.isLive()) {
4952
+ return;
4953
+ }
4950
4954
 
4951
- if (this.isLive() && retryErrorCodes.includes(error.code)) {
4952
- error.severity = shaka.util.Error.Severity.RECOVERABLE;
4955
+ let retryDelaySeconds = null;
4956
+ if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS ||
4957
+ error.code == shaka.util.Error.Code.HTTP_ERROR) {
4958
+ // These errors can be near-instant, so delay a bit before retrying.
4959
+ retryDelaySeconds = 1;
4960
+ } else if (error.code == shaka.util.Error.Code.TIMEOUT) {
4961
+ // We already waited for a timeout, so retry quickly.
4962
+ retryDelaySeconds = 0.1;
4963
+ }
4953
4964
 
4965
+ if (retryDelaySeconds != null) {
4966
+ error.severity = shaka.util.Error.Severity.RECOVERABLE;
4954
4967
  shaka.log.warning('Live streaming error. Retrying automatically...');
4955
- this.retryStreaming();
4968
+ this.retryStreaming(retryDelaySeconds);
4956
4969
  }
4957
4970
  }
4958
4971
 
@@ -6582,7 +6595,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6582
6595
  * @define {string} A version number taken from git at compile time.
6583
6596
  * @export
6584
6597
  */
6585
- shaka.Player.version = 'v4.2.3-uncompiled';
6598
+ shaka.Player.version = 'v4.2.5-uncompiled';
6586
6599
 
6587
6600
  // Initialize the deprecation system using the version string we just set
6588
6601
  // on the player.
@@ -0,0 +1,143 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.polyfill.AbortController');
8
+
9
+ goog.require('shaka.polyfill');
10
+ goog.require('shaka.util.FakeEvent');
11
+ goog.require('shaka.util.FakeEventTarget');
12
+
13
+ /**
14
+ * @summary A polyfill for systems that do not implement AbortController.
15
+ * This is used both with the fetch API for HTTP requests and inside the HLS
16
+ * parser.
17
+ * @export
18
+ * @extends AbortController
19
+ */
20
+ shaka.polyfill.AbortController = class {
21
+ /**
22
+ * Install the polyfill if needed.
23
+ * @export
24
+ */
25
+ static install() {
26
+ if (window.AbortController) {
27
+ // Not needed.
28
+ return;
29
+ }
30
+
31
+ window.AbortController = shaka.polyfill.AbortController;
32
+ window.AbortSignal = shaka.polyfill.AbortController.AbortSignal;
33
+ }
34
+
35
+ /** */
36
+ constructor() {
37
+ /** @private {!shaka.polyfill.AbortController.AbortSignal} */
38
+ this.signal_ = new shaka.polyfill.AbortController.AbortSignal();
39
+ }
40
+
41
+ /**
42
+ * @override
43
+ * @suppress {const|duplicate} Since the original is defined as "const", we
44
+ * need this suppression to override it.
45
+ */
46
+ get signal() {
47
+ return this.signal_;
48
+ }
49
+
50
+ /**
51
+ * @param {*=} reason
52
+ * @override
53
+ */
54
+ abort(reason) {
55
+ this.signal_.doAbort_(reason);
56
+ }
57
+ };
58
+
59
+ /**
60
+ * @summary A polyfill for AbortSignal, part of the AbortController API.
61
+ * @implements {AbortSignal}
62
+ */
63
+ shaka.polyfill.AbortController.AbortSignal =
64
+ class extends shaka.util.FakeEventTarget {
65
+ /** */
66
+ constructor() {
67
+ super();
68
+
69
+ /** @private {boolean} */
70
+ this.aborted_ = false;
71
+
72
+ /** @private {*} */
73
+ this.reason_ = undefined;
74
+
75
+ /** @type {?function(!Event)} */
76
+ this.onabort = null;
77
+ }
78
+
79
+ /** @override */
80
+ get aborted() {
81
+ return this.aborted_;
82
+ }
83
+
84
+ /** @return {*} */
85
+ get reason() {
86
+ return this.reason_;
87
+ }
88
+
89
+ /**
90
+ * @param {*} reason
91
+ * @private
92
+ */
93
+ doAbort_(reason) {
94
+ if (this.aborted_) {
95
+ return;
96
+ }
97
+
98
+ this.aborted_ = true;
99
+ this.reason_ = reason;
100
+ if (this.reason_ === undefined) {
101
+ // This is equivalent to a native implementation.
102
+ this.reason_ = new DOMException(
103
+ 'signal is aborted without reason', 'AbortError');
104
+ }
105
+
106
+ // According to MDN:
107
+ // "Event type - A generic Event with no added properties."
108
+ const event = new shaka.util.FakeEvent('abort');
109
+ if (this.onabort) {
110
+ this.onabort(event);
111
+ }
112
+ this.dispatchEvent(event);
113
+ }
114
+
115
+
116
+ /**
117
+ * @param {*=} reason
118
+ * @return {!AbortSignal}
119
+ */
120
+ static abort(reason) {
121
+ const signal = new shaka.polyfill.AbortController.AbortSignal();
122
+ signal.doAbort_(reason);
123
+ return signal;
124
+ }
125
+
126
+ /**
127
+ * @param {number} timeMs
128
+ * @return {!AbortSignal}
129
+ */
130
+ static timeout(timeMs) {
131
+ const signal = new shaka.polyfill.AbortController.AbortSignal();
132
+
133
+ window.setTimeout(() => {
134
+ // This is equivalent to a native implementation.
135
+ signal.doAbort_(new DOMException('signal timed out', 'TimeoutError'));
136
+ }, timeMs);
137
+
138
+ return signal;
139
+ }
140
+ };
141
+
142
+
143
+ shaka.polyfill.register(shaka.polyfill.AbortController.install);