senza-sdk 4.3.7-b2908c5.0 → 4.4.1-4ca75a1.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 +10 -3
- package/package.json +6 -6
- package/src/api.js +0 -25
- package/src/implementation/api.js +1 -1
- package/src/implementation/lifecycle.js +37 -25
- package/src/implementation/remotePlayer.js +55 -71
- package/src/implementation/senzaShakaPlayer.js +69 -85
- package/src/implementation/utils.js +224 -0
- package/src/interface/deviceManager.js +1 -1
- package/src/interface/remotePlayer.js +2 -43
- package/src/interface/senzaShakaPlayer.js +2 -1
- package/src/interface/version.js +1 -1
|
@@ -35,6 +35,13 @@
|
|
|
35
35
|
SOFTWARE.
|
|
36
36
|
*/
|
|
37
37
|
|
|
38
|
+
/*
|
|
39
|
+
@license
|
|
40
|
+
EME Encryption Scheme Polyfill
|
|
41
|
+
Copyright 2019 Google LLC
|
|
42
|
+
SPDX-License-Identifier: Apache-2.0
|
|
43
|
+
*/
|
|
44
|
+
|
|
38
45
|
/*
|
|
39
46
|
@license
|
|
40
47
|
MSS Transmuxer
|
|
@@ -65,9 +72,9 @@
|
|
|
65
72
|
|
|
66
73
|
/*
|
|
67
74
|
@license
|
|
68
|
-
|
|
69
|
-
Copyright
|
|
70
|
-
SPDX-License-Identifier:
|
|
75
|
+
glMatrix: https://github.com/toji/gl-matrix/
|
|
76
|
+
Copyright 2015-2021, Brandon Jones, Colin MacKenzie IV
|
|
77
|
+
SPDX-License-Identifier: MIT
|
|
71
78
|
*/
|
|
72
79
|
|
|
73
80
|
/*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "senza-sdk",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.1-4ca75a1.0",
|
|
4
4
|
"main": "./src/api.js",
|
|
5
5
|
"description": "API for Senza application",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,15 +53,15 @@
|
|
|
53
53
|
"statements": 93.46
|
|
54
54
|
},
|
|
55
55
|
"src/interface": {
|
|
56
|
-
"branches":
|
|
57
|
-
"functions": 39.
|
|
58
|
-
"lines": 64.
|
|
59
|
-
"statements": 65
|
|
56
|
+
"branches": 68.42,
|
|
57
|
+
"functions": 39.63,
|
|
58
|
+
"lines": 64.94,
|
|
59
|
+
"statements": 65.32
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"shaka-player": "4.
|
|
64
|
+
"shaka-player": "^4.12.5",
|
|
65
65
|
"moment": "^2.30.1"
|
|
66
66
|
}
|
|
67
67
|
}
|
package/src/api.js
CHANGED
|
@@ -370,31 +370,6 @@ export function setTimezone(timezone) {
|
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
372
|
|
|
373
|
-
/**
|
|
374
|
-
* @deprecated use deviceManager.deviceInfo instead.
|
|
375
|
-
* Returns the device information, including device id, connection id, community, tenant and client ip
|
|
376
|
-
* */
|
|
377
|
-
export function getDeviceInfo() {
|
|
378
|
-
// STOP DO NOT CHANGE THIS FUNCTION AS IT IS DEPRECATED!!!!
|
|
379
|
-
if (isRunningE2E()) {
|
|
380
|
-
return {
|
|
381
|
-
deviceId: deviceManager.deviceInfo.deviceId,
|
|
382
|
-
connectionId: deviceManager.deviceInfo.connectionId,
|
|
383
|
-
community: deviceManager.deviceInfo.community,
|
|
384
|
-
tenant: deviceManager.deviceInfo.tenant,
|
|
385
|
-
clientIp: deviceManager.deviceInfo.clientIp
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
sdkLogger.log("getDeviceInfo running locally, returning dummy info");
|
|
389
|
-
return {
|
|
390
|
-
deviceId: "123456789",
|
|
391
|
-
connectionId: "dummy",
|
|
392
|
-
community: "LocalDev",
|
|
393
|
-
tenant: "XXXXXX",
|
|
394
|
-
clientIp: "0.0.0.0"
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
373
|
export { lifecycle, remotePlayer, alarmManager, deviceManager, platformManager, messageManager, ShakaPlayerImplementation as ShakaPlayer, shakaImplementation as shaka, showSequence, initSequence };
|
|
399
374
|
|
|
400
375
|
// Assign the senza library to the old name (hs) for backward compatibility
|
|
@@ -130,7 +130,7 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
|
|
|
130
130
|
|
|
131
131
|
// Initialize lifecycle first to make sure the state is updated.
|
|
132
132
|
await lifecycle._init(sessionInfoObj?.settings?.["ui-streamer"], triggerEvent);
|
|
133
|
-
await remotePlayer._init(sessionInfoObj
|
|
133
|
+
await remotePlayer._init(sessionInfoObj, triggerEvent);
|
|
134
134
|
alarmManager._init();
|
|
135
135
|
messageManager._init();
|
|
136
136
|
sdkLogger.log("All submodules initialized");
|
|
@@ -29,7 +29,9 @@ class Lifecycle extends LifecycleInterface {
|
|
|
29
29
|
* @private
|
|
30
30
|
*/
|
|
31
31
|
this._isInitialized = false;
|
|
32
|
-
this.
|
|
32
|
+
this._inTransitionToForeground = false;
|
|
33
|
+
this._inTransitionToBackground = false;
|
|
34
|
+
this._inTransitionToStandby = false;
|
|
33
35
|
|
|
34
36
|
/**
|
|
35
37
|
* Event listeners manager for the userdisconnected event
|
|
@@ -310,7 +312,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
310
312
|
// This api is part of epic HSDEV-713
|
|
311
313
|
_moveToUiStandby() {
|
|
312
314
|
if (window.cefQuery) {
|
|
313
|
-
this.
|
|
315
|
+
this._inTransitionToStandby = true;
|
|
314
316
|
return new Promise((resolve, reject) => {
|
|
315
317
|
const FCID = getFCID();
|
|
316
318
|
const request = { target: "TC", waitForResponse: false, internalAction: "uiExit", message: JSON.stringify({ type: "uiStandbyRequest", fcid: FCID }) };
|
|
@@ -321,12 +323,12 @@ class Lifecycle extends LifecycleInterface {
|
|
|
321
323
|
persistent: false,
|
|
322
324
|
onSuccess: () => {
|
|
323
325
|
logger.log("[ moveToUiStandby ] moveToUiStandby successfully sent");
|
|
324
|
-
this.
|
|
326
|
+
this._inTransitionToStandby = false;
|
|
325
327
|
resolve(true);
|
|
326
328
|
},
|
|
327
329
|
onFailure: (code, msg) => {
|
|
328
330
|
logger.error(`[ moveToUiStandby ] moveToUiStandby failed: ${code} ${msg}`);
|
|
329
|
-
this.
|
|
331
|
+
this._inTransitionToStandby = false;
|
|
330
332
|
reject(`moveToUiStandby failed: ${code} ${msg}`);
|
|
331
333
|
}
|
|
332
334
|
});
|
|
@@ -423,6 +425,14 @@ class Lifecycle extends LifecycleInterface {
|
|
|
423
425
|
this._countdown = null;
|
|
424
426
|
}
|
|
425
427
|
|
|
428
|
+
/**
|
|
429
|
+
* @private
|
|
430
|
+
*/
|
|
431
|
+
_isInTransition() {
|
|
432
|
+
return this._inTransitionToForeground || this._inTransitionToBackground || this._inTransitionToStandby;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
|
|
426
436
|
getState() {
|
|
427
437
|
if (window.cefQuery) {
|
|
428
438
|
return new Promise((resolve, reject) => {
|
|
@@ -445,14 +455,19 @@ class Lifecycle extends LifecycleInterface {
|
|
|
445
455
|
|
|
446
456
|
moveToForeground() {
|
|
447
457
|
if (window.cefQuery) {
|
|
448
|
-
|
|
449
|
-
|
|
458
|
+
const inTransition = this._isInTransition();
|
|
459
|
+
if (inTransition || this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND) {
|
|
460
|
+
sdkLogger.warn(`lifecycle moveToForeground: No need to transition to foreground, state: ${this._state} transition: ${inTransition}`);
|
|
450
461
|
return Promise.resolve(false);
|
|
451
462
|
}
|
|
452
|
-
this.
|
|
463
|
+
this._inTransitionToForeground = true;
|
|
453
464
|
alarmManager._moveToForegroundCalled();
|
|
454
465
|
const FCID = getFCID();
|
|
455
466
|
if (this._remotePlayerApiVersion >= 2) {
|
|
467
|
+
// Only update to playing UI if we started seeking in ABR. But, if we are seeking while already paused, keep the target seek state as is.
|
|
468
|
+
if (remotePlayer._isSeekingByApplication && remotePlayer._targetSeekPlayingState === TargetPlayingState.PLAYING_ABR) {
|
|
469
|
+
remotePlayer._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
|
|
470
|
+
}
|
|
456
471
|
return new Promise((resolve, reject) => {
|
|
457
472
|
const FCID = getFCID();
|
|
458
473
|
const logger = sdkLogger.withFields({ FCID });
|
|
@@ -472,14 +487,14 @@ class Lifecycle extends LifecycleInterface {
|
|
|
472
487
|
onSuccess: () => {
|
|
473
488
|
const duration = Date.now() - timeBeforeSendingRequest;
|
|
474
489
|
logger.withFields({ duration }).log(`stop completed successfully after ${duration} ms`);
|
|
475
|
-
this.
|
|
490
|
+
this._inTransitionToForeground = false;
|
|
476
491
|
timerId = clearTimer(timerId);
|
|
477
492
|
resolve(true);
|
|
478
493
|
},
|
|
479
494
|
onFailure: (code, msg) => {
|
|
480
495
|
const duration = Date.now() - timeBeforeSendingRequest;
|
|
481
496
|
logger.withFields({ duration }).log(`stop failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
482
|
-
this.
|
|
497
|
+
this._inTransitionToForeground = false;
|
|
483
498
|
timerId = clearTimer(timerId);
|
|
484
499
|
reject(new SenzaError(code, msg));
|
|
485
500
|
}
|
|
@@ -488,7 +503,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
488
503
|
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
489
504
|
timerId = setTimeout(() => {
|
|
490
505
|
logger.log(`stop reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
491
|
-
this.
|
|
506
|
+
this._inTransitionToForeground = false;
|
|
492
507
|
window.cefQueryCancel(queryId);
|
|
493
508
|
reject(new SenzaError(6000, `stop reached timeout of ${timeout} ms`));
|
|
494
509
|
}, timeout, queryId);
|
|
@@ -503,11 +518,11 @@ class Lifecycle extends LifecycleInterface {
|
|
|
503
518
|
persistent: false,
|
|
504
519
|
onSuccess: () => {
|
|
505
520
|
logger.log("uiActiveRequest successfully sent");
|
|
506
|
-
this.
|
|
521
|
+
this._inTransitionToForeground = false;
|
|
507
522
|
resolve(true);
|
|
508
523
|
},
|
|
509
524
|
onFailure: (code, msg) => {
|
|
510
|
-
this.
|
|
525
|
+
this._inTransitionToForeground = false;
|
|
511
526
|
logger.error(`uiActiveRequest failed: ${code} ${msg}`);
|
|
512
527
|
reject(`uiActiveRequest failed: ${code} ${msg}`);
|
|
513
528
|
}
|
|
@@ -520,10 +535,6 @@ class Lifecycle extends LifecycleInterface {
|
|
|
520
535
|
|
|
521
536
|
_moveToBackground() {
|
|
522
537
|
if (window.cefQuery) {
|
|
523
|
-
if (this._inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
|
|
524
|
-
sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${this._inTransition}`);
|
|
525
|
-
return Promise.resolve(false);
|
|
526
|
-
}
|
|
527
538
|
// If audio sync is disabled, we only need to sync before remote player starts playing
|
|
528
539
|
if (!isAudioSyncConfigured()) {
|
|
529
540
|
remotePlayer._syncRemotePlayerWithLocalPlayer();
|
|
@@ -534,8 +545,8 @@ class Lifecycle extends LifecycleInterface {
|
|
|
534
545
|
if (!remotePlayer._isPlaying) {
|
|
535
546
|
return this._moveToUiStandby();
|
|
536
547
|
}
|
|
537
|
-
|
|
538
|
-
this.
|
|
548
|
+
remotePlayer._changePlayMode(true);
|
|
549
|
+
this._inTransitionToBackground = true;
|
|
539
550
|
return new Promise((resolve, reject) => {
|
|
540
551
|
const FCID = getFCID();
|
|
541
552
|
const logger = sdkLogger.withFields({ FCID });
|
|
@@ -553,7 +564,8 @@ class Lifecycle extends LifecycleInterface {
|
|
|
553
564
|
if (this._remotePlayerApiVersion >= 2) {
|
|
554
565
|
message.type = "remotePlayer.play";
|
|
555
566
|
message.class = "remotePlayer";
|
|
556
|
-
|
|
567
|
+
// in background, client expects to get NON_SEAMLESS switch mode.
|
|
568
|
+
message.switchMode = remotePlayer._isAudioSyncEnabled() && this._state !== this.UiState.BACKGROUND ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
|
|
557
569
|
message.streamType = remotePlayer.textTrackVisibility ? (StreamType.AUDIO | StreamType.VIDEO | StreamType.SUBTITLE) : (StreamType.AUDIO | StreamType.VIDEO);
|
|
558
570
|
request = {
|
|
559
571
|
target: "TC",
|
|
@@ -576,14 +588,14 @@ class Lifecycle extends LifecycleInterface {
|
|
|
576
588
|
onSuccess: () => {
|
|
577
589
|
const duration = Date.now() - timeBeforeSendingRequest;
|
|
578
590
|
logger.withFields({ duration }).log(`play completed successfully after ${duration} ms`);
|
|
579
|
-
this.
|
|
591
|
+
this._inTransitionToBackground = false;
|
|
580
592
|
timerId = clearTimer(timerId);
|
|
581
593
|
resolve();
|
|
582
594
|
},
|
|
583
595
|
onFailure: (code, msg) => {
|
|
584
596
|
const duration = Date.now() - timeBeforeSendingRequest;
|
|
585
597
|
logger.withFields({ duration }).log(`play failed after ${duration} ms. Error code: ${code}, error message: ${msg}`);
|
|
586
|
-
this.
|
|
598
|
+
this._inTransitionToBackground = false;
|
|
587
599
|
timerId = clearTimer(timerId);
|
|
588
600
|
reject(new SenzaError(code, msg));
|
|
589
601
|
}
|
|
@@ -593,7 +605,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
593
605
|
const timeout = this._remotePlayerConfirmationTimeout + 1000;
|
|
594
606
|
timerId = setTimeout(() => {
|
|
595
607
|
logger.log(`play reached timeout of ${timeout} ms, canceling query id ${queryId}`);
|
|
596
|
-
this.
|
|
608
|
+
this._inTransitionToBackground = false;
|
|
597
609
|
window.cefQueryCancel(queryId);
|
|
598
610
|
reject(new SenzaError(6000, `play reached timeout of ${timeout} ms`));
|
|
599
611
|
}, timeout, queryId);
|
|
@@ -606,11 +618,11 @@ class Lifecycle extends LifecycleInterface {
|
|
|
606
618
|
|
|
607
619
|
moveToBackground() {
|
|
608
620
|
if (window.cefQuery) {
|
|
609
|
-
|
|
610
|
-
|
|
621
|
+
const inTransition = this._isInTransition();
|
|
622
|
+
if (inTransition || this._state === this.UiState.BACKGROUND || this._state === this.UiState.IN_TRANSITION_TO_BACKGROUND) {
|
|
623
|
+
sdkLogger.warn(`lifecycle moveToBackground: No need to transition to background, state: ${this._state} transition: ${inTransition}`);
|
|
611
624
|
return Promise.resolve(false);
|
|
612
625
|
}
|
|
613
|
-
|
|
614
626
|
if (remotePlayer._isSeekingByApplication) {
|
|
615
627
|
remotePlayer._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
616
628
|
return Promise.resolve(true);
|
|
@@ -11,7 +11,8 @@ import {
|
|
|
11
11
|
SeekState,
|
|
12
12
|
TargetPlayingState,
|
|
13
13
|
isSubtitlesTranslationAllowed,
|
|
14
|
-
isSubtitlesTranslationPattern
|
|
14
|
+
isSubtitlesTranslationPattern,
|
|
15
|
+
isAppVersionAboveOrEqual
|
|
15
16
|
} from "./utils";
|
|
16
17
|
import { lifecycle } from "./lifecycle";
|
|
17
18
|
import { writeLicenseResponse } from "./api";
|
|
@@ -143,10 +144,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
143
144
|
}
|
|
144
145
|
|
|
145
146
|
/** @private Initialize the remote player
|
|
146
|
-
* @param {Object}
|
|
147
|
+
* @param {Object} sessionObj session information object
|
|
148
|
+
* @param {Object} triggerEvent trigger event object
|
|
147
149
|
* */
|
|
148
|
-
async _init(
|
|
150
|
+
async _init(sessionObj, triggerEvent) {
|
|
149
151
|
sdkLogger.info("Initializing RemotePlayer");
|
|
152
|
+
const uiStreamerSettings = sessionObj?.settings?.["ui-streamer"];
|
|
150
153
|
let playerState = {
|
|
151
154
|
isLoaded: false,
|
|
152
155
|
playbackUrl: ""
|
|
@@ -193,6 +196,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
193
196
|
this._remotePlayerConfirmationTimeout = uiStreamerSettings?.remotePlayerConfirmationTimeout ?? DEFAULT_REMOTE_PLAYER_CONFIRMATION_TIMEOUT;
|
|
194
197
|
this._remotePlayerApiVersion = uiStreamerSettings?.remotePlayerApiVersion || 1;
|
|
195
198
|
this._multiSeekDelay = uiStreamerSettings?.multiSeekDelay || MULTI_SEEK_DELAY_MSEC;
|
|
199
|
+
this._deviceAppVersion = sessionObj?.manifest?.["device-app"] || "";
|
|
196
200
|
|
|
197
201
|
sdkLogger.info(`remotePLayer isPlaying=${this._isPlaying}`);
|
|
198
202
|
|
|
@@ -527,7 +531,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
527
531
|
};
|
|
528
532
|
let waitForResponse = false;
|
|
529
533
|
if (this._remotePlayerApiVersion >= 2) {
|
|
530
|
-
|
|
534
|
+
// in background, client expects to get NON_SEAMLESS switch mode.
|
|
535
|
+
message.switchMode = this._isAudioSyncEnabled() && lifecycle.state !== lifecycle.UiState.BACKGROUND ? SwitchMode.SEAMLESS : SwitchMode.NON_SEAMLESS;
|
|
531
536
|
message.streamType = streamType;
|
|
532
537
|
waitForResponse = true;
|
|
533
538
|
|
|
@@ -948,8 +953,13 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
948
953
|
|
|
949
954
|
// If seeking in progress, wait for seek to complete before playing
|
|
950
955
|
if (this._isSeekingByApplication) {
|
|
951
|
-
|
|
952
|
-
|
|
956
|
+
if (this._inTransitionToForeground || lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND) {
|
|
957
|
+
sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_UI");
|
|
958
|
+
this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
|
|
959
|
+
} else {
|
|
960
|
+
sdkLogger.info("application requesting play during seek. setting targetSeekPlayingState to PLAYING_ABR");
|
|
961
|
+
this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
962
|
+
}
|
|
953
963
|
return Promise.resolve(true);
|
|
954
964
|
}
|
|
955
965
|
/*
|
|
@@ -1081,30 +1091,24 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1081
1091
|
* @throws {RemotePlayerError} If the player is not initialized or not loaded.
|
|
1082
1092
|
**/
|
|
1083
1093
|
selectAudioTrack(audioTrackId) {
|
|
1084
|
-
return this._selectAudioTrack(audioTrackId, false);
|
|
1085
|
-
}
|
|
1086
|
-
|
|
1087
|
-
_selectAudioTrack(audioTrackId, force = false) {
|
|
1088
1094
|
if (!this._isInitialized) {
|
|
1089
1095
|
throw new RemotePlayerError(6500, "Cannot call selectAudioTrack() if remote player is not initialized");
|
|
1090
1096
|
}
|
|
1091
1097
|
if (this._loadMode !== this.LoadMode.LOADED) {
|
|
1092
1098
|
throw new RemotePlayerError(6001, "Cannot call selectAudioTrack() if remote player is not loaded");
|
|
1093
1099
|
}
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
if (!found) {
|
|
1103
|
-
sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
|
|
1104
|
-
return Promise.resolve();
|
|
1100
|
+
let found = false;
|
|
1101
|
+
const prevSelectedAudioTrack = this._selectedAudioTrack;
|
|
1102
|
+
for (const track of this.getAudioTracks()) {
|
|
1103
|
+
if (track.id === audioTrackId) {
|
|
1104
|
+
found = true;
|
|
1105
|
+
break;
|
|
1105
1106
|
}
|
|
1106
1107
|
}
|
|
1107
|
-
|
|
1108
|
+
if (!found) {
|
|
1109
|
+
sdkLogger.warn(`Invalid audioTrackId ${audioTrackId}`);
|
|
1110
|
+
return Promise.resolve();
|
|
1111
|
+
}
|
|
1108
1112
|
if (this._selectedAudioTrack === audioTrackId) {
|
|
1109
1113
|
return Promise.resolve(); // Audio language already selected
|
|
1110
1114
|
}
|
|
@@ -1121,12 +1125,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1121
1125
|
}
|
|
1122
1126
|
}
|
|
1123
1127
|
|
|
1124
|
-
async selectAudioLanguage(language, role="", accessibilityPurposeCode=this.AccessibilityPurposeCode.NONE) {
|
|
1125
|
-
const trackId = this._generateSenzaTrackId(language, role, accessibilityPurposeCode);
|
|
1126
|
-
return this._selectAudioTrack(trackId, true);
|
|
1127
|
-
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
1128
|
/**
|
|
1131
1129
|
* Handles the asynchronous selection of an audio track.
|
|
1132
1130
|
* If the player is playing, it pauses before changing the track and resumes playback if necessary.
|
|
@@ -1220,11 +1218,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1220
1218
|
return Promise.resolve(undefined);
|
|
1221
1219
|
}
|
|
1222
1220
|
|
|
1223
|
-
async selectTextLanguage(language, role="", accessibilityPurposeCode=this.AccessibilityPurposeCode.NONE) {
|
|
1224
|
-
const trackId = this._generateSenzaTrackId(language, role, accessibilityPurposeCode);
|
|
1225
|
-
return this._selectTextTrack(trackId, true);
|
|
1226
|
-
}
|
|
1227
|
-
|
|
1228
1221
|
/** Select a specific text (subtitle) track.
|
|
1229
1222
|
* Track id should come from a call to getTextTracks.
|
|
1230
1223
|
* If no tracks exist - this is a no-op.
|
|
@@ -1234,31 +1227,24 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1234
1227
|
* @throws {RemotePlayerError} If the player is not initialized or not loaded.
|
|
1235
1228
|
* */
|
|
1236
1229
|
selectTextTrack(textTrackId) {
|
|
1237
|
-
return this._selectTextTrack(textTrackId, false);
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
_selectTextTrack(textTrackId, force = false) {
|
|
1241
1230
|
if (!this._isInitialized) {
|
|
1242
1231
|
throw new RemotePlayerError(6500, "Cannot call selectTextTrack() if remote player is not initialized");
|
|
1243
1232
|
}
|
|
1244
1233
|
if (this._loadMode !== this.LoadMode.LOADED) {
|
|
1245
1234
|
throw new RemotePlayerError(6001, "Cannot call selectTextTrack() if remote player is not loaded");
|
|
1246
1235
|
}
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
}
|
|
1254
|
-
}
|
|
1255
|
-
if (!found) {
|
|
1256
|
-
sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
|
|
1257
|
-
return Promise.resolve();
|
|
1236
|
+
let found = false;
|
|
1237
|
+
const prevSelectedTextTrack = this._selectedSubtitlesTrack;
|
|
1238
|
+
for (const track of this.getTextTracks()) {
|
|
1239
|
+
if (track.id === textTrackId) {
|
|
1240
|
+
found = true;
|
|
1241
|
+
break;
|
|
1258
1242
|
}
|
|
1259
1243
|
}
|
|
1260
|
-
|
|
1261
|
-
|
|
1244
|
+
if (!found) {
|
|
1245
|
+
sdkLogger.warn(`Invalid textTrackId ${textTrackId}`);
|
|
1246
|
+
return Promise.resolve();
|
|
1247
|
+
}
|
|
1262
1248
|
if (this._selectedSubtitlesTrack === textTrackId) {
|
|
1263
1249
|
return Promise.resolve(); // Subtitle language already selected
|
|
1264
1250
|
}
|
|
@@ -1534,6 +1520,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1534
1520
|
* @private
|
|
1535
1521
|
*/
|
|
1536
1522
|
async _startSeeking(playbackPosition) {
|
|
1523
|
+
const backgroundSeekMinClientAppVersion = "25.27.8";
|
|
1524
|
+
|
|
1537
1525
|
if (this._isSetAudioInProgress || this._isSetSubtitlesInProgress) {
|
|
1538
1526
|
sdkLogger.info("Seeking not supported while setAudioLanguage or setSubtitleLanguage are in progress.");
|
|
1539
1527
|
return;
|
|
@@ -1549,10 +1537,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1549
1537
|
return;
|
|
1550
1538
|
}
|
|
1551
1539
|
}
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1540
|
+
const backgroundSeekSupported = isAppVersionAboveOrEqual(this._deviceAppVersion, backgroundSeekMinClientAppVersion);
|
|
1541
|
+
if (!backgroundSeekSupported && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
|
|
1542
|
+
sdkLogger.warn(`Seeking: background seek not supported for device app version: ${this._deviceAppVersion}`);
|
|
1543
|
+
}
|
|
1544
|
+
if (this._remotePlayerApiVersion >= 2 && !this._isSeekingByPlatform && !this._isSeekingByApplication
|
|
1545
|
+
&& (lifecycle.state === lifecycle.UiState.FOREGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND || backgroundSeekSupported)) {
|
|
1556
1546
|
this._atomicSeek();
|
|
1557
1547
|
} else {
|
|
1558
1548
|
sdkLogger.info(`Seeking: skipping seeking event to currentTime: ${playbackPosition}, internalSeek: ${this._isSeekingByPlatform}, localPlayerSeek: ${this._isSeekingByApplication}, state: ${lifecycle.state}`);
|
|
@@ -1571,16 +1561,16 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1571
1561
|
* */
|
|
1572
1562
|
async _atomicSeek() {
|
|
1573
1563
|
sdkLogger.info("Seeking: local video element seeking start while isPlaying: ", this._isPlaying);
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1564
|
+
if (this._isPlaying) {
|
|
1565
|
+
if (!(lifecycle._inTransitionToForeground || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND) && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
|
|
1566
|
+
sdkLogger.info("seek in background", this._isPlaying);
|
|
1567
|
+
this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
1568
|
+
} else {
|
|
1569
|
+
this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
|
|
1570
|
+
}
|
|
1571
|
+
} else {
|
|
1572
|
+
this._targetSeekPlayingState = TargetPlayingState.PAUSED;
|
|
1573
|
+
}
|
|
1584
1574
|
|
|
1585
1575
|
// The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
|
|
1586
1576
|
if (this._videoElement) {
|
|
@@ -1647,7 +1637,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1647
1637
|
|
|
1648
1638
|
// If in TargetPlayingState.PAUSE, no need to resume.
|
|
1649
1639
|
// Resume without awaiting to avoid blocking the seek process anymore
|
|
1650
|
-
// In case where we aborted, we don't want to resume playback.
|
|
1640
|
+
// In case where we aborted (new load or unload called), we don't want to resume playback.
|
|
1651
1641
|
if (!this._abortSeeking) {
|
|
1652
1642
|
if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_UI) {
|
|
1653
1643
|
if (!this._isAudioSyncEnabled()) {
|
|
@@ -1798,12 +1788,6 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1798
1788
|
return setLanguageError ? Promise.reject(setLanguageError) : Promise.resolve();
|
|
1799
1789
|
}
|
|
1800
1790
|
|
|
1801
|
-
_generateSenzaTrackId(language, role, accessibilityPurpose) {
|
|
1802
|
-
const l = language || "";
|
|
1803
|
-
const r = role || "";
|
|
1804
|
-
const ap = accessibilityPurpose || "";
|
|
1805
|
-
return `${l}:${r}:${ap}`;
|
|
1806
|
-
}
|
|
1807
1791
|
_setScreenBlackout(blackoutTime) {
|
|
1808
1792
|
if (window.cefQuery) {
|
|
1809
1793
|
const FCID = getFCID();
|