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

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 (167) hide show
  1. package/CHANGELOG.md +2 -1
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
  4. package/dist/_esm5.processed/compat/event_listeners.js +1 -1
  5. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +4 -2
  6. package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
  7. package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
  8. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
  9. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +105 -0
  10. package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
  11. package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
  12. package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
  13. package/dist/_esm5.processed/core/api/debug/index.js +2 -0
  14. package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
  15. package/dist/_esm5.processed/core/api/debug/modules/general_info.js +199 -0
  16. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
  17. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
  18. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
  19. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
  20. package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
  21. package/dist/_esm5.processed/core/api/debug/render.js +32 -0
  22. package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
  23. package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
  24. package/dist/_esm5.processed/core/api/playback_observer.js +3 -2
  25. package/dist/_esm5.processed/core/api/public_api.d.ts +3 -0
  26. package/dist/_esm5.processed/core/api/public_api.js +24 -13
  27. package/dist/_esm5.processed/core/api/utils.js +3 -3
  28. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +5 -5
  29. package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
  30. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
  31. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +37 -47
  32. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
  33. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
  34. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
  35. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
  36. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
  37. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
  38. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
  39. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +6 -5
  40. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
  41. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +27 -31
  42. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +2 -2
  43. package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
  44. package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
  45. package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
  46. package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
  47. package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
  48. package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +23 -19
  49. package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
  50. package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
  51. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +7 -10
  52. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +4 -2
  53. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
  54. package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +10 -11
  55. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +26 -12
  56. package/dist/_esm5.processed/core/stream/period/period_stream.js +11 -10
  57. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -15
  58. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +1 -1
  59. package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
  60. package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
  61. package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
  62. package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
  63. package/dist/_esm5.processed/experimental/features/index.js +1 -0
  64. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
  65. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +5 -4
  66. package/dist/_esm5.processed/features/features_object.js +1 -0
  67. package/dist/_esm5.processed/features/initialize_features.js +13 -10
  68. package/dist/_esm5.processed/features/types.d.ts +3 -0
  69. package/dist/_esm5.processed/manifest/adaptation.js +4 -0
  70. package/dist/_esm5.processed/manifest/manifest.js +2 -0
  71. package/dist/_esm5.processed/manifest/representation.js +11 -4
  72. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
  73. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
  74. package/dist/_esm5.processed/public_types.d.ts +1 -0
  75. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +13 -12
  76. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
  77. package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
  78. package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
  79. package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
  80. package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
  81. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
  82. package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
  83. package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
  84. package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
  85. package/dist/_esm5.processed/utils/reference.js +6 -0
  86. package/dist/_esm5.processed/utils/request/xhr.js +1 -1
  87. package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
  88. package/dist/_esm5.processed/utils/task_canceller.js +55 -22
  89. package/dist/rx-player.js +739 -626
  90. package/dist/rx-player.min.js +1 -1
  91. package/jest.config.js +1 -0
  92. package/package.json +18 -18
  93. package/scripts/build/constants.d.ts +1 -0
  94. package/scripts/report_build_sizes +4 -0
  95. package/sonar-project.properties +1 -1
  96. package/src/compat/eme/close_session.ts +2 -2
  97. package/src/compat/event_listeners.ts +1 -1
  98. package/src/core/adaptive/adaptive_representation_selector.ts +4 -2
  99. package/src/core/api/debug/buffer_graph.ts +247 -0
  100. package/src/core/api/debug/buffer_size_graph.ts +131 -0
  101. package/src/core/api/debug/constants.ts +2 -0
  102. package/src/core/api/debug/index.ts +3 -0
  103. package/src/core/api/debug/modules/general_info.ts +204 -0
  104. package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
  105. package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
  106. package/src/core/api/debug/render.ts +40 -0
  107. package/src/core/api/debug/utils.ts +103 -0
  108. package/src/core/api/playback_observer.ts +4 -2
  109. package/src/core/api/public_api.ts +27 -13
  110. package/src/core/api/utils.ts +3 -3
  111. package/src/core/decrypt/attach_media_keys.ts +1 -1
  112. package/src/core/decrypt/content_decryptor.ts +1 -1
  113. package/src/core/decrypt/session_events_listener.ts +37 -49
  114. package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
  115. package/src/core/fetchers/cdn_prioritizer.ts +18 -9
  116. package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
  117. package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
  118. package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
  119. package/src/core/fetchers/utils/schedule_request.ts +6 -5
  120. package/src/core/init/directfile_content_initializer.ts +1 -1
  121. package/src/core/init/media_source_content_initializer.ts +37 -41
  122. package/src/core/init/utils/content_time_boundaries_observer.ts +2 -2
  123. package/src/core/init/utils/create_media_source.ts +4 -16
  124. package/src/core/init/utils/end_of_stream.ts +6 -3
  125. package/src/core/init/utils/get_loaded_reference.ts +2 -1
  126. package/src/core/init/utils/initial_seek_and_play.ts +9 -5
  127. package/src/core/init/utils/initialize_content_decryption.ts +2 -1
  128. package/src/core/init/utils/media_duration_updater.ts +25 -20
  129. package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
  130. package/src/core/init/utils/throw_on_media_error.ts +1 -1
  131. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +7 -11
  132. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +4 -2
  133. package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
  134. package/src/core/stream/adaptation/adaptation_stream.ts +10 -11
  135. package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -12
  136. package/src/core/stream/period/period_stream.ts +11 -10
  137. package/src/core/stream/representation/representation_stream.ts +15 -15
  138. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +1 -1
  139. package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
  140. package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
  141. package/src/experimental/features/debug_element.ts +13 -0
  142. package/src/experimental/features/index.ts +1 -0
  143. package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
  144. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +5 -4
  145. package/src/features/__tests__/initialize_features.test.ts +11 -0
  146. package/src/features/features_object.ts +1 -0
  147. package/src/features/initialize_features.ts +15 -10
  148. package/src/features/types.ts +9 -0
  149. package/src/manifest/adaptation.ts +7 -0
  150. package/src/manifest/manifest.ts +4 -0
  151. package/src/manifest/representation.ts +10 -4
  152. package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
  153. package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
  154. package/src/public_types.ts +1 -0
  155. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +30 -24
  156. package/src/transports/dash/low_latency_segment_loader.ts +2 -2
  157. package/src/transports/dash/manifest_parser.ts +1 -1
  158. package/src/transports/dash/segment_loader.ts +4 -4
  159. package/src/transports/local/segment_loader.ts +14 -30
  160. package/src/transports/smooth/segment_loader.ts +4 -4
  161. package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
  162. package/src/typings/globals.d.ts +2 -0
  163. package/src/utils/cancellable_sleep.ts +5 -14
  164. package/src/utils/create_cancellable_promise.ts +69 -0
  165. package/src/utils/reference.ts +6 -0
  166. package/src/utils/request/xhr.ts +1 -1
  167. package/src/utils/task_canceller.ts +63 -34
