rx-player 4.0.0-beta.1 → 4.0.0-beta.2

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 (169) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/CONTRIBUTING.md +48 -168
  3. package/FILES.md +40 -92
  4. package/VERSION +1 -1
  5. package/dist/_esm5.processed/compat/browser_detection.d.ts +3 -1
  6. package/dist/_esm5.processed/compat/browser_detection.js +7 -2
  7. package/dist/_esm5.processed/compat/eme/load_session.js +1 -1
  8. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.d.ts +21 -0
  9. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.js +26 -0
  10. package/dist/_esm5.processed/config.d.ts +2 -0
  11. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +5 -4
  12. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.d.ts +18 -1
  13. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.js +106 -25
  14. package/dist/_esm5.processed/core/adaptive/guess_based_chooser.js +6 -6
  15. package/dist/_esm5.processed/core/adaptive/network_analyzer.js +8 -5
  16. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.d.ts +19 -1
  17. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.js +1 -1
  18. package/dist/_esm5.processed/core/api/debug/render.js +1 -1
  19. package/dist/_esm5.processed/core/api/playback_observer.js +1 -0
  20. package/dist/_esm5.processed/core/api/public_api.d.ts +54 -1
  21. package/dist/_esm5.processed/core/api/public_api.js +232 -35
  22. package/dist/_esm5.processed/core/api/track_management/media_element_tracks_store.js +10 -1
  23. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.d.ts +13 -1
  24. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.js +30 -15
  25. package/dist/_esm5.processed/core/api/track_management/tracks_store.d.ts +3 -1
  26. package/dist/_esm5.processed/core/api/track_management/tracks_store.js +67 -152
  27. package/dist/_esm5.processed/core/api/utils.d.ts +10 -0
  28. package/dist/_esm5.processed/core/api/utils.js +20 -0
  29. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +7 -1
  30. package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +2 -0
  31. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +5 -1
  32. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
  33. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +47 -10
  34. package/dist/_esm5.processed/core/init/types.d.ts +9 -1
  35. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.d.ts +28 -1
  36. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +22 -9
  37. package/dist/_esm5.processed/core/init/utils/media_source_duration_updater.d.ts +58 -0
  38. package/dist/_esm5.processed/core/init/utils/{media_duration_updater.js → media_source_duration_updater.js} +84 -87
  39. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.d.ts +36 -2
  40. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +82 -2
  41. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -7
  42. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +31 -40
  43. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +8 -0
  44. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +12 -0
  45. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +8 -0
  46. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +12 -0
  47. package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +11 -4
  48. package/dist/_esm5.processed/core/segment_buffers/index.d.ts +2 -2
  49. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.d.ts +47 -0
  50. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.js +70 -0
  51. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +15 -8
  52. package/dist/_esm5.processed/core/stream/period/period_stream.js +1 -1
  53. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +22 -13
  54. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.d.ts +4 -2
  55. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +2 -2
  56. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.d.ts +3 -2
  57. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.js +8 -8
  58. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.d.ts +2 -2
  59. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.js +2 -3
  60. package/dist/_esm5.processed/default_config.d.ts +25 -0
  61. package/dist/_esm5.processed/default_config.js +27 -2
  62. package/dist/_esm5.processed/errors/index.d.ts +2 -2
  63. package/dist/_esm5.processed/errors/media_error.d.ts +23 -1
  64. package/dist/_esm5.processed/errors/media_error.js +18 -5
  65. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +1 -1
  66. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +8 -7
  67. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +17 -9
  68. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/index.js +0 -2
  69. package/dist/_esm5.processed/manifest/adaptation.d.ts +21 -2
  70. package/dist/_esm5.processed/manifest/adaptation.js +76 -1
  71. package/dist/_esm5.processed/manifest/manifest.js +1 -1
  72. package/dist/_esm5.processed/manifest/period.js +2 -2
  73. package/dist/_esm5.processed/manifest/representation.d.ts +33 -2
  74. package/dist/_esm5.processed/manifest/representation.js +21 -0
  75. package/dist/_esm5.processed/manifest/utils.js +1 -3
  76. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.d.ts +1 -1
  77. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.js +1 -1
  78. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +1 -0
  79. package/dist/_esm5.processed/public_types.d.ts +13 -3
  80. package/dist/_esm5.processed/tools/TextTrackRenderer/text_track_renderer.js +1 -1
  81. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.d.ts +4 -6
  82. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.js +4 -6
  83. package/dist/_esm5.processed/utils/is_null_or_undefined.d.ts +1 -1
  84. package/dist/_esm5.processed/utils/is_null_or_undefined.js +1 -1
  85. package/dist/mpd-parser.wasm +0 -0
  86. package/dist/rx-player.js +4709 -4218
  87. package/dist/rx-player.min.js +1 -1
  88. package/package.json +42 -36
  89. package/scripts/build/generate_build.js +1 -1
  90. package/scripts/fast_demo_build.js +4 -3
  91. package/scripts/generate_full_demo.js +1 -1
  92. package/sonar-project.properties +1 -1
  93. package/src/compat/browser_detection.ts +7 -1
  94. package/src/compat/eme/load_session.ts +1 -1
  95. package/src/compat/has_issues_with_high_media_source_duration.ts +27 -0
  96. package/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +147 -48
  97. package/src/core/adaptive/adaptive_representation_selector.ts +7 -4
  98. package/src/core/adaptive/buffer_based_chooser.ts +144 -26
  99. package/src/core/adaptive/guess_based_chooser.ts +9 -8
  100. package/src/core/adaptive/network_analyzer.ts +9 -4
  101. package/src/core/adaptive/utils/representation_score_calculator.ts +22 -2
  102. package/src/core/api/debug/render.ts +1 -1
  103. package/src/core/api/playback_observer.ts +1 -0
  104. package/src/core/api/public_api.ts +277 -44
  105. package/src/core/api/track_management/media_element_tracks_store.ts +17 -8
  106. package/src/core/api/track_management/track_dispatcher.ts +37 -14
  107. package/src/core/api/track_management/tracks_store.ts +77 -167
  108. package/src/core/api/utils.ts +26 -0
  109. package/src/core/decrypt/session_events_listener.ts +6 -1
  110. package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +2 -1
  111. package/src/core/decrypt/utils/loaded_sessions_store.ts +8 -1
  112. package/src/core/init/directfile_content_initializer.ts +1 -0
  113. package/src/core/init/media_source_content_initializer.ts +52 -9
  114. package/src/core/init/types.ts +9 -1
  115. package/src/core/init/utils/content_time_boundaries_observer.ts +46 -10
  116. package/src/core/init/utils/{media_duration_updater.ts → media_source_duration_updater.ts} +100 -112
  117. package/src/core/init/utils/rebuffering_controller.ts +114 -3
  118. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +56 -55
  119. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +16 -0
  120. package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +16 -0
  121. package/src/core/segment_buffers/implementations/types.ts +16 -4
  122. package/src/core/segment_buffers/index.ts +2 -0
  123. package/src/core/stream/adaptation/utils/create_representation_estimator.ts +114 -0
  124. package/src/core/stream/orchestrator/stream_orchestrator.ts +16 -8
  125. package/src/core/stream/period/period_stream.ts +2 -1
  126. package/src/core/stream/representation/representation_stream.ts +34 -22
  127. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +8 -3
  128. package/src/core/stream/representation/utils/push_init_segment.ts +11 -6
  129. package/src/core/stream/representation/utils/push_media_segment.ts +3 -3
  130. package/src/default_config.ts +29 -2
  131. package/src/errors/__tests__/media_error.test.ts +6 -6
  132. package/src/errors/index.ts +4 -1
  133. package/src/errors/media_error.ts +67 -1
  134. package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +10 -7
  135. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +17 -6
  136. package/src/experimental/tools/mediaCapabilitiesProber/index.ts +0 -4
  137. package/src/manifest/__tests__/manifest.test.ts +7 -7
  138. package/src/manifest/__tests__/period.test.ts +90 -45
  139. package/src/manifest/adaptation.ts +89 -1
  140. package/src/manifest/manifest.ts +1 -1
  141. package/src/manifest/period.ts +4 -2
  142. package/src/manifest/representation.ts +67 -1
  143. package/src/manifest/utils.ts +1 -3
  144. package/src/parsers/manifest/dash/js-parser/parse_from_document.ts +1 -1
  145. package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +1 -0
  146. package/src/parsers/texttracks/ttml/parse_ttml.ts +1 -1
  147. package/src/public_types.ts +16 -1
  148. package/src/tools/TextTrackRenderer/text_track_renderer.ts +1 -1
  149. package/src/transports/smooth/isobmff/create_boxes.ts +4 -6
  150. package/src/typings/globals.d.ts +20 -20
  151. package/src/utils/is_null_or_undefined.ts +1 -1
  152. package/dist/_esm5.processed/core/init/utils/media_duration_updater.d.ts +0 -56
  153. package/scripts/doc-generator/construct_table_of_contents.js +0 -76
  154. package/scripts/doc-generator/convert_MD_to_HMTL.js +0 -26
  155. package/scripts/doc-generator/create_documentation.js +0 -331
  156. package/scripts/doc-generator/create_documentation_page.js +0 -209
  157. package/scripts/doc-generator/create_page.js +0 -210
  158. package/scripts/doc-generator/generate_header_html.js +0 -147
  159. package/scripts/doc-generator/generate_page_html.js +0 -115
  160. package/scripts/doc-generator/generate_page_list_html.js +0 -92
  161. package/scripts/doc-generator/generate_sidebar_html.js +0 -85
  162. package/scripts/doc-generator/get_search_data_for_content.js +0 -137
  163. package/scripts/doc-generator/index.js +0 -34
  164. package/scripts/doc-generator/parse_doc_configs.js +0 -327
  165. package/scripts/doc-generator/scripts/lunr.js +0 -10
  166. package/scripts/doc-generator/scripts/script.js +0 -451
  167. package/scripts/doc-generator/styles/code.css +0 -99
  168. package/scripts/doc-generator/styles/style.css +0 -835
  169. package/scripts/doc-generator/utils.js +0 -74
