rx-player 3.28.1-dev.2022083000 → 3.29.0-dev.2022090500

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 (139) hide show
  1. package/CHANGELOG.md +11 -1
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/compat/event_listeners.d.ts +18 -2
  4. package/dist/_esm5.processed/compat/event_listeners.js +64 -2
  5. package/dist/_esm5.processed/compat/on_height_width_change.d.ts +5 -4
  6. package/dist/_esm5.processed/compat/on_height_width_change.js +43 -34
  7. package/dist/_esm5.processed/core/api/playback_observer.d.ts +12 -2
  8. package/dist/_esm5.processed/core/api/playback_observer.js +27 -12
  9. package/dist/_esm5.processed/core/api/public_api.js +14 -14
  10. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.d.ts +7 -0
  11. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +10 -2
  12. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +8 -1
  13. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +10 -4
  14. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.d.ts +7 -0
  15. package/dist/_esm5.processed/core/init/initialize_directfile.js +1 -1
  16. package/dist/_esm5.processed/core/init/load_on_media_source.js +1 -1
  17. package/dist/_esm5.processed/core/init/stall_avoider.d.ts +4 -2
  18. package/dist/_esm5.processed/core/init/stall_avoider.js +32 -26
  19. package/dist/_esm5.processed/core/segment_buffers/garbage_collector.d.ts +12 -6
  20. package/dist/_esm5.processed/core/segment_buffers/garbage_collector.js +142 -78
  21. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -16
  22. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +53 -41
  23. package/dist/_esm5.processed/core/segment_buffers/implementations/image/image_segment_buffer.d.ts +6 -5
  24. package/dist/_esm5.processed/core/segment_buffers/implementations/image/image_segment_buffer.js +37 -39
  25. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +23 -22
  26. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +84 -72
  27. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +6 -12
  28. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +33 -43
  29. package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +12 -9
  30. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.d.ts +7 -6
  31. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +17 -10
  32. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +20 -9
  33. package/dist/_esm5.processed/core/stream/period/period_stream.js +25 -14
  34. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.d.ts +4 -7
  35. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +80 -23
  36. package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.d.ts +5 -4
  37. package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.js +78 -26
  38. package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +7 -3
  39. package/dist/_esm5.processed/core/stream/representation/push_init_segment.js +7 -1
  40. package/dist/_esm5.processed/core/stream/representation/push_media_segment.js +7 -1
  41. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -8
  42. package/dist/_esm5.processed/default_config.js +1 -1
  43. package/dist/_esm5.processed/errors/custom_loader_error.d.ts +3 -2
  44. package/dist/_esm5.processed/errors/custom_loader_error.js +3 -2
  45. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.js +5 -2
  46. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/push_data.js +5 -2
  47. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.js +9 -2
  48. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/thumbnail_loader.js +3 -1
  49. package/dist/_esm5.processed/experimental/tools/createMetaplaylist/get_duration_from_manifest.js +4 -3
  50. package/dist/_esm5.processed/public_types.d.ts +22 -3
  51. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +2 -2
  52. package/dist/_esm5.processed/transports/dash/image_pipelines.d.ts +3 -2
  53. package/dist/_esm5.processed/transports/dash/image_pipelines.js +3 -1
  54. package/dist/_esm5.processed/transports/dash/init_segment_loader.d.ts +3 -2
  55. package/dist/_esm5.processed/transports/dash/init_segment_loader.js +12 -6
  56. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.d.ts +3 -2
  57. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +3 -2
  58. package/dist/_esm5.processed/transports/dash/manifest_parser.js +6 -2
  59. package/dist/_esm5.processed/transports/dash/segment_loader.d.ts +3 -2
  60. package/dist/_esm5.processed/transports/dash/segment_loader.js +12 -9
  61. package/dist/_esm5.processed/transports/dash/text_loader.js +6 -3
  62. package/dist/_esm5.processed/transports/local/pipelines.d.ts +2 -2
  63. package/dist/_esm5.processed/transports/local/pipelines.js +6 -6
  64. package/dist/_esm5.processed/transports/local/segment_loader.d.ts +3 -2
  65. package/dist/_esm5.processed/transports/local/segment_loader.js +4 -3
  66. package/dist/_esm5.processed/transports/metaplaylist/manifest_loader.d.ts +2 -2
  67. package/dist/_esm5.processed/transports/metaplaylist/manifest_loader.js +5 -2
  68. package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +15 -10
  69. package/dist/_esm5.processed/transports/smooth/pipelines.d.ts +1 -1
  70. package/dist/_esm5.processed/transports/smooth/pipelines.js +18 -14
  71. package/dist/_esm5.processed/transports/smooth/segment_loader.d.ts +2 -2
  72. package/dist/_esm5.processed/transports/smooth/segment_loader.js +8 -6
  73. package/dist/_esm5.processed/transports/types.d.ts +25 -2
  74. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.d.ts +2 -2
  75. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
  76. package/dist/_esm5.processed/transports/utils/generate_manifest_loader.d.ts +2 -2
  77. package/dist/_esm5.processed/transports/utils/generate_manifest_loader.js +9 -6
  78. package/dist/_esm5.processed/utils/request/fetch.js +7 -8
  79. package/dist/_esm5.processed/utils/request/xhr.d.ts +1 -1
  80. package/dist/_esm5.processed/utils/request/xhr.js +28 -14
  81. package/dist/_esm5.processed/utils/task_canceller.d.ts +1 -2
  82. package/dist/_esm5.processed/utils/task_canceller.js +1 -2
  83. package/dist/mpd-parser.wasm +0 -0
  84. package/dist/rx-player.js +1116 -695
  85. package/dist/rx-player.min.js +1 -1
  86. package/package.json +7 -7
  87. package/sonar-project.properties +1 -1
  88. package/src/compat/event_listeners.ts +86 -1
  89. package/src/compat/on_height_width_change.ts +48 -49
  90. package/src/core/api/playback_observer.ts +34 -14
  91. package/src/core/api/public_api.ts +23 -18
  92. package/src/core/fetchers/manifest/manifest_fetcher.ts +20 -2
  93. package/src/core/fetchers/segment/segment_fetcher.ts +23 -3
  94. package/src/core/fetchers/segment/segment_fetcher_creator.ts +7 -0
  95. package/src/core/init/initialize_directfile.ts +1 -1
  96. package/src/core/init/load_on_media_source.ts +1 -0
  97. package/src/core/init/stall_avoider.ts +40 -26
  98. package/src/core/segment_buffers/garbage_collector.ts +55 -47
  99. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +92 -70
  100. package/src/core/segment_buffers/implementations/image/image_segment_buffer.ts +37 -42
  101. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +103 -105
  102. package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +35 -46
  103. package/src/core/segment_buffers/implementations/types.ts +22 -9
  104. package/src/core/segment_buffers/segment_buffers_store.ts +23 -14
  105. package/src/core/stream/orchestrator/stream_orchestrator.ts +31 -12
  106. package/src/core/stream/period/period_stream.ts +31 -18
  107. package/src/core/stream/representation/append_segment_to_buffer.ts +27 -42
  108. package/src/core/stream/representation/force_garbage_collection.ts +28 -32
  109. package/src/core/stream/representation/get_buffer_status.ts +7 -3
  110. package/src/core/stream/representation/push_init_segment.ts +12 -6
  111. package/src/core/stream/representation/push_media_segment.ts +12 -6
  112. package/src/core/stream/representation/representation_stream.ts +11 -5
  113. package/src/default_config.ts +17 -17
  114. package/src/errors/custom_loader_error.ts +3 -2
  115. package/src/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.ts +7 -2
  116. package/src/experimental/tools/VideoThumbnailLoader/push_data.ts +6 -2
  117. package/src/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.ts +10 -2
  118. package/src/experimental/tools/VideoThumbnailLoader/thumbnail_loader.ts +3 -1
  119. package/src/experimental/tools/createMetaplaylist/get_duration_from_manifest.ts +4 -3
  120. package/src/public_types.ts +28 -4
  121. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +2 -2
  122. package/src/transports/dash/image_pipelines.ts +4 -0
  123. package/src/transports/dash/init_segment_loader.ts +8 -0
  124. package/src/transports/dash/low_latency_segment_loader.ts +4 -0
  125. package/src/transports/dash/manifest_parser.ts +4 -0
  126. package/src/transports/dash/segment_loader.ts +21 -5
  127. package/src/transports/dash/text_loader.ts +7 -2
  128. package/src/transports/local/pipelines.ts +7 -5
  129. package/src/transports/local/segment_loader.ts +4 -2
  130. package/src/transports/metaplaylist/manifest_loader.ts +9 -2
  131. package/src/transports/metaplaylist/pipelines.ts +16 -6
  132. package/src/transports/smooth/pipelines.ts +17 -9
  133. package/src/transports/smooth/segment_loader.ts +8 -0
  134. package/src/transports/types.ts +27 -0
  135. package/src/transports/utils/call_custom_manifest_loader.ts +8 -2
  136. package/src/transports/utils/generate_manifest_loader.ts +18 -5
  137. package/src/utils/request/fetch.ts +7 -8
  138. package/src/utils/request/xhr.ts +31 -15
  139. package/src/utils/task_canceller.ts +1 -2
