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.
- package/CHANGELOG.md +11 -1
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/event_listeners.d.ts +18 -2
- package/dist/_esm5.processed/compat/event_listeners.js +64 -2
- package/dist/_esm5.processed/compat/on_height_width_change.d.ts +5 -4
- package/dist/_esm5.processed/compat/on_height_width_change.js +43 -34
- package/dist/_esm5.processed/core/api/playback_observer.d.ts +12 -2
- package/dist/_esm5.processed/core/api/playback_observer.js +27 -12
- package/dist/_esm5.processed/core/api/public_api.js +14 -14
- package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.d.ts +7 -0
- package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +10 -2
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +8 -1
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +10 -4
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.d.ts +7 -0
- package/dist/_esm5.processed/core/init/initialize_directfile.js +1 -1
- package/dist/_esm5.processed/core/init/load_on_media_source.js +1 -1
- package/dist/_esm5.processed/core/init/stall_avoider.d.ts +4 -2
- package/dist/_esm5.processed/core/init/stall_avoider.js +32 -26
- package/dist/_esm5.processed/core/segment_buffers/garbage_collector.d.ts +12 -6
- package/dist/_esm5.processed/core/segment_buffers/garbage_collector.js +142 -78
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -16
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +53 -41
- package/dist/_esm5.processed/core/segment_buffers/implementations/image/image_segment_buffer.d.ts +6 -5
- package/dist/_esm5.processed/core/segment_buffers/implementations/image/image_segment_buffer.js +37 -39
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +23 -22
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +84 -72
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +6 -12
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +33 -43
- package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +12 -9
- package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.d.ts +7 -6
- package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +17 -10
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +20 -9
- package/dist/_esm5.processed/core/stream/period/period_stream.js +25 -14
- package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.d.ts +4 -7
- package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +80 -23
- package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.d.ts +5 -4
- package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.js +78 -26
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +7 -3
- package/dist/_esm5.processed/core/stream/representation/push_init_segment.js +7 -1
- package/dist/_esm5.processed/core/stream/representation/push_media_segment.js +7 -1
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -8
- package/dist/_esm5.processed/default_config.js +1 -1
- package/dist/_esm5.processed/errors/custom_loader_error.d.ts +3 -2
- package/dist/_esm5.processed/errors/custom_loader_error.js +3 -2
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.js +5 -2
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/push_data.js +5 -2
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.js +9 -2
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/thumbnail_loader.js +3 -1
- package/dist/_esm5.processed/experimental/tools/createMetaplaylist/get_duration_from_manifest.js +4 -3
- package/dist/_esm5.processed/public_types.d.ts +22 -3
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +2 -2
- package/dist/_esm5.processed/transports/dash/image_pipelines.d.ts +3 -2
- package/dist/_esm5.processed/transports/dash/image_pipelines.js +3 -1
- package/dist/_esm5.processed/transports/dash/init_segment_loader.d.ts +3 -2
- package/dist/_esm5.processed/transports/dash/init_segment_loader.js +12 -6
- package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.d.ts +3 -2
- package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +3 -2
- package/dist/_esm5.processed/transports/dash/manifest_parser.js +6 -2
- package/dist/_esm5.processed/transports/dash/segment_loader.d.ts +3 -2
- package/dist/_esm5.processed/transports/dash/segment_loader.js +12 -9
- package/dist/_esm5.processed/transports/dash/text_loader.js +6 -3
- package/dist/_esm5.processed/transports/local/pipelines.d.ts +2 -2
- package/dist/_esm5.processed/transports/local/pipelines.js +6 -6
- package/dist/_esm5.processed/transports/local/segment_loader.d.ts +3 -2
- package/dist/_esm5.processed/transports/local/segment_loader.js +4 -3
- package/dist/_esm5.processed/transports/metaplaylist/manifest_loader.d.ts +2 -2
- package/dist/_esm5.processed/transports/metaplaylist/manifest_loader.js +5 -2
- package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +15 -10
- package/dist/_esm5.processed/transports/smooth/pipelines.d.ts +1 -1
- package/dist/_esm5.processed/transports/smooth/pipelines.js +18 -14
- package/dist/_esm5.processed/transports/smooth/segment_loader.d.ts +2 -2
- package/dist/_esm5.processed/transports/smooth/segment_loader.js +8 -6
- package/dist/_esm5.processed/transports/types.d.ts +25 -2
- package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.d.ts +2 -2
- package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
- package/dist/_esm5.processed/transports/utils/generate_manifest_loader.d.ts +2 -2
- package/dist/_esm5.processed/transports/utils/generate_manifest_loader.js +9 -6
- package/dist/_esm5.processed/utils/request/fetch.js +7 -8
- package/dist/_esm5.processed/utils/request/xhr.d.ts +1 -1
- package/dist/_esm5.processed/utils/request/xhr.js +28 -14
- package/dist/_esm5.processed/utils/task_canceller.d.ts +1 -2
- package/dist/_esm5.processed/utils/task_canceller.js +1 -2
- package/dist/mpd-parser.wasm +0 -0
- package/dist/rx-player.js +1116 -695
- package/dist/rx-player.min.js +1 -1
- package/package.json +7 -7
- package/sonar-project.properties +1 -1
- package/src/compat/event_listeners.ts +86 -1
- package/src/compat/on_height_width_change.ts +48 -49
- package/src/core/api/playback_observer.ts +34 -14
- package/src/core/api/public_api.ts +23 -18
- package/src/core/fetchers/manifest/manifest_fetcher.ts +20 -2
- package/src/core/fetchers/segment/segment_fetcher.ts +23 -3
- package/src/core/fetchers/segment/segment_fetcher_creator.ts +7 -0
- package/src/core/init/initialize_directfile.ts +1 -1
- package/src/core/init/load_on_media_source.ts +1 -0
- package/src/core/init/stall_avoider.ts +40 -26
- package/src/core/segment_buffers/garbage_collector.ts +55 -47
- package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +92 -70
- package/src/core/segment_buffers/implementations/image/image_segment_buffer.ts +37 -42
- package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +103 -105
- package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +35 -46
- package/src/core/segment_buffers/implementations/types.ts +22 -9
- package/src/core/segment_buffers/segment_buffers_store.ts +23 -14
- package/src/core/stream/orchestrator/stream_orchestrator.ts +31 -12
- package/src/core/stream/period/period_stream.ts +31 -18
- package/src/core/stream/representation/append_segment_to_buffer.ts +27 -42
- package/src/core/stream/representation/force_garbage_collection.ts +28 -32
- package/src/core/stream/representation/get_buffer_status.ts +7 -3
- package/src/core/stream/representation/push_init_segment.ts +12 -6
- package/src/core/stream/representation/push_media_segment.ts +12 -6
- package/src/core/stream/representation/representation_stream.ts +11 -5
- package/src/default_config.ts +17 -17
- package/src/errors/custom_loader_error.ts +3 -2
- package/src/experimental/tools/VideoThumbnailLoader/get_initialized_source_buffer.ts +7 -2
- package/src/experimental/tools/VideoThumbnailLoader/push_data.ts +6 -2
- package/src/experimental/tools/VideoThumbnailLoader/remove_buffer_around_time.ts +10 -2
- package/src/experimental/tools/VideoThumbnailLoader/thumbnail_loader.ts +3 -1
- package/src/experimental/tools/createMetaplaylist/get_duration_from_manifest.ts +4 -3
- package/src/public_types.ts +28 -4
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +2 -2
- package/src/transports/dash/image_pipelines.ts +4 -0
- package/src/transports/dash/init_segment_loader.ts +8 -0
- package/src/transports/dash/low_latency_segment_loader.ts +4 -0
- package/src/transports/dash/manifest_parser.ts +4 -0
- package/src/transports/dash/segment_loader.ts +21 -5
- package/src/transports/dash/text_loader.ts +7 -2
- package/src/transports/local/pipelines.ts +7 -5
- package/src/transports/local/segment_loader.ts +4 -2
- package/src/transports/metaplaylist/manifest_loader.ts +9 -2
- package/src/transports/metaplaylist/pipelines.ts +16 -6
- package/src/transports/smooth/pipelines.ts +17 -9
- package/src/transports/smooth/segment_loader.ts +8 -0
- package/src/transports/types.ts +27 -0
- package/src/transports/utils/call_custom_manifest_loader.ts +8 -2
- package/src/transports/utils/generate_manifest_loader.ts +18 -5
- package/src/utils/request/fetch.ts +7 -8
- package/src/utils/request/xhr.ts +31 -15
- 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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
* @
|
|
190
|
+
* @param {Object} cancelWaitSignal
|
|
191
|
+
* @return {Promise}
|
|
191
192
|
*/
|
|
192
|
-
public waitForUsableBuffers() :
|
|
193
|
+
public waitForUsableBuffers(cancelWaitSignal : CancellationSignal) : Promise<void> {
|
|
193
194
|
if (this._areNativeBuffersUsable()) {
|
|
194
|
-
return
|
|
195
|
+
return Promise.resolve();
|
|
195
196
|
}
|
|
196
|
-
return new
|
|
197
|
-
|
|
197
|
+
return new Promise((res, rej) => {
|
|
198
|
+
const onAddedOrDisabled = () => {
|
|
198
199
|
if (this._areNativeBuffersUsable()) {
|
|
199
|
-
|
|
200
|
-
|
|
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 {
|
|
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
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
327
|
-
|
|
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
|
|
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
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
* @
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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 {
|
|
30
|
+
* @param {number} currentPosition
|
|
37
31
|
* @param {Object} bufferingQueue
|
|
38
|
-
* @
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
62
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
|
|
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`.
|
package/src/default_config.ts
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|