rx-player 3.27.0-dev.2022032100 → 3.27.0

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 (60) hide show
  1. package/CHANGELOG.md +5 -2
  2. package/VERSION +1 -1
  3. package/dist/_esm5.processed/compat/eme/custom_media_keys/old_webkit_media_keys.js +15 -11
  4. package/dist/_esm5.processed/compat/eme/custom_media_keys/webkit_media_keys.js +22 -6
  5. package/dist/_esm5.processed/compat/eme/generate_key_request.d.ts +4 -6
  6. package/dist/_esm5.processed/compat/eme/generate_key_request.js +4 -6
  7. package/dist/_esm5.processed/compat/get_start_date.d.ts +30 -0
  8. package/dist/_esm5.processed/compat/get_start_date.js +44 -0
  9. package/dist/_esm5.processed/compat/index.d.ts +2 -1
  10. package/dist/_esm5.processed/compat/index.js +2 -1
  11. package/dist/_esm5.processed/config.d.ts +1 -5
  12. package/dist/_esm5.processed/core/api/public_api.js +25 -25
  13. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +11 -3
  14. package/dist/_esm5.processed/core/decrypt/create_or_load_session.js +1 -1
  15. package/dist/_esm5.processed/core/decrypt/create_session.d.ts +3 -1
  16. package/dist/_esm5.processed/core/decrypt/create_session.js +15 -5
  17. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.d.ts +94 -1
  18. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +237 -96
  19. package/dist/_esm5.processed/core/segment_buffers/garbage_collector.js +4 -1
  20. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +2 -1
  21. package/dist/_esm5.processed/core/stream/period/period_stream.js +9 -3
  22. package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +4 -3
  23. package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.js +3 -2
  24. package/dist/_esm5.processed/core/stream/representation/get_buffer_status.d.ts +2 -2
  25. package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +9 -3
  26. package/dist/_esm5.processed/core/stream/representation/get_needed_segments.d.ts +11 -1
  27. package/dist/_esm5.processed/core/stream/representation/get_needed_segments.js +27 -45
  28. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +6 -4
  29. package/dist/_esm5.processed/default_config.d.ts +2 -35
  30. package/dist/_esm5.processed/default_config.js +2 -35
  31. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +39 -38
  32. package/dist/_esm5.processed/utils/reference.js +0 -2
  33. package/dist/_esm5.processed/utils/task_canceller.d.ts +8 -1
  34. package/dist/_esm5.processed/utils/task_canceller.js +9 -1
  35. package/dist/rx-player.js +927 -587
  36. package/dist/rx-player.min.js +1 -1
  37. package/package.json +1 -1
  38. package/sonar-project.properties +1 -1
  39. package/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts +16 -12
  40. package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +21 -8
  41. package/src/compat/eme/generate_key_request.ts +4 -6
  42. package/src/compat/get_start_date.ts +48 -0
  43. package/src/compat/index.ts +2 -0
  44. package/src/core/api/public_api.ts +23 -27
  45. package/src/core/decrypt/content_decryptor.ts +15 -4
  46. package/src/core/decrypt/create_or_load_session.ts +4 -1
  47. package/src/core/decrypt/create_session.ts +23 -9
  48. package/src/core/decrypt/utils/loaded_sessions_store.ts +254 -102
  49. package/src/core/segment_buffers/garbage_collector.ts +4 -0
  50. package/src/core/stream/orchestrator/stream_orchestrator.ts +2 -1
  51. package/src/core/stream/period/period_stream.ts +9 -4
  52. package/src/core/stream/representation/append_segment_to_buffer.ts +17 -13
  53. package/src/core/stream/representation/force_garbage_collection.ts +4 -1
  54. package/src/core/stream/representation/get_buffer_status.ts +21 -13
  55. package/src/core/stream/representation/get_needed_segments.ts +40 -55
  56. package/src/core/stream/representation/representation_stream.ts +6 -4
  57. package/src/default_config.ts +20 -57
  58. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +41 -44
  59. package/src/utils/reference.ts +0 -2
  60. package/src/utils/task_canceller.ts +16 -1
