rx-player 3.30.0-dev.2023020100 → 3.30.0-dev.2023030200

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 (183) hide show
  1. package/.eslintrc.js +8 -0
  2. package/CHANGELOG.md +5 -1
  3. package/VERSION +1 -1
  4. package/dist/_esm5.processed/compat/browser_detection.d.ts +23 -12
  5. package/dist/_esm5.processed/compat/browser_detection.js +80 -38
  6. package/dist/_esm5.processed/compat/can_reuse_media_keys.js +2 -2
  7. package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
  8. package/dist/_esm5.processed/compat/event_listeners.js +1 -1
  9. package/dist/_esm5.processed/config.d.ts +2 -0
  10. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +4 -2
  11. package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
  12. package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
  13. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
  14. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +104 -0
  15. package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
  16. package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
  17. package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
  18. package/dist/_esm5.processed/core/api/debug/index.js +2 -0
  19. package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
  20. package/dist/_esm5.processed/core/api/debug/modules/general_info.js +199 -0
  21. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
  22. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
  23. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
  24. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
  25. package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
  26. package/dist/_esm5.processed/core/api/debug/render.js +32 -0
  27. package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
  28. package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
  29. package/dist/_esm5.processed/core/api/playback_observer.js +3 -2
  30. package/dist/_esm5.processed/core/api/public_api.d.ts +3 -0
  31. package/dist/_esm5.processed/core/api/public_api.js +25 -14
  32. package/dist/_esm5.processed/core/api/utils.js +3 -3
  33. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +32 -13
  34. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +28 -7
  35. package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
  36. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
  37. package/dist/_esm5.processed/core/decrypt/find_key_system.js +33 -24
  38. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +54 -50
  39. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
  40. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
  41. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
  42. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
  43. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
  44. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
  45. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
  46. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +17 -7
  47. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
  48. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +27 -31
  49. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +2 -2
  50. package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
  51. package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
  52. package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
  53. package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
  54. package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
  55. package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +23 -19
  56. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +1 -1
  57. package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
  58. package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
  59. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +7 -10
  60. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +4 -2
  61. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
  62. package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +25 -16
  63. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +26 -12
  64. package/dist/_esm5.processed/core/stream/period/period_stream.js +11 -10
  65. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -15
  66. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +1 -1
  67. package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
  68. package/dist/_esm5.processed/default_config.d.ts +16 -0
  69. package/dist/_esm5.processed/default_config.js +19 -0
  70. package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
  71. package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
  72. package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
  73. package/dist/_esm5.processed/experimental/features/index.js +1 -0
  74. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
  75. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +8 -4
  76. package/dist/_esm5.processed/features/features_object.js +1 -0
  77. package/dist/_esm5.processed/features/initialize_features.js +13 -10
  78. package/dist/_esm5.processed/features/types.d.ts +3 -0
  79. package/dist/_esm5.processed/manifest/adaptation.js +4 -0
  80. package/dist/_esm5.processed/manifest/manifest.js +2 -0
  81. package/dist/_esm5.processed/manifest/representation.js +11 -4
  82. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
  83. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
  84. package/dist/_esm5.processed/public_types.d.ts +1 -0
  85. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +15 -11
  86. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
  87. package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
  88. package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
  89. package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
  90. package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
  91. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
  92. package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
  93. package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
  94. package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
  95. package/dist/_esm5.processed/utils/reference.js +6 -0
  96. package/dist/_esm5.processed/utils/request/xhr.js +1 -1
  97. package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
  98. package/dist/_esm5.processed/utils/task_canceller.js +55 -22
  99. package/dist/rx-player.js +880 -677
  100. package/dist/rx-player.min.js +1 -1
  101. package/jest.config.js +1 -0
  102. package/package.json +19 -18
  103. package/scripts/build/constants.d.ts +1 -0
  104. package/scripts/report_build_sizes +4 -0
  105. package/sonar-project.properties +1 -1
  106. package/src/compat/browser_detection.ts +99 -52
  107. package/src/compat/can_reuse_media_keys.ts +5 -2
  108. package/src/compat/eme/close_session.ts +2 -2
  109. package/src/compat/event_listeners.ts +1 -1
  110. package/src/core/adaptive/adaptive_representation_selector.ts +4 -2
  111. package/src/core/api/debug/buffer_graph.ts +247 -0
  112. package/src/core/api/debug/buffer_size_graph.ts +130 -0
  113. package/src/core/api/debug/constants.ts +2 -0
  114. package/src/core/api/debug/index.ts +3 -0
  115. package/src/core/api/debug/modules/general_info.ts +204 -0
  116. package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
  117. package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
  118. package/src/core/api/debug/render.ts +40 -0
  119. package/src/core/api/debug/utils.ts +103 -0
  120. package/src/core/api/playback_observer.ts +4 -2
  121. package/src/core/api/public_api.ts +28 -14
  122. package/src/core/api/utils.ts +3 -3
  123. package/src/core/decrypt/__tests__/__global__/utils.ts +61 -40
  124. package/src/core/decrypt/attach_media_keys.ts +1 -1
  125. package/src/core/decrypt/content_decryptor.ts +1 -1
  126. package/src/core/decrypt/find_key_system.ts +36 -35
  127. package/src/core/decrypt/session_events_listener.ts +57 -56
  128. package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
  129. package/src/core/fetchers/cdn_prioritizer.ts +18 -9
  130. package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
  131. package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
  132. package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
  133. package/src/core/fetchers/utils/schedule_request.ts +18 -7
  134. package/src/core/init/directfile_content_initializer.ts +1 -1
  135. package/src/core/init/media_source_content_initializer.ts +37 -41
  136. package/src/core/init/utils/content_time_boundaries_observer.ts +2 -2
  137. package/src/core/init/utils/create_media_source.ts +4 -16
  138. package/src/core/init/utils/end_of_stream.ts +6 -3
  139. package/src/core/init/utils/get_loaded_reference.ts +2 -1
  140. package/src/core/init/utils/initial_seek_and_play.ts +9 -5
  141. package/src/core/init/utils/initialize_content_decryption.ts +2 -1
  142. package/src/core/init/utils/media_duration_updater.ts +25 -20
  143. package/src/core/init/utils/rebuffering_controller.ts +1 -1
  144. package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
  145. package/src/core/init/utils/throw_on_media_error.ts +1 -1
  146. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +7 -11
  147. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +4 -2
  148. package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
  149. package/src/core/stream/adaptation/adaptation_stream.ts +28 -19
  150. package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -12
  151. package/src/core/stream/period/period_stream.ts +11 -10
  152. package/src/core/stream/representation/representation_stream.ts +15 -15
  153. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +1 -1
  154. package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
  155. package/src/default_config.ts +30 -9
  156. package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
  157. package/src/experimental/features/debug_element.ts +13 -0
  158. package/src/experimental/features/index.ts +1 -0
  159. package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
  160. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +8 -4
  161. package/src/features/__tests__/initialize_features.test.ts +11 -0
  162. package/src/features/features_object.ts +1 -0
  163. package/src/features/initialize_features.ts +15 -10
  164. package/src/features/types.ts +9 -0
  165. package/src/manifest/adaptation.ts +7 -0
  166. package/src/manifest/manifest.ts +4 -0
  167. package/src/manifest/representation.ts +10 -4
  168. package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
  169. package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
  170. package/src/public_types.ts +1 -0
  171. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +31 -22
  172. package/src/transports/dash/low_latency_segment_loader.ts +2 -2
  173. package/src/transports/dash/manifest_parser.ts +1 -1
  174. package/src/transports/dash/segment_loader.ts +4 -4
  175. package/src/transports/local/segment_loader.ts +14 -30
  176. package/src/transports/smooth/segment_loader.ts +4 -4
  177. package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
  178. package/src/typings/globals.d.ts +2 -0
  179. package/src/utils/cancellable_sleep.ts +5 -14
  180. package/src/utils/create_cancellable_promise.ts +69 -0
  181. package/src/utils/reference.ts +6 -0
  182. package/src/utils/request/xhr.ts +1 -1
  183. package/src/utils/task_canceller.ts +63 -34