@@ -87,7 +87,7 @@ import getInitialTime from "./utils/get_initial_time";
87
87
  import getLoadedReference from "./utils/get_loaded_reference";
88
88
  import performInitialSeekAndPlay from "./utils/initial_seek_and_play";
89
89
  import initializeContentDecryption from "./utils/initialize_content_decryption";
90
- import MediaDurationUpdater from "./utils/media_duration_updater";
90
+ import MediaSourceDurationUpdater from "./utils/media_source_duration_updater";
91
91
  import RebufferingController from "./utils/rebuffering_controller";
92
92
  import streamEventsEmitter from "./utils/stream_events_emitter";
93
93
  import listenToMediaError from "./utils/throw_on_media_error";
@@ -249,7 +249,7 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
249
249
  if (initCanceller.isUsed()) {
250
250
  return;
251
251
  }
252
- triggerEvent("reloadingMediaSource", null);
252
+ triggerEvent("reloadingMediaSource", reloadOrder);
253
253
  if (initCanceller.isUsed()) {
254
254
  return;
255
255
  }
@@ -341,7 +341,18 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
341
341
  }
342
342
  }, { clearSignal: cancelSignal, emitCurrentValue: true });
343
343
  var streamObserver = createStreamPlaybackObserver(manifest, playbackObserver, { autoPlay: autoPlay, initialPlayPerformed: initialPlayPerformed, initialSeekPerformed: initialSeekPerformed, speed: speed, startTime: initialTime });