@@ -37,7 +37,7 @@ export default class LoadedSessionsStore {
37
37
  constructor(mediaKeys: MediaKeys | ICustomMediaKeys);
38
38
  /**
39
39
  * Create a new MediaKeySession and store it in this store.
40
- * @param {Object} initializationData
40
+ * @param {Object} initData
41
41
  * @param {string} sessionType
42
42
  * @returns {Object}
43
43
  */
@@ -55,6 +55,32 @@ export default class LoadedSessionsStore {
55
55
  * @returns {Object|null}
56
56
  */
57
57
  reuse(initializationData: IProcessedProtectionData): IStoredSessionEntry | null;
58
+ /**
59
+ * Get `LoadedSessionsStore`'s entry for a given MediaKeySession.
60
+ * Returns `null` if the given MediaKeySession is not stored in the
61
+ * `LoadedSessionsStore`.
62
+ * @param {MediaKeySession} mediaKeySession
63
+ * @returns {Object|null}
64
+ */
65
+ getEntryForSession(mediaKeySession: MediaKeySession | ICustomMediaKeySession): IStoredSessionEntry | null;
66
+ /**
67
+ * Generate a license request on the given MediaKeySession, while indicating
68
+ * to the LoadedSessionsStore that a license-request is pending so
69
+ * session-closing orders are properly scheduled after it is done.
70
+ * @param {Object} mediaKeySession
71
+ * @param {string} initializationDataType - Initialization data type given
72
+ * e.g. by the "encrypted" event for the corresponding request.
73
+ * @param {Uint8Array} initializationData - Initialization data given e.g. by
74
+ * the "encrypted" event for the corresponding request.
75
+ * @returns {Promise}
76
+ */
77
+ generateLicenseRequest(mediaKeySession: MediaKeySession | ICustomMediaKeySession, initializationDataType: string | undefined, initializationData: Uint8Array): Promise<unknown>;
78
+ /**
79
+ * @param {Object} mediaKeySession
80
+ * @param {string} sessionId
81
+ * @returns {Promise}
82
+ */
83
+ loadPersistentSession(mediaKeySession: MediaKeySession | ICustomMediaKeySession, sessionId: string): Promise<boolean>;
58
84
  /**
59
85
  * Close a MediaKeySession and remove its related stored information from the
60
86
  * `LoadedSessionsStore`.
@@ -80,7 +106,23 @@ export default class LoadedSessionsStore {
80
106
  * @returns {Promise}
81
107
  */
82
108
  closeAllSessions(): Promise<void>;
109
+ /**
110
+ * Get the index of a stored MediaKeySession entry based on its
111
+ * `KeySessionRecord`.
112
+ * Returns -1 if not found.
113
+ * @param {Object} record
114
+ * @returns {number}
115
+ */
83
116
  private getIndex;
117
+ /**
118
+ * Prepare the closure of a `MediaKeySession` stored as an entry of the
119
+ * `LoadedSessionsStore`.
120
+ * Allows to postpone the closure action if another MediaKeySession action
121
+ * is already pending.
122
+ * @param {Object} entry
123
+ * @returns {Promise.<boolean>}
124
+ */
125
+ private _closeEntry;
84
126
  }
85
127
  /** Information linked to a `MediaKeySession` created by the `LoadedSessionsStore`. */
86
128
  export interface IStoredSessionEntry {
@@ -105,4 +147,55 @@ export interface IStoredSessionEntry {
105
147
  * which the MediaKeySession was created.
106
148
  */
107
149
  sessionType: MediaKeySessionType;
150
+ /**
151
+ * Set to `true` while a `generateRequest` call is pending.
152
+ * This information might be useful as it is one of the operation we have to
153
+ * wait for before closing a MediaKeySession.
154
+ */
155
+ isGeneratingRequest: boolean;
156
+ /**
157
+ * Set to `true` while a `load` call is pending.
158
+ * This information might be useful as it is one of the operation we have to
159
+ * wait for before closing a MediaKeySession.
160
+ */
161
+ isLoadingPersistentSession: boolean;
162
+ /**
163
+ * The status of a potential `MediaKeySession`'s close request.
164
+ * Closing a MediaKeySession could be made complex as it normally cannot
165
+ * happen until `generateRequest` or `load` has been called.
166
+ *
167
+ * To avoid problems while still staying compatible to the most devices
168
+ * possible - which may have strange implementation of the specification -
169
+ * we're adding the `closingStatus` property allowing to perform multiple
170
+ * type of interaction while a close operation is either pending or is
171
+ * awaited.
172
+ */
173
+ closingStatus:
174
+ /** Status when the MediaKeySession is currently being closed. */
175
+ {
176
+ type: "pending";
177
+ } |
178
+ /** Status when the MediaKeySession has been closed. */
179
+ {
180
+ type: "done";
181
+ } |
182
+ /** Status when the MediaKeySession failed to close. */
183
+ {
184
+ type: "failed";
185
+ } |
186
+ /**
187
+ * Status when a close order has been received for this MediaKeySession
188
+ * while some sensitive operation (examples are `generateRequest` and `load`
189
+ * calls).
190
+ * The `LoadedSessionsStore` should call `start` once it has finished those
191
+ * operations.
192
+ */
193
+ {
194
+ type: "awaiting";
195
+ start: () => void;
196
+ } |
197
+ /** Status when the MediaKeySession failed to close. */
198
+ {
199
+ type: "none";
200
+ };
108
201
  }
@@ -13,6 +13,17 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
+ var __assign = (this && this.__assign) || function () {
17
+ __assign = Object.assign || function(t) {
18
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
19
+ s = arguments[i];
20
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
21
+ t[p] = s[p];
22
+ }
23
+ return t;
24
+ };
25
+ return __assign.apply(this, arguments);
26
+ };
16
27
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
28
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
29
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -49,9 +60,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
49
60
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
50
61
  }