@@ -16,19 +16,22 @@
16
16
 
17
17
  import config from "../../config";
18
18
  import { ICdnMetadata } from "../../parsers/manifest";
19
- import { IPlayerError } from "../../public_types";
20
19
  import arrayFindIndex from "../../utils/array_find_index";
21
20
  import EventEmitter from "../../utils/event_emitter";
22
21
  import { CancellationSignal } from "../../utils/task_canceller";
23
22
 
24
23
  /**
25
- * Class signaling the priority between multiple CDN available for any given
26
- * resource.
24
+ * Class storing and signaling the priority between multiple CDN available for
25
+ * any given resource.
27
26
  *
28
- * This class might perform requests and schedule timeouts by itself to keep its
29
- * internal list of CDN priority up-to-date.
30
- * When it is not needed anymore, you should call the `dispose` method to clear
31
- * all resources.
27
+ * This class was first created to implement the complexities behind
28
+ * Content Steering features, though its handling hasn't been added yet as we
29
+ * wait for its specification to be both standardized and relied on in the wild.
30
+ * In the meantime, it acts as an abstraction for the simple concept of
31
+ * avoiding to request a CDN for any segment when an issue is encountered with
32
+ * one (e.g. HTTP 500 statuses) and several CDN exist for a given resource. It
33
+ * should be noted that this is also one of the planified features of the
34
+ * Content Steering specification.
32
35
  *
33
36
  * @class CdnPrioritizer
34
37
  */
