rx-player 4.3.0-dev.2024112900 → 4.3.0-dev.2025012900
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 +30 -8
- package/VERSION +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/commonjs/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/commonjs/compat/browser_compatibility_types.d.ts +7 -2
- package/dist/commonjs/compat/browser_compatibility_types.d.ts.map +1 -1
- package/dist/commonjs/compat/browser_compatibility_types.js +0 -1
- package/dist/commonjs/compat/browser_detection.js +1 -1
- package/dist/commonjs/compat/event_listeners.d.ts.map +1 -1
- package/dist/commonjs/compat/event_listeners.js +2 -4
- package/dist/commonjs/compat/has_mse_in_worker.d.ts.map +1 -1
- package/dist/commonjs/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/commonjs/core/cmcd/cmcd_data_builder.js +1 -2
- package/dist/commonjs/core/main/worker/send_message.d.ts.map +1 -1
- package/dist/commonjs/core/main/worker/send_message.js +0 -1
- package/dist/commonjs/core/main/worker/worker_main.js +1 -1
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/commonjs/core/stream/adaptation/adaptation_stream.js +4 -8
- package/dist/commonjs/core/stream/period/period_stream.js +1 -3
- package/dist/commonjs/core/stream/period/utils/get_adaptation_switch_strategy.js +1 -2
- package/dist/commonjs/experimental/tools/mediaCapabilitiesProber/api/index.d.ts.map +1 -1
- package/dist/commonjs/main_thread/api/public_api.js +4 -4
- package/dist/commonjs/main_thread/decrypt/clear_on_stop.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/clear_on_stop.js +60 -14
- package/dist/commonjs/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/content_decryptor.js +21 -9
- package/dist/commonjs/main_thread/decrypt/dispose_decryption_resources.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/dispose_decryption_resources.js +2 -18
- package/dist/commonjs/main_thread/decrypt/find_key_system.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/find_key_system.js +134 -68
- package/dist/commonjs/main_thread/decrypt/get_key_system_configuration.js +2 -2
- package/dist/commonjs/main_thread/decrypt/get_media_keys.js +5 -3
- package/dist/commonjs/main_thread/decrypt/init_media_keys.d.ts.map +1 -1
- package/dist/commonjs/main_thread/decrypt/init_media_keys.js +2 -2
- package/dist/{es2017/main_thread/decrypt/utils/media_keys_infos_store.d.ts → commonjs/main_thread/decrypt/utils/media_keys_attacher.d.ts} +31 -9
- package/dist/commonjs/main_thread/decrypt/utils/media_keys_attacher.d.ts.map +1 -0
- package/dist/commonjs/main_thread/decrypt/utils/media_keys_attacher.js +282 -0
- package/dist/commonjs/main_thread/init/utils/rebuffering_controller.d.ts.map +1 -1
- package/dist/commonjs/main_thread/init/utils/rebuffering_controller.js +20 -9
- package/dist/commonjs/main_thread/text_displayer/html/text_track_cues_store.js +1 -1
- package/dist/commonjs/main_thread/tracks_store/track_dispatcher.d.ts.map +1 -1
- package/dist/commonjs/main_thread/tracks_store/track_dispatcher.js +3 -4
- package/dist/commonjs/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
- package/dist/commonjs/main_thread/tracks_store/tracks_store.js +1 -1
- package/dist/commonjs/manifest/classes/representation.d.ts +15 -1
- package/dist/commonjs/manifest/classes/representation.d.ts.map +1 -1
- package/dist/commonjs/manifest/classes/representation.js +17 -0
- package/dist/commonjs/manifest/utils.d.ts +15 -0
- package/dist/commonjs/manifest/utils.d.ts.map +1 -1
- package/dist/commonjs/manifest/utils.js +28 -5
- package/dist/commonjs/parsers/manifest/dash/common/parse_representations.js +2 -2
- package/dist/commonjs/playback_observer/media_element_playback_observer.js +1 -1
- package/dist/commonjs/public_types.d.ts +44 -0
- package/dist/commonjs/public_types.d.ts.map +1 -1
- package/dist/commonjs/utils/flat_map.d.ts +2 -2
- package/dist/commonjs/utils/flat_map.js +2 -2
- package/dist/commonjs/utils/url-utils.d.ts.map +1 -1
- package/dist/commonjs/utils/url-utils.js +11 -10
- package/dist/commonjs/utils/xml-parser.d.ts.map +1 -1
- package/dist/commonjs/utils/xml-parser.js +10 -8
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_dash_wasm.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.d.ts.map +1 -1
- package/dist/es2017/__GENERATED_CODE/embedded_worker.js +1 -1
- package/dist/es2017/compat/browser_compatibility_types.d.ts +7 -2
- package/dist/es2017/compat/browser_compatibility_types.d.ts.map +1 -1
- package/dist/es2017/compat/browser_compatibility_types.js +0 -1
- package/dist/es2017/compat/browser_detection.js +1 -1
- package/dist/es2017/compat/event_listeners.d.ts.map +1 -1
- package/dist/es2017/compat/event_listeners.js +2 -4
- package/dist/es2017/compat/has_mse_in_worker.d.ts.map +1 -1
- package/dist/es2017/core/cmcd/cmcd_data_builder.d.ts.map +1 -1
- package/dist/es2017/core/cmcd/cmcd_data_builder.js +1 -2
- package/dist/es2017/core/main/worker/send_message.d.ts.map +1 -1
- package/dist/es2017/core/main/worker/send_message.js +0 -1
- package/dist/es2017/core/main/worker/worker_main.js +1 -1
- package/dist/es2017/core/stream/adaptation/adaptation_stream.d.ts.map +1 -1
- package/dist/es2017/core/stream/adaptation/adaptation_stream.js +4 -6
- package/dist/es2017/core/stream/period/period_stream.js +1 -3
- package/dist/es2017/core/stream/period/utils/get_adaptation_switch_strategy.js +1 -2
- package/dist/es2017/experimental/tools/mediaCapabilitiesProber/api/index.d.ts.map +1 -1
- package/dist/es2017/main_thread/api/public_api.js +4 -4
- package/dist/es2017/main_thread/decrypt/clear_on_stop.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/clear_on_stop.js +3 -3
- package/dist/es2017/main_thread/decrypt/content_decryptor.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/content_decryptor.js +21 -9
- package/dist/es2017/main_thread/decrypt/dispose_decryption_resources.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/dispose_decryption_resources.js +2 -12
- package/dist/es2017/main_thread/decrypt/find_key_system.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/find_key_system.js +102 -56
- package/dist/es2017/main_thread/decrypt/get_key_system_configuration.js +2 -2
- package/dist/es2017/main_thread/decrypt/get_media_keys.js +2 -2
- package/dist/es2017/main_thread/decrypt/init_media_keys.d.ts.map +1 -1
- package/dist/es2017/main_thread/decrypt/init_media_keys.js +2 -2
- package/dist/{commonjs/main_thread/decrypt/utils/media_keys_infos_store.d.ts → es2017/main_thread/decrypt/utils/media_keys_attacher.d.ts} +31 -9
- package/dist/es2017/main_thread/decrypt/utils/media_keys_attacher.d.ts.map +1 -0
- package/dist/es2017/main_thread/decrypt/utils/media_keys_attacher.js +180 -0
- package/dist/es2017/main_thread/init/utils/rebuffering_controller.d.ts.map +1 -1
- package/dist/es2017/main_thread/init/utils/rebuffering_controller.js +20 -9
- package/dist/es2017/main_thread/text_displayer/html/text_track_cues_store.js +1 -1
- package/dist/es2017/main_thread/tracks_store/track_dispatcher.d.ts.map +1 -1
- package/dist/es2017/main_thread/tracks_store/track_dispatcher.js +3 -4
- package/dist/es2017/main_thread/tracks_store/tracks_store.d.ts.map +1 -1
- package/dist/es2017/main_thread/tracks_store/tracks_store.js +2 -2
- package/dist/es2017/manifest/classes/representation.d.ts +15 -1
- package/dist/es2017/manifest/classes/representation.d.ts.map +1 -1
- package/dist/es2017/manifest/classes/representation.js +17 -0
- package/dist/es2017/manifest/utils.d.ts +15 -0
- package/dist/es2017/manifest/utils.d.ts.map +1 -1
- package/dist/es2017/manifest/utils.js +27 -5
- package/dist/es2017/parsers/manifest/dash/common/parse_representations.js +2 -2
- package/dist/es2017/playback_observer/media_element_playback_observer.js +1 -1
- package/dist/es2017/public_types.d.ts +44 -0
- package/dist/es2017/public_types.d.ts.map +1 -1
- package/dist/es2017/utils/flat_map.d.ts +2 -2
- package/dist/es2017/utils/flat_map.js +2 -2
- package/dist/es2017/utils/url-utils.d.ts.map +1 -1
- package/dist/es2017/utils/url-utils.js +11 -10
- package/dist/es2017/utils/xml-parser.d.ts.map +1 -1
- package/dist/es2017/utils/xml-parser.js +10 -8
- package/dist/rx-player.js +505 -372
- package/dist/rx-player.min.js +18 -18
- package/dist/worker.js +6 -6
- package/package.json +21 -19
- package/src/__GENERATED_CODE/embedded_worker.ts +1 -1
- package/src/compat/browser_compatibility_types.ts +22 -4
- package/src/compat/browser_detection.ts +1 -1
- package/src/compat/event_listeners.ts +2 -4
- package/src/compat/has_mse_in_worker.ts +1 -1
- package/src/core/cmcd/cmcd_data_builder.ts +1 -4
- package/src/core/main/worker/send_message.ts +4 -2
- package/src/core/main/worker/worker_main.ts +1 -1
- package/src/core/stream/adaptation/adaptation_stream.ts +4 -7
- package/src/core/stream/period/period_stream.ts +3 -3
- package/src/core/stream/period/utils/get_adaptation_switch_strategy.ts +1 -2
- package/src/main_thread/api/public_api.ts +4 -4
- package/src/main_thread/decrypt/__tests__/__global__/media_key_system_access.test.ts +239 -6
- package/src/main_thread/decrypt/__tests__/find_key_system.test.ts +325 -0
- package/src/main_thread/decrypt/clear_on_stop.ts +3 -3
- package/src/main_thread/decrypt/content_decryptor.ts +26 -8
- package/src/main_thread/decrypt/dispose_decryption_resources.ts +2 -13
- package/src/main_thread/decrypt/find_key_system.ts +120 -81
- package/src/main_thread/decrypt/get_key_system_configuration.ts +2 -2
- package/src/main_thread/decrypt/get_media_keys.ts +2 -2
- package/src/main_thread/decrypt/init_media_keys.ts +2 -2
- package/src/main_thread/decrypt/utils/media_keys_attacher.ts +284 -0
- package/src/main_thread/init/utils/rebuffering_controller.ts +20 -10
- package/src/main_thread/text_displayer/html/text_track_cues_store.ts +1 -1
- package/src/main_thread/tracks_store/track_dispatcher.ts +3 -6
- package/src/main_thread/tracks_store/tracks_store.ts +2 -1
- package/src/manifest/classes/representation.ts +18 -1
- package/src/manifest/utils.ts +30 -9
- package/src/parsers/manifest/dash/common/parse_representations.ts +2 -2
- package/src/playback_observer/media_element_playback_observer.ts +1 -1
- package/src/public_types.ts +45 -0
- package/src/utils/__tests__/flat_map.test.ts +7 -20
- package/src/utils/flat_map.ts +2 -2
- package/src/utils/url-utils.ts +11 -10
- package/src/utils/xml-parser.ts +13 -8
- package/vitest.config.mjs +9 -23
- package/dist/commonjs/main_thread/decrypt/attach_media_keys.d.ts +0 -60
- package/dist/commonjs/main_thread/decrypt/attach_media_keys.d.ts.map +0 -1
- package/dist/commonjs/main_thread/decrypt/attach_media_keys.js +0 -124
- package/dist/commonjs/main_thread/decrypt/utils/media_keys_infos_store.d.ts.map +0 -1
- package/dist/commonjs/main_thread/decrypt/utils/media_keys_infos_store.js +0 -45
- package/dist/es2017/main_thread/decrypt/attach_media_keys.d.ts +0 -60
- package/dist/es2017/main_thread/decrypt/attach_media_keys.d.ts.map +0 -1
- package/dist/es2017/main_thread/decrypt/attach_media_keys.js +0 -73
- package/dist/es2017/main_thread/decrypt/utils/media_keys_infos_store.d.ts.map +0 -1
- package/dist/es2017/main_thread/decrypt/utils/media_keys_infos_store.js +0 -43
- package/src/main_thread/decrypt/attach_media_keys.ts +0 -125
- package/src/main_thread/decrypt/utils/media_keys_infos_store.ts +0 -88
|
@@ -32,7 +32,7 @@ import arrayIncludes from "../../utils/array_includes";
|
|
|
32
32
|
import flatMap from "../../utils/flat_map";
|
|
33
33
|
import isNullOrUndefined from "../../utils/is_null_or_undefined";
|
|
34
34
|
import type { CancellationSignal } from "../../utils/task_canceller";
|
|
35
|
-
import
|
|
35
|
+
import MediaKeysAttacher from "./utils/media_keys_attacher";
|
|
36
36
|
|
|
37
37
|
type MediaKeysRequirement = "optional" | "required" | "not-allowed";
|
|
38
38
|
|
|
@@ -81,59 +81,81 @@ interface IKeySystemType {
|
|
|
81
81
|
* system.
|
|
82
82
|
*/
|
|
83
83
|
keyName: string | undefined;
|
|
84
|
-
/**
|
|
84
|
+
/** KeySystem type (e.g. "com.widevine.alpha") */
|
|
85
85
|
keyType: string;
|
|
86
|
-
/**
|
|
86
|
+
/** The original keySystem object */
|
|
87
87
|
keySystemOptions: IKeySystemOption;
|
|
88
88
|
}
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
91
|
+
* Takes a `newConfiguration` `MediaKeySystemConfiguration`, that is intended
|
|
92
|
+
* for the creation of a `MediaKeySystemAccess`, and a `prevConfiguration`
|
|
93
|
+
* `MediaKeySystemConfiguration`, that was the one used at creation of the
|
|
94
|
+
* current `MediaKeySystemAccess`.
|
|
95
|
+
*
|
|
96
|
+
* This function will then return `true` if it determined that the new
|
|
97
|
+
* configuration is conceptually compatible with the one used before, and
|
|
98
|
+
* `false` otherwise.
|
|
99
|
+
* @param {Object} newConfiguration - New wanted `MediaKeySystemConfiguration`
|
|
100
|
+
* @param {Object} prevConfiguration - The `MediaKeySystemConfiguration` that is
|
|
101
|
+
* relied on util now.
|
|
102
|
+
* @returns {boolean} - `true` if `newConfiguration` is compatible with
|
|
103
|
+
* `prevConfiguration`.
|
|
96
104
|
*/
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
keySystemOptions: IKeySystemOption;
|
|
104
|
-
askedConfiguration: MediaKeySystemConfiguration;
|
|
105
|
-
keySystemAccess: MediaKeySystemAccess | ICustomMediaKeySystemAccess;
|
|
106
|
-
} {
|
|
107
|
-
const mksConfiguration = currentKeySystemAccess.getConfiguration();
|
|
108
|
-
if (shouldRenewMediaKeySystemAccess() || isNullOrUndefined(mksConfiguration)) {
|
|
109
|
-
return null;
|
|
105
|
+
function isNewMediaKeySystemConfigurationCompatibleWithPreviousOne(
|
|
106
|
+
newConfiguration: MediaKeySystemConfiguration,
|
|
107
|
+
prevConfiguration: MediaKeySystemConfiguration,
|
|
108
|
+
): boolean {
|
|
109
|
+
if (newConfiguration.label !== prevConfiguration.label) {
|
|
110
|
+
return false;
|
|
110
111
|
}
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
const prevDistinctiveIdentifier = prevConfiguration.distinctiveIdentifier ?? "optional";
|
|
114
|
+
const newDistinctiveIdentifier = newConfiguration.distinctiveIdentifier ?? "optional";
|
|
115
|
+
if (prevDistinctiveIdentifier !== newDistinctiveIdentifier) {
|
|
116
|
+
return false;
|
|
115
117
|
}
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
) {
|
|
122
|
-
return null;
|
|
119
|
+
const prevPersistentState = prevConfiguration.persistentState ?? "optional";
|
|
120
|
+
const newPersistentState = newConfiguration.persistentState ?? "optional";
|
|
121
|
+
if (prevPersistentState !== newPersistentState) {
|
|
122
|
+
return false;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
return null;
|
|
125
|
+
const prevInitDataTypes = prevConfiguration.initDataTypes ?? [];
|
|
126
|
+
const newInitDataTypes = newConfiguration.initDataTypes ?? [];
|
|
127
|
+
if (!isArraySubsetOf(newInitDataTypes, prevInitDataTypes)) {
|
|
128
|
+
return false;
|
|
130
129
|
}
|
|
131
130
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
131
|
+
const prevSessionTypes = prevConfiguration.sessionTypes ?? [];
|
|
132
|
+
const newSessionTypes = newConfiguration.sessionTypes ?? [];
|
|
133
|
+
if (!isArraySubsetOf(newSessionTypes, prevSessionTypes)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
for (const prop of ["audioCapabilities", "videoCapabilities"] as const) {
|
|
138
|
+
const newCapabilities = newConfiguration[prop] ?? [];
|
|
139
|
+
const prevCapabilities = prevConfiguration[prop] ?? [];
|
|
140
|
+
const wasFound = newCapabilities.every((n) => {
|
|
141
|
+
for (let i = 0; i < prevCapabilities.length; i++) {
|
|
142
|
+
const prevCap = prevCapabilities[i];
|
|
143
|
+
if (
|
|
144
|
+
(prevCap.robustness ?? "") === (n.robustness ?? "") ||
|
|
145
|
+
(prevCap.encryptionScheme ?? null) === (n.encryptionScheme ?? null) ||
|
|
146
|
+
(prevCap.robustness ?? "") === (n.robustness ?? "")
|
|
147
|
+
) {
|
|
148
|
+
return true;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return false;
|
|
152
|
+
});
|
|
153
|
+
if (!wasFound) {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return true;
|
|
137
159
|
}
|
|
138
160
|
|
|
139
161
|
/**
|
|
@@ -162,13 +184,23 @@ function buildKeySystemConfigurations(
|
|
|
162
184
|
keySystemTypeInfo: IKeySystemType,
|
|
163
185
|
): MediaKeySystemConfiguration[] {
|
|
164
186
|
const { keyName, keyType, keySystemOptions: keySystem } = keySystemTypeInfo;
|
|
165
|
-
|
|
187
|
+
let sessionTypes: string[];
|
|
166
188
|
let persistentState: MediaKeysRequirement = "optional";
|
|
167
189
|
let distinctiveIdentifier: MediaKeysRequirement = "optional";
|
|
168
190
|
|
|
169
|
-
if (
|
|
191
|
+
if (Array.isArray(keySystem.wantedSessionTypes)) {
|
|
192
|
+
sessionTypes = keySystem.wantedSessionTypes;
|
|
193
|
+
if (
|
|
194
|
+
arrayIncludes(keySystem.wantedSessionTypes, "persistent-license") &&
|
|
195
|
+
!isNullOrUndefined(keySystem.persistentLicenseConfig)
|
|
196
|
+
) {
|
|
197
|
+
persistentState = "required";
|
|
198
|
+
}
|
|
199
|
+
} else if (!isNullOrUndefined(keySystem.persistentLicenseConfig)) {
|
|
170
200
|
persistentState = "required";
|
|
171
|
-
sessionTypes
|
|
201
|
+
sessionTypes = ["persistent-license"];
|
|
202
|
+
} else {
|
|
203
|
+
sessionTypes = ["temporary"];
|
|
172
204
|
}
|
|
173
205
|
|
|
174
206
|
if (!isNullOrUndefined(keySystem.persistentState)) {
|
|
@@ -384,14 +416,7 @@ export default function getMediaKeySystemAccess(
|
|
|
384
416
|
cancelSignal: CancellationSignal,
|
|
385
417
|
): Promise<IFoundMediaKeySystemAccessEvent> {
|
|
386
418
|
log.info("DRM: Searching for compatible MediaKeySystemAccess");
|
|
387
|
-
/**
|
|
388
|
-
* Array of set keySystems for this content.
|
|
389
|
-
* Each item of this array is an object containing the following keys:
|
|
390
|
-
* - keyName {string}: keySystem canonical name (e.g. "widevine")
|
|
391
|
-
* - keyType {string}: keySystem type (e.g. "com.widevine.alpha")
|
|
392
|
-
* - keySystem {Object}: the original keySystem object
|
|
393
|
-
* @type {Array.<Object>}
|
|
394
|
-
*/
|
|
419
|
+
/** Array of set keySystems for this content. */
|
|
395
420
|
const keySystemsType: IKeySystemType[] = keySystemsConfigs.reduce(
|
|
396
421
|
(arr: IKeySystemType[], keySystemOptions) => {
|
|
397
422
|
const { EME_KEY_SYSTEMS } = config.getCurrent();
|
|
@@ -444,36 +469,6 @@ export default function getMediaKeySystemAccess(
|
|
|
444
469
|
}
|
|
445
470
|
|
|
446
471
|
const chosenType = keySystemsType[index];
|
|
447
|
-
|
|
448
|
-
const currentState = MediaKeysInfosStore.getState(mediaElement);
|
|
449
|
-
if (currentState !== null) {
|
|
450
|
-
if (eme.implementation === currentState.emeImplementation.implementation) {
|
|
451
|
-
// Fast way to find a compatible keySystem if the currently loaded
|
|
452
|
-
// one as exactly the same compatibility options.
|
|
453
|
-
const cachedKeySystemAccess = checkCachedMediaKeySystemAccess(
|
|
454
|
-
chosenType.keySystemOptions,
|
|
455
|
-
currentState.askedConfiguration,
|
|
456
|
-
currentState.mediaKeySystemAccess,
|
|
457
|
-
currentState.keySystemOptions,
|
|
458
|
-
);
|
|
459
|
-
if (cachedKeySystemAccess !== null) {
|
|
460
|
-
log.info("DRM: Found cached compatible keySystem");
|
|
461
|
-
return Promise.resolve({
|
|
462
|
-
type: "reuse-media-key-system-access" as const,
|
|
463
|
-
value: {
|
|
464
|
-
mediaKeySystemAccess: cachedKeySystemAccess.keySystemAccess,
|
|
465
|
-
askedConfiguration: cachedKeySystemAccess.askedConfiguration,
|
|
466
|
-
options: cachedKeySystemAccess.keySystemOptions,
|
|
467
|
-
codecSupport: extractCodecSupportListFromConfiguration(
|
|
468
|
-
cachedKeySystemAccess.askedConfiguration,
|
|
469
|
-
cachedKeySystemAccess.keySystemAccess.getConfiguration(),
|
|
470
|
-
),
|
|
471
|
-
},
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
472
|
const { keyType, keySystemOptions } = chosenType;
|
|
478
473
|
|
|
479
474
|
const keySystemConfigurations = buildKeySystemConfigurations(chosenType);
|
|
@@ -484,8 +479,37 @@ export default function getMediaKeySystemAccess(
|
|
|
484
479
|
);
|
|
485
480
|
|
|
486
481
|
let keySystemAccess;
|
|
482
|
+
const currentState = await MediaKeysAttacher.getAttachedMediaKeysState(mediaElement);
|
|
487
483
|
for (let configIdx = 0; configIdx < keySystemConfigurations.length; configIdx++) {
|
|
488
484
|
const keySystemConfiguration = keySystemConfigurations[configIdx];
|
|
485
|
+
|
|
486
|
+
// Check if the current `MediaKeySystemAccess` created cannot be reused here
|
|
487
|
+
if (
|
|
488
|
+
currentState !== null &&
|
|
489
|
+
!shouldRenewMediaKeySystemAccess() &&
|
|
490
|
+
// TODO: Do it with MediaKeySystemAccess.prototype.keySystem instead?
|
|
491
|
+
keyType === currentState.mediaKeySystemAccess.keySystem &&
|
|
492
|
+
eme.implementation === currentState.emeImplementation.implementation &&
|
|
493
|
+
isNewMediaKeySystemConfigurationCompatibleWithPreviousOne(
|
|
494
|
+
keySystemConfiguration,
|
|
495
|
+
currentState.askedConfiguration,
|
|
496
|
+
)
|
|
497
|
+
) {
|
|
498
|
+
log.info("DRM: Found cached compatible keySystem");
|
|
499
|
+
return Promise.resolve({
|
|
500
|
+
type: "reuse-media-key-system-access" as const,
|
|
501
|
+
value: {
|
|
502
|
+
mediaKeySystemAccess: currentState.mediaKeySystemAccess,
|
|
503
|
+
askedConfiguration: currentState.askedConfiguration,
|
|
504
|
+
options: keySystemOptions,
|
|
505
|
+
codecSupport: extractCodecSupportListFromConfiguration(
|
|
506
|
+
currentState.askedConfiguration,
|
|
507
|
+
currentState.mediaKeySystemAccess.getConfiguration(),
|
|
508
|
+
),
|
|
509
|
+
},
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
|
|
489
513
|
try {
|
|
490
514
|
keySystemAccess = await testKeySystem(keyType, [keySystemConfiguration]);
|
|
491
515
|
log.info("DRM: Found compatible keysystem", keyType, index + 1);
|
|
@@ -544,3 +568,18 @@ export async function testKeySystem(
|
|
|
544
568
|
}
|
|
545
569
|
return keySystemAccess;
|
|
546
570
|
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Returns `true` if `arr1`'s values are entirely contained in `arr2`.
|
|
574
|
+
* @param {string} arr1
|
|
575
|
+
* @param {string} arr2
|
|
576
|
+
* @return {boolean}
|
|
577
|
+
*/
|
|
578
|
+
function isArraySubsetOf(arr1: string[], arr2: string[]): boolean {
|
|
579
|
+
for (let i = 0; i < arr1.length; i++) {
|
|
580
|
+
if (!arrayIncludes(arr2, arr1[i])) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import type { IMediaElement } from "../../compat/browser_compatibility_types";
|
|
18
|
-
import
|
|
18
|
+
import MediaKeysAttacher from "./utils/media_keys_attacher";
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Returns the name of the current key system used as well as its configuration,
|
|
@@ -26,7 +26,7 @@ import MediaKeysInfosStore from "./utils/media_keys_infos_store";
|
|
|
26
26
|
export default function getKeySystemConfiguration(
|
|
27
27
|
mediaElement: IMediaElement,
|
|
28
28
|
): [string, MediaKeySystemConfiguration] | null {
|
|
29
|
-
const currentState =
|
|
29
|
+
const currentState = MediaKeysAttacher.getAwaitedState(mediaElement);
|
|
30
30
|
if (currentState === null) {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
@@ -26,7 +26,7 @@ import getMediaKeySystemAccess from "./find_key_system";
|
|
|
26
26
|
import type { ICodecSupportList } from "./find_key_system";
|
|
27
27
|
import type { IMediaKeySessionStores } from "./types";
|
|
28
28
|
import LoadedSessionsStore from "./utils/loaded_sessions_store";
|
|
29
|
-
import
|
|
29
|
+
import MediaKeysAttacher from "./utils/media_keys_attacher";
|
|
30
30
|
import PersistentSessionsStore from "./utils/persistent_sessions_store";
|
|
31
31
|
import ServerCertificateStore from "./utils/server_certificate_store";
|
|
32
32
|
|
|
@@ -94,7 +94,7 @@ export default async function getMediaKeysInfos(
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
const { options, mediaKeySystemAccess, askedConfiguration, codecSupport } = evt.value;
|
|
97
|
-
const currentState =
|
|
97
|
+
const currentState = await MediaKeysAttacher.getAttachedMediaKeysState(mediaElement);
|
|
98
98
|
const persistentSessionsStore = createPersistentSessionsStorage(options);
|
|
99
99
|
|
|
100
100
|
if (
|
|
@@ -18,9 +18,9 @@ import type { IMediaElement } from "../../compat/browser_compatibility_types";
|
|
|
18
18
|
import log from "../../log";
|
|
19
19
|
import type { IKeySystemOption } from "../../public_types";
|
|
20
20
|
import type { CancellationSignal } from "../../utils/task_canceller";
|
|
21
|
-
import { disableMediaKeys } from "./attach_media_keys";
|
|
22
21
|
import type { IMediaKeysInfos } from "./get_media_keys";
|
|
23
22
|
import getMediaKeysInfos from "./get_media_keys";
|
|
23
|
+
import MediaKeysAttacher from "./utils/media_keys_attacher";
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
26
|
* Get media keys infos from key system configs then attach media keys to media element.
|
|
@@ -48,7 +48,7 @@ export default async function initMediaKeys(
|
|
|
48
48
|
|
|
49
49
|
if (shouldDisableOldMediaKeys) {
|
|
50
50
|
log.debug("DRM: Disabling old MediaKeys");
|
|
51
|
-
await
|
|
51
|
+
await MediaKeysAttacher.clearMediaKeys(mediaElement);
|
|
52
52
|
}
|
|
53
53
|
return mediaKeysInfo;
|
|
54
54
|
}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright 2015 CANAL+ Group
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import type { IMediaElement } from "../../../compat/browser_compatibility_types";
|
|
18
|
+
import type {
|
|
19
|
+
ICustomMediaKeys,
|
|
20
|
+
ICustomMediaKeySystemAccess,
|
|
21
|
+
IEmeApiImplementation,
|
|
22
|
+
} from "../../../compat/eme";
|
|
23
|
+
import { setMediaKeys } from "../../../compat/eme/set_media_keys";
|
|
24
|
+
import { EncryptedMediaError } from "../../../errors";
|
|
25
|
+
import log from "../../../log";
|
|
26
|
+
import type { IKeySystemOption } from "../../../public_types";
|
|
27
|
+
import isNullOrUndefined from "../../../utils/is_null_or_undefined";
|
|
28
|
+
import type LoadedSessionsStore from "./loaded_sessions_store";
|
|
29
|
+
|
|
30
|
+
/** DRM-related state that can be associated to a single HTMLMediaElement. */
|
|
31
|
+
export interface IMediaElementMediaKeysInfos {
|
|
32
|
+
emeImplementation: IEmeApiImplementation;
|
|
33
|
+
|
|
34
|
+
/** Last keySystemOptions used with that HTMLMediaElement. */
|
|
35
|
+
keySystemOptions: IKeySystemOption;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The actual MediaKeySystemConfiguration asked to the
|
|
39
|
+
* `requestMediaKeySystemAccess` API.
|
|
40
|
+
*/
|
|
41
|
+
askedConfiguration: MediaKeySystemConfiguration;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Last MediaKeySystemAccess used to create a MediaKeys bound to that
|
|
45
|
+
* HTMLMediaElement.
|
|
46
|
+
*/
|
|
47
|
+
mediaKeySystemAccess: ICustomMediaKeySystemAccess | MediaKeySystemAccess;
|
|
48
|
+
|
|
49
|
+
/** Last MediaKeys instance bound to that HTMLMediaElement. */
|
|
50
|
+
mediaKeys: MediaKeys | ICustomMediaKeys;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Store containing information about every MediaKeySession active on the
|
|
54
|
+
* MediaKeys instance bound to that HTMLMediaElement.
|
|
55
|
+
*/
|
|
56
|
+
loadedSessionsStore: LoadedSessionsStore;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Store the MediaKeys infos attached to a media element.
|
|
60
|
+
const currentMediaState = new WeakMap<IMediaElement, IMediaKeysAttacherItem>();
|
|
61
|
+
|
|
62
|
+
interface IMediaKeysAttacherItem {
|
|
63
|
+
/**
|
|
64
|
+
* Information on `MediaKeys` which has been attached or is being attached
|
|
65
|
+
* to the `HTMLMediaElement`.
|
|
66
|
+
*
|
|
67
|
+
* `null` either if no `MediaKeys` is attached or if the last attachment
|
|
68
|
+
* task failed.
|
|
69
|
+
*/
|
|
70
|
+
mediaKeysState: IMediaElementMediaKeysInfos | null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Property set to a Promise **only** when the `MediaKeys` attachment is
|
|
74
|
+
* pending.
|
|
75
|
+
*
|
|
76
|
+
* This promise resolves once `mediaKeys` is attached to that
|
|
77
|
+
* `HTMLMediaElement`, and rejects if it failed to do so.
|
|
78
|
+
*
|
|
79
|
+
* `null` if no `MediaKeys` attachment is pending (it is already attached
|
|
80
|
+
* or it already failed to do so).
|
|
81
|
+
*/
|
|
82
|
+
pendingTask: Promise<void> | null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export default {
|
|
86
|
+
/**
|
|
87
|
+
* Attach new MediaKeys infos set on a HMTLMediaElement.
|
|
88
|
+
* @param {HTMLMediaElement} mediaElement
|
|
89
|
+
* @param {Object} mediaKeysInfo
|
|
90
|
+
* @returns {Promise}
|
|
91
|
+
*/
|
|
92
|
+
async attach(
|
|
93
|
+
mediaElement: IMediaElement,
|
|
94
|
+
mediaKeysInfo: IMediaElementMediaKeysInfos,
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
const previousState = currentMediaState.get(mediaElement);
|
|
97
|
+
const pendingTask = attachMediaKeys(mediaElement, previousState, mediaKeysInfo).then(
|
|
98
|
+
() => {
|
|
99
|
+
currentMediaState.set(mediaElement, {
|
|
100
|
+
pendingTask: null,
|
|
101
|
+
mediaKeysState: mediaKeysInfo,
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
() => {
|
|
105
|
+
currentMediaState.set(mediaElement, {
|
|
106
|
+
pendingTask: null,
|
|
107
|
+
mediaKeysState: null,
|
|
108
|
+
});
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
currentMediaState.set(mediaElement, {
|
|
112
|
+
pendingTask,
|
|
113
|
+
mediaKeysState: mediaKeysInfo,
|
|
114
|
+
});
|
|
115
|
+
return pendingTask;
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get MediaKeys information expected to be linked to the given
|
|
120
|
+
* `HTMLMediaElement`.
|
|
121
|
+
*
|
|
122
|
+
* Unlike `getAttachedMediaKeysState`, this method is synchronous and will
|
|
123
|
+
* also return the expected state when `MediaKeys` attachment is still
|
|
124
|
+
* pending and thus when that state is not truly applied (and where it
|
|
125
|
+
* might fail before being applied).
|
|
126
|
+
*
|
|
127
|
+
* As such, only call this method if you want the currently expected state,
|
|
128
|
+
* not the actual one.
|
|
129
|
+
* @param {HTMLMediaElement} mediaElement
|
|
130
|
+
* @returns {Array}
|
|
131
|
+
*/
|
|
132
|
+
getAwaitedState(mediaElement: IMediaElement): IMediaElementMediaKeysInfos | null {
|
|
133
|
+
const currentState = currentMediaState.get(mediaElement);
|
|
134
|
+
return currentState?.mediaKeysState ?? null;
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get MediaKeys information set on a HMTLMediaElement.
|
|
139
|
+
*
|
|
140
|
+
* This method is asynchronous because that state may still be in a process
|
|
141
|
+
* of being attached to the `HTMLMediaElement` (and the state we're
|
|
142
|
+
* currently setting may not work out).
|
|
143
|
+
* @param {HTMLMediaElement} mediaElement
|
|
144
|
+
* @returns {Object|null}
|
|
145
|
+
*/
|
|
146
|
+
async getAttachedMediaKeysState(
|
|
147
|
+
mediaElement: IMediaElement,
|
|
148
|
+
): Promise<IMediaElementMediaKeysInfos | null> {
|
|
149
|
+
const currentState = currentMediaState.get(mediaElement);
|
|
150
|
+
if (currentState === undefined) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
if (currentState.pendingTask !== null) {
|
|
154
|
+
await currentState.pendingTask;
|
|
155
|
+
return this.getAttachedMediaKeysState(mediaElement);
|
|
156
|
+
}
|
|
157
|
+
return currentState.mediaKeysState;
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Remove MediaKeys currently set on a HMTLMediaElement and update state
|
|
162
|
+
* accordingly.
|
|
163
|
+
* @param {HTMLMediaElement} mediaElement
|
|
164
|
+
* @returns {Promise}
|
|
165
|
+
*/
|
|
166
|
+
clearMediaKeys(mediaElement: IMediaElement): Promise<void> {
|
|
167
|
+
const previousState = currentMediaState.get(mediaElement);
|
|
168
|
+
const pendingTask = clearMediaKeys(mediaElement, previousState).then(
|
|
169
|
+
() => {
|
|
170
|
+
currentMediaState.set(mediaElement, {
|
|
171
|
+
pendingTask: null,
|
|
172
|
+
mediaKeysState: null,
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
() => {
|
|
176
|
+
currentMediaState.set(mediaElement, {
|
|
177
|
+
pendingTask: null,
|
|
178
|
+
mediaKeysState: null,
|
|
179
|
+
});
|
|
180
|
+
},
|
|
181
|
+
);
|
|
182
|
+
currentMediaState.set(mediaElement, {
|
|
183
|
+
pendingTask,
|
|
184
|
+
mediaKeysState: null,
|
|
185
|
+
});
|
|
186
|
+
return pendingTask;
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Ensure that the last `MediaKeys` set on the given HTMLMediaElement is
|
|
192
|
+
* attached.
|
|
193
|
+
*
|
|
194
|
+
* The returned Promise never rejects, it will just log an error if the
|
|
195
|
+
* previous attachment failed.
|
|
196
|
+
*
|
|
197
|
+
* @param {Object} previousState
|
|
198
|
+
* @returns {Promise.<undefined>}
|
|
199
|
+
*/
|
|
200
|
+
async function awaitMediaKeysAttachment(
|
|
201
|
+
previousState: IMediaKeysAttacherItem,
|
|
202
|
+
): Promise<void> {
|
|
203
|
+
const promise = previousState.pendingTask;
|
|
204
|
+
if (isNullOrUndefined(promise)) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
log.info("DRM: Awaiting previous MediaKeys attachment operation");
|
|
208
|
+
try {
|
|
209
|
+
await previousState.pendingTask;
|
|
210
|
+
} catch (err) {
|
|
211
|
+
log.info(
|
|
212
|
+
"DRM: previous MediaKeys attachment operation failed",
|
|
213
|
+
err instanceof Error ? err : "Unknown error",
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function attachMediaKeys(
|
|
219
|
+
mediaElement: IMediaElement,
|
|
220
|
+
previousState: IMediaKeysAttacherItem | undefined,
|
|
221
|
+
mediaKeysInfo: IMediaElementMediaKeysInfos,
|
|
222
|
+
): Promise<void> {
|
|
223
|
+
if (previousState !== undefined) {
|
|
224
|
+
if (previousState.pendingTask !== null) {
|
|
225
|
+
// Ensure the `MediaKeys` has been fully attached to the HTMLMediaElement before
|
|
226
|
+
// resetting things, to avoid browser errors due to an invalid state.
|
|
227
|
+
await awaitMediaKeysAttachment(previousState);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const closeAllSessions =
|
|
231
|
+
!isNullOrUndefined(previousState.mediaKeysState) &&
|
|
232
|
+
previousState.mediaKeysState.loadedSessionsStore !==
|
|
233
|
+
mediaKeysInfo.loadedSessionsStore
|
|
234
|
+
? previousState.mediaKeysState.loadedSessionsStore.closeAllSessions()
|
|
235
|
+
: Promise.resolve();
|
|
236
|
+
|
|
237
|
+
await closeAllSessions;
|
|
238
|
+
|
|
239
|
+
if (mediaElement.mediaKeys === mediaKeysInfo.mediaKeys) {
|
|
240
|
+
log.debug("DRM: Right MediaKeys already set");
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
log.info("DRM: Attaching MediaKeys to the media element");
|
|
246
|
+
try {
|
|
247
|
+
await setMediaKeys(
|
|
248
|
+
mediaKeysInfo.emeImplementation,
|
|
249
|
+
mediaElement,
|
|
250
|
+
mediaKeysInfo.mediaKeys,
|
|
251
|
+
);
|
|
252
|
+
log.info("DRM: MediaKeys attached with success");
|
|
253
|
+
} catch (err) {
|
|
254
|
+
const errMessage = err instanceof Error ? err.toString() : "Unknown Error";
|
|
255
|
+
throw new EncryptedMediaError(
|
|
256
|
+
"MEDIA_KEYS_ATTACHMENT_ERROR",
|
|
257
|
+
"Could not attach the MediaKeys to the media element: " + errMessage,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function clearMediaKeys(
|
|
263
|
+
mediaElement: IMediaElement,
|
|
264
|
+
previousState: IMediaKeysAttacherItem | undefined,
|
|
265
|
+
): Promise<unknown> {
|
|
266
|
+
if (previousState === undefined) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
if (previousState.pendingTask !== null) {
|
|
271
|
+
// Ensure the `MediaKeys` has been fully attached to the HTMLMediaElement before
|
|
272
|
+
// resetting things, to avoid browser errors due to an invalid state.
|
|
273
|
+
await awaitMediaKeysAttachment(previousState);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (previousState.mediaKeysState === null) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
log.info("DRM: Disposing of the current MediaKeys");
|
|
281
|
+
const { loadedSessionsStore } = previousState.mediaKeysState;
|
|
282
|
+
await loadedSessionsStore.closeAllSessions();
|
|
283
|
+
return setMediaKeys(previousState.mediaKeysState.emeImplementation, mediaElement, null);
|
|
284
|
+
}
|
|
@@ -171,15 +171,16 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
171
171
|
if (position.isAwaitingFuturePosition()) {
|
|
172
172
|
playbackRateUpdater.stopRebuffering();
|
|
173
173
|
log.debug("Init: let rebuffering happen as we're awaiting a future position");
|
|
174
|
-
|
|
175
|
-
|
|
174
|
+
} else {
|
|
175
|
+
playbackRateUpdater.startRebuffering();
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
playbackRateUpdater.startRebuffering();
|
|
179
|
-
|
|
180
178
|
if (
|
|
181
179
|
this._manifest === null ||
|
|
182
180
|
(isSeekingApproximate &&
|
|
181
|
+
// Don't handle discontinuities on devices with broken seeks before
|
|
182
|
+
// enough time have passed because seeking brings more risks to
|
|
183
|
+
// lead to a lengthy rebuffering-exiting process
|
|
183
184
|
getMonotonicTimeStamp() - rebuffering.timestamp <= 1000)
|
|
184
185
|
) {
|
|
185
186
|
this.trigger("stalled", stalledReason);
|
|
@@ -189,6 +190,16 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
189
190
|
/** Position at which data is awaited. */
|
|
190
191
|
const { position: stalledPosition } = rebuffering;
|
|
191
192
|
|
|
193
|
+
/**
|
|
194
|
+
* We may still be in the process of waiting for a position to be seeked
|
|
195
|
+
* to. When calculating a potential position to e.g. skip over
|
|
196
|
+
* discontinuities, we should compare it to that "target" position if
|
|
197
|
+
* one, not the one we're currently playing.
|
|
198
|
+
*/
|
|
199
|
+
const targetTime = observation.position.isAwaitingFuturePosition()
|
|
200
|
+
? observation.position.getWanted()
|
|
201
|
+
: this._playbackObserver.getCurrentTime();
|
|
202
|
+
|
|
192
203
|
if (
|
|
193
204
|
stalledPosition !== null &&
|
|
194
205
|
stalledPosition !== undefined &&
|
|
@@ -201,10 +212,10 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
201
212
|
);
|
|
202
213
|
if (skippableDiscontinuity !== null) {
|
|
203
214
|
const realSeekTime = skippableDiscontinuity + 0.001;
|
|
204
|
-
if (realSeekTime <=
|
|
215
|
+
if (realSeekTime <= targetTime) {
|
|
205
216
|
log.info(
|
|
206
217
|
"Init: position to seek already reached, no seeking",
|
|
207
|
-
|
|
218
|
+
targetTime,
|
|
208
219
|
realSeekTime,
|
|
209
220
|
);
|
|
210
221
|
} else {
|
|
@@ -243,7 +254,7 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
243
254
|
nextBufferRangeGap < BUFFER_DISCONTINUITY_THRESHOLD
|
|
244
255
|
) {
|
|
245
256
|
const seekTo = positionBlockedAt + nextBufferRangeGap + EPSILON;
|
|
246
|
-
if (
|
|
257
|
+
if (targetTime < seekTo) {
|
|
247
258
|
log.warn(
|
|
248
259
|
"Init: discontinuity encountered inferior to the threshold",
|
|
249
260
|
positionBlockedAt,
|
|
@@ -266,8 +277,7 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
266
277
|
if (period.end !== undefined && period.end <= positionBlockedAt) {
|
|
267
278
|
if (
|
|
268
279
|
this._manifest.periods[i + 1].start > positionBlockedAt &&
|
|
269
|
-
this._manifest.periods[i + 1].start >
|
|
270
|
-
this._playbackObserver.getCurrentTime()
|
|
280
|
+
this._manifest.periods[i + 1].start > targetTime
|
|
271
281
|
) {
|
|
272
282
|
const nextPeriod = this._manifest.periods[i + 1];
|
|
273
283
|
this._playbackObserver.setCurrentTime(nextPeriod.start);
|
|
@@ -315,7 +325,7 @@ export default class RebufferingController extends EventEmitter<IRebufferingCont
|
|
|
315
325
|
}
|
|
316
326
|
const observation = this._playbackObserver.getReference().getValue();
|
|
317
327
|
if (
|
|
318
|
-
|
|
328
|
+
observation.rebuffering === null ||
|
|
319
329
|
observation.paused ||
|
|
320
330
|
this._speed.getValue() <= 0 ||
|
|
321
331
|
(bufferType !== "audio" && bufferType !== "video")
|