344
- var rebufferingController = this._createRebufferingController(playbackObserver, manifest, speed, cancelSignal);
344
+ var rebufferingController = this._createRebufferingController(playbackObserver, manifest, segmentBuffersStore, speed, cancelSignal);
345
+ rebufferingController.addEventListener("needsReload", function () {
346
+ // NOTE couldn't both be always calculated at event destination?
347
+ // Maybe there are exceptions?
348
+ var position = initialSeekPerformed.getValue() ?
349
+ playbackObserver.getCurrentTime() :
350
+ initialTime;
351
+ var autoplay = initialPlayPerformed.getValue() ?
352
+ !playbackObserver.getIsPaused() :
353
+ autoPlay;
354
+ onReloadOrder({ position: position, autoPlay: autoplay });
355
+ }, cancelSignal);
345
356
  var contentTimeBoundariesObserver = this
346
357
  ._createContentTimeBoundariesObserver(manifest, mediaSource, streamObserver, segmentBuffersStore, cancelSignal);
347
358
  /**
@@ -376,7 +387,32 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
376
387
  function handleStreamOrchestratorCallbacks() {
377
388
  return {
378
389
  needsBufferFlush: function () {
379
- return playbackObserver.setCurrentTime(mediaElement.currentTime + 0.001);
390
+ var seekedTime = mediaElement.currentTime + 0.001;
391
+ playbackObserver.setCurrentTime(seekedTime);
392
+ // Seek again once data begins to be buffered.
393
+ // This is sadly necessary on some browsers to avoid decoding
394
+ // issues after a flush.
395
+ //
396
+ // NOTE: there's in theory a potential race condition in the following
397
+ // logic as the callback could be called when media data is still
398
+ // being removed by the browser - which is an asynchronous process.
399
+ // The following condition checking for buffered data could thus lead
400
+ // to a false positive where we're actually checking previous data.
401
+ // For now, such scenario is avoided by setting the
402
+ // `includeLastObservation` option to `false` and calling
403
+ // `needsBufferFlush` once MSE media removal operations have been
404
+ // explicitely validated by the browser, but that's a complex and easy
405
+ // to break system.
406
+ playbackObserver.listen(function (obs, stopListening) {
407
+ if (
408
+ // Data is buffered around the current position
409
+ obs.currentRange !== null ||
410
+ // Or, for whatever reason, playback is already advancing
411
+ obs.position > seekedTime + 0.1) {
412
+ stopListening();
413
+ playbackObserver.setCurrentTime(obs.position + 0.001);
414
+ }
415
+ }, { includeLastObservation: false, clearSignal: cancelSignal });
380
416
  },
381
417
  streamStatusUpdate: function (value) {
382
418
  // Announce discontinuities if found
@@ -498,9 +534,9 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
498
534
  MediaSourceContentInitializer.prototype._createContentTimeBoundariesObserver = function (manifest, mediaSource, streamObserver, segmentBuffersStore, cancelSignal) {
499
535
  var _this = this;
500
536
  /** Maintains the MediaSource's duration up-to-date with the Manifest */
501
- var mediaDurationUpdater = new MediaDurationUpdater(manifest, mediaSource);
537
+ var mediaSourceDurationUpdater = new MediaSourceDurationUpdater(mediaSource);
502
538
  cancelSignal.register(function () {
503
- mediaDurationUpdater.stop();
539
+ mediaSourceDurationUpdater.stopUpdating();
504
540
  });
505
541
  /** Allows to cancel a pending `end-of-stream` operation. */
506
542
  var endOfStreamCanceller = null;
@@ -515,8 +551,7 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
515
551
  _this.trigger("activePeriodChanged", { period: period });
516
552
  });
517
553
  contentTimeBoundariesObserver.addEventListener("durationUpdate", function (newDuration) {
518
- log.debug("Init: Duration has to be updated.", newDuration);
519
- mediaDurationUpdater.updateKnownDuration(newDuration);
554
+ mediaSourceDurationUpdater.updateDuration(newDuration.duration, newDuration.isEnd);
520
555
  });
521
556
  contentTimeBoundariesObserver.addEventListener("endOfStream", function () {
522
557
  if (endOfStreamCanceller === null) {
@@ -533,6 +568,8 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
533
568
  endOfStreamCanceller = null;
534
569
  }
535
570
  });
