rx-player 3.29.0-dev.2022090500 → 3.29.0-dev.2022091600

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 (142) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/core/api/option_utils.js +7 -11
  4. package/dist/_esm5.processed/core/api/public_api.d.ts +4 -4
  5. package/dist/_esm5.processed/core/api/public_api.js +18 -19
  6. package/dist/_esm5.processed/core/api/{media_element_track_choice_manager.d.ts → tracks_management/media_element_track_choice_manager.d.ts} +2 -2
  7. package/dist/_esm5.processed/core/api/{media_element_track_choice_manager.js → tracks_management/media_element_track_choice_manager.js} +7 -4
  8. package/dist/_esm5.processed/core/api/{track_choice_manager.d.ts → tracks_management/track_choice_manager.d.ts} +2 -2
  9. package/dist/_esm5.processed/core/api/{track_choice_manager.js → tracks_management/track_choice_manager.js} +7 -7
  10. package/dist/_esm5.processed/core/api/{emit_seek_events.d.ts → utils.d.ts} +24 -1
  11. package/dist/_esm5.processed/core/api/{emit_seek_events.js → utils.js} +32 -1
  12. package/dist/_esm5.processed/core/decrypt/content_decryptor.d.ts +26 -0
  13. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +101 -9
  14. package/dist/_esm5.processed/core/decrypt/session_events_listener.d.ts +15 -18
  15. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +9 -7
  16. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.d.ts +19 -22
  17. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +87 -16
  18. package/dist/_esm5.processed/core/decrypt/utils/persistent_sessions_store.d.ts +1 -2
  19. package/dist/_esm5.processed/core/decrypt/utils/persistent_sessions_store.js +1 -2
  20. package/dist/_esm5.processed/core/fetchers/index.d.ts +2 -2
  21. package/dist/_esm5.processed/core/fetchers/segment/index.d.ts +2 -3
  22. package/dist/_esm5.processed/core/fetchers/segment/prioritized_segment_fetcher.d.ts +9 -9
  23. package/dist/_esm5.processed/core/fetchers/segment/prioritized_segment_fetcher.js +17 -15
  24. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +33 -40
  25. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +156 -153
  26. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.d.ts +3 -3
  27. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.js +2 -2
  28. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.d.ts +145 -0
  29. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +325 -0
  30. package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.d.ts +2 -2
  31. package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.js +3 -3
  32. package/dist/_esm5.processed/core/init/create_media_source.d.ts +10 -0
  33. package/dist/_esm5.processed/core/init/create_media_source.js +1 -1
  34. package/dist/_esm5.processed/core/init/stall_avoider.d.ts +2 -1
  35. package/dist/_esm5.processed/core/init/stall_avoider.js +2 -1
  36. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +1 -1
  37. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +3 -3
  38. package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +6 -2
  39. package/dist/_esm5.processed/core/stream/orchestrator/{get_blacklisted_ranges.d.ts → get_time_ranges_for_content.d.ts} +1 -1
  40. package/dist/_esm5.processed/core/stream/orchestrator/{get_blacklisted_ranges.js → get_time_ranges_for_content.js} +1 -1
  41. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +61 -19
  42. package/dist/_esm5.processed/core/stream/representation/downloading_queue.d.ts +8 -4
  43. package/dist/_esm5.processed/core/stream/representation/downloading_queue.js +200 -83
  44. package/dist/_esm5.processed/core/stream/types.d.ts +3 -1
  45. package/dist/_esm5.processed/errors/encrypted_media_error.d.ts +6 -1
  46. package/dist/_esm5.processed/errors/encrypted_media_error.js +4 -5
  47. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/index.d.ts +1 -1
  48. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/index.js +1 -1
  49. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +26 -0
  50. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +60 -0
  51. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.d.ts +6 -5
  52. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +40 -9
  53. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.d.ts +3 -2
  54. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.js +7 -14
  55. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/types.d.ts +1 -1
  56. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/{thumbnail_loader.d.ts → video_thumbnail_loader.d.ts} +1 -13
  57. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +331 -0
  58. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader_error.d.ts +1 -2
  59. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader_error.js +1 -2
  60. package/dist/_esm5.processed/features/initialize_features.js +2 -1
  61. package/dist/_esm5.processed/features/list/directfile.js +1 -1
  62. package/dist/_esm5.processed/features/types.d.ts +1 -1
  63. package/dist/_esm5.processed/manifest/index.d.ts +2 -2
  64. package/dist/_esm5.processed/public_types.d.ts +47 -0
  65. package/dist/_esm5.processed/utils/reference.d.ts +23 -3
  66. package/dist/_esm5.processed/utils/reference.js +23 -2
  67. package/dist/rx-player.js +1253 -1014
  68. package/dist/rx-player.min.js +1 -1
  69. package/package.json +1 -1
  70. package/sonar-project.properties +1 -1
  71. package/src/core/api/README.md +1 -1
  72. package/src/core/api/__tests__/{get_player_state.test.ts → utils.test.ts} +1 -1
  73. package/src/core/api/option_utils.ts +1 -5
  74. package/src/core/api/public_api.ts +15 -13
  75. package/src/core/api/{__tests__ → tracks_management/__tests__}/media_element_track_choice_manager.test.ts +0 -0
  76. package/src/core/api/{media_element_track_choice_manager.ts → tracks_management/media_element_track_choice_manager.ts} +10 -7
  77. package/src/core/api/{track_choice_manager.ts → tracks_management/track_choice_manager.ts} +9 -9
  78. package/src/core/api/{get_player_state.ts → utils.ts} +62 -11
  79. package/src/core/decrypt/content_decryptor.ts +112 -13
  80. package/src/core/decrypt/session_events_listener.ts +34 -30
  81. package/src/core/decrypt/utils/check_key_statuses.ts +89 -45
  82. package/src/core/decrypt/utils/persistent_sessions_store.ts +1 -2
  83. package/src/core/fetchers/index.ts +0 -12
  84. package/src/core/fetchers/segment/__tests__/task_prioritizer.test.ts +763 -0
  85. package/src/core/fetchers/segment/index.ts +0 -12
  86. package/src/core/fetchers/segment/prioritized_segment_fetcher.ts +40 -47
  87. package/src/core/fetchers/segment/segment_fetcher.ts +249 -268
  88. package/src/core/fetchers/segment/segment_fetcher_creator.ts +6 -7
  89. package/src/core/fetchers/segment/task_prioritizer.ts +460 -0
  90. package/src/core/fetchers/utils/try_urls_with_backoff.ts +3 -3
  91. package/src/core/init/create_media_source.ts +1 -1
  92. package/src/core/init/stall_avoider.ts +2 -1
  93. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +1 -1
  94. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +3 -3
  95. package/src/core/segment_buffers/implementations/types.ts +6 -2
  96. package/src/core/stream/orchestrator/{get_blacklisted_ranges.ts → get_time_ranges_for_content.ts} +1 -1
  97. package/src/core/stream/orchestrator/stream_orchestrator.ts +115 -56
  98. package/src/core/stream/representation/downloading_queue.ts +269 -142
  99. package/src/core/stream/types.ts +3 -1
  100. package/src/errors/encrypted_media_error.ts +20 -1
  101. package/src/experimental/tools/VideoThumbnailLoader/index.ts +1 -1
  102. package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +74 -0
  103. package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +50 -16
  104. package/src/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.ts +14 -21
  105. package/src/experimental/tools/VideoThumbnailLoader/types.ts +7 -4
  106. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +407 -0
  107. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader_error.ts +1 -2
  108. package/src/features/__tests__/initialize_features.test.ts +3 -1
  109. package/src/features/initialize_features.ts +2 -1
  110. package/src/features/list/__tests__/directfile.test.ts +1 -1
  111. package/src/features/list/directfile.ts +1 -1
  112. package/src/features/types.ts +1 -1
  113. package/src/manifest/index.ts +2 -0
  114. package/src/public_types.ts +53 -0
  115. package/src/utils/reference.ts +58 -4
  116. package/dist/_esm5.processed/core/api/get_player_state.d.ts +0 -28
  117. package/dist/_esm5.processed/core/api/get_player_state.js +0 -56
  118. package/dist/_esm5.processed/core/fetchers/segment/prioritizer.d.ts +0 -232
  119. package/dist/_esm5.processed/core/fetchers/segment/prioritizer.js +0 -422
  120. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/create_request.d.ts +0 -29
  121. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/create_request.js +0 -68
  122. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_complete_segment_id.d.ts +0 -24
  123. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_complete_segment_id.js +0 -29
  124. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_content_infos.d.ts +0 -25
  125. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_content_infos.js +0 -40
  126. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.d.ts +0 -33
  127. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.js +0 -124
  128. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_segments.d.ts +0 -17
  129. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_segments.js +0 -41
  130. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/push_data.d.ts +0 -22
  131. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/push_data.js +0 -21
  132. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/thumbnail_loader.js +0 -212
  133. package/src/core/api/emit_seek_events.ts +0 -66
  134. package/src/core/fetchers/segment/__tests__/prioritizer.test.ts +0 -962
  135. package/src/core/fetchers/segment/prioritizer.ts +0 -558
  136. package/src/experimental/tools/VideoThumbnailLoader/create_request.ts +0 -113
  137. package/src/experimental/tools/VideoThumbnailLoader/get_complete_segment_id.ts +0 -34
  138. package/src/experimental/tools/VideoThumbnailLoader/get_content_infos.ts +0 -48
  139. package/src/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.ts +0 -171
  140. package/src/experimental/tools/VideoThumbnailLoader/load_segments.ts +0 -69
  141. package/src/experimental/tools/VideoThumbnailLoader/push_data.ts +0 -48
  142. package/src/experimental/tools/VideoThumbnailLoader/thumbnail_loader.ts +0 -311
