rx-player 3.27.0-dev.20220317 → 3.27.0-dev.2022032800

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.
Files changed (134) hide show
  1. package/.eslintrc.js +0 -4
  2. package/CHANGELOG.md +5 -2
  3. package/VERSION +1 -1
  4. package/dist/_esm5.processed/compat/eme/close_session.js +1 -2
  5. package/dist/_esm5.processed/compat/eme/custom_key_system_access.d.ts +15 -0
  6. package/dist/_esm5.processed/compat/eme/custom_key_system_access.js +1 -17
  7. package/dist/_esm5.processed/compat/eme/custom_media_keys/ie11_media_keys.js +6 -7
  8. package/dist/_esm5.processed/compat/eme/custom_media_keys/index.js +3 -4
  9. package/dist/_esm5.processed/compat/eme/custom_media_keys/old_webkit_media_keys.js +20 -17
  10. package/dist/_esm5.processed/compat/eme/custom_media_keys/webkit_media_keys.js +29 -14
  11. package/dist/_esm5.processed/compat/eme/generate_key_request.d.ts +4 -6
  12. package/dist/_esm5.processed/compat/eme/generate_key_request.js +4 -6
  13. package/dist/_esm5.processed/compat/eme/load_session.js +1 -2
  14. package/dist/_esm5.processed/config.d.ts +0 -3
  15. package/dist/_esm5.processed/core/api/option_utils.d.ts +3 -2
  16. package/dist/_esm5.processed/core/api/option_utils.js +2 -1
  17. package/dist/_esm5.processed/core/api/public_api.js +3 -4
  18. package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -2
  19. package/dist/_esm5.processed/core/decrypt/clear_on_stop.js +1 -2
  20. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +12 -5
  21. package/dist/_esm5.processed/core/decrypt/create_or_load_session.js +1 -1
  22. package/dist/_esm5.processed/core/decrypt/create_session.d.ts +3 -1
  23. package/dist/_esm5.processed/core/decrypt/create_session.js +16 -7
  24. package/dist/_esm5.processed/core/decrypt/find_key_system.js +1 -2
  25. package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +1 -2
  26. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.d.ts +94 -1
  27. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +238 -98
  28. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +1 -3
  29. package/dist/_esm5.processed/core/fetchers/utils/try_urls_with_backoff.js +2 -3
  30. package/dist/_esm5.processed/core/init/initialize_media_source.d.ts +2 -1
  31. package/dist/_esm5.processed/core/stream/index.d.ts +1 -0
  32. package/dist/_esm5.processed/core/stream/period/get_adaptation_switch_strategy.d.ts +15 -11
  33. package/dist/_esm5.processed/core/stream/period/get_adaptation_switch_strategy.js +5 -2
  34. package/dist/_esm5.processed/core/stream/period/index.d.ts +1 -0
  35. package/dist/_esm5.processed/core/stream/period/period_stream.d.ts +3 -11
  36. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +4 -3
  37. package/dist/_esm5.processed/default_config.d.ts +0 -27
  38. package/dist/_esm5.processed/default_config.js +0 -27
  39. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/thumbnail_loader.js +5 -6
  40. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/api/index.js +2 -3
  41. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/api/probeMediaConfiguration.d.ts +15 -0
  42. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/api/probeMediaConfiguration.js +1 -2
  43. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.js +2 -3
  44. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/HDCPPolicy.js +2 -3
  45. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/decodingInfo.js +1 -2
  46. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/mediaContentType.js +1 -2
  47. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/mediaContentTypeWithFeatures/index.js +1 -2
  48. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/probers/mediaDisplayInfos.js +1 -2
  49. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +4 -5
  50. package/dist/_esm5.processed/public_types.d.ts +1 -0
  51. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +1 -2
  52. package/dist/_esm5.processed/transports/dash/image_pipelines.d.ts +4 -3
  53. package/dist/_esm5.processed/transports/dash/image_pipelines.js +57 -11
  54. package/dist/_esm5.processed/transports/dash/init_segment_loader.js +1 -2
  55. package/dist/_esm5.processed/transports/dash/manifest_parser.js +2 -3
  56. package/dist/_esm5.processed/transports/dash/segment_loader.d.ts +1 -1
  57. package/dist/_esm5.processed/transports/dash/segment_loader.js +3 -4
  58. package/dist/_esm5.processed/transports/dash/text_loader.js +1 -2
  59. package/dist/_esm5.processed/transports/local/segment_loader.js +2 -3
  60. package/dist/_esm5.processed/transports/metaplaylist/pipelines.js +2 -3
  61. package/dist/_esm5.processed/transports/smooth/pipelines.js +3 -4
  62. package/dist/_esm5.processed/transports/smooth/segment_loader.js +3 -4
  63. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +1 -2
  64. package/dist/_esm5.processed/utils/cancellable_sleep.js +1 -2
  65. package/dist/_esm5.processed/utils/cast_to_observable.js +1 -3
  66. package/dist/_esm5.processed/utils/request/xhr.js +1 -2
  67. package/dist/rx-player.js +851 -937
  68. package/dist/rx-player.min.js +1 -1
  69. package/package.json +1 -2
  70. package/scripts/update-version +2 -2
  71. package/sonar-project.properties +1 -1
  72. package/src/compat/__tests__/play.test.ts +2 -4
  73. package/src/compat/eme/close_session.ts +1 -2
  74. package/src/compat/eme/custom_key_system_access.ts +1 -2
  75. package/src/compat/eme/custom_media_keys/ie11_media_keys.ts +6 -7
  76. package/src/compat/eme/custom_media_keys/index.ts +3 -4
  77. package/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts +21 -18
  78. package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +28 -16
  79. package/src/compat/eme/generate_key_request.ts +4 -6
  80. package/src/compat/eme/load_session.ts +1 -2
  81. package/src/core/api/__tests__/option_utils.test.ts +12 -0
  82. package/src/core/api/option_utils.ts +5 -3
  83. package/src/core/api/public_api.ts +3 -4
  84. package/src/core/decrypt/attach_media_keys.ts +1 -2
  85. package/src/core/decrypt/clear_on_stop.ts +1 -2
  86. package/src/core/decrypt/content_decryptor.ts +16 -6
  87. package/src/core/decrypt/create_or_load_session.ts +4 -1
  88. package/src/core/decrypt/create_session.ts +25 -12
  89. package/src/core/decrypt/find_key_system.ts +1 -2
  90. package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +1 -2
  91. package/src/core/decrypt/utils/loaded_sessions_store.ts +256 -105
  92. package/src/core/fetchers/manifest/manifest_fetcher.ts +1 -3
  93. package/src/core/fetchers/utils/try_urls_with_backoff.ts +2 -3
  94. package/src/core/init/initialize_media_source.ts +2 -1
  95. package/src/core/stream/index.ts +1 -0
  96. package/src/core/stream/period/get_adaptation_switch_strategy.ts +24 -14
  97. package/src/core/stream/period/index.ts +1 -0
  98. package/src/core/stream/period/period_stream.ts +5 -14
  99. package/src/core/stream/representation/append_segment_to_buffer.ts +17 -13
  100. package/src/default_config.ts +0 -30
  101. package/src/experimental/tools/VideoThumbnailLoader/thumbnail_loader.ts +5 -6
  102. package/src/experimental/tools/mediaCapabilitiesProber/__tests__/probers/HDCPPolicy.test.ts +5 -6
  103. package/src/experimental/tools/mediaCapabilitiesProber/__tests__/probers/decodingInfos.test.ts +2 -4
  104. package/src/experimental/tools/mediaCapabilitiesProber/api/index.ts +2 -3
  105. package/src/experimental/tools/mediaCapabilitiesProber/api/probeMediaConfiguration.ts +1 -2
  106. package/src/experimental/tools/mediaCapabilitiesProber/probers/DRMInfos.ts +2 -3
  107. package/src/experimental/tools/mediaCapabilitiesProber/probers/HDCPPolicy.ts +2 -3
  108. package/src/experimental/tools/mediaCapabilitiesProber/probers/decodingInfo.ts +1 -2
  109. package/src/experimental/tools/mediaCapabilitiesProber/probers/mediaContentType.ts +1 -2
  110. package/src/experimental/tools/mediaCapabilitiesProber/probers/mediaContentTypeWithFeatures/index.ts +1 -2
  111. package/src/experimental/tools/mediaCapabilitiesProber/probers/mediaDisplayInfos.ts +1 -2
  112. package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +4 -5
  113. package/src/public_types.ts +1 -0
  114. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +1 -2
  115. package/src/transports/dash/image_pipelines.ts +12 -14
  116. package/src/transports/dash/init_segment_loader.ts +1 -2
  117. package/src/transports/dash/manifest_parser.ts +2 -3
  118. package/src/transports/dash/segment_loader.ts +4 -5
  119. package/src/transports/dash/text_loader.ts +2 -3
  120. package/src/transports/local/segment_loader.ts +2 -3
  121. package/src/transports/metaplaylist/pipelines.ts +2 -3
  122. package/src/transports/smooth/pipelines.ts +4 -5
  123. package/src/transports/smooth/segment_loader.ts +5 -6
  124. package/src/transports/utils/call_custom_manifest_loader.ts +1 -2
  125. package/src/utils/cancellable_sleep.ts +1 -2
  126. package/src/utils/cast_to_observable.ts +0 -2
  127. package/src/utils/request/xhr.ts +1 -2
  128. package/tsconfig.json +0 -1
  129. package/tsconfig.modules.json +0 -1
  130. package/dist/_esm5.processed/utils/promise.d.ts +0 -17
  131. package/dist/_esm5.processed/utils/promise.js +0 -18
  132. package/src/typings/pinkie.d.ts +0 -19
  133. package/src/utils/__tests__/promise.test.ts +0 -23
  134. package/src/utils/promise.ts +0 -20
