senza-sdk 4.2.64-70c0747.0 → 4.2.65-4527e57.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/dist/bundle.js.LICENSE.txt +4 -0
- package/package.json +9 -16
- package/src/{implementation/alarmManager.js → alarmManager.js} +52 -15
- package/src/api.js +350 -183
- package/src/{interface/devSequence.js → devSequence.js} +35 -0
- package/src/{implementation/deviceManager.js → deviceManager.js} +65 -4
- package/src/{implementation/lifecycle.js → lifecycle.js} +182 -5
- package/src/{implementation/messageManager.js → messageManager.js} +6 -6
- package/src/{implementation/platformManager.js → platformManager.js} +4 -5
- package/src/{implementation/remotePlayer.js → remotePlayer.js} +139 -7
- package/src/{implementation/senzaShakaPlayer.js → senzaShakaPlayer.js} +114 -25
- package/src/{implementation/utils.js → utils.js} +6 -15
- package/src/implementation/api.js +0 -367
- package/src/interface/alarmManager.js +0 -69
- package/src/interface/api.js +0 -8
- package/src/interface/deviceManager.js +0 -133
- package/src/interface/lifecycle.js +0 -278
- package/src/interface/messageManager.js +0 -46
- package/src/interface/platformManager.js +0 -35
- package/src/interface/remotePlayer.js +0 -441
- package/src/interface/senzaShakaPlayer.js +0 -171
- package/src/interface/utils.js +0 -45
- /package/src/{implementation/SessionInfo.js → SessionInfo.js} +0 -0
- /package/src/{implementation/devHelper.js → devHelper.js} +0 -0
- /package/src/{implementation/eventListenersManager.js → eventListenersManager.js} +0 -0
- /package/src/{implementation/subtitlesUtils.js → subtitlesUtils.js} +0 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RemotePlayer as RemotePlayerInterface } from "../interface/remotePlayer";
|
|
2
1
|
import {
|
|
3
2
|
getFCID,
|
|
4
3
|
isAudioSyncConfigured,
|
|
@@ -69,6 +68,7 @@ function setPlaybackInfo(playbackInfo) {
|
|
|
69
68
|
* @typedef {Object} Config
|
|
70
69
|
* @property {string} preferredAudioLanguage
|
|
71
70
|
* @property {string} preferredSubtitlesLanguage
|
|
71
|
+
* @property {number} minSuggestedPresentationDelay - minimal delay allowed for live playback in seconds
|
|
72
72
|
* @property {boolean} autoPlay - (Not implemented yet) upon loading start playing automatically
|
|
73
73
|
*/
|
|
74
74
|
|
|
@@ -79,11 +79,11 @@ function setPlaybackInfo(playbackInfo) {
|
|
|
79
79
|
* @fires ended
|
|
80
80
|
* @fires error
|
|
81
81
|
* @fires onloadmodechange
|
|
82
|
+
* @fires playing
|
|
82
83
|
* @fires seeking (Not implemented yet)
|
|
83
84
|
* @fires seeked (Not implemented yet)
|
|
84
|
-
* @fires loadedmetadata (Not implemented yet)
|
|
85
85
|
*/
|
|
86
|
-
class RemotePlayer extends
|
|
86
|
+
class RemotePlayer extends EventTarget {
|
|
87
87
|
constructor() {
|
|
88
88
|
super();
|
|
89
89
|
/**
|
|
@@ -92,7 +92,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
92
92
|
*/
|
|
93
93
|
this._config = {
|
|
94
94
|
preferredAudioLanguage: "",
|
|
95
|
-
preferredSubtitlesLanguage: ""
|
|
95
|
+
preferredSubtitlesLanguage: "",
|
|
96
|
+
minSuggestedPresentationDelay: 0
|
|
96
97
|
};
|
|
97
98
|
/**
|
|
98
99
|
* @type {string}
|
|
@@ -162,6 +163,13 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
162
163
|
*/
|
|
163
164
|
this._isPlaying = false;
|
|
164
165
|
|
|
166
|
+
/**
|
|
167
|
+
* @type {string}
|
|
168
|
+
* @description the last set blackout time as epoch seconds.
|
|
169
|
+
* @private
|
|
170
|
+
*/
|
|
171
|
+
this._blackoutTime = 0;
|
|
172
|
+
|
|
165
173
|
/**
|
|
166
174
|
* @event RemotePlayer#canplay
|
|
167
175
|
* @description canplay event will be dispatched when the remote player can start play the event
|
|
@@ -525,10 +533,10 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
525
533
|
|
|
526
534
|
/** setting values for properties in the player configuration using an object.
|
|
527
535
|
* If the config does not support a property this is a no-op.
|
|
528
|
-
* @param {
|
|
536
|
+
* @param {Config} props the object with all the different properties to change.
|
|
529
537
|
* @example
|
|
530
538
|
* remotePlayer.configure({ preferredAudioLanguage: 'en-US' })
|
|
531
|
-
*
|
|
539
|
+
* remotePlayer.configure({ minSuggestedPresentationDelay: 6 })
|
|
532
540
|
* */
|
|
533
541
|
configure(props) {
|
|
534
542
|
Object.entries(props).forEach(([key, value]) => {
|
|
@@ -897,6 +905,9 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
897
905
|
this._updateSeekListeners(video);
|
|
898
906
|
}
|
|
899
907
|
this._videoElement = video;
|
|
908
|
+
|
|
909
|
+
// Emit a custom event to notify about the attachment of the video element
|
|
910
|
+
this.dispatchEvent(new Event("videoelementattached"));
|
|
900
911
|
}
|
|
901
912
|
}
|
|
902
913
|
|
|
@@ -912,7 +923,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
912
923
|
/** Tell the remote player to load the given URL.
|
|
913
924
|
* @param {string} url url to load
|
|
914
925
|
* @param {number} [position] start position in seconds (if not provided, start from beginning (VOD) or current time (LTV))
|
|
915
|
-
|
|
926
|
+
* @returns {Promise}
|
|
916
927
|
* @throws {RemotePlayerError} error object contains code & msg
|
|
917
928
|
*
|
|
918
929
|
* */
|
|
@@ -947,6 +958,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
947
958
|
this._abortSetAudioLanguage = true;
|
|
948
959
|
this._abortSetSubtitleLanguage = true;
|
|
949
960
|
this._abortSeeking = true;
|
|
961
|
+
this._blackoutTime = 0;
|
|
950
962
|
if (reset) {
|
|
951
963
|
this._reset();
|
|
952
964
|
}
|
|
@@ -977,6 +989,11 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
977
989
|
message.action = "load";
|
|
978
990
|
message.audioLanguage = audioLanguage;
|
|
979
991
|
message.subtitlesLanguage = subtitlesLanguage;
|
|
992
|
+
if (this.getConfiguration().minSuggestedPresentationDelay > 0) {
|
|
993
|
+
message.cloudPlayerParams = {
|
|
994
|
+
"mspd": this.getConfiguration().minSuggestedPresentationDelay
|
|
995
|
+
};
|
|
996
|
+
}
|
|
980
997
|
} else {
|
|
981
998
|
message.type = "setPlayableUri";
|
|
982
999
|
}
|
|
@@ -1553,6 +1570,121 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1553
1570
|
}
|
|
1554
1571
|
}
|
|
1555
1572
|
|
|
1573
|
+
setBlackoutTime(date) {
|
|
1574
|
+
|
|
1575
|
+
if (!this._isInitialized) {
|
|
1576
|
+
throw new RemotePlayerError(6500, "Cannot call setBlackoutTime() if remote player is not initialized");
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
if (this._loadMode !== this.LoadMode.LOADED) {
|
|
1580
|
+
throw new RemotePlayerError(6001, "Cannot call setBlackoutTime() if player is not loaded");
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
if (!(date instanceof Date)) {
|
|
1584
|
+
throw new TypeError("blackoutTime must be a Date object"); // remotePlayer error
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
const blackoutTime = (date.getTime()/1000).toFixed(2)
|
|
1588
|
+
if (window.cefQuery) {
|
|
1589
|
+
const FCID = getFCID();
|
|
1590
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1591
|
+
logger.log("remotePlayer setBlackoutTime: sending screenBlackout action");
|
|
1592
|
+
const message = {
|
|
1593
|
+
type: "remotePlayer.screenBlackout",
|
|
1594
|
+
class: "remotePlayer",
|
|
1595
|
+
action: "screenBlackout",
|
|
1596
|
+
fcid: FCID,
|
|
1597
|
+
blackoutTime
|
|
1598
|
+
};
|
|
1599
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1600
|
+
return new Promise((resolve, reject) => {
|
|
1601
|
+
let timerId = 0;
|
|
1602
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1603
|
+
const queryId = window.cefQuery({
|
|
1604
|
+
request: JSON.stringify(request),
|
|
1605
|
+
persistent: false,
|
|
1606
|
+
onSuccess: () => {
|
|
1607
|
+
this._blackoutTime = blackoutTime;
|
|
1608
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1609
|
+
logger.withFields({ duration }).log(`setBlackoutTime completed successfully after ${duration} ms`);
|
|
1610
|
+
timerId = clearTimer(timerId);
|
|
1611
|
+
resolve();
|
|
1612
|
+
},
|
|
1613
|
+
onFailure: (code, msg) => {
|
|
1614
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1615
|
+
logger.withFields({ duration }).log(`setBlackoutTime failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1616
|
+
timerId = clearTimer(timerId);
|
|
1617
|
+
reject(new RemotePlayerError(code, msg));
|
|
1618
|
+
}
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
logger.log(`window.cefQuery for setBlackoutTime returned query id ${queryId}`);
|
|
1622
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1623
|
+
timerId = setTimeout(() => {
|
|
1624
|
+
logger.log(`setBlackoutTime reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1625
|
+
window.cefQueryCancel(queryId);
|
|
1626
|
+
reject(new RemotePlayerError(6000, `setBlackoutTime reached timeout of ${timeout} ms`));
|
|
1627
|
+
}, timeout, queryId);
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
sdkLogger.error("remotePlayer setBlackoutTime: window.cefQuery is undefined");
|
|
1631
|
+
return Promise.resolve(undefined);
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
resetBlackoutTime() {
|
|
1635
|
+
if (!this._isInitialized) {
|
|
1636
|
+
throw new RemotePlayerError(6500, "Cannot call resetBlackoutTime() if remote player is not initialized");
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
if (this._loadMode !== this.LoadMode.LOADED) {
|
|
1640
|
+
throw new RemotePlayerError(6001, "Cannot call resetBlackoutTime() if player is not loaded");
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
if (window.cefQuery) {
|
|
1644
|
+
const FCID = getFCID();
|
|
1645
|
+
const logger = sdkLogger.withFields({ FCID });
|
|
1646
|
+
logger.log("remotePlayer resetBlackoutTime: sending screenBlackout action");
|
|
1647
|
+
const message = {
|
|
1648
|
+
type: "remotePlayer.screenBlackout",
|
|
1649
|
+
class: "remotePlayer",
|
|
1650
|
+
action: "screenBlackout",
|
|
1651
|
+
fcid: FCID,
|
|
1652
|
+
blackoutTime: 0 // Resetting the blackout time
|
|
1653
|
+
};
|
|
1654
|
+
const request = { target: "TC", waitForResponse: true, message: JSON.stringify(message) };
|
|
1655
|
+
return new Promise((resolve, reject) => {
|
|
1656
|
+
let timerId = 0;
|
|
1657
|
+
const timeBeforeSendingRequest = Date.now();
|
|
1658
|
+
const queryId = window.cefQuery({
|
|
1659
|
+
request: JSON.stringify(request),
|
|
1660
|
+
persistent: false,
|
|
1661
|
+
onSuccess: () => {
|
|
1662
|
+
this._blackoutTime = 0; // Resetting the blackout time
|
|
1663
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1664
|
+
logger.withFields({ duration }).log(`resetBlackoutTime completed successfully after ${duration} ms`);
|
|
1665
|
+
timerId = clearTimer(timerId);
|
|
1666
|
+
resolve();
|
|
1667
|
+
},
|
|
1668
|
+
onFailure: (code, msg) => {
|
|
1669
|
+
const duration = Date.now() - timeBeforeSendingRequest;
|
|
1670
|
+
logger.withFields({ duration }).log(`resetBlackoutTime failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
1671
|
+
timerId = clearTimer(timerId);
|
|
1672
|
+
reject(new RemotePlayerError(code, msg));
|
|
1673
|
+
}
|
|
1674
|
+
});
|
|
1675
|
+
logger.log(`window.cefQuery for resetBlackoutTime returned query id ${queryId}`);
|
|
1676
|
+
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
1677
|
+
timerId = setTimeout(() => {
|
|
1678
|
+
logger.log(`resetBlackoutTime reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
1679
|
+
window.cefQueryCancel(queryId);
|
|
1680
|
+
reject(new RemotePlayerError(6000, `resetBlackoutTime reached timeout of ${timeout} ms`));
|
|
1681
|
+
}, timeout, queryId);
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
sdkLogger.error("remotePlayer resetBlackoutTime: window.cefQuery is undefined");
|
|
1685
|
+
return Promise.resolve(undefined);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1556
1688
|
/**
|
|
1557
1689
|
* Getter/Setter for currentTime
|
|
1558
1690
|
*/
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import * as shaka from "shaka-player";
|
|
3
2
|
import { remotePlayer, lifecycle, getPlatformInfo } from "./api";
|
|
4
3
|
import { sdkLogger, iso6393to1 } from "./utils";
|
|
4
|
+
import moment from "moment";
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
// Define custom error category
|
|
7
8
|
shaka.util.Error.Category.SENZA_PLAYER_ERROR = 50;
|
|
@@ -31,6 +32,11 @@ class SenzaError extends shaka.util.Error {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
// Copy the shaka module and replace the Player class with SenzaShakaPlayer
|
|
36
|
+
// if we don't Copy the shaka module, the Player class will be replaced for all the other modules that import shaka
|
|
37
|
+
const senzaShaka = { ...shaka };
|
|
38
|
+
|
|
39
|
+
|
|
34
40
|
/**
|
|
35
41
|
* SenzaShakaPlayer subclass of Shaka that handles both local and remote playback.
|
|
36
42
|
*
|
|
@@ -72,9 +78,9 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
72
78
|
* @private
|
|
73
79
|
* @type {number}
|
|
74
80
|
* @description Timeout in milliseconds to wait for playing event
|
|
75
|
-
* @default
|
|
81
|
+
* @default 4000
|
|
76
82
|
*/
|
|
77
|
-
_playingTimeout =
|
|
83
|
+
_playingTimeout = 4000;
|
|
78
84
|
|
|
79
85
|
/**
|
|
80
86
|
* @private
|
|
@@ -89,7 +95,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
89
95
|
* @description Whether to stop remote player on error
|
|
90
96
|
* @default false
|
|
91
97
|
*/
|
|
92
|
-
|
|
98
|
+
_shouldStopOnRemotePlayerError = false;
|
|
93
99
|
|
|
94
100
|
/**
|
|
95
101
|
* @private
|
|
@@ -181,7 +187,7 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
181
187
|
originatesFromRemotePlayer: true
|
|
182
188
|
};
|
|
183
189
|
|
|
184
|
-
const response = await this.getNetworkingEngine().request(
|
|
190
|
+
const response = await this.getNetworkingEngine().request(senzaShaka.net.NetworkingEngine.RequestType.LICENSE, request).promise;
|
|
185
191
|
|
|
186
192
|
let responseBody = response.data;
|
|
187
193
|
if (response.status < 200 || response.status >= 300) {
|
|
@@ -280,13 +286,15 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
280
286
|
|
|
281
287
|
|
|
282
288
|
/**
|
|
283
|
-
* Handles errors
|
|
289
|
+
* Handles errors received by remote player while waiting for the playing event.
|
|
290
|
+
* The promise returned to the call for video element play will be rejected.
|
|
284
291
|
* @private
|
|
285
292
|
* @param {Error} error - The error object.
|
|
286
293
|
*/
|
|
287
294
|
_handlePlayPromiseError(error) {
|
|
288
295
|
|
|
289
296
|
sdkLogger.error("Error while waiting for playing event:", error);
|
|
297
|
+
|
|
290
298
|
if (this._playPromiseReject) {
|
|
291
299
|
this._playPromiseReject(error);
|
|
292
300
|
this._playPromiseResolve = null;
|
|
@@ -296,6 +304,46 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
296
304
|
clearTimeout(this._playTimeoutId);
|
|
297
305
|
this._playTimeoutId = null;
|
|
298
306
|
}
|
|
307
|
+
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* @private
|
|
312
|
+
* @type {number}
|
|
313
|
+
* @description Minimum suggested presentation delay in seconds
|
|
314
|
+
* @default 15
|
|
315
|
+
*/
|
|
316
|
+
_minSuggestedPresentationDelay = 15;
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Modifies the suggestedPresentationDelay in the manifest text
|
|
320
|
+
* @private
|
|
321
|
+
* @param {string} manifestText - The MPD manifest text
|
|
322
|
+
* @returns {string} - Modified manifest text , or undefined if no modification was done
|
|
323
|
+
*/
|
|
324
|
+
_updateManifestDelayIfBelowMinimum(manifestText) {
|
|
325
|
+
// Look for suggestedPresentationDelay attribute
|
|
326
|
+
const match = manifestText.match(/suggestedPresentationDelay="([^"]+)"/);
|
|
327
|
+
if (match) {
|
|
328
|
+
const durationString = match[1];
|
|
329
|
+
const duration = moment.duration(durationString);
|
|
330
|
+
const currentDelay = duration.asSeconds();
|
|
331
|
+
|
|
332
|
+
sdkLogger.info(`Found suggestedPresentationDelay in manifest: ${currentDelay.toFixed(3)}s`);
|
|
333
|
+
|
|
334
|
+
if (currentDelay < this._minSuggestedPresentationDelay) {
|
|
335
|
+
// Replace the value in the manifest text with 3 decimal places
|
|
336
|
+
manifestText = manifestText.replace(
|
|
337
|
+
/suggestedPresentationDelay="[^"]+"/,
|
|
338
|
+
`suggestedPresentationDelay="PT${this._minSuggestedPresentationDelay.toFixed(3)}S"`
|
|
339
|
+
);
|
|
340
|
+
sdkLogger.info(`Updated manifest suggestedPresentationDelay to ${this._minSuggestedPresentationDelay.toFixed(3)}s`);
|
|
341
|
+
return manifestText;
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
sdkLogger.info("suggestedPresentationDelay is not defined at the manifest");
|
|
345
|
+
}
|
|
346
|
+
return undefined;
|
|
299
347
|
}
|
|
300
348
|
|
|
301
349
|
/**
|
|
@@ -325,14 +373,38 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
325
373
|
const playTimeout = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"]?.playingEventTimeout;
|
|
326
374
|
this._playingTimeout = (playTimeout >= 0) ? playTimeout*1000 : this._playingTimeout;
|
|
327
375
|
|
|
376
|
+
// Initialize minSuggestedPresentationDelay from UI settings or use default
|
|
377
|
+
const uiSettings = getPlatformInfo()?.sessionInfo?.settings?.["ui-streamer"];
|
|
378
|
+
if (uiSettings?.minSuggestedPresentationDelay !== undefined) {
|
|
379
|
+
this._minSuggestedPresentationDelay = uiSettings.minSuggestedPresentationDelay;
|
|
380
|
+
sdkLogger.info(`Using configured minSuggestedPresentationDelay: ${this._minSuggestedPresentationDelay}s`);
|
|
381
|
+
}
|
|
382
|
+
|
|
328
383
|
// if video element is provided, add the listeres here. In this case ,there is no need to call attach.
|
|
329
384
|
if (videoElement) {
|
|
330
385
|
this._attach(videoElement);
|
|
331
386
|
sdkLogger.warn("SenzaShakaPlayer constructor Adding videoElement in the constructor is going to be deprecated in the future. Please use attach method instead.");
|
|
332
387
|
}
|
|
333
388
|
|
|
389
|
+
this.configure({
|
|
390
|
+
manifest: {
|
|
391
|
+
defaultPresentationDelay: this._minSuggestedPresentationDelay // in seconds
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
remotePlayer.configure({
|
|
396
|
+
minSuggestedPresentationDelay: this._minSuggestedPresentationDelay
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
this.addEventListener("buffering", () => {
|
|
400
|
+
if (this.videoElement) {
|
|
401
|
+
sdkLogger.info("Buffering at time:", this.videoElement.currentTime);
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
334
405
|
}
|
|
335
406
|
|
|
407
|
+
|
|
336
408
|
_attach(videoElement) {
|
|
337
409
|
this.videoElement = videoElement;
|
|
338
410
|
|
|
@@ -556,11 +628,12 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
556
628
|
const isCritical = this._isErrorCritical(remotePlayerErrorCode);
|
|
557
629
|
const error = this._createSenzaError(remotePlayerErrorCode, message, isCritical);
|
|
558
630
|
const errorMap = new Map();
|
|
631
|
+
const shouldStopPlayback = this._shouldStopOnRemotePlayerError && this.videoElement && isCritical;
|
|
559
632
|
errorMap.set("detail", error);
|
|
560
633
|
|
|
561
634
|
// Check if we should stop playback - only for critical errors when configured
|
|
562
|
-
|
|
563
|
-
|
|
635
|
+
|
|
636
|
+
if (shouldStopPlayback) {
|
|
564
637
|
try {
|
|
565
638
|
sdkLogger.warn("Stopping local player playback due to critical error");
|
|
566
639
|
// Stop only local playback
|
|
@@ -568,9 +641,11 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
568
641
|
} catch (stopError) {
|
|
569
642
|
sdkLogger.error("Error while trying to stop video element playback:", stopError);
|
|
570
643
|
}
|
|
644
|
+
// Handle error while waiting for play event. If the error is not critical or the system is configured not to stop remote player on error,
|
|
645
|
+
// the playback timeout will start the local playback
|
|
646
|
+
this._handlePlayPromiseError(error);
|
|
571
647
|
}
|
|
572
|
-
|
|
573
|
-
this._handlePlayPromiseError(error);
|
|
648
|
+
|
|
574
649
|
this.dispatchEvent(new shaka.util.FakeEvent("error", errorMap));
|
|
575
650
|
}
|
|
576
651
|
|
|
@@ -608,16 +683,30 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
608
683
|
|
|
609
684
|
// This callbakc will be activated when the manifest is loaded. It will trigger load of the remote player.
|
|
610
685
|
// This will ensure that the remote player is loaded only after the manifest is loaded by local player.
|
|
611
|
-
const responseFilterCallback = async (type) => {
|
|
612
|
-
if (type === shaka.net.NetworkingEngine.RequestType.MANIFEST
|
|
613
|
-
manifestLoadHandled = true;
|
|
686
|
+
const responseFilterCallback = async (type, response) => {
|
|
687
|
+
if (type === shaka.net.NetworkingEngine.RequestType.MANIFEST) {
|
|
614
688
|
try {
|
|
615
|
-
|
|
616
|
-
|
|
689
|
+
if (response.data && this._minSuggestedPresentationDelay > 0) {
|
|
690
|
+
const manifestText = new TextDecoder().decode(response.data);
|
|
691
|
+
const modifiedText = this._updateManifestDelayIfBelowMinimum(manifestText);
|
|
692
|
+
if (modifiedText) {
|
|
693
|
+
const responseData = new TextEncoder().encode(modifiedText).buffer;
|
|
694
|
+
response.data = responseData;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
617
697
|
} catch (error) {
|
|
618
|
-
|
|
698
|
+
sdkLogger.error("Error processing manifest:", error);
|
|
619
699
|
}
|
|
620
700
|
|
|
701
|
+
if (!manifestLoadHandled) {
|
|
702
|
+
manifestLoadHandled = true;
|
|
703
|
+
try {
|
|
704
|
+
await this._remotePlayerLoad(url, startTime);
|
|
705
|
+
remoteLoadResolver();
|
|
706
|
+
} catch (error) {
|
|
707
|
+
remoteLoadRejecter(error);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
621
710
|
}
|
|
622
711
|
};
|
|
623
712
|
|
|
@@ -680,14 +769,14 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
680
769
|
/**
|
|
681
770
|
* Override the configure method to add custom configuration handling
|
|
682
771
|
* Supports the following additional configuration options:
|
|
683
|
-
* -
|
|
772
|
+
* - shouldStopOnRemotePlayerError: boolean - If true, local player will be stopped on remote player error
|
|
684
773
|
*
|
|
685
774
|
* @override
|
|
686
775
|
* @param {Object} config - Configuration object to be merged with existing config
|
|
687
|
-
* @param {boolean} [config.
|
|
776
|
+
* @param {boolean} [config.shouldStopOnRemotePlayerError=true] - Whether to stop local player on remote player error
|
|
688
777
|
* @example
|
|
689
778
|
* player.configure({
|
|
690
|
-
*
|
|
779
|
+
* shouldStopOnRemotePlayerError: false, // Don't stop local playback on remote player error
|
|
691
780
|
* // ... other shaka configurations
|
|
692
781
|
* });
|
|
693
782
|
*/
|
|
@@ -695,12 +784,12 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
695
784
|
sdkLogger.log("configure player with: ", JSON.stringify(config));
|
|
696
785
|
|
|
697
786
|
// Handle custom configuration
|
|
698
|
-
if (config.
|
|
699
|
-
this.
|
|
787
|
+
if (config.shouldStopOnRemotePlayerError !== undefined) {
|
|
788
|
+
this._shouldStopOnRemotePlayerError = !!config.shouldStopOnRemotePlayerError;
|
|
700
789
|
// Remove our custom config so it doesn't get passed to parent
|
|
701
790
|
// Use rest operator without creating a named variable for the removed property
|
|
702
791
|
// eslint-disable-next-line no-unused-vars
|
|
703
|
-
const {
|
|
792
|
+
const { shouldStopOnRemotePlayerError, ...shakaConfig } = config;
|
|
704
793
|
config = shakaConfig;
|
|
705
794
|
}
|
|
706
795
|
|
|
@@ -800,5 +889,5 @@ export class SenzaShakaPlayer extends shaka.Player {
|
|
|
800
889
|
|
|
801
890
|
}
|
|
802
891
|
|
|
803
|
-
|
|
804
|
-
export { shaka };
|
|
892
|
+
senzaShaka.Player = SenzaShakaPlayer;
|
|
893
|
+
export { senzaShaka as shaka };
|
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
import { getPlatformInfo } from "./api";
|
|
2
|
+
import pack from "../package.json";
|
|
2
3
|
import { sessionInfo } from "./SessionInfo";
|
|
4
|
+
const { version } = pack;
|
|
3
5
|
|
|
4
6
|
const REST_RESPONSE_TIMEOUT_SECONDS = 5;
|
|
5
7
|
|
|
6
|
-
export function getVersion() {
|
|
7
|
-
return typeof IMPLEMENTATION_VERSION !== "undefined"
|
|
8
|
-
// eslint-disable-next-line no-undef
|
|
9
|
-
? IMPLEMENTATION_VERSION
|
|
10
|
-
: "unknown";
|
|
11
|
-
};
|
|
12
|
-
|
|
13
8
|
export function getFCID() {
|
|
14
9
|
return Math.round(Math.random() * 100000) + "-" + getPlatformInfo().sessionInfo?.connectionId;
|
|
15
10
|
}
|
|
@@ -47,7 +42,7 @@ class SdkLogger {
|
|
|
47
42
|
console.info(`[hs-sdk] [metrics] ${JSON.stringify(metricObj)}`);
|
|
48
43
|
}
|
|
49
44
|
withFields(logFields) {
|
|
50
|
-
return new SdkLogger({
|
|
45
|
+
return new SdkLogger({...this.logFields, ...logFields});
|
|
51
46
|
}
|
|
52
47
|
formatLogString(data) {
|
|
53
48
|
let logString = "[hs-sdk] " + data.join(" ");
|
|
@@ -56,13 +51,9 @@ class SdkLogger {
|
|
|
56
51
|
}
|
|
57
52
|
return logString;
|
|
58
53
|
}
|
|
59
|
-
addfields(fields) {
|
|
60
|
-
this.logFields = { ...this.logFields, ...fields };
|
|
61
|
-
return this;
|
|
62
|
-
}
|
|
63
54
|
}
|
|
64
55
|
|
|
65
|
-
export const sdkLogger = new SdkLogger({
|
|
56
|
+
export const sdkLogger = new SdkLogger({sdkVersion: version, url: window?.location?.href ?? ""});
|
|
66
57
|
|
|
67
58
|
export async function getRestResponse(messageName) {
|
|
68
59
|
|
|
@@ -73,14 +64,14 @@ export async function getRestResponse(messageName) {
|
|
|
73
64
|
|
|
74
65
|
return new Promise((resolve, reject) => {
|
|
75
66
|
const FCID = getFCID();
|
|
76
|
-
const logger = sdkLogger.withFields({
|
|
67
|
+
const logger = sdkLogger.withFields({FCID});
|
|
77
68
|
const message = {
|
|
78
69
|
type: "restRequest",
|
|
79
70
|
name: messageName,
|
|
80
71
|
method: "GET",
|
|
81
72
|
fcid: FCID
|
|
82
73
|
};
|
|
83
|
-
const request = {
|
|
74
|
+
const request = {target: "TC", waitForResponse: true, message: JSON.stringify(message)};
|
|
84
75
|
let timeoutHandler = 0;
|
|
85
76
|
const queryId = window.cefQuery({
|
|
86
77
|
request: JSON.stringify(request),
|