571
+ var currentDuration = contentTimeBoundariesObserver.getCurrentDuration();
572
+ mediaSourceDurationUpdater.updateDuration(currentDuration.duration, currentDuration.isEnd);
536
573
  return contentTimeBoundariesObserver;
537
574
  };
538
575
  /**
@@ -553,9 +590,9 @@ var MediaSourceContentInitializer = /** @class */ (function (_super) {
553
590
  * @param {Object} cancelSignal
554
591
  * @returns {Object}
555
592
  */
556
- MediaSourceContentInitializer.prototype._createRebufferingController = function (playbackObserver, manifest, speed, cancelSignal) {
593
+ MediaSourceContentInitializer.prototype._createRebufferingController = function (playbackObserver, manifest, segmentBuffersStore, speed, cancelSignal) {
557
594
  var _this = this;
558
- var rebufferingController = new RebufferingController(playbackObserver, manifest, speed);
595
+ var rebufferingController = new RebufferingController(playbackObserver, manifest, segmentBuffersStore, speed);
559
596
  // Bubble-up events
560
597
  rebufferingController.addEventListener("stalled", function (evt) { return _this.trigger("stalled", evt); });
561
598
  rebufferingController.addEventListener("unstalled", function () { return _this.trigger("unstalled", null); });
@@ -95,7 +95,15 @@ export interface IContentInitializerEvents {
95
95
  * Event sent when we're starting attach a new MediaSource to the media element
96
96
  * (after removing the previous one).
97
97
  */
98
- reloadingMediaSource: null;
98
+ reloadingMediaSource: {
99
+ /** The position we're reloading at, in seconds. */
100
+ position: number;
101
+ /**
102
+ * If `true`, we'll play directly after finishing the reloading operation.
103
+ * If `false`, we'll be paused after it.
104
+ */
105
+ autoPlay: boolean;
106
+ };
99
107
  /** Event sent after the player stalled. */
100
108
  stalled: IStallingSituation;
101
109
  /** Event sent when the player goes out of a stalling situation. */
@@ -55,6 +55,11 @@ export default class ContentTimeBoundariesObserver extends EventEmitter<IContent
55
55
  * @param {Object} playbackObserver
56
56
  */
57
57
  constructor(manifest: Manifest, playbackObserver: IReadOnlyPlaybackObserver<IStreamOrchestratorPlaybackObservation>, bufferTypes: IBufferType[]);
58
+ /**
59
+ * Returns an estimate of the current duration of the content.
60
+ * @returns {Object}
61
+ */
62
+ getCurrentDuration(): IDurationItem;
58
63
  /**
59
64
  * Method to call any time an Adaptation has been selected.
60
65
  *
@@ -121,9 +126,31 @@ export default class ContentTimeBoundariesObserver extends EventEmitter<IContent
121
126
  private _addActivelyLoadedPeriod;
122
127
  private _removeActivelyLoadedPeriod;
123
128
  private _checkCurrentPeriod;
129
+ private _getManifestDuration;
124
130
  private _lazilyCreateActiveStreamInfo;
125
131
  private _checkEndOfStream;
126
132
  }
133
+ export interface IDurationItem {
134
+ /**
135
+ * The new maximum known position (note that this is the ending position
136
+ * currently known of the current content, it might be superior to the last
137
+ * position at which segments are available and it might also evolve over
138
+ * time), in seconds.
139
+ */
140
+ duration: number;
141
+ /**
142
+ * If `true`, the communicated `duration` is the actual end of the content.
143
+ * It may still be updated due to a track change or to add precision, but it
144
+ * is still a (rough) estimate of the maximum position that content should
145
+ * have.
146
+ *
147
+ * If `false`, this is the currently known maximum position associated to
148
+ * the content, but the content is still evolving (typically, new media
149
+ * segments are still being generated) and as such it can still have a
150
+ * longer duration in the future.
151
+ */
152
+ isEnd: boolean;
153
+ }
127
154
  /**
128
155
  * Events triggered by a `ContentTimeBoundariesObserver` where the keys are the
129
156
  * event names and the value is the payload of those events.
@@ -137,7 +164,7 @@ export interface IContentTimeBoundariesObserverEvent {
137
164
  * Triggered when the duration of the currently-playing content became known
138
165
  * or changed.
139
166
  */
140
- durationUpdate: number | undefined;
167
+ durationUpdate: IDurationItem;
141
168
  /**
142
169
  * Triggered when the last possible chronological segment for all types of
143
170
  * buffers has either been pushed or is being pushed to the corresponding
@@ -82,19 +82,21 @@ var ContentTimeBoundariesObserver = /** @class */ (function (_super) {
82
82
  }
83
83
  }, { includeLastObservation: true, clearSignal: cancelSignal });
84
84
  manifest.addEventListener("manifestUpdate", function () {
85
- _this.trigger("durationUpdate", getManifestDuration());
85
+ _this.trigger("durationUpdate", _this._getManifestDuration());
86
86
  if (cancelSignal.isCancelled()) {
87
87
  return;
88
88
  }
89
89
  _this._checkEndOfStream();
90
90
  }, cancelSignal);
91
- function getManifestDuration() {
92
- return manifest.isDynamic ?
93
- maximumPositionCalculator.getMaximumAvailablePosition() :
94
- maximumPositionCalculator.getEndingPosition();
95
- }
96
91
  return _this;
97
92
  }
93
+ /**
94
+ * Returns an estimate of the current duration of the content.
95
+ * @returns {Object}
96
+ */
97
+ ContentTimeBoundariesObserver.prototype.getCurrentDuration = function () {
98
+ return this._getManifestDuration();
99
+ };
98
100
  /**
99
101
  * Method to call any time an Adaptation has been selected.
100
102
  *
@@ -121,9 +123,12 @@ var ContentTimeBoundariesObserver = /** @class */ (function (_super) {
121
123
  this._maximumPositionCalculator
122
124
  .updateLastVideoAdaptation(adaptation);
123
125
  }
124
- var newDuration = this._manifest.isDynamic ?
125
- this._maximumPositionCalculator.getMaximumAvailablePosition() :
126
- this._maximumPositionCalculator.getEndingPosition();
126
+ var endingPosition = this._maximumPositionCalculator.getEndingPosition();
127
+ var newDuration = endingPosition !== undefined ?
128
+ { isEnd: true,
129
+ duration: endingPosition } :
130
+ { isEnd: false,
131
+ duration: this._maximumPositionCalculator.getMaximumAvailablePosition() };
127
132
  this.trigger("durationUpdate", newDuration);
128
133
  }
129
134
  }
@@ -258,6 +263,14 @@ var ContentTimeBoundariesObserver = /** @class */ (function (_super) {
258
263
  return state_1.value;
259
264
  }
260
265
  };
266
+ ContentTimeBoundariesObserver.prototype._getManifestDuration = function () {
267
+ var endingPosition = this._maximumPositionCalculator.getEndingPosition();
268
+ return endingPosition !== undefined ?
269
+ { isEnd: true,
270
+ duration: endingPosition } :
271
+ { isEnd: false,
272
+ duration: this._maximumPositionCalculator.getMaximumAvailablePosition() };
273
+ };
261
274
  ContentTimeBoundariesObserver.prototype._lazilyCreateActiveStreamInfo = function (bufferType) {
262
275
  var streamInfo = this._activeStreams.get(bufferType);
263
276
  if (streamInfo === undefined) {
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Copyright 2015 CANAL+ Group
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /**
17
+ * Keep the MediaSource's `duration` attribute up-to-date with the duration of
18
+ * the content played on it.
19
+ * @class MediaSourceDurationUpdater
20
+ */
21
+ export default class MediaSourceDurationUpdater {
22
+ /**
23
+ * `MediaSource` on which we're going to update the `duration` attribute.
24
+ */
25
+ private _mediaSource;
26
+ /**
27
+ * Abort the current duration-setting logic.
28
+ * `null` if no such logic is pending.
29
+ */
30
+ private _currentMediaSourceDurationUpdateCanceller;
31
+ /**
32
+ * Create a new `MediaSourceDurationUpdater`,
33
+ * @param {MediaSource} mediaSource - The MediaSource on which the content is
34
+ * played.
35
+ */
36
+ constructor(mediaSource: MediaSource);
37
+ /**
38
+ * Indicate to the `MediaSourceDurationUpdater` the currently known duration
39
+ * of the content.
40
+ *
41
+ * The `MediaSourceDurationUpdater` will then use that value to determine
42
+ * which `duration` attribute should be set on the `MediaSource` associated
43
+ *
44
+ * @param {number} newDuration
45
+ * @param {boolean} isRealEndKnown - If set to `false`, the current content is
46
+ * a dynamic content (it might evolve in the future) and the `newDuration`
47
+ * communicated might be greater still. In effect the
48
+ * `MediaSourceDurationUpdater` will actually set a much higher value to the
49
+ * `MediaSource`'s duration to prevent being annoyed by the HTML-related
50
+ * side-effects of having a too low duration (such as the impossibility to
51
+ * seek over that value).
52
+ */
53
+ updateDuration(newDuration: number, isRealEndKnown: boolean): void;
54
+ /**
55
+ * Abort the last duration-setting operation and free its resources.
56
+ */
57
+ stopUpdating(): void;
58
+ }
@@ -14,96 +14,91 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { onSourceOpen, onSourceEnded, onSourceClose, } from "../../../compat/event_listeners";
17
+ /* eslint-disable-next-line max-len */
18
+ import hasIssuesWithHighMediaSourceDuration from "../../../compat/has_issues_with_high_media_source_duration";
17
19
  import log from "../../../log";
18
20
  import createSharedReference from "../../../utils/reference";
19
21
  import TaskCanceller from "../../../utils/task_canceller";
20
22
  /** Number of seconds in a regular year. */
21
23
  var YEAR_IN_SECONDS = 365 * 24 * 3600;
22
24
  /**
23
- * Keep the MediaSource's duration up-to-date with what is being played.
24
- * @class MediaDurationUpdater
25
+ * Keep the MediaSource's `duration` attribute up-to-date with the duration of
26
+ * the content played on it.
27
+ * @class MediaSourceDurationUpdater
25
28
  */
26
- var MediaDurationUpdater = /** @class */ (function () {
29
+ var MediaSourceDurationUpdater = /** @class */ (function () {
27
30
  /**
28
- * Create a new `MediaDurationUpdater` that will keep the given MediaSource's
29
- * duration as soon as possible.
30
- * This duration will be updated until the `stop` method is called.
31
- * @param {Object} manifest - The Manifest currently played.
32
- * For another content, you will have to create another `MediaDurationUpdater`.
31
+ * Create a new `MediaSourceDurationUpdater`,
33
32
  * @param {MediaSource} mediaSource - The MediaSource on which the content is
34
- * pushed.
33
+ * played.
35
34
  */
36
- function MediaDurationUpdater(manifest, mediaSource) {
37
- var canceller = new TaskCanceller();
38
- var currentKnownDuration = createSharedReference(undefined, canceller.signal);
39
- this._canceller = canceller;
40
- this._currentKnownDuration = currentKnownDuration;
41
- var isMediaSourceOpened = createMediaSourceOpenReference(mediaSource, this._canceller.signal);
42
- /** TaskCanceller triggered each time the MediaSource open status changes. */
43
- var msUpdateCanceller = new TaskCanceller();
44
- msUpdateCanceller.linkToSignal(this._canceller.signal);
35
+ function MediaSourceDurationUpdater(mediaSource) {
36
+ this._mediaSource = mediaSource;
37
+ this._currentMediaSourceDurationUpdateCanceller = null;
38
+ }
39
+ /**
40
+ * Indicate to the `MediaSourceDurationUpdater` the currently known duration
41
+ * of the content.
42
+ *
43
+ * The `MediaSourceDurationUpdater` will then use that value to determine
44
+ * which `duration` attribute should be set on the `MediaSource` associated
45
+ *
46
+ * @param {number} newDuration
47
+ * @param {boolean} isRealEndKnown - If set to `false`, the current content is
48
+ * a dynamic content (it might evolve in the future) and the `newDuration`
49
+ * communicated might be greater still. In effect the
50
+ * `MediaSourceDurationUpdater` will actually set a much higher value to the
51
+ * `MediaSource`'s duration to prevent being annoyed by the HTML-related
52
+ * side-effects of having a too low duration (such as the impossibility to
53
+ * seek over that value).
54
+ */
55
+ MediaSourceDurationUpdater.prototype.updateDuration = function (newDuration, isRealEndKnown) {
56
+ if (this._currentMediaSourceDurationUpdateCanceller !== null) {
57
+ this._currentMediaSourceDurationUpdateCanceller.cancel();
58
+ }
59
+ this._currentMediaSourceDurationUpdateCanceller = new TaskCanceller();
60
+ var mediaSource = this._mediaSource;
61
+ var currentSignal = this._currentMediaSourceDurationUpdateCanceller.signal;
62
+ var isMediaSourceOpened = createMediaSourceOpenReference(mediaSource, currentSignal);
63
+ /** TaskCanceller triggered each time the MediaSource switches to and from "open". */
64
+ var msOpenStatusCanceller = new TaskCanceller();
65
+ msOpenStatusCanceller.linkToSignal(currentSignal);
45
66
  isMediaSourceOpened.onUpdate(onMediaSourceOpenedStatusChanged, { emitCurrentValue: true,
46
- clearSignal: this._canceller.signal });
67
+ clearSignal: currentSignal });
47
68
  function onMediaSourceOpenedStatusChanged() {
48
- msUpdateCanceller.cancel();
69
+ msOpenStatusCanceller.cancel();
49
70
  if (!isMediaSourceOpened.getValue()) {
50
71
  return;
51
72
  }
52
- msUpdateCanceller = new TaskCanceller();
53
- msUpdateCanceller.linkToSignal(canceller.signal);
54
- /** TaskCanceller triggered each time the content's duration may have changed */
55
- var durationChangeCanceller = new TaskCanceller();
56
- durationChangeCanceller.linkToSignal(msUpdateCanceller.signal);
57
- var reSetDuration = function () {
58
- durationChangeCanceller.cancel();
59
- durationChangeCanceller = new TaskCanceller();
60
- durationChangeCanceller.linkToSignal(msUpdateCanceller.signal);
61
- onDurationMayHaveChanged(durationChangeCanceller.signal);
62
- };
63
- currentKnownDuration.onUpdate(reSetDuration, { emitCurrentValue: false,
64
- clearSignal: msUpdateCanceller.signal });
65
- manifest.addEventListener("manifestUpdate", reSetDuration, msUpdateCanceller.signal);
66
- onDurationMayHaveChanged(durationChangeCanceller.signal);
67
- }
68
- function onDurationMayHaveChanged(cancelSignal) {
69
- var areSourceBuffersUpdating = createSourceBuffersUpdatingReference(mediaSource.sourceBuffers, cancelSignal);
73
+ msOpenStatusCanceller = new TaskCanceller();
74
+ msOpenStatusCanceller.linkToSignal(currentSignal);
75
+ var areSourceBuffersUpdating = createSourceBuffersUpdatingReference(mediaSource.sourceBuffers, msOpenStatusCanceller.signal);
70
76
  /** TaskCanceller triggered each time SourceBuffers' updating status changes */
71
77
  var sourceBuffersUpdatingCanceller = new TaskCanceller();
72
- sourceBuffersUpdatingCanceller.linkToSignal(cancelSignal);
78
+ sourceBuffersUpdatingCanceller.linkToSignal(msOpenStatusCanceller.signal);
73
79
  return areSourceBuffersUpdating.onUpdate(function (areUpdating) {
74
80
  sourceBuffersUpdatingCanceller.cancel();
75
81
  sourceBuffersUpdatingCanceller = new TaskCanceller();
76
- sourceBuffersUpdatingCanceller.linkToSignal(cancelSignal);
82
+ sourceBuffersUpdatingCanceller.linkToSignal(msOpenStatusCanceller.signal);
77
83
  if (areUpdating) {
78
84
  return;
79
85
  }
80
- recursivelyForceDurationUpdate(mediaSource, manifest, currentKnownDuration.getValue(), cancelSignal);
81
- }, { clearSignal: cancelSignal, emitCurrentValue: true });
86
+ recursivelyForceDurationUpdate(mediaSource, newDuration, isRealEndKnown, sourceBuffersUpdatingCanceller.signal);
87
+ }, { clearSignal: msOpenStatusCanceller.signal, emitCurrentValue: true });
82
88
  }
83
- }
84
- /**
85
- * By default, the `MediaDurationUpdater` only set a safe estimate for the
86
- * MediaSource's duration.
87
- * A more precize duration can be set by communicating to it a more precize
88
- * media duration through `updateKnownDuration`.
89
- * If the duration becomes unknown, `undefined` can be given to it so the
90
- * `MediaDurationUpdater` goes back to a safe estimate.
91
- * @param {number | undefined} newDuration
92
- */
93
- MediaDurationUpdater.prototype.updateKnownDuration = function (newDuration) {
94
- this._currentKnownDuration.setValueIfChanged(newDuration);
95
89
  };
96
90
  /**
97
- * Stop the `MediaDurationUpdater` from updating and free its resources.
98
- * Once stopped, it is not possible to start it again, beside creating another
99
- * `MediaDurationUpdater`.
91
+ * Abort the last duration-setting operation and free its resources.
100
92
  */
101
- MediaDurationUpdater.prototype.stop = function () {
102
- this._canceller.cancel();
93
+ MediaSourceDurationUpdater.prototype.stopUpdating = function () {
94
+ if (this._currentMediaSourceDurationUpdateCanceller !== null) {
95
+ this._currentMediaSourceDurationUpdateCanceller.cancel();
96
+ this._currentMediaSourceDurationUpdateCanceller = null;
97
+ }
103
98
  };
104
- return MediaDurationUpdater;
99
+ return MediaSourceDurationUpdater;
105
100
  }());
106
- export default MediaDurationUpdater;
101
+ export default MediaSourceDurationUpdater;
107
102
  /**
108
103
  * Checks that duration can be updated on the MediaSource, and then
109
104
  * sets it.
@@ -113,28 +108,16 @@ export default MediaDurationUpdater;
113
108
  * - `null` if it hasn'nt been updated
114
109
  *
115
110
  * @param {MediaSource} mediaSource
116
- * @param {Object} manifest
111
+ * @param {number} duration
112
+ * @param {boolean} isRealEndKnown
117
113
  * @returns {string}
118
114
  */
119
- function setMediaSourceDuration(mediaSource, manifest, knownDuration) {
120
- var _a;
121
- var newDuration = knownDuration;
122
- if (newDuration === undefined) {
123
- if (manifest.isDynamic) {
124
- newDuration = (_a = manifest.getLivePosition()) !== null && _a !== void 0 ? _a : manifest.getMaximumSafePosition();
125
- }
126
- else {
127
- newDuration = manifest.getMaximumSafePosition();
128
- }
129
- }
130
- if (manifest.isDynamic) {
131
- // Some targets poorly support setting a very high number for durations.
132
- // Yet, in dynamic contents, we would prefer setting a value as high as possible
133
- // to still be able to seek anywhere we want to (even ahead of the Manifest if
134
- // we want to). As such, we put it at a safe default value of 2^32 excepted
135
- // when the maximum position is already relatively close to that value, where
136
- // we authorize exceptionally going over it.
137
- newDuration = Math.max(Math.pow(2, 32), newDuration + YEAR_IN_SECONDS);
115
+ function setMediaSourceDuration(mediaSource, duration, isRealEndKnown) {
116
+ var newDuration = duration;
117
+ if (!isRealEndKnown) {
118
+ newDuration = hasIssuesWithHighMediaSourceDuration() ?
119
+ Infinity :
120
+ getMaximumLiveSeekablePosition(duration);
138
121
  }
139
122
  var maxBufferedEnd = 0;
140
123
  for (var i = 0; i < mediaSource.sourceBuffers.length; i++) {
@@ -167,6 +150,9 @@ function setMediaSourceDuration(mediaSource, manifest, knownDuration) {
167
150
  try {
168
151
  log.info("Init: Updating duration", newDuration);
169
152
  mediaSource.duration = newDuration;
153
+ if (mediaSource.readyState === "open" && !isFinite(newDuration)) {
154
+ mediaSource.setLiveSeekableRange(0, getMaximumLiveSeekablePosition(duration));
155
+ }
170
156
  }
171
157
  catch (err) {
172
158
  log.warn("Duration Updater: Can't update duration on the MediaSource.", err instanceof Error ? err : "");
@@ -242,25 +228,36 @@ function createMediaSourceOpenReference(mediaSource, cancelSignal) {
242
228
  }
243
229
  /**
244
230
  * Immediately tries to set the MediaSource's duration to the most appropriate
245
- * one according to the Manifest and duration given.
231
+ * one.
246
232
  *
247
233
  * If it fails, wait 2 seconds and retries.
248
234
  *
249
235
  * @param {MediaSource} mediaSource
250
- * @param {Object} manifest
251
- * @param {number|undefined} duration
236
+ * @param {number} duration
237
+ * @param {boolean} isRealEndKnown
252
238
  * @param {Object} cancelSignal
253
239
  */
254
- function recursivelyForceDurationUpdate(mediaSource, manifest, duration, cancelSignal) {
255
- var res = setMediaSourceDuration(mediaSource, manifest, duration);
240
+ function recursivelyForceDurationUpdate(mediaSource, duration, isRealEndKnown, cancelSignal) {
241
+ var res = setMediaSourceDuration(mediaSource, duration, isRealEndKnown);
256
242
  if (res === "success" /* MediaSourceDurationUpdateStatus.Success */) {
257
243
  return;
258
244
  }
259
245
  var timeoutId = setTimeout(function () {
260
246
  unregisterClear();
261
- recursivelyForceDurationUpdate(mediaSource, manifest, duration, cancelSignal);
247
+ recursivelyForceDurationUpdate(mediaSource, duration, isRealEndKnown, cancelSignal);
262
248
  }, 2000);
263
249
  var unregisterClear = cancelSignal.register(function () {
264
250
  clearTimeout(timeoutId);
265
251
  });
266
252
  }
253
+ function getMaximumLiveSeekablePosition(contentLastPosition) {
254
+ // Some targets poorly support setting a very high number for seekable
255
+ // ranges.
256
+ // Yet, in contents whose end is not yet known (e.g. live contents), we
257
+ // would prefer setting a value as high as possible to still be able to
258
+ // seek anywhere we want to (even ahead of the Manifest if we want to).
259
+ // As such, we put it at a safe default value of 2^32 excepted when the
260
+ // maximum position is already relatively close to that value, where we
261
+ // authorize exceptionally going over it.
262
+ return Math.max(Math.pow(2, 32), contentLastPosition + YEAR_IN_SECONDS);
263
+ }
@@ -18,7 +18,7 @@ import { IPlayerError } from "../../../public_types";
18
18
  import EventEmitter from "../../../utils/event_emitter";
19
19
  import { IReadOnlySharedReference } from "../../../utils/reference";
20
20
  import { PlaybackObserver } from "../../api";
21
- import { IBufferType } from "../../segment_buffers";
21
+ import SegmentBuffersStore, { IBufferType } from "../../segment_buffers";
22
22
  import { IStallingSituation } from "../types";
23
23
  /**
24
24
  * Monitor playback, trying to avoid stalling situation.
@@ -32,6 +32,7 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
32
32
  /** Emit the current playback conditions */
33
33
  private _playbackObserver;
34
34
  private _manifest;
35
+ private _segmentBuffersStore;
35
36
  private _speed;
36
37
  private _isStarted;
37
38
  /**
@@ -40,12 +41,23 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
40
41
  */
41
42
  private _discontinuitiesStore;
42
43
  private _canceller;
44
+ /**
45
+ * If set to something else than `null`, this is the DOMHighResTimestamp as
46
+ * outputed by `performance.now()` when playback begin to seem to not start
47
+ * despite having decipherable data in the buffer(s).
48
+ *
49
+ * If enough time in that condition is spent, special considerations are
50
+ * taken at which point `_currentFreezeTimestamp` is reset to `null`.
51
+ *
52
+ * It is also reset to `null` when and if there is no such issue anymore.
53
+ */
54
+ private _currentFreezeTimestamp;
43
55
  /**
44
56
  * @param {object} playbackObserver - emit the current playback conditions.
45
57
  * @param {Object} manifest - The Manifest of the currently-played content.
46
58
  * @param {Object} speed - The last speed set by the user
47
59
  */
48
- constructor(playbackObserver: PlaybackObserver, manifest: Manifest | null, speed: IReadOnlySharedReference<number>);
60
+ constructor(playbackObserver: PlaybackObserver, manifest: Manifest | null, segmentBuffersStore: SegmentBuffersStore | null, speed: IReadOnlySharedReference<number>);
49
61
  start(): void;
50
62
  /**
51
63
  * Update information on an upcoming discontinuity for a given buffer type and
@@ -68,10 +80,32 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
68
80
  * forever.
69
81
  */
70
82
  destroy(): void;
83
+ /**
84
+ * Support of contents with DRM on all the platforms out there is a pain in
85
+ * the *ss considering all the DRM-related bugs there are.
86
+ *
87
+ * We found out a frequent issue which is to be unable to play despite having
88
+ * all the decryption keys to play what is currently buffered.
89
+ * When this happens, re-creating the buffers from scratch, with a reload, is
90
+ * usually sufficient to unlock the situation.
91
+ *
92
+ * Although we prefer providing more targeted fixes or telling to platform
93
+ * developpers to fix their implementation, it's not always possible.
94
+ * We thus resorted to developping an heuristic which detects such situation
95
+ * and reload in that case.
96
+ *
97
+ * @param {Object} observation - The last playback observation produced, it
98
+ * has to be recent (just triggered for example).
99
+ * @returns {boolean} - Returns `true` if it seems to be such kind of
100
+ * decipherability freeze, in which case this method already performed the
101
+ * right handling steps.
102
+ */
103
+ private _checkDecipherabilityFreeze;
71
104
  }
72
105
  export interface IRebufferingControllerEvent {
73
106
  stalled: IStallingSituation;
74
107
  unstalled: null;
108
+ needsReload: null;
75
109
  warning: IPlayerError;
76
110
  }
77
111
  /**