@@ -14,12 +14,9 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import PPromise from "pinkie";
18
- import {
19
- ICustomMediaKeySession,
20
- loadSession,
21
- } from "../../compat";
17
+ import { ICustomMediaKeySession } from "../../compat";
22
18
  import log from "../../log";
19
+ import { CancellationSignal } from "../../utils/task_canceller";
23
20
  import {
24
21
  IProcessedProtectionData,
25
22
  IMediaKeySessionStores,
@@ -43,12 +40,14 @@ import PersistentSessionsStore from "./utils/persistent_sessions_store";
43
40
  * @param {Object} stores
44
41
  * @param {Object} initData
45
42
  * @param {string} wantedSessionType
43
+ * @param {Object} cancelSignal
46
44
  * @returns {Promise}
47
45
  */
48
46
  export default function createSession(
49
47
  stores : IMediaKeySessionStores,
50
48
  initData : IProcessedProtectionData,
51
- wantedSessionType : MediaKeySessionType
49
+ wantedSessionType : MediaKeySessionType,
50
+ cancelSignal : CancellationSignal
52
51
  ) : Promise<ICreateSessionEvent> {
53
52
  const { loadedSessionsStore,
54
53
  persistentSessionsStore } = stores;
@@ -62,7 +61,8 @@ export default function createSession(
62
61
  }
63
62
  return createAndTryToRetrievePersistentSession(loadedSessionsStore,
64
63
  persistentSessionsStore,
65
- initData);
64
+ initData,
65
+ cancelSignal);
66
66
  }