@@ -40,7 +40,7 @@ import EVENTS from "../events_generators";
40
40
  import PeriodStream from "../period";
41
41
  import ActivePeriodEmitter from "./active_period_emitter";
42
42
  import areStreamsComplete from "./are_streams_complete";
43
- import getBlacklistedRanges from "./get_blacklisted_ranges";
43
+ import getTimeRangesForContent from "./get_time_ranges_for_content";
44
44
  /**
45
45
  * Create and manage the various Stream Observables needed for the content to
46
46
  * play:
@@ -204,29 +204,43 @@ export default function StreamOrchestrator(content, playbackObserver, representa
204
204
  }
205
205
  return launchConsecutiveStreamsForPeriod(newInitialPeriod);
206
206
  }));
207
- // Free the buffer of undecipherable data
208
207
  var handleDecipherabilityUpdate$ = fromEvent(manifest, "decipherabilityUpdate")
209
- .pipe(mergeMap(function (updates) {
208
+ .pipe(mergeMap(onDecipherabilityUpdates));
209
+ return observableMerge(restartStreamsWhenOutOfBounds$, handleDecipherabilityUpdate$, launchConsecutiveStreamsForPeriod(basePeriod));
210
+ function onDecipherabilityUpdates(updates) {
210
211
  var segmentBufferStatus = segmentBuffersStore.getStatus(bufferType);
211
212
  var ofCurrentType = updates
212
213
  .filter(function (update) { return update.adaptation.type === bufferType; });
213
214
  if (ofCurrentType.length === 0 || segmentBufferStatus.type !== "initialized") {
214
215
  return EMPTY; // no need to stop the current Streams.
215
216
  }
216
- var undecipherableUpdates = ofCurrentType.filter(function (update) {
217
+ var segmentBuffer = segmentBufferStatus.value;
218
+ var resettedContent = ofCurrentType.filter(function (update) {
219
+ return update.representation.decipherable === undefined;
220
+ });
221
+ var undecipherableContent = ofCurrentType.filter(function (update) {
217
222
  return update.representation.decipherable === false;
218
223
  });
219
- var segmentBuffer = segmentBufferStatus.value;
220
- var rangesToClean = getBlacklistedRanges(segmentBuffer, undecipherableUpdates);
221
- if (rangesToClean.length === 0) {
222
- // Nothing to clean => no buffer to flush.
223
- return EMPTY;
224
- }
225
- // We have to remove the undecipherable media data and then ask the
226
- // current media element to be "flushed"
224
+ /**
225
+ * Time ranges now containing undecipherable content.
226
+ * Those should first be removed and, depending on the platform, may
227
+ * need Supplementary actions as playback issues may remain even after
228
+ * removal.
229
+ */
230
+ var undecipherableRanges = getTimeRangesForContent(segmentBuffer, undecipherableContent);
231
+ /**
232
+ * Time ranges now containing content whose decipherability status came
233
+ * back to being unknown.
234
+ * To simplify its handling, those are just removed from the buffer.
235
+ * Less considerations have to be taken than for the `undecipherableRanges`.
236
+ */
237
+ var rangesToRemove = getTimeRangesForContent(segmentBuffer, resettedContent);
238
+ // First close all Stream currently active so they don't continue to
239
+ // load and push segments.
227
240
  enableOutOfBoundsCheck = false;
