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
|
@@ -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.
|
|
258
|
-
|
|
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.
|
|
273
|
-
|
|
290
|
+
observation.pendingInternalSeek !== null ?
|
|
291
|
+
"internal-seek" as const :
|
|
292
|
+
rebuffering.reason;
|
|
274
293
|
|
|
275
|
-
if (
|
|
276
|
-
lastSeekingPosition = observation.position;
|
|
277
|
-
} else if (lastSeekingPosition !== null) {
|
|
294
|
+
if (ignoredStallTimeStamp !== null) {
|
|
278
295
|
const now = performance.now();
|
|
279
|
-
if (ignoredStallTimeStamp
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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 (
|
|
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
|
-
|
|
29
|
+
playbackObserver : IReadOnlyPlaybackObserver<
|
|
30
|
+
Pick<IStreamOrchestratorPlaybackObservation, "position">
|
|
31
|
+
>;
|
|
36
32
|
/** Maximum time to keep behind current time position, in seconds */
|
|
37
|
-
maxBufferBehind
|
|
33
|
+
maxBufferBehind : IReadOnlySharedReference<number>;
|
|
38
34
|
/** Minimum time to keep behind current time position, in seconds */
|
|
39
|
-
maxBufferAhead
|
|
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 `
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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 {
|
|
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
|
-
|
|
99
|
+
maxBufferAhead : number,
|
|
100
|
+
cancellationSignal : CancellationSignal
|
|
101
|
+
) : Promise<void> {
|
|
87
102
|
if (!isFinite(maxBufferBehind) && !isFinite(maxBufferAhead)) {
|
|
88
|
-
return
|
|
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
|
-
|
|
154
|
-
|
|
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 (
|
|
157
|
-
|
|
172
|
+
if (cancellationSignal.cancellationError !== null) {
|
|
173
|
+
throw cancellationSignal.cancellationError;
|
|
158
174
|
}
|
|
159
|
-
|
|
160
|
-
}
|
|
161
|
-
|
|
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> & {
|
|
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 & {
|
|
74
|
-
|
|
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
|
-
/**
|
|
92
|
-
|
|
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
|
-
*
|
|
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
|
|
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 {
|
|
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.
|
|
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
|
|
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
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
* @
|
|
225
|
+
* @param {Object} cancellationSignal
|
|
226
|
+
* @returns {Promise}
|
|
222
227
|
*/
|
|
223
|
-
public pushChunk(
|
|
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
|
-
* @
|
|
245
|
+
* @param {Object} cancellationSignal
|
|
246
|
+
* @returns {Promise}
|
|
237
247
|
*/
|
|
238
|
-
public removeBuffer(
|
|
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
|
|
252
|
-
*
|
|
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 {
|
|
269
|
+
* @returns {Promise}
|
|
255
270
|
*/
|
|
256
|
-
public endOfSegment(
|
|
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.
|
|
306
|
-
this._destroy$.complete();
|
|
324
|
+
this._canceller.cancel();
|
|
307
325
|
|
|
308
326
|
if (this._pendingTask !== null) {
|
|
309
|
-
this._pendingTask.
|
|
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.
|
|
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}
|
|
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.
|
|
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
|
-
* @
|
|
371
|
+
* @param {Object} cancellationSignal
|
|
372
|
+
* @returns {Promise}
|
|
354
373
|
*/
|
|
355
|
-
private _addToQueue(
|
|
356
|
-
|
|
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
|
|
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 {
|
|
435
|
+
const { resolve } = task;
|
|
413
436
|
this._pendingTask = null;
|
|
414
|
-
|
|
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.
|
|
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}
|
|
517
|
+
* @param {Object} data
|
|
496
518
|
* @returns {Object}
|
|
497
519
|
*/
|
|
498
520
|
private _preparePushOperation(
|