senza-sdk 4.5.9 → 4.6.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/implementation.bundle.js +1 -1
- package/package.json +1 -1
- package/src/implementation/api.js +7 -5
- package/src/implementation/displayManager.js +7 -0
- package/src/implementation/remotePlayer.js +84 -26
- package/src/implementation/senzaShakaPlayer.js +22 -4
- package/src/interface/overlay.js +0 -1
- package/src/interface/version.js +1 -1
package/package.json
CHANGED
|
@@ -60,7 +60,7 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
|
|
|
60
60
|
|
|
61
61
|
if (window.cefQuery) {
|
|
62
62
|
|
|
63
|
-
// First, check
|
|
63
|
+
// First, check compatibility with ui-streamer api version
|
|
64
64
|
await new Promise((resolve, reject) => {
|
|
65
65
|
window.cefQuery({
|
|
66
66
|
request: "apiVersion " + API_VERSION,
|
|
@@ -140,13 +140,15 @@ export async function init(interfaceApiVersion, showSequenceFunc, initSequenceFu
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
await Promise.all([
|
|
144
|
+
lifecycle._init(sessionInfoObj?.settings?.["ui-streamer"], triggerEvent),
|
|
145
|
+
remotePlayer._init(sessionInfoObj, triggerEvent),
|
|
146
|
+
displayManager._init()
|
|
147
|
+
]);
|
|
148
|
+
|
|
146
149
|
alarmManager._init();
|
|
147
150
|
messageManager._init();
|
|
148
151
|
backgroundRenderControl._init();
|
|
149
|
-
await displayManager._init();
|
|
150
152
|
sdkLogger.log("All submodules initialized");
|
|
151
153
|
|
|
152
154
|
|
|
@@ -4,6 +4,7 @@ import { parseEdid } from "./edidParser";
|
|
|
4
4
|
import { EventListenersManager } from "./eventListenersManager";
|
|
5
5
|
import { bus, Events } from "./eventBus";
|
|
6
6
|
import { lifecycle } from "./lifecycle";
|
|
7
|
+
import { sessionInfo } from "./SessionInfo";
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
export class DisplayManager extends DisplayManagerInterface {
|
|
@@ -38,6 +39,12 @@ export class DisplayManager extends DisplayManagerInterface {
|
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
async _init() {
|
|
42
|
+
const enableCec = sessionInfo?.sessionInfoObj?.settings?.client?.hdmi?.cec?.enable_cec;
|
|
43
|
+
if (enableCec === false) {
|
|
44
|
+
sdkLogger.log("CEC disabled via config (client.hdmi.cec.enable_cec=false), skipping DisplayManager init");
|
|
45
|
+
this._setDisplayProperties({ connection: this.DisplayConnectionStatus.UNKNOWN });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
41
48
|
sdkLogger.log("Initializing DisplayManager");
|
|
42
49
|
await this._updateDisplayInfo();
|
|
43
50
|
if (!this._isInitialized) {
|
|
@@ -29,6 +29,7 @@ const MULTI_SEEK_DELAY_MSEC = 250;
|
|
|
29
29
|
const LOAD_START_SYNC_DIFF = 1;
|
|
30
30
|
const INVALID_SET_PLAYABLE_URI_CODE = 99;
|
|
31
31
|
export const ONE_DAY_SECONDS = 86400;
|
|
32
|
+
export const VOD_INIT_SEEK_EPSILON_SEC = 1;
|
|
32
33
|
const BACKGROUND_LOAD_MIN_SDK_VERSION = "4.5.0";
|
|
33
34
|
|
|
34
35
|
// Default timeout for waiting for the remote player confirmation
|
|
@@ -145,6 +146,14 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
145
146
|
*/
|
|
146
147
|
this._isSeekingByPlatform = false;
|
|
147
148
|
|
|
149
|
+
/**
|
|
150
|
+
* Start position (seconds) from the most recent load(). Used to ignore the local player's
|
|
151
|
+
* first seeking event when VOD is loaded from a non-zero position (Shaka init seek).
|
|
152
|
+
* @type {number}
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
this._loadStartPosition = 0;
|
|
156
|
+
|
|
148
157
|
/**
|
|
149
158
|
* @type {boolean}
|
|
150
159
|
* @description field indicating if the remote player is playing abr or not. Should be changed when starting/stoping playback
|
|
@@ -225,14 +234,18 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
225
234
|
this._autoTune = false;
|
|
226
235
|
|
|
227
236
|
this._isPlaying = window?.sessionStorage?.getItem("senzaSdk_isPlaying") === "true";
|
|
237
|
+
this._textTrackVisibility = window?.sessionStorage?.getItem("senzaSdk_textTrackVisibility") === "true";
|
|
228
238
|
if (this._isPlaying) {
|
|
229
239
|
this._playbackPositionAnchor = getPlaybackInfo()?.playbackPosition || 0;
|
|
230
240
|
this._startPlaybackClock();
|
|
241
|
+
|
|
242
|
+
// UI release while playing: local player may load and seek; seed load start so that seek is treated as init.
|
|
243
|
+
this._loadStartPosition = this.currentTime;
|
|
231
244
|
} else {
|
|
232
245
|
this._stopPlaybackClock();
|
|
233
246
|
}
|
|
234
247
|
|
|
235
|
-
sdkLogger.info(`RemotePlayer initialized with load mode: ${this._loadMode}, senzaSdk_isPlaying: ${this._isPlaying}`);
|
|
248
|
+
sdkLogger.info(`RemotePlayer initialized with load mode: ${this._loadMode}, senzaSdk_isPlaying: ${this._isPlaying}, senzaSdk_textTrackVisibility: ${this._textTrackVisibility}`);
|
|
236
249
|
this._loadedUrl = playerState?.playbackUrl || "";
|
|
237
250
|
// Make sure that the event listeners are registered only once
|
|
238
251
|
if (!this._isInitialized) {
|
|
@@ -601,6 +614,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
601
614
|
this._isSeekingByPlatform = false;
|
|
602
615
|
this._isSeekingByApplication = false;
|
|
603
616
|
this._isPlayingEventPending = false;
|
|
617
|
+
this._loadStartPosition = 0;
|
|
604
618
|
this._stopPlaybackClock();
|
|
605
619
|
this._playbackPositionAnchor = 0;
|
|
606
620
|
}
|
|
@@ -949,6 +963,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
949
963
|
this._changePlayMode(false);
|
|
950
964
|
return new Promise((resolve, reject) => {
|
|
951
965
|
const playbackPosition = position || 0;
|
|
966
|
+
this._loadStartPosition = playbackPosition;
|
|
952
967
|
if (reset) {
|
|
953
968
|
// first, zero playback position
|
|
954
969
|
setPlaybackInfo({ playbackPosition: 0 });
|
|
@@ -1641,14 +1656,33 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1641
1656
|
// Convert to boolean in case apps pass 0/1 instead false/true.
|
|
1642
1657
|
const newVisibility = !!visible;
|
|
1643
1658
|
|
|
1659
|
+
this._textTrackVisibility = newVisibility;
|
|
1660
|
+
window?.sessionStorage?.setItem("senzaSdk_textTrackVisibility", String(newVisibility));
|
|
1661
|
+
|
|
1644
1662
|
if (oldVisibility === newVisibility) {
|
|
1645
1663
|
return;
|
|
1646
1664
|
}
|
|
1647
1665
|
|
|
1648
|
-
this.
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1666
|
+
if (this._remotePlayerApiVersion <= 2) {
|
|
1667
|
+
if (!this._textTrackVisibility) {
|
|
1668
|
+
// For older versions, we need to clear the selected subtitles track
|
|
1669
|
+
this._selectedSubtitlesTrack = "";
|
|
1670
|
+
}
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
if (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND) {
|
|
1675
|
+
if (!this._textTrackVisibility) {
|
|
1676
|
+
this._stop(StreamType.SUBTITLE).catch((error) => {
|
|
1677
|
+
sdkLogger.warn(`remotePlayer setTextTrackVisibility: stop subtitles failed with error ${error.message}.`);
|
|
1678
|
+
});
|
|
1679
|
+
} else {
|
|
1680
|
+
this._play(StreamType.SUBTITLE).catch((error) => {
|
|
1681
|
+
sdkLogger.warn(`remotePlayer setTextTrackVisibility: play subtitles failed with error ${error.message}.`);
|
|
1682
|
+
});
|
|
1683
|
+
}
|
|
1684
|
+
} else {
|
|
1685
|
+
sdkLogger.info("remotePlayer setTextTrackVisibility V3+: not updating remote subtitle stream outside background");
|
|
1652
1686
|
}
|
|
1653
1687
|
}
|
|
1654
1688
|
|
|
@@ -1919,16 +1953,24 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1919
1953
|
this._pendingSeekPosition = playbackPosition;
|
|
1920
1954
|
this._setPlaybackPositionAnchor(playbackPosition);
|
|
1921
1955
|
// If the local player is just initializing after loading the manifest, ignore the seeking event.
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1956
|
+
// Live: Shaka jumps from 0 to a UTC wall-clock time (> one day in seconds).
|
|
1957
|
+
// VOD: load(url, startPosition) causes Shaka to seek near that start position (within epsilon).
|
|
1958
|
+
if (this._videoElement) {
|
|
1959
|
+
const currentTime = this._videoElement.currentTime;
|
|
1960
|
+
const isLiveInitSeek = this._localPlayerLoadCurrentTime === 0 && currentTime > ONE_DAY_SECONDS;
|
|
1961
|
+
const isVodInitSeek = this._localPlayerLoadCurrentTime === 0 && this._loadStartPosition > 0 && Math.abs(currentTime - this._loadStartPosition) <= VOD_INIT_SEEK_EPSILON_SEC;
|
|
1962
|
+
sdkLogger.info(`Check for init seek: currentTime: ${currentTime}, loadStartPosition: ${this._loadStartPosition}, isLiveInitSeek: ${isLiveInitSeek}, isVodInitSeek: ${isVodInitSeek}`);
|
|
1963
|
+
if (isLiveInitSeek || isVodInitSeek) {
|
|
1964
|
+
sdkLogger.info(`Seeking ignored for video currentTime init: currentTime: ${currentTime}, loadStartPosition: ${this._loadStartPosition}`);
|
|
1965
|
+
this._localPlayerLoadCurrentTime = currentTime;
|
|
1966
|
+
// if the difference between the remote player and the video element current time is greater than the load start sync diff (default 1 second), seek to the remote player current time
|
|
1967
|
+
if (isLiveInitSeek && this._isPlaying && this.currentTime > 0 && Math.abs(currentTime - this.currentTime) > this._loadStartSyncDiff) {
|
|
1968
|
+
sdkLogger.info(`Remote player is playing, seeking from ${currentTime} to ${this.currentTime}`);
|
|
1969
|
+
this._isSeekingByPlatform = true;
|
|
1970
|
+
this._videoElement.currentTime = this.currentTime;
|
|
1971
|
+
}
|
|
1972
|
+
return;
|
|
1930
1973
|
}
|
|
1931
|
-
return;
|
|
1932
1974
|
}
|
|
1933
1975
|
}
|
|
1934
1976
|
const backgroundSeekSupported = isAppVersionAboveOrEqual(this._deviceAppVersion, backgroundSeekMinClientAppVersion);
|
|
@@ -1943,6 +1985,31 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1943
1985
|
}
|
|
1944
1986
|
}
|
|
1945
1987
|
|
|
1988
|
+
/**
|
|
1989
|
+
* Resolve the initial target playing state from current playback and lifecycle state.
|
|
1990
|
+
*
|
|
1991
|
+
* | _isPlaying | lifecycle.state | _inTransitionToForeground | Result |
|
|
1992
|
+
* |------------|-----------------------------|---------------------------|--------------|
|
|
1993
|
+
* | false | any | any | PAUSED |
|
|
1994
|
+
* | true | FOREGROUND | any | PLAYING_UI |
|
|
1995
|
+
* | true | BACKGROUND | true | PLAYING_UI |
|
|
1996
|
+
* | true | BACKGROUND | false | PLAYING_ABR |
|
|
1997
|
+
* | true | IN_TRANSITION_TO_BACKGROUND | any | PLAYING_ABR |
|
|
1998
|
+
*
|
|
1999
|
+
* @private
|
|
2000
|
+
* @returns {string} TargetPlayingState value
|
|
2001
|
+
*/
|
|
2002
|
+
_getInitialTargetPlayingState() {
|
|
2003
|
+
if (!this._isPlaying) {
|
|
2004
|
+
return TargetPlayingState.PAUSED;
|
|
2005
|
+
}
|
|
2006
|
+
if (!lifecycle._inTransitionToForeground
|
|
2007
|
+
&& (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
|
|
2008
|
+
return TargetPlayingState.PLAYING_ABR;
|
|
2009
|
+
}
|
|
2010
|
+
return TargetPlayingState.PLAYING_UI;
|
|
2011
|
+
}
|
|
2012
|
+
|
|
1946
2013
|
/** Performs the seek operation which comprises the following steps:
|
|
1947
2014
|
* 1) Possible pause if playing
|
|
1948
2015
|
* 2) Wait for any further changes in the currentTime
|
|
@@ -1955,16 +2022,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
1955
2022
|
* */
|
|
1956
2023
|
async _atomicSeek() {
|
|
1957
2024
|
sdkLogger.info("Seeking: local video element seeking start while isPlaying: ", this._isPlaying);
|
|
1958
|
-
|
|
1959
|
-
if (!(lifecycle._inTransitionToForeground || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_FOREGROUND) && (lifecycle.state === lifecycle.UiState.BACKGROUND || lifecycle.state === lifecycle.UiState.IN_TRANSITION_TO_BACKGROUND)) {
|
|
1960
|
-
sdkLogger.info("seek in background", this._isPlaying);
|
|
1961
|
-
this._targetSeekPlayingState = TargetPlayingState.PLAYING_ABR;
|
|
1962
|
-
} else {
|
|
1963
|
-
this._targetSeekPlayingState = TargetPlayingState.PLAYING_UI;
|
|
1964
|
-
}
|
|
1965
|
-
} else {
|
|
1966
|
-
this._targetSeekPlayingState = TargetPlayingState.PAUSED;
|
|
1967
|
-
}
|
|
2025
|
+
this._targetSeekPlayingState = this._getInitialTargetPlayingState();
|
|
1968
2026
|
|
|
1969
2027
|
// The platform could be currently syncing audio/video using playback rate. Reset when performing seek.
|
|
1970
2028
|
if (this._videoElement) {
|
|
@@ -2057,7 +2115,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
2057
2115
|
return Promise.resolve();
|
|
2058
2116
|
}
|
|
2059
2117
|
|
|
2060
|
-
this._targetSetAudioPlayingState = this.
|
|
2118
|
+
this._targetSetAudioPlayingState = this._getInitialTargetPlayingState();
|
|
2061
2119
|
sdkLogger.log(`remotePlayer _atomicSetAudioLanguage: prevAudioTrack=${prevSelectedAudioTrack} audioTrackId=${audioTrackId} isPlaying=${this._isPlaying} targetState=${this._targetSetAudioPlayingState}`);
|
|
2062
2120
|
|
|
2063
2121
|
this._abortSetAudioLanguage = false;
|
|
@@ -2124,7 +2182,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
2124
2182
|
return Promise.resolve();
|
|
2125
2183
|
}
|
|
2126
2184
|
|
|
2127
|
-
this._targetSetSubtitlePlayingState = this.
|
|
2185
|
+
this._targetSetSubtitlePlayingState = this._getInitialTargetPlayingState();
|
|
2128
2186
|
sdkLogger.log(`remotePlayer _atomicSetSubtitleLanguage: prevTextTrack=${prevSelectedTextTrack} textTrackId=${textTrackId} isPlaying=${this._isPlaying} targetState=${this._targetSetSubtitlePlayingState}`);
|
|
2129
2187
|
|
|
2130
2188
|
this._abortSetSubtitleLanguage = false;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SenzaShakaPlayer as SenzaShakaInterface, shaka } from "../interface/senzaShakaPlayer";
|
|
2
2
|
import * as shakaDebug from "shaka-player/dist/shaka-player.compiled.debug.js";
|
|
3
3
|
import { remotePlayer, lifecycle, getPlatformInfo } from "./api";
|
|
4
|
-
import { ONE_DAY_SECONDS } from "./remotePlayer.js";
|
|
4
|
+
import { ONE_DAY_SECONDS, VOD_INIT_SEEK_EPSILON_SEC } from "./remotePlayer.js";
|
|
5
5
|
import { sdkLogger, SwitchMode } from "./utils";
|
|
6
6
|
import moment from "moment";
|
|
7
7
|
import set from "lodash.set";
|
|
@@ -156,12 +156,19 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
|
|
|
156
156
|
_seekCounterAfterLoad = 0;
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
|
-
* When true, the next `seeked` is Shaka jumping to live-edge
|
|
159
|
+
* When true, the next `seeked` is Shaka jumping to live-edge / VOD startTime after load; skip background pause logic.
|
|
160
160
|
* @private
|
|
161
161
|
* @type {boolean}
|
|
162
162
|
*/
|
|
163
163
|
_ignoreNextSeekedForInit = false;
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Start position (seconds) from the most recent load(). Used to ignore Shaka's VOD init seeking/seeked.
|
|
167
|
+
* @private
|
|
168
|
+
* @type {number}
|
|
169
|
+
*/
|
|
170
|
+
_loadStartPosition = 0;
|
|
171
|
+
|
|
165
172
|
/**
|
|
166
173
|
* Whether the player should call lifecycle.moveToForeground on pause, remote ended/error, unload, and destroy.
|
|
167
174
|
* Only when auto-background is enabled (including streamer overrides) and the UI moved to background via the
|
|
@@ -185,8 +192,16 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
|
|
|
185
192
|
this._ignoreNextSeekedForInit = false;
|
|
186
193
|
},
|
|
187
194
|
"seeking": () => {
|
|
188
|
-
//
|
|
189
|
-
|
|
195
|
+
// Ignore the first seeking after load when it is Shaka's initialization jump:
|
|
196
|
+
// - Live: currentTime is a UTC wall-clock time (> one day in seconds)
|
|
197
|
+
// - VOD: currentTime is within epsilon of this._loadStartPosition
|
|
198
|
+
const isFirstSeekAfterLoadAtZero = this._seekCounterAfterLoad < 1 && this._loadStartedAtZero;
|
|
199
|
+
const isLiveInitSeek = isFirstSeekAfterLoadAtZero && this.videoElement.currentTime > ONE_DAY_SECONDS;
|
|
200
|
+
const isVodInitSeek = isFirstSeekAfterLoadAtZero
|
|
201
|
+
&& this._loadStartPosition > 0
|
|
202
|
+
&& Math.abs(this.videoElement.currentTime - this._loadStartPosition) <= VOD_INIT_SEEK_EPSILON_SEC;
|
|
203
|
+
if (isLiveInitSeek || isVodInitSeek) {
|
|
204
|
+
sdkLogger.info("senzaShakaPlayer seeking: ignoring next seeked for init");
|
|
190
205
|
this._ignoreNextSeekedForInit = true;
|
|
191
206
|
} else {
|
|
192
207
|
// Another seeking before seeked means this is no longer the load initialization jump.
|
|
@@ -820,6 +835,9 @@ export class SenzaShakaPlayer extends SenzaShakaInterface {
|
|
|
820
835
|
startTime = -minInitialLiveOffset;
|
|
821
836
|
}
|
|
822
837
|
|
|
838
|
+
// Used to ignore Shaka's init seeking/seeked when currentTime matches load start.
|
|
839
|
+
this._loadStartPosition = startTime ?? 0;
|
|
840
|
+
|
|
823
841
|
// This callback will be activated when the manifest is loaded. It will trigger load of the remote player.
|
|
824
842
|
// This will ensure that the remote player is loaded only after the manifest is loaded by local player.
|
|
825
843
|
const responseFilterCallback = async (type, response) => {
|
package/src/interface/overlay.js
CHANGED
|
@@ -10,7 +10,6 @@ import { noop } from "./utils.js";
|
|
|
10
10
|
/**
|
|
11
11
|
* Overlay is a singleton class that manages displaying UI frames as image overlays. The image can be displayed over background video playback.
|
|
12
12
|
* It can be used in any lifecycle state.
|
|
13
|
-
* @private
|
|
14
13
|
* @fires error
|
|
15
14
|
*/
|
|
16
15
|
class Overlay extends EventTarget {
|
package/src/interface/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "4.
|
|
1
|
+
export const version = "4.6.0";
|