rx-player 3.27.1-dev.2022040500 → 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 +7 -2
- 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/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 +850 -348
- 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 +2 -5
- 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
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v3.27.1-dev.
|
|
3
|
+
## v3.27.1-dev.2022041500 (2022-04-15)
|
|
4
4
|
|
|
5
5
|
### Bug fixes
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- Use the first compatible codec of the current AdaptationSet when creating a SourceBuffer [#1094]
|
|
8
|
+
- DASH: fix wrong segment estimates on some SegmentTimeline-based contents where the last segment has a negative `S@d` attribute set in the MPD [#1098]
|
|
8
9
|
|
|
10
|
+
### Other improvements
|
|
11
|
+
|
|
12
|
+
- Update duration of a content based on the last chosen tracks [#1102]
|
|
13
|
+
- If seeking after the last potential position, load last segments before ending [#1097]
|
|
9
14
|
|
|
10
15
|
## v3.27.0 (2022-03-31)
|
|
11
16
|
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.27.1-dev.
|
|
1
|
+
3.27.1-dev.2022041500
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { Observable } from "rxjs";
|
|
17
|
-
import { Representation } from "../../manifest";
|
|
17
|
+
import Manifest, { Adaptation, Period, Representation } from "../../manifest";
|
|
18
18
|
import { ISharedReference } from "../../utils/reference";
|
|
19
19
|
import { IBufferType } from "../segment_buffers";
|
|
20
20
|
import { IABREstimate, IABRStreamEvents, IRepresentationEstimatorPlaybackObservation } from "./representation_estimator";
|
|
@@ -59,13 +59,17 @@ export default class ABRManager {
|
|
|
59
59
|
* Take type and an array of the available representations, spit out an
|
|
60
60
|
* observable emitting the best representation (given the network/buffer
|
|
61
61
|
* state).
|
|
62
|
-
* @param {
|
|
62
|
+
* @param {Object} context
|
|
63
63
|
* @param {Array.<Representation>} representations
|
|
64
64
|
* @param {Observable<Object>} observation$
|
|
65
65
|
* @param {Observable<Object>} streamEvents$
|
|
66
66
|
* @returns {Observable}
|
|
67
67
|
*/
|
|
68
|
-
get$(
|
|
68
|
+
get$(context: {
|
|
69
|
+
manifest: Manifest;
|
|
70
|
+
period: Period;
|
|
71
|
+
adaptation: Adaptation;
|
|
72
|
+
}, representations: Representation[], observation$: Observable<IABRManagerPlaybackObservation>, streamEvents$: Observable<IABRStreamEvents>): Observable<IABREstimate>;
|
|
69
73
|
/**
|
|
70
74
|
* @param {string} bufferType
|
|
71
75
|
* @returns {Object}
|
|
@@ -52,21 +52,22 @@ var ABRManager = /** @class */ (function () {
|
|
|
52
52
|
* Take type and an array of the available representations, spit out an
|
|
53
53
|
* observable emitting the best representation (given the network/buffer
|
|
54
54
|
* state).
|
|
55
|
-
* @param {
|
|
55
|
+
* @param {Object} context
|
|
56
56
|
* @param {Array.<Representation>} representations
|
|
57
57
|
* @param {Observable<Object>} observation$
|
|
58
58
|
* @param {Observable<Object>} streamEvents$
|
|
59
59
|
* @returns {Observable}
|
|
60
60
|
*/
|
|
61
|
-
ABRManager.prototype.get$ = function (
|
|
61
|
+
ABRManager.prototype.get$ = function (context, representations, observation$, streamEvents$) {
|
|
62
62
|
var _a, _b, _c;
|
|
63
|
+
var type = context.adaptation.type;
|
|
63
64
|
var bandwidthEstimator = this._getBandwidthEstimator(type);
|
|
64
65
|
var manualBitrate$ = takeFirstSet((_a = this._manualBitrates[type]) === null || _a === void 0 ? void 0 : _a.asObservable(), observableOf(-1));
|
|
65
66
|
var minAutoBitrate$ = takeFirstSet((_b = this._minAutoBitrates[type]) === null || _b === void 0 ? void 0 : _b.asObservable(), observableOf(0));
|
|
66
67
|
var maxAutoBitrate$ = takeFirstSet((_c = this._maxAutoBitrates[type]) === null || _c === void 0 ? void 0 : _c.asObservable(), observableOf(Infinity));
|
|
67
68
|
var initialBitrate = takeFirstSet(this._initialBitrates[type], 0);
|
|
68
69
|
var filters$ = createFilters(this._throttlers.limitWidth[type], this._throttlers.throttleBitrate[type], this._throttlers.throttle[type]);
|
|
69
|
-
return RepresentationEstimator({ bandwidthEstimator: bandwidthEstimator, streamEvents$: streamEvents$, observation$: observation$, filters$: filters$, initialBitrate: initialBitrate, manualBitrate$: manualBitrate$, minAutoBitrate$: minAutoBitrate$, maxAutoBitrate$: maxAutoBitrate$, representations: representations, lowLatencyMode: this._lowLatencyMode });
|
|
70
|
+
return RepresentationEstimator({ bandwidthEstimator: bandwidthEstimator, context: context, streamEvents$: streamEvents$, observation$: observation$, filters$: filters$, initialBitrate: initialBitrate, manualBitrate$: manualBitrate$, minAutoBitrate$: minAutoBitrate$, maxAutoBitrate$: maxAutoBitrate$, representations: representations, lowLatencyMode: this._lowLatencyMode });
|
|
70
71
|
};
|
|
71
72
|
/**
|
|
72
73
|
* @param {string} bufferType
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import { Observable } from "rxjs";
|
|
17
|
-
import { Adaptation, ISegment, Representation } from "../../manifest";
|
|
17
|
+
import Manifest, { Adaptation, ISegment, Period, Representation } from "../../manifest";
|
|
18
18
|
import BandwidthEstimator from "./bandwidth_estimator";
|
|
19
19
|
import { IPendingRequestStoreBegin, IPendingRequestStoreProgress } from "./pending_requests_store";
|
|
20
20
|
/**
|
|
@@ -100,12 +100,8 @@ export interface IRepresentationEstimatorPlaybackObservation {
|
|
|
100
100
|
speed: number;
|
|
101
101
|
/** `duration` property of the HTMLMediaElement on which the content plays. */
|
|
102
102
|
duration: number;
|
|
103
|
-
/**
|
|
104
|
-
|
|
105
|
-
* and the current position.
|
|
106
|
-
* `undefined` for non-live contents.
|
|
107
|
-
*/
|
|
108
|
-
liveGap: number | undefined;
|
|
103
|
+
/** Theoretical maximum position on the content that can currently be played. */
|
|
104
|
+
maximumPosition: number;
|
|
109
105
|
}
|
|
110
106
|
/** Content of the `IABRMetricsEvent` event's payload. */
|
|
111
107
|
export interface IABRMetricsEventValue {
|
|
@@ -304,6 +300,15 @@ export interface IRepresentationEstimatorArguments {
|
|
|
304
300
|
maxAutoBitrate$: Observable<number>;
|
|
305
301
|
/** The list of Representations the `RepresentationEstimator` can choose from. */
|
|
306
302
|
representations: Representation[];
|
|
303
|
+
/** Context for the list of Representations to choose. */
|
|
304
|
+
context: {
|
|
305
|
+
/** In which Manifest the Representations are. */
|
|
306
|
+
manifest: Manifest;
|
|
307
|
+
/** In which Period the Representations are. */
|
|
308
|
+
period: Period;
|
|
309
|
+
/** In which Adaptation the Representations are. */
|
|
310
|
+
adaptation: Adaptation;
|
|
311
|
+
};
|
|
307
312
|
}
|
|
308
313
|
/**
|
|
309
314
|
* Estimate regularly the current network bandwidth and the best Representation
|
|
@@ -317,4 +322,4 @@ export interface IRepresentationEstimatorArguments {
|
|
|
317
322
|
* @param {Object} args
|
|
318
323
|
* @returns {Observable}
|
|
319
324
|
*/
|
|
320
|
-
export default function RepresentationEstimator({ bandwidthEstimator, observation$, filters$, initialBitrate, lowLatencyMode, manualBitrate$, minAutoBitrate$, maxAutoBitrate$, representations, streamEvents$, }: IRepresentationEstimatorArguments): Observable<IABREstimate>;
|
|
325
|
+
export default function RepresentationEstimator({ bandwidthEstimator, context, observation$, filters$, initialBitrate, lowLatencyMode, manualBitrate$, minAutoBitrate$, maxAutoBitrate$, representations, streamEvents$, }: IRepresentationEstimatorArguments): Observable<IABREstimate>;
|
|
@@ -54,7 +54,7 @@ function getFilteredRepresentations(representations, filters) {
|
|
|
54
54
|
* @returns {Observable}
|
|
55
55
|
*/
|
|
56
56
|
export default function RepresentationEstimator(_a) {
|
|
57
|
-
var bandwidthEstimator = _a.bandwidthEstimator, observation$ = _a.observation$, filters$ = _a.filters$, initialBitrate = _a.initialBitrate, lowLatencyMode = _a.lowLatencyMode, manualBitrate$ = _a.manualBitrate$, minAutoBitrate$ = _a.minAutoBitrate$, maxAutoBitrate$ = _a.maxAutoBitrate$, representations = _a.representations, streamEvents$ = _a.streamEvents$;
|
|
57
|
+
var bandwidthEstimator = _a.bandwidthEstimator, context = _a.context, observation$ = _a.observation$, filters$ = _a.filters$, initialBitrate = _a.initialBitrate, lowLatencyMode = _a.lowLatencyMode, manualBitrate$ = _a.manualBitrate$, minAutoBitrate$ = _a.minAutoBitrate$, maxAutoBitrate$ = _a.maxAutoBitrate$, representations = _a.representations, streamEvents$ = _a.streamEvents$;
|
|
58
58
|
var scoreCalculator = new RepresentationScoreCalculator();
|
|
59
59
|
var networkAnalyzer = new NetworkAnalyzer(initialBitrate == null ? 0 :
|
|
60
60
|
initialBitrate, lowLatencyMode);
|
|
@@ -145,7 +145,7 @@ export default function RepresentationEstimator(_a) {
|
|
|
145
145
|
filters$,
|
|
146
146
|
bufferBasedEstimation$]).pipe(withLatestFrom(currentRepresentation$), map(function (_a) {
|
|
147
147
|
var _b = _a[0], observation = _b[0], minAutoBitrate = _b[1], maxAutoBitrate = _b[2], filters = _b[3], bufferBasedBitrate = _b[4], currentRepresentation = _a[1];
|
|
148
|
-
var bufferGap = observation.bufferGap,
|
|
148
|
+
var bufferGap = observation.bufferGap, position = observation.position, maximumPosition = observation.maximumPosition;
|
|
149
149
|
var filteredReps = getFilteredRepresentations(representations, filters);
|
|
150
150
|
var requests = requestsStore.getRequests();
|
|
151
151
|
var _c = networkAnalyzer
|
|
@@ -206,8 +206,8 @@ export default function RepresentationEstimator(_a) {
|
|
|
206
206
|
var chosenRepFromGuessMode = null;
|
|
207
207
|
if (lowLatencyMode &&
|
|
208
208
|
currentRepresentation !== null &&
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
context.manifest.isDynamic &&
|
|
210
|
+
maximumPosition - position < 40) {
|
|
211
211
|
chosenRepFromGuessMode = guessBasedChooser.getGuess(representations, observation, currentRepresentation, currentBestBitrate, requests);
|
|
212
212
|
}
|
|
213
213
|
if (chosenRepFromGuessMode !== null &&
|
|
@@ -87,7 +87,7 @@ var Player = /** @class */ (function (_super) {
|
|
|
87
87
|
// Workaround to support Firefox autoplay on FF 42.
|
|
88
88
|
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1194624
|
|
89
89
|
videoElement.preload = "auto";
|
|
90
|
-
_this.version = /* PLAYER_VERSION */ "3.27.1-dev.
|
|
90
|
+
_this.version = /* PLAYER_VERSION */ "3.27.1-dev.2022041500";
|
|
91
91
|
_this.log = log;
|
|
92
92
|
_this.state = "STOPPED";
|
|
93
93
|
_this.videoElement = videoElement;
|
|
@@ -1102,13 +1102,7 @@ var Player = /** @class */ (function (_super) {
|
|
|
1102
1102
|
if (positionWanted === undefined) {
|
|
1103
1103
|
throw new Error("invalid time given");
|
|
1104
1104
|
}
|
|
1105
|
-
|
|
1106
|
-
if (manifest !== null && !manifest.isLive) {
|
|
1107
|
-
var maximumTime = manifest.getMaximumPosition();
|
|
1108
|
-
seekAt = maximumTime !== undefined ? Math.min(positionWanted, maximumTime - 0.001) :
|
|
1109
|
-
positionWanted;
|
|
1110
|
-
}
|
|
1111
|
-
this.videoElement.currentTime = seekAt;
|
|
1105
|
+
this.videoElement.currentTime = positionWanted;
|
|
1112
1106
|
return positionWanted;
|
|
1113
1107
|
};
|
|
1114
1108
|
/**
|
|
@@ -1691,7 +1685,7 @@ var Player = /** @class */ (function (_super) {
|
|
|
1691
1685
|
}
|
|
1692
1686
|
var manifest = this._priv_contentInfos.manifest;
|
|
1693
1687
|
if (manifest !== null) {
|
|
1694
|
-
return manifest.
|
|
1688
|
+
return manifest.getMinimumSafePosition();
|
|
1695
1689
|
}
|
|
1696
1690
|
return null;
|
|
1697
1691
|
};
|
|
@@ -1711,7 +1705,10 @@ var Player = /** @class */ (function (_super) {
|
|
|
1711
1705
|
return this.videoElement.duration;
|
|
1712
1706
|
}
|
|
1713
1707
|
if (manifest !== null) {
|
|
1714
|
-
|
|
1708
|
+
if (!manifest.isDynamic && this.videoElement !== null) {
|
|
1709
|
+
return this.videoElement.duration;
|
|
1710
|
+
}
|
|
1711
|
+
return manifest.getMaximumSafePosition();
|
|
1715
1712
|
}
|
|
1716
1713
|
return null;
|
|
1717
1714
|
};
|
|
@@ -2236,7 +2233,7 @@ var Player = /** @class */ (function (_super) {
|
|
|
2236
2233
|
return;
|
|
2237
2234
|
}
|
|
2238
2235
|
this._priv_lastContentPlaybackInfos.lastPlaybackPosition = observation.position;
|
|
2239
|
-
var maximumPosition = manifest !== null ? manifest.
|
|
2236
|
+
var maximumPosition = manifest !== null ? manifest.getMaximumSafePosition() :
|
|
2240
2237
|
undefined;
|
|
2241
2238
|
var positionData = {
|
|
2242
2239
|
position: observation.position,
|
|
@@ -2248,12 +2245,14 @@ var Player = /** @class */ (function (_super) {
|
|
|
2248
2245
|
0,
|
|
2249
2246
|
};
|
|
2250
2247
|
if (manifest !== null &&
|
|
2251
|
-
maximumPosition !== undefined &&
|
|
2252
2248
|
manifest.isLive &&
|
|
2253
2249
|
observation.position > 0) {
|
|
2254
2250
|
var ast = (_a = manifest.availabilityStartTime) !== null && _a !== void 0 ? _a : 0;
|
|
2255
2251
|
positionData.wallClockTime = observation.position + ast;
|
|
2256
|
-
|
|
2252
|
+
var livePosition = manifest.getLivePosition();
|
|
2253
|
+
if (livePosition !== undefined) {
|
|
2254
|
+
positionData.liveGap = livePosition - observation.position;
|
|
2255
|
+
}
|
|
2257
2256
|
}
|
|
2258
2257
|
else if (isDirectFile && this.videoElement !== null) {
|
|
2259
2258
|
var startDate = getStartDate(this.videoElement);
|
|
@@ -2302,5 +2301,5 @@ var Player = /** @class */ (function (_super) {
|
|
|
2302
2301
|
};
|
|
2303
2302
|
return Player;
|
|
2304
2303
|
}(EventEmitter));
|
|
2305
|
-
Player.version = /* PLAYER_VERSION */ "3.27.1-dev.
|
|
2304
|
+
Player.version = /* PLAYER_VERSION */ "3.27.1-dev.2022041500";
|
|
2306
2305
|
export default Player;
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
import { IReadOnlyPlaybackObserver } from "../api";
|
|
19
|
+
import { IStreamOrchestratorEvent } from "../stream";
|
|
20
|
+
import { IWarningEvent } from "./types";
|
|
21
|
+
/**
|
|
22
|
+
* Observes the position and Adaptations being played and deduce various events
|
|
23
|
+
* related to the available time boundaries:
|
|
24
|
+
* - Emit when the theoretical duration of the content becomes known or when it
|
|
25
|
+
* changes.
|
|
26
|
+
* - Emit warnings when the duration goes out of what is currently
|
|
27
|
+
* theoretically playable.
|
|
28
|
+
*
|
|
29
|
+
* @param {Object} manifest
|
|
30
|
+
* @param {Observable} streams
|
|
31
|
+
* @param {Object} playbackObserver
|
|
32
|
+
* @returns {Observable}
|
|
33
|
+
*/
|
|
34
|
+
export default function ContentTimeBoundariesObserver(manifest: Manifest, streams: Observable<IStreamOrchestratorEvent>, playbackObserver: IReadOnlyPlaybackObserver<IContentTimeObserverPlaybackObservation>): Observable<IContentDurationUpdateEvent | IWarningEvent>;
|
|
35
|
+
/**
|
|
36
|
+
* Emitted when the duration of the full content (== the last playable position)
|
|
37
|
+
* has changed.
|
|
38
|
+
*/
|
|
39
|
+
export interface IContentDurationUpdateEvent {
|
|
40
|
+
type: "contentDurationUpdate";
|
|
41
|
+
/** The new theoretical duration, `undefined` if unknown, */
|
|
42
|
+
value: number | undefined;
|
|
43
|
+
}
|
|
44
|
+
export interface IContentTimeObserverPlaybackObservation {
|
|
45
|
+
/** The position we are in the video in seconds at the time of the observation. */
|
|
46
|
+
position: number;
|
|
47
|
+
/**
|
|
48
|
+
* Offset, in seconds to add to `position` to obtain the starting position at
|
|
49
|
+
* which we actually want to download segments for.
|
|
50
|
+
*/
|
|
51
|
+
wantedTimeOffset: number;
|
|
52
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
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 { distinctUntilChanged, ignoreElements, map, merge as observableMerge, skipWhile, startWith, tap, } from "rxjs";
|
|
17
|
+
import { MediaError } from "../../errors";
|
|
18
|
+
import { fromEvent } from "../../utils/event_emitter";
|
|
19
|
+
import filterMap from "../../utils/filter_map";
|
|
20
|
+
import createSharedReference from "../../utils/reference";
|
|
21
|
+
import EVENTS from "./events_generators";
|
|
22
|
+
// NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
|
|
23
|
+
// first type parameter as `any` instead of the perfectly fine `unknown`,
|
|
24
|
+
// leading to linter issues, as it forbids the usage of `any`.
|
|
25
|
+
// This is why we're disabling the eslint rule.
|
|
26
|
+
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
27
|
+
/**
|
|
28
|
+
* Observes the position and Adaptations being played and deduce various events
|
|
29
|
+
* related to the available time boundaries:
|
|
30
|
+
* - Emit when the theoretical duration of the content becomes known or when it
|
|
31
|
+
* changes.
|
|
32
|
+
* - Emit warnings when the duration goes out of what is currently
|
|
33
|
+
* theoretically playable.
|
|
34
|
+
*
|
|
35
|
+
* @param {Object} manifest
|
|
36
|
+
* @param {Observable} streams
|
|
37
|
+
* @param {Object} playbackObserver
|
|
38
|
+
* @returns {Observable}
|
|
39
|
+
*/
|
|
40
|
+
export default function ContentTimeBoundariesObserver(manifest, streams, playbackObserver) {
|
|
41
|
+
/**
|
|
42
|
+
* Allows to calculate the minimum and maximum playable position on the
|
|
43
|
+
* whole content.
|
|
44
|
+
*/
|
|
45
|
+
var maximumPositionCalculator = new MaximumPositionCalculator(manifest);
|
|
46
|
+
// trigger warnings when the wanted time is before or after the manifest's
|
|
47
|
+
// segments
|
|
48
|
+
var outOfManifest$ = playbackObserver.observe(true).pipe(filterMap(function (_a) {
|
|
49
|
+
var position = _a.position, wantedTimeOffset = _a.wantedTimeOffset;
|
|
50
|
+
var offsetedPosition = wantedTimeOffset + position;
|
|
51
|
+
if (offsetedPosition < manifest.getMinimumSafePosition()) {
|
|
52
|
+
var warning = new MediaError("MEDIA_TIME_BEFORE_MANIFEST", "The current position is behind the " +
|
|
53
|
+
"earliest time announced in the Manifest.");
|
|
54
|
+
return EVENTS.warning(warning);
|
|
55
|
+
}
|
|
56
|
+
else if (offsetedPosition > maximumPositionCalculator.getCurrentMaximumPosition()) {
|
|
57
|
+
var warning = new MediaError("MEDIA_TIME_AFTER_MANIFEST", "The current position is after the latest " +
|
|
58
|
+
"time announced in the Manifest.");
|
|
59
|
+
return EVENTS.warning(warning);
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}, null));
|
|
63
|
+
/**
|
|
64
|
+
* Contains the content duration according to the last audio and video
|
|
65
|
+
* Adaptation chosen for the last Period.
|
|
66
|
+
* `undefined` if unknown yet.
|
|
67
|
+
*/
|
|
68
|
+
var contentDuration = createSharedReference(undefined);
|
|
69
|
+
var updateDurationOnManifestUpdate$ = fromEvent(manifest, "manifestUpdate").pipe(startWith(null), tap(function () {
|
|
70
|
+
if (!manifest.isDynamic) {
|
|
71
|
+
var maxPos = maximumPositionCalculator.getCurrentMaximumPosition();
|
|
72
|
+
contentDuration.setValue(maxPos);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// TODO handle finished dynamic contents?
|
|
76
|
+
contentDuration.setValue(undefined);
|
|
77
|
+
}
|
|
78
|
+
}), ignoreElements());
|
|
79
|
+
var updateDurationAndTimeBoundsOnTrackChange$ = streams.pipe(tap(function (message) {
|
|
80
|
+
if (message.type === "adaptationChange") {
|
|
81
|
+
var lastPeriod = manifest.periods[manifest.periods.length - 1];
|
|
82
|
+
if (message.value.period.id === (lastPeriod === null || lastPeriod === void 0 ? void 0 : lastPeriod.id)) {
|
|
83
|
+
if (message.value.type === "audio") {
|
|
84
|
+
maximumPositionCalculator
|
|
85
|
+
.updateLastAudioAdaptation(message.value.adaptation);
|
|
86
|
+
if (!manifest.isDynamic) {
|
|
87
|
+
contentDuration.setValue(maximumPositionCalculator.getCurrentMaximumPosition());
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (message.value.type === "video") {
|
|
91
|
+
maximumPositionCalculator
|
|
92
|
+
.updateLastVideoAdaptation(message.value.adaptation);
|
|
93
|
+
if (!manifest.isDynamic) {
|
|
94
|
+
contentDuration.setValue(maximumPositionCalculator.getCurrentMaximumPosition());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}), ignoreElements());
|
|
100
|
+
return observableMerge(updateDurationOnManifestUpdate$, updateDurationAndTimeBoundsOnTrackChange$, outOfManifest$, contentDuration.asObservable().pipe(skipWhile(function (val) { return val === undefined; }), distinctUntilChanged(), map(function (value) { return ({ type: "contentDurationUpdate", value: value }); })));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Calculate the last position from the last chosen audio and video Adaptations
|
|
104
|
+
* for the last Period (or a default one, if no Adaptations has been chosen).
|
|
105
|
+
* @class MaximumPositionCalculator
|
|
106
|
+
*/
|
|
107
|
+
var MaximumPositionCalculator = /** @class */ (function () {
|
|
108
|
+
/**
|
|
109
|
+
* @param {Object} manifest
|
|
110
|
+
*/
|
|
111
|
+
function MaximumPositionCalculator(manifest) {
|
|
112
|
+
this._manifest = manifest;
|
|
113
|
+
this._lastAudioAdaptation = undefined;
|
|
114
|
+
this._lastVideoAdaptation = undefined;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Update the last known audio Adaptation for the last Period.
|
|
118
|
+
* If no Adaptation has been set, it should be set to `null`.
|
|
119
|
+
*
|
|
120
|
+
* Allows to calculate the maximum position more precizely in
|
|
121
|
+
* `getCurrentMaximumPosition`.
|
|
122
|
+
* @param {Object|null} adaptation
|
|
123
|
+
*/
|
|
124
|
+
MaximumPositionCalculator.prototype.updateLastAudioAdaptation = function (adaptation) {
|
|
125
|
+
this._lastAudioAdaptation = adaptation;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Update the last known video Adaptation for the last Period.
|
|
129
|
+
* If no Adaptation has been set, it should be set to `null`.
|
|
130
|
+
*
|
|
131
|
+
* Allows to calculate the maximum position more precizely in
|
|
132
|
+
* `getCurrentMaximumPosition`.
|
|
133
|
+
* @param {Object|null} adaptation
|
|
134
|
+
*/
|
|
135
|
+
MaximumPositionCalculator.prototype.updateLastVideoAdaptation = function (adaptation) {
|
|
136
|
+
this._lastVideoAdaptation = adaptation;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Returns an estimate of the maximum position reachable under the current
|
|
140
|
+
* circumstances.
|
|
141
|
+
* @returns {number}
|
|
142
|
+
*/
|
|
143
|
+
MaximumPositionCalculator.prototype.getCurrentMaximumPosition = function () {
|
|
144
|
+
var _a;
|
|
145
|
+
if (this._manifest.isDynamic) {
|
|
146
|
+
return (_a = this._manifest.getLivePosition()) !== null && _a !== void 0 ? _a : this._manifest.getMaximumSafePosition();
|
|
147
|
+
}
|
|
148
|
+
if (this._lastVideoAdaptation === undefined ||
|
|
149
|
+
this._lastAudioAdaptation === undefined) {
|
|
150
|
+
return this._manifest.getMaximumSafePosition();
|
|
151
|
+
}
|
|
152
|
+
else if (this._lastAudioAdaptation === null) {
|
|
153
|
+
if (this._lastVideoAdaptation === null) {
|
|
154
|
+
return this._manifest.getMaximumSafePosition();
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
var lastVideoPosition = getLastPositionFromAdaptation(this._lastVideoAdaptation);
|
|
158
|
+
if (typeof lastVideoPosition !== "number") {
|
|
159
|
+
return this._manifest.getMaximumSafePosition();
|
|
160
|
+
}
|
|
161
|
+
return lastVideoPosition;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else if (this._lastVideoAdaptation === null) {
|
|
165
|
+
var lastAudioPosition = getLastPositionFromAdaptation(this._lastAudioAdaptation);
|
|
166
|
+
if (typeof lastAudioPosition !== "number") {
|
|
167
|
+
return this._manifest.getMaximumSafePosition();
|
|
168
|
+
}
|
|
169
|
+
return lastAudioPosition;
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
var lastAudioPosition = getLastPositionFromAdaptation(this._lastAudioAdaptation);
|
|
173
|
+
var lastVideoPosition = getLastPositionFromAdaptation(this._lastVideoAdaptation);
|
|
174
|
+
if (typeof lastAudioPosition !== "number" ||
|
|
175
|
+
typeof lastVideoPosition !== "number") {
|
|
176
|
+
return this._manifest.getMaximumSafePosition();
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
return Math.min(lastAudioPosition, lastVideoPosition);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
return MaximumPositionCalculator;
|
|
184
|
+
}());
|
|
185
|
+
/**
|
|
186
|
+
* Returns "last time of reference" from the adaptation given.
|
|
187
|
+
* `undefined` if a time could not be found.
|
|
188
|
+
* Null if the Adaptation has no segments (it could be that it didn't started or
|
|
189
|
+
* that it already finished for example).
|
|
190
|
+
*
|
|
191
|
+
* We consider the earliest last time from every representations in the given
|
|
192
|
+
* adaptation.
|
|
193
|
+
* @param {Object} adaptation
|
|
194
|
+
* @returns {Number|undefined|null}
|
|
195
|
+
*/
|
|
196
|
+
function getLastPositionFromAdaptation(adaptation) {
|
|
197
|
+
var representations = adaptation.representations;
|
|
198
|
+
var min = null;
|
|
199
|
+
/**
|
|
200
|
+
* Some Manifest parsers use the exact same `IRepresentationIndex` reference
|
|
201
|
+
* for each Representation of a given Adaptation, because in the actual source
|
|
202
|
+
* Manifest file, indexing data is often defined at Adaptation-level.
|
|
203
|
+
* This variable allows to optimize the logic here when this is the case.
|
|
204
|
+
*/
|
|
205
|
+
var lastIndex;
|
|
206
|
+
for (var i = 0; i < representations.length; i++) {
|
|
207
|
+
if (representations[i].index !== lastIndex) {
|
|
208
|
+
lastIndex = representations[i].index;
|
|
209
|
+
var lastPosition = representations[i].index.getLastPosition();
|
|
210
|
+
if (lastPosition === undefined) { // we cannot tell
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
if (lastPosition !== null) {
|
|
214
|
+
min = min == null ? lastPosition :
|
|
215
|
+
Math.min(min, lastPosition);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if (min === null) { // It means that all positions were null === no segments (yet?)
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return min;
|
|
223
|
+
}
|
|
@@ -25,10 +25,23 @@ export default function createStreamPlaybackObserver(manifest, playbackObserver,
|
|
|
25
25
|
return playbackObserver.deriveReadOnlyObserver(function mapObservable(observation$) {
|
|
26
26
|
return observableCombineLatest([observation$, speed.asObservable()]).pipe(map(function (_a) {
|
|
27
27
|
var observation = _a[0], lastSpeed = _a[1];
|
|
28
|
+
var wantedTimeOffset = 0;
|
|
29
|
+
if (!initialSeekPerformed.getValue()) {
|
|
30
|
+
wantedTimeOffset = startTime - observation.position;
|
|
31
|
+
}
|
|
32
|
+
else if (!manifest.isDynamic || manifest.isLastPeriodKnown) {
|
|
33
|
+
var lastPeriod = manifest.periods[manifest.periods.length - 1];
|
|
34
|
+
if (lastPeriod !== undefined &&
|
|
35
|
+
lastPeriod.end !== undefined &&
|
|
36
|
+
observation.position > lastPeriod.end) {
|
|
37
|
+
wantedTimeOffset = lastPeriod.end -
|
|
38
|
+
observation.position -
|
|
39
|
+
1;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
28
42
|
return {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
undefined,
|
|
43
|
+
// TODO more exact according to the current Adaptation chosen?
|
|
44
|
+
maximumPosition: manifest.getMaximumSafePosition(),
|
|
32
45
|
position: observation.position,
|
|
33
46
|
duration: observation.duration,
|
|
34
47
|
isPaused: initialPlayPerformed.getValue() ? observation.paused :
|
|
@@ -41,9 +54,7 @@ export default function createStreamPlaybackObserver(manifest, playbackObserver,
|
|
|
41
54
|
// initial position, the currentTime will most probably be 0 where the
|
|
42
55
|
// effective starting position will be _startTime_.
|
|
43
56
|
// Thus we initially set a wantedTimeOffset equal to startTime.
|
|
44
|
-
wantedTimeOffset:
|
|
45
|
-
0 :
|
|
46
|
-
startTime - observation.position,
|
|
57
|
+
wantedTimeOffset: wantedTimeOffset,
|
|
47
58
|
};
|
|
48
59
|
}));
|
|
49
60
|
});
|
|
@@ -14,12 +14,37 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import Manifest from "../../manifest";
|
|
17
|
+
/**
|
|
18
|
+
* All possible initial time options that can be set.
|
|
19
|
+
*
|
|
20
|
+
* Written this way (with many type possibilities) on purpose to avoid issues
|
|
21
|
+
* if the application using the RxPlayer gives undocumented values such as
|
|
22
|
+
* `null`.
|
|
23
|
+
*
|
|
24
|
+
* TODO we shouldn't be that robust, at least not outside the API code, because
|
|
25
|
+
* if we begin to be that liberal here, where should we end?
|
|
26
|
+
* We may instead either want to progressively remove tolerance on the wrong
|
|
27
|
+
* type being given or be better to enforce type-compliancy in the API code.
|
|
28
|
+
*/
|
|
17
29
|
export interface IInitialTimeOptions {
|
|
18
|
-
position
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
/** If set, we should begin at this position, in seconds. */
|
|
31
|
+
position?: number | null | undefined;
|
|
32
|
+
/** If set, we should begin at this unix timestamp, in seconds. */
|
|
33
|
+
wallClockTime?: number | null | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* If set, we should begin at this position relative to the content's start,
|
|
36
|
+
* in seconds.
|
|
37
|
+
*/
|
|
38
|
+
fromFirstPosition?: number | null | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* If set, we should begin at this position relative to the content's end,
|
|
41
|
+
* in seconds.
|
|
42
|
+
*/
|
|
43
|
+
fromLastPosition?: number | null | undefined;
|
|
44
|
+
/** If set, we should begin at this position relative to the whole duration of
|
|
45
|
+
* the content, in percentage.
|
|
46
|
+
*/
|
|
47
|
+
percentage?: number | null | undefined;
|
|
23
48
|
}
|
|
24
49
|
/**
|
|
25
50
|
* Returns the calculated initial time for the content described by the given
|