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
@@ -14,13 +14,13 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- Observable,
19
- of as observableOf,
20
- } from "rxjs";
21
17
  import { MediaError } from "../../errors";
22
18
  import features from "../../features";
23
19
  import log from "../../log";
20
+ import {
21
+ CancellationError,
22
+ CancellationSignal,
23
+ } from "../../utils/task_canceller";
24
24
  import {
25
25
  AudioVideoSegmentBuffer,
26
26
  IBufferType,
@@ -60,7 +60,7 @@ type INativeMediaBufferType = "audio" | "video";
60
60
  * To be able to use a SegmentBuffer linked to a native media buffer, you
61
61
  * will first need to create it, but also wait until the other one is either
62
62
  * created or explicitely disabled through the `disableSegmentBuffer` method.
63
- * The Observable returned by `waitForUsableBuffers` will emit when
63
+ * The Promise returned by `waitForUsableBuffers` will emit when
64
64
  * that is the case.
65
65
  *
66
66
  * @class SegmentBuffersStore
@@ -96,7 +96,7 @@ export default class SegmentBuffersStore {
96
96
  /**
97
97
  * Callbacks called after a SourceBuffer is either created or disabled.
98
98
  * Used for example to trigger the `this.waitForUsableBuffers`
99
- * Observable.
99
+ * Promise.
100
100
  */
101
101
  private _onNativeBufferAddedOrDisabled : Array<() => void>;
102
102
 
@@ -179,7 +179,7 @@ export default class SegmentBuffersStore {
179
179
  * content need to all be created (by creating SegmentBuffers linked to them)
180
180
  * before any one can be used.
181
181
  *
182
- * This function will return an Observable emitting when any and all native
182
+ * This function will return a Promise resolving when any and all native
183
183
  * SourceBuffers can be used.
184
184
  *
185
185
  * From https://w3c.github.io/media-source/#methods
@@ -187,18 +187,27 @@ export default class SegmentBuffersStore {
187
187
  * exception if the media element has reached the HAVE_METADATA
188
188
  * readyState. This can occur if the user agent's media engine
189
189
  * does not support adding more tracks during playback.
190
- * @return {Observable}
190
+ * @param {Object} cancelWaitSignal
191
+ * @return {Promise}
191
192
  */
192
- public waitForUsableBuffers() : Observable<void> {
193
+ public waitForUsableBuffers(cancelWaitSignal : CancellationSignal) : Promise<void> {
193
194
  if (this._areNativeBuffersUsable()) {
194
- return observableOf(undefined);
195
+ return Promise.resolve();
195
196
  }
196
- return new Observable(obs => {
197
- this._onNativeBufferAddedOrDisabled.push(() => {
197
+ return new Promise((res, rej) => {
198
+ const onAddedOrDisabled = () => {
198
199
  if (this._areNativeBuffersUsable()) {
199
- obs.next(undefined);
200
- obs.complete();
200
+ res();
201
+ }
202
+ };
203
+ this._onNativeBufferAddedOrDisabled.push(onAddedOrDisabled);
204
+
205
+ cancelWaitSignal.register((error : CancellationError) => {
206
+ const indexOf = this._onNativeBufferAddedOrDisabled.indexOf(onAddedOrDisabled);
207
+ if (indexOf >= 0) {
208
+ this._onNativeBufferAddedOrDisabled.splice(indexOf, 1);
201
209
  }
210
+ rej(error);
202
211
  });
203
212
  });
204
213
  }
@@ -44,9 +44,14 @@ import Manifest, {
44
44
  import deferSubscriptions from "../../../utils/defer_subscriptions";
45
45
  import { fromEvent } from "../../../utils/event_emitter";
46
46
  import filterMap from "../../../utils/filter_map";
47
- import { IReadOnlySharedReference } from "../../../utils/reference";
47
+ import {
48
+ createMappedReference,
49
+ IReadOnlySharedReference,
50
+ } from "../../../utils/reference";
51
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
48
52
  import nextTickObs from "../../../utils/rx-next-tick";
49
53
  import SortedList from "../../../utils/sorted_list";
54
+ import TaskCanceller from "../../../utils/task_canceller";
50
55
  import WeakMapMemory from "../../../utils/weak_map_memory";
51
56
  import { IRepresentationEstimator } from "../../adaptive";
52
57
  import type { IReadOnlyPlaybackObserver } from "../../api";
@@ -140,14 +145,22 @@ export default function StreamOrchestrator(
140
145
  const defaultMaxAhead = MAXIMUM_MAX_BUFFER_AHEAD[bufferType] != null ?
141
146
  MAXIMUM_MAX_BUFFER_AHEAD[bufferType] as number :
142
147
  Infinity;
143
- return BufferGarbageCollector({
144
- segmentBuffer,
145
- currentTime$: playbackObserver.getReference().asObservable()
146
- .pipe(map(o => o.position.pending ?? o.position.last)),
147
- maxBufferBehind$: maxBufferBehind.asObservable().pipe(
148
- map(val => Math.min(val, defaultMaxBehind))),
149
- maxBufferAhead$: maxBufferAhead.asObservable().pipe(
150
- map(val => Math.min(val, defaultMaxAhead))),
148
+ return new Observable<never>(() => {
149
+ const canceller = new TaskCanceller();
150
+ BufferGarbageCollector(
151
+ { segmentBuffer,
152
+ playbackObserver,
153
+ maxBufferBehind: createMappedReference(maxBufferBehind,
154
+ (val) =>
155
+ Math.min(val, defaultMaxBehind),
156
+ canceller.signal),
157
+ maxBufferAhead: createMappedReference(maxBufferAhead,
158
+ (val) =>
159
+ Math.min(val, defaultMaxAhead),
160
+ canceller.signal) },
161
+ canceller.signal
162
+ );
163
+ return () => { canceller.cancel(); };
151
164
  });
152
165
  });
153
166
 
@@ -322,9 +335,15 @@ export default function StreamOrchestrator(
322
335
  destroyStreams$.next();
323
336
 
324
337
  return observableConcat(
325
- ...rangesToClean.map(({ start, end }) =>
326
- start >= end ? EMPTY :
327
- segmentBuffer.removeBuffer(start, end).pipe(ignoreElements())),
338
+ ...rangesToClean.map(({ start, end }) => {
339
+ if (start >= end) {
340
+ return EMPTY;
341
+ }
342
+ const canceller = new TaskCanceller();
343
+ return fromCancellablePromise(canceller, () => {
344
+ return segmentBuffer.removeBuffer(start, end, canceller.signal);
345
+ }).pipe(ignoreElements());
346
+ }),
328
347
 
329
348
  // Schedule micro task before checking the last playback observation
330
349
  // to reduce the risk of race conditions where the next observation
@@ -45,7 +45,10 @@ import { getLeftSizeOfRange } from "../../../utils/ranges";
45
45
  import createSharedReference, {
46
46
  IReadOnlySharedReference,
47
47
  } from "../../../utils/reference";
48
- import { CancellationSignal } from "../../../utils/task_canceller";
48
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
49
+ import TaskCanceller, {
50
+ CancellationSignal,
51
+ } from "../../../utils/task_canceller";
49
52
  import WeakMapMemory from "../../../utils/weak_map_memory";
50
53
  import { IRepresentationEstimator } from "../../adaptive";
51
54
  import { IReadOnlyPlaybackObserver } from "../../api";
@@ -176,15 +179,20 @@ export default function PeriodStream({
176
179
  if (SegmentBuffersStore.isNative(bufferType)) {
177
180
  return reloadAfterSwitch(period, bufferType, playbackObserver, 0);
178
181
  }
179
- if (period.end === undefined) {
180
- cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start,
181
- Infinity);
182
- } else if (period.end <= period.start) {
183
- cleanBuffer$ = observableOf(null);
184
- } else {
185
- cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start,
186
- period.end);
187
- }
182
+ const canceller = new TaskCanceller();
183
+ cleanBuffer$ = fromCancellablePromise(canceller, () => {
184
+ if (period.end === undefined) {
185
+ return segmentBufferStatus.value.removeBuffer(period.start,
186
+ Infinity,
187
+ canceller.signal);
188
+ } else if (period.end <= period.start) {
189
+ return Promise.resolve();
190
+ } else {
191
+ return segmentBufferStatus.value.removeBuffer(period.start,
192
+ period.end,
193
+ canceller.signal);
194
+ }
195
+ });
188
196
  } else {
189
197
  if (segmentBufferStatus.type === "uninitialized") {
190
198
  segmentBuffersStore.disableSegmentBuffer(bufferType);
@@ -237,8 +245,11 @@ export default function PeriodStream({
237
245
 
238
246
  const cleanBuffer$ =
239
247
  strategy.type === "clean-buffer" || strategy.type === "flush-buffer" ?
240
- observableConcat(...strategy.value.map(({ start, end }) =>
241
- segmentBuffer.removeBuffer(start, end))
248
+ observableConcat(...strategy.value.map(({ start, end }) => {
249
+ const canceller = new TaskCanceller();
250
+ return fromCancellablePromise(canceller, () =>
251
+ segmentBuffer.removeBuffer(start, end, canceller.signal));
252
+ })
242
253
  // NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
243
254
  // first type parameter as `any` instead of the perfectly fine `unknown`,
244
255
  // leading to linter issues, as it forbids the usage of `any`.
@@ -249,12 +260,14 @@ export default function PeriodStream({
249
260
  const bufferGarbageCollector$ = garbageCollectors.get(segmentBuffer);
250
261
  const adaptationStream$ = createAdaptationStream(adaptation, segmentBuffer);
251
262
 
252
- return segmentBuffersStore.waitForUsableBuffers().pipe(mergeMap(() => {
253
- return observableConcat(cleanBuffer$,
254
- needsBufferFlush$,
255
- observableMerge(adaptationStream$,
256
- bufferGarbageCollector$));
257
- }));
263
+ const cancelWait = new TaskCanceller();
264
+ return fromCancellablePromise(cancelWait, () =>
265
+ segmentBuffersStore.waitForUsableBuffers(cancelWait.signal)
266
+ ).pipe(mergeMap(() =>
267
+ observableConcat(cleanBuffer$,
268
+ needsBufferFlush$,
269
+ observableMerge(adaptationStream$,
270
+ bufferGarbageCollector$))));
258
271
  });
259
272
 
260
273
  return observableConcat(
@@ -18,15 +18,8 @@
18
18
  * This file allows any Stream to push data to a SegmentBuffer.
19
19
  */
20
20
 
21
- import {
22
- catchError,
23
- concat as observableConcat,
24
- mergeMap,
25
- ignoreElements,
26
- Observable,
27
- take,
28
- } from "rxjs";
29
21
  import { MediaError } from "../../../errors";
22
+ import { CancellationSignal } from "../../../utils/task_canceller";
30
23
  import { IReadOnlyPlaybackObserver } from "../../api";
31
24
  import {
32
25
  IPushChunkInfos,
@@ -39,45 +32,37 @@ import { IRepresentationStreamPlaybackObservation } from "./representation_strea
39
32
  * Append a segment to the given segmentBuffer.
40
33
  * If it leads to a QuotaExceededError, try to run our custom range
41
34
  * _garbage collector_ then retry.
42
- *
43
35
  * @param {Observable} playbackObserver
44
36
  * @param {Object} segmentBuffer
45
37
  * @param {Object} dataInfos
46
- * @returns {Observable}
38
+ * @param {Object} cancellationSignal
39
+ * @returns {Promise}
47
40
  */
48
- export default function appendSegmentToBuffer<T>(
41
+ export default async function appendSegmentToBuffer<T>(
49
42
  playbackObserver : IReadOnlyPlaybackObserver<IRepresentationStreamPlaybackObservation>,
50
43
  segmentBuffer : SegmentBuffer,
51
- dataInfos : IPushChunkInfos<T>
52
- ) : Observable<unknown> {
53
- const append$ = segmentBuffer.pushChunk(dataInfos);
54
-
55
- return append$.pipe(
56
- catchError((appendError : unknown) => {
57
- if (!(appendError instanceof Error) || appendError.name !== "QuotaExceededError") {
58
- const reason = appendError instanceof Error ?
59
- appendError.toString() :
60
- "An unknown error happened when pushing content";
61
- throw new MediaError("BUFFER_APPEND_ERROR", reason);
62
- }
63
-
64
- return playbackObserver.getReference().asObservable().pipe(
65
- take(1),
66
- mergeMap((observation) => {
67
- const currentPos = observation.position.pending ??
68
- observation.position.last;
69
- return observableConcat(
70
- forceGarbageCollection(currentPos, segmentBuffer).pipe(ignoreElements()),
71
- append$
72
- ).pipe(
73
- catchError((forcedGCError : unknown) => {
74
- const reason = forcedGCError instanceof Error ?
75
- forcedGCError.toString() :
76
- "Could not clean the buffer";
44
+ dataInfos : IPushChunkInfos<T>,
45
+ cancellationSignal : CancellationSignal
46
+ ) : Promise<void> {
47
+ try {
48
+ await segmentBuffer.pushChunk(dataInfos, cancellationSignal);
49
+ } catch (appendError : unknown) {
50
+ if (!(appendError instanceof Error) || appendError.name !== "QuotaExceededError") {
51
+ const reason = appendError instanceof Error ?
52
+ appendError.toString() :
53
+ "An unknown error happened when pushing content";
54
+ throw new MediaError("BUFFER_APPEND_ERROR", reason);
55
+ }
56
+ const { position } = playbackObserver.getReference().getValue();
57
+ const currentPos = position.pending ?? position.last;
58
+ try {
59
+ await forceGarbageCollection(currentPos, segmentBuffer, cancellationSignal);
60
+ await segmentBuffer.pushChunk(dataInfos, cancellationSignal);
61
+ } catch (err2) {
62
+ const reason = err2 instanceof Error ? err2.toString() :
63
+ "Could not clean the buffer";
77
64
 
78
- throw new MediaError("BUFFER_FULL_ERROR", reason);
79
- })
80
- );
81
- }));
82
- }));
65
+ throw new MediaError("BUFFER_FULL_ERROR", reason);
66
+ }
67
+ }
83
68
  }
@@ -14,16 +14,10 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import {
18
- concatAll,
19
- defer as observableDefer,
20
- from as observableFrom,
21
- Observable,
22
- of as observableOf,
23
- } from "rxjs";
24
17
  import config from "../../../config";
25
18
  import log from "../../../log";
26
19
  import { getInnerAndOuterTimeRanges } from "../../../utils/ranges";
20
+ import { CancellationSignal } from "../../../utils/task_canceller";
27
21
  import { SegmentBuffer } from "../../segment_buffers";
28
22
 
29
23
 
@@ -33,37 +27,39 @@ import { SegmentBuffer } from "../../segment_buffers";
33
27
  * Try to clean up buffered ranges from a low gcGap at first.
34
28
  * If it does not succeed to clean up space, use a higher gcCap.
35
29
  *
36
- * @param {Observable} timings$
30
+ * @param {number} currentPosition
37
31
  * @param {Object} bufferingQueue
38
- * @returns {Observable}
32
+ * @param {Object} cancellationSignal
33
+ * @returns {Promise}
39
34
  */
40
- export default function forceGarbageCollection(
35
+ export default async function forceGarbageCollection(
41
36
  currentPosition : number,
42
- bufferingQueue : SegmentBuffer
43
- ) : Observable<unknown> {
44
- return observableDefer(() => {
45
- const GC_GAP_CALM = config.getCurrent().BUFFER_GC_GAPS.CALM;
46
- const GC_GAP_BEEFY = config.getCurrent().BUFFER_GC_GAPS.BEEFY;
47
- log.warn("Stream: Running garbage collector");
48
- const buffered = bufferingQueue.getBufferedRanges();
49
- let cleanedupRanges = selectGCedRanges(currentPosition, buffered, GC_GAP_CALM);
37
+ bufferingQueue : SegmentBuffer,
38
+ cancellationSignal : CancellationSignal
39
+ ) : Promise<void> {
40
+ const GC_GAP_CALM = config.getCurrent().BUFFER_GC_GAPS.CALM;
41
+ const GC_GAP_BEEFY = config.getCurrent().BUFFER_GC_GAPS.BEEFY;
42
+ log.warn("Stream: Running garbage collector");
43
+ const buffered = bufferingQueue.getBufferedRanges();
44
+ let cleanedupRanges = selectGCedRanges(currentPosition, buffered, GC_GAP_CALM);
50
45
 
51
- // more aggressive GC if we could not find any range to clean
52
- if (cleanedupRanges.length === 0) {
53
- cleanedupRanges = selectGCedRanges(currentPosition, buffered, GC_GAP_BEEFY);
54
- }
46
+ // more aggressive GC if we could not find any range to clean
47
+ if (cleanedupRanges.length === 0) {
48
+ cleanedupRanges = selectGCedRanges(currentPosition, buffered, GC_GAP_BEEFY);
49
+ }
55
50
 
56
- if (log.hasLevel("DEBUG")) {
57
- log.debug("Stream: GC cleaning",
58
- cleanedupRanges.map(({ start, end }) => `start: ${start} - end ${end}`)
59
- .join(", "));
51
+ if (log.hasLevel("DEBUG")) {
52
+ log.debug("Stream: GC cleaning",
53
+ cleanedupRanges.map(({ start, end }) => `start: ${start} - end ${end}`)
54
+ .join(", "));
55
+ }
56
+ for (const range of cleanedupRanges) {
57
+ const { start, end } = range;
58
+ if (start < end) {
59
+ await bufferingQueue.removeBuffer(start, end, cancellationSignal);
60
60
  }
61
- return observableFrom(
62
- cleanedupRanges.map(({ start, end }) =>
63
- start >= end ? observableOf(null) :
64
- bufferingQueue.removeBuffer(start, end))
65
- ).pipe(concatAll());
66
- });
61
+ }
62
+ return;
67
63
  }
68
64
 
69
65
  /**
@@ -100,8 +100,12 @@ export default function getBufferStatus(
100
100
  segmentBuffer.synchronizeInventory();
101
101
 
102
102
  const { representation } = content;
103
+ const askedStart = playbackObserver.getIsPaused() ||
104
+ playbackObserver.getPlaybackRate() <= 0 ?
105
+ initialWantedTime - 0.1 :
106
+ initialWantedTime;
103
107
  const neededRange = getRangeOfNeededSegments(content,
104
- initialWantedTime,
108
+ askedStart,
105
109
  bufferGoal);
106
110
  const shouldRefreshManifest = representation.index.shouldRefresh(neededRange.start,
107
111
  neededRange.end);
@@ -139,7 +143,7 @@ export default function getBufferStatus(
139
143
 
140
144
  const prioritizedNeededSegments: IQueuedSegment[] = segmentsToLoad.map((segment) => (
141
145
  {
142
- priority: getSegmentPriority(segment.time, initialWantedTime),
146
+ priority: getSegmentPriority(segment.time, askedStart),
143
147
  segment,
144
148
  }
145
149
  ));
@@ -221,7 +225,7 @@ function getRangeOfNeededSegments(
221
225
  {
222
226
  wantedStartPosition = lastIndexPosition - 1;
223
227
  } else {
224
- wantedStartPosition = initialWantedTime;
228
+ wantedStartPosition = initialWantedTime - 0.1;
225
229
  }
226
230
 
227
231
  const wantedEndPosition = wantedStartPosition + bufferGoal;
@@ -26,6 +26,8 @@ import Manifest, {
26
26
  Period,
27
27
  Representation,
28
28
  } from "../../../manifest";
29
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
30
+ import TaskCanceller from "../../../utils/task_canceller";
29
31
  import { IReadOnlyPlaybackObserver } from "../../api";
30
32
  import {
31
33
  IPushedChunkData,
@@ -71,11 +73,15 @@ export default function pushInitSegment<T>(
71
73
  timestampOffset: 0,
72
74
  appendWindow: [ undefined, undefined ],
73
75
  codec };
74
- return appendSegmentToBuffer(playbackObserver,
75
- segmentBuffer,
76
- { data, inventoryInfos: null }).pipe(map(() => {
77
- const buffered = segmentBuffer.getBufferedRanges();
78
- return EVENTS.addedSegment(content, segment, buffered, segmentData);
79
- }));
76
+ const canceller = new TaskCanceller();
77
+ return fromCancellablePromise(canceller, () =>
78
+ appendSegmentToBuffer(playbackObserver,
79
+ segmentBuffer,
80
+ { data, inventoryInfos: null },
81
+ canceller.signal))
82
+ .pipe(map(() => {
83
+ const buffered = segmentBuffer.getBufferedRanges();
84
+ return EVENTS.addedSegment(content, segment, buffered, segmentData);
85
+ }));
80
86
  });
81
87
  }
@@ -29,6 +29,8 @@ import Manifest, {
29
29
  } from "../../../manifest";
30
30
  import { ISegmentParserParsedMediaChunk } from "../../../transports";
31
31
  import objectAssign from "../../../utils/object_assign";
32
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
33
+ import TaskCanceller from "../../../utils/task_canceller";
32
34
  import { IReadOnlyPlaybackObserver } from "../../api";
33
35
  import { SegmentBuffer } from "../../segment_buffers";
34
36
  import EVENTS from "../events_generators";
@@ -108,12 +110,16 @@ export default function pushMediaSegment<T>(
108
110
  start: estimatedStart,
109
111
  end: estimatedEnd },
110
112
  content);
113
+ const canceller = new TaskCanceller();
111
114
 
112
- return appendSegmentToBuffer(playbackObserver,
113
- segmentBuffer,
114
- { data, inventoryInfos }).pipe(map(() => {
115
- const buffered = segmentBuffer.getBufferedRanges();
116
- return EVENTS.addedSegment(content, segment, buffered, chunkData);
117
- }));
115
+ return fromCancellablePromise(canceller, () =>
116
+ appendSegmentToBuffer(playbackObserver,
117
+ segmentBuffer,
118
+ { data, inventoryInfos },
119
+ canceller.signal))
120
+ .pipe(map(() => {
121
+ const buffered = segmentBuffer.getBufferedRanges();
122
+ return EVENTS.addedSegment(content, segment, buffered, chunkData);
123
+ }));
118
124
  });
119
125
  }
@@ -52,6 +52,8 @@ import Manifest, {
52
52
  import assertUnreachable from "../../../utils/assert_unreachable";
53
53
  import objectAssign from "../../../utils/object_assign";
54
54
  import { createSharedReference } from "../../../utils/reference";
55
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
56
+ import TaskCanceller from "../../../utils/task_canceller";
55
57
  import { IReadOnlyPlaybackObserver } from "../../api";
56
58
  import { IPrioritizedSegmentFetcher } from "../../fetchers";
57
59
  import { SegmentBuffer } from "../../segment_buffers";
@@ -267,10 +269,11 @@ export default function RepresentationStream<TSegmentDataType>({
267
269
  0,
268
270
  initialWantedTime - UPTO_CURRENT_POSITION_CLEANUP);
269
271
  if (gcedPosition > 0) {
270
- bufferRemoval = segmentBuffer
271
- .removeBuffer(0, gcedPosition)
272
- // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
273
- .pipe(ignoreElements());
272
+ const removalCanceller = new TaskCanceller();
273
+ bufferRemoval = fromCancellablePromise(removalCanceller, () =>
274
+ segmentBuffer.removeBuffer(0, gcedPosition, removalCanceller.signal)
275
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
276
+ ).pipe(ignoreElements());
274
277
  }
275
278
  }
276
279
  return status.shouldRefreshManifest ?
@@ -318,7 +321,10 @@ export default function RepresentationStream<TSegmentDataType>({
318
321
 
319
322
  case "end-of-segment": {
320
323
  const { segment } = evt.value;
321
- return segmentBuffer.endOfSegment(objectAssign({ segment }, content))
324
+ const endOfSegmentCanceller = new TaskCanceller();
325
+ return fromCancellablePromise(endOfSegmentCanceller, () =>
326
+ segmentBuffer.endOfSegment(objectAssign({ segment }, content),
327
+ endOfSegmentCanceller.signal))
322
328
  // NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
323
329
  // first type parameter as `any` instead of the perfectly fine `unknown`,
324
330
  // leading to linter issues, as it forbids the usage of `any`.
@@ -342,7 +342,7 @@ const DEFAULT_CONFIG = {
342
342
  * small enough so this (arguably rare) situation won't lead to too much
343
343
  * waiting time.
344
344
  */
345
- FORCE_DISCONTINUITY_SEEK_DELAY: 2000,
345
+ FORCE_DISCONTINUITY_SEEK_DELAY: 3000,
346
346
 
347
347
  /**
348
348
  * Ratio used to know if an already loaded segment should be re-buffered.
@@ -754,22 +754,22 @@ const DEFAULT_CONFIG = {
754
754
  END: 0.1,
755
755
  },
756
756
 
757
- /**
758
- * Maximum interval at which text tracks are refreshed in an "html"
759
- * textTrackMode.
760
- *
761
- * The text tracks are also refreshed on various video events, this interval
762
- * will only trigger a refresh if none of those events was received during
763
- * that timespan.
764
- *
765
- * Note that if the TextTrack cue did not change between two intervals or
766
- * events, the DOM won't be refreshed.
767
- * The TextTrack cues structure is also optimized for fast retrieval.
768
- * We should thus not have much of a performance impact here if we set a low
769
- * interval.
770
- *
771
- * @type {Number}
772
- */
757
+ /**
758
+ * Maximum interval at which text tracks are refreshed in an "html"
759
+ * textTrackMode.
760
+ *
761
+ * The text tracks are also refreshed on various video events, this interval
762
+ * will only trigger a refresh if none of those events was received during
763
+ * that timespan.
764
+ *
765
+ * Note that if the TextTrack cue did not change between two intervals or
766
+ * events, the DOM won't be refreshed.
767
+ * The TextTrack cues structure is also optimized for fast retrieval.
768
+ * We should thus not have much of a performance impact here if we set a low
769
+ * interval.
770
+ *
771
+ * @type {Number}
772
+ */
773
773
  MAXIMUM_HTML_TEXT_TRACK_UPDATE_INTERVAL: 50,
774
774
 
775
775
  /**
@@ -31,9 +31,10 @@ export default class CustomLoaderError extends Error {
31
31
  public readonly xhr : XMLHttpRequest | undefined;
32
32
 
33
33
  /**
34
+ * @param {string} message
35
+ * @param {boolean} canRetry
36
+ * @param {boolean} isOfflineError
34
37
  * @param {XMLHttpRequest} xhr
35
- * @param {string} url
36
- * @param {string} type
37
38
  */
38
39
  constructor(
39
40
  message : string,
@@ -29,6 +29,8 @@ import {
29
29
  import { ISegmentFetcher } from "../../../core/fetchers/segment/segment_fetcher";
30
30
  import { AudioVideoSegmentBuffer } from "../../../core/segment_buffers/implementations";
31
31
  import { ISegment } from "../../../manifest";
32
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
33
+ import TaskCanceller from "../../../utils/task_canceller";
32
34
  import prepareSourceBuffer from "./prepare_source_buffer";
33
35
  import { IContentInfos } from "./types";
34
36
 
@@ -88,14 +90,17 @@ function loadAndPushInitData(contentInfos: IContentInfos,
88
90
  const initSegmentData = initializationData instanceof ArrayBuffer ?
89
91
  new Uint8Array(initializationData) :
90
92
  initializationData;
91
- return sourceBuffer
93
+
94
+ const pushCanceller = new TaskCanceller();
95
+ return fromCancellablePromise(pushCanceller, () => sourceBuffer
92
96
  .pushChunk({ data: { initSegment: initSegmentData,
93
97
  chunk: null,
94
98
  appendWindow: [undefined, undefined],
95
99
  timestampOffset: 0,
96
100
  codec: contentInfos
97
101
  .representation.getMimeTypeString() },
98
- inventoryInfos: null });
102
+ inventoryInfos: null },
103
+ pushCanceller.signal));
99
104
  })
100
105
  );
101
106
  }
@@ -9,6 +9,8 @@ import Manifest, {
9
9
  Representation,
10
10
  } from "../../../manifest";
11
11
  import { ISegmentParserParsedMediaChunk } from "../../../transports";
12
+ import fromCancellablePromise from "../../../utils/rx-from_cancellable_promise";
13
+ import TaskCanceller from "../../../utils/task_canceller";
12
14
 
13
15
  /**
14
16
  * Push data to the video source buffer.
@@ -33,12 +35,14 @@ export default function pushData(
33
35
  const { chunkData, appendWindow } = parsed;
34
36
  const segmentData = chunkData instanceof ArrayBuffer ?
35
37
  new Uint8Array(chunkData) : chunkData;
36
- return videoSourceBuffer
38
+ const pushCanceller = new TaskCanceller();
39
+ return fromCancellablePromise(pushCanceller, () => videoSourceBuffer
37
40
  .pushChunk({ data: { chunk: segmentData,
38
41
  timestampOffset: 0,
39
42
  appendWindow,
40
43
  initSegment: null,
41
44
  codec: inventoryInfos
42
45
  .representation.getMimeTypeString() },
43
- inventoryInfos });
46
+ inventoryInfos },
47
+ pushCanceller.signal));
44
48
  }