rx-player 3.27.0-dev.2022032800 → 3.27.1-dev.2022040500
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 +10 -2
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/get_start_date.d.ts +30 -0
- package/dist/_esm5.processed/compat/get_start_date.js +44 -0
- package/dist/_esm5.processed/compat/index.d.ts +2 -1
- package/dist/_esm5.processed/compat/index.js +2 -1
- package/dist/_esm5.processed/config.d.ts +1 -2
- package/dist/_esm5.processed/core/api/public_api.js +25 -25
- package/dist/_esm5.processed/core/segment_buffers/garbage_collector.js +4 -1
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +2 -1
- package/dist/_esm5.processed/core/stream/period/period_stream.js +15 -7
- package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.js +3 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +9 -3
- package/dist/_esm5.processed/core/stream/representation/get_needed_segments.d.ts +11 -1
- package/dist/_esm5.processed/core/stream/representation/get_needed_segments.js +27 -45
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +6 -4
- package/dist/_esm5.processed/default_config.d.ts +2 -8
- package/dist/_esm5.processed/default_config.js +2 -8
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +39 -38
- package/dist/_esm5.processed/utils/reference.js +0 -2
- package/dist/_esm5.processed/utils/task_canceller.d.ts +8 -1
- package/dist/_esm5.processed/utils/task_canceller.js +9 -1
- package/dist/rx-player.js +202 -148
- package/dist/rx-player.min.js +1 -1
- package/package.json +1 -1
- package/sonar-project.properties +1 -1
- package/src/compat/get_start_date.ts +48 -0
- package/src/compat/index.ts +2 -0
- package/src/core/api/public_api.ts +23 -27
- package/src/core/segment_buffers/garbage_collector.ts +4 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +2 -1
- package/src/core/stream/period/period_stream.ts +19 -8
- package/src/core/stream/representation/force_garbage_collection.ts +4 -1
- package/src/core/stream/representation/get_buffer_status.ts +21 -13
- package/src/core/stream/representation/get_needed_segments.ts +40 -55
- package/src/core/stream/representation/representation_stream.ts +6 -4
- package/src/default_config.ts +20 -27
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +41 -44
- package/src/utils/reference.ts +0 -2
- package/src/utils/task_canceller.ts +16 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v3.27.
|
|
3
|
+
## v3.27.1-dev.2022040500 (2022-04-05)
|
|
4
|
+
|
|
5
|
+
### Bug fixes
|
|
6
|
+
|
|
7
|
+
- When creating SourceBuffers, use the first _supported_ codec of the chosen `Adaptation`, not the lowest-bitrate one [#1094]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## v3.27.0 (2022-03-31)
|
|
4
11
|
|
|
5
12
|
### Features
|
|
6
13
|
|
|
@@ -16,6 +23,7 @@
|
|
|
16
23
|
- avoid performing a small seek when changing the audio track [#1080]
|
|
17
24
|
- api: allow switching to RELOADING state synchronously after LOADED [#1083]
|
|
18
25
|
- Safari Mobile: Improve decryption support on Safari mobile by relying on the vendored `WebKitMediaKeys` API [#1072]
|
|
26
|
+
- DASH: Fix issue which prevented the integrity check of most MP4 DASH segments when `transportOptions.checkMediaSegmentIntegrity` was set to `true`
|
|
19
27
|
- avoid unnecessary warning logs when loading some initialization segments [#1049]
|
|
20
28
|
- TypeScript: Add forgotten TypeScript types in the exposed segment and manifest loader APIs [#1057]
|
|
21
29
|
- DRM: Avoid decryption issues when a license is persisted in a `singleLicensePer` `"init-data"` mode but loaded in a `"content"` mode [#1031, #1042]
|
|
@@ -26,7 +34,7 @@
|
|
|
26
34
|
- DASH: always consider that the non-last Period is finished when it contains SegmentTimeline elements [#1047]
|
|
27
35
|
- add better buffer cleaning logic on a browser's `QuotaExceededError` to better handle memory limitations [#1065]
|
|
28
36
|
- DASH: Prioritize selectionPriority attribute over a "main" Role when ordering AdaptationSets [#1082]
|
|
29
|
-
- directfile/Safari: use the `getStartDate` method in `getWallClockTime` when available to obtain true offseted times when playing HLS contents on Safari [#1055]
|
|
37
|
+
- directfile/Safari: use the `getStartDate` method in `getWallClockTime`, `seekTo` and the `positionUpdate` event when available to obtain true offseted "wall-clock" times when playing HLS contents on Safari [#1055]
|
|
30
38
|
- DRM: Improve DRM Session caches performance when `singleLicensePer` is set to `"content"`
|
|
31
39
|
- DRM: Stop retrying closing MediaKeySessions multiple times when it fails, instead doing it only once when it should work [#1093]
|
|
32
40
|
- TypeScript: Add IBitrateEstimate, IPositionUpdate and IPlayerState types to the exported types [#1084]
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
3.27.
|
|
1
|
+
3.27.1-dev.2022040500
|
|
@@ -0,0 +1,30 @@
|
|
|
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
|
+
* Calculating a live-offseted media position necessitate to obtain first an
|
|
18
|
+
* offset, and then adding that offset to the wanted position.
|
|
19
|
+
*
|
|
20
|
+
* That offset is in most case present inside the Manifest file, yet in cases
|
|
21
|
+
* without it or without a Manifest, such as the "directfile" mode, the RxPlayer
|
|
22
|
+
* won't know that offset.
|
|
23
|
+
*
|
|
24
|
+
* Thankfully Safari declares a `getStartDate` method allowing to obtain that
|
|
25
|
+
* offset when available. This logic is mainly useful when playing HLS contents
|
|
26
|
+
* in directfile mode on Safari.
|
|
27
|
+
* @param {HTMLMediaElement} mediaElement
|
|
28
|
+
* @returns {number|undefined}
|
|
29
|
+
*/
|
|
30
|
+
export default function getStartDate(mediaElement: HTMLMediaElement): number | undefined;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
+
* Calculating a live-offseted media position necessitate to obtain first an
|
|
18
|
+
* offset, and then adding that offset to the wanted position.
|
|
19
|
+
*
|
|
20
|
+
* That offset is in most case present inside the Manifest file, yet in cases
|
|
21
|
+
* without it or without a Manifest, such as the "directfile" mode, the RxPlayer
|
|
22
|
+
* won't know that offset.
|
|
23
|
+
*
|
|
24
|
+
* Thankfully Safari declares a `getStartDate` method allowing to obtain that
|
|
25
|
+
* offset when available. This logic is mainly useful when playing HLS contents
|
|
26
|
+
* in directfile mode on Safari.
|
|
27
|
+
* @param {HTMLMediaElement} mediaElement
|
|
28
|
+
* @returns {number|undefined}
|
|
29
|
+
*/
|
|
30
|
+
export default function getStartDate(mediaElement) {
|
|
31
|
+
var _mediaElement = mediaElement;
|
|
32
|
+
if (typeof _mediaElement.getStartDate === "function") {
|
|
33
|
+
var startDate = _mediaElement.getStartDate();
|
|
34
|
+
if (typeof startDate === "object" && startDate !== null) {
|
|
35
|
+
var startDateNum = +startDate;
|
|
36
|
+
if (!isNaN(startDateNum)) {
|
|
37
|
+
return startDateNum / 1000;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (typeof startDate === "number" && !isNaN(startDate)) {
|
|
41
|
+
return startDate;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -22,6 +22,7 @@ import clearElementSrc from "./clear_element_src";
|
|
|
22
22
|
import { closeSession, CustomMediaKeySystemAccess, generateKeyRequest, getInitData, ICustomMediaKeys, ICustomMediaKeySession, ICustomMediaKeySystemAccess, loadSession, requestMediaKeySystemAccess, setMediaKeys } from "./eme";
|
|
23
23
|
import * as events from "./event_listeners";
|
|
24
24
|
import { exitFullscreen, isFullscreen, requestFullscreen } from "./fullscreen";
|
|
25
|
+
import getStartDate from "./get_start_date";
|
|
25
26
|
import hasEMEAPIs from "./has_eme_apis";
|
|
26
27
|
import isCodecSupported from "./is_codec_supported";
|
|
27
28
|
import isNode from "./is_node";
|
|
@@ -38,4 +39,4 @@ import shouldValidateMetadata from "./should_validate_metadata";
|
|
|
38
39
|
import shouldWaitForDataBeforeLoaded from "./should_wait_for_data_before_loaded";
|
|
39
40
|
import whenLoadedMetadata$ from "./when_loaded_metadata";
|
|
40
41
|
import whenMediaSourceOpen$ from "./when_media_source_open";
|
|
41
|
-
export { addClassName, addTextTrack, canPatchISOBMFFSegment, clearElementSrc, closeSession, CustomMediaKeySystemAccess, events, exitFullscreen, generateKeyRequest, getInitData, hasEMEAPIs, ICompatTextTrack, ICompatVTTCue, ICustomMediaKeySession, ICustomMediaKeySystemAccess, ICustomMediaKeys, ICompatSourceBuffer, isCodecSupported, isFullscreen, isNode, isOffline, isVTTCue, loadSession, makeVTTCue, MediaSource_, onHeightWidthChange, play, requestFullscreen, requestMediaKeySystemAccess, setElementSrc$, setMediaKeys, shouldReloadMediaSourceOnDecipherabilityUpdate, shouldRenewMediaKeys, shouldUnsetMediaKeys, shouldValidateMetadata, shouldWaitForDataBeforeLoaded, tryToChangeSourceBufferType, whenLoadedMetadata$, whenMediaSourceOpen$, };
|
|
42
|
+
export { addClassName, addTextTrack, canPatchISOBMFFSegment, clearElementSrc, closeSession, CustomMediaKeySystemAccess, events, exitFullscreen, generateKeyRequest, getInitData, getStartDate, hasEMEAPIs, ICompatTextTrack, ICompatVTTCue, ICustomMediaKeySession, ICustomMediaKeySystemAccess, ICustomMediaKeys, ICompatSourceBuffer, isCodecSupported, isFullscreen, isNode, isOffline, isVTTCue, loadSession, makeVTTCue, MediaSource_, onHeightWidthChange, play, requestFullscreen, requestMediaKeySystemAccess, setElementSrc$, setMediaKeys, shouldReloadMediaSourceOnDecipherabilityUpdate, shouldRenewMediaKeys, shouldUnsetMediaKeys, shouldValidateMetadata, shouldWaitForDataBeforeLoaded, tryToChangeSourceBufferType, whenLoadedMetadata$, whenMediaSourceOpen$, };
|
|
@@ -22,6 +22,7 @@ import clearElementSrc from "./clear_element_src";
|
|
|
22
22
|
import { closeSession, CustomMediaKeySystemAccess, generateKeyRequest, getInitData, loadSession, requestMediaKeySystemAccess, setMediaKeys, } from "./eme";
|
|
23
23
|
import * as events from "./event_listeners";
|
|
24
24
|
import { exitFullscreen, isFullscreen, requestFullscreen, } from "./fullscreen";
|
|
25
|
+
import getStartDate from "./get_start_date";
|
|
25
26
|
import hasEMEAPIs from "./has_eme_apis";
|
|
26
27
|
import isCodecSupported from "./is_codec_supported";
|
|
27
28
|
import isNode from "./is_node";
|
|
@@ -44,4 +45,4 @@ import whenMediaSourceOpen$ from "./when_media_source_open";
|
|
|
44
45
|
// we would prefer to disallow (both for the understandability of the code and
|
|
45
46
|
// to better exploit tree shaking.
|
|
46
47
|
patchWebkitSourceBuffer();
|
|
47
|
-
export { addClassName, addTextTrack, canPatchISOBMFFSegment, clearElementSrc, closeSession, CustomMediaKeySystemAccess, events, exitFullscreen, generateKeyRequest, getInitData, hasEMEAPIs, isCodecSupported, isFullscreen, isNode, isOffline, isVTTCue, loadSession, makeVTTCue, MediaSource_, onHeightWidthChange, play, requestFullscreen, requestMediaKeySystemAccess, setElementSrc$, setMediaKeys, shouldReloadMediaSourceOnDecipherabilityUpdate, shouldRenewMediaKeys, shouldUnsetMediaKeys, shouldValidateMetadata, shouldWaitForDataBeforeLoaded, tryToChangeSourceBufferType, whenLoadedMetadata$, whenMediaSourceOpen$, };
|
|
48
|
+
export { addClassName, addTextTrack, canPatchISOBMFFSegment, clearElementSrc, closeSession, CustomMediaKeySystemAccess, events, exitFullscreen, generateKeyRequest, getInitData, getStartDate, hasEMEAPIs, isCodecSupported, isFullscreen, isNode, isOffline, isVTTCue, loadSession, makeVTTCue, MediaSource_, onHeightWidthChange, play, requestFullscreen, requestMediaKeySystemAccess, setElementSrc$, setMediaKeys, shouldReloadMediaSourceOnDecipherabilityUpdate, shouldRenewMediaKeys, shouldUnsetMediaKeys, shouldValidateMetadata, shouldWaitForDataBeforeLoaded, tryToChangeSourceBufferType, whenLoadedMetadata$, whenMediaSourceOpen$, };
|
|
@@ -175,8 +175,7 @@ declare class ConfigHandler {
|
|
|
175
175
|
DEFAULT_MAXIMUM_TIME_ROUNDING_ERROR: number;
|
|
176
176
|
BUFFERED_HISTORY_RETENTION_TIME: number;
|
|
177
177
|
BUFFERED_HISTORY_MAXIMUM_ENTRIES: number;
|
|
178
|
-
|
|
179
|
-
MIN_BUFFER_DISTANCE_BEFORE_CLEAN_UP: number;
|
|
178
|
+
MIN_BUFFER_AHEAD: number;
|
|
180
179
|
UPTO_CURRENT_POSITION_CLEANUP: number;
|
|
181
180
|
};
|
|
182
181
|
update(config: Partial<IDefaultConfig>): void;
|
|
@@ -44,7 +44,7 @@ var __assign = (this && this.__assign) || function () {
|
|
|
44
44
|
* It also starts the different sub-parts of the player on various API calls.
|
|
45
45
|
*/
|
|
46
46
|
import { combineLatest as observableCombineLatest, concat as observableConcat, connectable, distinctUntilChanged, EMPTY, filter, map, merge as observableMerge, mergeMap, of as observableOf, ReplaySubject, share, shareReplay, skipWhile, startWith, Subject, switchMap, take, takeUntil, } from "rxjs";
|
|
47
|
-
import { events, exitFullscreen, isFullscreen, requestFullscreen, } from "../../compat";
|
|
47
|
+
import { events, exitFullscreen, getStartDate, isFullscreen, requestFullscreen, } from "../../compat";
|
|
48
48
|
/* eslint-disable-next-line max-len */
|
|
49
49
|
import canRelyOnVideoVisibilityAndSize from "../../compat/can_rely_on_video_visibility_and_size";
|
|
50
50
|
import config from "../../config";
|
|
@@ -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.
|
|
90
|
+
_this.version = /* PLAYER_VERSION */ "3.27.1-dev.2022040500";
|
|
91
91
|
_this.log = log;
|
|
92
92
|
_this.state = "STOPPED";
|
|
93
93
|
_this.videoElement = videoElement;
|
|
@@ -805,22 +805,8 @@ var Player = /** @class */ (function (_super) {
|
|
|
805
805
|
}
|
|
806
806
|
var _a = this._priv_contentInfos, isDirectFile = _a.isDirectFile, manifest = _a.manifest;
|
|
807
807
|
if (isDirectFile) {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
// That offset is in most case present inside the Manifest file, yet in
|
|
811
|
-
// directfile mode, the RxPlayer won't parse that file, the browser does it.
|
|
812
|
-
//
|
|
813
|
-
// Thankfully Safari declares a `getStartDate` method allowing to obtain
|
|
814
|
-
// that offset when available. This logic is thus mainly useful when
|
|
815
|
-
// playing HLS contents in directfile mode on Safari.
|
|
816
|
-
var mediaElement = this.videoElement;
|
|
817
|
-
if (typeof mediaElement.getStartDate === "function") {
|
|
818
|
-
var startDate = mediaElement.getStartDate();
|
|
819
|
-
if (typeof startDate === "number" && !isNaN(startDate)) {
|
|
820
|
-
return startDate + mediaElement.currentTime;
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
return mediaElement.currentTime;
|
|
808
|
+
var startDate = getStartDate(this.videoElement);
|
|
809
|
+
return (startDate !== null && startDate !== void 0 ? startDate : 0) + this.videoElement.currentTime;
|
|
824
810
|
}
|
|
825
811
|
if (manifest !== null) {
|
|
826
812
|
var currentTime = this.videoElement.currentTime;
|
|
@@ -1069,13 +1055,14 @@ var Player = /** @class */ (function (_super) {
|
|
|
1069
1055
|
* @returns {Number} - The time the player has seek to
|
|
1070
1056
|
*/
|
|
1071
1057
|
Player.prototype.seekTo = function (time) {
|
|
1058
|
+
var _a;
|
|
1072
1059
|
if (this.videoElement === null) {
|
|
1073
1060
|
throw new Error("Disposed player");
|
|
1074
1061
|
}
|
|
1075
1062
|
if (this._priv_contentInfos === null) {
|
|
1076
1063
|
throw new Error("player: no content loaded");
|
|
1077
1064
|
}
|
|
1078
|
-
var
|
|
1065
|
+
var _b = this._priv_contentInfos, isDirectFile = _b.isDirectFile, manifest = _b.manifest;
|
|
1079
1066
|
if (!isDirectFile && manifest === null) {
|
|
1080
1067
|
throw new Error("player: the content did not load yet");
|
|
1081
1068
|
}
|
|
@@ -1093,11 +1080,18 @@ var Player = /** @class */ (function (_super) {
|
|
|
1093
1080
|
positionWanted = timeObj.position;
|
|
1094
1081
|
}
|
|
1095
1082
|
else if (!isNullOrUndefined(timeObj.wallClockTime)) {
|
|
1096
|
-
|
|
1097
|
-
timeObj.wallClockTime :
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1083
|
+
if (manifest !== null) {
|
|
1084
|
+
positionWanted = timeObj.wallClockTime - ((_a = manifest.availabilityStartTime) !== null && _a !== void 0 ? _a : 0);
|
|
1085
|
+
}
|
|
1086
|
+
else if (isDirectFile && this.videoElement !== null) {
|
|
1087
|
+
var startDate = getStartDate(this.videoElement);
|
|
1088
|
+
if (startDate !== undefined) {
|
|
1089
|
+
positionWanted = timeObj.wallClockTime - startDate;
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
if (positionWanted === undefined) {
|
|
1093
|
+
positionWanted = timeObj.wallClockTime;
|
|
1094
|
+
}
|
|
1101
1095
|
}
|
|
1102
1096
|
else {
|
|
1103
1097
|
throw new Error("invalid time object. You must set one of the " +
|
|
@@ -2261,6 +2255,12 @@ var Player = /** @class */ (function (_super) {
|
|
|
2261
2255
|
positionData.wallClockTime = observation.position + ast;
|
|
2262
2256
|
positionData.liveGap = maximumPosition - observation.position;
|
|
2263
2257
|
}
|
|
2258
|
+
else if (isDirectFile && this.videoElement !== null) {
|
|
2259
|
+
var startDate = getStartDate(this.videoElement);
|
|
2260
|
+
if (startDate !== undefined) {
|
|
2261
|
+
positionData.wallClockTime = startDate + observation.position;
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
2264
|
this.trigger("positionUpdate", positionData);
|
|
2265
2265
|
};
|
|
2266
2266
|
/**
|
|
@@ -2302,5 +2302,5 @@ var Player = /** @class */ (function (_super) {
|
|
|
2302
2302
|
};
|
|
2303
2303
|
return Player;
|
|
2304
2304
|
}(EventEmitter));
|
|
2305
|
-
Player.version = /* PLAYER_VERSION */ "3.27.
|
|
2305
|
+
Player.version = /* PLAYER_VERSION */ "3.27.1-dev.2022040500";
|
|
2306
2306
|
export default Player;
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { combineLatest as observableCombineLatest, concatAll, EMPTY, from as observableFrom, ignoreElements, mergeMap, } from "rxjs";
|
|
16
|
+
import { combineLatest as observableCombineLatest, concatAll, EMPTY, from as observableFrom, ignoreElements, mergeMap, of as observableOf, } from "rxjs";
|
|
17
17
|
import log from "../../log";
|
|
18
18
|
import { getInnerAndOuterTimeRanges } from "../../utils/ranges";
|
|
19
19
|
/**
|
|
@@ -105,6 +105,9 @@ function clearBuffer(segmentBuffer, position, maxBufferBehind, maxBufferAhead) {
|
|
|
105
105
|
collectBufferAhead();
|
|
106
106
|
var clean$ = observableFrom(cleanedupRanges.map(function (range) {
|
|
107
107
|
log.debug("GC: cleaning range from SegmentBuffer", range);
|
|
108
|
+
if (range.start >= range.end) {
|
|
109
|
+
return observableOf(null);
|
|
110
|
+
}
|
|
108
111
|
return segmentBuffer.removeBuffer(range.start, range.end);
|
|
109
112
|
})).pipe(concatAll(),
|
|
110
113
|
// NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
|
|
@@ -240,7 +240,8 @@ export default function StreamOrchestrator(content, playbackObserver, abrManager
|
|
|
240
240
|
destroyStreams$.next();
|
|
241
241
|
return observableConcat.apply(void 0, __spreadArray(__spreadArray([], rangesToClean.map(function (_a) {
|
|
242
242
|
var start = _a.start, end = _a.end;
|
|
243
|
-
return
|
|
243
|
+
return start >= end ? EMPTY :
|
|
244
|
+
segmentBuffer.removeBuffer(start, end).pipe(ignoreElements());
|
|
244
245
|
}), false), [playbackObserver.observe(true).pipe(take(1), mergeMap(function (observation) {
|
|
245
246
|
return observableConcat(observableOf(EVENTS.needsDecipherabilityFlush(observation.position, !observation.isPaused, observation.duration)), observableDefer(function () {
|
|
246
247
|
var lastPosition = observation.position +
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { catchError, concat as observableConcat, defer as observableDefer, EMPTY, ignoreElements, map, merge as observableMerge, mergeMap, of as observableOf, ReplaySubject, startWith, switchMap, } from "rxjs";
|
|
17
17
|
import config from "../../../config";
|
|
18
|
-
import { formatError } from "../../../errors";
|
|
18
|
+
import { formatError, MediaError, } from "../../../errors";
|
|
19
19
|
import log from "../../../log";
|
|
20
20
|
import objectAssign from "../../../utils/object_assign";
|
|
21
21
|
import { getLeftSizeOfRange } from "../../../utils/ranges";
|
|
@@ -63,9 +63,15 @@ export default function PeriodStream(_a) {
|
|
|
63
63
|
if (SegmentBuffersStore.isNative(bufferType)) {
|
|
64
64
|
return reloadAfterSwitch(period, bufferType, playbackObserver, 0);
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
.removeBuffer(period.start,
|
|
68
|
-
|
|
66
|
+
if (period.end === undefined) {
|
|
67
|
+
cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start, Infinity);
|
|
68
|
+
}
|
|
69
|
+
else if (period.end <= period.start) {
|
|
70
|
+
cleanBuffer$ = observableOf(null);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start, period.end);
|
|
74
|
+
}
|
|
69
75
|
}
|
|
70
76
|
else {
|
|
71
77
|
if (segmentBufferStatus.type === "uninitialized") {
|
|
@@ -160,9 +166,11 @@ function createOrReuseSegmentBuffer(segmentBuffersStore, bufferType, adaptation,
|
|
|
160
166
|
* @returns {string}
|
|
161
167
|
*/
|
|
162
168
|
function getFirstDeclaredMimeType(adaptation) {
|
|
163
|
-
var representations = adaptation.
|
|
164
|
-
if (representations
|
|
165
|
-
|
|
169
|
+
var representations = adaptation.getPlayableRepresentations();
|
|
170
|
+
if (representations.length === 0) {
|
|
171
|
+
var noRepErr = new MediaError("NO_PLAYABLE_REPRESENTATION", "No Representation in the chosen " +
|
|
172
|
+
adaptation.type + " Adaptation can be played");
|
|
173
|
+
throw noRepErr;
|
|
166
174
|
}
|
|
167
175
|
return representations[0].getMimeTypeString();
|
|
168
176
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { concatAll, defer as observableDefer, from as observableFrom, } from "rxjs";
|
|
16
|
+
import { concatAll, defer as observableDefer, from as observableFrom, of as observableOf, } from "rxjs";
|
|
17
17
|
import config from "../../../config";
|
|
18
18
|
import log from "../../../log";
|
|
19
19
|
import { getInnerAndOuterTimeRanges } from "../../../utils/ranges";
|
|
@@ -41,7 +41,8 @@ export default function forceGarbageCollection(currentPosition, bufferingQueue)
|
|
|
41
41
|
log.debug("Stream: GC cleaning", cleanedupRanges);
|
|
42
42
|
return observableFrom(cleanedupRanges.map(function (_a) {
|
|
43
43
|
var start = _a.start, end = _a.end;
|
|
44
|
-
return
|
|
44
|
+
return start >= end ? observableOf(null) :
|
|
45
|
+
bufferingQueue.removeBuffer(start, end);
|
|
45
46
|
})).pipe(concatAll());
|
|
46
47
|
});
|
|
47
48
|
}
|
|
@@ -43,8 +43,8 @@ export interface IBufferStatus {
|
|
|
43
43
|
*/
|
|
44
44
|
shouldRefreshManifest: boolean;
|
|
45
45
|
/**
|
|
46
|
-
* If 'true', the buffer memory is saturated
|
|
47
|
-
*
|
|
46
|
+
* If 'true', the buffer memory is saturated, thus we may have issues loading
|
|
47
|
+
* new segments.
|
|
48
48
|
*/
|
|
49
49
|
isBufferFull: boolean;
|
|
50
50
|
}
|
|
@@ -55,8 +55,8 @@ export default function getBufferStatus(content, wantedStartPosition, playbackOb
|
|
|
55
55
|
/** Callback allowing to retrieve a segment's history in the buffer. */
|
|
56
56
|
var getBufferedHistory = segmentBuffer.getSegmentHistory.bind(segmentBuffer);
|
|
57
57
|
/** List of segments we will need to download. */
|
|
58
|
-
var _b = getNeededSegments({ content: content, bufferedSegments: bufferedSegments, currentPlaybackTime: currentPlaybackTime, fastSwitchThreshold: fastSwitchThreshold, getBufferedHistory: getBufferedHistory, neededRange: neededRange, segmentsBeingPushed: segmentsBeingPushed, maxBufferSize: maxBufferSize }),
|
|
59
|
-
var prioritizedNeededSegments =
|
|
58
|
+
var _b = getNeededSegments({ content: content, bufferedSegments: bufferedSegments, currentPlaybackTime: currentPlaybackTime, fastSwitchThreshold: fastSwitchThreshold, getBufferedHistory: getBufferedHistory, neededRange: neededRange, segmentsBeingPushed: segmentsBeingPushed, maxBufferSize: maxBufferSize }), segmentsToLoad = _b.segmentsToLoad, segmentsOnHold = _b.segmentsOnHold, isBufferFull = _b.isBufferFull;
|
|
59
|
+
var prioritizedNeededSegments = segmentsToLoad.map(function (segment) { return ({
|
|
60
60
|
priority: getSegmentPriority(segment.time, wantedStartPosition),
|
|
61
61
|
segment: segment,
|
|
62
62
|
}); });
|
|
@@ -68,7 +68,8 @@ export default function getBufferStatus(content, wantedStartPosition, playbackOb
|
|
|
68
68
|
var lastPosition = representation.index.getLastPosition();
|
|
69
69
|
if (!representation.index.isInitialized() ||
|
|
70
70
|
period.end === undefined ||
|
|
71
|
-
prioritizedNeededSegments.length > 0
|
|
71
|
+
prioritizedNeededSegments.length > 0 ||
|
|
72
|
+
segmentsOnHold.length > 0) {
|
|
72
73
|
hasFinishedLoading = false;
|
|
73
74
|
}
|
|
74
75
|
else {
|
|
@@ -112,6 +113,11 @@ export default function getBufferStatus(content, wantedStartPosition, playbackOb
|
|
|
112
113
|
if (segmentsBeingPushed.length > 0) {
|
|
113
114
|
nextSegmentStart = Math.min.apply(Math, segmentsBeingPushed.map(function (info) { return info.segment.time; }));
|
|
114
115
|
}
|
|
116
|
+
if (segmentsOnHold.length > 0) {
|
|
117
|
+
nextSegmentStart = nextSegmentStart !== null ?
|
|
118
|
+
Math.min(nextSegmentStart, segmentsOnHold[0].time) :
|
|
119
|
+
segmentsOnHold[0].time;
|
|
120
|
+
}
|
|
115
121
|
if (prioritizedNeededSegments.length > 0) {
|
|
116
122
|
nextSegmentStart = nextSegmentStart !== null ?
|
|
117
123
|
Math.min(nextSegmentStart, prioritizedNeededSegments[0].segment.time) :
|
|
@@ -65,7 +65,17 @@ export interface IGetNeededSegmentsArguments {
|
|
|
65
65
|
getBufferedHistory: (context: IChunkContext) => IBufferedHistoryEntry[];
|
|
66
66
|
}
|
|
67
67
|
interface INeededSegments {
|
|
68
|
-
|
|
68
|
+
/** Segments that should be loaded right now, by chronological order. */
|
|
69
|
+
segmentsToLoad: ISegment[];
|
|
70
|
+
/**
|
|
71
|
+
* Segments that should be loaded, but not right now, due to some other
|
|
72
|
+
* constraints, such as memory limitations.
|
|
73
|
+
*/
|
|
74
|
+
segmentsOnHold: ISegment[];
|
|
75
|
+
/**
|
|
76
|
+
* If `true` the buffer is currently full according to the given limits.
|
|
77
|
+
* Memory should be freed if possible, for example by cleaning the buffers.
|
|
78
|
+
*/
|
|
69
79
|
isBufferFull: boolean;
|
|
70
80
|
}
|
|
71
81
|
/**
|
|
@@ -32,8 +32,6 @@ export default function getNeededSegments(_a) {
|
|
|
32
32
|
var bufferedSegments = _a.bufferedSegments, content = _a.content, currentPlaybackTime = _a.currentPlaybackTime, fastSwitchThreshold = _a.fastSwitchThreshold, getBufferedHistory = _a.getBufferedHistory, neededRange = _a.neededRange, segmentsBeingPushed = _a.segmentsBeingPushed, maxBufferSize = _a.maxBufferSize;
|
|
33
33
|
var representation = content.representation;
|
|
34
34
|
var availableBufferSize = getAvailableBufferSize(bufferedSegments, segmentsBeingPushed, maxBufferSize);
|
|
35
|
-
// Current buffer length in seconds
|
|
36
|
-
var bufferLength = getBufferLength(bufferedSegments, segmentsBeingPushed);
|
|
37
35
|
var availableSegmentsForRange = representation.index
|
|
38
36
|
.getSegments(neededRange.start, neededRange.end - neededRange.start);
|
|
39
37
|
// Remove from `bufferedSegments` any segments we would prefer to replace:
|
|
@@ -63,15 +61,16 @@ export default function getNeededSegments(_a) {
|
|
|
63
61
|
}
|
|
64
62
|
return true;
|
|
65
63
|
});
|
|
66
|
-
var _b = config.getCurrent(), MINIMUM_SEGMENT_SIZE = _b.MINIMUM_SEGMENT_SIZE,
|
|
67
|
-
var
|
|
64
|
+
var _b = config.getCurrent(), MINIMUM_SEGMENT_SIZE = _b.MINIMUM_SEGMENT_SIZE, MIN_BUFFER_AHEAD = _b.MIN_BUFFER_AHEAD;
|
|
65
|
+
var shouldStopLoadingSegments = false;
|
|
68
66
|
/**
|
|
69
67
|
* Epsilon compensating for rounding errors when comparing the start and end
|
|
70
68
|
* time of multiple segments.
|
|
71
69
|
*/
|
|
72
70
|
var ROUNDING_ERROR = Math.min(1 / 60, MINIMUM_SEGMENT_SIZE);
|
|
73
71
|
var isBufferFull = false;
|
|
74
|
-
var
|
|
72
|
+
var segmentsOnHold = [];
|
|
73
|
+
var segmentsToLoad = availableSegmentsForRange.filter(function (segment) {
|
|
75
74
|
var contentObject = objectAssign({ segment: segment }, content);
|
|
76
75
|
// First, check that the segment is not already being pushed
|
|
77
76
|
if (segmentsBeingPushed.length > 0) {
|
|
@@ -85,17 +84,8 @@ export default function getNeededSegments(_a) {
|
|
|
85
84
|
if (segment.isInit) {
|
|
86
85
|
return true; // never skip initialization segments
|
|
87
86
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
// That we cannot download atleast till
|
|
91
|
-
// NeededRange.Start ( current position ) + a CONST
|
|
92
|
-
// Then the buffer is full
|
|
93
|
-
if (time < neededRange.start + MIN_BUFFER_DISTANCE_BEFORE_CLEAN_UP) {
|
|
94
|
-
isBufferFull = true;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
if (isMemorySaturated &&
|
|
98
|
-
bufferLength > MIN_BUFFER_LENGTH) {
|
|
87
|
+
if (shouldStopLoadingSegments) {
|
|
88
|
+
segmentsOnHold.push(segment);
|
|
99
89
|
return false;
|
|
100
90
|
}
|
|
101
91
|
if (segment.complete && duration < MINIMUM_SEGMENT_SIZE) {
|
|
@@ -138,27 +128,34 @@ export default function getNeededSegments(_a) {
|
|
|
138
128
|
}
|
|
139
129
|
}
|
|
140
130
|
}
|
|
131
|
+
var estimatedSegmentSize = (duration * content.representation.bitrate) / 8000;
|
|
132
|
+
if (availableBufferSize - estimatedSegmentSize < 0) {
|
|
133
|
+
isBufferFull = true;
|
|
134
|
+
if (time > neededRange.start + MIN_BUFFER_AHEAD) {
|
|
135
|
+
shouldStopLoadingSegments = true;
|
|
136
|
+
segmentsOnHold.push(segment);
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
141
140
|
// check if there is an hole in place of the segment currently
|
|
142
141
|
for (var i = 0; i < segmentsToKeep.length; i++) {
|
|
143
142
|
var completeSeg = segmentsToKeep[i];
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
getLastContiguousSegment(segmentsToKeep, i).end <
|
|
143
|
+
// For the first already-loaded segment, take the first one ending after
|
|
144
|
+
// this one' s start
|
|
145
|
+
if ((completeSeg.end + ROUNDING_ERROR) > time) {
|
|
146
|
+
var shouldLoad = completeSeg.start > time + ROUNDING_ERROR ||
|
|
147
|
+
getLastContiguousSegment(segmentsToKeep, i).end <
|
|
148
|
+
end - ROUNDING_ERROR;
|
|
149
|
+
if (shouldLoad) {
|
|
150
|
+
availableBufferSize -= estimatedSegmentSize;
|
|
151
|
+
}
|
|
152
|
+
return shouldLoad;
|
|
149
153
|
}
|
|
150
154
|
}
|
|
151
|
-
var estimatedSegmentSize = (duration * content.representation.bitrate) / 8000;
|
|
152
|
-
if (availableBufferSize - estimatedSegmentSize < 0 &&
|
|
153
|
-
bufferLength > MIN_BUFFER_LENGTH) {
|
|
154
|
-
isMemorySaturated = true;
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
155
|
availableBufferSize -= estimatedSegmentSize;
|
|
158
|
-
bufferLength += duration;
|
|
159
156
|
return true;
|
|
160
157
|
});
|
|
161
|
-
return {
|
|
158
|
+
return { segmentsToLoad: segmentsToLoad, segmentsOnHold: segmentsOnHold, isBufferFull: isBufferFull };
|
|
162
159
|
}
|
|
163
160
|
/**
|
|
164
161
|
* Compute the estimated available buffer size in memory in kilobytes
|
|
@@ -178,28 +175,13 @@ function getAvailableBufferSize(bufferedSegments, segmentsBeingPushed, maxVideoB
|
|
|
178
175
|
}, 0);
|
|
179
176
|
return bufferedSegments.reduce(function (size, chunk) {
|
|
180
177
|
if (chunk.chunkSize !== undefined) {
|
|
181
|
-
return size - (chunk.chunkSize /
|
|
178
|
+
return size - (chunk.chunkSize / 1000);
|
|
182
179
|
}
|
|
183
180
|
else {
|
|
184
181
|
return size;
|
|
185
182
|
}
|
|
186
183
|
}, availableBufferSize);
|
|
187
184
|
}
|
|
188
|
-
/**
|
|
189
|
-
* Compute the length of the buffer in seconds
|
|
190
|
-
* @param bufferedSegments
|
|
191
|
-
* @param segmentsBeingPushed
|
|
192
|
-
* @returns bufferLength in seconds
|
|
193
|
-
*/
|
|
194
|
-
function getBufferLength(bufferedSegments, segmentsBeingPushed) {
|
|
195
|
-
var bufferLength = bufferedSegments.reduce(function (length, segment) {
|
|
196
|
-
return length + (segment.end - segment.start);
|
|
197
|
-
}, 0);
|
|
198
|
-
var bufferBeingPushed = segmentsBeingPushed.reduce(function (length, segment) {
|
|
199
|
-
return length + segment.segment.duration;
|
|
200
|
-
}, 0);
|
|
201
|
-
return bufferLength + bufferBeingPushed;
|
|
202
|
-
}
|
|
203
185
|
/**
|
|
204
186
|
* From the given array of buffered chunks (`bufferedSegments`) returns the last
|
|
205
187
|
* buffered chunk contiguous with the one at the `startIndex` index given.
|
|
@@ -166,10 +166,12 @@ export default function RepresentationStream(_a) {
|
|
|
166
166
|
var UPTO_CURRENT_POSITION_CLEANUP = config.getCurrent().UPTO_CURRENT_POSITION_CLEANUP;
|
|
167
167
|
if (status.isBufferFull) {
|
|
168
168
|
var gcedPosition = Math.max(0, wantedStartPosition - UPTO_CURRENT_POSITION_CLEANUP);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
if (gcedPosition > 0) {
|
|
170
|
+
bufferRemoval = segmentBuffer
|
|
171
|
+
.removeBuffer(0, gcedPosition)
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
173
|
+
.pipe(ignoreElements());
|
|
174
|
+
}
|
|
173
175
|
}
|
|
174
176
|
return status.shouldRefreshManifest ?
|
|
175
177
|
observableConcat(observableOf(EVENTS.needsManifestRefresh()), bufferStatusEvt, bufferRemoval) :
|
|
@@ -1065,17 +1065,11 @@ declare const DEFAULT_CONFIG: {
|
|
|
1065
1065
|
* there can be in that history.
|
|
1066
1066
|
*/
|
|
1067
1067
|
BUFFERED_HISTORY_MAXIMUM_ENTRIES: number;
|
|
1068
|
-
/**
|
|
1069
|
-
* Minimum buffer (in seconds) we should have, regardless of memory
|
|
1070
|
-
* constraints
|
|
1071
|
-
*/
|
|
1072
|
-
MIN_BUFFER_LENGTH: number;
|
|
1073
1068
|
/**
|
|
1074
1069
|
* Minimum buffer in seconds ahead relative to current time
|
|
1075
|
-
* we should be able to download
|
|
1076
|
-
* Before trying to agressively free up memory
|
|
1070
|
+
* we should be able to download, even in cases of saturated memory.
|
|
1077
1071
|
*/
|
|
1078
|
-
|
|
1072
|
+
MIN_BUFFER_AHEAD: number;
|
|
1079
1073
|
/**
|
|
1080
1074
|
* Distance in seconds behind the current position
|
|
1081
1075
|
* the player will free up to in the case we agressively free up memory
|
|
@@ -1097,17 +1097,11 @@ var DEFAULT_CONFIG = {
|
|
|
1097
1097
|
* there can be in that history.
|
|
1098
1098
|
*/
|
|
1099
1099
|
BUFFERED_HISTORY_MAXIMUM_ENTRIES: 200,
|
|
1100
|
-
/**
|
|
1101
|
-
* Minimum buffer (in seconds) we should have, regardless of memory
|
|
1102
|
-
* constraints
|
|
1103
|
-
*/
|
|
1104
|
-
MIN_BUFFER_LENGTH: 5,
|
|
1105
1100
|
/**
|
|
1106
1101
|
* Minimum buffer in seconds ahead relative to current time
|
|
1107
|
-
* we should be able to download
|
|
1108
|
-
* Before trying to agressively free up memory
|
|
1102
|
+
* we should be able to download, even in cases of saturated memory.
|
|
1109
1103
|
*/
|
|
1110
|
-
|
|
1104
|
+
MIN_BUFFER_AHEAD: 5,
|
|
1111
1105
|
/**
|
|
1112
1106
|
* Distance in seconds behind the current position
|
|
1113
1107
|
* the player will free up to in the case we agressively free up memory
|