senza-sdk 4.4.4-86e7d7a.0 → 4.4.5
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 +2 -0
- package/dist/implementation.bundle.js.LICENSE.txt +82 -0
- package/package.json +8 -7
- package/src/api.js +16 -2
- package/src/implementation/api.js +9 -5
- package/src/implementation/backgroundRenderControl.js +113 -0
- package/src/implementation/deviceManager.js +6 -177
- package/src/implementation/displayManager.js +181 -0
- package/src/implementation/edidParser.js +125 -0
- package/src/implementation/lifecycle.js +42 -17
- package/src/implementation/remotePlayer.js +5 -2
- package/src/interface/api.js +1 -0
- package/src/interface/deviceManager.js +0 -33
- package/src/interface/displayManager.js +126 -0
- package/src/interface/lifecycle.js +18 -1
- package/src/interface/version.js +1 -1
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { DisplayManager as DisplayManagerInterface } from "../interface/displayManager";
|
|
2
|
+
import { sdkLogger, getRestResponse } from "./utils";
|
|
3
|
+
import { parseEdid } from "./edidParser";
|
|
4
|
+
import { EventListenersManager } from "./eventListenersManager";
|
|
5
|
+
import { bus, Events } from "./eventBus";
|
|
6
|
+
import { lifecycle } from "./lifecycle";
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export class DisplayManager extends DisplayManagerInterface {
|
|
10
|
+
|
|
11
|
+
constructor() {
|
|
12
|
+
super();
|
|
13
|
+
/**
|
|
14
|
+
* Event listeners manager for the displayManager events
|
|
15
|
+
* @type {EventListenersManager}
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
this._eventManager = new EventListenersManager({
|
|
19
|
+
timeoutMs: 10000 // Default timeout of 10 seconds, can be overridden by _setDefaultTimeout
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @type {boolean}
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
this._isInitialized = false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async _updateDisplayInfo() {
|
|
30
|
+
try {
|
|
31
|
+
const displayInfoResponse = await getRestResponse("display-info");
|
|
32
|
+
const displayInfo = this._parseDisplayInfo(displayInfoResponse);
|
|
33
|
+
this._setDisplayProperties(displayInfo);
|
|
34
|
+
} catch (e) {
|
|
35
|
+
sdkLogger.error(e);
|
|
36
|
+
this._setDisplayProperties({ connection: this.DisplayConnectionStatus.UNKNOWN });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async _init() {
|
|
41
|
+
sdkLogger.log("Initializing DisplayManager");
|
|
42
|
+
await this._updateDisplayInfo();
|
|
43
|
+
if (!this._isInitialized) {
|
|
44
|
+
this._isInitialized = true;
|
|
45
|
+
this._addSenzaEventListeners();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @private Add event listeners for system events
|
|
51
|
+
*/
|
|
52
|
+
_addSenzaEventListeners() {
|
|
53
|
+
bus.addEventListener(Events.LifecycleForeground, () => {
|
|
54
|
+
this._moveToForegroundHasBeenCalled = true;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
typeof document !== "undefined" && document.addEventListener("hs/displayInfoChanged", async (event) => {
|
|
58
|
+
sdkLogger.info("Got hs/displayInfoChanged event with detail", JSON.stringify(event?.detail));
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const displayInfo = this._parseDisplayInfo(event.detail.displayInfo);
|
|
62
|
+
this._setDisplayProperties(displayInfo);
|
|
63
|
+
|
|
64
|
+
const timeBeforeCallbacks = Date.now();
|
|
65
|
+
|
|
66
|
+
// Dispatch event to application and allow a chance to move to foreground.
|
|
67
|
+
// If there are no callbacks or the application doesn't move to foreground, the UI will be disconnected.
|
|
68
|
+
await this._eventManager.dispatch("displayinfochanged", { displayInfo });
|
|
69
|
+
|
|
70
|
+
const callbackDuration = Date.now() - timeBeforeCallbacks;
|
|
71
|
+
sdkLogger.log(`All callbacks for displayInfoChanged are finished within ${callbackDuration}ms`);
|
|
72
|
+
} catch (error) {
|
|
73
|
+
sdkLogger.error("Failed to handle displayInfo update", error);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const isTriggering = lifecycle.triggerEvent.type === "displayInfoChanged" && lifecycle._triggerEventFcid && lifecycle._triggerEventFcid === event.detail.fcid;
|
|
77
|
+
if (isTriggering) {
|
|
78
|
+
if (!this._moveToForegroundHasBeenCalled && window.cefQuery) {
|
|
79
|
+
sdkLogger.log("Application is about to be disconnected since didn't move to foreground");
|
|
80
|
+
const message = { type: "disconnect" };
|
|
81
|
+
const request = { target: "TC", waitForResponse: false, message: JSON.stringify(message) };
|
|
82
|
+
window.cefQuery({
|
|
83
|
+
request: JSON.stringify(request),
|
|
84
|
+
persistent: false,
|
|
85
|
+
onSuccess: () => {
|
|
86
|
+
sdkLogger.log("disconnect request successfully sent");
|
|
87
|
+
},
|
|
88
|
+
onFailure: (code, msg) => {
|
|
89
|
+
sdkLogger.error(`disconnect request failed: ${code} ${msg}`);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Set the default timeout for display manager event listeners
|
|
99
|
+
* @param {number} timeout - Timeout in milliseconds for display manager event listeners
|
|
100
|
+
* @private
|
|
101
|
+
*/
|
|
102
|
+
_setDefaultTimeout(timeout) {
|
|
103
|
+
if (typeof timeout === "number" && timeout > 0) {
|
|
104
|
+
this._eventManager.timeoutMs = timeout;
|
|
105
|
+
sdkLogger.log(`displayManager event listener timeout set to ${timeout}ms`);
|
|
106
|
+
} else {
|
|
107
|
+
sdkLogger.warn(`Invalid timeout value: ${timeout}. Must be a positive number.`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
_setDisplayProperties(displayInfo) {
|
|
112
|
+
|
|
113
|
+
if (displayInfo.connection !== undefined) {
|
|
114
|
+
this.connection = displayInfo.connection;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// If the connection is disconnected/unknown, reset all the display properties
|
|
118
|
+
if (this.connection !== this.DisplayConnectionStatus.CONNECTED) {
|
|
119
|
+
this.selected = undefined;
|
|
120
|
+
this.resolution = undefined;
|
|
121
|
+
this.framerate = undefined;
|
|
122
|
+
this.security = undefined;
|
|
123
|
+
this.make = undefined;
|
|
124
|
+
this.model = undefined;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const [key, value] of Object.entries(displayInfo)) {
|
|
129
|
+
if (value !== undefined) {
|
|
130
|
+
this[key] = value;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
_parseDisplayInfo(displayInfoResponseStr) {
|
|
136
|
+
const displayInfoResponse = JSON.parse(displayInfoResponseStr);
|
|
137
|
+
|
|
138
|
+
const displayInfo = {
|
|
139
|
+
connection: displayInfoResponse?.connection,
|
|
140
|
+
selected: displayInfoResponse?.connectedDisplayInfo?.selected,
|
|
141
|
+
resolution: displayInfoResponse?.connectedDisplayInfo?.resolution,
|
|
142
|
+
framerate: displayInfoResponse?.connectedDisplayInfo?.framerate,
|
|
143
|
+
security: displayInfoResponse?.connectedDisplayInfo?.hdcpVersion
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
if (displayInfoResponse?.connectedDisplayInfo?.edid) {
|
|
148
|
+
try {
|
|
149
|
+
const { manufacturerName, modelName } = parseEdid(displayInfoResponse.connectedDisplayInfo.edid);
|
|
150
|
+
displayInfo.make = manufacturerName ? manufacturerName : "unknown";
|
|
151
|
+
displayInfo.model = modelName ? modelName : "unknown";
|
|
152
|
+
} catch (error) {
|
|
153
|
+
sdkLogger.error("Failed to parse EDID", error);
|
|
154
|
+
displayInfo.make = "unknown";
|
|
155
|
+
displayInfo.model = "unknown";
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return displayInfo;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
addEventListener(type, callback) {
|
|
162
|
+
this._eventManager.addEventListener(type, callback);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
removeEventListener(type, callback) {
|
|
166
|
+
this._eventManager.removeEventListener(type, callback);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* @module
|
|
173
|
+
* @type {DisplayManager}
|
|
174
|
+
* @example
|
|
175
|
+
* import { displayManager } from "senza-sdk";
|
|
176
|
+
* const connection = await displayManager.connection;
|
|
177
|
+
* console.info(connection);
|
|
178
|
+
*
|
|
179
|
+
* @return {DisplayManager} pointer to the DisplayManager singleton
|
|
180
|
+
*/
|
|
181
|
+
export const displayManager = new DisplayManager();
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
const MANUFACTURER_MAP = {
|
|
2
|
+
// Major PC & Display Brands
|
|
3
|
+
ACR: "Acer",
|
|
4
|
+
APP: "Apple",
|
|
5
|
+
AOC: "AOC",
|
|
6
|
+
AUO: "AU Optronics",
|
|
7
|
+
BNQ: "BenQ",
|
|
8
|
+
BOE: "BOE Technology",
|
|
9
|
+
CMN: "Innolux (Chimei Innolux)",
|
|
10
|
+
CMO: "Chi Mei Optoelectronics",
|
|
11
|
+
CPQ: "Compaq",
|
|
12
|
+
DELL: "Dell", // some EDIDs use DEL
|
|
13
|
+
DEL: "Dell",
|
|
14
|
+
EIZ: "EIZO",
|
|
15
|
+
FUS: "Fujitsu",
|
|
16
|
+
HPN: "HP",
|
|
17
|
+
HWP: "HP",
|
|
18
|
+
IVM: "Iiyama",
|
|
19
|
+
LEN: "Lenovo",
|
|
20
|
+
LPL: "LG Philips LCD",
|
|
21
|
+
LGE: "LG Electronics",
|
|
22
|
+
GSM: "LG Electronics (GoldStar)",
|
|
23
|
+
MEI: "Panasonic (Matsushita)",
|
|
24
|
+
NEC: "NEC",
|
|
25
|
+
NVD: "Nvidia",
|
|
26
|
+
PHL: "Philips",
|
|
27
|
+
PHI: "Philips",
|
|
28
|
+
SAM: "Samsung",
|
|
29
|
+
SNY: "Sony",
|
|
30
|
+
TOS: "Toshiba",
|
|
31
|
+
VSC: "ViewSonic",
|
|
32
|
+
VIZ: "Vizio",
|
|
33
|
+
|
|
34
|
+
// Panel Makers / ODMs
|
|
35
|
+
CPT: "Chunghwa Picture Tubes",
|
|
36
|
+
HSD: "HannStar Display",
|
|
37
|
+
QDS: "Quanta Display",
|
|
38
|
+
SEC: "Samsung Electronics Display",
|
|
39
|
+
SHP: "Sharp",
|
|
40
|
+
TMX: "Tianma",
|
|
41
|
+
CMI: "Chi Mei Innolux",
|
|
42
|
+
|
|
43
|
+
// Other common consumer brands
|
|
44
|
+
JVC: "JVC (Victor Company of Japan)",
|
|
45
|
+
PIO: "Pioneer",
|
|
46
|
+
FUN: "Funai",
|
|
47
|
+
GRN: "Grundig",
|
|
48
|
+
MAG: "MAG Innovision",
|
|
49
|
+
TCL: "TCL",
|
|
50
|
+
HAI: "Haier",
|
|
51
|
+
HIT: "Hitachi",
|
|
52
|
+
SMC: "Sumitomo",
|
|
53
|
+
WAC: "Wacom",
|
|
54
|
+
ASUS: "ASUSTek", // often ASU
|
|
55
|
+
ASU: "ASUS",
|
|
56
|
+
GIG: "Gigabyte",
|
|
57
|
+
MSI: "Micro-Star International",
|
|
58
|
+
|
|
59
|
+
// Catch-alls / misc seen in monitors
|
|
60
|
+
ENC: "Energizer",
|
|
61
|
+
ZOW: "Zowie",
|
|
62
|
+
TTX: "Tatung",
|
|
63
|
+
KDS: "KDS",
|
|
64
|
+
HIS: "HIS",
|
|
65
|
+
XXX: "TCL",
|
|
66
|
+
XMD: "Xiaomi"
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// --- helpers ---
|
|
70
|
+
function hexToBytes(hex) {
|
|
71
|
+
const clean = hex.replace(/[^0-9a-fA-F]/g, "");
|
|
72
|
+
if (clean.length % 2 !== 0) throw new Error("Hex length must be even");
|
|
73
|
+
const out = new Uint8Array(clean.length / 2);
|
|
74
|
+
for (let i = 0; i < clean.length; i += 2) {
|
|
75
|
+
out[i / 2] = parseInt(clean.slice(i, i + 2), 16);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function decodeEisaId(word) {
|
|
81
|
+
const a = (word >> 10 & 0x1f) + 64;
|
|
82
|
+
const b = (word >> 5 & 0x1f) + 64;
|
|
83
|
+
const c = (word & 0x1f) + 64;
|
|
84
|
+
return [a, b, c].map(n => n >= 65 && n <= 90 ? String.fromCharCode(n) : "?").join("");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function sanitizeEdidString(bytes) {
|
|
88
|
+
let out = "";
|
|
89
|
+
for (const b of bytes) {
|
|
90
|
+
if (b === 0x00 || b === 0x0a) break;
|
|
91
|
+
if (b >= 0x20 && b <= 0x7e) out += String.fromCharCode(b);
|
|
92
|
+
}
|
|
93
|
+
return out.trim();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
export function parseEdid(input) {
|
|
98
|
+
const edid = input instanceof Uint8Array ? input : hexToBytes(input);
|
|
99
|
+
|
|
100
|
+
// Manufacturer ID (bytes 8–9)
|
|
101
|
+
const mfgWord = edid[8] << 8 | edid[9];
|
|
102
|
+
const manufacturerId = decodeEisaId(mfgWord);
|
|
103
|
+
const manufacturerName = MANUFACTURER_MAP[manufacturerId] || manufacturerId;
|
|
104
|
+
|
|
105
|
+
// Product code (bytes 10–11)
|
|
106
|
+
const productCode = (edid[10] | edid[11] << 8) >>> 0;
|
|
107
|
+
const modelNumber = String(productCode);
|
|
108
|
+
|
|
109
|
+
// Monitor name descriptor (0xFC) and serial number descriptor (0xFF) in base block
|
|
110
|
+
let modelName;
|
|
111
|
+
let serialNumber;
|
|
112
|
+
for (let off = 0x36; off <= 0x7e - 18; off += 18) {
|
|
113
|
+
const d = edid.slice(off, off + 18);
|
|
114
|
+
if (d[0] === 0x00 && d[1] === 0x00 && d[2] === 0x00) {
|
|
115
|
+
if (d[3] === 0xFC) {
|
|
116
|
+
modelName = sanitizeEdidString(d.slice(5, 18));
|
|
117
|
+
} else if (d[3] === 0xFF) {
|
|
118
|
+
serialNumber = sanitizeEdidString(d.slice(5, 18));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return { manufacturerName, modelName, modelNumber, serialNumber };
|
|
124
|
+
}
|
|
125
|
+
|
|
@@ -62,6 +62,12 @@ class Lifecycle extends LifecycleInterface {
|
|
|
62
62
|
this._autoBackgroundOnUIDelay = DEFAULT_AUTO_BACKGROUND_UI_DELAY;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// List of event types that should use the _eventManager
|
|
66
|
+
static _waitForListenersEvents = [
|
|
67
|
+
"userdisconnected","beforestatechange"
|
|
68
|
+
// Add more event types here if needed in the future
|
|
69
|
+
];
|
|
70
|
+
|
|
65
71
|
/**
|
|
66
72
|
* @private Initialize the lifecycle
|
|
67
73
|
* @param {Object} uiStreamerSettings - UI-streamer portion of the settings taken from session info
|
|
@@ -177,7 +183,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
177
183
|
const event = new Event("onstatechange");
|
|
178
184
|
event.state = e.detail;
|
|
179
185
|
this._state = event.state;
|
|
180
|
-
if (this._isAutoBackgroundEnabled() && this.
|
|
186
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
|
|
181
187
|
this._startCountdown();
|
|
182
188
|
}
|
|
183
189
|
this.dispatchEvent(event);
|
|
@@ -213,7 +219,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
213
219
|
|
|
214
220
|
// Add playModeChange listener
|
|
215
221
|
remotePlayer.addEventListener("playModeChange", (event) => {
|
|
216
|
-
if (this._isAutoBackgroundEnabled() && this.
|
|
222
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
|
|
217
223
|
sdkLogger.log("Resetting auto background timer due to play mode change", event.detail.isPlaying);
|
|
218
224
|
this._startCountdown(event.detail.isPlaying);
|
|
219
225
|
}
|
|
@@ -221,6 +227,14 @@ class Lifecycle extends LifecycleInterface {
|
|
|
221
227
|
sdkLogger.log("[Lifecycle] Added event listeners for system events");
|
|
222
228
|
}
|
|
223
229
|
|
|
230
|
+
/**
|
|
231
|
+
* @private Checks if the UI is in foreground or transitioning to foreground.
|
|
232
|
+
* @returns {boolean}
|
|
233
|
+
*/
|
|
234
|
+
_isForegroundOrTransitioning() {
|
|
235
|
+
return this._state === this.UiState.FOREGROUND || this._state === this.UiState.IN_TRANSITION_TO_FOREGROUND;
|
|
236
|
+
}
|
|
237
|
+
|
|
224
238
|
/**
|
|
225
239
|
* @private Checks if auto background is enabled including overrides.
|
|
226
240
|
* @returns {boolean}
|
|
@@ -284,7 +298,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
284
298
|
}
|
|
285
299
|
|
|
286
300
|
// Start or stop countdown based on new configuration
|
|
287
|
-
if (this._isAutoBackgroundEnabled()) {
|
|
301
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
|
|
288
302
|
this._startCountdown();
|
|
289
303
|
} else {
|
|
290
304
|
this._stopCountdown();
|
|
@@ -364,7 +378,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
364
378
|
|
|
365
379
|
set autoBackground(enabled) {
|
|
366
380
|
this._autoBackground = enabled;
|
|
367
|
-
if (this._isAutoBackgroundEnabled()) {
|
|
381
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning()) {
|
|
368
382
|
this._startCountdown();
|
|
369
383
|
} else {
|
|
370
384
|
this._stopCountdown();
|
|
@@ -377,7 +391,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
377
391
|
|
|
378
392
|
set autoBackgroundDelay(delay) {
|
|
379
393
|
this._autoBackgroundOnVideoDelay = delay;
|
|
380
|
-
if (this._isAutoBackgroundEnabled() && remotePlayer._isPlaying) {
|
|
394
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning() && remotePlayer._isPlaying) {
|
|
381
395
|
this._startCountdown();
|
|
382
396
|
}
|
|
383
397
|
}
|
|
@@ -388,7 +402,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
388
402
|
|
|
389
403
|
set autoBackgroundOnUIDelay(delay) {
|
|
390
404
|
this._autoBackgroundOnUIDelay = delay;
|
|
391
|
-
if (this._isAutoBackgroundEnabled() && !remotePlayer._isPlaying) {
|
|
405
|
+
if (this._isAutoBackgroundEnabled() && this._isForegroundOrTransitioning() && !remotePlayer._isPlaying) {
|
|
392
406
|
this._startCountdown();
|
|
393
407
|
}
|
|
394
408
|
}
|
|
@@ -408,9 +422,24 @@ class Lifecycle extends LifecycleInterface {
|
|
|
408
422
|
// Only start countdown if delay is positive
|
|
409
423
|
if (timeoutDelay > 0) {
|
|
410
424
|
sdkLogger.debug("Starting countdown timeout", timeoutDelay, isPlaying ? "(video)" : "(UI)");
|
|
411
|
-
this._countdown = setTimeout(() => {
|
|
425
|
+
this._countdown = setTimeout(async () => {
|
|
412
426
|
sdkLogger.debug("Countdown timeout reached, moving to background");
|
|
413
|
-
|
|
427
|
+
|
|
428
|
+
// Create the event with cancelable: true
|
|
429
|
+
const event = new Event("beforestatechange", { cancelable: true });
|
|
430
|
+
event.state = this.UiState.BACKGROUND;
|
|
431
|
+
// Use the event manager to dispatch the event and wait for all listeners
|
|
432
|
+
await this._eventManager.dispatch("beforestatechange", event);
|
|
433
|
+
// Check if any listener called preventDefault()
|
|
434
|
+
if (event.defaultPrevented) {
|
|
435
|
+
sdkLogger.info("moveToBackground was prevented by a listener");
|
|
436
|
+
// Restart the countdown since the move was cancelled
|
|
437
|
+
if (this._isForegroundOrTransitioning()) {
|
|
438
|
+
this._startCountdown();
|
|
439
|
+
}
|
|
440
|
+
} else {
|
|
441
|
+
this.moveToBackground();
|
|
442
|
+
}
|
|
414
443
|
}, timeoutDelay * 1000);
|
|
415
444
|
} else {
|
|
416
445
|
sdkLogger.debug("Countdown not started - delay is negative or zero");
|
|
@@ -424,8 +453,8 @@ class Lifecycle extends LifecycleInterface {
|
|
|
424
453
|
if (this._countdown) {
|
|
425
454
|
sdkLogger.debug("Stopping countdown timer");
|
|
426
455
|
clearTimeout(this._countdown);
|
|
456
|
+
this._countdown = null;
|
|
427
457
|
}
|
|
428
|
-
this._countdown = null;
|
|
429
458
|
}
|
|
430
459
|
|
|
431
460
|
/**
|
|
@@ -459,7 +488,7 @@ class Lifecycle extends LifecycleInterface {
|
|
|
459
488
|
moveToForeground() {
|
|
460
489
|
if (window.cefQuery) {
|
|
461
490
|
const inTransition = this._isInTransition();
|
|
462
|
-
if (inTransition || this.
|
|
491
|
+
if (inTransition || this._isForegroundOrTransitioning()) {
|
|
463
492
|
sdkLogger.warn(`lifecycle moveToForeground: No need to transition to foreground, state: ${this._state} transition: ${inTransition}`);
|
|
464
493
|
return Promise.resolve(false);
|
|
465
494
|
}
|
|
@@ -771,23 +800,19 @@ class Lifecycle extends LifecycleInterface {
|
|
|
771
800
|
return Promise.reject("disconnect is not supported if NOT running e2e");
|
|
772
801
|
}
|
|
773
802
|
|
|
803
|
+
|
|
774
804
|
addEventListener(type, listener, options) {
|
|
775
|
-
if (type
|
|
776
|
-
// Use the event manager for userdisconnected events
|
|
805
|
+
if (Lifecycle._waitForListenersEvents.includes(type)) {
|
|
777
806
|
this._eventManager.addEventListener(type, listener);
|
|
778
807
|
} else {
|
|
779
|
-
// For all other event types, use the parent class implementation
|
|
780
808
|
super.addEventListener(type, listener, options);
|
|
781
809
|
}
|
|
782
810
|
}
|
|
783
811
|
|
|
784
|
-
|
|
785
812
|
removeEventListener(type, listener, options) {
|
|
786
|
-
if (type
|
|
787
|
-
// Use the event manager for userdisconnected events
|
|
813
|
+
if (Lifecycle._waitForListenersEvents.includes(type)) {
|
|
788
814
|
this._eventManager.removeEventListener(type, listener);
|
|
789
815
|
} else {
|
|
790
|
-
// For all other event types, use the parent class implementation
|
|
791
816
|
super.removeEventListener(type, listener, options);
|
|
792
817
|
}
|
|
793
818
|
}
|
|
@@ -246,6 +246,7 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
246
246
|
|
|
247
247
|
typeof document !== "undefined" && document.addEventListener("hs/EOS", () => {
|
|
248
248
|
sdkLogger.info("Got hs/EOS event");
|
|
249
|
+
this._changePlayMode(false);
|
|
249
250
|
this.dispatchEvent(new Event("ended"));
|
|
250
251
|
});
|
|
251
252
|
|
|
@@ -790,10 +791,12 @@ class RemotePlayer extends RemotePlayerInterface {
|
|
|
790
791
|
fcid: FCID
|
|
791
792
|
};
|
|
792
793
|
if (this._uiAvSyncIntervalMs !== undefined) {
|
|
793
|
-
message.
|
|
794
|
+
message.connectorSettings = message.connectorSettings || {};
|
|
795
|
+
message.connectorSettings.uiAvSyncIntervalMs = this._uiAvSyncIntervalMs;
|
|
794
796
|
}
|
|
795
797
|
if (this._abrAvSyncIntervalMs !== undefined) {
|
|
796
|
-
message.
|
|
798
|
+
message.connectorSettings = message.connectorSettings || {};
|
|
799
|
+
message.connectorSettings.abrAvSyncIntervalMs = this._abrAvSyncIntervalMs;
|
|
797
800
|
}
|
|
798
801
|
if (this._remotePlayerApiVersion >= 2) {
|
|
799
802
|
message.type = "remotePlayer.load";
|
package/src/interface/api.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Lifecycle } from "./lifecycle.js";
|
|
2
2
|
export { DeviceManager } from "./deviceManager.js";
|
|
3
|
+
export { DisplayManager } from "./displayManager.js";
|
|
3
4
|
export { PlatformManager } from "./platformManager.js";
|
|
4
5
|
export { AlarmManager } from "./alarmManager.js";
|
|
5
6
|
export { MessageManager } from "./messageManager.js";
|
|
@@ -1,32 +1,9 @@
|
|
|
1
1
|
import { sdkLogger, noop } from "./utils.js";
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* @event DeviceManager#hdmistatuschanged
|
|
5
|
-
* @description Fired when the HDMI connection status changes.<br>
|
|
6
|
-
* @property {HdmiStatus} hdmiStatus - the status of the HDMI connection.
|
|
7
|
-
* @example
|
|
8
|
-
* deviceManager.addEventListener("hdmistatuschanged", (e) => {
|
|
9
|
-
* console.info("HDMI status changed:", e.detail.hdmiStatus);
|
|
10
|
-
* });
|
|
11
|
-
* @private
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
3
|
/**
|
|
15
4
|
* DeviceManager is a singleton class that manages the device.<br>
|
|
16
|
-
* @fires hdmistatuschanged
|
|
17
5
|
*/
|
|
18
6
|
export class DeviceManager extends EventTarget {
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {Object} HdmiStatus - The HDMI status of the device
|
|
21
|
-
* @property {string} ACTIVE - The device is connected to the active HDMI source.
|
|
22
|
-
* @property {string} INACTIVE - The active HDMI source is different than the one connected to the device or the TV is in standby.
|
|
23
|
-
* @property {string} UNKNOWN - The status is unknown. This can happen if the TV does not support HDMI status detection.
|
|
24
|
-
*/
|
|
25
|
-
HdmiStatus = Object.freeze({
|
|
26
|
-
ACTIVE: "active",
|
|
27
|
-
INACTIVE: "inactive",
|
|
28
|
-
UNKNOWN: "unknown"
|
|
29
|
-
});
|
|
30
7
|
|
|
31
8
|
/**
|
|
32
9
|
* @property {object} DeviceInfo
|
|
@@ -54,16 +31,6 @@ export class DeviceManager extends EventTarget {
|
|
|
54
31
|
|
|
55
32
|
}
|
|
56
33
|
|
|
57
|
-
/**
|
|
58
|
-
* Get the current HDMI status from the connector
|
|
59
|
-
* @return {Promise<HdmiStatus>} Promise which is resolved when getHdmiStatus has been successfully performed
|
|
60
|
-
* Failure to getHdmiStatus for any reason, results in the promise being rejected.
|
|
61
|
-
* @private
|
|
62
|
-
*/
|
|
63
|
-
getHdmiStatus() {
|
|
64
|
-
return noop("DeviceManager.getHdmiStatus");
|
|
65
|
-
}
|
|
66
|
-
|
|
67
34
|
/**
|
|
68
35
|
* Reboot the device
|
|
69
36
|
* @return {Promise} Promise which is resolved when the reboot command has been successfully processed.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @event DisplayManager#displayinfochanged
|
|
3
|
+
* @description Fired when the display information changes.
|
|
4
|
+
* @property {DisplayInfoChanged} [detail.displayInfo] - the updated display information.
|
|
5
|
+
* @example
|
|
6
|
+
* @private
|
|
7
|
+
* displayManager.addEventListener("displayinfochanged", (e) => {
|
|
8
|
+
* console.info("Display information changed:", e.detail.displayInfo);
|
|
9
|
+
* });
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} DisplayInfoChanged
|
|
14
|
+
* @property {DisplayConnectionStatus | undefined} connection Display connection status.
|
|
15
|
+
* @property {DisplaySelectedStatus | undefined} selected Display selected status.
|
|
16
|
+
* @property {DisplayResolution | undefined} resolution Display resolution information.
|
|
17
|
+
* @property {number | undefined} framerate Display frame rate.
|
|
18
|
+
* @property {string | undefined} security Display HDCP / security level.
|
|
19
|
+
* @property {string | undefined} make Display vendor (make) information.
|
|
20
|
+
* @property {string | undefined} model Display model information.
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* DisplayManager exposes information about the connected display / sink device.
|
|
26
|
+
* It allows querying several negotiated HDMI parameters and (for a subset) receiving change notifications.
|
|
27
|
+
* @private
|
|
28
|
+
* @fires displayinfochanged
|
|
29
|
+
*/
|
|
30
|
+
export class DisplayManager extends EventTarget {
|
|
31
|
+
/**
|
|
32
|
+
* @typedef {Object} DisplayConnectionStatus
|
|
33
|
+
* @property {string} CONNECTED Display is connected and communication established.
|
|
34
|
+
* @property {string} DISCONNECTED Display cable removed / not detected.
|
|
35
|
+
* @property {string} UNKNOWN The platform cannot determine (e.g. unsupported).
|
|
36
|
+
* @private
|
|
37
|
+
*/
|
|
38
|
+
DisplayConnectionStatus = Object.freeze({
|
|
39
|
+
CONNECTED: "connected",
|
|
40
|
+
DISCONNECTED: "disconnected",
|
|
41
|
+
UNKNOWN: "unknown"
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @typedef {Object} DisplaySelectedStatus
|
|
46
|
+
* @property {string} ACTIVE The display input where the device is connected is currently active.
|
|
47
|
+
* @property {string} INACTIVE The device is connected but another source is active / TV in standby.
|
|
48
|
+
* @property {string} UNKNOWN The platform cannot determine (e.g. unsupported).
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
DisplaySelectedStatus = Object.freeze({
|
|
52
|
+
ACTIVE: "active",
|
|
53
|
+
INACTIVE: "inactive",
|
|
54
|
+
UNKNOWN: "unknown"
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {Object} DisplayResolution
|
|
59
|
+
* @property {number} width Negotiated active horizontal pixels
|
|
60
|
+
* @property {number} height Negotiated active vertical pixels
|
|
61
|
+
* @property {boolean} interlace True if the negotiated mode is interlaced
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Display connection status.
|
|
67
|
+
* @property {DisplayConnectionStatus} connection
|
|
68
|
+
*/
|
|
69
|
+
connection = this.DisplayConnectionStatus.DISCONNECTED;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Whether the display input this device is connected to is currently active.
|
|
73
|
+
* undefined when the display is not connected.
|
|
74
|
+
* @property {DisplaySelectedStatus} selected
|
|
75
|
+
*/
|
|
76
|
+
selected;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Negotiated resolution information.
|
|
80
|
+
* undefined when the display is not connected.
|
|
81
|
+
* @property {DisplayResolution} resolution
|
|
82
|
+
*/
|
|
83
|
+
resolution;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Negotiated frame rate (frames per second).
|
|
87
|
+
* undefined when the display is not connected.
|
|
88
|
+
* @property {number} framerate
|
|
89
|
+
*/
|
|
90
|
+
framerate;
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Negotiated HDCP / security level.
|
|
94
|
+
* undefined when the display is not connected.
|
|
95
|
+
* @property {string} security
|
|
96
|
+
*/
|
|
97
|
+
security;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Vendor (make) information parsed from EDID.
|
|
101
|
+
* undefined when the display is not connected.
|
|
102
|
+
* @property {string} make
|
|
103
|
+
*/
|
|
104
|
+
make;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Model information parsed from EDID.
|
|
108
|
+
* undefined when the display is not connected.
|
|
109
|
+
* @property {string} model
|
|
110
|
+
*/
|
|
111
|
+
model;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @module
|
|
117
|
+
* @type {DisplayManager}
|
|
118
|
+
* @private
|
|
119
|
+
* @example
|
|
120
|
+
* import { displayManager } from "senza-sdk";
|
|
121
|
+
* const connection = await displayManager.connection;
|
|
122
|
+
* console.info(connection);
|
|
123
|
+
*
|
|
124
|
+
* @return {DisplayManager} pointer to the DisplayManager singleton
|
|
125
|
+
*/
|
|
126
|
+
"needed for the module doc comment to be recognized";
|