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.
- package/CHANGELOG.md +5 -2
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/eme/custom_media_keys/old_webkit_media_keys.js +15 -11
- package/dist/_esm5.processed/compat/eme/custom_media_keys/webkit_media_keys.js +22 -6
- package/dist/_esm5.processed/compat/eme/generate_key_request.d.ts +4 -6
- package/dist/_esm5.processed/compat/eme/generate_key_request.js +4 -6
- package/dist/_esm5.processed/compat/get_start_date.d.ts +30 -0
- package/dist/_esm5.processed/compat/get_start_date.js +44 -0
- package/dist/_esm5.processed/compat/index.d.ts +2 -1
- package/dist/_esm5.processed/compat/index.js +2 -1
- package/dist/_esm5.processed/config.d.ts +1 -5
- package/dist/_esm5.processed/core/api/public_api.js +25 -25
- package/dist/_esm5.processed/core/decrypt/content_decryptor.js +11 -3
- package/dist/_esm5.processed/core/decrypt/create_or_load_session.js +1 -1
- package/dist/_esm5.processed/core/decrypt/create_session.d.ts +3 -1
- package/dist/_esm5.processed/core/decrypt/create_session.js +15 -5
- package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.d.ts +94 -1
- package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +237 -96
- package/dist/_esm5.processed/core/segment_buffers/garbage_collector.js +4 -1
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +2 -1
- package/dist/_esm5.processed/core/stream/period/period_stream.js +9 -3
- package/dist/_esm5.processed/core/stream/representation/append_segment_to_buffer.js +4 -3
- package/dist/_esm5.processed/core/stream/representation/force_garbage_collection.js +3 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/representation/get_buffer_status.js +9 -3
- package/dist/_esm5.processed/core/stream/representation/get_needed_segments.d.ts +11 -1
- package/dist/_esm5.processed/core/stream/representation/get_needed_segments.js +27 -45
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +6 -4
- package/dist/_esm5.processed/default_config.d.ts +2 -35
- package/dist/_esm5.processed/default_config.js +2 -35
- package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +39 -38
- package/dist/_esm5.processed/utils/reference.js +0 -2
- package/dist/_esm5.processed/utils/task_canceller.d.ts +8 -1
- package/dist/_esm5.processed/utils/task_canceller.js +9 -1
- package/dist/rx-player.js +927 -587
- package/dist/rx-player.min.js +1 -1
- package/package.json +1 -1
- package/sonar-project.properties +1 -1
- package/src/compat/eme/custom_media_keys/old_webkit_media_keys.ts +16 -12
- package/src/compat/eme/custom_media_keys/webkit_media_keys.ts +21 -8
- package/src/compat/eme/generate_key_request.ts +4 -6
- package/src/compat/get_start_date.ts +48 -0
- package/src/compat/index.ts +2 -0
- package/src/core/api/public_api.ts +23 -27
- package/src/core/decrypt/content_decryptor.ts +15 -4
- package/src/core/decrypt/create_or_load_session.ts +4 -1
- package/src/core/decrypt/create_session.ts +23 -9
- package/src/core/decrypt/utils/loaded_sessions_store.ts +254 -102
- package/src/core/segment_buffers/garbage_collector.ts +4 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +2 -1
- package/src/core/stream/period/period_stream.ts +9 -4
- package/src/core/stream/representation/append_segment_to_buffer.ts +17 -13
- package/src/core/stream/representation/force_garbage_collection.ts +4 -1
- package/src/core/stream/representation/get_buffer_status.ts +21 -13
- package/src/core/stream/representation/get_needed_segments.ts +40 -55
- package/src/core/stream/representation/representation_stream.ts +6 -4
- package/src/default_config.ts +20 -57
- package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +41 -44
- package/src/utils/reference.ts +0 -2
- 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}
|
|
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}
|
|
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({
|
|
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 {
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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.
|
|
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.
|
|
180
|
+
log.error("DRM-LSS: generateRequest error. No MediaKeySession found with " +
|
|
152
181
|
"the given initData and initDataType");
|
|
153
|
-
return [2 /*return*/,
|
|
182
|
+
return [2 /*return*/, generateKeyRequest(mediaKeySession, initializationDataType, initializationData)];
|
|
154
183
|
}
|
|
155
|
-
|
|
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 [
|
|
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
|
|
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
|
|
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
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
|
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
|
-
|
|
67
|
-
.removeBuffer(period.start,
|
|
68
|
-
|
|
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 ?
|
|
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
|
|
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
|
|
47
|
-
*
|
|
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
|
}
|