rx-player 3.29.0-dev.2022091600 → 3.29.0-dev.2022103100
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 +22 -10
- package/MAINTAINERS.md +2 -1
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/browser_detection.d.ts +4 -1
- package/dist/_esm5.processed/compat/browser_detection.js +7 -1
- package/dist/_esm5.processed/compat/can_reuse_media_keys.d.ts +12 -0
- package/dist/_esm5.processed/compat/can_reuse_media_keys.js +15 -0
- package/dist/_esm5.processed/compat/enable_audio_track.d.ts +11 -0
- package/dist/_esm5.processed/compat/enable_audio_track.js +26 -0
- package/dist/_esm5.processed/compat/index.d.ts +4 -2
- package/dist/_esm5.processed/compat/index.js +4 -2
- package/dist/_esm5.processed/compat/{should_renew_media_keys.d.ts → should_renew_media_key_system_access.d.ts} +3 -3
- package/dist/_esm5.processed/compat/{should_renew_media_keys.js → should_renew_media_key_system_access.js} +3 -3
- package/dist/_esm5.processed/config.d.ts +1 -0
- package/dist/_esm5.processed/core/api/playback_observer.d.ts +14 -5
- package/dist/_esm5.processed/core/api/playback_observer.js +54 -22
- package/dist/_esm5.processed/core/api/public_api.d.ts +7 -4
- package/dist/_esm5.processed/core/api/public_api.js +79 -61
- package/dist/_esm5.processed/core/api/tracks_management/media_element_track_choice_manager.js +9 -10
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +1 -1
- package/dist/_esm5.processed/core/decrypt/find_key_system.js +3 -3
- package/dist/_esm5.processed/core/decrypt/get_media_keys.js +4 -1
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +98 -0
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +173 -0
- package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +4 -4
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +2 -1
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +15 -16
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.d.ts +3 -1
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher_creator.js +5 -2
- package/dist/_esm5.processed/core/fetchers/utils/schedule_request.d.ts +103 -0
- package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +414 -0
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.d.ts +4 -3
- package/dist/_esm5.processed/core/init/content_time_boundaries_observer.js +21 -22
- package/dist/_esm5.processed/core/init/initialize_media_source.d.ts +18 -4
- package/dist/_esm5.processed/core/init/initialize_media_source.js +10 -2
- package/dist/_esm5.processed/core/init/load_on_media_source.js +7 -1
- package/dist/_esm5.processed/core/init/stall_avoider.js +3 -1
- package/dist/_esm5.processed/default_config.d.ts +11 -0
- package/dist/_esm5.processed/default_config.js +11 -0
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +1 -1
- package/dist/_esm5.processed/manifest/manifest.js +11 -2
- package/dist/_esm5.processed/manifest/representation.d.ts +12 -1
- package/dist/_esm5.processed/manifest/representation.js +1 -0
- package/dist/_esm5.processed/manifest/representation_index/static.d.ts +1 -1
- package/dist/_esm5.processed/manifest/representation_index/static.js +2 -2
- package/dist/_esm5.processed/manifest/representation_index/types.d.ts +32 -15
- package/dist/_esm5.processed/manifest/update_period_in_place.js +1 -0
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.d.ts +10 -9
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/base.js +11 -11
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_init_segment.d.ts +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_init_segment.js +3 -3
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_segments_from_timeline.d.ts +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/get_segments_from_timeline.js +4 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.d.ts +7 -7
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/list.js +12 -8
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.d.ts +5 -6
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/template.js +16 -16
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.d.ts +11 -10
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.js +14 -13
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/tokens.d.ts +3 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/indexes/tokens.js +4 -12
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_mpd.js +2 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representation_index.js +1 -3
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +6 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/resolve_base_urls.d.ts +1 -2
- package/dist/_esm5.processed/parsers/manifest/dash/common/resolve_base_urls.js +3 -8
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/node_parsers/BaseURL.d.ts +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/node_parsers/BaseURL.js +2 -20
- package/dist/_esm5.processed/parsers/manifest/dash/node_parser_types.d.ts +0 -6
- package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/generators/BaseURL.js +0 -11
- package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/types.d.ts +5 -1
- package/dist/_esm5.processed/parsers/manifest/local/parse_local_manifest.js +2 -1
- package/dist/_esm5.processed/parsers/manifest/local/representation_index.js +2 -2
- package/dist/_esm5.processed/parsers/manifest/metaplaylist/metaplaylist_parser.js +7 -1
- package/dist/_esm5.processed/parsers/manifest/smooth/create_parser.js +17 -11
- package/dist/_esm5.processed/parsers/manifest/smooth/representation_index.js +2 -2
- package/dist/_esm5.processed/parsers/manifest/types.d.ts +24 -0
- package/dist/_esm5.processed/parsers/texttracks/ttml/html/apply_extent.js +8 -1
- package/dist/_esm5.processed/parsers/texttracks/ttml/html/apply_origin.js +9 -1
- package/dist/_esm5.processed/parsers/texttracks/ttml/html/create_element.js +2 -1
- package/dist/_esm5.processed/transports/dash/construct_segment_url.d.ts +18 -0
- package/dist/_esm5.processed/transports/dash/construct_segment_url.js +21 -0
- package/dist/_esm5.processed/transports/dash/image_pipelines.d.ts +3 -2
- package/dist/_esm5.processed/transports/dash/image_pipelines.js +5 -3
- package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -2
- package/dist/_esm5.processed/transports/dash/text_loader.js +4 -2
- package/dist/_esm5.processed/transports/dash/text_parser.js +3 -1
- package/dist/_esm5.processed/transports/local/segment_loader.d.ts +3 -2
- package/dist/_esm5.processed/transports/local/segment_loader.js +2 -2
- package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +8 -8
- package/dist/_esm5.processed/transports/smooth/pipelines.js +11 -7
- package/dist/_esm5.processed/transports/smooth/utils.d.ts +4 -2
- package/dist/_esm5.processed/transports/smooth/utils.js +7 -1
- package/dist/_esm5.processed/transports/types.d.ts +5 -3
- package/dist/_esm5.processed/utils/event_emitter.d.ts +1 -2
- package/dist/_esm5.processed/utils/resolve_url.d.ts +7 -4
- package/dist/_esm5.processed/utils/resolve_url.js +11 -8
- package/dist/mpd-parser.wasm +0 -0
- package/dist/rx-player.js +5656 -13236
- package/dist/rx-player.min.js +1 -1
- package/jest.config.js +28 -23
- package/package.json +29 -29
- package/sonar-project.properties +1 -1
- package/src/compat/__tests__/can_reuse_media_keys.test.ts +54 -0
- package/src/compat/__tests__/enable_audio_track.test.ts +341 -0
- package/src/compat/__tests__/{should_renew_media_keys.test.ts → should_renew_media_key_system_access.test.ts} +7 -5
- package/src/compat/browser_detection.ts +10 -0
- package/src/compat/can_reuse_media_keys.ts +19 -0
- package/src/compat/enable_audio_track.ts +33 -0
- package/src/compat/index.ts +6 -2
- package/src/compat/{should_renew_media_keys.ts → should_renew_media_key_system_access.ts} +3 -3
- package/src/core/api/playback_observer.ts +97 -33
- package/src/core/api/public_api.ts +126 -74
- package/src/core/api/tracks_management/media_element_track_choice_manager.ts +3 -11
- package/src/core/decrypt/__tests__/__global__/media_keys.test.ts +182 -0
- package/src/core/decrypt/__tests__/__global__/utils.ts +2 -0
- package/src/core/decrypt/find_key_system.ts +3 -3
- package/src/core/decrypt/get_media_keys.ts +5 -1
- package/src/core/fetchers/cdn_prioritizer.ts +198 -0
- package/src/core/fetchers/manifest/manifest_fetcher.ts +7 -11
- package/src/core/fetchers/segment/segment_fetcher.ts +12 -10
- package/src/core/fetchers/segment/segment_fetcher_creator.ts +10 -1
- package/src/core/fetchers/utils/schedule_request.ts +482 -0
- package/src/core/init/content_time_boundaries_observer.ts +12 -12
- package/src/core/init/initialize_media_source.ts +31 -4
- package/src/core/init/load_on_media_source.ts +11 -2
- package/src/core/init/stall_avoider.ts +4 -1
- package/src/core/stream/orchestrator/stream_orchestrator.ts +0 -1
- package/src/default_config.ts +12 -0
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +1 -0
- package/src/manifest/manifest.ts +11 -2
- package/src/manifest/representation.ts +15 -0
- package/src/manifest/representation_index/__tests__/static.test.ts +1 -1
- package/src/manifest/representation_index/static.ts +3 -3
- package/src/manifest/representation_index/types.ts +33 -15
- package/src/manifest/update_period_in_place.ts +1 -0
- package/src/parsers/manifest/dash/common/indexes/base.ts +22 -23
- package/src/parsers/manifest/dash/common/indexes/get_init_segment.ts +6 -4
- package/src/parsers/manifest/dash/common/indexes/get_segments_from_timeline.ts +5 -5
- package/src/parsers/manifest/dash/common/indexes/list.ts +19 -20
- package/src/parsers/manifest/dash/common/indexes/template.ts +23 -27
- package/src/parsers/manifest/dash/common/indexes/timeline/timeline_representation_index.ts +25 -23
- package/src/parsers/manifest/dash/common/indexes/tokens.ts +7 -18
- package/src/parsers/manifest/dash/common/parse_mpd.ts +2 -4
- package/src/parsers/manifest/dash/common/parse_representation_index.ts +1 -6
- package/src/parsers/manifest/dash/common/parse_representations.ts +9 -0
- package/src/parsers/manifest/dash/common/resolve_base_urls.ts +5 -9
- package/src/parsers/manifest/dash/js-parser/node_parsers/BaseURL.ts +2 -29
- package/src/parsers/manifest/dash/js-parser/node_parsers/__tests__/AdaptationSet.test.ts +7 -11
- package/src/parsers/manifest/dash/node_parser_types.ts +0 -7
- package/src/parsers/manifest/dash/wasm-parser/rs/events.rs +3 -1
- package/src/parsers/manifest/dash/wasm-parser/rs/processor/attributes.rs +1 -8
- package/src/parsers/manifest/dash/wasm-parser/ts/generators/BaseURL.ts +0 -13
- package/src/parsers/manifest/dash/wasm-parser/ts/types.ts +8 -0
- package/src/parsers/manifest/local/parse_local_manifest.ts +1 -0
- package/src/parsers/manifest/local/representation_index.ts +2 -2
- package/src/parsers/manifest/metaplaylist/metaplaylist_parser.ts +7 -1
- package/src/parsers/manifest/smooth/create_parser.ts +17 -13
- package/src/parsers/manifest/smooth/representation_index.ts +2 -2
- package/src/parsers/manifest/types.ts +26 -1
- package/src/parsers/manifest/utils/__tests__/get_first_time_from_adaptations.test.ts +15 -0
- package/src/parsers/manifest/utils/__tests__/get_last_time_from_adaptation.test.ts +15 -0
- package/src/parsers/texttracks/ttml/html/apply_extent.ts +8 -1
- package/src/parsers/texttracks/ttml/html/apply_origin.ts +9 -1
- package/src/parsers/texttracks/ttml/html/create_element.ts +2 -2
- package/src/transports/dash/construct_segment_url.ts +28 -0
- package/src/transports/dash/image_pipelines.ts +5 -2
- package/src/transports/dash/segment_loader.ts +5 -2
- package/src/transports/dash/text_loader.ts +5 -2
- package/src/transports/dash/text_parser.ts +3 -1
- package/src/transports/local/segment_loader.ts +3 -2
- package/src/transports/metaplaylist/pipelines.ts +28 -9
- package/src/transports/smooth/pipelines.ts +12 -6
- package/src/transports/smooth/utils.ts +13 -1
- package/src/transports/types.ts +6 -3
- package/src/utils/__tests__/event_emitter.test.ts +72 -67
- package/src/utils/__tests__/initialization_segment_cache.test.ts +7 -7
- package/src/utils/__tests__/resolve_url.test.ts +27 -23
- package/src/utils/event_emitter.ts +1 -3
- package/src/utils/resolve_url.ts +11 -8
- package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.d.ts +0 -85
- package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.js +0 -264
- package/dummy +0 -1
- package/src/core/fetchers/utils/try_urls_with_backoff.ts +0 -287
|
@@ -0,0 +1,414 @@
|
|
|
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
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
17
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
18
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
19
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
20
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
21
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
22
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
26
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
|
27
|
+
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
28
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
29
|
+
function step(op) {
|
|
30
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
31
|
+
while (_) try {
|
|
32
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
33
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
34
|
+
switch (op[0]) {
|
|
35
|
+
case 0: case 1: t = op; break;
|
|
36
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
37
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
38
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
39
|
+
default:
|
|
40
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
41
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
42
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
43
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
44
|
+
if (t[2]) _.ops.pop();
|
|
45
|
+
_.trys.pop(); continue;
|
|
46
|
+
}
|
|
47
|
+
op = body.call(thisArg, _);
|
|
48
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
49
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
import { isOffline } from "../../../compat";
|
|
53
|
+
import { CustomLoaderError, isKnownError, NetworkErrorTypes, RequestError, } from "../../../errors";
|
|
54
|
+
import log from "../../../log";
|
|
55
|
+
import cancellableSleep from "../../../utils/cancellable_sleep";
|
|
56
|
+
import getFuzzedDelay from "../../../utils/get_fuzzed_delay";
|
|
57
|
+
import noop from "../../../utils/noop";
|
|
58
|
+
import TaskCanceller from "../../../utils/task_canceller";
|
|
59
|
+
/**
|
|
60
|
+
* Called on a loader error.
|
|
61
|
+
* Returns whether the loader request should be retried.
|
|
62
|
+
*
|
|
63
|
+
* TODO the notion of retrying or not could be transport-specific (e.g. 412 are
|
|
64
|
+
* mainly used for Smooth contents) and thus as part of the transport code (e.g.
|
|
65
|
+
* by rejecting with an error always having a `canRetry` property?).
|
|
66
|
+
* Or not, to ponder.
|
|
67
|
+
*
|
|
68
|
+
* @param {Error} error
|
|
69
|
+
* @returns {Boolean} - If true, the request can be retried.
|
|
70
|
+
*/
|
|
71
|
+
function shouldRetry(error) {
|
|
72
|
+
if (error instanceof RequestError) {
|
|
73
|
+
if (error.type === NetworkErrorTypes.ERROR_HTTP_CODE) {
|
|
74
|
+
return error.status >= 500 ||
|
|
75
|
+
error.status === 404 ||
|
|
76
|
+
error.status === 415 || // some CDN seems to use that code when
|
|
77
|
+
// requesting low-latency segments too much
|
|
78
|
+
// in advance
|
|
79
|
+
error.status === 412;
|
|
80
|
+
}
|
|
81
|
+
return error.type === NetworkErrorTypes.TIMEOUT ||
|
|
82
|
+
error.type === NetworkErrorTypes.ERROR_EVENT;
|
|
83
|
+
}
|
|
84
|
+
else if (error instanceof CustomLoaderError) {
|
|
85
|
+
if (typeof error.canRetry === "boolean") {
|
|
86
|
+
return error.canRetry;
|
|
87
|
+
}
|
|
88
|
+
if (error.xhr !== undefined) {
|
|
89
|
+
return error.xhr.status >= 500 ||
|
|
90
|
+
error.xhr.status === 404 ||
|
|
91
|
+
error.xhr.status === 415 || // some CDN seems to use that code when
|
|
92
|
+
// requesting low-latency segments too much
|
|
93
|
+
// in advance
|
|
94
|
+
error.xhr.status === 412;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return isKnownError(error) && error.code === "INTEGRITY_ERROR";
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Returns true if we're pretty sure that the current error is due to the
|
|
102
|
+
* user being offline.
|
|
103
|
+
* @param {Error} error
|
|
104
|
+
* @returns {Boolean}
|
|
105
|
+
*/
|
|
106
|
+
function isOfflineRequestError(error) {
|
|
107
|
+
if (error instanceof RequestError) {
|
|
108
|
+
return error.type === NetworkErrorTypes.ERROR_EVENT &&
|
|
109
|
+
isOffline();
|
|
110
|
+
}
|
|
111
|
+
else if (error instanceof CustomLoaderError) {
|
|
112
|
+
return error.isOfflineError;
|
|
113
|
+
}
|
|
114
|
+
return false; // under doubt, return false
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Guess the type of error obtained.
|
|
118
|
+
* @param {*} error
|
|
119
|
+
* @returns {number}
|
|
120
|
+
*/
|
|
121
|
+
function getRequestErrorType(error) {
|
|
122
|
+
return isOfflineRequestError(error) ? 2 /* REQUEST_ERROR_TYPES.Offline */ :
|
|
123
|
+
1 /* REQUEST_ERROR_TYPES.Regular */;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Specific algorithm used to perform segment and manifest requests.
|
|
127
|
+
*
|
|
128
|
+
* Here how it works:
|
|
129
|
+
*
|
|
130
|
+
* 1. You give it one or multiple of the CDN available for the resource you
|
|
131
|
+
* want to request (from the most important one to the least important),
|
|
132
|
+
* a callback doing the request with the chosen CDN in argument, and some
|
|
133
|
+
* options.
|
|
134
|
+
*
|
|
135
|
+
* 2. it tries to call the request callback with the most prioritized CDN
|
|
136
|
+
* first:
|
|
137
|
+
* - if it works as expected, it resolves the returned Promise with that
|
|
138
|
+
* request's response.
|
|
139
|
+
* - if it fails, it calls ther `onRetry` callback given with the
|
|
140
|
+
* corresponding error, un-prioritize that CDN and try with the new
|
|
141
|
+
* most prioritized CDN.
|
|
142
|
+
*
|
|
143
|
+
* Each CDN might be retried multiple times, depending on the nature of the
|
|
144
|
+
* error and the Configuration given.
|
|
145
|
+
*
|
|
146
|
+
* Multiple retries of the same CDN are done after a delay to avoid
|
|
147
|
+
* overwhelming it, this is what we call a "backoff". That delay raises
|
|
148
|
+
* exponentially as multiple consecutive errors are encountered on this
|
|
149
|
+
* CDN.
|
|
150
|
+
*
|
|
151
|
+
* @param {Array.<string>|null} cdns - The different CDN on which the
|
|
152
|
+
* wanted resource is available. `scheduleRequestWithCdns` will call the
|
|
153
|
+
* `performRequest` callback with the right element from that array if different
|
|
154
|
+
* from `null`.
|
|
155
|
+
*
|
|
156
|
+
* Can be set to `null` when that resource is not reachable through a CDN, in
|
|
157
|
+
* which case the `performRequest` callback may be called with `null`.
|
|
158
|
+
* @param {Object|null} cdnPrioritizer - Interface allowing to give the priority
|
|
159
|
+
* between multiple CDNs.
|
|
160
|
+
* @param {Function} performRequest - Callback implementing the request in
|
|
161
|
+
* itself. Resolving when the resource request succeed and rejecting with the
|
|
162
|
+
* corresponding error when the request failed.
|
|
163
|
+
* @param {Object} options - Configuration allowing to tweak the number on which
|
|
164
|
+
* the algorithm behind `scheduleRequestWithCdns` bases itself.
|
|
165
|
+
* @param {Object} cancellationSignal - CancellationSignal allowing to cancel
|
|
166
|
+
* the logic of `scheduleRequestWithCdns`.
|
|
167
|
+
* To trigger if the resource is not needed anymore.
|
|
168
|
+
* @returns {Promise} - Promise resolving, with the corresponding
|
|
169
|
+
* `performRequest`'s data, when the resource request succeed and rejecting in
|
|
170
|
+
* the following scenarios:
|
|
171
|
+
* - `scheduleRequestWithCdns` has been cancelled due to `cancellationSignal`
|
|
172
|
+
* being triggered. In that case a `CancellationError` is thrown.
|
|
173
|
+
*
|
|
174
|
+
* - The resource request(s) failed and will not be retried anymore.
|
|
175
|
+
*/
|
|
176
|
+
export function scheduleRequestWithCdns(cdns, cdnPrioritizer, performRequest, options, cancellationSignal) {
|
|
177
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
178
|
+
/**
|
|
179
|
+
* Returns what is now the most prioritary CDN to request the wanted resource.
|
|
180
|
+
*
|
|
181
|
+
* A return value of `null` indicates that the resource can be requested
|
|
182
|
+
* through another mean than by doing an HTTP request.
|
|
183
|
+
*
|
|
184
|
+
* A return value of `undefined` indicates that there's no CDN left to request
|
|
185
|
+
* the resource.
|
|
186
|
+
* @returns {Object|null|undefined}
|
|
187
|
+
*/
|
|
188
|
+
function getCdnToRequest() {
|
|
189
|
+
if (cdns === null) {
|
|
190
|
+
var nullAttemptObject = missedAttempts.get(null);
|
|
191
|
+
if (nullAttemptObject !== undefined && nullAttemptObject.isBlacklisted) {
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
else if (cdnPrioritizer === null) {
|
|
197
|
+
return getPrioritaryRequestableCdnFromSortedList(cdns);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
var prioritized = cdnPrioritizer.getCdnPreferenceForResource(cdns);
|
|
201
|
+
return getPrioritaryRequestableCdnFromSortedList(prioritized);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Perform immediately the request for the given CDN.
|
|
206
|
+
*
|
|
207
|
+
* If it fails, forbid the CDN from being used - optionally and in some
|
|
208
|
+
* conditions, only temporarily, then try the next CDN according to
|
|
209
|
+
* previously-set delays (with a potential sleep before to respect them).
|
|
210
|
+
*
|
|
211
|
+
* Reject if both the request fails and there's no CDN left to use.
|
|
212
|
+
* @param {string|null} cdn
|
|
213
|
+
* @returns {Promise}
|
|
214
|
+
*/
|
|
215
|
+
function requestCdn(cdn) {
|
|
216
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
217
|
+
var res, error_1, currentErrorType, missedAttemptsObj, maxRetry, errorCounter, delay, fuzzedDelay;
|
|
218
|
+
return __generator(this, function (_a) {
|
|
219
|
+
switch (_a.label) {
|
|
220
|
+
case 0:
|
|
221
|
+
_a.trys.push([0, 2, , 3]);
|
|
222
|
+
return [4 /*yield*/, performRequest(cdn, cancellationSignal)];
|
|
223
|
+
case 1:
|
|
224
|
+
res = _a.sent();
|
|
225
|
+
return [2 /*return*/, res];
|
|
226
|
+
case 2:
|
|
227
|
+
error_1 = _a.sent();
|
|
228
|
+
if (TaskCanceller.isCancellationError(error_1)) {
|
|
229
|
+
throw error_1;
|
|
230
|
+
}
|
|
231
|
+
if (cdn !== null && cdnPrioritizer !== null) {
|
|
232
|
+
// We failed requesting the resource on this CDN.
|
|
233
|
+
// Globally give priority to the next CDN through the CdnPrioritizer.
|
|
234
|
+
cdnPrioritizer.downgradeCdn(cdn);
|
|
235
|
+
}
|
|
236
|
+
currentErrorType = getRequestErrorType(error_1);
|
|
237
|
+
missedAttemptsObj = missedAttempts.get(cdn);
|
|
238
|
+
if (missedAttemptsObj === undefined) {
|
|
239
|
+
missedAttemptsObj = { errorCounter: 1,
|
|
240
|
+
lastErrorType: currentErrorType,
|
|
241
|
+
blockedUntil: undefined,
|
|
242
|
+
isBlacklisted: false };
|
|
243
|
+
missedAttempts.set(cdn, missedAttemptsObj);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
if (currentErrorType !== missedAttemptsObj.lastErrorType) {
|
|
247
|
+
missedAttemptsObj.errorCounter = 1;
|
|
248
|
+
missedAttemptsObj.lastErrorType = currentErrorType;
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
missedAttemptsObj.errorCounter++;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
if (!shouldRetry(error_1)) {
|
|
255
|
+
missedAttemptsObj.blockedUntil = undefined;
|
|
256
|
+
missedAttemptsObj.isBlacklisted = true;
|
|
257
|
+
return [2 /*return*/, retryWithNextCdn(error_1)];
|
|
258
|
+
}
|
|
259
|
+
maxRetry = currentErrorType === 2 /* REQUEST_ERROR_TYPES.Offline */ ?
|
|
260
|
+
maxRetryOffline :
|
|
261
|
+
maxRetryRegular;
|
|
262
|
+
if (missedAttemptsObj.errorCounter > maxRetry) {
|
|
263
|
+
missedAttemptsObj.blockedUntil = undefined;
|
|
264
|
+
missedAttemptsObj.isBlacklisted = true;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
errorCounter = missedAttemptsObj.errorCounter;
|
|
268
|
+
delay = Math.min(baseDelay * Math.pow(2, errorCounter - 1), maxDelay);
|
|
269
|
+
fuzzedDelay = getFuzzedDelay(delay);
|
|
270
|
+
missedAttemptsObj.blockedUntil = performance.now() + fuzzedDelay;
|
|
271
|
+
}
|
|
272
|
+
return [2 /*return*/, retryWithNextCdn(error_1)];
|
|
273
|
+
case 3: return [2 /*return*/];
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* After a request error, find the new most prioritary CDN and perform the
|
|
280
|
+
* request with it, optionally after a delay.
|
|
281
|
+
*
|
|
282
|
+
* If there's no CDN left to test, reject the original request error.
|
|
283
|
+
* @param {*} prevRequestError
|
|
284
|
+
* @returns {Promise}
|
|
285
|
+
*/
|
|
286
|
+
function retryWithNextCdn(prevRequestError) {
|
|
287
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
288
|
+
var nextCdn;
|
|
289
|
+
return __generator(this, function (_a) {
|
|
290
|
+
nextCdn = getCdnToRequest();
|
|
291
|
+
if (cancellationSignal.isCancelled) {
|
|
292
|
+
throw cancellationSignal.cancellationError;
|
|
293
|
+
}
|
|
294
|
+
if (nextCdn === undefined) {
|
|
295
|
+
throw prevRequestError;
|
|
296
|
+
}
|
|
297
|
+
onRetry(prevRequestError);
|
|
298
|
+
if (cancellationSignal.isCancelled) {
|
|
299
|
+
throw cancellationSignal.cancellationError;
|
|
300
|
+
}
|
|
301
|
+
return [2 /*return*/, waitPotentialBackoffAndRequest(nextCdn, prevRequestError)];
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Request the corresponding CDN after the optional backoff needed before
|
|
307
|
+
* requesting it.
|
|
308
|
+
*
|
|
309
|
+
* If a new CDN become prioritary in the meantime, request it instead, again
|
|
310
|
+
* awaiting its optional backoff delay if it exists.
|
|
311
|
+
* @param {string|null} nextWantedCdn
|
|
312
|
+
* @param {*} prevRequestError
|
|
313
|
+
* @returns {Promise}
|
|
314
|
+
*/
|
|
315
|
+
function waitPotentialBackoffAndRequest(nextWantedCdn, prevRequestError) {
|
|
316
|
+
var nextCdnAttemptObj = missedAttempts.get(nextWantedCdn);
|
|
317
|
+
if (nextCdnAttemptObj === undefined ||
|
|
318
|
+
nextCdnAttemptObj.blockedUntil === undefined) {
|
|
319
|
+
return requestCdn(nextWantedCdn);
|
|
320
|
+
}
|
|
321
|
+
var now = performance.now();
|
|
322
|
+
var blockedFor = nextCdnAttemptObj.blockedUntil - now;
|
|
323
|
+
if (blockedFor <= 0) {
|
|
324
|
+
return requestCdn(nextWantedCdn);
|
|
325
|
+
}
|
|
326
|
+
var canceller = new TaskCanceller({ cancelOn: cancellationSignal });
|
|
327
|
+
return new Promise(function (res, rej) {
|
|
328
|
+
/* eslint-disable-next-line @typescript-eslint/no-misused-promises */
|
|
329
|
+
cdnPrioritizer === null || cdnPrioritizer === void 0 ? void 0 : cdnPrioritizer.addEventListener("priorityChange", function () {
|
|
330
|
+
var updatedPrioritaryCdn = getCdnToRequest();
|
|
331
|
+
if (cancellationSignal.isCancelled) {
|
|
332
|
+
throw cancellationSignal.cancellationError;
|
|
333
|
+
}
|
|
334
|
+
if (updatedPrioritaryCdn === undefined) {
|
|
335
|
+
return rej(prevRequestError);
|
|
336
|
+
}
|
|
337
|
+
if (updatedPrioritaryCdn !== nextWantedCdn) {
|
|
338
|
+
canceller.cancel();
|
|
339
|
+
waitPotentialBackoffAndRequest(updatedPrioritaryCdn, prevRequestError)
|
|
340
|
+
.then(res, rej);
|
|
341
|
+
}
|
|
342
|
+
}, canceller.signal);
|
|
343
|
+
cancellableSleep(blockedFor, canceller.signal)
|
|
344
|
+
.then(function () { return requestCdn(nextWantedCdn).then(res, rej); }, noop);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Takes in input the list of CDN that can be used to request the resource, in
|
|
349
|
+
* a general preference order.
|
|
350
|
+
*
|
|
351
|
+
* Returns the actual most prioritary Cdn to request, based on the current
|
|
352
|
+
* attempts already done for that resource.
|
|
353
|
+
*
|
|
354
|
+
* Returns `undefined` if there's no Cdn left to request the resource.
|
|
355
|
+
* @param {Array.<Object>}
|
|
356
|
+
* @returns {Object|undefined}
|
|
357
|
+
*/
|
|
358
|
+
function getPrioritaryRequestableCdnFromSortedList(sortedCdns) {
|
|
359
|
+
var _a;
|
|
360
|
+
if (missedAttempts.size === 0) {
|
|
361
|
+
return sortedCdns[0];
|
|
362
|
+
}
|
|
363
|
+
var now = performance.now();
|
|
364
|
+
return (_a = sortedCdns
|
|
365
|
+
.filter(function (c) { var _a; return ((_a = missedAttempts.get(c)) === null || _a === void 0 ? void 0 : _a.isBlacklisted) !== true; })
|
|
366
|
+
.reduce(function (acc, x) {
|
|
367
|
+
var _a;
|
|
368
|
+
var blockedUntil = (_a = missedAttempts.get(x)) === null || _a === void 0 ? void 0 : _a.blockedUntil;
|
|
369
|
+
if (blockedUntil !== undefined && blockedUntil <= now) {
|
|
370
|
+
blockedUntil = undefined;
|
|
371
|
+
}
|
|
372
|
+
if (acc === undefined) {
|
|
373
|
+
return [x, blockedUntil];
|
|
374
|
+
}
|
|
375
|
+
if (blockedUntil === undefined) {
|
|
376
|
+
if (acc[1] === undefined) {
|
|
377
|
+
return acc;
|
|
378
|
+
}
|
|
379
|
+
return [x, undefined];
|
|
380
|
+
}
|
|
381
|
+
return acc[1] === undefined ? acc :
|
|
382
|
+
blockedUntil < acc[1] ? [x, blockedUntil] :
|
|
383
|
+
acc;
|
|
384
|
+
}, undefined)) === null || _a === void 0 ? void 0 : _a[0];
|
|
385
|
+
}
|
|
386
|
+
var baseDelay, maxDelay, maxRetryRegular, maxRetryOffline, onRetry, missedAttempts, initialCdnToRequest;
|
|
387
|
+
return __generator(this, function (_a) {
|
|
388
|
+
if (cancellationSignal.cancellationError !== null) {
|
|
389
|
+
return [2 /*return*/, Promise.reject(cancellationSignal.cancellationError)];
|
|
390
|
+
}
|
|
391
|
+
baseDelay = options.baseDelay, maxDelay = options.maxDelay, maxRetryRegular = options.maxRetryRegular, maxRetryOffline = options.maxRetryOffline, onRetry = options.onRetry;
|
|
392
|
+
if (cdns !== null && cdns.length === 0) {
|
|
393
|
+
log.warn("Fetchers: no CDN given to `scheduleRequestWithCdns`.");
|
|
394
|
+
}
|
|
395
|
+
missedAttempts = new Map();
|
|
396
|
+
initialCdnToRequest = getCdnToRequest();
|
|
397
|
+
if (initialCdnToRequest === undefined) {
|
|
398
|
+
throw new Error("No CDN to request");
|
|
399
|
+
}
|
|
400
|
+
return [2 /*return*/, requestCdn(initialCdnToRequest)];
|
|
401
|
+
});
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Lightweight version of the request algorithm, this time with only a simple
|
|
406
|
+
* Promise given.
|
|
407
|
+
* @param {Function} performRequest
|
|
408
|
+
* @param {Object} options
|
|
409
|
+
* @returns {Promise}
|
|
410
|
+
*/
|
|
411
|
+
export function scheduleRequestPromise(performRequest, options, cancellationSignal) {
|
|
412
|
+
// same than for a single unknown CDN
|
|
413
|
+
return scheduleRequestWithCdns(null, null, performRequest, options, cancellationSignal);
|
|
414
|
+
}
|
|
@@ -15,8 +15,9 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { Observable } from "rxjs";
|
|
17
17
|
import Manifest from "../../manifest";
|
|
18
|
+
import { IReadOnlySharedReference } from "../../utils/reference";
|
|
18
19
|
import { IReadOnlyPlaybackObserver } from "../api";
|
|
19
|
-
import {
|
|
20
|
+
import { IAdaptationChangeEvent, IStreamOrchestratorPlaybackObservation } from "../stream";
|
|
20
21
|
import { IWarningEvent } from "./types";
|
|
21
22
|
/**
|
|
22
23
|
* Observes the position and Adaptations being played and deduce various events
|
|
@@ -27,11 +28,11 @@ import { IWarningEvent } from "./types";
|
|
|
27
28
|
* theoretically playable.
|
|
28
29
|
*
|
|
29
30
|
* @param {Object} manifest
|
|
30
|
-
* @param {
|
|
31
|
+
* @param {Object} lastAdaptationChange
|
|
31
32
|
* @param {Object} playbackObserver
|
|
32
33
|
* @returns {Observable}
|
|
33
34
|
*/
|
|
34
|
-
export default function ContentTimeBoundariesObserver(manifest: Manifest,
|
|
35
|
+
export default function ContentTimeBoundariesObserver(manifest: Manifest, lastAdaptationChange: IReadOnlySharedReference<IAdaptationChangeEvent | null>, playbackObserver: IReadOnlyPlaybackObserver<IContentTimeObserverPlaybackObservation>): Observable<IContentDurationUpdateEvent | IWarningEvent>;
|
|
35
36
|
/**
|
|
36
37
|
* Emitted when the duration of the full content (== the last playable position)
|
|
37
38
|
* has changed.
|
|
@@ -34,11 +34,11 @@ import EVENTS from "./events_generators";
|
|
|
34
34
|
* theoretically playable.
|
|
35
35
|
*
|
|
36
36
|
* @param {Object} manifest
|
|
37
|
-
* @param {
|
|
37
|
+
* @param {Object} lastAdaptationChange
|
|
38
38
|
* @param {Object} playbackObserver
|
|
39
39
|
* @returns {Observable}
|
|
40
40
|
*/
|
|
41
|
-
export default function ContentTimeBoundariesObserver(manifest,
|
|
41
|
+
export default function ContentTimeBoundariesObserver(manifest, lastAdaptationChange, playbackObserver) {
|
|
42
42
|
/**
|
|
43
43
|
* Allows to calculate the minimum and maximum playable position on the
|
|
44
44
|
* whole content.
|
|
@@ -74,27 +74,26 @@ export default function ContentTimeBoundariesObserver(manifest, streams, playbac
|
|
|
74
74
|
maximumPositionCalculator.getMaximumAvailablePosition();
|
|
75
75
|
contentDuration.setValue(duration);
|
|
76
76
|
}), ignoreElements());
|
|
77
|
-
var updateDurationAndTimeBoundsOnTrackChange$ =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
var newDuration = manifest.isDynamic ?
|
|
94
|
-
maximumPositionCalculator.getMaximumAvailablePosition() :
|
|
95
|
-
maximumPositionCalculator.getEndingPosition();
|
|
96
|
-
contentDuration.setValue(newDuration);
|
|
77
|
+
var updateDurationAndTimeBoundsOnTrackChange$ = lastAdaptationChange
|
|
78
|
+
.asObservable().pipe(tap(function (message) {
|
|
79
|
+
if (message === null || !manifest.isLastPeriodKnown) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
var lastPeriod = manifest.periods[manifest.periods.length - 1];
|
|
83
|
+
if (message.value.period.id === (lastPeriod === null || lastPeriod === void 0 ? void 0 : lastPeriod.id)) {
|
|
84
|
+
if (message.value.type === "audio" || message.value.type === "video") {
|
|
85
|
+
if (message.value.type === "audio") {
|
|
86
|
+
maximumPositionCalculator
|
|
87
|
+
.updateLastAudioAdaptation(message.value.adaptation);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
maximumPositionCalculator
|
|
91
|
+
.updateLastVideoAdaptation(message.value.adaptation);
|
|
97
92
|
}
|
|
93
|
+
var newDuration = manifest.isDynamic ?
|
|
94
|
+
maximumPositionCalculator.getMaximumAvailablePosition() :
|
|
95
|
+
maximumPositionCalculator.getEndingPosition();
|
|
96
|
+
contentDuration.setValue(newDuration);
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
99
|
}), ignoreElements());
|
|
@@ -15,10 +15,11 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { Observable } from "rxjs";
|
|
17
17
|
import { IKeySystemOption } from "../../public_types";
|
|
18
|
+
import { ITransportPipelines } from "../../transports";
|
|
18
19
|
import { IReadOnlySharedReference } from "../../utils/reference";
|
|
19
20
|
import { IAdaptiveRepresentationSelectorArguments } from "../adaptive";
|
|
20
21
|
import { PlaybackObserver } from "../api";
|
|
21
|
-
import { IManifestFetcherParsedResult, IManifestFetcherWarningEvent, ManifestFetcher
|
|
22
|
+
import { IManifestFetcherParsedResult, IManifestFetcherWarningEvent, ManifestFetcher } from "../fetchers";
|
|
22
23
|
import { ITextTrackSegmentBufferOptions } from "../segment_buffers";
|
|
23
24
|
import { IAudioTrackSwitchingMode } from "../stream";
|
|
24
25
|
import { IInitialTimeOptions } from "./get_initial_time";
|
|
@@ -65,8 +66,21 @@ export interface IInitializeArguments {
|
|
|
65
66
|
mediaElement: HTMLMediaElement;
|
|
66
67
|
/** Limit the frequency of Manifest updates. */
|
|
67
68
|
minimumManifestUpdateInterval: number;
|
|
68
|
-
/** Interface allowing to
|
|
69
|
-
|
|
69
|
+
/** Interface allowing to interact with the transport protocol */
|
|
70
|
+
transport: ITransportPipelines;
|
|
71
|
+
/** Configuration for the segment requesting logic. */
|
|
72
|
+
segmentRequestOptions: {
|
|
73
|
+
/** Maximum number of time a request on error will be retried. */
|
|
74
|
+
regularError: number | undefined;
|
|
75
|
+
/** Maximum number of time a request be retried when the user is offline. */
|
|
76
|
+
offlineError: number | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* Amount of time after which a request should be aborted.
|
|
79
|
+
* `undefined` indicates that a default value is wanted.
|
|
80
|
+
* `-1` indicates no timeout.
|
|
81
|
+
*/
|
|
82
|
+
requestTimeout: number | undefined;
|
|
83
|
+
};
|
|
70
84
|
/** Emit the playback rate (speed) set by the user. */
|
|
71
85
|
speed: IReadOnlySharedReference<number>;
|
|
72
86
|
/** The configured starting position. */
|
|
@@ -105,4 +119,4 @@ export interface IInitializeArguments {
|
|
|
105
119
|
* @param {Object} args
|
|
106
120
|
* @returns {Observable}
|
|
107
121
|
*/
|
|
108
|
-
export default function InitializeOnMediaSource({ adaptiveOptions, autoPlay, bufferOptions, keySystems, lowLatencyMode, manifest$, manifestFetcher, mediaElement, minimumManifestUpdateInterval, playbackObserver,
|
|
122
|
+
export default function InitializeOnMediaSource({ adaptiveOptions, autoPlay, bufferOptions, keySystems, lowLatencyMode, manifest$, manifestFetcher, mediaElement, minimumManifestUpdateInterval, playbackObserver, segmentRequestOptions, speed, startAt, transport, textTrackOptions }: IInitializeArguments): Observable<IInitEvent>;
|
|
@@ -21,8 +21,10 @@ import deferSubscriptions from "../../utils/defer_subscriptions";
|
|
|
21
21
|
import { fromEvent } from "../../utils/event_emitter";
|
|
22
22
|
import filterMap from "../../utils/filter_map";
|
|
23
23
|
import objectAssign from "../../utils/object_assign";
|
|
24
|
+
import TaskCanceller from "../../utils/task_canceller";
|
|
24
25
|
import AdaptiveRepresentationSelector from "../adaptive";
|
|
25
26
|
import { getCurrentKeySystem, } from "../decrypt";
|
|
27
|
+
import { SegmentFetcherCreator, } from "../fetchers";
|
|
26
28
|
import openMediaSource from "./create_media_source";
|
|
27
29
|
import EVENTS from "./events_generators";
|
|
28
30
|
import getInitialTime from "./get_initial_time";
|
|
@@ -62,9 +64,10 @@ import throwOnMediaError from "./throw_on_media_error";
|
|
|
62
64
|
* @returns {Observable}
|
|
63
65
|
*/
|
|
64
66
|
export default function InitializeOnMediaSource(_a) {
|
|
65
|
-
var adaptiveOptions = _a.adaptiveOptions, autoPlay = _a.autoPlay, bufferOptions = _a.bufferOptions, keySystems = _a.keySystems, lowLatencyMode = _a.lowLatencyMode, manifest$ = _a.manifest$, manifestFetcher = _a.manifestFetcher, mediaElement = _a.mediaElement, minimumManifestUpdateInterval = _a.minimumManifestUpdateInterval, playbackObserver = _a.playbackObserver,
|
|
67
|
+
var adaptiveOptions = _a.adaptiveOptions, autoPlay = _a.autoPlay, bufferOptions = _a.bufferOptions, keySystems = _a.keySystems, lowLatencyMode = _a.lowLatencyMode, manifest$ = _a.manifest$, manifestFetcher = _a.manifestFetcher, mediaElement = _a.mediaElement, minimumManifestUpdateInterval = _a.minimumManifestUpdateInterval, playbackObserver = _a.playbackObserver, segmentRequestOptions = _a.segmentRequestOptions, speed = _a.speed, startAt = _a.startAt, transport = _a.transport, textTrackOptions = _a.textTrackOptions;
|
|
66
68
|
/** Choose the right "Representation" for a given "Adaptation". */
|
|
67
69
|
var representationEstimator = AdaptiveRepresentationSelector(adaptiveOptions);
|
|
70
|
+
var playbackCanceller = new TaskCanceller();
|
|
68
71
|
/**
|
|
69
72
|
* Create and open a new MediaSource object on the given media element on
|
|
70
73
|
* subscription.
|
|
@@ -108,6 +111,10 @@ export default function InitializeOnMediaSource(_a) {
|
|
|
108
111
|
log.debug("Init: Calculating initial time");
|
|
109
112
|
var initialTime = getInitialTime(manifest, lowLatencyMode, startAt);
|
|
110
113
|
log.debug("Init: Initial time calculated:", initialTime);
|
|
114
|
+
var requestOptions = { lowLatencyMode: lowLatencyMode, requestTimeout: segmentRequestOptions.requestTimeout,
|
|
115
|
+
maxRetryRegular: segmentRequestOptions.regularError,
|
|
116
|
+
maxRetryOffline: segmentRequestOptions.offlineError };
|
|
117
|
+
var segmentFetcherCreator = new SegmentFetcherCreator(transport, requestOptions, playbackCanceller.signal);
|
|
111
118
|
var mediaSourceLoader = createMediaSourceLoader({
|
|
112
119
|
bufferOptions: objectAssign({ textTrackOptions: textTrackOptions, drmSystemId: drmSystemId }, bufferOptions),
|
|
113
120
|
manifest: manifest,
|
|
@@ -189,5 +196,6 @@ export default function InitializeOnMediaSource(_a) {
|
|
|
189
196
|
return observableMerge(handleReloads$, currentLoad$);
|
|
190
197
|
}
|
|
191
198
|
}));
|
|
192
|
-
return observableMerge(loadContent$, mediaError$, drmEvents$.pipe(ignoreElements()))
|
|
199
|
+
return observableMerge(loadContent$, mediaError$, drmEvents$.pipe(ignoreElements()))
|
|
200
|
+
.pipe(finalize(function () { playbackCanceller.cancel(); }));
|
|
193
201
|
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
import { EMPTY, filter, finalize, ignoreElements, merge as observableMerge, mergeMap, of as observableOf, Subject, switchMap, takeUntil, throwError, } from "rxjs";
|
|
17
17
|
import { MediaError } from "../../errors";
|
|
18
18
|
import log from "../../log";
|
|
19
|
+
import createSharedReference from "../../utils/reference";
|
|
19
20
|
import SegmentBuffersStore from "../segment_buffers";
|
|
20
21
|
import StreamOrchestrator from "../stream";
|
|
21
22
|
import ContentTimeBoundariesObserver from "./content_time_boundaries_observer";
|
|
@@ -63,6 +64,8 @@ export default function createMediaSourceLoader(_a) {
|
|
|
63
64
|
var discontinuityUpdate$ = new Subject();
|
|
64
65
|
/** Emits event when streams are "locked", meaning they cannot load segments. */
|
|
65
66
|
var lockedStream$ = new Subject();
|
|
67
|
+
/** Emit each time a new Adaptation is considered by the `StreamOrchestrator`. */
|
|
68
|
+
var lastAdaptationChange = createSharedReference(null);
|
|
66
69
|
// Creates Observable which will manage every Stream for the given Content.
|
|
67
70
|
var streams$ = StreamOrchestrator({ manifest: manifest, initialPeriod: initialPeriod }, streamObserver, representationEstimator, segmentBuffersStore, segmentFetcherCreator, bufferOptions).pipe(mergeMap(function (evt) {
|
|
68
71
|
switch (evt.type) {
|
|
@@ -80,11 +83,14 @@ export default function createMediaSourceLoader(_a) {
|
|
|
80
83
|
case "locked-stream":
|
|
81
84
|
lockedStream$.next(evt.value);
|
|
82
85
|
return EMPTY;
|
|
86
|
+
case "adaptationChange":
|
|
87
|
+
lastAdaptationChange.setValue(evt);
|
|
88
|
+
return observableOf(evt);
|
|
83
89
|
default:
|
|
84
90
|
return observableOf(evt);
|
|
85
91
|
}
|
|
86
92
|
}));
|
|
87
|
-
var contentTimeObserver = ContentTimeBoundariesObserver(manifest,
|
|
93
|
+
var contentTimeObserver = ContentTimeBoundariesObserver(manifest, lastAdaptationChange, streamObserver)
|
|
88
94
|
.pipe(mergeMap(function (evt) {
|
|
89
95
|
switch (evt.type) {
|
|
90
96
|
case "contentDurationUpdate":
|
|
@@ -180,7 +180,9 @@ export default function StallAvoider(playbackObserver, manifest, speed, lockedSt
|
|
|
180
180
|
}
|
|
181
181
|
/** Position at which data is awaited. */
|
|
182
182
|
var stalledPosition = rebuffering.position;
|
|
183
|
-
if (stalledPosition !== null &&
|
|
183
|
+
if (stalledPosition !== null &&
|
|
184
|
+
stalledPosition !== undefined &&
|
|
185
|
+
speed.getValue() > 0) {
|
|
184
186
|
var skippableDiscontinuity = findSeekableDiscontinuity(discontinuitiesStore, manifest, stalledPosition);
|
|
185
187
|
if (skippableDiscontinuity !== null) {
|
|
186
188
|
var realSeekTime = skippableDiscontinuity + 0.001;
|
|
@@ -336,6 +336,17 @@ declare const DEFAULT_CONFIG: {
|
|
|
336
336
|
* @type Number
|
|
337
337
|
*/
|
|
338
338
|
DEFAULT_MAX_MANIFEST_REQUEST_RETRY: number;
|
|
339
|
+
/**
|
|
340
|
+
* Default delay, in seconds, during which a CDN will be "downgraded".
|
|
341
|
+
*
|
|
342
|
+
* For example in case of media content being available on multiple CDNs, the
|
|
343
|
+
* RxPlayer may decide that a CDN is less reliable (for example, it returned a
|
|
344
|
+
* server error) and should thus be avoided, at least for some time
|
|
345
|
+
*
|
|
346
|
+
* This value is the amount of time this CDN will be "less considered" than the
|
|
347
|
+
* alternatives.
|
|
348
|
+
*/
|
|
349
|
+
DEFAULT_CDN_DOWNGRADE_TIME: number;
|
|
339
350
|
/**
|
|
340
351
|
* The default number of times a segment request will be re-performed when
|
|
341
352
|
* on error which justify a retry.
|