@@ -86,12 +86,13 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
86
86
  log.info("DRM: Binding session events", session.sessionId);
87
87
  var _a = keySystemOptions.getLicenseConfig, getLicenseConfig = _a === void 0 ? {} : _a;
88
88
  /** Allows to manually cancel everything the `SessionEventsListener` is doing. */
89
- var manualCanceller = new TaskCanceller({ cancelOn: cancelSignal });
89
+ var manualCanceller = new TaskCanceller();
90
+ manualCanceller.linkToSignal(cancelSignal);
90
91
  if (!isNullOrUndefined(session.closed)) {
91
92
  session.closed
92
93
  .then(function () { return manualCanceller.cancel(); })
93
94
  .catch(function (err) {
94
- if (cancelSignal.isCancelled) {
95
+ if (cancelSignal.isCancelled()) {
95
96
  return;
96
97
  }
97
98
  manualCanceller.cancel();
@@ -104,8 +105,8 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
104
105
  }, manualCanceller.signal);
105
106
  onKeyStatusesChange(session, function (keyStatusesEvent) {
106
107
  handleKeyStatusesChangeEvent(keyStatusesEvent).catch(function (error) {
107
- if (cancelSignal.isCancelled ||
108
- (manualCanceller.isUsed && error instanceof CancellationSignal)) {
108
+ if (cancelSignal.isCancelled() ||
109
+ (manualCanceller.isUsed() && error instanceof CancellationSignal)) {
109
110
  return;
110
111
  }
111
112
  manualCanceller.cancel();
@@ -122,7 +123,7 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
122
123
  var backoffOptions = getLicenseBackoffOptions(getLicenseConfig.retry);
123
124
  retryPromiseWithBackoff(function () { return runGetLicense(message, messageType); }, backoffOptions, manualCanceller.signal)
124
125
  .then(function (licenseObject) {
125
- if (manualCanceller.isUsed) {
126
+ if (manualCanceller.isUsed()) {
126
127
  return Promise.resolve();
127
128
  }
128
129
  if (isNullOrUndefined(licenseObject)) {
@@ -133,7 +134,7 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
133
134
  }
134
135
  })
135
136
  .catch(function (err) {
136
- if (manualCanceller.isUsed) {
137
+ if (manualCanceller.isUsed()) {
137
138
  return;
138
139
  }
139
140
  manualCanceller.cancel();
@@ -150,6 +151,8 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
150
151
  callbacks.onError(formattedError);
151
152
  });
152
153
  }, manualCanceller.signal);
154
+ checkAndHandleCurrentKeyStatuses();
155
+ return;
153
156
  /**
154
157
  * @param {Event} keyStatusesEvent
155
158
  * @returns {Promise}
@@ -162,7 +165,7 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
162
165
  return __generator(this, function (_a) {
163
166
  switch (_a.label) {
164
167
  case 0:
165
- if (manualCanceller.isUsed) {
168
+ if (manualCanceller.isUsed()) {
166
169
  return [2 /*return*/];
167
170
  }
168
171
  if (!(typeof keySystemOptions.onKeyStatusesChange === "function")) return [3 /*break*/, 7];
@@ -173,13 +176,13 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
173
176
  return [4 /*yield*/, keySystemOptions.onKeyStatusesChange(keyStatusesEvent, session)];
174
177
  case 2:
175
178
  ret = _a.sent();
176
- if (manualCanceller.isUsed) {
179
+ if (manualCanceller.isUsed()) {
177
180
  return [2 /*return*/];
178
181
  }
179
182
  return [3 /*break*/, 4];
180
183
  case 3:
181
184
  error_1 = _a.sent();
182
- if (cancelSignal.isCancelled) {
185
+ if (cancelSignal.isCancelled()) {
183
186
  return [2 /*return*/];
184
187
  }
185
188
  err = new EncryptedMediaError("KEY_STATUS_CHANGE_ERROR", "Unknown `onKeyStatusesChange` error");
@@ -201,32 +204,13 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
201
204
  });
202
205
  });
203
206
  }
204
- /**
205
- * Check current MediaKeyStatus for each key in the given MediaKeySession and:
206
- * - throw if at least one status is a non-recoverable error
207
- * - call warning callback for recoverable errors
208
- * - call onKeyUpdate callback when the MediaKeyStatus of any key is updated
209
- */
210
- function handleKeyStatusEvent() {
211
- if (manualCanceller.isUsed || session.keyStatuses.size === 0) {
212
- return;
213
- }
214
- var _a = checkKeyStatuses(session, keySystemOptions, keySystem), warning = _a.warning, blacklistedKeyIds = _a.blacklistedKeyIds, whitelistedKeyIds = _a.whitelistedKeyIds;
215
- if (warning !== undefined) {
216
- callbacks.onWarning(warning);
217
- if (manualCanceller.isUsed) {
218
- return;
219
- }
220
- }
221
- callbacks.onKeyUpdate({ whitelistedKeyIds: whitelistedKeyIds, blacklistedKeyIds: blacklistedKeyIds });
222
- }
223
207
  return __generator(this, function (_a) {
224
208
  switch (_a.label) {
225
209
  case 0:
226
210
  log.info("DRM: keystatuseschange event received", session.sessionId);
227
211
  return [4 /*yield*/, Promise.all([
228
212
  runOnKeyStatusesChangeCallback(),
229
- Promise.resolve(handleKeyStatusEvent()),
213
+ Promise.resolve(checkAndHandleCurrentKeyStatuses()),
230
214
  ])];
231
215
  case 1:
232
216
  _a.sent();
@@ -235,36 +219,42 @@ export default function SessionEventsListener(session, keySystemOptions, keySyst
235
219
  });
236
220
  });
237
221
  }
222
+ /**
223
+ * Check current MediaKeyStatus for each key in the given MediaKeySession and:
224
+ * - throw if at least one status is a non-recoverable error
225
+ * - call warning callback for recoverable errors
226
+ * - call onKeyUpdate callback when the MediaKeyStatus of any key is updated
227
+ */
228
+ function checkAndHandleCurrentKeyStatuses() {
229
+ if (manualCanceller.isUsed() || session.keyStatuses.size === 0) {
230
+ return;
231
+ }
232
+ var _a = checkKeyStatuses(session, keySystemOptions, keySystem), warning = _a.warning, blacklistedKeyIds = _a.blacklistedKeyIds, whitelistedKeyIds = _a.whitelistedKeyIds;
233
+ if (warning !== undefined) {
234
+ callbacks.onWarning(warning);
235
+ if (manualCanceller.isUsed()) {
236
+ return;
237
+ }
238
+ }
239
+ callbacks.onKeyUpdate({ whitelistedKeyIds: whitelistedKeyIds, blacklistedKeyIds: blacklistedKeyIds });
240
+ }
238
241
  function runGetLicense(message, messageType) {
242
+ var timeoutId;
239
243
  return new Promise(function (res, rej) {
240
244
  log.debug("DRM: Calling `getLicense`", messageType);
241
245
  var getLicense = keySystemOptions.getLicense(message, messageType);
242
246
  var getLicenseTimeout = isNullOrUndefined(getLicenseConfig.timeout) ?
243
247
  10 * 1000 :
244
248
  getLicenseConfig.timeout;
245
- var timeoutId;
246
249
  if (getLicenseTimeout >= 0) {
247
250
  timeoutId = setTimeout(function () {
248
251
  rej(new GetLicenseTimeoutError("\"getLicense\" timeout exceeded (".concat(getLicenseTimeout, " ms)")));
249
252
  }, getLicenseTimeout);
250
253
  }
251
- try {
252
- Promise.resolve(getLicense).then(function (val) {
253
- clearTimeoutIfOne();
254
- res(val);
255
- }, function (err) {
256
- clearTimeoutIfOne();
257
- rej(err);
258
- });
259
- }
260
- catch (err) {
261
- clearTimeoutIfOne();
262
- rej(err);
263
- }
264
- function clearTimeoutIfOne() {
265
- if (timeoutId !== undefined) {
266
- clearTimeout(timeoutId);
267
- }
254
+ Promise.resolve(getLicense).then(res, rej);
255
+ }).finally(function () {
256
+ if (timeoutId !== undefined) {
257
+ clearTimeout(timeoutId);
268
258
  }
269
259
  });
270
260
  }
@@ -40,6 +40,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
40
40
  /* eslint-disable-next-line max-len */
41
41
  import getUUIDKidFromKeyStatusKID from "../../../compat/eme/get_uuid_kid_from_keystatus_kid";
42
42
  import { EncryptedMediaError } from "../../../errors";
43
+ import log from "../../../log";
43
44
  import assertUnreachable from "../../../utils/assert_unreachable";
44
45
  import { bytesToHex } from "../../../utils/string_parsing";
45
46
  /**
@@ -95,6 +96,9 @@ export default function checkKeyStatuses(session, options, keySystem) {
95
96
  })(), keyStatus = _a[0], keyStatusKeyId = _a[1];
96
97
  var keyId = getUUIDKidFromKeyStatusKID(keySystem, new Uint8Array(keyStatusKeyId));
97
98
  var keyStatusObj = { keyId: keyId.buffer, keyStatus: keyStatus };
99
+ if (log.hasLevel("DEBUG")) {
100
+ log.debug("DRM: key status update (".concat(bytesToHex(keyId), "): ").concat(keyStatus));
101
+ }
98
102
  switch (keyStatus) {
99
103
  case KEY_STATUSES.EXPIRED: {
100
104
  var error = new EncryptedMediaError("KEY_STATUS_CHANGE_ERROR", "A decryption key expired (".concat(bytesToHex(keyId), ")"), { keyStatuses: __spreadArray([keyStatusObj], badKeyStatuses, true) });
@@ -14,17 +14,20 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  import { ICdnMetadata } from "../../parsers/manifest";
17
- import { IPlayerError } from "../../public_types";
18
17
  import EventEmitter from "../../utils/event_emitter";
19
18
  import { CancellationSignal } from "../../utils/task_canceller";
20
19
  /**
21
- * Class signaling the priority between multiple CDN available for any given
22
- * resource.
20
+ * Class storing and signaling the priority between multiple CDN available for
21
+ * any given resource.
23
22
  *
24
- * This class might perform requests and schedule timeouts by itself to keep its
25
- * internal list of CDN priority up-to-date.
26
- * When it is not needed anymore, you should call the `dispose` method to clear
27
- * all resources.
23
+ * This class was first created to implement the complexities behind
24
+ * Content Steering features, though its handling hasn't been added yet as we
25
+ * wait for its specification to be both standardized and relied on in the wild.
26
+ * In the meantime, it acts as an abstraction for the simple concept of
27
+ * avoiding to request a CDN for any segment when an issue is encountered with
28
+ * one (e.g. HTTP 500 statuses) and several CDN exist for a given resource. It
29
+ * should be noted that this is also one of the planified features of the
30
+ * Content Steering specification.
28
31
  *
29
32
  * @class CdnPrioritizer
30
33
  */
@@ -92,7 +95,13 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
92
95
  */
93
96
  private _removeIndexFromDowngradeList;
94
97
  }
98
+ /** Events sent by a `CdnPrioritizer` */
95
99
  export interface ICdnPrioritizerEvents {
96
- warnings: IPlayerError[];
100
+ /**
101
+ * The priority of one or several CDN changed.
102
+ *
103
+ * You might want to re-check if a CDN should still be used when this event
104
+ * is triggered.
105
+ */
97
106
  priorityChange: null;
98
107
  }
@@ -32,13 +32,17 @@ import config from "../../config";
32
32
  import arrayFindIndex from "../../utils/array_find_index";
33
33
  import EventEmitter from "../../utils/event_emitter";
34
34
  /**
35
- * Class signaling the priority between multiple CDN available for any given
36
- * resource.
35
+ * Class storing and signaling the priority between multiple CDN available for
36
+ * any given resource.
37
37
  *
38
- * This class might perform requests and schedule timeouts by itself to keep its
39
- * internal list of CDN priority up-to-date.
40
- * When it is not needed anymore, you should call the `dispose` method to clear
41
- * all resources.
38
+ * This class was first created to implement the complexities behind
39
+ * Content Steering features, though its handling hasn't been added yet as we
40
+ * wait for its specification to be both standardized and relied on in the wild.
41
+ * In the meantime, it acts as an abstraction for the simple concept of
42
+ * avoiding to request a CDN for any segment when an issue is encountered with
43
+ * one (e.g. HTTP 500 statuses) and several CDN exist for a given resource. It
44
+ * should be noted that this is also one of the planified features of the
45
+ * Content Steering specification.
42
46
  *
43
47
  * @class CdnPrioritizer
44
48
  */
@@ -145,7 +145,7 @@ var ManifestFetcher = /** @class */ (function (_super) {
145
145
  manifestProm
146
146
  .then(function (val) {
147
147
  _this.trigger("manifestReady", val.manifest);
148
- if (!_this._canceller.isUsed) {
148
+ if (!_this._canceller.isUsed()) {
149
149
  _this._recursivelyRefreshManifest(val.manifest, val);
150
150
  }
151
151
  })
@@ -311,7 +311,7 @@ var ManifestFetcher = /** @class */ (function (_super) {
311
311
  function onWarnings(warnings) {
312
312
  for (var _i = 0, warnings_1 = warnings; _i < warnings_1.length; _i++) {
313
313
  var warning = warnings_1[_i];
314
- if (cancelSignal.isCancelled) {
314
+ if (cancelSignal.isCancelled()) {
315
315
  return;
316
316
  }
317
317
  var formattedError = formatError(warning, {
@@ -430,7 +430,8 @@ var ManifestFetcher = /** @class */ (function (_super) {
430
430
  * be effectively considered.
431
431
  * `nextRefreshCanceller` will allow to cancel every other when one is triggered.
432
432
  */
433
- var nextRefreshCanceller = new TaskCanceller({ cancelOn: this._canceller.signal });
433
+ var nextRefreshCanceller = new TaskCanceller();
434
+ nextRefreshCanceller.linkToSignal(this._canceller.signal);
434
435
  /* Function to manually schedule a Manifest refresh */
435
436
  this.scheduleManualRefresh = function (settings) {
436
437
  var enablePartialRefresh = settings.enablePartialRefresh, delay = settings.delay, canUseUnsafeMode = settings.canUseUnsafeMode;
@@ -603,7 +604,7 @@ var ManifestFetcher = /** @class */ (function (_super) {
603
604
  });
604
605
  };
605
606
  ManifestFetcher.prototype._onFatalError = function (err) {
606
- if (this._canceller.isUsed) {
607
+ if (this._canceller.isUsed()) {
607
608
  return;
608
609
  }
609
610
  this.trigger("error", err);
@@ -26,10 +26,22 @@ import CdnPrioritizer from "../cdn_prioritizer";
26
26
  * An `ISegmentFetcher` also implements a retry mechanism, based on the given
27
27
  * `options` argument, which may retry a segment request when it fails.
28
28
  *
29
- * @param {string} bufferType
30
- * @param {Object} pipeline
31
- * @param {Object} lifecycleCallbacks
32
- * @param {Object} options
29
+ * @param {string} bufferType - Type of buffer concerned (e.g. `"audio"`,
30
+ * `"video"`, `"text" etc.)
31
+ * @param {Object} pipeline - The transport-specific logic allowing to load
32
+ * segments of the given buffer type and transport protocol (e.g. DASH).
33
+ * @param {Object|null} cdnPrioritizer - Abstraction allowing to synchronize,
34
+ * update and keep track of the priorization of the CDN to use to load any given
35
+ * segment, in cases where multiple ones are available.
36
+ *
37
+ * Can be set to `null` in which case a minimal priorization logic will be used
38
+ * instead.
39
+ * @param {Object} lifecycleCallbacks - Callbacks that can be registered to be
40
+ * informed when new requests are made, ended, new metrics are available etc.
41
+ * This should be mainly useful to implement an adaptive logic relying on those
42
+ * metrics and events.
43
+ * @param {Object} options - Various tweaking options allowing to configure the
44
+ * behavior of the returned `ISegmentFetcher`.
33
45
  * @returns {Function}
34
46
  */
35
47
  export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(bufferType: IBufferType, pipeline: ISegmentPipeline<TLoadedFormat, TSegmentDataType>, cdnPrioritizer: CdnPrioritizer | null, lifecycleCallbacks: ISegmentFetcherLifecycleCallbacks, options: ISegmentFetcherOptions): ISegmentFetcher<TSegmentDataType>;
@@ -59,10 +71,15 @@ export interface ISegmentFetcherCallbacks<TSegmentDataType> {
59
71
  /**
60
72
  * Callback called when all decodable chunks of the loaded segment have been
61
73
  * communicated through the `onChunk` callback.
74
+ *
75
+ * This callback is called before the corresponding `ISegmentFetcher`'s
76
+ * returned Promise is resolved.
62
77
  */
63
78
  onAllChunksReceived(): void;
64
79
  /**
65
- * Callback called when the segment request has to restart from scratch. */
80
+ * Callback called when the segment request has to restart from scratch, e.g.
81
+ * due to a request error.
82
+ */
66
83
  onRetry(error: IPlayerError): void;
67
84
  }
68
85
  /** Content used by the segment loader as a context to load a new segment. */
@@ -69,10 +69,22 @@ var generateRequestID = idGenerator();
69
69
  * An `ISegmentFetcher` also implements a retry mechanism, based on the given
70
70
  * `options` argument, which may retry a segment request when it fails.
71
71
  *
72
- * @param {string} bufferType
73
- * @param {Object} pipeline
74
- * @param {Object} lifecycleCallbacks
75
- * @param {Object} options
72
+ * @param {string} bufferType - Type of buffer concerned (e.g. `"audio"`,
73
+ * `"video"`, `"text" etc.)
74
+ * @param {Object} pipeline - The transport-specific logic allowing to load
75
+ * segments of the given buffer type and transport protocol (e.g. DASH).
76
+ * @param {Object|null} cdnPrioritizer - Abstraction allowing to synchronize,
77
+ * update and keep track of the priorization of the CDN to use to load any given
78
+ * segment, in cases where multiple ones are available.
79
+ *
80
+ * Can be set to `null` in which case a minimal priorization logic will be used
81
+ * instead.
82
+ * @param {Object} lifecycleCallbacks - Callbacks that can be registered to be
83
+ * informed when new requests are made, ended, new metrics are available etc.
84
+ * This should be mainly useful to implement an adaptive logic relying on those
85
+ * metrics and events.
86
+ * @param {Object} options - Various tweaking options allowing to configure the
87
+ * behavior of the returned `ISegmentFetcher`.
76
88
  * @returns {Function}
77
89
  */
78
90
  export default function createSegmentFetcher(bufferType, pipeline, cdnPrioritizer, lifecycleCallbacks, options) {
@@ -97,8 +109,17 @@ export default function createSegmentFetcher(bufferType, pipeline, cdnPrioritize
97
109
  * @returns {Promise}
98
110
  */
99
111
  return function fetchSegment(content, fetcherCallbacks, cancellationSignal) {
100
- var _a, _b;
112
+ var _a, _b, _c;
101
113
  return __awaiter(this, void 0, void 0, function () {
114
+ function onCancellation() {
115
+ var _a;
116
+ if (requestInfo !== undefined) {
117
+ return; // Request already terminated
118
+ }
119
+ log.debug("SF: Segment request cancelled", segmentIdString);
120
+ requestInfo = null;
121
+ (_a = lifecycleCallbacks.onRequestEnd) === null || _a === void 0 ? void 0 : _a.call(lifecycleCallbacks, { id: requestId });
122
+ }
102
123
  /**
103
124
  * Call a segment loader for the given URL with the right arguments.
104
125
  * @param {Object|null} cdnMetadata
@@ -169,8 +190,8 @@ export default function createSegmentFetcher(bufferType, pipeline, cdnPrioritize
169
190
  }
170
191
  }
171
192
  var segmentIdString, requestId, requestInfo, parsedChunks, segmentDurationAcc, metricsSent, loaderCallbacks, cached, res, loadedData, err_1;
172
- return __generator(this, function (_c) {
173
- switch (_c.label) {
193
+ return __generator(this, function (_d) {
194
+ switch (_d.label) {
174
195
  case 0:
175
196
  segmentIdString = getLoggableSegmentId(content);
176
197
  requestId = generateRequestID();
@@ -216,21 +237,13 @@ export default function createSegmentFetcher(bufferType, pipeline, cdnPrioritize
216
237
  log.debug("SF: Beginning request", segmentIdString);
217
238
  (_a = lifecycleCallbacks.onRequestBegin) === null || _a === void 0 ? void 0 : _a.call(lifecycleCallbacks, { requestTimestamp: performance.now(),
218
239
  id: requestId, content: content });
219
- cancellationSignal.register(function () {
220
- var _a;
221
- if (requestInfo !== undefined) {
222
- return; // Request already terminated
223
- }
224
- log.debug("SF: Segment request cancelled", segmentIdString);
225
- requestInfo = null;
226
- (_a = lifecycleCallbacks.onRequestEnd) === null || _a === void 0 ? void 0 : _a.call(lifecycleCallbacks, { id: requestId });
227
- });
228
- _c.label = 1;
240
+ cancellationSignal.register(onCancellation);
241
+ _d.label = 1;
229
242
  case 1:
230
- _c.trys.push([1, 3, , 4]);
243
+ _d.trys.push([1, 3, , 4]);
231
244
  return [4 /*yield*/, scheduleRequestWithCdns(content.representation.cdnMetadata, cdnPrioritizer, callLoaderWithUrl, objectAssign({ onRetry: onRetry }, options), cancellationSignal)];
232
245
  case 2:
233
- res = _c.sent();
246
+ res = _d.sent();
234
247
  if (res.resultType === "segment-loaded") {
235
248
  loadedData = res.resultData.responseData;
236
249
  if (cache !== undefined) {
@@ -250,21 +263,24 @@ export default function createSegmentFetcher(bufferType, pipeline, cdnPrioritize
250
263
  else {
251
264
  requestInfo = null;
252
265
  }
253
- if (!cancellationSignal.isCancelled) {
266
+ if (!cancellationSignal.isCancelled()) {
254
267
  // The current task could have been canceled as a result of one
255
268
  // of the previous callbacks call. In that case, we don't want to send
256
269
  // a "requestEnd" again as it has already been sent on cancellation.
257
270
  (_b = lifecycleCallbacks.onRequestEnd) === null || _b === void 0 ? void 0 : _b.call(lifecycleCallbacks, { id: requestId });
258
271
  }
272
+ cancellationSignal.deregister(onCancellation);
259
273
  return [3 /*break*/, 4];
260
274
  case 3:
261
- err_1 = _c.sent();
275
+ err_1 = _d.sent();
276
+ cancellationSignal.deregister(onCancellation);
262
277
  requestInfo = null;
263
278
  if (err_1 instanceof CancellationError) {
264
279
  log.debug("SF: Segment request aborted", segmentIdString);
265
280
  throw err_1;
266
281
  }
267
282
  log.debug("SF: Segment request failed", segmentIdString);
283
+ (_c = lifecycleCallbacks.onRequestEnd) === null || _c === void 0 ? void 0 : _c.call(lifecycleCallbacks, { id: requestId });
268
284
  throw errorSelector(err_1);
269
285
  case 4: return [2 /*return*/];
270
286
  }
@@ -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, { CancellationError, } from "../../../utils/task_canceller";
4
5
  var TaskPrioritizer = /** @class */ (function () {
5
6
  /**
@@ -41,18 +42,31 @@ var TaskPrioritizer = /** @class */ (function () {
41
42
  TaskPrioritizer.prototype.create = function (taskFn, priority, callbacks, cancelSignal) {
42
43
  var _this = this;
43
44
  var newTask;
44
- return new Promise(function (resolve, reject) {
45
+ return createCancellablePromise(cancelSignal, function (resolve, reject) {
45
46
  /** Function allowing to start the underlying Promise. */
46
47
  var trigger = function () {
47
48
  if (newTask.hasEnded) {
48
- unregisterCancelSignal();
49
49
  return;
50
50
  }
51
- var interrupter = new TaskCanceller({ cancelOn: cancelSignal });
51
+ var finishTask = function () {
52
+ unlinkInterrupter();
53
+ _this._endTask(newTask);
54
+ };
55
+ var onResolve = function (value) {
56
+ callbacks.beforeEnded();
57
+ finishTask();
58
+ resolve(value);
59
+ };
60
+ var onReject = function (err) {
61
+ finishTask();
62
+ reject(err);
63
+ };
64
+ var interrupter = new TaskCanceller();
65
+ var unlinkInterrupter = interrupter.linkToSignal(cancelSignal);
52
66
  newTask.interrupter = interrupter;
53
67
  interrupter.signal.register(function () {
54
68
  newTask.interrupter = null;
55
- if (!cancelSignal.isCancelled) {
69
+ if (!cancelSignal.isCancelled()) {
56
70
  callbacks.beforeInterrupted();
57
71
  }
58
72
  });
@@ -63,31 +77,14 @@ var TaskPrioritizer = /** @class */ (function () {
63
77
  newTask.taskFn(interrupter.signal)
64
78
  .then(onResolve)
65
79
  .catch(function (err) {
66
- if (!cancelSignal.isCancelled &&
67
- interrupter.isUsed &&
80
+ if (!cancelSignal.isCancelled() &&
81
+ interrupter.isUsed() &&
68
82
  err instanceof CancellationError) {
69
83
  return;
70
84
  }
71
85
  onReject(err);
72
86
  });
73
87
  };
74
- var unregisterCancelSignal = cancelSignal.register(function (cancellationError) {
75
- _this._endTask(newTask);
76
- reject(cancellationError);
77
- });
78
- var finishTask = function () {
79
- unregisterCancelSignal();
80
- _this._endTask(newTask);
81
- };
82
- var onResolve = function (value) {
83
- callbacks.beforeEnded();
84
- finishTask();
85
- resolve(value);
86
- };
87
- var onReject = function (err) {
88
- finishTask();
89
- reject(err);
90
- };
91
88
  newTask = {
92
89
  hasEnded: false,
93
90
  priority: priority,
@@ -112,6 +109,7 @@ var TaskPrioritizer = /** @class */ (function () {
112
109
  _this._interruptCancellableTasks();
113
110
  }
114
111
  }
112
+ return function () { return _this._endTask(newTask); };
115
113
  });
116
114
  };
117
115
  TaskPrioritizer.prototype._endTask = function (task) {
@@ -288,14 +288,14 @@ export function scheduleRequestWithCdns(cdns, cdnPrioritizer, performRequest, op
288
288
  var nextCdn;
289
289
  return __generator(this, function (_a) {
290
290
  nextCdn = getCdnToRequest();
291
- if (cancellationSignal.isCancelled) {
291
+ if (cancellationSignal.isCancelled()) {
292
292
  throw cancellationSignal.cancellationError;
293
293
  }
294
294
  if (nextCdn === undefined) {
295
295
  throw prevRequestError;
296
296
  }
297
297
  onRetry(prevRequestError);
298
- if (cancellationSignal.isCancelled) {
298
+ if (cancellationSignal.isCancelled()) {
299
299
  throw cancellationSignal.cancellationError;
300
300
  }
301
301
  return [2 /*return*/, waitPotentialBackoffAndRequest(nextCdn, prevRequestError)];
@@ -323,12 +323,13 @@ export function scheduleRequestWithCdns(cdns, cdnPrioritizer, performRequest, op
323
323
  if (blockedFor <= 0) {
324
324
  return requestCdn(nextWantedCdn);
325
325
  }
326
- var canceller = new TaskCanceller({ cancelOn: cancellationSignal });
326
+ var canceller = new TaskCanceller();
327
+ var unlinkCanceller = canceller.linkToSignal(cancellationSignal);
327
328
  return new Promise(function (res, rej) {
328
329
  /* eslint-disable-next-line @typescript-eslint/no-misused-promises */
329
330
  cdnPrioritizer === null || cdnPrioritizer === void 0 ? void 0 : cdnPrioritizer.addEventListener("priorityChange", function () {
330
331
  var updatedPrioritaryCdn = getCdnToRequest();
331
- if (cancellationSignal.isCancelled) {
332
+ if (cancellationSignal.isCancelled()) {
332
333
  throw cancellationSignal.cancellationError;
333
334
  }
334
335
  if (updatedPrioritaryCdn === undefined) {
@@ -342,7 +343,7 @@ export function scheduleRequestWithCdns(cdns, cdnPrioritizer, performRequest, op
342
343
  }, canceller.signal);
343
344
  cancellableSleep(blockedFor, canceller.signal)
344
345
  .then(function () { return requestCdn(nextWantedCdn).then(res, rej); }, noop);
345
- });
346
+ }).finally(unlinkCanceller);
346
347
  }
347
348
  /**
348
349
  * Takes in input the list of CDN that can be used to request the resource, in
@@ -145,7 +145,7 @@ var DirectFileContentInitializer = /** @class */ (function (_super) {
145
145
  }, { emitCurrentValue: true, clearSignal: cancelSignal });
146
146
  })
147
147
  .catch(function (err) {
148
- if (!cancelSignal.isCancelled) {
148
+ if (!cancelSignal.isCancelled()) {
149
149
  _this._onFatalError(err);
150
150
  }
151
151
  });