228
241
  destroyStreams$.next();
229
- return observableConcat.apply(void 0, __spreadArray(__spreadArray([], rangesToClean.map(function (_a) {
242
+ /** Remove from the `SegmentBuffer` all the concerned time ranges. */
243
+ var cleanOperations = __spreadArray(__spreadArray([], undecipherableRanges, true), rangesToRemove, true).map(function (_a) {
230
244
  var start = _a.start, end = _a.end;
231
245
  if (start >= end) {
232
246
  return EMPTY;
@@ -235,15 +249,15 @@ export default function StreamOrchestrator(content, playbackObserver, representa
235
249
  return fromCancellablePromise(canceller, function () {
236
250
  return segmentBuffer.removeBuffer(start, end, canceller.signal);
237
251
  }).pipe(ignoreElements());
238
- }), false), [
252
+ });
253
+ return observableConcat.apply(void 0, __spreadArray(__spreadArray([], cleanOperations, false), [
239
254
  // Schedule micro task before checking the last playback observation
240
255
  // to reduce the risk of race conditions where the next observation
241
256
  // was going to be emitted synchronously.
242
257
  nextTickObs().pipe(ignoreElements()),
243
258
  playbackObserver.getReference().asObservable().pipe(take(1), mergeMap(function (observation) {
244
259
  var _a;
245
- var shouldAutoPlay = !((_a = observation.paused.pending) !== null && _a !== void 0 ? _a : playbackObserver.getIsPaused());
246
- return observableConcat(observableOf(EVENTS.needsDecipherabilityFlush(observation.position.last, shouldAutoPlay, observation.duration)), observableDefer(function () {
260
+ var restartStream$ = observableDefer(function () {
247
261
  var _a;
248
262
  var lastPosition = (_a = observation.position.pending) !== null && _a !== void 0 ? _a : observation.position.last;
249
263
  var newInitialPeriod = manifest.getPeriodForTime(lastPosition);
@@ -251,10 +265,17 @@ export default function StreamOrchestrator(content, playbackObserver, representa
251
265
  throw new MediaError("MEDIA_TIME_NOT_FOUND", "The wanted position is not found in the Manifest.");
252
266
  }
253
267
  return launchConsecutiveStreamsForPeriod(newInitialPeriod);
254
- }));
268
+ });
269
+ if (needsFlushingAfterClean(observation, undecipherableRanges)) {
270
+ var shouldAutoPlay = !((_a = observation.paused.pending) !== null && _a !== void 0 ? _a : playbackObserver.getIsPaused());
271
+ return observableConcat(observableOf(EVENTS.needsDecipherabilityFlush(observation.position.last, shouldAutoPlay, observation.duration)), restartStream$);
272
+ }
273
+ else if (needsFlushingAfterClean(observation, rangesToRemove)) {
274
+ return observableConcat(observableOf(EVENTS.needsBufferFlush()), restartStream$);
275
+ }
276
+ return restartStream$;
255
277
  }))], false));
256
- }));
257
- return observableMerge(restartStreamsWhenOutOfBounds$, handleDecipherabilityUpdate$, launchConsecutiveStreamsForPeriod(basePeriod));
278
+ }
258
279
  }