@@ -31,6 +31,7 @@ import Manifest, {
31
31
  Period,
32
32
  } from "../../manifest";
33
33
  import { getNextRangeGap } from "../../utils/ranges";
34
+ import { IReadOnlySharedReference } from "../../utils/reference";
34
35
  import {
35
36
  IPlaybackObservation,
36
37
  PlaybackObserver,
@@ -121,14 +122,16 @@ interface IDiscontinuityStoredInfo {
121
122
  * encountered and exited.
122
123
  * @param {object} playbackObserver - emit the current playback conditions.
123
124
  * @param {Object} manifest - The Manifest of the currently-played content.
125
+ * @param {Object} speed - The last speed set by the user
126
+ * @param {Observable} lockedStream$
124
127
  * @param {Observable} discontinuityUpdate$ - Observable emitting encountered
125
128
  * discontinuities for loaded Period and buffer types.
126
- * @param {Function} setCurrentTime
127
129
  * @returns {Observable}
128
130
  */
129
131
  export default function StallAvoider(
130
132
  playbackObserver : PlaybackObserver,
131
133
  manifest: Manifest | null,
134
+ speed : IReadOnlySharedReference<number>,
132
135
  lockedStream$ : Observable<ILockedStreamEvent>,
133
136
  discontinuityUpdate$: Observable<IDiscontinuityEvent>
134
137
  ) : Observable<IStalledEvent | IUnstalledEvent | IWarningEvent> {
@@ -181,11 +184,10 @@ export default function StallAvoider(
181
184
  const unlock$ = lockedStream$.pipe(
182
185
  withLatestFrom(playbackObserver.getReference().asObservable()),
183
186
  tap(([lockedStreamEvt, observation]) => {
184
- // TODO(PaulB) also skip when the user's wanted speed is set to `0`, as we
185
- // might not want to seek in that case?
186
187
  if (
187
188
  !observation.rebuffering ||
188
- observation.paused || (
189
+ observation.paused ||
190
+ speed.getValue() <= 0 || (
189
191
  lockedStreamEvt.bufferType !== "audio" &&
190
192
  lockedStreamEvt.bufferType !== "video"
191
193
  )
@@ -226,6 +228,22 @@ export default function StallAvoider(
226
228
  UNFREEZING_SEEK_DELAY,
227
229
  UNFREEZING_DELTA_POSITION } = config.getCurrent();
228
230
 
231
+ if (
232
+ !observation.seeking &&
233
+ isSeekingApproximate &&
234
+ ignoredStallTimeStamp === null &&
235
+ lastSeekingPosition !== null &&
236
+ observation.position < lastSeekingPosition
237
+ ) {
238
+ log.debug("Init: the device appeared to have seeked back by itself.");
239
+ const now = performance.now();
240
+ ignoredStallTimeStamp = now;
241
+ }
242
+
243
+ lastSeekingPosition = observation.seeking ?
244
+ Math.max(observation.pendingInternalSeek ?? 0, observation.position) :
245
+ null;
246
+
229
247
  if (freezing !== null) {
230
248
  const now = performance.now();
231
249
 
@@ -254,8 +272,8 @@ export default function StallAvoider(
254
272
  // Return that we're stalled
255
273
  let reason : IStallingSituation;
256
274
  if (observation.seeking) {
257
- reason = observation.internalSeeking ? "internal-seek" :
258
- "seeking";
275
+ reason = observation.pendingInternalSeek !== null ? "internal-seek" :
276
+ "seeking";
259
277
  } else {
260
278
  reason = "not-ready";
261
279
  }
@@ -269,27 +287,20 @@ export default function StallAvoider(
269
287
  // We want to separate a stall situation when a seek is due to a seek done
270
288
  // internally by the player to when its due to a regular user seek.
271
289
  const stalledReason = rebuffering.reason === "seeking" &&
272
- observation.internalSeeking ? "internal-seek" as const :
273
- rebuffering.reason;
290
+ observation.pendingInternalSeek !== null ?
291
+ "internal-seek" as const :
292
+ rebuffering.reason;
274
293
 
275
- if (observation.seeking) {
276
- lastSeekingPosition = observation.position;
277
- } else if (lastSeekingPosition !== null) {
294
+ if (ignoredStallTimeStamp !== null) {
278
295
  const now = performance.now();
279
- if (ignoredStallTimeStamp === null) {
280
- ignoredStallTimeStamp = now;
281
- }
282
- if (isSeekingApproximate && observation.position < lastSeekingPosition) {
283
- log.debug("Init: the device appeared to have seeked back by itself.");
284
- if (now - ignoredStallTimeStamp < FORCE_DISCONTINUITY_SEEK_DELAY) {
285
- return { type: "stalled" as const,
286
- value: stalledReason };
287
- } else {
288
- log.warn("Init: ignored stall for too long, checking discontinuity",
289
- now - ignoredStallTimeStamp);
290
- }
296
+ if (now - ignoredStallTimeStamp < FORCE_DISCONTINUITY_SEEK_DELAY) {
297
+ log.debug("Init: letting the device get out of a stall by itself");
298
+ return { type: "stalled" as const,
299
+ value: stalledReason };
300
+ } else {
301
+ log.warn("Init: ignored stall for too long, checking discontinuity",
302
+ now - ignoredStallTimeStamp);
291
303
  }
292
- lastSeekingPosition = null;
293
304
  }
294
305
 
295
306
  ignoredStallTimeStamp = null;
@@ -302,7 +313,7 @@ export default function StallAvoider(
302
313
  /** Position at which data is awaited. */
303
314
  const { position: stalledPosition } = rebuffering;
304
315
 
305
- if (stalledPosition !== null) {
316
+ if (stalledPosition !== null && speed.getValue() > 0) {
306
317
  const skippableDiscontinuity = findSeekableDiscontinuity(discontinuitiesStore,
307
318
  manifest,
308
319
  stalledPosition);
@@ -331,7 +342,10 @@ export default function StallAvoider(
331
342
  // implementation that might drop an injected segment, or in
332
343
  // case of small discontinuity in the content.
333
344
  const nextBufferRangeGap = getNextRangeGap(buffered, freezePosition);
334
- if (nextBufferRangeGap < BUFFER_DISCONTINUITY_THRESHOLD) {
345
+ if (
346
+ speed.getValue() > 0 &&
347
+ nextBufferRangeGap < BUFFER_DISCONTINUITY_THRESHOLD
348
+ ) {
335
349
  const seekTo = (freezePosition + nextBufferRangeGap + EPSILON);
336
350
  if (playbackObserver.getCurrentTime() < seekTo) {
337
351
  log.warn("Init: discontinuity encountered inferior to the threshold",
@@ -14,52 +14,66 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- combineLatest as observableCombineLatest,
19
- concatAll,
20
- EMPTY,
21
- from as observableFrom,
22
- ignoreElements,
23
- mergeMap,
24
- Observable,
25
- of as observableOf,
26
- } from "rxjs";
27
17
  import log from "../../log";
28
18
  import { getInnerAndOuterTimeRanges } from "../../utils/ranges";
19
+ import { IReadOnlySharedReference } from "../../utils/reference";
20
+ import { CancellationSignal } from "../../utils/task_canceller";
21
+ import { IReadOnlyPlaybackObserver } from "../api";
22
+ import { IStreamOrchestratorPlaybackObservation } from "../stream";
29
23
  import { SegmentBuffer } from "./implementations";
30
24
 
31
25
  export interface IGarbageCollectorArgument {
32
26
  /** SegmentBuffer implementation */
33
27
  segmentBuffer : SegmentBuffer;
34
28
  /** Emit current position in seconds regularly */
35
- currentTime$ : Observable<number>;
29
+ playbackObserver : IReadOnlyPlaybackObserver<
30
+ Pick<IStreamOrchestratorPlaybackObservation, "position">
31
+ >;
36
32
  /** Maximum time to keep behind current time position, in seconds */
37
- maxBufferBehind$ : Observable<number>;
33
+ maxBufferBehind : IReadOnlySharedReference<number>;
38
34
  /** Minimum time to keep behind current time position, in seconds */
39
- maxBufferAhead$ : Observable<number>;
35
+ maxBufferAhead : IReadOnlySharedReference<number>;
40
36
  }
41
37
 
42
38
  /**
43
39
  * Perform cleaning of the buffer according to the values set by the user
44
- * each time `currentTime$` emits and each times the
40
+ * each time `playbackObserver` emits and each times the
45
41
  * maxBufferBehind/maxBufferAhead values change.
46
42
  *
43
+ * Abort this operation when the `cancellationSignal` emits.
44
+ *
47
45
  * @param {Object} opt
46
+ * @param {Object} cancellationSignal
48
47
  * @returns {Observable}
49
48
  */
50
- export default function BufferGarbageCollector({
51
- segmentBuffer,
52
- currentTime$,
53
- maxBufferBehind$,
54
- maxBufferAhead$,
55
- } : IGarbageCollectorArgument) : Observable<never> {
56
- return observableCombineLatest([currentTime$, maxBufferBehind$, maxBufferAhead$]).pipe(
57
- mergeMap(([currentTime, maxBufferBehind, maxBufferAhead]) => {
58
- return clearBuffer(segmentBuffer,
59
- currentTime,
60
- maxBufferBehind,
61
- maxBufferAhead);
62
- }));
49
+ export default function BufferGarbageCollector(
50
+ { segmentBuffer,
51
+ playbackObserver,
52
+ maxBufferBehind,
53
+ maxBufferAhead } : IGarbageCollectorArgument,
54
+ cancellationSignal : CancellationSignal
55
+ ) : void {
56
+ let lastPosition : number;
57
+ playbackObserver.listen((o) => {
58
+ lastPosition = o.position.pending ?? o.position.last;
59
+ clean();
60
+ }, { includeLastObservation: true, clearSignal: cancellationSignal });
61
+ function clean() {
62
+ clearBuffer(segmentBuffer,
63
+ lastPosition,
64
+ maxBufferBehind.getValue(),
65
+ maxBufferAhead.getValue(),
66
+ cancellationSignal)
67
+ .catch(e => {
68
+ const errMsg = e instanceof Error ?
69
+ e.message :
70
+ "Unknown error";
71
+ log.error("Could not run BufferGarbageCollector:", errMsg);
72
+ });
73
+ }
74
+ maxBufferBehind.onUpdate(clean, { clearSignal: cancellationSignal });
75
+ maxBufferAhead.onUpdate(clean, { clearSignal: cancellationSignal });
76
+ clean();
63
77
  }
64
78
 
65
79
  /**
@@ -76,16 +90,17 @@ export default function BufferGarbageCollector({
76
90
  * @param {Number} position - The current position
77
91
  * @param {Number} maxBufferBehind
78
92
  * @param {Number} maxBufferAhead
79
- * @returns {Observable}
93
+ * @returns {Promise}
80
94
  */
81
- function clearBuffer(
95
+ async function clearBuffer(
82
96
  segmentBuffer : SegmentBuffer,
83
97
  position : number,
84
98
  maxBufferBehind : number,
85
- maxBufferAhead : number
86
- ) : Observable<never> {
99
+ maxBufferAhead : number,
100
+ cancellationSignal : CancellationSignal
101
+ ) : Promise<void> {
87
102
  if (!isFinite(maxBufferBehind) && !isFinite(maxBufferAhead)) {
88
- return EMPTY;
103
+ return Promise.resolve();
89
104
  }
90
105
 
91
106
  const cleanedupRanges : Array<{ start : number;
@@ -150,21 +165,14 @@ function clearBuffer(
150
165
 
151
166
  collectBufferBehind();
152
167
  collectBufferAhead();
153
- const clean$ = observableFrom(
154
- cleanedupRanges.map((range) => {
168
+
169
+ for (const range of cleanedupRanges) {
170
+ if (range.start < range.end) {
155
171
  log.debug("GC: cleaning range from SegmentBuffer", range.start, range.end);
156
- if (range.start >= range.end) {
157
- return observableOf(null);
172
+ if (cancellationSignal.cancellationError !== null) {
173
+ throw cancellationSignal.cancellationError;
158
174
  }
159
- return segmentBuffer.removeBuffer(range.start, range.end);
160
- })
161
- ).pipe(concatAll(),
162
- // NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
163
- // first type parameter as `any` instead of the perfectly fine `unknown`,
164
- // leading to linter issues, as it forbids the usage of `any`.
165
- // This is why we're disabling the eslint rule.
166
- /* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */
167
- ignoreElements());
168
-
169
- return clean$;
175
+ await segmentBuffer.removeBuffer(range.start, range.end, cancellationSignal);
176
+ }
177
+ }
170
178
  }
@@ -14,15 +14,6 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- fromEvent,
19
- interval,
20
- Observable,
21
- Observer,
22
- Subject,
23
- takeUntil,
24
- tap,
25
- } from "rxjs";
26
17
  import {
27
18
  ICompatSourceBuffer,
28
19
  tryToChangeSourceBufferType,
@@ -34,7 +25,12 @@ import areArraysOfNumbersEqual from "../../../../utils/are_arrays_of_numbers_equ
34
25
  import assertUnreachable from "../../../../utils/assert_unreachable";
35
26
  import { toUint8Array } from "../../../../utils/byte_parsing";
36
27
  import hashBuffer from "../../../../utils/hash_buffer";
28
+ import noop from "../../../../utils/noop";
37
29
  import objectAssign from "../../../../utils/object_assign";
30
+ import TaskCanceller, {
31
+ CancellationError,
32
+ CancellationSignal,
33
+ } from "../../../../utils/task_canceller";
38
34
  import { IInsertedChunkInfos } from "../../inventory";
39
35
  import {
40
36
  IEndOfSegmentInfos,
@@ -57,7 +53,10 @@ import {
57
53
  * AudioVideoSegmentBuffer to emit an event when the corresponding queued
58
54
  * operation is completely processed.
59
55
  */
60
- type IAVSBQueueItem = ISBOperation<BufferSource> & { subject: Subject<void> };
56
+ type IAVSBQueueItem = ISBOperation<BufferSource> & {
57
+ resolve : () => void;
58
+ reject : (err : Error) => void;
59
+ };
61
60
 
62
61
  /**
63
62
  * Task currently processed by the AudioVideoSegmentBuffer.
@@ -70,8 +69,14 @@ type IAVSBQueueItem = ISBOperation<BufferSource> & { subject: Subject<void> };
70
69
  * segment before the wanted media segment.
71
70
  */
72
71
  type IAVSBPendingTask = IPushTask |
73
- IRemoveOperation & { subject: Subject<void> } |
74
- IEndOfSegmentOperation & { subject: Subject<void> };
72
+ IRemoveOperation & {
73
+ resolve : () => void;
74
+ reject : (err : Error) => void;
75
+ } |
76
+ IEndOfSegmentOperation & {
77
+ resolve : () => void;
78
+ reject : (err : Error) => void;
79
+ };
75
80
 
76
81
  /** Structure of a `IAVSBPendingTask` item corresponding to a "Push" operation. */
77
82
  type IPushTask = IPushOperation<BufferSource> & {
@@ -88,8 +93,10 @@ type IPushTask = IPushOperation<BufferSource> & {
88
93
  */
89
94
  inventoryData : IInsertedChunkInfos |
90
95
  null;
91
- /** Subject used to emit an event to the caller when the operation is finished. */
92
- subject : Subject<void>;
96
+ /** Callback to call when the push operation succeed. */
97
+ resolve : () => void;
98
+ /** Callback to call when the push operation fails. */
99
+ reject : (err : Error) => void;
93
100
  };
94
101
 
95
102
  /**
@@ -110,10 +117,9 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
110
117
  private readonly _sourceBuffer : ICompatSourceBuffer;
111
118
 
112
119
  /**
113
- * Subject triggered when this AudioVideoSegmentBuffer is disposed.
114
- * Helps to clean-up Observables created at its creation.
120
+ * Helps to clean-up resource taken at the AudioVideoSegmentBuffer creation.
115
121
  */
116
- private _destroy$ : Subject<void>;
122
+ private _canceller : TaskCanceller;
117
123
 
118
124
  /**
119
125
  * Queue of awaited buffer "operations".
@@ -152,7 +158,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
152
158
  * @constructor
153
159
  * @param {string} bufferType
154
160
  * @param {string} codec
155
- * @param {SourceBuffer} sourceBuffer
161
+ * @param {MediaSource} mediaSource
156
162
  */
157
163
  constructor(
158
164
  bufferType : "audio" | "video",
@@ -162,7 +168,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
162
168
  super();
163
169
  const sourceBuffer = mediaSource.addSourceBuffer(codec);
164
170
 
165
- this._destroy$ = new Subject<void>();
171
+ this._canceller = new TaskCanceller();
166
172
  this.bufferType = bufferType;
167
173
  this._mediaSource = mediaSource;
168
174
  this._sourceBuffer = sourceBuffer;
@@ -171,7 +177,8 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
171
177
  this._lastInitSegment = null;
172
178
  this.codec = codec;
173
179
 
174
- const { SOURCE_BUFFER_FLUSHING_INTERVAL } = config.getCurrent();
180
+ const onError = this._onPendingTaskError.bind(this);
181
+ const reCheck = this._flush.bind(this);
175
182
 
176
183
  // Some browsers (happened with firefox 66) sometimes "forget" to send us
177
184
  // `update` or `updateend` events.
@@ -179,19 +186,16 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
179
186
  // stay locked in a waiting state.
180
187
  // This interval is here to check at regular intervals if the underlying
181
188
  // SourceBuffer is currently updating.
182
- interval(SOURCE_BUFFER_FLUSHING_INTERVAL).pipe(
183
- tap(() => this._flush()),
184
- takeUntil(this._destroy$)
185
- ).subscribe();
186
-
187
- fromEvent(this._sourceBuffer, "error").pipe(
188
- tap((err) => this._onPendingTaskError(err)),
189
- takeUntil(this._destroy$)
190
- ).subscribe();
191
- fromEvent(this._sourceBuffer, "updateend").pipe(
192
- tap(() => this._flush()),
193
- takeUntil(this._destroy$)
194
- ).subscribe();
189
+ const { SOURCE_BUFFER_FLUSHING_INTERVAL } = config.getCurrent();
190
+ const intervalId = setInterval(reCheck, SOURCE_BUFFER_FLUSHING_INTERVAL);
191
+ this._sourceBuffer.addEventListener("error", onError);
192
+ this._sourceBuffer.addEventListener("updateend", reCheck);
193
+
194
+ this._canceller.signal.register(() => {
195
+ clearInterval(intervalId);
196
+ this._sourceBuffer.removeEventListener("error", onError);
197
+ this._sourceBuffer.removeEventListener("updateend", reCheck);
198
+ });
195
199
  }
196
200
 
197
201
  /**
@@ -218,47 +222,62 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
218
222
  * `data.chunk` argument to null.
219
223
  *
220
224
  * @param {Object} infos
221
- * @returns {Observable}
225
+ * @param {Object} cancellationSignal
226
+ * @returns {Promise}
222
227
  */
223
- public pushChunk(infos : IPushChunkInfos<unknown>) : Observable<void> {
228
+ public pushChunk(
229
+ infos : IPushChunkInfos<unknown>,
230
+ cancellationSignal : CancellationSignal
231
+ ) : Promise<void> {
224
232
  assertPushedDataIsBufferSource(infos);
225
233
  log.debug("AVSB: receiving order to push data to the SourceBuffer",
226
234
  this.bufferType,
227
235
  getLoggableSegmentId(infos.inventoryInfos));
228
236
  return this._addToQueue({ type: SegmentBufferOperation.Push,
229
- value: infos });
237
+ value: infos },
238
+ cancellationSignal);
230
239
  }
231
240
 
232
241
  /**
233
242
  * Remove buffered data (added to the same FIFO queue than `pushChunk`).
234
243
  * @param {number} start - start position, in seconds
235
244
  * @param {number} end - end position, in seconds
236
- * @returns {Observable}
245
+ * @param {Object} cancellationSignal
246
+ * @returns {Promise}
237
247
  */
238
- public removeBuffer(start : number, end : number) : Observable<void> {
248
+ public removeBuffer(
249
+ start : number,
250
+ end : number,
251
+ cancellationSignal : CancellationSignal
252
+ ) : Promise<void> {
239
253
  log.debug("AVSB: receiving order to remove data from the SourceBuffer",
240
254
  this.bufferType,
241
255
  start,
242
256
  end);
243
257
  return this._addToQueue({ type: SegmentBufferOperation.Remove,
244
- value: { start, end } });
258
+ value: { start, end } },
259
+ cancellationSignal);
245
260
  }
246
261
 
247
262
  /**
248
263
  * Indicate that every chunks from a Segment has been given to pushChunk so
249
264
  * far.
250
265
  * This will update our internal Segment inventory accordingly.
251
- * The returned Observable will emit and complete successively once the whole
252
- * segment has been pushed and this indication is acknowledged.
266
+ * The returned Promise will resolve once the whole segment has been pushed
267
+ * and this indication is acknowledged.
253
268
  * @param {Object} infos
254
- * @returns {Observable}
269
+ * @returns {Promise}
255
270
  */
256
- public endOfSegment(infos : IEndOfSegmentInfos) : Observable<void> {
271
+ public endOfSegment(
272
+ infos : IEndOfSegmentInfos,
273
+ cancellationSignal : CancellationSignal
274
+ ) : Promise<void> {
257
275
  log.debug("AVSB: receiving order for validating end of segment",
258
276
  this.bufferType,
259
277
  getLoggableSegmentId(infos));
260
278
  return this._addToQueue({ type: SegmentBufferOperation.EndOfSegment,
261
- value: infos });
279
+ value: infos },
280
+ cancellationSignal);
262
281
  }
263
282
 
264
283
  /**
@@ -302,18 +321,17 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
302
321
  * @private
303
322
  */
304
323
  public dispose() : void {
305
- this._destroy$.next();
306
- this._destroy$.complete();
324
+ this._canceller.cancel();
307
325
 
308
326
  if (this._pendingTask !== null) {
309
- this._pendingTask.subject.complete();
327
+ this._pendingTask.reject(new CancellationError());
310
328
  this._pendingTask = null;
311
329
  }
312
330
 
313
331
  while (this._queue.length > 0) {
314
332
  const nextElement = this._queue.shift();
315
333
  if (nextElement !== undefined) {
316
- nextElement.subject.complete();
334
+ nextElement.reject(new CancellationError());
317
335
  }
318
336
  }
319
337
 
@@ -329,7 +347,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
329
347
 
330
348
  /**
331
349
  * Called when an error arised that made the current task fail.
332
- * @param {Event} error
350
+ * @param {Event} err
333
351
  */
334
352
  private _onPendingTaskError(err : unknown) : void {
335
353
  this._lastInitSegment = null; // initialize init segment as a security
@@ -338,7 +356,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
338
356
  err :
339
357
  new Error("An unknown error occured when doing operations " +
340
358
  "on the SourceBuffer");
341
- this._pendingTask.subject.error(error);
359
+ this._pendingTask.reject(error);
342
360
  }
343
361
  }
344
362
 
@@ -350,24 +368,22 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
350
368
  * Cancel queued operation on unsubscription.
351
369
  * @private
352
370
  * @param {Object} operation
353
- * @returns {Observable}
371
+ * @param {Object} cancellationSignal
372
+ * @returns {Promise}
354
373
  */
355
- private _addToQueue(operation : ISBOperation<BufferSource>) : Observable<void> {
356
- return new Observable((obs : Observer<void>) => {
374
+ private _addToQueue(
375
+ operation : ISBOperation<BufferSource>,
376
+ cancellationSignal : CancellationSignal
377
+ ) : Promise<void> {
378
+ return new Promise((resolve, reject) => {
379
+ if (cancellationSignal.cancellationError !== null) {
380
+ return reject(cancellationSignal.cancellationError);
381
+ }
357
382
  const shouldRestartQueue = this._queue.length === 0 &&
358
383
  this._pendingTask === null;
359
- const subject = new Subject<void>();
360
- const queueItem = objectAssign({ subject }, operation);
384
+ const queueItem = objectAssign({ resolve, reject }, operation);
361
385
  this._queue.push(queueItem);
362
-
363
- const subscription = subject.subscribe(obs);
364
- if (shouldRestartQueue) {
365
- this._flush();
366
- }
367
-
368
- return () => {
369
- subscription.unsubscribe();
370
-
386
+ cancellationSignal.register((error : CancellationError) => {
371
387
  // Remove the corresponding element from the AudioVideoSegmentBuffer's
372
388
  // queue.
373
389
  // If the operation was a pending task, it should still continue to not
@@ -376,7 +392,14 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
376
392
  if (index >= 0) {
377
393
  this._queue.splice(index, 1);
378
394
  }
379
- };
395
+ queueItem.resolve = noop;
396
+ queueItem.reject = noop;
397
+ reject(error);
398
+ });
399
+
400
+ if (shouldRestartQueue) {
401
+ this._flush();
402
+ }
380
403
  });
381
404
  }
382
405
 
@@ -409,10 +432,9 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
409
432
  assertUnreachable(task);
410
433
  }
411
434
 
412
- const { subject } = task;
435
+ const { resolve } = task;
413
436
  this._pendingTask = null;
414
- subject.next();
415
- subject.complete();
437
+ resolve();
416
438
  this._flush(); // Go to next item in queue
417
439
  return;
418
440
  }
@@ -436,7 +458,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
436
458
  e :
437
459
  new Error("An unknown error occured when preparing a push operation");
438
460
  this._lastInitSegment = null; // initialize init segment as a security
439
- nextItem.subject.error(error);
461
+ nextItem.reject(error);
440
462
  return;
441
463
  }
442
464
 
@@ -492,7 +514,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
492
514
  *
493
515
  * This method allows to "prepare" that push operation so that all is left is
494
516
  * to push the returned segment data one after the other (from first to last).
495
- * @param {Object} item
517
+ * @param {Object} data
496
518
  * @returns {Object}
497
519
  */
498
520
  private _preparePushOperation(