rx-player 3.27.0 → 3.27.1-dev.2022041500
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 +12 -0
- package/VERSION +1 -1
- package/dist/_esm5.processed/core/abr/abr_manager.d.ts +7 -3
- package/dist/_esm5.processed/core/abr/abr_manager.js +4 -3
- package/dist/_esm5.processed/core/abr/representation_estimator.d.ts +13 -8
- package/dist/_esm5.processed/core/abr/representation_estimator.js +4 -4
- package/dist/_esm5.processed/core/api/public_api.js +13 -14
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.d.ts +52 -0
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +223 -0
- package/dist/_esm5.processed/core/init/create_stream_playback_observer.js +17 -6
- package/dist/_esm5.processed/core/init/get_initial_time.d.ts +30 -5
- package/dist/_esm5.processed/core/init/get_initial_time.js +20 -13
- package/dist/_esm5.processed/core/init/load_on_media_source.js +16 -3
- package/dist/_esm5.processed/core/init/media_duration_updater.d.ts +56 -0
- package/dist/_esm5.processed/core/init/media_duration_updater.js +188 -0
- package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.d.ts +2 -5
- package/dist/_esm5.processed/core/stream/adaptation/create_representation_estimator.d.ts +3 -2
- package/dist/_esm5.processed/core/stream/adaptation/create_representation_estimator.js +3 -3
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +2 -22
- package/dist/_esm5.processed/core/stream/period/period_stream.d.ts +2 -5
- package/dist/_esm5.processed/core/stream/period/period_stream.js +6 -4
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.d.ts +4 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +85 -42
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +3 -3
- package/dist/_esm5.processed/manifest/manifest.d.ts +14 -5
- package/dist/_esm5.processed/manifest/manifest.js +31 -12
- package/dist/_esm5.processed/manifest/period.d.ts +6 -0
- package/dist/_esm5.processed/manifest/period.js +9 -0
- package/dist/_esm5.processed/manifest/representation_index/types.d.ts +7 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/get_minimum_and_maximum_positions.d.ts +5 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/get_minimum_and_maximum_positions.js +6 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +2 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +4 -0
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +4 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +4 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +3 -3
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_mpd.js +28 -20
- package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +3 -2
- package/dist/_esm5.processed/parsers/manifest/metaplaylist/metaplaylist_parser.js +9 -3
- package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +26 -17
- package/dist/_esm5.processed/parsers/manifest/types.d.ts +45 -17
- package/dist/_esm5.processed/parsers/manifest/utils/{get_maximum_position.d.ts → get_maximum_positions.d.ts} +4 -1
- package/dist/_esm5.processed/parsers/manifest/utils/{get_maximum_position.js → get_maximum_positions.js} +10 -6
- package/dist/rx-player.js +854 -351
- package/dist/rx-player.min.js +1 -1
- package/package.json +1 -1
- package/sonar-project.properties +1 -1
- package/src/core/abr/abr_manager.ts +11 -3
- package/src/core/abr/representation_estimator.ts +17 -10
- package/src/core/api/public_api.ts +13 -15
- package/src/core/init/content_time_boundaries_observer.ts +312 -0
- package/src/core/init/create_stream_playback_observer.ts +18 -7
- package/src/core/init/get_initial_time.ts +52 -19
- package/src/core/init/load_on_media_source.ts +22 -5
- package/src/core/init/media_duration_updater.ts +268 -0
- package/src/core/stream/adaptation/adaptation_stream.ts +2 -5
- package/src/core/stream/adaptation/create_representation_estimator.ts +6 -3
- package/src/core/stream/orchestrator/stream_orchestrator.ts +2 -29
- package/src/core/stream/period/period_stream.ts +12 -9
- package/src/core/stream/representation/get_buffer_status.ts +102 -41
- package/src/core/stream/representation/representation_stream.ts +3 -3
- package/src/manifest/__tests__/manifest.test.ts +94 -68
- package/src/manifest/manifest.ts +73 -33
- package/src/manifest/period.ts +10 -0
- package/src/manifest/representation_index/types.ts +7 -2
- package/src/parsers/manifest/dash/common/get_minimum_and_maximum_positions.ts +10 -5
- package/src/parsers/manifest/dash/common/indexes/base.ts +4 -3
- package/src/parsers/manifest/dash/common/indexes/list.ts +8 -1
- package/src/parsers/manifest/dash/common/indexes/template.ts +8 -3
- package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +7 -7
- package/src/parsers/manifest/dash/common/parse_mpd.ts +38 -20
- package/src/parsers/manifest/local/parse_local_manifest.ts +3 -2
- package/src/parsers/manifest/metaplaylist/metaplaylist_parser.ts +9 -4
- package/src/parsers/manifest/smooth/create_parser.ts +36 -18
- package/src/parsers/manifest/types.ts +45 -17
- package/src/parsers/manifest/utils/{get_maximum_position.ts → get_maximum_positions.ts} +13 -7
- package/dist/_esm5.processed/core/init/duration_updater.d.ts +0 -27
- package/dist/_esm5.processed/core/init/duration_updater.js +0 -136
- package/src/core/init/duration_updater.ts +0 -206
|
@@ -263,10 +263,15 @@ export interface IParsedManifest {
|
|
|
263
263
|
*/
|
|
264
264
|
timeBounds : {
|
|
265
265
|
/**
|
|
266
|
-
*
|
|
267
|
-
*
|
|
266
|
+
* This is the theoretical minimum playable position on the content
|
|
267
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
268
|
+
* time.
|
|
269
|
+
* `undefined` if unknown.
|
|
270
|
+
*
|
|
271
|
+
* More technically, the `minimumSafePosition` is the maximum between all
|
|
272
|
+
* the minimum positions reachable in any of the audio and video Adaptation.
|
|
268
273
|
*/
|
|
269
|
-
|
|
274
|
+
minimumSafePosition? : number | undefined;
|
|
270
275
|
/**
|
|
271
276
|
* Some dynamic contents have the concept of a "window depth" (or "buffer
|
|
272
277
|
* depth") which allows to set a minimum position for all reachable
|
|
@@ -283,27 +288,50 @@ export interface IParsedManifest {
|
|
|
283
288
|
timeshiftDepth : number | null;
|
|
284
289
|
/** Data allowing to calculate the maximum position at any given time. */
|
|
285
290
|
maximumTimeData : {
|
|
286
|
-
/** Maximum seekable time in milliseconds calculated at `time`. */
|
|
287
|
-
value : number;
|
|
288
291
|
/**
|
|
289
|
-
*
|
|
290
|
-
*
|
|
291
|
-
*
|
|
292
|
+
* Current position representing live content.
|
|
293
|
+
* Only makes sense for un-ended live contents.
|
|
294
|
+
*
|
|
295
|
+
* `undefined` if unknown or if it doesn't make sense in the current context.
|
|
292
296
|
*/
|
|
293
|
-
|
|
297
|
+
livePosition : number | undefined;
|
|
294
298
|
/**
|
|
295
|
-
* Whether the maximum
|
|
299
|
+
* Whether the maximum positions should evolve linearly over time.
|
|
296
300
|
*
|
|
297
|
-
* If set to `
|
|
298
|
-
*
|
|
299
|
-
* If set to `true`, the maximum seekable time continuously increase at
|
|
301
|
+
* If set to `true`, the maximum seekable position continuously increase at
|
|
300
302
|
* the same rate than the time since `time` does.
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
303
|
+
*
|
|
304
|
+
* For example, a `maximumSafePosition` of 50000 (50 seconds) will
|
|
305
|
+
* indicate a maximum time of 51 seconds after 1 second have passed, of 56
|
|
306
|
+
* seconds after 6 seconds have passed (we know how many seconds have
|
|
307
|
+
* passed since the initial calculation of the maximum position by
|
|
308
|
+
* checking the `time` property) etc.
|
|
305
309
|
*/
|
|
306
310
|
isLinear: boolean;
|
|
311
|
+
/**
|
|
312
|
+
* This is the theoretical maximum playable position on the content,
|
|
313
|
+
* regardless of the current Adaptation chosen, as estimated at parsing
|
|
314
|
+
* time.
|
|
315
|
+
*
|
|
316
|
+
* More technically, the `maximumSafePosition` is the minimum between all
|
|
317
|
+
* attributes indicating the duration of the content in the Manifest.
|
|
318
|
+
*
|
|
319
|
+
* That is the minimum between:
|
|
320
|
+
* - The Manifest original attributes relative to its duration
|
|
321
|
+
* - The minimum between all known maximum audio positions
|
|
322
|
+
* - The minimum between all known maximum video positions
|
|
323
|
+
*
|
|
324
|
+
* This can for example be understood as the safe maximum playable
|
|
325
|
+
* position through all possible tacks.
|
|
326
|
+
*/
|
|
327
|
+
maximumSafePosition : number;
|
|
328
|
+
/**
|
|
329
|
+
* `Performance.now()` output at the time both `maximumSafePosition` and
|
|
330
|
+
* `maximumPositionWithMediaData` were calculated.
|
|
331
|
+
* This can be used to retrieve a new maximum position from them when they
|
|
332
|
+
* linearly evolves over time (see `isLinear` property).
|
|
333
|
+
*/
|
|
334
|
+
time : number;
|
|
307
335
|
};
|
|
308
336
|
};
|
|
309
337
|
/**
|
|
@@ -24,7 +24,9 @@ import getLastPositionFromAdaptation from "./get_last_time_from_adaptation";
|
|
|
24
24
|
*/
|
|
25
25
|
export default function getMaximumPosition(
|
|
26
26
|
periods : IParsedPeriod[]
|
|
27
|
-
) : number | undefined
|
|
27
|
+
) : { safe : number | undefined;
|
|
28
|
+
unsafe : number | undefined; }
|
|
29
|
+
{
|
|
28
30
|
for (let i = periods.length - 1; i >= 0; i--) {
|
|
29
31
|
const periodAdaptations = periods[i].adaptations;
|
|
30
32
|
const firstAudioAdaptationFromPeriod = periodAdaptations.audio === undefined ?
|
|
@@ -44,7 +46,7 @@ export default function getMaximumPosition(
|
|
|
44
46
|
const lastPosition =
|
|
45
47
|
getLastPositionFromAdaptation(firstAudioAdaptationFromPeriod);
|
|
46
48
|
if (lastPosition === undefined) {
|
|
47
|
-
return undefined;
|
|
49
|
+
return { safe: undefined, unsafe: undefined };
|
|
48
50
|
}
|
|
49
51
|
maximumAudioPosition = lastPosition;
|
|
50
52
|
}
|
|
@@ -52,7 +54,7 @@ export default function getMaximumPosition(
|
|
|
52
54
|
const lastPosition =
|
|
53
55
|
getLastPositionFromAdaptation(firstVideoAdaptationFromPeriod);
|
|
54
56
|
if (lastPosition === undefined) {
|
|
55
|
-
return undefined;
|
|
57
|
+
return { safe: undefined, unsafe: undefined };
|
|
56
58
|
}
|
|
57
59
|
maximumVideoPosition = lastPosition;
|
|
58
60
|
}
|
|
@@ -64,18 +66,22 @@ export default function getMaximumPosition(
|
|
|
64
66
|
) {
|
|
65
67
|
log.info("Parser utils: found Period with no segment. ",
|
|
66
68
|
"Going to previous one to calculate last position");
|
|
67
|
-
return undefined;
|
|
69
|
+
return { safe: undefined, unsafe: undefined };
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
if (maximumVideoPosition !== null) {
|
|
71
73
|
if (maximumAudioPosition !== null) {
|
|
72
|
-
return Math.min(maximumAudioPosition, maximumVideoPosition)
|
|
74
|
+
return { safe: Math.min(maximumAudioPosition, maximumVideoPosition),
|
|
75
|
+
unsafe: Math.max(maximumAudioPosition, maximumVideoPosition) };
|
|
73
76
|
}
|
|
74
|
-
return maximumVideoPosition
|
|
77
|
+
return { safe: maximumVideoPosition,
|
|
78
|
+
unsafe: maximumVideoPosition };
|
|
75
79
|
}
|
|
76
80
|
if (maximumAudioPosition !== null) {
|
|
77
|
-
return maximumAudioPosition
|
|
81
|
+
return { safe: maximumAudioPosition,
|
|
82
|
+
unsafe: maximumAudioPosition };
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
}
|
|
86
|
+
return { safe: undefined, unsafe: undefined };
|
|
81
87
|
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2015 CANAL+ Group
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { Observable } from "rxjs";
|
|
17
|
-
import Manifest from "../../manifest";
|
|
18
|
-
/**
|
|
19
|
-
* Keep the MediaSource duration up-to-date with the Manifest one on
|
|
20
|
-
* subscription:
|
|
21
|
-
* Set the current duration initially and then update if needed after
|
|
22
|
-
* each Manifest updates.
|
|
23
|
-
* @param {Object} manifest
|
|
24
|
-
* @param {MediaSource} mediaSource
|
|
25
|
-
* @returns {Observable}
|
|
26
|
-
*/
|
|
27
|
-
export default function DurationUpdater(manifest: Manifest, mediaSource: MediaSource): Observable<never>;
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2015 CANAL+ Group
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
import { catchError, combineLatest as observableCombineLatest, defer as observableDefer, distinctUntilChanged, EMPTY, filter, fromEvent as observableFromEvent, ignoreElements, interval as observableInterval, map, merge as observableMerge, mergeMap, of as observableOf, startWith, switchMap, take, tap, } from "rxjs";
|
|
17
|
-
import { onSourceOpen$, onSourceClose$, onSourceEnded$, } from "../../compat/event_listeners";
|
|
18
|
-
import log from "../../log";
|
|
19
|
-
import { fromEvent } from "../../utils/event_emitter";
|
|
20
|
-
/** Number of seconds in a regular year. */
|
|
21
|
-
var YEAR_IN_SECONDS = 365 * 24 * 3600;
|
|
22
|
-
/**
|
|
23
|
-
* Keep the MediaSource duration up-to-date with the Manifest one on
|
|
24
|
-
* subscription:
|
|
25
|
-
* Set the current duration initially and then update if needed after
|
|
26
|
-
* each Manifest updates.
|
|
27
|
-
* @param {Object} manifest
|
|
28
|
-
* @param {MediaSource} mediaSource
|
|
29
|
-
* @returns {Observable}
|
|
30
|
-
*/
|
|
31
|
-
export default function DurationUpdater(manifest, mediaSource) {
|
|
32
|
-
return observableDefer(function () {
|
|
33
|
-
var lastDurationUpdate;
|
|
34
|
-
return setMediaSourceDuration(mediaSource, manifest).pipe(mergeMap(function (initialDurationUpdate) {
|
|
35
|
-
// only update `lastDurationUpdate` if the MediaSource's duration has
|
|
36
|
-
// been updated.
|
|
37
|
-
if (initialDurationUpdate !== null) {
|
|
38
|
-
lastDurationUpdate = initialDurationUpdate;
|
|
39
|
-
}
|
|
40
|
-
return fromEvent(manifest, "manifestUpdate").pipe(switchMap(function () { return setMediaSourceDuration(mediaSource, manifest, lastDurationUpdate); }), tap(function (durationUpdate) {
|
|
41
|
-
if (durationUpdate !== null) {
|
|
42
|
-
lastDurationUpdate = durationUpdate;
|
|
43
|
-
}
|
|
44
|
-
}));
|
|
45
|
-
}),
|
|
46
|
-
// NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
|
|
47
|
-
// first type parameter as `any` instead of the perfectly fine `unknown`,
|
|
48
|
-
// leading to linter issues, as it forbids the usage of `any`.
|
|
49
|
-
// This is why we're disabling the eslint rule.
|
|
50
|
-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */
|
|
51
|
-
ignoreElements());
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Checks that duration can be updated on the MediaSource, and then
|
|
56
|
-
* sets it.
|
|
57
|
-
*
|
|
58
|
-
* Returns either:
|
|
59
|
-
* - the new duration it has been updated to if it has
|
|
60
|
-
* - `null` if it hasn'nt been updated
|
|
61
|
-
*
|
|
62
|
-
* @param {MediaSource} mediaSource
|
|
63
|
-
* @param {Object} manifest
|
|
64
|
-
* @param {number | undefined} lastSetDuration
|
|
65
|
-
* @returns {Observable.<number | null>}
|
|
66
|
-
*/
|
|
67
|
-
function setMediaSourceDuration(mediaSource, manifest, lastSetDuration) {
|
|
68
|
-
return isMediaSourceOpened$(mediaSource).pipe(switchMap(function (isMediaSourceOpened) {
|
|
69
|
-
if (!isMediaSourceOpened) {
|
|
70
|
-
return EMPTY;
|
|
71
|
-
}
|
|
72
|
-
return whenSourceBuffersEndedUpdates$(mediaSource.sourceBuffers);
|
|
73
|
-
}), take(1), map(function () {
|
|
74
|
-
var maximumPosition = manifest.getMaximumPosition();
|
|
75
|
-
var isLive = manifest.isLive;
|
|
76
|
-
// Some targets poorly support setting a very high number for durations.
|
|
77
|
-
// Yet, in live contents, we would prefer setting a value as high as possible
|
|
78
|
-
// to still be able to seek anywhere we want to (even ahead of the Manifest if
|
|
79
|
-
// we want to). As such, we put it at a safe default value of 2^32 excepted
|
|
80
|
-
// when the maximum position is already relatively close to that value, where
|
|
81
|
-
// we authorize exceptionally going over it.
|
|
82
|
-
var newDuration = !isLive ? maximumPosition :
|
|
83
|
-
Math.max(Math.pow(2, 32), maximumPosition + YEAR_IN_SECONDS);
|
|
84
|
-
if (mediaSource.duration >= newDuration ||
|
|
85
|
-
// Even if the MediaSource duration is different than the duration that
|
|
86
|
-
// we want to set now, the last duration we wanted to set may be the same,
|
|
87
|
-
// as the MediaSource duration may have been changed by the browser.
|
|
88
|
-
//
|
|
89
|
-
// In that case, we do not want to update it.
|
|
90
|
-
//
|
|
91
|
-
newDuration === lastSetDuration) {
|
|
92
|
-
return null;
|
|
93
|
-
}
|
|
94
|
-
if (isNaN(mediaSource.duration) || !isFinite(mediaSource.duration) ||
|
|
95
|
-
newDuration - mediaSource.duration > 0.01) {
|
|
96
|
-
log.info("Init: Updating duration", newDuration);
|
|
97
|
-
mediaSource.duration = newDuration;
|
|
98
|
-
return newDuration;
|
|
99
|
-
}
|
|
100
|
-
return null;
|
|
101
|
-
}), catchError(function (err) {
|
|
102
|
-
log.warn("Duration Updater: Can't update duration on the MediaSource.", err);
|
|
103
|
-
return observableOf(null);
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* Returns an Observable which will emit only when all the SourceBuffers ended
|
|
108
|
-
* all pending updates.
|
|
109
|
-
* @param {SourceBufferList} sourceBuffers
|
|
110
|
-
* @returns {Observable}
|
|
111
|
-
*/
|
|
112
|
-
function whenSourceBuffersEndedUpdates$(sourceBuffers) {
|
|
113
|
-
if (sourceBuffers.length === 0) {
|
|
114
|
-
return observableOf(undefined);
|
|
115
|
-
}
|
|
116
|
-
var sourceBufferUpdatingStatuses = [];
|
|
117
|
-
var _loop_1 = function (i) {
|
|
118
|
-
var sourceBuffer = sourceBuffers[i];
|
|
119
|
-
sourceBufferUpdatingStatuses.push(observableMerge(observableFromEvent(sourceBuffer, "updatestart").pipe(map(function () { return true; })), observableFromEvent(sourceBuffer, "update").pipe(map(function () { return false; })), observableInterval(500).pipe(map(function () { return sourceBuffer.updating; }))).pipe(startWith(sourceBuffer.updating), distinctUntilChanged()));
|
|
120
|
-
};
|
|
121
|
-
for (var i = 0; i < sourceBuffers.length; i++) {
|
|
122
|
-
_loop_1(i);
|
|
123
|
-
}
|
|
124
|
-
return observableCombineLatest(sourceBufferUpdatingStatuses).pipe(filter(function (areUpdating) {
|
|
125
|
-
return areUpdating.every(function (isUpdating) { return !isUpdating; });
|
|
126
|
-
}), map(function () { return undefined; }));
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Emit a boolean that tells if the media source is opened or not.
|
|
130
|
-
* @param {MediaSource} mediaSource
|
|
131
|
-
* @returns {Object}
|
|
132
|
-
*/
|
|
133
|
-
function isMediaSourceOpened$(mediaSource) {
|
|
134
|
-
return observableMerge(onSourceOpen$(mediaSource).pipe(map(function () { return true; })), onSourceEnded$(mediaSource).pipe(map(function () { return false; })), onSourceClose$(mediaSource).pipe(map(function () { return false; }))).pipe(startWith(mediaSource.readyState === "open"), distinctUntilChanged());
|
|
135
|
-
}
|
|
136
|
-
/* eslint-enable @typescript-eslint/no-unsafe-argument */
|
|
@@ -1,206 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright 2015 CANAL+ Group
|
|
3
|
-
*
|
|
4
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
*
|
|
8
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
-
*
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
catchError,
|
|
19
|
-
combineLatest as observableCombineLatest,
|
|
20
|
-
defer as observableDefer,
|
|
21
|
-
distinctUntilChanged,
|
|
22
|
-
EMPTY,
|
|
23
|
-
filter,
|
|
24
|
-
fromEvent as observableFromEvent,
|
|
25
|
-
ignoreElements,
|
|
26
|
-
interval as observableInterval,
|
|
27
|
-
map,
|
|
28
|
-
merge as observableMerge,
|
|
29
|
-
mergeMap,
|
|
30
|
-
Observable,
|
|
31
|
-
of as observableOf,
|
|
32
|
-
startWith,
|
|
33
|
-
switchMap,
|
|
34
|
-
take,
|
|
35
|
-
tap,
|
|
36
|
-
} from "rxjs";
|
|
37
|
-
import {
|
|
38
|
-
onSourceOpen$,
|
|
39
|
-
onSourceClose$,
|
|
40
|
-
onSourceEnded$,
|
|
41
|
-
} from "../../compat/event_listeners";
|
|
42
|
-
import log from "../../log";
|
|
43
|
-
import Manifest from "../../manifest";
|
|
44
|
-
import { fromEvent } from "../../utils/event_emitter";
|
|
45
|
-
|
|
46
|
-
/** Number of seconds in a regular year. */
|
|
47
|
-
const YEAR_IN_SECONDS = 365 * 24 * 3600;
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Keep the MediaSource duration up-to-date with the Manifest one on
|
|
51
|
-
* subscription:
|
|
52
|
-
* Set the current duration initially and then update if needed after
|
|
53
|
-
* each Manifest updates.
|
|
54
|
-
* @param {Object} manifest
|
|
55
|
-
* @param {MediaSource} mediaSource
|
|
56
|
-
* @returns {Observable}
|
|
57
|
-
*/
|
|
58
|
-
export default function DurationUpdater(
|
|
59
|
-
manifest : Manifest,
|
|
60
|
-
mediaSource : MediaSource
|
|
61
|
-
) : Observable<never> {
|
|
62
|
-
return observableDefer(() => {
|
|
63
|
-
let lastDurationUpdate: number | undefined;
|
|
64
|
-
return setMediaSourceDuration(mediaSource, manifest).pipe(
|
|
65
|
-
mergeMap((initialDurationUpdate) => {
|
|
66
|
-
// only update `lastDurationUpdate` if the MediaSource's duration has
|
|
67
|
-
// been updated.
|
|
68
|
-
if (initialDurationUpdate !== null) {
|
|
69
|
-
lastDurationUpdate = initialDurationUpdate;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
return fromEvent(manifest, "manifestUpdate").pipe(
|
|
73
|
-
switchMap(() => setMediaSourceDuration(mediaSource,
|
|
74
|
-
manifest,
|
|
75
|
-
lastDurationUpdate)),
|
|
76
|
-
tap((durationUpdate) => {
|
|
77
|
-
if (durationUpdate !== null) {
|
|
78
|
-
lastDurationUpdate = durationUpdate;
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
);
|
|
82
|
-
}),
|
|
83
|
-
// NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
|
|
84
|
-
// first type parameter as `any` instead of the perfectly fine `unknown`,
|
|
85
|
-
// leading to linter issues, as it forbids the usage of `any`.
|
|
86
|
-
// This is why we're disabling the eslint rule.
|
|
87
|
-
/* eslint-disable-next-line @typescript-eslint/no-unsafe-argument */
|
|
88
|
-
ignoreElements()
|
|
89
|
-
);
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Checks that duration can be updated on the MediaSource, and then
|
|
95
|
-
* sets it.
|
|
96
|
-
*
|
|
97
|
-
* Returns either:
|
|
98
|
-
* - the new duration it has been updated to if it has
|
|
99
|
-
* - `null` if it hasn'nt been updated
|
|
100
|
-
*
|
|
101
|
-
* @param {MediaSource} mediaSource
|
|
102
|
-
* @param {Object} manifest
|
|
103
|
-
* @param {number | undefined} lastSetDuration
|
|
104
|
-
* @returns {Observable.<number | null>}
|
|
105
|
-
*/
|
|
106
|
-
function setMediaSourceDuration(
|
|
107
|
-
mediaSource: MediaSource,
|
|
108
|
-
manifest: Manifest,
|
|
109
|
-
lastSetDuration?: number
|
|
110
|
-
): Observable<number | null> {
|
|
111
|
-
return isMediaSourceOpened$(mediaSource).pipe(
|
|
112
|
-
switchMap((isMediaSourceOpened) => {
|
|
113
|
-
if (!isMediaSourceOpened) {
|
|
114
|
-
return EMPTY;
|
|
115
|
-
}
|
|
116
|
-
return whenSourceBuffersEndedUpdates$(mediaSource.sourceBuffers);
|
|
117
|
-
}),
|
|
118
|
-
take(1),
|
|
119
|
-
map(() : number | null => {
|
|
120
|
-
const maximumPosition = manifest.getMaximumPosition();
|
|
121
|
-
const { isLive } = manifest;
|
|
122
|
-
// Some targets poorly support setting a very high number for durations.
|
|
123
|
-
// Yet, in live contents, we would prefer setting a value as high as possible
|
|
124
|
-
// to still be able to seek anywhere we want to (even ahead of the Manifest if
|
|
125
|
-
// we want to). As such, we put it at a safe default value of 2^32 excepted
|
|
126
|
-
// when the maximum position is already relatively close to that value, where
|
|
127
|
-
// we authorize exceptionally going over it.
|
|
128
|
-
const newDuration = !isLive ? maximumPosition :
|
|
129
|
-
Math.max(Math.pow(2, 32),
|
|
130
|
-
maximumPosition + YEAR_IN_SECONDS);
|
|
131
|
-
|
|
132
|
-
if (mediaSource.duration >= newDuration ||
|
|
133
|
-
// Even if the MediaSource duration is different than the duration that
|
|
134
|
-
// we want to set now, the last duration we wanted to set may be the same,
|
|
135
|
-
// as the MediaSource duration may have been changed by the browser.
|
|
136
|
-
//
|
|
137
|
-
// In that case, we do not want to update it.
|
|
138
|
-
//
|
|
139
|
-
newDuration === lastSetDuration) {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
if (isNaN(mediaSource.duration) || !isFinite(mediaSource.duration) ||
|
|
143
|
-
newDuration - mediaSource.duration > 0.01) {
|
|
144
|
-
log.info("Init: Updating duration", newDuration);
|
|
145
|
-
mediaSource.duration = newDuration;
|
|
146
|
-
return newDuration;
|
|
147
|
-
}
|
|
148
|
-
return null;
|
|
149
|
-
}),
|
|
150
|
-
catchError((err) => {
|
|
151
|
-
log.warn("Duration Updater: Can't update duration on the MediaSource.", err);
|
|
152
|
-
return observableOf(null);
|
|
153
|
-
})
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Returns an Observable which will emit only when all the SourceBuffers ended
|
|
159
|
-
* all pending updates.
|
|
160
|
-
* @param {SourceBufferList} sourceBuffers
|
|
161
|
-
* @returns {Observable}
|
|
162
|
-
*/
|
|
163
|
-
function whenSourceBuffersEndedUpdates$(
|
|
164
|
-
sourceBuffers: SourceBufferList
|
|
165
|
-
) : Observable<undefined> {
|
|
166
|
-
if (sourceBuffers.length === 0) {
|
|
167
|
-
return observableOf(undefined);
|
|
168
|
-
}
|
|
169
|
-
const sourceBufferUpdatingStatuses : Array<Observable<boolean>> = [];
|
|
170
|
-
|
|
171
|
-
for (let i = 0; i < sourceBuffers.length; i++) {
|
|
172
|
-
const sourceBuffer = sourceBuffers[i];
|
|
173
|
-
sourceBufferUpdatingStatuses.push(
|
|
174
|
-
observableMerge(
|
|
175
|
-
observableFromEvent(sourceBuffer, "updatestart").pipe(map(() => true)),
|
|
176
|
-
observableFromEvent(sourceBuffer, "update").pipe(map(() => false)),
|
|
177
|
-
observableInterval(500).pipe(map(() => sourceBuffer.updating))
|
|
178
|
-
).pipe(
|
|
179
|
-
startWith(sourceBuffer.updating),
|
|
180
|
-
distinctUntilChanged()
|
|
181
|
-
)
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
return observableCombineLatest(sourceBufferUpdatingStatuses).pipe(
|
|
185
|
-
filter((areUpdating) => {
|
|
186
|
-
return areUpdating.every((isUpdating) => !isUpdating);
|
|
187
|
-
}),
|
|
188
|
-
map(() => undefined)
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Emit a boolean that tells if the media source is opened or not.
|
|
194
|
-
* @param {MediaSource} mediaSource
|
|
195
|
-
* @returns {Object}
|
|
196
|
-
*/
|
|
197
|
-
function isMediaSourceOpened$(mediaSource: MediaSource): Observable<boolean> {
|
|
198
|
-
return observableMerge(onSourceOpen$(mediaSource).pipe(map(() => true)),
|
|
199
|
-
onSourceEnded$(mediaSource).pipe(map(() => false)),
|
|
200
|
-
onSourceClose$(mediaSource).pipe(map(() => false))
|
|
201
|
-
).pipe(
|
|
202
|
-
startWith(mediaSource.readyState === "open"),
|
|
203
|
-
distinctUntilChanged()
|
|
204
|
-
);
|
|
205
|
-
}
|
|
206
|
-
/* eslint-enable @typescript-eslint/no-unsafe-argument */
|