senza-sdk 4.4.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 +32 -0
- package/package.json +1 -1
- package/src/implementation/api.js +1 -1
- package/src/implementation/lifecycle.js +37 -25
- package/src/implementation/remotePlayer.js +35 -21
- package/src/implementation/senzaShakaPlayer.js +22 -8
- package/src/implementation/utils.js +18 -0
- package/src/interface/senzaShakaPlayer.js +2 -1
- package/src/interface/version.js +1 -1
- package/dist/implementation.bundle.js +0 -2
- package/dist/implementation.bundle.js.LICENSE.txt +0 -57
|
@@ -10,6 +10,31 @@
|
|
|
10
10
|
SPDX-License-Identifier: Apache-2.0
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
+
/*
|
|
14
|
+
@license
|
|
15
|
+
Copyright 2013 Ali Al Dallal
|
|
16
|
+
|
|
17
|
+
Licensed under the MIT license.
|
|
18
|
+
|
|
19
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
20
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
21
|
+
in the Software without restriction, including without limitation the rights
|
|
22
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
23
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
24
|
+
furnished to do so, subject to the following conditions:
|
|
25
|
+
|
|
26
|
+
The above copyright notice and this permission notice shall be included in
|
|
27
|
+
all copies or substantial portions of the Software.
|
|
28
|
+
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
32
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
33
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
34
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
|
+
SOFTWARE.
|
|
36
|
+
*/
|
|
37
|
+
|
|
13
38
|
/*
|
|
14
39
|
@license
|
|
15
40
|
EME Encryption Scheme Polyfill
|
|
@@ -45,6 +70,13 @@
|
|
|
45
70
|
SPDX-License-Identifier: Apache-2.0
|
|
46
71
|
*/
|
|
47
72
|
|
|
73
|
+
/*
|
|
74
|
+
@license
|
|
75
|
+
glMatrix: https://github.com/toji/gl-matrix/
|
|
76
|
+
Copyright 2015-2021, Brandon Jones, Colin MacKenzie IV
|
|
77
|
+
SPDX-License-Identifier: MIT
|
|
78
|
+
*/
|
|
79
|
+
|
|
48
80
|
/*
|
|
49
81
|
@license
|
|
50
82
|
tXml
|
package/package.json
CHANGED
|
@@ -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
|
/*
|
|
@@ -1510,6 +1520,8 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1510
1520
|
* @private
|
|
1511
1521
|
*/
|
|
1512
1522
|
async _startSeeking(playbackPosition) {
|
|
1523
|
+
const backgroundSeekMinClientAppVersion = "25.27.8";
|
|
1524
|
+
|
|
1513
1525
|
if (this._isSetAudioInProgress || this._isSetSubtitlesInProgress) {
|
|
1514
1526
|
sdkLogger.info("Seeking not supported while setAudioLanguage or setSubtitleLanguage are in progress.");
|
|
1515
1527
|
return;
|
|
@@ -1525,10 +1537,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1525
1537
|
return;
|
|
1526
1538
|
}
|
|
1527
1539
|
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
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)) {
|
|
1532
1546
|
this._atomicSeek();
|
|
1533
1547
|
} else {
|
|
1534
1548
|
sdkLogger.info(`Seeking: skipping seeking event to currentTime: ${playbackPosition}, internalSeek: ${this._isSeekingByPlatform}, localPlayerSeek: ${this._isSeekingByApplication}, state: ${lifecycle.state}`);
|
|
@@ -1547,16 +1561,16 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1547
1561
|
* */
|
|
1548
1562
|
async _atomicSeek() {
|
|
1549
1563
|
sdkLogger.info("Seeking: local video element seeking start while isPlaying: ", this._isPlaying);
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
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
|
+
}
|
|
1560
1574
|
|
|
1561
1575
|
// The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
|
|
1562
1576
|
if (this._videoElement) {
|
|
@@ -1623,7 +1637,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1623
1637
|
|
|
1624
1638
|
// If in TargetPlayingState.PAUSE, no need to resume.
|
|
1625
1639
|
// Resume without awaiting to avoid blocking the seek process anymore
|
|
1626
|
-
// 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.
|
|
1627
1641
|
if (!this._abortSeeking) {
|
|
1628
1642
|
if (this._targetSeekPlayingState === TargetPlayingState.PLAYING_UI) {
|
|
1629
1643
|
if (!this._isAudioSyncEnabled()) {
|
|
@@ -120,13 +120,27 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
|
|
|
120
120
|
},
|
|
121
121
|
"pause": () => {
|
|
122
122
|
this._resetPlayPromise();
|
|
123
|
-
this.
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
if (!this._pauseForDelayedSeek) {
|
|
124
|
+
this.remotePlayer.pause()
|
|
125
|
+
.catch(error => {
|
|
126
|
+
sdkLogger.error("Failed to pause remote player:", error);
|
|
127
|
+
this.handleSenzaError(error.code, error.message || "Unknown pause error");
|
|
128
|
+
});
|
|
129
|
+
lifecycle.moveToForeground();
|
|
130
|
+
} else {
|
|
131
|
+
// in case of background seek, pause was called on local player, calling play() on local player will wait until "playing" event arrives (or timesout)
|
|
132
|
+
sdkLogger.info("senzaShakaPlayer pause Callback calling play() on local player. playing event should follow");
|
|
133
|
+
this._pauseForDelayedSeek = false;
|
|
134
|
+
this.videoElement.play();
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
"seeked" : () => {
|
|
138
|
+
// In case of background seek - we need to pause the local player after "seeked" until remote player is "playing" to avoid difference between the players.
|
|
139
|
+
if (this.isInRemotePlayback) {
|
|
140
|
+
this._pauseForDelayedSeek = true;
|
|
141
|
+
sdkLogger.info("senzaShakaPlayer seeked Callback: pausing local player. play should follow");
|
|
142
|
+
this.videoElement.pause();
|
|
143
|
+
}
|
|
130
144
|
}
|
|
131
145
|
};
|
|
132
146
|
|
|
@@ -348,7 +362,7 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
|
|
|
348
362
|
this._minSuggestedPresentationDelay = uiSettings.minSuggestedPresentationDelay;
|
|
349
363
|
sdkLogger.info(`Using configured minSuggestedPresentationDelay: ${this._minSuggestedPresentationDelay}s`);
|
|
350
364
|
}
|
|
351
|
-
|
|
365
|
+
this._pauseForDelayedSeek = false;
|
|
352
366
|
// if video element is provided, add the listeres here. In this case ,there is no need to call attach.
|
|
353
367
|
if (videoElement) {
|
|
354
368
|
this._attach(videoElement);
|
|
@@ -135,6 +135,24 @@ export function isSubtitlesTranslationPattern(lang) {
|
|
|
135
135
|
return (lang?.toString() || "").startsWith("*:");
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
export function isAppVersionAboveOrEqual(appVersion, minAppVersion) {
|
|
139
|
+
const version = appVersion.replace(/-dev|-prod/g, "").replace(/^.*\//, "");
|
|
140
|
+
|
|
141
|
+
sdkLogger.info(`isAppVersionAboveOrEqual: Comparing appVersion: ${version} with minAppVersion: ${minAppVersion}`);
|
|
142
|
+
const v1Parts = version.split(".").map(num => parseInt(num, 10) || 0);
|
|
143
|
+
const v2Parts = minAppVersion.split(".").map(num => parseInt(num, 10) || 0);
|
|
144
|
+
|
|
145
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length);
|
|
146
|
+
for (let i = 0; i < maxLength; i++) {
|
|
147
|
+
const v1 = v1Parts[i] ?? 0; // default missing parts to 0
|
|
148
|
+
const v2 = v2Parts[i] ?? 0;
|
|
149
|
+
if (v1 > v2) return true; // `appVersion` is newer
|
|
150
|
+
if (v1 < v2) return false; // `appVersion` is older
|
|
151
|
+
}
|
|
152
|
+
return true; // equal.
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
|
|
138
156
|
// These StreamType constants are used as a mask for the client to play/stop the specific types
|
|
139
157
|
export const StreamType = Object.freeze({
|
|
140
158
|
NONE: 0,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as shaka from "shaka-player";
|
|
2
|
+
import shakaUI from "shaka-player/dist/shaka-player.ui.js";
|
|
2
3
|
|
|
3
4
|
// Define custom error category
|
|
4
5
|
shaka.util.Error.Category.SENZA_PLAYER_ERROR = 50;
|
|
@@ -6,7 +7,7 @@ shaka.util.Error.Code.SENZA_PLAYER_ERROR = 10500;
|
|
|
6
7
|
|
|
7
8
|
// Copy the shaka module and replace the Player class with SenzaShakaPlayer
|
|
8
9
|
// if we don't Copy the shaka module, the Player class will be replaced for all the other modules that import shaka
|
|
9
|
-
const senzaShaka = { ...shaka };
|
|
10
|
+
const senzaShaka = { ...shaka, ui: shakaUI.ui };
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
/**
|
package/src/interface/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "4.4.0";
|
|
1
|
+
export const version = "4.4.1-4ca75a1.0";
|