51
62
  };
52
- import { closeSession, } from "../../../compat";
53
- import { onKeyMessage$, onKeyStatusesChange$, } from "../../../compat/event_listeners";
54
- import config from "../../../config";
63
+ import { closeSession, generateKeyRequest, loadSession, } from "../../../compat";
55
64
  import log from "../../../log";
56
65
  import isNullOrUndefined from "../../../utils/is_null_or_undefined";
57
66
  import KeySessionRecord from "./key_session_record";
@@ -75,7 +84,7 @@ var LoadedSessionsStore = /** @class */ (function () {
75
84
  }
76
85
  /**
77
86
  * Create a new MediaKeySession and store it in this store.
78
- * @param {Object} initializationData
87
+ * @param {Object} initData
79
88
  * @param {string} sessionType
80
89
  * @returns {Object}
81
90
  */
@@ -83,7 +92,9 @@ var LoadedSessionsStore = /** @class */ (function () {
83
92
  var _this = this;
84
93
  var keySessionRecord = new KeySessionRecord(initData);
85
94
  var mediaKeySession = this._mediaKeys.createSession(sessionType);
86
- var entry = { mediaKeySession: mediaKeySession, sessionType: sessionType, keySessionRecord: keySessionRecord };
95
+ var entry = { mediaKeySession: mediaKeySession, sessionType: sessionType, keySessionRecord: keySessionRecord, isGeneratingRequest: false,
96
+ isLoadingPersistentSession: false,
97
+ closingStatus: { type: "none" } };
87
98
  if (!isNullOrUndefined(mediaKeySession.closed)) {
88
99
  mediaKeySession.closed
89
100
  .then(function () {
@@ -99,7 +110,7 @@ var LoadedSessionsStore = /** @class */ (function () {
99
110
  });
100
111
  }
101
112
  log.debug("DRM-LSS: Add MediaKeySession", entry.sessionType);
102
- this._storage.push({ keySessionRecord: keySessionRecord, mediaKeySession: mediaKeySession, sessionType: sessionType });
113
+ this._storage.push(__assign({}, entry));
103
114
  return entry;
104
115
  };
105
116
  /**
@@ -120,23 +131,41 @@ var LoadedSessionsStore = /** @class */ (function () {
120
131
  if (stored.keySessionRecord.isCompatibleWith(initializationData)) {
121
132
  this._storage.splice(i, 1);
122
133
  this._storage.push(stored);
123
- return { keySessionRecord: stored.keySessionRecord,
124
- mediaKeySession: stored.mediaKeySession,
125
- sessionType: stored.sessionType };
134
+ return __assign({}, stored);
126
135
  }
127
136
  }
128
137
  return null;
129
138
  };
130
139
  /**
131
- * Close a MediaKeySession and remove its related stored information from the
140
+ * Get `LoadedSessionsStore`'s entry for a given MediaKeySession.
141
+ * Returns `null` if the given MediaKeySession is not stored in the
132
142
  * `LoadedSessionsStore`.
133
- * Emit when done.
143
+ * @param {MediaKeySession} mediaKeySession
144
+ * @returns {Object|null}
145
+ */
146
+ LoadedSessionsStore.prototype.getEntryForSession = function (mediaKeySession) {
147
+ for (var i = this._storage.length - 1; i >= 0; i--) {
148
+ var stored = this._storage[i];
149
+ if (stored.mediaKeySession === mediaKeySession) {
150
+ return __assign({}, stored);
151
+ }
152
+ }
153
+ return null;
154
+ };
155
+ /**
156
+ * Generate a license request on the given MediaKeySession, while indicating
157
+ * to the LoadedSessionsStore that a license-request is pending so
158
+ * session-closing orders are properly scheduled after it is done.
134
159
  * @param {Object} mediaKeySession
160
+ * @param {string} initializationDataType - Initialization data type given
161
+ * e.g. by the "encrypted" event for the corresponding request.
162
+ * @param {Uint8Array} initializationData - Initialization data given e.g. by
163
+ * the "encrypted" event for the corresponding request.
135
164
  * @returns {Promise}
136
165
  */
137
- LoadedSessionsStore.prototype.closeSession = function (mediaKeySession) {
166
+ LoadedSessionsStore.prototype.generateLicenseRequest = function (mediaKeySession, initializationDataType, initializationData) {
138
167
  return __awaiter(this, void 0, void 0, function () {
139
- var entry, _i, _a, stored;
168
+ var entry, _i, _a, stored, err_1;
140
169
  return __generator(this, function (_b) {
141
170
  switch (_b.label) {
142
171
  case 0:
@@ -148,18 +177,132 @@ var LoadedSessionsStore = /** @class */ (function () {
148
177
  }
149
178
  }
150
179
  if (entry === undefined) {
151
- log.warn("DRM-LSS: No MediaKeySession found with " +
180
+ log.error("DRM-LSS: generateRequest error. No MediaKeySession found with " +
152
181
  "the given initData and initDataType");
153
- return [2 /*return*/, Promise.resolve(false)];
182
+ return [2 /*return*/, generateKeyRequest(mediaKeySession, initializationDataType, initializationData)];
154
183
  }
155
- return [4 /*yield*/, safelyCloseMediaKeySession(entry.mediaKeySession)];
184
+ entry.isGeneratingRequest = true;
185
+ // Note the `as string` is needed due to TypeScript not understanding that
186
+ // the `closingStatus` might change in the next checks
187
+ if (entry.closingStatus.type !== "none") {
188
+ throw new Error("The `MediaKeySession` is being closed.");
189
+ }
190
+ _b.label = 1;
156
191
  case 1:
192
+ _b.trys.push([1, 3, , 4]);
193
+ return [4 /*yield*/, generateKeyRequest(mediaKeySession, initializationDataType, initializationData)];
194
+ case 2:
157
195
  _b.sent();
158
- return [2 /*return*/, Promise.resolve(true)];
196
+ return [3 /*break*/, 4];
197
+ case 3:
198
+ err_1 = _b.sent();
199
+ if (entry === undefined) {
200
+ throw err_1;
201
+ }
202
+ entry.isGeneratingRequest = false;
203
+ if (entry.closingStatus.type === "awaiting") {
204
+ entry.closingStatus.start();
205
+ }
206
+ throw err_1;
207
+ case 4:
208
+ if (entry === undefined) {
209
+ return [2 /*return*/, undefined];
210
+ }
211
+ entry.isGeneratingRequest = false;
212
+ if (entry.closingStatus.type === "awaiting") {
213
+ entry.closingStatus.start();
214
+ }
215
+ return [2 /*return*/];
159
216
  }
160
217
  });
161
218
  });
162
219
  };
220
+ /**
221
+ * @param {Object} mediaKeySession
222
+ * @param {string} sessionId
223
+ * @returns {Promise}
224
+ */
225
+ LoadedSessionsStore.prototype.loadPersistentSession = function (mediaKeySession, sessionId) {
226
+ return __awaiter(this, void 0, void 0, function () {
227
+ var entry, _i, _a, stored, ret, err_2;
228
+ return __generator(this, function (_b) {
229
+ switch (_b.label) {
230
+ case 0:
231
+ for (_i = 0, _a = this._storage; _i < _a.length; _i++) {
232
+ stored = _a[_i];
233
+ if (stored.mediaKeySession === mediaKeySession) {
234
+ entry = stored;
235
+ break;
236
+ }
237
+ }
238
+ if (entry === undefined) {
239
+ log.error("DRM-LSS: loadPersistentSession error. No MediaKeySession found with " +
240
+ "the given initData and initDataType");
241
+ return [2 /*return*/, loadSession(mediaKeySession, sessionId)];
242
+ }
243
+ entry.isLoadingPersistentSession = true;
244
+ // Note the `as string` is needed due to TypeScript not understanding that
245
+ // the `closingStatus` might change in the next checks
246
+ if (entry.closingStatus.type !== "none") {
247
+ throw new Error("The `MediaKeySession` is being closed.");
248
+ }
249
+ _b.label = 1;
250
+ case 1:
251
+ _b.trys.push([1, 3, , 4]);
252
+ return [4 /*yield*/, loadSession(mediaKeySession, sessionId)];
253
+ case 2:
254
+ ret = _b.sent();
255
+ return [3 /*break*/, 4];
256
+ case 3:
257
+ err_2 = _b.sent();
258
+ if (entry === undefined) {
259
+ throw err_2;
260
+ }
261
+ entry.isLoadingPersistentSession = false;
262
+ if (entry.closingStatus.type === "awaiting") {
263
+ entry.closingStatus.start();
264
+ }
265
+ throw err_2;
266
+ case 4:
267
+ if (entry === undefined) {
268
+ return [2 /*return*/, ret];
269
+ }
270
+ entry.isLoadingPersistentSession = false;
271
+ if (entry.closingStatus.type === "awaiting") {
272
+ entry.closingStatus.start();
273
+ }
274
+ return [2 /*return*/, ret];
275
+ }
276
+ });
277
+ });
278
+ };
279
+ /**
280
+ * Close a MediaKeySession and remove its related stored information from the
281
+ * `LoadedSessionsStore`.
282
+ * Emit when done.
283
+ * @param {Object} mediaKeySession
284
+ * @returns {Promise}
285
+ */
286
+ LoadedSessionsStore.prototype.closeSession = function (mediaKeySession) {
287
+ return __awaiter(this, void 0, void 0, function () {
288
+ var entry, _i, _a, stored;
289
+ return __generator(this, function (_b) {
290
+ for (_i = 0, _a = this._storage; _i < _a.length; _i++) {
291
+ stored = _a[_i];
292
+ if (stored.mediaKeySession === mediaKeySession) {
293
+ entry = stored;
294
+ break;
295
+ }
296
+ }
297
+ if (entry === undefined) {
298
+ log.warn("DRM-LSS: No MediaKeySession found with " +
299
+ "the given initData and initDataType");
300
+ return [2 /*return*/, Promise.resolve(false)];
301
+ }
302
+ return [2 /*return*/, this._closeEntry(entry)];
303
+ });
304
+ });
305
+ };
163
306
  /**
164
307
  * Returns the number of stored MediaKeySessions in this LoadedSessionsStore.
165
308
  * @returns {number}
@@ -183,6 +326,7 @@ var LoadedSessionsStore = /** @class */ (function () {
183
326
  LoadedSessionsStore.prototype.closeAllSessions = function () {
184
327
  return __awaiter(this, void 0, void 0, function () {
185
328
  var allEntries, closingProms;
329
+ var _this = this;
186
330
  return __generator(this, function (_a) {
187
331
  switch (_a.label) {
188
332
  case 0:
@@ -193,7 +337,7 @@ var LoadedSessionsStore = /** @class */ (function () {
193
337
  // process of removing
194
338
  this._storage = [];
195
339
  closingProms = allEntries
196
- .map(function (entry) { return safelyCloseMediaKeySession(entry.mediaKeySession); });
340
+ .map(function (entry) { return _this._closeEntry(entry); });
197
341
  return [4 /*yield*/, Promise.all(closingProms)];
198
342
  case 1:
199
343
  _a.sent();
@@ -202,6 +346,13 @@ var LoadedSessionsStore = /** @class */ (function () {
202
346
  });
203
347
  });
204
348
  };
349
+ /**
350
+ * Get the index of a stored MediaKeySession entry based on its
351
+ * `KeySessionRecord`.
352
+ * Returns -1 if not found.
353
+ * @param {Object} record
354
+ * @returns {number}
355
+ */
205
356
  LoadedSessionsStore.prototype.getIndex = function (record) {
206
357
  for (var i = 0; i < this._storage.length; i++) {
207
358
  var stored = this._storage[i];
@@ -211,92 +362,82 @@ var LoadedSessionsStore = /** @class */ (function () {
211
362
  }
212
363
  return -1;
213
364
  };
365
+ /**
366
+ * Prepare the closure of a `MediaKeySession` stored as an entry of the
367
+ * `LoadedSessionsStore`.
368
+ * Allows to postpone the closure action if another MediaKeySession action
369
+ * is already pending.
370
+ * @param {Object} entry
371
+ * @returns {Promise.<boolean>}
372
+ */
373
+ LoadedSessionsStore.prototype._closeEntry = function (entry) {
374
+ return __awaiter(this, void 0, void 0, function () {
375
+ var mediaKeySession;
376
+ return __generator(this, function (_a) {
377
+ mediaKeySession = entry.mediaKeySession;
378
+ return [2 /*return*/, new Promise(function (resolve, reject) {
379
+ if (entry !== undefined &&
380
+ (entry.isLoadingPersistentSession || entry.isGeneratingRequest)) {
381
+ entry.closingStatus = { type: "awaiting",
382
+ start: tryClosingEntryAndResolve };
383
+ }
384
+ else {
385
+ tryClosingEntryAndResolve();
386
+ }
387
+ function tryClosingEntryAndResolve() {
388
+ if (entry !== undefined) {
389
+ entry.closingStatus = { type: "pending" };
390
+ }
391
+ safelyCloseMediaKeySession(mediaKeySession)
392
+ .then(function () {
393
+ if (entry !== undefined) {
394
+ entry.closingStatus = { type: "done" };
395
+ }
396
+ resolve(true);
397
+ })
398
+ .catch(function (err) {
399
+ if (entry !== undefined) {
400
+ entry.closingStatus = { type: "failed" };
401
+ }
402
+ reject(err);
403
+ });
404
+ }
405
+ })];
406
+ });
407
+ });
408
+ };
214
409
  return LoadedSessionsStore;
215
410
  }());
216
411
  export default LoadedSessionsStore;
217
412
  /**
218
- * Close a MediaKeySession with multiple attempts if needed and do not throw if
219
- * this action throws an error.
413
+ * Close a MediaKeySession and just log an error if it fails (while resolving).
220
414
  * Emits then complete when done.
221
415
  * @param {MediaKeySession} mediaKeySession
222
416
  * @returns {Observable}
223
417
  */
224
418
  function safelyCloseMediaKeySession(mediaKeySession) {
225
- return recursivelyTryToCloseMediaKeySession(0);
226
- /**
227
- * Perform a new attempt at closing the MediaKeySession.
228
- * If this operation fails due to a not-"callable" (an EME term)
229
- * MediaKeySession, retry based on either a timer or on MediaKeySession
230
- * events, whichever comes first.
231
- * Emits then complete when done.
232
- * @param {number} retryNb - The attempt number starting at 0.
233
- * @returns {Observable}
234
- */
235
- function recursivelyTryToCloseMediaKeySession(retryNb) {
236
- return __awaiter(this, void 0, void 0, function () {
237
- var err_1, _a, EME_SESSION_CLOSING_MAX_RETRY, EME_SESSION_CLOSING_INITIAL_DELAY, EME_SESSION_CLOSING_MAX_DELAY, nextRetryNb, delay_1, ksChangeSub_1, ksChangeProm, ksMsgSub_1, ksMsgProm, sleepTimer_1, sleepProm;
238
- return __generator(this, function (_b) {
239
- switch (_b.label) {
240
- case 0:
241
- log.debug("DRM: Trying to close a MediaKeySession", mediaKeySession.sessionId, retryNb);
242
- _b.label = 1;
243
- case 1:
244
- _b.trys.push([1, 3, , 5]);
245
- return [4 /*yield*/, closeSession(mediaKeySession)];
246
- case 2:
247
- _b.sent();
248
- log.debug("DRM: Succeeded to close MediaKeySession");
249
- return [2 /*return*/, undefined];
250
- case 3:
251
- err_1 = _b.sent();
252
- // Unitialized MediaKeySession may not close properly until their
253
- // corresponding `generateRequest` or `load` call are handled by the
254
- // browser.
255
- // In that case the EME specification tells us that the browser is
256
- // supposed to reject the `close` call with an InvalidStateError.
257
- if (!(err_1 instanceof Error) || err_1.name !== "InvalidStateError" ||
258
- mediaKeySession.sessionId !== "") {
259
- return [2 /*return*/, failToCloseSession(err_1)];
260
- }
261
- _a = config.getCurrent(), EME_SESSION_CLOSING_MAX_RETRY = _a.EME_SESSION_CLOSING_MAX_RETRY, EME_SESSION_CLOSING_INITIAL_DELAY = _a.EME_SESSION_CLOSING_INITIAL_DELAY, EME_SESSION_CLOSING_MAX_DELAY = _a.EME_SESSION_CLOSING_MAX_DELAY;
262
- nextRetryNb = retryNb + 1;
263
- if (nextRetryNb > EME_SESSION_CLOSING_MAX_RETRY) {
264
- return [2 /*return*/, failToCloseSession(err_1)];
265
- }
266
- delay_1 = Math.min(Math.pow(2, retryNb) * EME_SESSION_CLOSING_INITIAL_DELAY, EME_SESSION_CLOSING_MAX_DELAY);
267
- log.warn("DRM: attempt to close a mediaKeySession failed, " +
268
- "scheduling retry...", delay_1);
269
- ksChangeProm = new Promise(function (res) {
270
- ksChangeSub_1 = onKeyStatusesChange$(mediaKeySession).subscribe(res);
271
- });
272
- ksMsgProm = new Promise(function (res) {
273
- ksMsgSub_1 = onKeyMessage$(mediaKeySession).subscribe(res);
274
- });
275
- sleepProm = new Promise(function (res) {
276
- sleepTimer_1 = window.setTimeout(res, delay_1);
277
- });
278
- return [4 /*yield*/, Promise.race([ksChangeProm, ksMsgProm, sleepProm])];
279
- case 4:
280
- _b.sent();
281
- ksChangeSub_1 === null || ksChangeSub_1 === void 0 ? void 0 : ksChangeSub_1.unsubscribe();
282
- ksMsgSub_1 === null || ksMsgSub_1 === void 0 ? void 0 : ksMsgSub_1.unsubscribe();
283
- clearTimeout(sleepTimer_1);
284
- return [2 /*return*/, recursivelyTryToCloseMediaKeySession(nextRetryNb)];
285
- case 5: return [2 /*return*/];
286
- }
287
- });
419
+ return __awaiter(this, void 0, void 0, function () {
420
+ var err_3;
421
+ return __generator(this, function (_a) {
422
+ switch (_a.label) {
423
+ case 0:
424
+ log.debug("DRM: Trying to close a MediaKeySession", mediaKeySession.sessionId);
425
+ _a.label = 1;
426
+ case 1:
427
+ _a.trys.push([1, 3, , 4]);
428
+ return [4 /*yield*/, closeSession(mediaKeySession)];
429
+ case 2:
430
+ _a.sent();
431
+ log.debug("DRM: Succeeded to close MediaKeySession");
432
+ return [2 /*return*/];
433
+ case 3:
434
+ err_3 = _a.sent();
435
+ log.error("DRM: Could not close MediaKeySession: " +
436
+ (err_3 instanceof Error ? err_3.toString() :
437
+ "Unknown error"));
438
+ return [2 /*return*/];
439
+ case 4: return [2 /*return*/];
440
+ }
288
441
  });
289
- }
290
- /**
291
- * Log error anouncing that we could not close the MediaKeySession and emits
292
- * then complete through Observable.
293
- * TODO Emit warning?
294
- * @returns {Observable}
295
- */
296
- function failToCloseSession(err) {
297
- log.error("DRM: Could not close MediaKeySession: " +
298
- (err instanceof Error ? err.toString() :
299
- "Unknown error"));
300
- return Promise.resolve(null);
301
- }
442
+ });
302
443
  }
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { combineLatest as observableCombineLatest, concatAll, EMPTY, from as observableFrom, ignoreElements, mergeMap, } from "rxjs";
16
+ import { combineLatest as observableCombineLatest, concatAll, EMPTY, from as observableFrom, ignoreElements, mergeMap, of as observableOf, } from "rxjs";
17
17
  import log from "../../log";
18
18
  import { getInnerAndOuterTimeRanges } from "../../utils/ranges";
19
19
  /**
@@ -105,6 +105,9 @@ function clearBuffer(segmentBuffer, position, maxBufferBehind, maxBufferAhead) {
105
105
  collectBufferAhead();
106
106
  var clean$ = observableFrom(cleanedupRanges.map(function (range) {
107
107
  log.debug("GC: cleaning range from SegmentBuffer", range);
108
+ if (range.start >= range.end) {
109
+ return observableOf(null);
110
+ }
108
111
  return segmentBuffer.removeBuffer(range.start, range.end);
109
112
  })).pipe(concatAll(),
110
113
  // NOTE As of now (RxJS 7.4.0), RxJS defines `ignoreElements` default
@@ -240,7 +240,8 @@ export default function StreamOrchestrator(content, playbackObserver, abrManager
240
240
  destroyStreams$.next();
241
241
  return observableConcat.apply(void 0, __spreadArray(__spreadArray([], rangesToClean.map(function (_a) {
242
242
  var start = _a.start, end = _a.end;
243
- return segmentBuffer.removeBuffer(start, end).pipe(ignoreElements());
243
+ return start >= end ? EMPTY :
244
+ segmentBuffer.removeBuffer(start, end).pipe(ignoreElements());
244
245
  }), false), [playbackObserver.observe(true).pipe(take(1), mergeMap(function (observation) {
245
246
  return observableConcat(observableOf(EVENTS.needsDecipherabilityFlush(observation.position, !observation.isPaused, observation.duration)), observableDefer(function () {
246
247
  var lastPosition = observation.position +
@@ -63,9 +63,15 @@ export default function PeriodStream(_a) {
63
63
  if (SegmentBuffersStore.isNative(bufferType)) {
64
64
  return reloadAfterSwitch(period, bufferType, playbackObserver, 0);
65
65
  }
66
- cleanBuffer$ = segmentBufferStatus.value
67
- .removeBuffer(period.start, period.end == null ? Infinity :
68
- period.end);
66
+ if (period.end === undefined) {
67
+ cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start, Infinity);
68
+ }
69
+ else if (period.end <= period.start) {
70
+ cleanBuffer$ = observableOf(null);
71
+ }
72
+ else {
73
+ cleanBuffer$ = segmentBufferStatus.value.removeBuffer(period.start, period.end);
74
+ }
69
75
  }
70
76
  else {
71
77
  if (segmentBufferStatus.type === "uninitialized") {
@@ -16,7 +16,7 @@
16
16
  /**
17
17
  * This file allows any Stream to push data to a SegmentBuffer.
18
18
  */
19
- import { catchError, concat as observableConcat, mergeMap, ignoreElements, } from "rxjs";
19
+ import { catchError, concat as observableConcat, mergeMap, ignoreElements, take, } from "rxjs";
20
20
  import { MediaError } from "../../../errors";
21
21
  import forceGarbageCollection from "./force_garbage_collection";
22
22
  /**
@@ -38,10 +38,11 @@ export default function appendSegmentToBuffer(playbackObserver, segmentBuffer, d
38
38
  "An unknown error happened when pushing content";
39
39
  throw new MediaError("BUFFER_APPEND_ERROR", reason);
40
40
  }
41
- return playbackObserver.observe(true).pipe(mergeMap(function (observation) {
41
+ return playbackObserver.observe(true).pipe(take(1), mergeMap(function (observation) {
42
42
  var currentPos = observation.position + observation.wantedTimeOffset;
43
43
  return observableConcat(forceGarbageCollection(currentPos, segmentBuffer).pipe(ignoreElements()), append$).pipe(catchError(function (forcedGCError) {
44
- var reason = forcedGCError instanceof Error ? forcedGCError.toString() :
44
+ var reason = forcedGCError instanceof Error ?
45
+ forcedGCError.toString() :
45
46
  "Could not clean the buffer";
46
47
  throw new MediaError("BUFFER_FULL_ERROR", reason);
47
48
  }));
@@ -13,7 +13,7 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { concatAll, defer as observableDefer, from as observableFrom, } from "rxjs";
16
+ import { concatAll, defer as observableDefer, from as observableFrom, of as observableOf, } from "rxjs";
17
17
  import config from "../../../config";
18
18
  import log from "../../../log";
19
19
  import { getInnerAndOuterTimeRanges } from "../../../utils/ranges";
@@ -41,7 +41,8 @@ export default function forceGarbageCollection(currentPosition, bufferingQueue)
41
41
  log.debug("Stream: GC cleaning", cleanedupRanges);
42
42
  return observableFrom(cleanedupRanges.map(function (_a) {
43
43
  var start = _a.start, end = _a.end;
44
- return bufferingQueue.removeBuffer(start, end);
44
+ return start >= end ? observableOf(null) :
45
+ bufferingQueue.removeBuffer(start, end);
45
46
  })).pipe(concatAll());
46
47
  });
47
48
  }
@@ -43,8 +43,8 @@ export interface IBufferStatus {
43
43
  */
44
44
  shouldRefreshManifest: boolean;
45
45
  /**
46
- * If 'true', the buffer memory is saturated before being able to download
47
- * at least MIN_REQUIRED_BUFFER_AHEAD ( default : 10sec )
46
+ * If 'true', the buffer memory is saturated, thus we may have issues loading
47
+ * new segments.
48
48
  */
49
49
  isBufferFull: boolean;
50
50
  }