67
67
 
68
68
  /**
@@ -78,8 +78,8 @@ function createTemporarySession(
78
78
  ) : Promise<INewSessionCreatedEvent> {
79
79
  log.info("DRM: Creating a new temporary session");
80
80
  const entry = loadedSessionsStore.createSession(initData, "temporary");
81
- return PPromise.resolve({ type: MediaKeySessionLoadingType.Created,
82
- value: entry });
81
+ return Promise.resolve({ type: MediaKeySessionLoadingType.Created,
82
+ value: entry });
83
83
  }
84
84
 
85
85
  /**
@@ -88,13 +88,18 @@ function createTemporarySession(
88
88
  * @param {Object} loadedSessionsStore
89
89
  * @param {Object} persistentSessionsStore
90
90
  * @param {Object} initData
91
+ * @param {Object} cancelSignal
91
92
  * @returns {Promise}
92
93
  */
93
94
  async function createAndTryToRetrievePersistentSession(
94
95
  loadedSessionsStore : LoadedSessionsStore,
95
96
  persistentSessionsStore : PersistentSessionsStore,
96
- initData : IProcessedProtectionData
97
+ initData : IProcessedProtectionData,
98
+ cancelSignal : CancellationSignal
97
99
  ) : Promise<INewSessionCreatedEvent | IPersistentSessionRecoveryEvent> {
100
+ if (cancelSignal.cancellationError !== null) {
101
+ throw cancelSignal.cancellationError;
102
+ }
98
103
  log.info("DRM: Creating persistent MediaKeySession");
99
104
 
100
105
  const entry = loadedSessionsStore.createSession(initData, "persistent-license");
@@ -105,8 +110,10 @@ async function createAndTryToRetrievePersistentSession(
105
110
  }
106
111
 
107
112
  try {
108
- const hasLoadedSession = await loadSession(entry.mediaKeySession,
109
- storedEntry.sessionId);
113
+ const hasLoadedSession = await loadedSessionsStore.loadPersistentSession(
114
+ entry.mediaKeySession,
115
+ storedEntry.sessionId
116
+ );
110
117
  if (!hasLoadedSession) {
111
118
  log.warn("DRM: No data stored for the loaded session");
112
119
  persistentSessionsStore.delete(storedEntry.sessionId);
@@ -137,6 +144,9 @@ async function createAndTryToRetrievePersistentSession(
137
144
  * @returns {Observable}
138
145
  */
139
146
  async function recreatePersistentSession() : Promise<INewSessionCreatedEvent> {
147
+ if (cancelSignal.cancellationError !== null) {
148
+ throw cancelSignal.cancellationError;
149
+ }
140
150
  log.info("DRM: Removing previous persistent session.");
141
151
  const persistentEntry = persistentSessionsStore.get(initData);
142
152
  if (persistentEntry !== null) {
@@ -144,6 +154,9 @@ async function createAndTryToRetrievePersistentSession(
144
154
  }
145
155
 
146
156
  await loadedSessionsStore.closeSession(entry.mediaKeySession);
157
+ if (cancelSignal.cancellationError !== null) {
158
+ throw cancelSignal.cancellationError;
159
+ }
147
160
  const newEntry = loadedSessionsStore.createSession(initData,
148
161
  "persistent-license");
149
162
  return { type: MediaKeySessionLoadingType.Created,
@@ -24,7 +24,6 @@ import { EncryptedMediaError } from "../../errors";
24
24
  import log from "../../log";
25
25
  import arrayIncludes from "../../utils/array_includes";
26
26
  import flatMap from "../../utils/flat_map";
27
- import PPromise from "../../utils/promise";
28
27
  import { CancellationSignal } from "../../utils/task_canceller";
29
28
  import { IKeySystemOption } from "./types";
30
29
  import MediaKeysInfosStore from "./utils/media_keys_infos_store";
@@ -258,7 +257,7 @@ export default function getMediaKeySystemAccess(
258
257
  currentState.keySystemOptions);
259
258
  if (cachedKeySystemAccess !== null) {
260
259
  log.info("DRM: Found cached compatible keySystem");
261
- return PPromise.resolve({
260
+ return Promise.resolve({
262
261
  type: "reuse-media-key-system-access" as const,
263
262
  value: { mediaKeySystemAccess: cachedKeySystemAccess.keySystemAccess,
264
263
  options: cachedKeySystemAccess.keySystemOptions },
@@ -14,7 +14,6 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import PPromise from "../../../utils/promise";
18
17
  import LoadedSessionsStore from "./loaded_sessions_store";
19
18
 
20
19
  /**
@@ -41,5 +40,5 @@ export default async function cleanOldLoadedSessions(
41
40
  const entry = entries[i];
42
41
  proms.push(loadedSessionsStore.closeSession(entry.mediaKeySession));
43
42
  }
44
- await PPromise.all(proms);
43
+ await Promise.all(proms);
45
44
  }
@@ -14,18 +14,13 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import PPromise from "pinkie";
18
- import { Subscription } from "rxjs";
19
17
  import {
20
18
  closeSession,
19
+ generateKeyRequest,
21
20
  ICustomMediaKeys,
22
21
  ICustomMediaKeySession,
22
+ loadSession,
23
23
  } from "../../../compat";
24
- import {
25
- onKeyMessage$,
26
- onKeyStatusesChange$,
27
- } from "../../../compat/event_listeners";
28
- import config from "../../../config";
29
24
  import log from "../../../log";
30
25
  import isNullOrUndefined from "../../../utils/is_null_or_undefined";
31
26
  import { IProcessedProtectionData } from "../types";
@@ -58,7 +53,7 @@ export default class LoadedSessionsStore {
58
53
 
59
54
  /**
60
55
  * Create a new MediaKeySession and store it in this store.
61
- * @param {Object} initializationData
56
+ * @param {Object} initData
62
57
  * @param {string} sessionType
63
58
  * @returns {Object}
64
59
  */
@@ -68,7 +63,12 @@ export default class LoadedSessionsStore {
68
63
  ) : IStoredSessionEntry {
69
64
  const keySessionRecord = new KeySessionRecord(initData);
70
65
  const mediaKeySession = this._mediaKeys.createSession(sessionType);
71
- const entry = { mediaKeySession, sessionType, keySessionRecord };
66
+ const entry = { mediaKeySession,
67
+ sessionType,
68
+ keySessionRecord,
69
+ isGeneratingRequest: false,
70
+ isLoadingPersistentSession: false,
71
+ closingStatus: { type: "none" as const } };
72
72
  if (!isNullOrUndefined(mediaKeySession.closed)) {
73
73
  mediaKeySession.closed
74
74
  .then(() => {
@@ -86,7 +86,7 @@ export default class LoadedSessionsStore {
86
86
  }
87
87
 
88
88
  log.debug("DRM-LSS: Add MediaKeySession", entry.sessionType);
89
- this._storage.push({ keySessionRecord, mediaKeySession, sessionType });
89
+ this._storage.push({ ...entry });
90
90
  return entry;
91
91
  }
92
92
 
@@ -110,14 +110,145 @@ export default class LoadedSessionsStore {
110
110
  if (stored.keySessionRecord.isCompatibleWith(initializationData)) {
111
111
  this._storage.splice(i, 1);
112
112
  this._storage.push(stored);
113
- return { keySessionRecord: stored.keySessionRecord,
114
- mediaKeySession: stored.mediaKeySession,
115
- sessionType: stored.sessionType };
113
+ return { ...stored };
114
+ }
115
+ }
116
+ return null;
117
+ }
118
+
119
+ /**
120
+ * Get `LoadedSessionsStore`'s entry for a given MediaKeySession.
121
+ * Returns `null` if the given MediaKeySession is not stored in the
122
+ * `LoadedSessionsStore`.
123
+ * @param {MediaKeySession} mediaKeySession
124
+ * @returns {Object|null}
125
+ */
126
+ public getEntryForSession(
127
+ mediaKeySession : MediaKeySession | ICustomMediaKeySession
128
+ ) : IStoredSessionEntry | null {
129
+ for (let i = this._storage.length - 1; i >= 0; i--) {
130
+ const stored = this._storage[i];
131
+ if (stored.mediaKeySession === mediaKeySession) {
132
+ return { ...stored };
116
133
  }
117
134
  }
118
135
  return null;
119
136
  }
120
137
 
138
+ /**
139
+ * Generate a license request on the given MediaKeySession, while indicating
140
+ * to the LoadedSessionsStore that a license-request is pending so
141
+ * session-closing orders are properly scheduled after it is done.
142
+ * @param {Object} mediaKeySession
143
+ * @param {string} initializationDataType - Initialization data type given
144
+ * e.g. by the "encrypted" event for the corresponding request.
145
+ * @param {Uint8Array} initializationData - Initialization data given e.g. by
146
+ * the "encrypted" event for the corresponding request.
147
+ * @returns {Promise}
148
+ */
149
+ public async generateLicenseRequest(
150
+ mediaKeySession : MediaKeySession | ICustomMediaKeySession,
151
+ initializationDataType : string | undefined,
152
+ initializationData : Uint8Array
153
+ ) : Promise<unknown> {
154
+ let entry : IStoredSessionEntry | undefined;
155
+ for (const stored of this._storage) {
156
+ if (stored.mediaKeySession === mediaKeySession) {
157
+ entry = stored;
158
+ break;
159
+ }
160
+ }
161
+ if (entry === undefined) {
162
+ log.error("DRM-LSS: generateRequest error. No MediaKeySession found with " +
163
+ "the given initData and initDataType");
164
+ return generateKeyRequest(mediaKeySession,
165
+ initializationDataType,
166
+ initializationData);
167
+ }
168
+
169
+ entry.isGeneratingRequest = true;
170
+
171
+ // Note the `as string` is needed due to TypeScript not understanding that
172
+ // the `closingStatus` might change in the next checks
173
+ if (entry.closingStatus.type as string !== "none") {
174
+ throw new Error("The `MediaKeySession` is being closed.");
175
+ }
176
+ try {
177
+ await generateKeyRequest(mediaKeySession,
178
+ initializationDataType,
179
+ initializationData);
180
+ } catch (err) {
181
+ if (entry === undefined) {
182
+ throw err;
183
+ }
184
+ entry.isGeneratingRequest = false;
185
+ if (entry.closingStatus.type === "awaiting") {
186
+ entry.closingStatus.start();
187
+ }
188
+ throw err;
189
+
190
+ }
191
+ if (entry === undefined) {
192
+ return undefined;
193
+ }
194
+ entry.isGeneratingRequest = false;
195
+ if (entry.closingStatus.type === "awaiting") {
196
+ entry.closingStatus.start();
197
+ }
198
+ }
199
+
200
+ /**
201
+ * @param {Object} mediaKeySession
202
+ * @param {string} sessionId
203
+ * @returns {Promise}
204
+ */
205
+ public async loadPersistentSession(
206
+ mediaKeySession : MediaKeySession | ICustomMediaKeySession,
207
+ sessionId : string
208
+ ) : Promise<boolean> {
209
+ let entry : IStoredSessionEntry | undefined;
210
+ for (const stored of this._storage) {
211
+ if (stored.mediaKeySession === mediaKeySession) {
212
+ entry = stored;
213
+ break;
214
+ }
215
+ }
216
+ if (entry === undefined) {
217
+ log.error("DRM-LSS: loadPersistentSession error. No MediaKeySession found with " +
218
+ "the given initData and initDataType");
219
+ return loadSession(mediaKeySession, sessionId);
220
+ }
221
+ entry.isLoadingPersistentSession = true;
222
+
223
+ // Note the `as string` is needed due to TypeScript not understanding that
224
+ // the `closingStatus` might change in the next checks
225
+ if (entry.closingStatus.type as string !== "none") {
226
+ throw new Error("The `MediaKeySession` is being closed.");
227
+ }
228
+ let ret : boolean;
229
+ try {
230
+ ret = await loadSession(mediaKeySession, sessionId);
231
+ } catch (err) {
232
+ if (entry === undefined) {
233
+ throw err;
234
+ }
235
+ entry.isLoadingPersistentSession = false;
236
+ if (entry.closingStatus.type === "awaiting") {
237
+ entry.closingStatus.start();
238
+ }
239
+ throw err;
240
+
241
+ }
242
+ if (entry === undefined) {
243
+ return ret;
244
+ }
245
+ entry.isLoadingPersistentSession = false;
246
+ if (entry.closingStatus.type === "awaiting") {
247
+ entry.closingStatus.start();
248
+ }
249
+ return ret;
250
+ }
251
+
121
252
  /**
122
253
  * Close a MediaKeySession and remove its related stored information from the
123
254
  * `LoadedSessionsStore`.
@@ -128,7 +259,7 @@ export default class LoadedSessionsStore {
128
259
  public async closeSession(
129
260
  mediaKeySession : MediaKeySession | ICustomMediaKeySession
130
261
  ) : Promise<boolean> {
131
- let entry;
262
+ let entry : IStoredSessionEntry | undefined;
132
263
  for (const stored of this._storage) {
133
264
  if (stored.mediaKeySession === mediaKeySession) {
134
265
  entry = stored;
@@ -138,10 +269,9 @@ export default class LoadedSessionsStore {
138
269
  if (entry === undefined) {
139
270
  log.warn("DRM-LSS: No MediaKeySession found with " +
140
271
  "the given initData and initDataType");
141
- return PPromise.resolve(false);
272
+ return Promise.resolve(false);
142
273
  }
143
- await safelyCloseMediaKeySession(entry.mediaKeySession);
144
- return PPromise.resolve(true);
274
+ return this._closeEntry(entry);
145
275
  }
146
276
 
147
277
  /**
@@ -176,10 +306,17 @@ export default class LoadedSessionsStore {
176
306
  this._storage = [];
177
307
 
178
308
  const closingProms = allEntries
179
- .map((entry) => safelyCloseMediaKeySession(entry.mediaKeySession));
180
- await PPromise.all(closingProms);
309
+ .map((entry) => this._closeEntry(entry));
310
+ await Promise.all(closingProms);
181
311
  }
182
312
 
313
+ /**
314
+ * Get the index of a stored MediaKeySession entry based on its
315
+ * `KeySessionRecord`.
316
+ * Returns -1 if not found.
317
+ * @param {Object} record
318
+ * @returns {number}
319
+ */
183
320
  private getIndex(record : KeySessionRecord) : number {
184
321
  for (let i = 0; i < this._storage.length; i++) {
185
322
  const stored = this._storage[i];
@@ -189,6 +326,50 @@ export default class LoadedSessionsStore {
189
326
  }
190
327
  return -1;
191
328
  }
329
+
330
+ /**
331
+ * Prepare the closure of a `MediaKeySession` stored as an entry of the
332
+ * `LoadedSessionsStore`.
333
+ * Allows to postpone the closure action if another MediaKeySession action
334
+ * is already pending.
335
+ * @param {Object} entry
336
+ * @returns {Promise.<boolean>}
337
+ */
338
+ private async _closeEntry(
339
+ entry : IStoredSessionEntry
340
+ ) : Promise<boolean> {
341
+ const { mediaKeySession } = entry;
342
+ return new Promise((resolve, reject) => {
343
+ if (entry !== undefined &&
344
+ (
345
+ entry.isLoadingPersistentSession || entry.isGeneratingRequest
346
+ ))
347
+ {
348
+ entry.closingStatus = { type: "awaiting",
349
+ start: tryClosingEntryAndResolve };
350
+ } else {
351
+ tryClosingEntryAndResolve();
352
+ }
353
+ function tryClosingEntryAndResolve() {
354
+ if (entry !== undefined) {
355
+ entry.closingStatus = { type: "pending" };
356
+ }
357
+ safelyCloseMediaKeySession(mediaKeySession)
358
+ .then(() => {
359
+ if (entry !== undefined) {
360
+ entry.closingStatus = { type: "done" };
361
+ }
362
+ resolve(true);
363
+ })
364
+ .catch((err) => {
365
+ if (entry !== undefined) {
366
+ entry.closingStatus = { type: "failed" };
367
+ }
368
+ reject(err);
369
+ });
370
+ }
371
+ });
372
+ }
192
373
  }
193
374
 
194
375
  /** Information linked to a `MediaKeySession` created by the `LoadedSessionsStore`. */
@@ -217,102 +398,72 @@ export interface IStoredSessionEntry {
217
398
  * which the MediaKeySession was created.
218
399
  */
219
400
  sessionType : MediaKeySessionType;
401
+
402
+ /**
403
+ * Set to `true` while a `generateRequest` call is pending.
404
+ * This information might be useful as it is one of the operation we have to
405
+ * wait for before closing a MediaKeySession.
406
+ */
407
+ isGeneratingRequest : boolean;
408
+
409
+ /**
410
+ * Set to `true` while a `load` call is pending.
411
+ * This information might be useful as it is one of the operation we have to
412
+ * wait for before closing a MediaKeySession.
413
+ */
414
+ isLoadingPersistentSession : boolean;
415
+
416
+ /**
417
+ * The status of a potential `MediaKeySession`'s close request.
418
+ * Closing a MediaKeySession could be made complex as it normally cannot
419
+ * happen until `generateRequest` or `load` has been called.
420
+ *
421
+ * To avoid problems while still staying compatible to the most devices
422
+ * possible - which may have strange implementation of the specification -
423
+ * we're adding the `closingStatus` property allowing to perform multiple
424
+ * type of interaction while a close operation is either pending or is
425
+ * awaited.
426
+ */
427
+ closingStatus :
428
+ /** Status when the MediaKeySession is currently being closed. */
429
+ { type : "pending" } |
430
+ /** Status when the MediaKeySession has been closed. */
431
+ { type : "done" } |
432
+ /** Status when the MediaKeySession failed to close. */
433
+ { type : "failed" } |
434
+ /**
435
+ * Status when a close order has been received for this MediaKeySession
436
+ * while some sensitive operation (examples are `generateRequest` and `load`
437
+ * calls).
438
+ * The `LoadedSessionsStore` should call `start` once it has finished those
439
+ * operations.
440
+ */
441
+ {
442
+ type : "awaiting";
443
+ start : () => void;
444
+ } |
445
+ /** Status when the MediaKeySession failed to close. */
446
+ { type : "none" };
220
447
  }
221
448
 
222
449
  /**
223
- * Close a MediaKeySession with multiple attempts if needed and do not throw if
224
- * this action throws an error.
450
+ * Close a MediaKeySession and just log an error if it fails (while resolving).
225
451
  * Emits then complete when done.
226
452
  * @param {MediaKeySession} mediaKeySession
227
453
  * @returns {Observable}
228
454
  */
229
- function safelyCloseMediaKeySession(
455
+ async function safelyCloseMediaKeySession(
230
456
  mediaKeySession : MediaKeySession | ICustomMediaKeySession
231
- ) : Promise<unknown> {
232
- return recursivelyTryToCloseMediaKeySession(0);
233
-
234
- /**
235
- * Perform a new attempt at closing the MediaKeySession.
236
- * If this operation fails due to a not-"callable" (an EME term)
237
- * MediaKeySession, retry based on either a timer or on MediaKeySession
238
- * events, whichever comes first.
239
- * Emits then complete when done.
240
- * @param {number} retryNb - The attempt number starting at 0.
241
- * @returns {Observable}
242
- */
243
- async function recursivelyTryToCloseMediaKeySession(
244
- retryNb : number
245
- ) : Promise<unknown> {
246
- log.debug("DRM: Trying to close a MediaKeySession",
247
- mediaKeySession.sessionId,
248
- retryNb);
249
- try {
250
- await closeSession(mediaKeySession);
251
- log.debug("DRM: Succeeded to close MediaKeySession");
252
- return undefined;
253
- } catch (err : unknown) {
254
- // Unitialized MediaKeySession may not close properly until their
255
- // corresponding `generateRequest` or `load` call are handled by the
256
- // browser.
257
- // In that case the EME specification tells us that the browser is
258
- // supposed to reject the `close` call with an InvalidStateError.
259
- if (!(err instanceof Error) || err.name !== "InvalidStateError" ||
260
- mediaKeySession.sessionId !== "")
261
- {
262
- return failToCloseSession(err);
263
- }
264
-
265
- const { EME_SESSION_CLOSING_MAX_RETRY,
266
- EME_SESSION_CLOSING_INITIAL_DELAY,
267
- EME_SESSION_CLOSING_MAX_DELAY } = config.getCurrent();
268
-
269
- // We will retry either:
270
- // - when an event indicates that the MediaKeySession is
271
- // initialized (`callable` is the proper EME term here)
272
- // - after a delay, raising exponentially
273
- const nextRetryNb = retryNb + 1;
274
- if (nextRetryNb > EME_SESSION_CLOSING_MAX_RETRY) {
275
- return failToCloseSession(err);
276
- }
277
- const delay = Math.min(Math.pow(2, retryNb) * EME_SESSION_CLOSING_INITIAL_DELAY,
278
- EME_SESSION_CLOSING_MAX_DELAY);
279
- log.warn("DRM: attempt to close a mediaKeySession failed, " +
280
- "scheduling retry...", delay);
281
-
282
- let ksChangeSub : undefined | Subscription;
283
- const ksChangeProm = new Promise((res) => {
284
- ksChangeSub = onKeyStatusesChange$(mediaKeySession).subscribe(res);
285
- });
286
-
287
- let ksMsgSub : undefined | Subscription;
288
- const ksMsgProm = new Promise((res) => {
289
- ksMsgSub = onKeyMessage$(mediaKeySession).subscribe(res);
290
- });
291
-
292
- let sleepTimer : undefined | number;
293
- const sleepProm = new Promise((res) => {
294
- sleepTimer = window.setTimeout(res, delay);
295
- });
296
-
297
- await PPromise.race([ksChangeProm, ksMsgProm, sleepProm]);
298
- ksChangeSub?.unsubscribe();
299
- ksMsgSub?.unsubscribe();
300
- clearTimeout(sleepTimer);
301
-
302
- return recursivelyTryToCloseMediaKeySession(nextRetryNb);
303
- }
304
- }
305
-
306
- /**
307
- * Log error anouncing that we could not close the MediaKeySession and emits
308
- * then complete through Observable.
309
- * TODO Emit warning?
310
- * @returns {Observable}
311
- */
312
- function failToCloseSession(err : unknown) : Promise<null> {
457
+ ) : Promise<void> {
458
+ log.debug("DRM: Trying to close a MediaKeySession", mediaKeySession.sessionId);
459
+ try {
460
+ await closeSession(mediaKeySession);
461
+ log.debug("DRM: Succeeded to close MediaKeySession");
462
+ return ;
463
+ } catch (err : unknown) {
313
464
  log.error("DRM: Could not close MediaKeySession: " +
314
465
  (err instanceof Error ? err.toString() :
315
466
  "Unknown error"));
316
- return PPromise.resolve(null);
467
+ return ;
317
468
  }
318
469
  }
@@ -30,7 +30,6 @@ import {
30
30
  ITransportPipelines,
31
31
  } from "../../../transports";
32
32
  import assert from "../../../utils/assert";
33
- import PPromise from "../../../utils/promise";
34
33
  import TaskCanceller from "../../../utils/task_canceller";
35
34
  import errorSelector from "../utils/error_selector";
36
35
  import {
@@ -431,6 +430,5 @@ export default class ManifestFetcher {
431
430
  * @returns {boolean}
432
431
  */
433
432
  function isPromise<T>(val : T | Promise<T>) : val is Promise<T> {
434
- return val instanceof PPromise ||
435
- val instanceof Promise;
433
+ return val instanceof Promise;
436
434
  }
@@ -24,7 +24,6 @@ import {
24
24
  import log from "../../../log";
25
25
  import cancellableSleep from "../../../utils/cancellable_sleep";
26
26
  import getFuzzedDelay from "../../../utils/get_fuzzed_delay";
27
- import PPromise from "../../../utils/promise";
28
27
  import TaskCanceller, {
29
28
  CancellationSignal,
30
29
  } from "../../../utils/task_canceller";
@@ -171,7 +170,7 @@ export function tryURLsWithBackoff<T>(
171
170
  cancellationSignal : CancellationSignal
172
171
  ) : Promise<T> {
173
172
  if (cancellationSignal.isCancelled) {
174
- return PPromise.reject(cancellationSignal.cancellationError);
173
+ return Promise.reject(cancellationSignal.cancellationError);
175
174
  }
176
175
 
177
176
  const { baseDelay,
@@ -185,7 +184,7 @@ export function tryURLsWithBackoff<T>(
185
184
  const urlsToTry = urls.slice();
186
185
  if (urlsToTry.length === 0) {
187
186
  log.warn("Fetchers: no URL given to `tryURLsWithBackoff`.");
188
- return PPromise.reject(new Error("No URL to request"));
187
+ return Promise.reject(new Error("No URL to request"));
189
188
  }
190
189
  return tryURLsRecursively(urlsToTry[0], 0);
191
190
 
@@ -56,6 +56,7 @@ import {
56
56
  SegmentFetcherCreator,
57
57
  } from "../fetchers";
58
58
  import { ITextTrackSegmentBufferOptions } from "../segment_buffers";
59
+ import { IAudioTrackSwitchingMode } from "../stream";
59
60
  import openMediaSource from "./create_media_source";
60
61
  import EVENTS from "./events_generators";
61
62
  import getInitialTime, {
@@ -105,7 +106,7 @@ export interface IInitializeArguments {
105
106
  */
106
107
  enableFastSwitching : boolean;
107
108
  /** Strategy when switching of audio track. */
108
- audioTrackSwitchingMode : "seamless" | "direct";
109
+ audioTrackSwitchingMode : IAudioTrackSwitchingMode;
109
110
  /** Behavior when a new video and/or audio codec is encountered. */
110
111
  onCodecSwitch : "continue" | "reload";
111
112
  };
@@ -18,6 +18,7 @@ import StreamOrchestrator, {
18
18
  IStreamOrchestratorOptions,
19
19
  IStreamOrchestratorPlaybackObservation,
20
20
  } from "./orchestrator";
21
+ export { IAudioTrackSwitchingMode } from "./period";
21
22
  export * from "./types";
22
23
 
23
24
  export default StreamOrchestrator;