senza-sdk 4.3.1-e113d43.0 → 4.3.2-2e967be.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/dist/bundle.js +1 -1
- package/package.json +9 -8
- package/src/api.js +14 -2
- package/src/implementation/alarmManager.js +21 -0
- package/src/implementation/api.js +14 -13
- package/src/implementation/deviceManager.js +20 -1
- package/src/implementation/lifecycle.js +66 -57
- package/src/implementation/messageManager.js +17 -30
- package/src/implementation/platformManager.js +0 -19
- package/src/implementation/remotePlayer.js +86 -272
- package/src/implementation/senzaShakaPlayer.js +104 -127
- package/src/interface/alarmManager.js +29 -22
- package/src/interface/devSequence.js +35 -0
- package/src/interface/deviceManager.js +30 -20
- package/src/interface/lifecycle.js +46 -40
- package/src/interface/messageManager.js +36 -28
- package/src/interface/platformManager.js +13 -6
- package/src/interface/remotePlayer.js +208 -180
- package/src/interface/senzaShakaPlayer.js +2 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// eslint-disable-next-line no-unused-vars
|
|
2
|
+
import { RemotePlayer as RemotePlayerInterface, RemotePlayerError as RemotePlayerErrorInterface, Config } from "../interface/remotePlayer";
|
|
2
3
|
import {
|
|
3
4
|
getFCID,
|
|
4
5
|
isAudioSyncConfigured,
|
|
@@ -28,11 +29,7 @@ const cloneDeep = (element) => {
|
|
|
28
29
|
return JSON.parse(JSON.stringify(element));
|
|
29
30
|
};
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
/** Error object to be thrown on remotePlayer api failures.
|
|
33
|
-
* [See error list]{@link RemotePlayer#error}
|
|
34
|
-
*/
|
|
35
|
-
export class RemotePlayerError extends Error {
|
|
32
|
+
export class RemotePlayerError extends RemotePlayerErrorInterface {
|
|
36
33
|
constructor(code, message) {
|
|
37
34
|
super(message);
|
|
38
35
|
this.code = code;
|
|
@@ -64,25 +61,6 @@ function setPlaybackInfo(playbackInfo) {
|
|
|
64
61
|
}
|
|
65
62
|
}
|
|
66
63
|
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @typedef {Object} Config
|
|
70
|
-
* @property {string} preferredAudioLanguage
|
|
71
|
-
* @property {string} preferredSubtitlesLanguage
|
|
72
|
-
* @property {boolean} autoPlay - (Not implemented yet) upon loading start playing automatically
|
|
73
|
-
*/
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* RemotePlayer a singleton class to communicate with remote player
|
|
77
|
-
* @fires timeupdate
|
|
78
|
-
* @fires tracksupdate
|
|
79
|
-
* @fires ended
|
|
80
|
-
* @fires error
|
|
81
|
-
* @fires onloadmodechange
|
|
82
|
-
* @fires seeking (Not implemented yet)
|
|
83
|
-
* @fires seeked (Not implemented yet)
|
|
84
|
-
* @fires loadedmetadata (Not implemented yet)
|
|
85
|
-
*/
|
|
86
64
|
class RemotePlayer extends RemotePlayerInterface {
|
|
87
65
|
constructor() {
|
|
88
66
|
super();
|
|
@@ -92,7 +70,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
92
70
|
*/
|
|
93
71
|
this._config = {
|
|
94
72
|
preferredAudioLanguage: "",
|
|
95
|
-
preferredSubtitlesLanguage: ""
|
|
73
|
+
preferredSubtitlesLanguage: "",
|
|
74
|
+
minSuggestedPresentationDelay: 0
|
|
96
75
|
};
|
|
97
76
|
/**
|
|
98
77
|
* @type {string}
|
|
@@ -161,50 +140,83 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
161
140
|
* @private
|
|
162
141
|
*/
|
|
163
142
|
this._isPlaying = false;
|
|
143
|
+
}
|
|
164
144
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
145
|
+
/** @private Initialize the remote player
|
|
146
|
+
* @param {Object} uiStreamerSettings ui-streamer portion of the settings taken from session info
|
|
147
|
+
* */
|
|
148
|
+
async _init(uiStreamerSettings, triggerEvent) {
|
|
149
|
+
if (!this._systemEventListenersAdded) {
|
|
150
|
+
this._systemEventListenersAdded = true;
|
|
151
|
+
}
|
|
152
|
+
let playerState = {
|
|
153
|
+
isLoaded: false,
|
|
154
|
+
playbackUrl: ""
|
|
155
|
+
};
|
|
156
|
+
if (window.cefQuery) {
|
|
157
|
+
const FCID = getFCID();
|
|
158
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
159
|
+
const message = {
|
|
160
|
+
type: "playerState",
|
|
161
|
+
fcid: FCID
|
|
162
|
+
};
|
|
163
|
+
const request = { target: "UI-Streamer", waitForResponse: false, message: JSON.stringify(message) };
|
|
164
|
+
try {
|
|
165
|
+
const playerStateStr = await new Promise((resolve, reject) => {
|
|
166
|
+
window.cefQuery({
|
|
167
|
+
request: JSON.stringify(request),
|
|
168
|
+
persistent: false,
|
|
169
|
+
onSuccess: (playerState) => {
|
|
170
|
+
logger.log("playerState request successfully returned", playerState);
|
|
171
|
+
resolve(playerState);
|
|
172
|
+
},
|
|
173
|
+
onFailure: (code, msg) => {
|
|
174
|
+
logger.error(`playerState request failed: ${code} ${msg}`);
|
|
175
|
+
reject();
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
playerState = JSON.parse(playerStateStr);
|
|
180
|
+
} catch {
|
|
181
|
+
logger.error("either failed to get or failed to parse player state");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
this._loadMode = playerState?.isLoaded ? this.LoadMode.LOADED : this.LoadMode.NOT_LOADED;
|
|
174
185
|
|
|
175
|
-
|
|
176
|
-
* @event RemotePlayer#playing
|
|
177
|
-
* @description playing event will be dispatched when the remote player started playback of the first audio frame. This event can be used to synchronize the local player with the remote player.
|
|
178
|
-
* @example
|
|
179
|
-
* remotePlayer.addEventListener("playing", () => {
|
|
180
|
-
* console.info("remotePlayer playing");
|
|
181
|
-
* });
|
|
182
|
-
* */
|
|
186
|
+
this._isPlaying = window?.sessionStorage?.getItem("senzaSdk_isPlaying") === "true";
|
|
183
187
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
188
|
+
sdkLogger.info(`RemotePlayer initialized with load mode: ${this._loadMode}, senzaSdk_isPlaying: ${this._isPlaying}`);
|
|
189
|
+
this._loadedUrl = playerState?.playbackUrl || "";
|
|
190
|
+
// Make sure that the event listeners are registered only once
|
|
191
|
+
if (!this._isInitialized) {
|
|
192
|
+
this._addSenzaEventListeners();
|
|
193
|
+
}
|
|
194
|
+
this._isInitialized = true;
|
|
195
|
+
this._remotePlayerConfirmationTimeout = uiStreamerSettings?.remotePlayerConfirmationTimeout ?? DEFAULT_REMOTE_PLAYER_CONFIRMATION_TIMEOUT;
|
|
196
|
+
this._remotePlayerApiVersion = uiStreamerSettings?.remotePlayerApiVersion || 1;
|
|
197
|
+
this._multiSeekDelay = uiStreamerSettings?.multiSeekDelay || MULTI_SEEK_DELAY_MSEC;
|
|
198
|
+
|
|
199
|
+
sdkLogger.info(`remotePLayer isPlaying=${this._isPlaying}`);
|
|
200
|
+
|
|
201
|
+
let playbackMetadata = {};
|
|
202
|
+
try {
|
|
203
|
+
playbackMetadata = JSON.parse(triggerEvent?.playbackMetadata || "{}");
|
|
204
|
+
} catch (e) {
|
|
205
|
+
sdkLogger.error(`failed to parse playbackMetadata: ${e.message}`);
|
|
206
|
+
}
|
|
207
|
+
this._availabilityStartTime = playbackMetadata.availabilityStartTime;
|
|
208
|
+
this._updateTracks(playbackMetadata);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* @private Add event listeners for system events
|
|
213
|
+
*/
|
|
214
|
+
_addSenzaEventListeners() {
|
|
193
215
|
typeof document !== "undefined" && document.addEventListener("hs/remotePlayerEvent", (e) => {
|
|
194
216
|
sdkLogger.info("Got hs/remotePlayerEvent event with detail", JSON.stringify(e?.detail));
|
|
195
217
|
this.dispatchEvent(new Event(e?.detail?.eventName));
|
|
196
218
|
});
|
|
197
219
|
|
|
198
|
-
/**
|
|
199
|
-
*
|
|
200
|
-
* @event RemotePlayer#timeupdate
|
|
201
|
-
* @example
|
|
202
|
-
* remotePlayer.addEventListener("timeupdate", () => {
|
|
203
|
-
* console.info("remotePlayer timeupdate", remotePlayer.currentTime);
|
|
204
|
-
* localPlayer.getMediaElement().currentTime = remotePlayer.currentTime || 0;
|
|
205
|
-
* });
|
|
206
|
-
*
|
|
207
|
-
*/
|
|
208
220
|
typeof document !== "undefined" && document.addEventListener("hs/playbackInfoEvent", () => {
|
|
209
221
|
sdkLogger.info("Got hs/playbackInfoEvent");
|
|
210
222
|
// When attached, the sdk controls the synchronization between the local and remote player.
|
|
@@ -218,15 +230,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
218
230
|
}
|
|
219
231
|
});
|
|
220
232
|
|
|
221
|
-
/**
|
|
222
|
-
*
|
|
223
|
-
* @event RemotePlayer#tracksupdate
|
|
224
|
-
* @example
|
|
225
|
-
* remotePlayer.addEventListener("tracksupdate", () => {
|
|
226
|
-
* console.info("remotePlayer tracksupdate", remotePlayer.getAudioTracks(), remotePlayer.getTextTracks());
|
|
227
|
-
* });
|
|
228
|
-
*
|
|
229
|
-
*/
|
|
230
233
|
typeof document !== "undefined" && document.addEventListener("hs/playback", (e) => {
|
|
231
234
|
sdkLogger.info("Got hs/playback event with detail", JSON.stringify(e?.detail));
|
|
232
235
|
this._availabilityStartTime = e?.detail?.availabilityStartTime;
|
|
@@ -234,13 +237,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
234
237
|
this.dispatchEvent(new Event("tracksupdate"));
|
|
235
238
|
});
|
|
236
239
|
|
|
237
|
-
/**
|
|
238
|
-
* @event RemotePlayer#ended
|
|
239
|
-
* @example
|
|
240
|
-
* remotePlayer.addEventListener("ended", () => {
|
|
241
|
-
* console.info("remotePlayer ended");
|
|
242
|
-
* });
|
|
243
|
-
*/
|
|
244
240
|
typeof document !== "undefined" && document.addEventListener("hs/EOS", () => {
|
|
245
241
|
sdkLogger.info("Got hs/EOS event");
|
|
246
242
|
this.dispatchEvent(new Event("ended"));
|
|
@@ -273,72 +269,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
273
269
|
sdkLogger.info(`Adding ${event.detail} seconds, previousTime=${previousCurrentTime} currentTime=${this._videoElement.currentTime}`);
|
|
274
270
|
});
|
|
275
271
|
|
|
276
|
-
|
|
277
|
-
*
|
|
278
|
-
* @event RemotePlayer#error
|
|
279
|
-
* @type {object}
|
|
280
|
-
* @property {int} detail.errorCode
|
|
281
|
-
* @property {string} detail.message
|
|
282
|
-
*
|
|
283
|
-
* @see Possible error codes:
|
|
284
|
-
*
|
|
285
|
-
* | Code | Domain | Description |
|
|
286
|
-
* | :-------- | :---------------- | :-----------------------------------------------------------------------------------------------|
|
|
287
|
-
* | 23 | Player | load() failed due to remote player initialization error |
|
|
288
|
-
* | 98 | Player | load() failed due to remote player failure to send message to the client |
|
|
289
|
-
* | 99 | Player | load() failed due to remote player reporting invalid message |
|
|
290
|
-
* | 1000 | Encrypted content | Failed to create or initialise the CDM |
|
|
291
|
-
* | 1001 | Encrypted content | Failed to create a CDM session |
|
|
292
|
-
* | 1002 | Encrypted content | CDM failed to generate a license request |
|
|
293
|
-
* | 1003 | Encrypted content | The CDM rejected the license server response |
|
|
294
|
-
* | 1004 | Encrypted content | The CDM rejected the license server certificate |
|
|
295
|
-
* | 1005 | Encrypted content | All keys in the license have expired |
|
|
296
|
-
* | 1006 | Encrypted content | Output device is incompatible with the license requirements (HDCP) |
|
|
297
|
-
* | 1007 | Encrypted content | The device has been revoked |
|
|
298
|
-
* | 1008 | Encrypted content | The device secrets aren't available |
|
|
299
|
-
* | 1009 | Encrypted content | Keys are loaded but the KID requested by playback isn't found. The app has likely issued a license for the wrong content or there is a mismatch between the KIDs in the license and the data plane |
|
|
300
|
-
* | 1010 | Encrypted content | The CDM failed to provision, therefore it is not possible to play encrypted content |
|
|
301
|
-
* | 1100 | Encrypted content | The CDM session has already received a license response. The app has likely issued 2, or more, license responses for the same request. The subsequent licenses will be ignored so this error is informational only |
|
|
302
|
-
* | 1101 | Encrypted content | The license has been rejected since an error was received by the CDM. The app has likely sent a non-200 code to `WriteLicenseResponse` |
|
|
303
|
-
* | 1102 | Encrypted content | A license response wasn't received from the app within a pre-defined timeout |
|
|
304
|
-
* | 1103 | Encrypted content | The CDM session associated with this license response is in an invalid state. This is an internal Senza platform error |
|
|
305
|
-
* | 1104 | Encrypted content | The CDM failed to send a license request to the app. This is an internal Senza platform error |
|
|
306
|
-
* | 1999 | Encrypted content | An unknown encrypted content error |
|
|
307
|
-
* | 2000 | Player | Content makes reference to no or unsupported key system |
|
|
308
|
-
* | 3000 | Player | Unexpected problem with playback, only used if no more specific code in 3xxx range applies |
|
|
309
|
-
* | 3001 | Player | Problem accessing content manifest, only used if no more specific code in 8xxx range applies |
|
|
310
|
-
* | 3002 | Player | Unexpectedly stopped playback |
|
|
311
|
-
* | 3100 | Player | Problem parsing MP4 content |
|
|
312
|
-
* | 3200 | Player | Problem with decoder |
|
|
313
|
-
* | 3300 | Player | DRM keys unavailable, player waited for keys but none arrived |
|
|
314
|
-
* | 3400 | Player | Problem accessing segments, only used if no more specific code in 34xx range applies |
|
|
315
|
-
* | 3401 | Player | Problem accessing segments, connection issue or timeout |
|
|
316
|
-
* | 3402 | Player | Problem accessing segments, server returned HTTP error code |
|
|
317
|
-
* | 3403 | Player | Problem accessing segments, server authentication issue |
|
|
318
|
-
* | 3404 | Player | Problem accessing segments, server returned not found |
|
|
319
|
-
* | 3900-3999 | Player | Internal player error |
|
|
320
|
-
* | 6000 | Player | The remote player api call has reached the configurable timeout with no response from the remote player |
|
|
321
|
-
* | 6001 | Player | play() was called while the remote player is not loaded |
|
|
322
|
-
* | 6002 | Player | load() was called while the application was in state 'background' or 'inTransitionToBackground' |
|
|
323
|
-
* | 6500 | Player | remotePlayer api was called before initializing remotePlayer |
|
|
324
|
-
* | 6501 | Player | load() was called while previous load/unload was still in progress |
|
|
325
|
-
* | 6502 | Player | unload() was called while previous unload/load was still in progress |
|
|
326
|
-
* | 8001 | Player | Error pulling manifest. bad parameters |
|
|
327
|
-
* | 8002 | Player | Error pulling manifest. filters returned no data |
|
|
328
|
-
* | 8003 | Player | Error pulling manifest. fetch error |
|
|
329
|
-
* | 8004 | Player | Error pulling manifest. parse error |
|
|
330
|
-
* | 8005 | Player | Error pulling manifest. stale manifest detected |
|
|
331
|
-
* | 8006 | Player | Error updating manifest. internal cache error |
|
|
332
|
-
* | 8007 | Player | Error updating manifest. internal error during backoff |
|
|
333
|
-
* | 8008 | Player | Error pulling manifest. sidx parsing error |
|
|
334
|
-
* | 8009 | Player | Error pulling manifest. internal error |
|
|
335
|
-
*
|
|
336
|
-
* @example
|
|
337
|
-
* remotePlayer.addEventListener("error", (event) => {
|
|
338
|
-
* console.error("received remotePlayer error:", event.detail.errorCode, event.detail.message);
|
|
339
|
-
* });
|
|
340
|
-
*
|
|
341
|
-
*/
|
|
272
|
+
|
|
342
273
|
typeof document !== "undefined" && document.addEventListener("hs/ERR", (event) => {
|
|
343
274
|
sdkLogger.info("Got hs/ERR event");
|
|
344
275
|
delete event?.detail?.type; // type is always videoPlaybackEvent, so no need to pass it
|
|
@@ -346,58 +277,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
346
277
|
this.dispatchEvent(new CustomEvent("error", event));
|
|
347
278
|
});
|
|
348
279
|
|
|
349
|
-
/**
|
|
350
|
-
*
|
|
351
|
-
* @event RemotePlayer#license-request
|
|
352
|
-
* @description Fired whenever the platform requires a license to play encrypted content.
|
|
353
|
-
* The Web App is responsible for passing the (opaque) license request blob to the license server and passing the (opaque) license server response to the CDM by calling the `writeLicenseResponse` method on the event.
|
|
354
|
-
* @type {LicenseRequestEvent}
|
|
355
|
-
* @property {object} detail - Object containing ievent data
|
|
356
|
-
* @property {string} detail.licenseRequest - Base64 coded opaque license request. The app is responsible for decoding the request before sending to the license server. Note that after decoding, the request may still be in Base64 form and this form should be sent to the license server without further decoding
|
|
357
|
-
* @property {writeLicenseResponse} writeLicenseResponse - Write the license server response to the platform
|
|
358
|
-
* @example
|
|
359
|
-
* Whilst the payload structure and access controls are specific to each license server implementation, the Widevine UAT license server requires no authentication and minimal payload formatting and therefore serves as a useful case study that may be adapted.
|
|
360
|
-
*
|
|
361
|
-
* remotePlayer.addEventListener("license-request", async (event) => {
|
|
362
|
-
* console.log("Got license-request event");
|
|
363
|
-
* const requestBuffer = event?.detail?.licenseRequest;
|
|
364
|
-
* const requestBufferStr = String.fromCharCode.apply(null, new Uint8Array(requestBuffer));
|
|
365
|
-
* console.log("License Request in base64:", requestBufferStr);
|
|
366
|
-
* const decodedLicenseRequest = window.atob(requestBufferStr); // from base 64
|
|
367
|
-
* const licenseRequestBytes = Uint8Array.from(decodedLicenseRequest, (l) => l.charCodeAt(0));
|
|
368
|
-
* // call Google API
|
|
369
|
-
* const res = await getLicenseFromServer(licenseRequestBytes.buffer);
|
|
370
|
-
* console.log("Writing response to platform ", res.code, res.responseBody);
|
|
371
|
-
* event.writeLicenseResponse(res.code, res.responseBody);
|
|
372
|
-
* });
|
|
373
|
-
|
|
374
|
-
* async function getLicenseFromServer(licenseRequest) {
|
|
375
|
-
* console.log("Requesting License from Widevine server");
|
|
376
|
-
* const response = await fetch("https://proxy.uat.widevine.com/proxy", {
|
|
377
|
-
* "method": "POST",
|
|
378
|
-
* "body": licenseRequest,
|
|
379
|
-
* "headers" : {
|
|
380
|
-
* "Content-Type": "application/octet-stream"
|
|
381
|
-
* }
|
|
382
|
-
* });
|
|
383
|
-
* const code = response.status;
|
|
384
|
-
* if (code !== 200) {
|
|
385
|
-
* console.error("failed to to get response from widevine:", code);
|
|
386
|
-
* const responseBody = await response.text();
|
|
387
|
-
* console.error(responseBody);
|
|
388
|
-
* return {code, responseBody};
|
|
389
|
-
* }
|
|
390
|
-
* const responseBody = await response.arrayBuffer();
|
|
391
|
-
* console.info("Got response: ");
|
|
392
|
-
* return {code, responseBody};
|
|
393
|
-
* }
|
|
394
|
-
**/
|
|
395
|
-
/**
|
|
396
|
-
* @function writeLicenseResponse
|
|
397
|
-
* @param {number} statusCode - License server HTTP response code, e.g. 200, 401, etc. Must be 200 to indicate a successful license exchange.
|
|
398
|
-
* @param {string} response - License server response as opaque binary data in an ArrayBuffer.
|
|
399
|
-
*
|
|
400
|
-
* */
|
|
401
280
|
typeof document !== "undefined" && document.addEventListener("hs/getLicense", (event) => {
|
|
402
281
|
sdkLogger.info("Got hs/getLicense event");
|
|
403
282
|
const getLicenseEventData = event?.detail;
|
|
@@ -417,79 +296,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
417
296
|
});
|
|
418
297
|
}
|
|
419
298
|
|
|
420
|
-
/**
|
|
421
|
-
* @typedef {Object} LoadMode
|
|
422
|
-
* @property {string} NOT_LOADED
|
|
423
|
-
* @property {string} LOADING
|
|
424
|
-
* @property {string} LOADED
|
|
425
|
-
* @property {string} UNLOADING
|
|
426
|
-
*/
|
|
427
|
-
LoadMode = Object.freeze({
|
|
428
|
-
NOT_LOADED: "notLoaded",
|
|
429
|
-
LOADING: "loading",
|
|
430
|
-
LOADED: "loaded",
|
|
431
|
-
UNLOADING: "unloading"
|
|
432
|
-
});
|
|
433
|
-
|
|
434
|
-
/** @private Initialize the remote player
|
|
435
|
-
* @param {Object} uiStreamerSettings ui-streamer portion of the settings taken from session info
|
|
436
|
-
* */
|
|
437
|
-
async _init(uiStreamerSettings, triggerEvent) {
|
|
438
|
-
let playerState = {
|
|
439
|
-
isLoaded: false,
|
|
440
|
-
playbackUrl: ""
|
|
441
|
-
};
|
|
442
|
-
if (window.cefQuery) {
|
|
443
|
-
const FCID = getFCID();
|
|
444
|
-
const logger = sdkLogger.withFields({ FCID });
|
|
445
|
-
const message = {
|
|
446
|
-
type: "playerState",
|
|
447
|
-
fcid: FCID
|
|
448
|
-
};
|
|
449
|
-
const request = { target: "UI-Streamer", waitForResponse: false, message: JSON.stringify(message) };
|
|
450
|
-
try {
|
|
451
|
-
const playerStateStr = await new Promise((resolve, reject) => {
|
|
452
|
-
window.cefQuery({
|
|
453
|
-
request: JSON.stringify(request),
|
|
454
|
-
persistent: false,
|
|
455
|
-
onSuccess: (playerState) => {
|
|
456
|
-
logger.log("playerState request successfully returned", playerState);
|
|
457
|
-
resolve(playerState);
|
|
458
|
-
},
|
|
459
|
-
onFailure: (code, msg) => {
|
|
460
|
-
logger.error(`playerState request failed: ${code} ${msg}`);
|
|
461
|
-
reject();
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
});
|
|
465
|
-
playerState = JSON.parse(playerStateStr);
|
|
466
|
-
} catch {
|
|
467
|
-
logger.error("either failed to get or failed to parse player state");
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
this._loadMode = playerState?.isLoaded ? this.LoadMode.LOADED : this.LoadMode.NOT_LOADED;
|
|
471
|
-
|
|
472
|
-
this._isPlaying = window?.sessionStorage?.getItem("senzaSdk_isPlaying") === "true";
|
|
473
|
-
|
|
474
|
-
sdkLogger.info(`RemotePlayer initialized with load mode: ${this._loadMode}, senzaSdk_isPlaying: ${this._isPlaying}`);
|
|
475
|
-
this._loadedUrl = playerState?.playbackUrl || "";
|
|
476
|
-
this._isInitialized = true;
|
|
477
|
-
this._remotePlayerConfirmationTimeout = uiStreamerSettings?.remotePlayerConfirmationTimeout ?? DEFAULT_REMOTE_PLAYER_CONFIRMATION_TIMEOUT;
|
|
478
|
-
this._remotePlayerApiVersion = uiStreamerSettings?.remotePlayerApiVersion || 1;
|
|
479
|
-
this._multiSeekDelay = uiStreamerSettings?.multiSeekDelay || MULTI_SEEK_DELAY_MSEC;
|
|
480
|
-
|
|
481
|
-
sdkLogger.info(`remotePLayer isPlaying=${this._isPlaying}`);
|
|
482
|
-
|
|
483
|
-
let playbackMetadata = {};
|
|
484
|
-
try {
|
|
485
|
-
playbackMetadata = JSON.parse(triggerEvent?.playbackMetadata || "{}");
|
|
486
|
-
} catch (e) {
|
|
487
|
-
sdkLogger.error(`failed to parse playbackMetadata: ${e.message}`);
|
|
488
|
-
}
|
|
489
|
-
this._availabilityStartTime = playbackMetadata.availabilityStartTime;
|
|
490
|
-
this._updateTracks(playbackMetadata);
|
|
491
|
-
}
|
|
492
|
-
|
|
493
299
|
/** @private update audio and text tracks
|
|
494
300
|
* @param {Object} playbackMetadata playback metadata that comes along the hs/playback event or trigger event
|
|
495
301
|
* */
|
|
@@ -525,10 +331,10 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
525
331
|
|
|
526
332
|
/** setting values for properties in the player configuration using an object.
|
|
527
333
|
* If the config does not support a property this is a no-op.
|
|
528
|
-
* @param {
|
|
334
|
+
* @param {Config} props the object with all the different properties to change.
|
|
529
335
|
* @example
|
|
530
336
|
* remotePlayer.configure({ preferredAudioLanguage: 'en-US' })
|
|
531
|
-
*
|
|
337
|
+
* remotePlayer.configure({ minSuggestedPresentationDelay: 6 })
|
|
532
338
|
* */
|
|
533
339
|
configure(props) {
|
|
534
340
|
Object.entries(props).forEach(([key, value]) => {
|
|
@@ -897,6 +703,9 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
897
703
|
this._updateSeekListeners(video);
|
|
898
704
|
}
|
|
899
705
|
this._videoElement = video;
|
|
706
|
+
|
|
707
|
+
// Emit a custom event to notify about the attachment of the video element
|
|
708
|
+
this.dispatchEvent(new Event("videoelementattached"));
|
|
900
709
|
}
|
|
901
710
|
}
|
|
902
711
|
|
|
@@ -912,7 +721,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
912
721
|
/** Tell the remote player to load the given URL.
|
|
913
722
|
* @param {string} url url to load
|
|
914
723
|
* @param {number} [position] start position in seconds (if not provided, start from beginning (VOD) or current time (LTV))
|
|
915
|
-
|
|
724
|
+
* @returns {Promise}
|
|
916
725
|
* @throws {RemotePlayerError} error object contains code & msg
|
|
917
726
|
*
|
|
918
727
|
* */
|
|
@@ -977,6 +786,11 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
977
786
|
message.action = "load";
|
|
978
787
|
message.audioLanguage = audioLanguage;
|
|
979
788
|
message.subtitlesLanguage = subtitlesLanguage;
|
|
789
|
+
if (this.getConfiguration().minSuggestedPresentationDelay > 0) {
|
|
790
|
+
message.cloudPlayerParams = {
|
|
791
|
+
"mspd": this.getConfiguration().minSuggestedPresentationDelay
|
|
792
|
+
};
|
|
793
|
+
}
|
|
980
794
|
} else {
|
|
981
795
|
message.type = "setPlayableUri";
|
|
982
796
|
}
|