259
280
  /**
260
281
  * Create lazily consecutive PeriodStreams:
@@ -342,3 +363,24 @@ export default function StreamOrchestrator(content, playbackObserver, representa
342
363
  return observableMerge(currentStream$, nextPeriodStream$, destroyAll$.pipe(ignoreElements()));
343
364
  }
344
365
  }
366
+ /**
367
+ * Returns `true` if low-level buffers have to be "flushed" after the given
368
+ * `cleanedRanges` time ranges have been removed from an audio or video
369
+ * SourceBuffer, to prevent playback issues.
370
+ * @param {Object} observation
371
+ * @param {Array.<Object>} cleanedRanges
372
+ * @returns {boolean}
373
+ */
374
+ function needsFlushingAfterClean(observation, cleanedRanges) {
375
+ if (cleanedRanges.length === 0) {
376
+ return false;
377
+ }
378
+ var curPos = observation.position.last;
379
+ // Based on the playback direction, we just check whether we may encounter
380
+ // the corresponding ranges, without seeking or re-switching playback
381
+ // direction which is expected to lead to a low-level flush anyway.
382
+ // There's a 5 seconds security, just to be sure.
383
+ return observation.speed >= 0 ?
384
+ cleanedRanges[cleanedRanges.length - 1].end >= curPos - 5 :
385
+ cleanedRanges[0].start <= curPos + 5;
386
+ }
@@ -52,8 +52,12 @@ export default class DownloadingQueue<T> {
52
52
  private _mediaSegmentRequest;
53
53
  /** Interface used to load segments. */
54
54
  private _segmentFetcher;
55
- /** Emit the timescale anounced in the initialization segment once parsed. */
56
- private _initSegmentMetadata$;
55
+ /**
56
+ * Emit the timescale anounced in the initialization segment once parsed.
57
+ * `undefined` when this is not yet known.
58
+ * `null` when no initialization segment or timescale exists.
59
+ */
60
+ private _initSegmentInfoRef;
57
61
  /**
58
62
  * Some media segments might have been loaded and are only awaiting for the
59
63
  * initialization segment to be parsed before being parsed themselves.
@@ -86,13 +90,13 @@ export default class DownloadingQueue<T> {
86
90
  /**
87
91
  * Returns the initialization segment currently being requested.
88
92
  * Returns `null` if no initialization segment request is pending.
89
- * @returns {Object}
93
+ * @returns {Object | null}
90
94
  */
91
95
  getRequestedInitSegment(): ISegment | null;
92
96
  /**
93
97
  * Returns the media segment currently being requested.
94
98
  * Returns `null` if no media segment request is pending.
95
- * @returns {Object}
99
+ * @returns {Object | null}
96
100
  */
97
101
  getRequestedMediaSegment(): ISegment | null;
98
102
  /**
@@ -13,11 +13,12 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { concat as observableConcat, defer as observableDefer, EMPTY, filter, finalize, map, merge as observableMerge, mergeMap, of as observableOf, ReplaySubject, share, switchMap, take, } from "rxjs";
16
+ import { defer as observableDefer, EMPTY, filter, finalize, merge as observableMerge, Observable, of as observableOf, share, switchMap, } from "rxjs";
17
17
  import log from "../../../log";
18
18
  import assert from "../../../utils/assert";
19
- import assertUnreachable from "../../../utils/assert_unreachable";
20
19
  import objectAssign from "../../../utils/object_assign";
20
+ import createSharedReference from "../../../utils/reference";
21
+ import TaskCanceller from "../../../utils/task_canceller";
21
22
  /**
22
23
  * Class scheduling segment downloads for a single Representation.
23
24
  * @class DownloadingQueue
@@ -51,16 +52,16 @@ var DownloadingQueue = /** @class */ (function () {
51
52
  this._initSegmentRequest = null;
52
53
  this._mediaSegmentRequest = null;
53
54
  this._segmentFetcher = segmentFetcher;
54
- this._initSegmentMetadata$ = new ReplaySubject(1);
55
+ this._initSegmentInfoRef = createSharedReference(undefined);
55
56
  this._mediaSegmentsAwaitingInitMetadata = new Set();
56
57
  if (!hasInitSegment) {
57
- this._initSegmentMetadata$.next(undefined);
58
+ this._initSegmentInfoRef.setValue(null);
58
59
  }
59
60
  }
60
61
  /**
61
62
  * Returns the initialization segment currently being requested.
62
63
  * Returns `null` if no initialization segment request is pending.
63
- * @returns {Object}
64
+ * @returns {Object | null}
64
65
  */
65
66
  DownloadingQueue.prototype.getRequestedInitSegment = function () {
66
67
  return this._initSegmentRequest === null ? null :
@@ -69,7 +70,7 @@ var DownloadingQueue = /** @class */ (function () {
69
70
  /**
70
71
  * Returns the media segment currently being requested.
71
72
  * Returns `null` if no media segment request is pending.
72
- * @returns {Object}
73
+ * @returns {Object | null}
73
74
  */
74
75
  DownloadingQueue.prototype.getRequestedMediaSegment = function () {
75
76
  return this._mediaSegmentRequest === null ? null :
@@ -112,7 +113,7 @@ var DownloadingQueue = /** @class */ (function () {
112
113
  return true;
113
114
  }
114
115
  if (currentSegmentRequest.priority !== nextItem.priority) {
115
- _this._segmentFetcher.updatePriority(currentSegmentRequest.request$, nextItem.priority);
116
+ _this._segmentFetcher.updatePriority(currentSegmentRequest.request, nextItem.priority);
116
117
  }
117
118
  return false;
118
119
  }), switchMap(function (_a) {
@@ -124,7 +125,7 @@ var DownloadingQueue = /** @class */ (function () {
124
125
  var initSegmentRequest = _this._initSegmentRequest;
125
126
  if (next.initSegment !== null && initSegmentRequest !== null) {
126
127
  if (next.initSegment.priority !== initSegmentRequest.priority) {
127
- _this._segmentFetcher.updatePriority(initSegmentRequest.request$, next.initSegment.priority);
128
+ _this._segmentFetcher.updatePriority(initSegmentRequest.request, next.initSegment.priority);
128
129
  }
129
130
  return false;
130
131
  }
@@ -150,57 +151,153 @@ var DownloadingQueue = /** @class */ (function () {
150
151
  var _this = this;
151
152
  var segmentQueue = this._downloadQueue.getValue().segmentQueue;
152
153
  var currentNeededSegment = segmentQueue[0];
153
- var recursivelyRequestSegments = function (startingSegment) {
154
+ /* eslint-disable-next-line @typescript-eslint/no-this-alias */
155
+ var self = this;
156
+ return observableDefer(function () {
157
+ return recursivelyRequestSegments(currentNeededSegment);
158
+ }).pipe(finalize(function () { _this._mediaSegmentRequest = null; }));
159
+ function recursivelyRequestSegments(startingSegment) {
154
160
  if (startingSegment === undefined) {
155
161
  return observableOf({ type: "end-of-queue",
156
162
  value: null });
157
163
  }
158
164
  var segment = startingSegment.segment, priority = startingSegment.priority;
159
- var context = objectAssign({ segment: segment }, _this._content);
160
- var request$ = _this._segmentFetcher.createRequest(context, priority);
161
- _this._mediaSegmentRequest = { segment: segment, priority: priority, request$: request$ };
162
- return request$
163
- .pipe(mergeMap(function (evt) {
164
- switch (evt.type) {
165
- case "retry":
166
- return observableOf({ type: "retry",
167
- value: { segment: segment, error: evt.value } });
168
- case "interrupted":
165
+ var context = objectAssign({ segment: segment }, self._content);
166
+ return new Observable(function (obs) {
167
+ /** TaskCanceller linked to this Observable's lifecycle. */
168
+ var canceller = new TaskCanceller();
169
+ /**
170
+ * If `true` , the Observable has either errored, completed, or was
171
+ * unsubscribed from.
172
+ * This only conserves the Observable for the current segment's request,
173
+ * not the other recursively-created future ones.
174
+ */
175
+ var isComplete = false;
176
+ /**
177
+ * Subscription to request the following segment (as this function is
178
+ * recursive).
179
+ * `undefined` if no following segment has been requested.
180
+ */
181
+ var nextSegmentSubscription;
182
+ /**
183
+ * If true, we're currently waiting for the initialization segment to be
184
+ * parsed before parsing a received chunk.
185
+ *
186
+ * In that case, the `DownloadingQueue` has to remain careful to only
187
+ * send further events and complete the Observable only once the
188
+ * initialization segment has been parsed AND the chunk parsing has been
189
+ * done (this can be done very simply by listening to the same
190
+ * `ISharedReference`, as its callbacks are called in the same order
191
+ * than the one in which they are added.
192
+ */
193
+ var isWaitingOnInitSegment = false;
194
+ /** Scheduled actual segment request. */
195
+ var request = self._segmentFetcher.createRequest(context, priority, {
196
+ /**
197
+ * Callback called when the request has to be retried.
198
+ * @param {Error} error
199
+ */
200
+ onRetry: function (error) {
201
+ obs.next({ type: "retry", value: { segment: segment, error: error } });
202
+ },
203
+ /**
204
+ * Callback called when the request has to be interrupted and
205
+ * restarted later.
206
+ */
207
+ beforeInterrupted: function () {
169
208
  log.info("Stream: segment request interrupted temporarly.", segment.id, segment.time);
170
- return EMPTY;
171
- case "ended":
172
- _this._mediaSegmentRequest = null;
173
- var lastQueue = _this._downloadQueue.getValue().segmentQueue;
174
- if (lastQueue.length === 0) {
175
- return observableOf({ type: "end-of-queue",
176
- value: null });
209
+ },
210
+ /**
211
+ * Callback called when a decodable chunk of the segment is available.
212
+ * @param {Function} parse - Function allowing to parse the segment.
213
+ */
214
+ onChunk: function (parse) {
215
+ var initTimescale = self._initSegmentInfoRef.getValue();
216
+ if (initTimescale !== undefined) {
217
+ emitChunk(parse(initTimescale !== null && initTimescale !== void 0 ? initTimescale : undefined));
218
+ }
219
+ else {
220
+ isWaitingOnInitSegment = true;
221
+ // We could also technically call `waitUntilDefined` in both cases,
222
+ // but I found it globally clearer to segregate the two cases,
223
+ // especially to always have a meaningful `isWaitingOnInitSegment`
224
+ // boolean which is a very important variable.
225
+ self._initSegmentInfoRef.waitUntilDefined(function (actualTimescale) {
226
+ emitChunk(parse(actualTimescale !== null && actualTimescale !== void 0 ? actualTimescale : undefined));
227
+ }, { clearSignal: canceller.signal });
228
+ }
229
+ },
230
+ /** Callback called after all chunks have been sent. */
231
+ onAllChunksReceived: function () {
232
+ if (!isWaitingOnInitSegment) {
233
+ obs.next({ type: "end-of-segment",
234
+ value: { segment: segment } });
235
+ }
236
+ else {
237
+ self._mediaSegmentsAwaitingInitMetadata.add(segment.id);
238
+ self._initSegmentInfoRef.waitUntilDefined(function () {
239
+ obs.next({ type: "end-of-segment",
240
+ value: { segment: segment } });
241
+ self._mediaSegmentsAwaitingInitMetadata.delete(segment.id);
242
+ isWaitingOnInitSegment = false;
243
+ }, { clearSignal: canceller.signal });
177
244
  }
178
- else if (lastQueue[0].segment.id === segment.id) {
179
- lastQueue.shift();
245
+ },
246
+ /**
247
+ * Callback called right after the request ended but before the next
248
+ * requests are scheduled. It is used to schedule the next segment.
249
+ */
250
+ beforeEnded: function () {
251
+ self._mediaSegmentRequest = null;
252
+ if (isWaitingOnInitSegment) {
253
+ self._initSegmentInfoRef.waitUntilDefined(continueToNextSegment, { clearSignal: canceller.signal });
180
254
  }
181
- return recursivelyRequestSegments(lastQueue[0]);
182
- case "chunk":
183
- case "chunk-complete":
184
- _this._mediaSegmentsAwaitingInitMetadata.add(segment.id);
185
- return _this._initSegmentMetadata$.pipe(take(1), map(function (initTimescale) {
186
- if (evt.type === "chunk-complete") {
187
- return { type: "end-of-segment",
188
- value: { segment: segment } };
189
- }
190
- var parsed = evt.parse(initTimescale);
191
- assert(parsed.segmentType === "media", "Should have loaded a media segment.");
192
- return objectAssign({}, parsed, { type: "parsed-media", segment: segment });
193
- }), finalize(function () {
194
- _this._mediaSegmentsAwaitingInitMetadata.delete(segment.id);
195
- }));
196
- default:
197
- assertUnreachable(evt);
255
+ else {
256
+ continueToNextSegment();
257
+ }
258
+ },
259
+ }, canceller.signal);
260
+ request.catch(function (error) {
261
+ if (!isComplete) {
262
+ isComplete = true;
263
+ obs.error(error);
264
+ }
265
+ });
266
+ self._mediaSegmentRequest = { segment: segment, priority: priority, request: request };
267
+ return function () {
268
+ self._mediaSegmentsAwaitingInitMetadata.delete(segment.id);
269
+ if (nextSegmentSubscription !== undefined) {
270
+ nextSegmentSubscription.unsubscribe();
271
+ }
272
+ if (isComplete) {
273
+ return;
274
+ }
275
+ isComplete = true;
276
+ isWaitingOnInitSegment = false;
277
+ canceller.cancel();
278
+ };
279
+ function emitChunk(parsed) {
280
+ assert(parsed.segmentType === "media", "Should have loaded a media segment.");
281
+ obs.next(objectAssign({}, parsed, { type: "parsed-media", segment: segment }));
198
282
  }
199
- }));
200
- };
201
- return observableDefer(function () {
202
- return recursivelyRequestSegments(currentNeededSegment);
203
- }).pipe(finalize(function () { _this._mediaSegmentRequest = null; }));
283
+ function continueToNextSegment() {
284
+ var lastQueue = self._downloadQueue.getValue().segmentQueue;
285
+ if (lastQueue.length === 0) {
286
+ obs.next({ type: "end-of-queue",
287
+ value: null });
288
+ isComplete = true;
289
+ obs.complete();
290
+ return;
291
+ }
292
+ else if (lastQueue[0].segment.id === segment.id) {
293
+ lastQueue.shift();
294
+ }
295
+ isComplete = true;
296
+ nextSegmentSubscription = recursivelyRequestSegments(lastQueue[0])
297
+ .subscribe(obs);
298
+ }
299
+ });
300
+ }
204
301
  };
205
302
  /**
206
303
  * Internal logic performing initialization segment requests.
@@ -213,41 +310,61 @@ var DownloadingQueue = /** @class */ (function () {
213
310
  this._initSegmentRequest = null;
214
311
  return EMPTY;
215
312
  }
216
- var segment = queuedInitSegment.segment, priority = queuedInitSegment.priority;
217
- var context = objectAssign({ segment: segment }, this._content);
218
- var request$ = this._segmentFetcher.createRequest(context, priority);
219
- this._initSegmentRequest = { segment: segment, priority: priority, request$: request$ };
220
- return request$
221
- .pipe(mergeMap(function (evt) {
222
- switch (evt.type) {
223
- case "retry":
224
- return observableOf({ type: "retry",
225
- value: { segment: segment, error: evt.value } });
226
- case "interrupted":
313
+ /* eslint-disable-next-line @typescript-eslint/no-this-alias */
314
+ var self = this;
315
+ return new Observable(function (obs) {
316
+ /** TaskCanceller linked to this Observable's lifecycle. */
317
+ var canceller = new TaskCanceller();
318
+ var segment = queuedInitSegment.segment, priority = queuedInitSegment.priority;
319
+ var context = objectAssign({ segment: segment }, _this._content);
320
+ /**
321
+ * If `true` , the Observable has either errored, completed, or was
322
+ * unsubscribed from.
323
+ */
324
+ var isComplete = false;
325
+ var request = _this._segmentFetcher.createRequest(context, priority, {
326
+ onRetry: function (err) {
327
+ obs.next({ type: "retry",
328
+ value: { segment: segment, error: err } });
329
+ },
330
+ beforeInterrupted: function () {
227
331
  log.info("Stream: init segment request interrupted temporarly.", segment.id);
228
- return EMPTY;
229
- case "chunk":
230
- var parsed_1 = evt.parse(undefined);
231
- assert(parsed_1.segmentType === "init", "Should have loaded an init segment.");
232
- return observableConcat(observableOf(objectAssign({}, parsed_1, { type: "parsed-init", segment: segment })),
233
- // We want to emit parsing information strictly AFTER the
234
- // initialization segment is emitted. Hence why we perform this
235
- // side-effect a posteriori in a concat operator
236
- observableDefer(function () {
237
- if (parsed_1.segmentType === "init") {
238
- _this._initSegmentMetadata$.next(parsed_1.initTimescale);
239
- }
240
- return EMPTY;
241
- }));
242
- case "chunk-complete":
243
- return observableOf({ type: "end-of-segment",
332
+ },
333
+ beforeEnded: function () {
334
+ self._initSegmentRequest = null;
335
+ isComplete = true;
336
+ obs.complete();
337
+ },
338
+ onChunk: function (parse) {
339
+ var _a;
340
+ var parsed = parse(undefined);
341
+ assert(parsed.segmentType === "init", "Should have loaded an init segment.");
342
+ obs.next(objectAssign({}, parsed, { type: "parsed-init", segment: segment }));
343
+ if (parsed.segmentType === "init") {
344
+ self._initSegmentInfoRef.setValue((_a = parsed.initTimescale) !== null && _a !== void 0 ? _a : null);
345
+ }
346
+ },
347
+ onAllChunksReceived: function () {
348
+ obs.next({ type: "end-of-segment",
244
349
  value: { segment: segment } });
245
- case "ended":
246
- return EMPTY; // Do nothing, just here to check every case
247
- default:
248
- assertUnreachable(evt);
249
- }
250
- })).pipe(finalize(function () { _this._initSegmentRequest = null; }));
350
+ },
351
+ }, canceller.signal);
352
+ request.catch(function (error) {
353
+ if (!isComplete) {
354
+ isComplete = true;
355
+ obs.error(error);
356
+ }
357
+ });
358
+ _this._initSegmentRequest = { segment: segment, priority: priority, request: request };
359
+ return function () {
360
+ _this._initSegmentRequest = null;
361
+ if (isComplete) {
362
+ return;
363
+ }
364
+ isComplete = true;
365
+ canceller.cancel();
366
+ };
367
+ });
251
368
  };
252
369
  return DownloadingQueue;
253
370
  }());
@@ -390,7 +390,9 @@ export interface ILockedStreamEvent {
390
390
  * decipherability status of some `Representation`(s).
391
391
  *
392
392
  * When that event is emitted, the current HTMLMediaElement's buffer might need
393
- * to be "flushed" to continue (e.g. through a little seek operation).
393
+ * to be "flushed" to continue (e.g. through a little seek operation) or in
394
+ * worst cases completely removed and re-created through the "reload" mechanism,
395
+ * depending on the platform.
394
396
  */
395
397
  export interface INeedsDecipherabilityFlush {
396
398
  type: "needs-decipherability-flush";
@@ -13,6 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ import { IEncryptedMediaErrorKeyStatusObject } from "../public_types";
16
17
  import { IEncryptedMediaErrorCode } from "./error_codes";
17
18
  /**
18
19
  * Error linked to the encryption of the media.
@@ -24,11 +25,15 @@ export default class EncryptedMediaError extends Error {
24
25
  readonly name: "EncryptedMediaError";
25
26
  readonly type: "ENCRYPTED_MEDIA_ERROR";
26
27
  readonly code: IEncryptedMediaErrorCode;
28
+ readonly keyStatuses?: IEncryptedMediaErrorKeyStatusObject[];
27
29
  message: string;
28
30
  fatal: boolean;
29
31
  /**
30
32
  * @param {string} code
31
33
  * @param {string} reason
32
34
  */
33
- constructor(code: IEncryptedMediaErrorCode, reason: string);
35
+ constructor(code: "KEY_STATUS_CHANGE_ERROR", reason: string, supplementaryInfos: {
36
+ keyStatuses: IEncryptedMediaErrorKeyStatusObject[];
37
+ });
38
+ constructor(code: Omit<IEncryptedMediaErrorCode, "KEY_STATUS_CHANGE_ERROR">, reason: string);
34
39
  }
@@ -38,11 +38,7 @@ import errorMessage from "./error_message";
38
38
  */
39
39
  var EncryptedMediaError = /** @class */ (function (_super) {
40
40
  __extends(EncryptedMediaError, _super);
41
- /**
42
- * @param {string} code
43
- * @param {string} reason
44
- */
45
- function EncryptedMediaError(code, reason) {
41
+ function EncryptedMediaError(code, reason, supplementaryInfos) {
46
42
  var _this = _super.call(this) || this;
47
43
  // @see https://stackoverflow.com/questions/41102060/typescript-extending-error-class
48
44
  Object.setPrototypeOf(_this, EncryptedMediaError.prototype);
@@ -51,6 +47,9 @@ var EncryptedMediaError = /** @class */ (function (_super) {
51
47
  _this.code = code;
52
48
  _this.message = errorMessage(_this.name, _this.code, reason);
53
49
  _this.fatal = false;
50
+ if (typeof (supplementaryInfos === null || supplementaryInfos === void 0 ? void 0 : supplementaryInfos.keyStatuses) === "string") {
51
+ _this.keyStatuses = supplementaryInfos.keyStatuses;
52
+ }
54
53
  return _this;
55
54
  }
56
55
  return EncryptedMediaError;
@@ -13,6 +13,6 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import VideoThumbnailLoader, { DASH_LOADER, MPL_LOADER } from "./thumbnail_loader";
16
+ import VideoThumbnailLoader, { DASH_LOADER, MPL_LOADER } from "./video_thumbnail_loader";
17
17
  export { DASH_LOADER, MPL_LOADER, };
18
18
  export default VideoThumbnailLoader;
@@ -13,6 +13,6 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import VideoThumbnailLoader, { DASH_LOADER, MPL_LOADER, } from "./thumbnail_loader";
16
+ import VideoThumbnailLoader, { DASH_LOADER, MPL_LOADER, } from "./video_thumbnail_loader";
17
17
  export { DASH_LOADER, MPL_LOADER, };
18
18
  export default VideoThumbnailLoader;
@@ -0,0 +1,26 @@
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
+ import { ISegmentFetcher, ISegmentLoaderContent } from "../../../core/fetchers/segment/segment_fetcher";
17
+ import { AudioVideoSegmentBuffer } from "../../../core/segment_buffers/implementations";
18
+ import { CancellationSignal } from "../../../utils/task_canceller";
19
+ /**
20
+ * @param {Object} segmentInfo
21
+ * @param {Object} segmentBuffer
22
+ * @param {Object} segmentFetcher
23
+ * @param {Object} cancelSignal
24
+ * @returns {Promise}
25
+ */
26
+ export default function loadAndPushSegment(segmentInfo: ISegmentLoaderContent, segmentBuffer: AudioVideoSegmentBuffer, segmentFetcher: ISegmentFetcher<ArrayBuffer | Uint8Array>, cancelSignal: CancellationSignal): Promise<unknown>;