@@ -41,7 +44,7 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
41
44
  private _downgradedCdnList : {
42
45
  /**
43
46
  * Metadata of downgraded CDN, sorted by the time at which they have
44
- * been downgraded.
47
+ * been downgraded ascending.
45
48
  */
46
49
  metadata : ICdnMetadata[];
47
50
  /**
@@ -174,8 +177,14 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
174
177
  }
175
178
  }
176
179
 
180
+ /** Events sent by a `CdnPrioritizer` */
177
181
  export interface ICdnPrioritizerEvents {
178
- warnings : IPlayerError[];
182
+ /**
183
+ * The priority of one or several CDN changed.
184
+ *
185
+ * You might want to re-check if a CDN should still be used when this event
186
+ * is triggered.
187
+ */
179
188
  priorityChange : null;
180
189
  }
181
190
 
@@ -155,7 +155,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
155
155
  manifestProm
156
156
  .then((val : IManifestFetcherParsedResult) => {
157
157
  this.trigger("manifestReady", val.manifest);
158
- if (!this._canceller.isUsed) {
158
+ if (!this._canceller.isUsed()) {
159
159
  this._recursivelyRefreshManifest(val.manifest, val);
160
160
  }
161
161
  })
@@ -362,7 +362,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
362
362
  */
363
363
  function onWarnings(warnings : Error[]) : void {
364
364
  for (const warning of warnings) {
365
- if (cancelSignal.isCancelled) {
365
+ if (cancelSignal.isCancelled()) {
366
366
  return;
367
367
  }
368
368
  const formattedError = formatError(warning, {
@@ -470,7 +470,8 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
470
470
  * be effectively considered.
471
471
  * `nextRefreshCanceller` will allow to cancel every other when one is triggered.
472
472
  */
473
- const nextRefreshCanceller = new TaskCanceller({ cancelOn: this._canceller.signal });
473
+ const nextRefreshCanceller = new TaskCanceller();
474
+ nextRefreshCanceller.linkToSignal(this._canceller.signal);
474
475
 
475
476
  /* Function to manually schedule a Manifest refresh */
476
477
  this.scheduleManualRefresh = (settings : IManifestRefreshSettings) => {
@@ -669,7 +670,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
669
670
  }
670
671
 
671
672
  private _onFatalError(err : unknown) : void {
672
- if (this._canceller.isUsed) {
673
+ if (this._canceller.isUsed()) {
673
674
  return;
674
675
  }
675
676
  this.trigger("error", err);
@@ -64,10 +64,22 @@ const generateRequestID = idGenerator();
64
64
  * An `ISegmentFetcher` also implements a retry mechanism, based on the given
65
65
  * `options` argument, which may retry a segment request when it fails.
66
66
  *
67
- * @param {string} bufferType
68
- * @param {Object} pipeline
69
- * @param {Object} lifecycleCallbacks
70
- * @param {Object} options
67
+ * @param {string} bufferType - Type of buffer concerned (e.g. `"audio"`,
68
+ * `"video"`, `"text" etc.)
69
+ * @param {Object} pipeline - The transport-specific logic allowing to load
70
+ * segments of the given buffer type and transport protocol (e.g. DASH).
71
+ * @param {Object|null} cdnPrioritizer - Abstraction allowing to synchronize,
72
+ * update and keep track of the priorization of the CDN to use to load any given
73
+ * segment, in cases where multiple ones are available.
74
+ *
75
+ * Can be set to `null` in which case a minimal priorization logic will be used
76
+ * instead.
77
+ * @param {Object} lifecycleCallbacks - Callbacks that can be registered to be
78
+ * informed when new requests are made, ended, new metrics are available etc.
79
+ * This should be mainly useful to implement an adaptive logic relying on those
80
+ * metrics and events.
81
+ * @param {Object} options - Various tweaking options allowing to configure the
82
+ * behavior of the returned `ISegmentFetcher`.
71
83
  * @returns {Function}
72
84
  */
73
85
  export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
@@ -184,14 +196,7 @@ export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
184
196
  id: requestId,
185
197
  content });
186
198
 
187
- cancellationSignal.register(() => {
188
- if (requestInfo !== undefined) {
189
- return; // Request already terminated
190
- }
191
- log.debug("SF: Segment request cancelled", segmentIdString);
192
- requestInfo = null;
193
- lifecycleCallbacks.onRequestEnd?.({ id: requestId });
194
- });
199
+ cancellationSignal.register(onCancellation);
195
200
 
196
201
  try {
197
202
  const res = await scheduleRequestWithCdns(content.representation.cdnMetadata,
@@ -220,22 +225,34 @@ export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
220
225
  requestInfo = null;
221
226
  }
222
227
 
223
- if (!cancellationSignal.isCancelled) {
228
+ if (!cancellationSignal.isCancelled()) {
224
229
  // The current task could have been canceled as a result of one
225
230
  // of the previous callbacks call. In that case, we don't want to send
226
231
  // a "requestEnd" again as it has already been sent on cancellation.
227
232
  lifecycleCallbacks.onRequestEnd?.({ id: requestId });
228
233
  }
234
+ cancellationSignal.deregister(onCancellation);
229
235
  } catch (err) {
236
+ cancellationSignal.deregister(onCancellation);
230
237
  requestInfo = null;
231
238
  if (err instanceof CancellationError) {
232
239
  log.debug("SF: Segment request aborted", segmentIdString);
233
240
  throw err;
234
241
  }
235
242
  log.debug("SF: Segment request failed", segmentIdString);
243
+ lifecycleCallbacks.onRequestEnd?.({ id: requestId });
236
244
  throw errorSelector(err);
237
245
  }
238
246
 
247
+ function onCancellation() {
248
+ if (requestInfo !== undefined) {
249
+ return; // Request already terminated
250
+ }
251
+ log.debug("SF: Segment request cancelled", segmentIdString);
252
+ requestInfo = null;
253
+ lifecycleCallbacks.onRequestEnd?.({ id: requestId });
254
+ }
255
+
239
256
  /**
240
257
  * Call a segment loader for the given URL with the right arguments.
241
258
  * @param {Object|null} cdnMetadata
@@ -360,11 +377,16 @@ export interface ISegmentFetcherCallbacks<TSegmentDataType> {
360
377
  /**
361
378
  * Callback called when all decodable chunks of the loaded segment have been
362
379
  * communicated through the `onChunk` callback.
380
+ *
381
+ * This callback is called before the corresponding `ISegmentFetcher`'s
382
+ * returned Promise is resolved.
363
383
  */
364
384
  onAllChunksReceived() : void;
365
385
 
366
386
  /**
367
- * Callback called when the segment request has to restart from scratch. */
387
+ * Callback called when the segment request has to restart from scratch, e.g.
388
+ * due to a request error.
389
+ */
368
390
  onRetry(error : IPlayerError) : void;
369
391
  }
370
392
 
@@ -1,5 +1,6 @@
1
1
  import log from "../../../log";
2
2
  import arrayFindIndex from "../../../utils/array_find_index";
3
+ import createCancellablePromise from "../../../utils/create_cancellable_promise";
3
4
  import TaskCanceller, {
4
5
  CancellationError,
5
6
  CancellationSignal,
@@ -64,19 +65,34 @@ export default class TaskPrioritizer<T> {
64
65
  cancelSignal: CancellationSignal
65
66
  ): Promise<T> {
66
67
  let newTask: IPrioritizerTask<T>;
67
-
68
- return new Promise<T>((resolve, reject) => {
68
+ return createCancellablePromise<T>(cancelSignal, (resolve, reject) => {
69
69
  /** Function allowing to start the underlying Promise. */
70
70
  const trigger = () => {
71
71
  if (newTask.hasEnded) {
72
- unregisterCancelSignal();
73
72
  return;
74
73
  }
75
- const interrupter = new TaskCanceller({ cancelOn: cancelSignal });
74
+ const finishTask = () => {
75
+ unlinkInterrupter();
76
+ this._endTask(newTask);
77
+ };
78
+
79
+ const onResolve = (value: T) => {
80
+ callbacks.beforeEnded();
81
+ finishTask();
82
+ resolve(value);
83
+ };
84
+
85
+ const onReject = (err: unknown) => {
86
+ finishTask();
87
+ reject(err);
88
+ };
89
+
90
+ const interrupter = new TaskCanceller();
91
+ const unlinkInterrupter = interrupter.linkToSignal(cancelSignal);
76
92
  newTask.interrupter = interrupter;
77
93
  interrupter.signal.register(() => {
78
94
  newTask.interrupter = null;
79
- if (!cancelSignal.isCancelled) {
95
+ if (!cancelSignal.isCancelled()) {
80
96
  callbacks.beforeInterrupted();
81
97
  }
82
98
  });
@@ -89,8 +105,8 @@ export default class TaskPrioritizer<T> {
89
105
  newTask.taskFn(interrupter.signal)
90
106
  .then(onResolve)
91
107
  .catch((err) => {
92
- if (!cancelSignal.isCancelled &&
93
- interrupter.isUsed &&
108
+ if (!cancelSignal.isCancelled() &&
109
+ interrupter.isUsed() &&
94
110
  err instanceof CancellationError)
95
111
  {
96
112
  return;
@@ -99,29 +115,6 @@ export default class TaskPrioritizer<T> {
99
115
  });
100
116
  };
101
117
 
102
- const unregisterCancelSignal = cancelSignal.register(
103
- (cancellationError: CancellationError) => {
104
- this._endTask(newTask);
105
- reject(cancellationError);
106
- }
107
- );
108
-
109
- const finishTask = () => {
110
- unregisterCancelSignal();
111
- this._endTask(newTask);
112
- };
113
-
114
- const onResolve = (value: T) => {
115
- callbacks.beforeEnded();
116
- finishTask();
117
- resolve(value);
118
- };
119
-
120
- const onReject = (err: unknown) => {
121
- finishTask();
122
- reject(err);
123
- };
124
-
125
118
  newTask = {
126
119
  hasEnded: false,
127
120
  priority,
@@ -146,6 +139,8 @@ export default class TaskPrioritizer<T> {
146
139
  this._interruptCancellableTasks();
147
140
  }
148
141
  }
142
+
143
+ return () => this._endTask(newTask);
149
144
  });
150
145
  }
151
146
 
@@ -315,7 +315,7 @@ export async function scheduleRequestWithCdns<T>(
315
315
  async function retryWithNextCdn(prevRequestError : unknown) : Promise<T> {
316
316
  const nextCdn = getCdnToRequest();
317
317
 
318
- if (cancellationSignal.isCancelled) {
318
+ if (cancellationSignal.isCancelled()) {
319
319
  throw cancellationSignal.cancellationError;
320
320
  }
321
321
 
@@ -324,7 +324,7 @@ export async function scheduleRequestWithCdns<T>(
324
324
  }
325
325
 
326
326
  onRetry(prevRequestError);
327
- if (cancellationSignal.isCancelled) {
327
+ if (cancellationSignal.isCancelled()) {
328
328
  throw cancellationSignal.cancellationError;
329
329
  }
330
330
 
@@ -358,26 +358,37 @@ export async function scheduleRequestWithCdns<T>(
358
358
  return requestCdn(nextWantedCdn);
359
359
  }
360
360
 
361
- const canceller = new TaskCanceller({ cancelOn: cancellationSignal });
361
+ const canceller = new TaskCanceller();
362
+ const unlinkCanceller = canceller.linkToSignal(cancellationSignal);
362
363
  return new Promise<T>((res, rej) => {
363
364
  /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
364
365
  cdnPrioritizer?.addEventListener("priorityChange", () => {
365
366
  const updatedPrioritaryCdn = getCdnToRequest();
366
- if (cancellationSignal.isCancelled) {
367
+ if (cancellationSignal.isCancelled()) {
367
368
  throw cancellationSignal.cancellationError;
368
369
  }
369
370
  if (updatedPrioritaryCdn === undefined) {
370
- return rej(prevRequestError);
371
+ return cleanAndReject(prevRequestError);
371
372
  }
372
373
  if (updatedPrioritaryCdn !== nextWantedCdn) {
373
374
  canceller.cancel();
374
375
  waitPotentialBackoffAndRequest(updatedPrioritaryCdn, prevRequestError)
375
- .then(res, rej);
376
+ .then(cleanAndResolve, cleanAndReject);
376
377
  }
377
378
  }, canceller.signal);
378
379
 
379
380
  cancellableSleep(blockedFor, canceller.signal)
380
- .then(() => requestCdn(nextWantedCdn).then(res, rej), noop);
381
+ .then(() => requestCdn(nextWantedCdn)
382
+ .then(cleanAndResolve, cleanAndReject), noop);
383
+
384
+ function cleanAndResolve(response : T) {
385
+ unlinkCanceller();
386
+ res(response);
387
+ }
388
+ function cleanAndReject(err : unknown) {
389
+ unlinkCanceller();
390
+ rej(err);
391
+ }
381
392
  });
382
393
  }
383
394
 
@@ -167,7 +167,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
167
167
  }
168
168
  }, { emitCurrentValue: true, clearSignal: cancelSignal }))
169
169
  .catch((err) => {
170
- if (!cancelSignal.isCancelled) {
170
+ if (!cancelSignal.isCancelled()) {
171
171
  this._onFatalError(err);
172
172
  }
173
173
  });
@@ -25,13 +25,13 @@ import {
25
25
  } from "../../public_types";
26
26
  import { ITransportPipelines } from "../../transports";
27
27
  import assert from "../../utils/assert";
28
+ import createCancellablePromise from "../../utils/create_cancellable_promise";
28
29
  import objectAssign from "../../utils/object_assign";
29
30
  import createSharedReference, {
30
31
  IReadOnlySharedReference,
31
32
  ISharedReference,
32
33
  } from "../../utils/reference";
33
34
  import TaskCanceller, {
34
- CancellationError,
35
35
  CancellationSignal,
36
36
  } from "../../utils/task_canceller";
37
37
  import AdaptiveRepresentationSelector, {
@@ -125,21 +125,22 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
125
125
  if (this._initialManifestProm !== null) {
126
126
  return;
127
127
  }
128
- this._initialManifestProm = new Promise((res, rej) => {
129
- this._initCanceller.signal.register((err : CancellationError) => {
130
- this._manifestFetcher.dispose();
131
- rej(err);
132
- });
133
- this._manifestFetcher.addEventListener("warning", (err : IPlayerError) =>
134
- this.trigger("warning", err));
135
- this._manifestFetcher.addEventListener("error", (err : unknown) => {
136
- this.trigger("error", err);
137
- rej(err);
138
- });
139
- this._manifestFetcher.addEventListener("manifestReady", (manifest) => {
140
- res(manifest);
128
+ this._initialManifestProm = createCancellablePromise(
129
+ this._initCanceller.signal,
130
+ (res, rej) => {
131
+ this._manifestFetcher.addEventListener("warning", (err : IPlayerError) =>
132
+ this.trigger("warning", err));
133
+ this._manifestFetcher.addEventListener("error", (err : unknown) => {
134
+ this.trigger("error", err);
135
+ rej(err);
136
+ });
137
+ this._manifestFetcher.addEventListener("manifestReady", (manifest) => {
138
+ res(manifest);
139
+ });
141
140
  });
142
- this._manifestFetcher.start();
141
+ this._manifestFetcher.start();
142
+ this._initCanceller.signal.register(() => {
143
+ this._manifestFetcher.dispose();
143
144
  });
144
145
  }
145
146
 
@@ -192,7 +193,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
192
193
  }
193
194
 
194
195
  private _onFatalError(err : unknown) {
195
- if (this._initCanceller.isUsed) {
196
+ if (this._initCanceller.isUsed()) {
196
197
  return;
197
198
  }
198
199
  this._initCanceller.cancel();
@@ -206,13 +207,9 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
206
207
  drmSystemId : string | undefined;
207
208
  unlinkMediaSource : TaskCanceller; }>
208
209
  {
209
- return new Promise((resolve, reject) => {
210
+ const initCanceller = this._initCanceller;
211
+ return createCancellablePromise(initCanceller.signal, (resolve) => {
210
212
  const { keySystems } = this._settings;
211
- const initCanceller = this._initCanceller;
212
-
213
- const unregisterReject = initCanceller.signal.register((err) => {
214
- reject(err);
215
- });
216
213
 
217
214
  /** Initialize decryption capabilities. */
218
215
  const drmInitRef =
@@ -227,9 +224,8 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
227
224
  }
228
225
  stopListeningToDrmUpdates();
229
226
 
230
- const mediaSourceCanceller = new TaskCanceller({
231
- cancelOn: initCanceller.signal,
232
- });
227
+ const mediaSourceCanceller = new TaskCanceller();
228
+ mediaSourceCanceller.linkToSignal(initCanceller.signal);
233
229
  openMediaSource(mediaElement, mediaSourceCanceller.signal)
234
230
  .then((mediaSource) => {
235
231
  const lastDrmStatus = drmInitRef.getValue();
@@ -238,7 +234,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
238
234
  drmInitRef.onUpdate((newDrmStatus, stopListeningToDrmUpdatesAgain) => {
239
235
  if (newDrmStatus.initializationState.type === "initialized") {
240
236
  stopListeningToDrmUpdatesAgain();
241
- unregisterReject();
242
237
  resolve({ mediaSource,
243
238
  drmSystemId: newDrmStatus.drmSystemId,
244
239
  unlinkMediaSource: mediaSourceCanceller });
@@ -246,7 +241,6 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
246
241
  }
247
242
  }, { emitCurrentValue: true, clearSignal: initCanceller.signal });
248
243
  } else if (drmStatus.initializationState.type === "initialized") {
249
- unregisterReject();
250
244
  resolve({ mediaSource,
251
245
  drmSystemId: drmStatus.drmSystemId,
252
246
  unlinkMediaSource: mediaSourceCanceller });
@@ -254,7 +248,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
254
248
  }
255
249
  })
256
250
  .catch((err) => {
257
- if (mediaSourceCanceller.isUsed) {
251
+ if (mediaSourceCanceller.isUsed()) {
258
252
  return;
259
253
  }
260
254
  this._onFatalError(err);
@@ -311,7 +305,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
311
305
  initCanceller.signal);
312
306
 
313
307
  this.trigger("manifestReady", manifest);
314
- if (initCanceller.isUsed) {
308
+ if (initCanceller.isUsed()) {
315
309
  return ;
316
310
  }
317
311
 
@@ -359,15 +353,16 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
359
353
  autoPlay : boolean; }
360
354
  ) : void {
361
355
  currentCanceller.cancel();
362
- if (initCanceller.isUsed) {
356
+ if (initCanceller.isUsed()) {
363
357
  return;
364
358
  }
365
359
  triggerEvent("reloadingMediaSource", null);
366
- if (initCanceller.isUsed) {
360
+ if (initCanceller.isUsed()) {
367
361
  return;
368
362
  }
369
363
 
370
- const newCanceller = new TaskCanceller({ cancelOn: initCanceller.signal });
364
+ const newCanceller = new TaskCanceller();
365
+ newCanceller.linkToSignal(initCanceller.signal);
371
366
  openMediaSource(mediaElement, newCanceller.signal)
372
367
  .then(newMediaSource => {
373
368
  recursivelyLoadOnMediaSource(newMediaSource,
@@ -376,7 +371,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
376
371
  newCanceller);
377
372
  })
378
373
  .catch((err) => {
379
- if (newCanceller.isUsed) {
374
+ if (newCanceller.isUsed()) {
380
375
  return;
381
376
  }
382
377
  onFatalError(err);
@@ -431,7 +426,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
431
426
  (err) => this.trigger("warning", err),
432
427
  cancelSignal);
433
428
 
434
- if (cancelSignal.isCancelled) {
429
+ if (cancelSignal.isCancelled()) {
435
430
  return;
436
431
  }
437
432
 
@@ -483,7 +478,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
483
478
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
484
479
  })
485
480
  .catch((err) => {
486
- if (cancelSignal.isCancelled) {
481
+ if (cancelSignal.isCancelled()) {
487
482
  return; // Current loading cancelled, no need to trigger the error
488
483
  }
489
484
  this._onFatalError(err);
@@ -519,7 +514,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
519
514
  discontinuity: imminentDiscontinuity,
520
515
  position,
521
516
  });
522
- if (cancelSignal.isCancelled) {
517
+ if (cancelSignal.isCancelled()) {
523
518
  return; // Previous call has stopped streams due to a side-effect
524
519
  }
525
520
 
@@ -560,7 +555,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
560
555
 
561
556
  adaptationChange: (value) => {
562
557
  self.trigger("adaptationChange", value);
563
- if (cancelSignal.isCancelled) {
558
+ if (cancelSignal.isCancelled()) {
564
559
  return; // Previous call has stopped streams due to a side-effect
565
560
  }
566
561
  contentTimeBoundariesObserver.onAdaptationChange(value.type,
@@ -570,7 +565,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
570
565
 
571
566
  representationChange: (value) => {
572
567
  self.trigger("representationChange", value);
573
- if (cancelSignal.isCancelled) {
568
+ if (cancelSignal.isCancelled()) {
574
569
  return; // Previous call has stopped streams due to a side-effect
575
570
  }
576
571
  contentTimeBoundariesObserver.onRepresentationChange(value.type, value.period);
@@ -584,7 +579,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
584
579
 
585
580
  periodStreamCleared: (value) => {
586
581
  contentTimeBoundariesObserver.onPeriodCleared(value.type, value.period);
587
- if (cancelSignal.isCancelled) {
582
+ if (cancelSignal.isCancelled()) {
588
583
  return; // Previous call has stopped streams due to a side-effect
589
584
  }
590
585
  self.trigger("periodStreamCleared", value);
@@ -615,7 +610,7 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
615
610
  encryptionDataEncountered: (value) => {
616
611
  for (const protectionData of value) {
617
612
  protectionRef.setValue(protectionData);
618
- if (cancelSignal.isCancelled) {
613
+ if (cancelSignal.isCancelled()) {
619
614
  return; // Previous call has stopped streams due to a side-effect
620
615
  }
621
616
  }
@@ -674,7 +669,8 @@ export default class MediaSourceContentInitializer extends ContentInitializer {
674
669
  });
675
670
  contentTimeBoundariesObserver.addEventListener("endOfStream", () => {
676
671
  if (endOfStreamCanceller === null) {
677
- endOfStreamCanceller = new TaskCanceller({ cancelOn: cancelSignal });
672
+ endOfStreamCanceller = new TaskCanceller();
673
+ endOfStreamCanceller.linkToSignal(cancelSignal);
678
674
  log.debug("Init: end-of-stream order received.");
679
675
  maintainEndOfStream(mediaSource, endOfStreamCanceller.signal);
680
676
  }
@@ -112,7 +112,7 @@ export default class ContentTimeBoundariesObserver
112
112
 
113
113
  manifest.addEventListener("manifestUpdate", () => {
114
114
  this.trigger("durationUpdate", getManifestDuration());
115
- if (cancelSignal.isCancelled) {
115
+ if (cancelSignal.isCancelled()) {
116
116
  return;
117
117
  }
118
118
  this._checkEndOfStream();
@@ -161,7 +161,7 @@ export default class ContentTimeBoundariesObserver
161
161
  }
162
162
  }
163
163
  }
164
- if (this._canceller.isUsed) {
164
+ if (this._canceller.isUsed()) {
165
165
  return;
166
166
  }
167
167
  if (adaptation === null) {
@@ -21,10 +21,9 @@ import {
21
21
  } from "../../../compat";
22
22
  import { MediaError } from "../../../errors";
23
23
  import log from "../../../log";
24
+ import createCancellablePromise from "../../../utils/create_cancellable_promise";
24
25
  import isNonEmptyString from "../../../utils/is_non_empty_string";
25
- import TaskCanceller, {
26
- CancellationSignal,
27
- } from "../../../utils/task_canceller";
26
+ import { CancellationSignal } from "../../../utils/task_canceller";
28
27
 
29
28
  /**
30
29
  * Dispose of ressources taken by the MediaSource:
@@ -128,21 +127,10 @@ export default function openMediaSource(
128
127
  mediaElement : HTMLMediaElement,
129
128
  unlinkSignal : CancellationSignal
130
129
  ) : Promise<MediaSource> {
131
- return new Promise((resolve, reject) => {
132
- let hasResolved = false;
130
+ return createCancellablePromise(unlinkSignal, (resolve) => {
133
131
  const mediaSource = createMediaSource(mediaElement, unlinkSignal);
134
- const eventListenerCanceller = new TaskCanceller({ cancelOn: unlinkSignal });
135
-
136
132
  events.onSourceOpen(mediaSource, () => {
137
- eventListenerCanceller.cancel();
138
- hasResolved = true;
139
133
  resolve(mediaSource);
140
- }, eventListenerCanceller.signal);
141
-
142
- unlinkSignal.register((error) => {
143
- if (!hasResolved) {
144
- reject(error);
145
- }
146
- });
134
+ }, unlinkSignal);
147
135
  });
148
136
  }
@@ -70,7 +70,8 @@ export default function triggerEndOfStream(
70
70
 
71
71
  log.debug("Init: Waiting SourceBuffers to be updated before calling endOfStream.");
72
72
 
73
- const innerCanceller = new TaskCanceller({ cancelOn: cancelSignal });
73
+ const innerCanceller = new TaskCanceller();
74
+ innerCanceller.linkToSignal(cancelSignal);
74
75
  for (const sourceBuffer of updatingSourceBuffers) {
75
76
  onSourceBufferUpdate(sourceBuffer, () => {
76
77
  innerCanceller.cancel();
@@ -94,10 +95,12 @@ export function maintainEndOfStream(
94
95
  mediaSource : MediaSource,
95
96
  cancelSignal : CancellationSignal
96
97
  ) : void {
97
- let endOfStreamCanceller = new TaskCanceller({ cancelOn: cancelSignal });
98
+ let endOfStreamCanceller = new TaskCanceller();
99
+ endOfStreamCanceller.linkToSignal(cancelSignal);
98
100
  onSourceOpen(mediaSource, () => {
99
101
  endOfStreamCanceller.cancel();
100
- endOfStreamCanceller = new TaskCanceller({ cancelOn: cancelSignal });
102
+ endOfStreamCanceller = new TaskCanceller();
103
+ endOfStreamCanceller.linkToSignal(cancelSignal);
101
104
  triggerEndOfStream(mediaSource, endOfStreamCanceller.signal);
102
105
  }, cancelSignal);
103
106
  triggerEndOfStream(mediaSource, endOfStreamCanceller.signal);
@@ -43,7 +43,8 @@ export default function getLoadedReference(
43
43
  isDirectfile : boolean,
44
44
  cancelSignal : CancellationSignal
45
45
  ) : IReadOnlySharedReference<boolean> {
46
- const listenCanceller = new TaskCanceller({ cancelOn: cancelSignal });
46
+ const listenCanceller = new TaskCanceller();
47
+ listenCanceller.linkToSignal(cancelSignal);
47
48
  const isLoaded = createSharedReference(false, listenCanceller.signal);
48
49
  playbackObserver.listen((observation) => {
49
50
  if (observation.rebuffering !== null ||