@apocaliss92/nodelink-js 0.1.17 → 0.1.20
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/README.md +41 -68
- package/dist/{DiagnosticsTools-6WEMO4L4.js → DiagnosticsTools-NUMCYEKQ.js} +2 -2
- package/dist/{chunk-ULSFEQSE.js → chunk-EHWVA3SG.js} +164 -17
- package/dist/chunk-EHWVA3SG.js.map +1 -0
- package/dist/{chunk-ZE7D7LI4.js → chunk-YPU7RAEY.js} +18 -12
- package/dist/{chunk-ZE7D7LI4.js.map → chunk-YPU7RAEY.js.map} +1 -1
- package/dist/cli/rtsp-server.cjs +177 -24
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +216 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +52 -6
- package/dist/index.d.ts +52 -6
- package/dist/index.js +41 -10
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-ULSFEQSE.js.map +0 -1
- /package/dist/{DiagnosticsTools-6WEMO4L4.js.map → DiagnosticsTools-NUMCYEKQ.js.map} +0 -0
package/dist/cli/rtsp-server.cjs
CHANGED
|
@@ -1196,6 +1196,22 @@ function detectVideoCodecFromNal(data) {
|
|
|
1196
1196
|
if (nalStart < 0 || nalStart >= data.length) return null;
|
|
1197
1197
|
const nalByte = data[nalStart];
|
|
1198
1198
|
if (nalByte === void 0) return null;
|
|
1199
|
+
const forbiddenBit264 = nalByte >> 7 & 1;
|
|
1200
|
+
const h264Type = nalByte & 31;
|
|
1201
|
+
if (forbiddenBit264 === 0 && h264Type > 0 && h264Type <= 12) {
|
|
1202
|
+
if (h264Type === 7 || h264Type === 8) {
|
|
1203
|
+
return "H264";
|
|
1204
|
+
}
|
|
1205
|
+
if (h264Type === 5) {
|
|
1206
|
+
return "H264";
|
|
1207
|
+
}
|
|
1208
|
+
if (h264Type === 1) {
|
|
1209
|
+
const nalRefIdc = nalByte >> 5 & 3;
|
|
1210
|
+
if (nalRefIdc >= 1) {
|
|
1211
|
+
return "H264";
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1199
1215
|
if (nalStart + 1 < data.length) {
|
|
1200
1216
|
const nalByte2 = data[nalStart + 1];
|
|
1201
1217
|
if (nalByte2 !== void 0) {
|
|
@@ -1209,22 +1225,12 @@ function detectVideoCodecFromNal(data) {
|
|
|
1209
1225
|
if (hevcType === 19 || hevcType === 20 || hevcType === 21) {
|
|
1210
1226
|
return "H265";
|
|
1211
1227
|
}
|
|
1212
|
-
if (hevcType <=
|
|
1228
|
+
if (hevcType <= 1 && nalByte <= 3) {
|
|
1213
1229
|
return "H265";
|
|
1214
1230
|
}
|
|
1215
1231
|
}
|
|
1216
1232
|
}
|
|
1217
1233
|
}
|
|
1218
|
-
const forbiddenBit264 = nalByte >> 7 & 1;
|
|
1219
|
-
const h264Type = nalByte & 31;
|
|
1220
|
-
if (forbiddenBit264 === 0 && h264Type > 0 && h264Type <= 12) {
|
|
1221
|
-
if (h264Type === 7 || h264Type === 8) {
|
|
1222
|
-
return "H264";
|
|
1223
|
-
}
|
|
1224
|
-
if (h264Type === 5 || h264Type === 1) {
|
|
1225
|
-
return "H264";
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
1234
|
return null;
|
|
1229
1235
|
}
|
|
1230
1236
|
var ANNEXB_START_CODE_4B, BcMediaAnnexBDecoder;
|
|
@@ -15652,7 +15658,8 @@ var logDebugStreamBlock = (params) => {
|
|
|
15652
15658
|
const bitRateText = getXmlText(raw, "bitRate") ?? getXmlText(raw, "BitRate");
|
|
15653
15659
|
const videoEncTypeText = getXmlText(raw, "videoEncType") ?? getXmlText(raw, "VideoEncType");
|
|
15654
15660
|
const audioText = getXmlText(raw, "audio") ?? getXmlText(raw, "Audio");
|
|
15655
|
-
const
|
|
15661
|
+
const enableCheckRaw = raw.replace(/<smartH265[\s\S]*?<\/smartH265>/g, "");
|
|
15662
|
+
const enableText = getXmlText(enableCheckRaw, "enable") ?? getXmlText(enableCheckRaw, "Enable");
|
|
15656
15663
|
const width = Number(widthText ?? "0");
|
|
15657
15664
|
const height = Number(heightText ?? "0");
|
|
15658
15665
|
const frameRate = Number(frameText ?? "0");
|
|
@@ -15676,7 +15683,8 @@ var buildStream = (params) => {
|
|
|
15676
15683
|
const frameRate = Number(getXmlText(streamXml, "frame") ?? "0");
|
|
15677
15684
|
const bitRate = Number(getXmlText(streamXml, "bitRate") ?? "0");
|
|
15678
15685
|
const audio = Number(getXmlText(streamXml, "audio") ?? "0");
|
|
15679
|
-
const
|
|
15686
|
+
const enableXml = streamXml.replace(/<smartH265[\s\S]*?<\/smartH265>/g, "");
|
|
15687
|
+
const enabled = getXmlText(enableXml, "enable");
|
|
15680
15688
|
const isEnabled = isEnabledFromText(enabled);
|
|
15681
15689
|
if (!isEnabled || !isPlausibleStream({ width, height, frameRate, bitRate }))
|
|
15682
15690
|
return void 0;
|
|
@@ -16365,14 +16373,16 @@ var DUAL_LENS_SINGLE_MOTION_MODELS = /* @__PURE__ */ new Set([
|
|
|
16365
16373
|
"Reolink TrackMix",
|
|
16366
16374
|
"Reolink TrackMix PoE",
|
|
16367
16375
|
"Reolink TrackMix WiFi",
|
|
16368
|
-
"RLC-81MA"
|
|
16376
|
+
"RLC-81MA",
|
|
16377
|
+
"TrackFlex Floodlight WiFi"
|
|
16369
16378
|
]);
|
|
16370
16379
|
var DUAL_LENS_MODELS = /* @__PURE__ */ new Set([
|
|
16371
16380
|
...DUAL_LENS_DUAL_MOTION_MODELS,
|
|
16372
16381
|
...DUAL_LENS_SINGLE_MOTION_MODELS
|
|
16373
16382
|
]);
|
|
16374
16383
|
var isDualLenseModel = (model) => {
|
|
16375
|
-
|
|
16384
|
+
const lower = model.toLowerCase();
|
|
16385
|
+
return Array.from(DUAL_LENS_MODELS).some((m) => m.toLowerCase() === lower) || lower.includes("trackmix") || lower.includes("trackflex");
|
|
16376
16386
|
};
|
|
16377
16387
|
var NVR_HUB_EXACT_TYPES = ["NVR", "WIFI_NVR", "HOMEHUB"];
|
|
16378
16388
|
var NVR_HUB_MODEL_PATTERNS = [
|
|
@@ -16456,6 +16466,14 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
16456
16466
|
* Set via setIsNvr() from the plugin or auto-detected via isNvrDevice().
|
|
16457
16467
|
*/
|
|
16458
16468
|
_isNvr;
|
|
16469
|
+
/**
|
|
16470
|
+
* Cached multi-focal detection result.
|
|
16471
|
+
* - true = dual-lens camera (e.g., TrackMix, TrackFlex) with multiple channels on a single device
|
|
16472
|
+
* - false = single-lens camera
|
|
16473
|
+
* Multi-focal cameras reject concurrent streaming TCP connections (response_code 430),
|
|
16474
|
+
* so all channels must multiplex on the same streaming socket.
|
|
16475
|
+
*/
|
|
16476
|
+
_isMultiFocal;
|
|
16459
16477
|
/** Maximum dedicated sessions allowed before triggering a reboot (default: 7). */
|
|
16460
16478
|
maxDedicatedSessionsBeforeReboot;
|
|
16461
16479
|
sessionGuardRebootInFlight;
|
|
@@ -16486,6 +16504,15 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
16486
16504
|
simpleEventResubscribeTimer;
|
|
16487
16505
|
simpleEventResubscribeInFlight;
|
|
16488
16506
|
simpleEventResubscribeIntervalMs = 5 * 6e4;
|
|
16507
|
+
// Event watchdog: auto-recovery when events stop flowing or subscription fails
|
|
16508
|
+
simpleEventWatchdogTimer;
|
|
16509
|
+
simpleEventLastReceivedAt = 0;
|
|
16510
|
+
simpleEventWatchdogRecoveryAttempts = 0;
|
|
16511
|
+
simpleEventWatchdogLastRecoveryAt = 0;
|
|
16512
|
+
simpleEventWatchdogIntervalMs = 1e4;
|
|
16513
|
+
// check every 10s
|
|
16514
|
+
simpleEventWatchdogSilenceThresholdMs = 5 * 6e4;
|
|
16515
|
+
// 5 min without events
|
|
16489
16516
|
statePollingInterval;
|
|
16490
16517
|
udpSleepInferenceInterval;
|
|
16491
16518
|
udpLastInferredSleepStateByChannel = /* @__PURE__ */ new Map();
|
|
@@ -16870,14 +16897,16 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
16870
16897
|
* Determine the socket tag for a given sessionKey.
|
|
16871
16898
|
* This implements the tag-based allocation strategy:
|
|
16872
16899
|
*
|
|
16873
|
-
* - "general" - commands, events
|
|
16874
|
-
* - "streaming:ch{N}" - main + sub for channel N (
|
|
16875
|
-
* - "streaming:ch{N}:ext" - ext for channel N (
|
|
16900
|
+
* - "general" - commands, events, ext on ch0
|
|
16901
|
+
* - "streaming:ch{N}" - main + sub for channel N (NVR/standalone single-lens)
|
|
16902
|
+
* - "streaming:ch{N}:ext" - ext for channel N (NVR/standalone, N>0)
|
|
16903
|
+
* - "streaming:a" - ch0 main + ch1 sub (multi-focal dedicated socket)
|
|
16904
|
+
* - "general" also carries ch1 main + ch0 sub for multi-focal (merged with commands/events)
|
|
16876
16905
|
* - "replay:deviceId:ch{N}" - dedicated per device+channel for replay
|
|
16877
16906
|
*
|
|
16878
|
-
*
|
|
16879
|
-
*
|
|
16880
|
-
*
|
|
16907
|
+
* Multi-focal cameras (TrackMix, TrackFlex, Duo) reject two main or two sub
|
|
16908
|
+
* streams on the same TCP connection (response_code 430). Cross-channel pairing
|
|
16909
|
+
* ensures each socket always has a valid M+S combination using only 2 TCP connections.
|
|
16881
16910
|
*
|
|
16882
16911
|
* @param sessionKey - The session key (e.g., "live:device:ch0:main", "replay:device:ch1:file")
|
|
16883
16912
|
* @returns The socket pool tag to use
|
|
@@ -16897,9 +16926,16 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
16897
16926
|
if (channel === 0) {
|
|
16898
16927
|
return "general";
|
|
16899
16928
|
}
|
|
16900
|
-
|
|
16929
|
+
if (this._isMultiFocal) {
|
|
16930
|
+
return "general";
|
|
16931
|
+
}
|
|
16932
|
+
return this._isNvr ? `streaming:ch${channel}:ext` : `streaming:ch${channel}:ext`;
|
|
16901
16933
|
}
|
|
16902
|
-
|
|
16934
|
+
if (this._isMultiFocal) {
|
|
16935
|
+
const isSocketA = channel === 0 && profile === "main" || channel === 1 && profile === "sub";
|
|
16936
|
+
return isSocketA ? "streaming:a" : "general";
|
|
16937
|
+
}
|
|
16938
|
+
return this._isNvr ? `streaming:ch${channel}` : `streaming:ch${channel}`;
|
|
16903
16939
|
}
|
|
16904
16940
|
return "general";
|
|
16905
16941
|
}
|
|
@@ -17224,6 +17260,14 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17224
17260
|
async cleanupDedicatedClients() {
|
|
17225
17261
|
}
|
|
17226
17262
|
dispatchSimpleEvent(evt) {
|
|
17263
|
+
this.simpleEventLastReceivedAt = Date.now();
|
|
17264
|
+
if (this.simpleEventWatchdogRecoveryAttempts > 0) {
|
|
17265
|
+
(this.logger.info ?? this.logger.log).call(
|
|
17266
|
+
this.logger,
|
|
17267
|
+
`[ReolinkBaichuanApi] event watchdog: events flowing again after ${this.simpleEventWatchdogRecoveryAttempts} recovery attempt(s)`
|
|
17268
|
+
);
|
|
17269
|
+
this.simpleEventWatchdogRecoveryAttempts = 0;
|
|
17270
|
+
}
|
|
17227
17271
|
const debugCfg = this.client.getDebugConfig?.();
|
|
17228
17272
|
if (debugCfg) {
|
|
17229
17273
|
const sid = this.client.getSocketSessionId?.();
|
|
@@ -17772,11 +17816,15 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17772
17816
|
/**
|
|
17773
17817
|
* Subscribe to minimal high-level events.
|
|
17774
17818
|
* The API manages Baichuan subscribe/unsubscribe automatically.
|
|
17819
|
+
* Includes built-in watchdog: if no events arrive for 5 minutes while
|
|
17820
|
+
* the connection is alive, the subscription is automatically renewed.
|
|
17775
17821
|
*/
|
|
17776
17822
|
async onSimpleEvent(callback) {
|
|
17777
17823
|
this.simpleEventListeners.add(callback);
|
|
17778
17824
|
await this.ensureSimpleEventSubscribed();
|
|
17825
|
+
this.simpleEventLastReceivedAt = Date.now();
|
|
17779
17826
|
this.startSimpleEventResubscribeTimer();
|
|
17827
|
+
this.startSimpleEventWatchdog();
|
|
17780
17828
|
}
|
|
17781
17829
|
/**
|
|
17782
17830
|
* Remove one callback, or all callbacks if omitted.
|
|
@@ -17790,6 +17838,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17790
17838
|
}
|
|
17791
17839
|
if (this.simpleEventListeners.size === 0) {
|
|
17792
17840
|
this.stopSimpleEventResubscribeTimer();
|
|
17841
|
+
this.stopSimpleEventWatchdog();
|
|
17793
17842
|
this.stopUdpSleepInference();
|
|
17794
17843
|
await this.ensureSimpleEventUnsubscribed();
|
|
17795
17844
|
} else {
|
|
@@ -17813,6 +17862,97 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17813
17862
|
clearInterval(this.simpleEventResubscribeTimer);
|
|
17814
17863
|
this.simpleEventResubscribeTimer = void 0;
|
|
17815
17864
|
}
|
|
17865
|
+
/**
|
|
17866
|
+
* Event watchdog: monitors whether events are flowing and auto-recovers if they stop.
|
|
17867
|
+
*
|
|
17868
|
+
* Handles two failure modes:
|
|
17869
|
+
* 1. Subscription flag is true but no events arrive for 5+ minutes (device dropped subscription silently)
|
|
17870
|
+
* 2. Subscription flag is false because initial/retry subscribe failed, but connection is now alive
|
|
17871
|
+
*
|
|
17872
|
+
* Uses exponential backoff (30s → 60s → 120s → 240s → max 5min) to avoid hammering the device.
|
|
17873
|
+
*/
|
|
17874
|
+
startSimpleEventWatchdog() {
|
|
17875
|
+
if (this.simpleEventWatchdogTimer) return;
|
|
17876
|
+
if (this.simpleEventListeners.size === 0) return;
|
|
17877
|
+
this.simpleEventWatchdogTimer = setInterval(() => {
|
|
17878
|
+
void this.simpleEventWatchdogTick();
|
|
17879
|
+
}, this.simpleEventWatchdogIntervalMs);
|
|
17880
|
+
}
|
|
17881
|
+
stopSimpleEventWatchdog() {
|
|
17882
|
+
if (!this.simpleEventWatchdogTimer) return;
|
|
17883
|
+
clearInterval(this.simpleEventWatchdogTimer);
|
|
17884
|
+
this.simpleEventWatchdogTimer = void 0;
|
|
17885
|
+
this.simpleEventWatchdogRecoveryAttempts = 0;
|
|
17886
|
+
this.simpleEventWatchdogLastRecoveryAt = 0;
|
|
17887
|
+
this.simpleEventLastReceivedAt = 0;
|
|
17888
|
+
}
|
|
17889
|
+
async simpleEventWatchdogTick() {
|
|
17890
|
+
if (this.simpleEventListeners.size === 0) return;
|
|
17891
|
+
if (!this.client.isSocketConnected?.() || !this.client.loggedIn) return;
|
|
17892
|
+
const now = Date.now();
|
|
17893
|
+
if (this.simpleEventSubscribed && this.simpleEventLastReceivedAt > 0) {
|
|
17894
|
+
const silence = now - this.simpleEventLastReceivedAt;
|
|
17895
|
+
if (silence < this.simpleEventWatchdogSilenceThresholdMs) return;
|
|
17896
|
+
(this.logger.warn ?? this.logger.log).call(
|
|
17897
|
+
this.logger,
|
|
17898
|
+
`[ReolinkBaichuanApi] event watchdog: no events for ${Math.round(silence / 6e4)} min, forcing resubscribe`,
|
|
17899
|
+
{ host: this.host, silenceMs: silence }
|
|
17900
|
+
);
|
|
17901
|
+
try {
|
|
17902
|
+
this.simpleEventSubscribed = false;
|
|
17903
|
+
this.client.subscribed = false;
|
|
17904
|
+
await this.ensureSimpleEventSubscribed();
|
|
17905
|
+
this.simpleEventLastReceivedAt = Date.now();
|
|
17906
|
+
this.simpleEventWatchdogRecoveryAttempts = 0;
|
|
17907
|
+
(this.logger.info ?? this.logger.log).call(
|
|
17908
|
+
this.logger,
|
|
17909
|
+
`[ReolinkBaichuanApi] event watchdog: resubscribed successfully after silence`
|
|
17910
|
+
);
|
|
17911
|
+
} catch (e) {
|
|
17912
|
+
(this.logger.debug ?? this.logger.log).call(
|
|
17913
|
+
this.logger,
|
|
17914
|
+
`[ReolinkBaichuanApi] event watchdog: resubscribe after silence failed`,
|
|
17915
|
+
formatErrorForLog(e)
|
|
17916
|
+
);
|
|
17917
|
+
}
|
|
17918
|
+
return;
|
|
17919
|
+
}
|
|
17920
|
+
if (!this.simpleEventSubscribed) {
|
|
17921
|
+
const backoffMs = Math.min(
|
|
17922
|
+
3e4 * Math.pow(2, this.simpleEventWatchdogRecoveryAttempts),
|
|
17923
|
+
this.simpleEventWatchdogSilenceThresholdMs
|
|
17924
|
+
);
|
|
17925
|
+
if (now - this.simpleEventWatchdogLastRecoveryAt < backoffMs) return;
|
|
17926
|
+
this.simpleEventWatchdogRecoveryAttempts++;
|
|
17927
|
+
this.simpleEventWatchdogLastRecoveryAt = now;
|
|
17928
|
+
const nextBackoff = Math.min(
|
|
17929
|
+
3e4 * Math.pow(2, this.simpleEventWatchdogRecoveryAttempts),
|
|
17930
|
+
this.simpleEventWatchdogSilenceThresholdMs
|
|
17931
|
+
);
|
|
17932
|
+
(this.logger.info ?? this.logger.log).call(
|
|
17933
|
+
this.logger,
|
|
17934
|
+
`[ReolinkBaichuanApi] event watchdog: subscription inactive, attempting auto-recovery (attempt #${this.simpleEventWatchdogRecoveryAttempts}, next backoff ${Math.round(nextBackoff / 1e3)}s)`,
|
|
17935
|
+
{ host: this.host }
|
|
17936
|
+
);
|
|
17937
|
+
try {
|
|
17938
|
+
await this.ensureSimpleEventSubscribed();
|
|
17939
|
+
if (this.simpleEventSubscribed) {
|
|
17940
|
+
this.simpleEventLastReceivedAt = Date.now();
|
|
17941
|
+
(this.logger.info ?? this.logger.log).call(
|
|
17942
|
+
this.logger,
|
|
17943
|
+
`[ReolinkBaichuanApi] event watchdog: auto-recovery successful after ${this.simpleEventWatchdogRecoveryAttempts} attempt(s)`
|
|
17944
|
+
);
|
|
17945
|
+
this.simpleEventWatchdogRecoveryAttempts = 0;
|
|
17946
|
+
}
|
|
17947
|
+
} catch (e) {
|
|
17948
|
+
(this.logger.debug ?? this.logger.log).call(
|
|
17949
|
+
this.logger,
|
|
17950
|
+
`[ReolinkBaichuanApi] event watchdog: recovery attempt #${this.simpleEventWatchdogRecoveryAttempts} failed`,
|
|
17951
|
+
formatErrorForLog(e)
|
|
17952
|
+
);
|
|
17953
|
+
}
|
|
17954
|
+
}
|
|
17955
|
+
}
|
|
17816
17956
|
async renewSimpleEventSubscription() {
|
|
17817
17957
|
if (this.simpleEventListeners.size === 0) return;
|
|
17818
17958
|
if (this.simpleEventResubscribeInFlight)
|
|
@@ -17951,6 +18091,17 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
17951
18091
|
this._isNvr = isNvr;
|
|
17952
18092
|
this.logger.debug?.(`[ReolinkBaichuanApi] setIsNvr: ${isNvr}`);
|
|
17953
18093
|
}
|
|
18094
|
+
/**
|
|
18095
|
+
* Set the multi-focal flag explicitly.
|
|
18096
|
+
* Call this early (before streaming) to ensure correct socket pooling.
|
|
18097
|
+
* Multi-focal cameras (TrackMix, TrackFlex, Duo, etc.) reject concurrent
|
|
18098
|
+
* streaming TCP connections, so all channels must share a single streaming socket.
|
|
18099
|
+
* @param isMultiFocal - true if this is a dual-lens/multi-focal camera
|
|
18100
|
+
*/
|
|
18101
|
+
setIsMultiFocal(isMultiFocal) {
|
|
18102
|
+
this._isMultiFocal = isMultiFocal;
|
|
18103
|
+
this.logger.debug?.(`[ReolinkBaichuanApi] setIsMultiFocal: ${isMultiFocal}`);
|
|
18104
|
+
}
|
|
17954
18105
|
/**
|
|
17955
18106
|
* Enable or disable idle disconnect dynamically.
|
|
17956
18107
|
*
|
|
@@ -18001,6 +18152,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18001
18152
|
}
|
|
18002
18153
|
this.stopStatePolling();
|
|
18003
18154
|
this.stopUdpSleepInference();
|
|
18155
|
+
this.stopSimpleEventWatchdog();
|
|
18156
|
+
this.stopSimpleEventResubscribeTimer();
|
|
18004
18157
|
await this.cleanup();
|
|
18005
18158
|
await this.stopAllActiveStreams();
|
|
18006
18159
|
await this.cleanupSocketPool();
|
|
@@ -23452,7 +23605,7 @@ ${xml}`
|
|
|
23452
23605
|
});
|
|
23453
23606
|
model = typeof info.type === "string" ? info.type.toLowerCase() : "";
|
|
23454
23607
|
isMultiFocal = isDualLenseModel(model);
|
|
23455
|
-
isTrackMix = model.includes("trackmix");
|
|
23608
|
+
isTrackMix = model.includes("trackmix") || model.includes("trackflex");
|
|
23456
23609
|
} catch (e) {
|
|
23457
23610
|
logDebug(
|
|
23458
23611
|
"[ReolinkBaichuanApi] buildVideoStreamOptions: getInfo(type) failed",
|