rx-player 3.30.0-dev.2023020100 → 3.30.0-dev.2023022200
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 +2 -1
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
- package/dist/_esm5.processed/compat/event_listeners.js +1 -1
- package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +4 -2
- package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
- package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
- package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
- package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +105 -0
- package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
- package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
- package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
- package/dist/_esm5.processed/core/api/debug/index.js +2 -0
- package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/modules/general_info.js +199 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
- package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
- package/dist/_esm5.processed/core/api/debug/render.js +32 -0
- package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
- package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
- package/dist/_esm5.processed/core/api/playback_observer.js +3 -2
- package/dist/_esm5.processed/core/api/public_api.d.ts +3 -0
- package/dist/_esm5.processed/core/api/public_api.js +24 -13
- package/dist/_esm5.processed/core/api/utils.js +3 -3
- package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +5 -5
- package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
- package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
- package/dist/_esm5.processed/core/decrypt/session_events_listener.js +37 -47
- package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
- package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
- package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
- package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
- package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
- package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +6 -5
- package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
- package/dist/_esm5.processed/core/init/media_source_content_initializer.js +27 -31
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +2 -2
- package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
- package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
- package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
- package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
- package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
- package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +23 -19
- package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
- package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +7 -10
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +4 -2
- package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
- package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +10 -11
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +26 -12
- package/dist/_esm5.processed/core/stream/period/period_stream.js +11 -10
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -15
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +1 -1
- package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
- package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
- package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
- package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
- package/dist/_esm5.processed/experimental/features/index.js +1 -0
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +5 -4
- package/dist/_esm5.processed/features/features_object.js +1 -0
- package/dist/_esm5.processed/features/initialize_features.js +13 -10
- package/dist/_esm5.processed/features/types.d.ts +3 -0
- package/dist/_esm5.processed/manifest/adaptation.js +4 -0
- package/dist/_esm5.processed/manifest/manifest.js +2 -0
- package/dist/_esm5.processed/manifest/representation.js +11 -4
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
- package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
- package/dist/_esm5.processed/public_types.d.ts +1 -0
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +13 -12
- package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
- package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
- package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
- package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
- package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
- package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
- package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
- package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
- package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
- package/dist/_esm5.processed/utils/reference.js +6 -0
- package/dist/_esm5.processed/utils/request/xhr.js +1 -1
- package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
- package/dist/_esm5.processed/utils/task_canceller.js +55 -22
- package/dist/rx-player.js +739 -626
- package/dist/rx-player.min.js +1 -1
- package/jest.config.js +1 -0
- package/package.json +18 -18
- package/scripts/build/constants.d.ts +1 -0
- package/scripts/report_build_sizes +4 -0
- package/sonar-project.properties +1 -1
- package/src/compat/eme/close_session.ts +2 -2
- package/src/compat/event_listeners.ts +1 -1
- package/src/core/adaptive/adaptive_representation_selector.ts +4 -2
- package/src/core/api/debug/buffer_graph.ts +247 -0
- package/src/core/api/debug/buffer_size_graph.ts +131 -0
- package/src/core/api/debug/constants.ts +2 -0
- package/src/core/api/debug/index.ts +3 -0
- package/src/core/api/debug/modules/general_info.ts +204 -0
- package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
- package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
- package/src/core/api/debug/render.ts +40 -0
- package/src/core/api/debug/utils.ts +103 -0
- package/src/core/api/playback_observer.ts +4 -2
- package/src/core/api/public_api.ts +27 -13
- package/src/core/api/utils.ts +3 -3
- package/src/core/decrypt/attach_media_keys.ts +1 -1
- package/src/core/decrypt/content_decryptor.ts +1 -1
- package/src/core/decrypt/session_events_listener.ts +37 -49
- package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
- package/src/core/fetchers/cdn_prioritizer.ts +18 -9
- package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
- package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
- package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
- package/src/core/fetchers/utils/schedule_request.ts +6 -5
- package/src/core/init/directfile_content_initializer.ts +1 -1
- package/src/core/init/media_source_content_initializer.ts +37 -41
- package/src/core/init/utils/content_time_boundaries_observer.ts +2 -2
- package/src/core/init/utils/create_media_source.ts +4 -16
- package/src/core/init/utils/end_of_stream.ts +6 -3
- package/src/core/init/utils/get_loaded_reference.ts +2 -1
- package/src/core/init/utils/initial_seek_and_play.ts +9 -5
- package/src/core/init/utils/initialize_content_decryption.ts +2 -1
- package/src/core/init/utils/media_duration_updater.ts +25 -20
- package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
- package/src/core/init/utils/throw_on_media_error.ts +1 -1
- package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +7 -11
- package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +4 -2
- package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
- package/src/core/stream/adaptation/adaptation_stream.ts +10 -11
- package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -12
- package/src/core/stream/period/period_stream.ts +11 -10
- package/src/core/stream/representation/representation_stream.ts +15 -15
- package/src/core/stream/representation/utils/append_segment_to_buffer.ts +1 -1
- package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
- package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
- package/src/experimental/features/debug_element.ts +13 -0
- package/src/experimental/features/index.ts +1 -0
- package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +5 -4
- package/src/features/__tests__/initialize_features.test.ts +11 -0
- package/src/features/features_object.ts +1 -0
- package/src/features/initialize_features.ts +15 -10
- package/src/features/types.ts +9 -0
- package/src/manifest/adaptation.ts +7 -0
- package/src/manifest/manifest.ts +4 -0
- package/src/manifest/representation.ts +10 -4
- package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
- package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
- package/src/public_types.ts +1 -0
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +30 -24
- package/src/transports/dash/low_latency_segment_loader.ts +2 -2
- package/src/transports/dash/manifest_parser.ts +1 -1
- package/src/transports/dash/segment_loader.ts +4 -4
- package/src/transports/local/segment_loader.ts +14 -30
- package/src/transports/smooth/segment_loader.ts +4 -4
- package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
- package/src/typings/globals.d.ts +2 -0
- package/src/utils/cancellable_sleep.ts +5 -14
- package/src/utils/create_cancellable_promise.ts +69 -0
- package/src/utils/reference.ts +6 -0
- package/src/utils/request/xhr.ts +1 -1
- package/src/utils/task_canceller.ts +63 -34
package/src/core/api/utils.ts
CHANGED
|
@@ -48,14 +48,14 @@ export function emitSeekEvents(
|
|
|
48
48
|
onSeeked: () => void,
|
|
49
49
|
cancelSignal : CancellationSignal
|
|
50
50
|
) : void {
|
|
51
|
-
if (cancelSignal.isCancelled || mediaElement === null) {
|
|
51
|
+
if (cancelSignal.isCancelled() || mediaElement === null) {
|
|
52
52
|
return ;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
let wasSeeking = playbackObserver.getReference().getValue().seeking;
|
|
56
56
|
if (wasSeeking) {
|
|
57
57
|
onSeeking();
|
|
58
|
-
if (cancelSignal.isCancelled) {
|
|
58
|
+
if (cancelSignal.isCancelled()) {
|
|
59
59
|
return;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -94,7 +94,7 @@ export function constructPlayerStateReference(
|
|
|
94
94
|
initializer.addEventListener("loaded", () => {
|
|
95
95
|
if (playerStateRef.getValue() === PLAYER_STATES.LOADING) {
|
|
96
96
|
playerStateRef.setValue(PLAYER_STATES.LOADED);
|
|
97
|
-
if (!cancelSignal.isCancelled) {
|
|
97
|
+
if (!cancelSignal.isCancelled()) {
|
|
98
98
|
const newState = getLoadedContentState(mediaElement, null);
|
|
99
99
|
if (newState !== PLAYER_STATES.PAUSED) {
|
|
100
100
|
playerStateRef.setValue(newState);
|
|
@@ -62,7 +62,7 @@ export default async function attachMediaKeys(
|
|
|
62
62
|
|
|
63
63
|
// If this task has been cancelled while we were closing previous sessions,
|
|
64
64
|
// stop now (and thus avoid setting the new media keys);
|
|
65
|
-
if (cancelSignal.isCancelled) {
|
|
65
|
+
if (cancelSignal.isCancelled()) {
|
|
66
66
|
throw cancelSignal.cancellationError;
|
|
67
67
|
}
|
|
68
68
|
|
|
@@ -727,7 +727,7 @@ export default class ContentDecryptor extends EventEmitter<IContentDecryptorEven
|
|
|
727
727
|
* formatted and sent in an "error" event.
|
|
728
728
|
*/
|
|
729
729
|
private _onFatalError(err : unknown) : void {
|
|
730
|
-
if (this._canceller.isUsed) {
|
|
730
|
+
if (this._canceller.isUsed()) {
|
|
731
731
|
return;
|
|
732
732
|
}
|
|
733
733
|
const formattedErr = err instanceof Error ?
|
|
@@ -58,13 +58,14 @@ export default function SessionEventsListener(
|
|
|
58
58
|
const { getLicenseConfig = {} } = keySystemOptions;
|
|
59
59
|
|
|
60
60
|
/** Allows to manually cancel everything the `SessionEventsListener` is doing. */
|
|
61
|
-
const manualCanceller = new TaskCanceller(
|
|
61
|
+
const manualCanceller = new TaskCanceller();
|
|
62
|
+
manualCanceller.linkToSignal(cancelSignal);
|
|
62
63
|
|
|
63
64
|
if (!isNullOrUndefined(session.closed)) {
|
|
64
65
|
session.closed
|
|
65
66
|
.then(() => manualCanceller.cancel())
|
|
66
67
|
.catch((err) => { // Should never happen
|
|
67
|
-
if (cancelSignal.isCancelled) {
|
|
68
|
+
if (cancelSignal.isCancelled()) {
|
|
68
69
|
return;
|
|
69
70
|
}
|
|
70
71
|
manualCanceller.cancel();
|
|
@@ -79,8 +80,8 @@ export default function SessionEventsListener(
|
|
|
79
80
|
|
|
80
81
|
onKeyStatusesChange(session, (keyStatusesEvent) => {
|
|
81
82
|
handleKeyStatusesChangeEvent(keyStatusesEvent as Event).catch(error => {
|
|
82
|
-
if (cancelSignal.isCancelled ||
|
|
83
|
-
(manualCanceller.isUsed && error instanceof CancellationSignal))
|
|
83
|
+
if (cancelSignal.isCancelled() ||
|
|
84
|
+
(manualCanceller.isUsed() && error instanceof CancellationSignal))
|
|
84
85
|
{
|
|
85
86
|
return;
|
|
86
87
|
}
|
|
@@ -104,7 +105,7 @@ export default function SessionEventsListener(
|
|
|
104
105
|
backoffOptions,
|
|
105
106
|
manualCanceller.signal)
|
|
106
107
|
.then((licenseObject) => {
|
|
107
|
-
if (manualCanceller.isUsed) {
|
|
108
|
+
if (manualCanceller.isUsed()) {
|
|
108
109
|
return Promise.resolve();
|
|
109
110
|
}
|
|
110
111
|
if (isNullOrUndefined(licenseObject)) {
|
|
@@ -114,7 +115,7 @@ export default function SessionEventsListener(
|
|
|
114
115
|
}
|
|
115
116
|
})
|
|
116
117
|
.catch((err : unknown) => {
|
|
117
|
-
if (manualCanceller.isUsed) {
|
|
118
|
+
if (manualCanceller.isUsed()) {
|
|
118
119
|
return;
|
|
119
120
|
}
|
|
120
121
|
manualCanceller.cancel();
|
|
@@ -133,6 +134,8 @@ export default function SessionEventsListener(
|
|
|
133
134
|
});
|
|
134
135
|
}, manualCanceller.signal);
|
|
135
136
|
|
|
137
|
+
checkAndHandleCurrentKeyStatuses();
|
|
138
|
+
return;
|
|
136
139
|
/**
|
|
137
140
|
* @param {Event} keyStatusesEvent
|
|
138
141
|
* @returns {Promise}
|
|
@@ -144,22 +147,22 @@ export default function SessionEventsListener(
|
|
|
144
147
|
|
|
145
148
|
await Promise.all([
|
|
146
149
|
runOnKeyStatusesChangeCallback(),
|
|
147
|
-
Promise.resolve(
|
|
150
|
+
Promise.resolve(checkAndHandleCurrentKeyStatuses()),
|
|
148
151
|
]);
|
|
149
152
|
|
|
150
153
|
async function runOnKeyStatusesChangeCallback() {
|
|
151
|
-
if (manualCanceller.isUsed) {
|
|
154
|
+
if (manualCanceller.isUsed()) {
|
|
152
155
|
return;
|
|
153
156
|
}
|
|
154
157
|
if (typeof keySystemOptions.onKeyStatusesChange === "function") {
|
|
155
158
|
let ret;
|
|
156
159
|
try {
|
|
157
160
|
ret = await keySystemOptions.onKeyStatusesChange(keyStatusesEvent, session);
|
|
158
|
-
if (manualCanceller.isUsed) {
|
|
161
|
+
if (manualCanceller.isUsed()) {
|
|
159
162
|
return;
|
|
160
163
|
}
|
|
161
164
|
} catch (error) {
|
|
162
|
-
if (cancelSignal.isCancelled) {
|
|
165
|
+
if (cancelSignal.isCancelled()) {
|
|
163
166
|
return;
|
|
164
167
|
}
|
|
165
168
|
const err = new EncryptedMediaError("KEY_STATUS_CHANGE_ERROR",
|
|
@@ -178,41 +181,41 @@ export default function SessionEventsListener(
|
|
|
178
181
|
}
|
|
179
182
|
}
|
|
180
183
|
}
|
|
184
|
+
}
|
|
181
185
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
}
|
|
186
|
+
/**
|
|
187
|
+
* Check current MediaKeyStatus for each key in the given MediaKeySession and:
|
|
188
|
+
* - throw if at least one status is a non-recoverable error
|
|
189
|
+
* - call warning callback for recoverable errors
|
|
190
|
+
* - call onKeyUpdate callback when the MediaKeyStatus of any key is updated
|
|
191
|
+
*/
|
|
192
|
+
function checkAndHandleCurrentKeyStatuses() : void {
|
|
193
|
+
if (manualCanceller.isUsed() || session.keyStatuses.size === 0) {
|
|
194
|
+
return ;
|
|
195
|
+
}
|
|
196
|
+
const { warning, blacklistedKeyIds, whitelistedKeyIds } =
|
|
197
|
+
checkKeyStatuses(session, keySystemOptions, keySystem);
|
|
198
|
+
if (warning !== undefined) {
|
|
199
|
+
callbacks.onWarning(warning);
|
|
200
|
+
if (manualCanceller.isUsed()) {
|
|
201
|
+
return;
|
|
199
202
|
}
|
|
200
|
-
callbacks.onKeyUpdate({ whitelistedKeyIds, blacklistedKeyIds });
|
|
201
203
|
}
|
|
204
|
+
callbacks.onKeyUpdate({ whitelistedKeyIds, blacklistedKeyIds });
|
|
202
205
|
}
|
|
203
206
|
|
|
204
207
|
function runGetLicense(
|
|
205
208
|
message : Uint8Array,
|
|
206
209
|
messageType : MediaKeyMessageType
|
|
207
210
|
) : Promise<BufferSource | null> {
|
|
208
|
-
|
|
211
|
+
let timeoutId : number | undefined;
|
|
212
|
+
return new Promise<BufferSource | null>((res, rej) => {
|
|
209
213
|
log.debug("DRM: Calling `getLicense`", messageType);
|
|
210
214
|
const getLicense = keySystemOptions.getLicense(message, messageType);
|
|
211
215
|
const getLicenseTimeout = isNullOrUndefined(getLicenseConfig.timeout) ?
|
|
212
216
|
10 * 1000 :
|
|
213
217
|
getLicenseConfig.timeout;
|
|
214
218
|
|
|
215
|
-
let timeoutId : number | undefined;
|
|
216
219
|
if (getLicenseTimeout >= 0) {
|
|
217
220
|
timeoutId = setTimeout(() => {
|
|
218
221
|
rej(new GetLicenseTimeoutError(
|
|
@@ -220,25 +223,10 @@ export default function SessionEventsListener(
|
|
|
220
223
|
));
|
|
221
224
|
}, getLicenseTimeout) as unknown as number;
|
|
222
225
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
clearTimeoutIfOne();
|
|
228
|
-
res(val);
|
|
229
|
-
},
|
|
230
|
-
(err) => {
|
|
231
|
-
clearTimeoutIfOne();
|
|
232
|
-
rej(err);
|
|
233
|
-
});
|
|
234
|
-
} catch (err) {
|
|
235
|
-
clearTimeoutIfOne();
|
|
236
|
-
rej(err);
|
|
237
|
-
}
|
|
238
|
-
function clearTimeoutIfOne() {
|
|
239
|
-
if (timeoutId !== undefined) {
|
|
240
|
-
clearTimeout(timeoutId);
|
|
241
|
-
}
|
|
226
|
+
Promise.resolve(getLicense).then(res, rej);
|
|
227
|
+
}).finally(() => {
|
|
228
|
+
if (timeoutId !== undefined) {
|
|
229
|
+
clearTimeout(timeoutId);
|
|
242
230
|
}
|
|
243
231
|
});
|
|
244
232
|
}
|
|
@@ -18,6 +18,7 @@ import { ICustomMediaKeySession } from "../../../compat";
|
|
|
18
18
|
/* eslint-disable-next-line max-len */
|
|
19
19
|
import getUUIDKidFromKeyStatusKID from "../../../compat/eme/get_uuid_kid_from_keystatus_kid";
|
|
20
20
|
import { EncryptedMediaError } from "../../../errors";
|
|
21
|
+
import log from "../../../log";
|
|
21
22
|
import {
|
|
22
23
|
IEncryptedMediaErrorKeyStatusObject,
|
|
23
24
|
IKeySystemOption,
|
|
@@ -109,6 +110,11 @@ export default function checkKeyStatuses(
|
|
|
109
110
|
new Uint8Array(keyStatusKeyId));
|
|
110
111
|
|
|
111
112
|
const keyStatusObj = { keyId: keyId.buffer, keyStatus };
|
|
113
|
+
|
|
114
|
+
if (log.hasLevel("DEBUG")) {
|
|
115
|
+
log.debug(`DRM: key status update (${bytesToHex(keyId)}): ${keyStatus}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
112
118
|
switch (keyStatus) {
|
|
113
119
|
case KEY_STATUSES.EXPIRED: {
|
|
114
120
|
const error = new EncryptedMediaError(
|
|
@@ -16,19 +16,22 @@
|
|
|
16
16
|
|
|
17
17
|
import config from "../../config";
|
|
18
18
|
import { ICdnMetadata } from "../../parsers/manifest";
|
|
19
|
-
import { IPlayerError } from "../../public_types";
|
|
20
19
|
import arrayFindIndex from "../../utils/array_find_index";
|
|
21
20
|
import EventEmitter from "../../utils/event_emitter";
|
|
22
21
|
import { CancellationSignal } from "../../utils/task_canceller";
|
|
23
22
|
|
|
24
23
|
/**
|
|
25
|
-
* Class signaling the priority between multiple CDN available for
|
|
26
|
-
* resource.
|
|
24
|
+
* Class storing and signaling the priority between multiple CDN available for
|
|
25
|
+
* any given resource.
|
|
27
26
|
*
|
|
28
|
-
* This class
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
27
|
+
* This class was first created to implement the complexities behind
|
|
28
|
+
* Content Steering features, though its handling hasn't been added yet as we
|
|
29
|
+
* wait for its specification to be both standardized and relied on in the wild.
|
|
30
|
+
* In the meantime, it acts as an abstraction for the simple concept of
|
|
31
|
+
* avoiding to request a CDN for any segment when an issue is encountered with
|
|
32
|
+
* one (e.g. HTTP 500 statuses) and several CDN exist for a given resource. It
|
|
33
|
+
* should be noted that this is also one of the planified features of the
|
|
34
|
+
* Content Steering specification.
|
|
32
35
|
*
|
|
33
36
|
* @class CdnPrioritizer
|
|
34
37
|
*/
|
|
@@ -41,7 +44,7 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
|
|
|
41
44
|
private _downgradedCdnList : {
|
|
42
45
|
/**
|
|
43
46
|
* Metadata of downgraded CDN, sorted by the time at which they have
|
|
44
|
-
* been downgraded.
|
|
47
|
+
* been downgraded ascending.
|
|
45
48
|
*/
|
|
46
49
|
metadata : ICdnMetadata[];
|
|
47
50
|
/**
|
|
@@ -174,8 +177,14 @@ export default class CdnPrioritizer extends EventEmitter<ICdnPrioritizerEvents>
|
|
|
174
177
|
}
|
|
175
178
|
}
|
|
176
179
|
|
|
180
|
+
/** Events sent by a `CdnPrioritizer` */
|
|
177
181
|
export interface ICdnPrioritizerEvents {
|
|
178
|
-
|
|
182
|
+
/**
|
|
183
|
+
* The priority of one or several CDN changed.
|
|
184
|
+
*
|
|
185
|
+
* You might want to re-check if a CDN should still be used when this event
|
|
186
|
+
* is triggered.
|
|
187
|
+
*/
|
|
179
188
|
priorityChange : null;
|
|
180
189
|
}
|
|
181
190
|
|
|
@@ -155,7 +155,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
155
155
|
manifestProm
|
|
156
156
|
.then((val : IManifestFetcherParsedResult) => {
|
|
157
157
|
this.trigger("manifestReady", val.manifest);
|
|
158
|
-
if (!this._canceller.isUsed) {
|
|
158
|
+
if (!this._canceller.isUsed()) {
|
|
159
159
|
this._recursivelyRefreshManifest(val.manifest, val);
|
|
160
160
|
}
|
|
161
161
|
})
|
|
@@ -362,7 +362,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
362
362
|
*/
|
|
363
363
|
function onWarnings(warnings : Error[]) : void {
|
|
364
364
|
for (const warning of warnings) {
|
|
365
|
-
if (cancelSignal.isCancelled) {
|
|
365
|
+
if (cancelSignal.isCancelled()) {
|
|
366
366
|
return;
|
|
367
367
|
}
|
|
368
368
|
const formattedError = formatError(warning, {
|
|
@@ -470,7 +470,8 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
470
470
|
* be effectively considered.
|
|
471
471
|
* `nextRefreshCanceller` will allow to cancel every other when one is triggered.
|
|
472
472
|
*/
|
|
473
|
-
const nextRefreshCanceller = new TaskCanceller(
|
|
473
|
+
const nextRefreshCanceller = new TaskCanceller();
|
|
474
|
+
nextRefreshCanceller.linkToSignal(this._canceller.signal);
|
|
474
475
|
|
|
475
476
|
/* Function to manually schedule a Manifest refresh */
|
|
476
477
|
this.scheduleManualRefresh = (settings : IManifestRefreshSettings) => {
|
|
@@ -669,7 +670,7 @@ export default class ManifestFetcher extends EventEmitter<IManifestFetcherEvent>
|
|
|
669
670
|
}
|
|
670
671
|
|
|
671
672
|
private _onFatalError(err : unknown) : void {
|
|
672
|
-
if (this._canceller.isUsed) {
|
|
673
|
+
if (this._canceller.isUsed()) {
|
|
673
674
|
return;
|
|
674
675
|
}
|
|
675
676
|
this.trigger("error", err);
|
|
@@ -64,10 +64,22 @@ const generateRequestID = idGenerator();
|
|
|
64
64
|
* An `ISegmentFetcher` also implements a retry mechanism, based on the given
|
|
65
65
|
* `options` argument, which may retry a segment request when it fails.
|
|
66
66
|
*
|
|
67
|
-
* @param {string} bufferType
|
|
68
|
-
*
|
|
69
|
-
* @param {Object}
|
|
70
|
-
*
|
|
67
|
+
* @param {string} bufferType - Type of buffer concerned (e.g. `"audio"`,
|
|
68
|
+
* `"video"`, `"text" etc.)
|
|
69
|
+
* @param {Object} pipeline - The transport-specific logic allowing to load
|
|
70
|
+
* segments of the given buffer type and transport protocol (e.g. DASH).
|
|
71
|
+
* @param {Object|null} cdnPrioritizer - Abstraction allowing to synchronize,
|
|
72
|
+
* update and keep track of the priorization of the CDN to use to load any given
|
|
73
|
+
* segment, in cases where multiple ones are available.
|
|
74
|
+
*
|
|
75
|
+
* Can be set to `null` in which case a minimal priorization logic will be used
|
|
76
|
+
* instead.
|
|
77
|
+
* @param {Object} lifecycleCallbacks - Callbacks that can be registered to be
|
|
78
|
+
* informed when new requests are made, ended, new metrics are available etc.
|
|
79
|
+
* This should be mainly useful to implement an adaptive logic relying on those
|
|
80
|
+
* metrics and events.
|
|
81
|
+
* @param {Object} options - Various tweaking options allowing to configure the
|
|
82
|
+
* behavior of the returned `ISegmentFetcher`.
|
|
71
83
|
* @returns {Function}
|
|
72
84
|
*/
|
|
73
85
|
export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
|
|
@@ -184,14 +196,7 @@ export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
|
|
|
184
196
|
id: requestId,
|
|
185
197
|
content });
|
|
186
198
|
|
|
187
|
-
cancellationSignal.register(
|
|
188
|
-
if (requestInfo !== undefined) {
|
|
189
|
-
return; // Request already terminated
|
|
190
|
-
}
|
|
191
|
-
log.debug("SF: Segment request cancelled", segmentIdString);
|
|
192
|
-
requestInfo = null;
|
|
193
|
-
lifecycleCallbacks.onRequestEnd?.({ id: requestId });
|
|
194
|
-
});
|
|
199
|
+
cancellationSignal.register(onCancellation);
|
|
195
200
|
|
|
196
201
|
try {
|
|
197
202
|
const res = await scheduleRequestWithCdns(content.representation.cdnMetadata,
|
|
@@ -220,22 +225,34 @@ export default function createSegmentFetcher<TLoadedFormat, TSegmentDataType>(
|
|
|
220
225
|
requestInfo = null;
|
|
221
226
|
}
|
|
222
227
|
|
|
223
|
-
if (!cancellationSignal.isCancelled) {
|
|
228
|
+
if (!cancellationSignal.isCancelled()) {
|
|
224
229
|
// The current task could have been canceled as a result of one
|
|
225
230
|
// of the previous callbacks call. In that case, we don't want to send
|
|
226
231
|
// a "requestEnd" again as it has already been sent on cancellation.
|
|
227
232
|
lifecycleCallbacks.onRequestEnd?.({ id: requestId });
|
|
228
233
|
}
|
|
234
|
+
cancellationSignal.deregister(onCancellation);
|
|
229
235
|
} catch (err) {
|
|
236
|
+
cancellationSignal.deregister(onCancellation);
|
|
230
237
|
requestInfo = null;
|
|
231
238
|
if (err instanceof CancellationError) {
|
|
232
239
|
log.debug("SF: Segment request aborted", segmentIdString);
|
|
233
240
|
throw err;
|
|
234
241
|
}
|
|
235
242
|
log.debug("SF: Segment request failed", segmentIdString);
|
|
243
|
+
lifecycleCallbacks.onRequestEnd?.({ id: requestId });
|
|
236
244
|
throw errorSelector(err);
|
|
237
245
|
}
|
|
238
246
|
|
|
247
|
+
function onCancellation() {
|
|
248
|
+
if (requestInfo !== undefined) {
|
|
249
|
+
return; // Request already terminated
|
|
250
|
+
}
|
|
251
|
+
log.debug("SF: Segment request cancelled", segmentIdString);
|
|
252
|
+
requestInfo = null;
|
|
253
|
+
lifecycleCallbacks.onRequestEnd?.({ id: requestId });
|
|
254
|
+
}
|
|
255
|
+
|
|
239
256
|
/**
|
|
240
257
|
* Call a segment loader for the given URL with the right arguments.
|
|
241
258
|
* @param {Object|null} cdnMetadata
|
|
@@ -360,11 +377,16 @@ export interface ISegmentFetcherCallbacks<TSegmentDataType> {
|
|
|
360
377
|
/**
|
|
361
378
|
* Callback called when all decodable chunks of the loaded segment have been
|
|
362
379
|
* communicated through the `onChunk` callback.
|
|
380
|
+
*
|
|
381
|
+
* This callback is called before the corresponding `ISegmentFetcher`'s
|
|
382
|
+
* returned Promise is resolved.
|
|
363
383
|
*/
|
|
364
384
|
onAllChunksReceived() : void;
|
|
365
385
|
|
|
366
386
|
/**
|
|
367
|
-
* Callback called when the segment request has to restart from scratch.
|
|
387
|
+
* Callback called when the segment request has to restart from scratch, e.g.
|
|
388
|
+
* due to a request error.
|
|
389
|
+
*/
|
|
368
390
|
onRetry(error : IPlayerError) : void;
|
|
369
391
|
}
|
|
370
392
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import log from "../../../log";
|
|
2
2
|
import arrayFindIndex from "../../../utils/array_find_index";
|
|
3
|
+
import createCancellablePromise from "../../../utils/create_cancellable_promise";
|
|
3
4
|
import TaskCanceller, {
|
|
4
5
|
CancellationError,
|
|
5
6
|
CancellationSignal,
|
|
@@ -64,19 +65,34 @@ export default class TaskPrioritizer<T> {
|
|
|
64
65
|
cancelSignal: CancellationSignal
|
|
65
66
|
): Promise<T> {
|
|
66
67
|
let newTask: IPrioritizerTask<T>;
|
|
67
|
-
|
|
68
|
-
return new Promise<T>((resolve, reject) => {
|
|
68
|
+
return createCancellablePromise<T>(cancelSignal, (resolve, reject) => {
|
|
69
69
|
/** Function allowing to start the underlying Promise. */
|
|
70
70
|
const trigger = () => {
|
|
71
71
|
if (newTask.hasEnded) {
|
|
72
|
-
unregisterCancelSignal();
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
75
|
-
const
|
|
74
|
+
const finishTask = () => {
|
|
75
|
+
unlinkInterrupter();
|
|
76
|
+
this._endTask(newTask);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const onResolve = (value: T) => {
|
|
80
|
+
callbacks.beforeEnded();
|
|
81
|
+
finishTask();
|
|
82
|
+
resolve(value);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const onReject = (err: unknown) => {
|
|
86
|
+
finishTask();
|
|
87
|
+
reject(err);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const interrupter = new TaskCanceller();
|
|
91
|
+
const unlinkInterrupter = interrupter.linkToSignal(cancelSignal);
|
|
76
92
|
newTask.interrupter = interrupter;
|
|
77
93
|
interrupter.signal.register(() => {
|
|
78
94
|
newTask.interrupter = null;
|
|
79
|
-
if (!cancelSignal.isCancelled) {
|
|
95
|
+
if (!cancelSignal.isCancelled()) {
|
|
80
96
|
callbacks.beforeInterrupted();
|
|
81
97
|
}
|
|
82
98
|
});
|
|
@@ -89,8 +105,8 @@ export default class TaskPrioritizer<T> {
|
|
|
89
105
|
newTask.taskFn(interrupter.signal)
|
|
90
106
|
.then(onResolve)
|
|
91
107
|
.catch((err) => {
|
|
92
|
-
if (!cancelSignal.isCancelled &&
|
|
93
|
-
interrupter.isUsed &&
|
|
108
|
+
if (!cancelSignal.isCancelled() &&
|
|
109
|
+
interrupter.isUsed() &&
|
|
94
110
|
err instanceof CancellationError)
|
|
95
111
|
{
|
|
96
112
|
return;
|
|
@@ -99,29 +115,6 @@ export default class TaskPrioritizer<T> {
|
|
|
99
115
|
});
|
|
100
116
|
};
|
|
101
117
|
|
|
102
|
-
const unregisterCancelSignal = cancelSignal.register(
|
|
103
|
-
(cancellationError: CancellationError) => {
|
|
104
|
-
this._endTask(newTask);
|
|
105
|
-
reject(cancellationError);
|
|
106
|
-
}
|
|
107
|
-
);
|
|
108
|
-
|
|
109
|
-
const finishTask = () => {
|
|
110
|
-
unregisterCancelSignal();
|
|
111
|
-
this._endTask(newTask);
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const onResolve = (value: T) => {
|
|
115
|
-
callbacks.beforeEnded();
|
|
116
|
-
finishTask();
|
|
117
|
-
resolve(value);
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const onReject = (err: unknown) => {
|
|
121
|
-
finishTask();
|
|
122
|
-
reject(err);
|
|
123
|
-
};
|
|
124
|
-
|
|
125
118
|
newTask = {
|
|
126
119
|
hasEnded: false,
|
|
127
120
|
priority,
|
|
@@ -146,6 +139,8 @@ export default class TaskPrioritizer<T> {
|
|
|
146
139
|
this._interruptCancellableTasks();
|
|
147
140
|
}
|
|
148
141
|
}
|
|
142
|
+
|
|
143
|
+
return () => this._endTask(newTask);
|
|
149
144
|
});
|
|
150
145
|
}
|
|
151
146
|
|
|
@@ -315,7 +315,7 @@ export async function scheduleRequestWithCdns<T>(
|
|
|
315
315
|
async function retryWithNextCdn(prevRequestError : unknown) : Promise<T> {
|
|
316
316
|
const nextCdn = getCdnToRequest();
|
|
317
317
|
|
|
318
|
-
if (cancellationSignal.isCancelled) {
|
|
318
|
+
if (cancellationSignal.isCancelled()) {
|
|
319
319
|
throw cancellationSignal.cancellationError;
|
|
320
320
|
}
|
|
321
321
|
|
|
@@ -324,7 +324,7 @@ export async function scheduleRequestWithCdns<T>(
|
|
|
324
324
|
}
|
|
325
325
|
|
|
326
326
|
onRetry(prevRequestError);
|
|
327
|
-
if (cancellationSignal.isCancelled) {
|
|
327
|
+
if (cancellationSignal.isCancelled()) {
|
|
328
328
|
throw cancellationSignal.cancellationError;
|
|
329
329
|
}
|
|
330
330
|
|
|
@@ -358,12 +358,13 @@ export async function scheduleRequestWithCdns<T>(
|
|
|
358
358
|
return requestCdn(nextWantedCdn);
|
|
359
359
|
}
|
|
360
360
|
|
|
361
|
-
const canceller = new TaskCanceller(
|
|
361
|
+
const canceller = new TaskCanceller();
|
|
362
|
+
const unlinkCanceller = canceller.linkToSignal(cancellationSignal);
|
|
362
363
|
return new Promise<T>((res, rej) => {
|
|
363
364
|
/* eslint-disable-next-line @typescript-eslint/no-misused-promises */
|
|
364
365
|
cdnPrioritizer?.addEventListener("priorityChange", () => {
|
|
365
366
|
const updatedPrioritaryCdn = getCdnToRequest();
|
|
366
|
-
if (cancellationSignal.isCancelled) {
|
|
367
|
+
if (cancellationSignal.isCancelled()) {
|
|
367
368
|
throw cancellationSignal.cancellationError;
|
|
368
369
|
}
|
|
369
370
|
if (updatedPrioritaryCdn === undefined) {
|
|
@@ -378,7 +379,7 @@ export async function scheduleRequestWithCdns<T>(
|
|
|
378
379
|
|
|
379
380
|
cancellableSleep(blockedFor, canceller.signal)
|
|
380
381
|
.then(() => requestCdn(nextWantedCdn).then(res, rej), noop);
|
|
381
|
-
});
|
|
382
|
+
}).finally(unlinkCanceller);
|
|
382
383
|
}
|
|
383
384
|
|
|
384
385
|
/**
|
|
@@ -167,7 +167,7 @@ export default class DirectFileContentInitializer extends ContentInitializer {
|
|
|
167
167
|
}
|
|
168
168
|
}, { emitCurrentValue: true, clearSignal: cancelSignal }))
|
|
169
169
|
.catch((err) => {
|
|
170
|
-
if (!cancelSignal.isCancelled) {
|
|
170
|
+
if (!cancelSignal.isCancelled()) {
|
|
171
171
|
this._onFatalError(err);
|
|
172
172
|
}
|
|
173
173
|
});
|