@apocaliss92/nodelink-js 0.4.7 → 0.4.10
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/{DiagnosticsTools-UMN4C7SY.js → DiagnosticsTools-RNIDFEJK.js} +2 -2
- package/dist/{chunk-TR3V5FTO.js → chunk-EDLMKBG2.js} +226 -3
- package/dist/chunk-EDLMKBG2.js.map +1 -0
- package/dist/{chunk-GKLOJJ34.js → chunk-HGQ53FB3.js} +1023 -341
- package/dist/chunk-HGQ53FB3.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +1172 -312
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +1407 -329
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +989 -6
- package/dist/index.d.ts +1050 -5
- package/dist/index.js +232 -19
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-GKLOJJ34.js.map +0 -1
- package/dist/chunk-TR3V5FTO.js.map +0 -1
- /package/dist/{DiagnosticsTools-UMN4C7SY.js.map → DiagnosticsTools-RNIDFEJK.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -34,7 +34,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
34
34
|
function bcHeaderHasPayloadOffset(messageClass) {
|
|
35
35
|
return messageClass === BC_CLASS_MODERN_24 || messageClass === BC_CLASS_MODERN_24_ALT || messageClass === BC_CLASS_FILE_DOWNLOAD;
|
|
36
36
|
}
|
|
37
|
-
var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGIN, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_COVER_PREVIEW, BC_CMD_ID_COVER_STANDALONE_458, BC_CMD_ID_COVER_STANDALONE_459, BC_CMD_ID_COVER_STANDALONE_460, BC_CMD_ID_COVER_STANDALONE_461, BC_CMD_ID_COVER_STANDALONE_462, BC_CMD_ID_COVER_RESPONSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_ALARM_EVENT_LIST, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST, BC_CMD_ID_DING_DONG_CTRL, BC_CMD_ID_GET_DING_DONG_LIST, BC_CMD_ID_DING_DONG_OPT, BC_CMD_ID_GET_DING_DONG_CFG, BC_CMD_ID_SET_DING_DONG_CFG, BC_CMD_ID_QUICK_REPLY_PLAY, BC_CMD_ID_GET_DING_DONG_SILENT, BC_CMD_ID_SET_DING_DONG_SILENT;
|
|
37
|
+
var BC_TCP_DEFAULT_PORT, BC_MAGIC, BC_MAGIC_REV, BC_XML_KEY, BC_AES_IV, BC_CLASS_LEGACY, BC_CLASS_MODERN_20, BC_CLASS_MODERN_24, BC_CLASS_MODERN_24_ALT, BC_CLASS_FILE_DOWNLOAD, BC_CMD_ID_LOGIN, BC_CMD_ID_LOGOUT, BC_CMD_ID_VIDEO, BC_CMD_ID_VIDEO_STOP, BC_CMD_ID_FILE_INFO_LIST_REPLAY, BC_CMD_ID_FILE_INFO_LIST_STOP, BC_CMD_ID_FILE_INFO_LIST_DL_VIDEO, BC_CMD_ID_FILE_INFO_LIST_DOWNLOAD, BC_CMD_ID_FILE_INFO_LIST_OPEN, BC_CMD_ID_FILE_INFO_LIST_GET, BC_CMD_ID_FILE_INFO_LIST_CLOSE, BC_CMD_ID_FIND_REC_VIDEO_OPEN, BC_CMD_ID_FIND_REC_VIDEO_GET, BC_CMD_ID_FIND_REC_VIDEO_CLOSE, BC_CMD_ID_COVER_PREVIEW, BC_CMD_ID_COVER_STANDALONE_458, BC_CMD_ID_COVER_STANDALONE_459, BC_CMD_ID_COVER_STANDALONE_460, BC_CMD_ID_COVER_STANDALONE_461, BC_CMD_ID_COVER_STANDALONE_462, BC_CMD_ID_COVER_RESPONSE, BC_CMD_ID_TALK_ABILITY, BC_CMD_ID_TALK_RESET, BC_CMD_ID_TALK_CONFIG, BC_CMD_ID_TALK, BC_CMD_ID_PTZ_CONTROL, BC_CMD_ID_PTZ_CONTROL_PRESET, BC_CMD_ID_GET_PTZ_PRESET, BC_CMD_ID_GET_PTZ_POSITION, BC_CMD_ID_GET_ZOOM_FOCUS, BC_CMD_ID_SET_ZOOM_FOCUS, BC_CMD_ID_GET_BATTERY_INFO_LIST, BC_CMD_ID_GET_BATTERY_INFO, BC_CMD_ID_UDP_KEEP_ALIVE, BC_CMD_ID_GET_PIR_INFO, BC_CMD_ID_SET_PIR_INFO, BC_CMD_ID_GET_MOTION_ALARM, BC_CMD_ID_SET_MOTION_ALARM, BC_CMD_ID_ALARM_EVENT_LIST, BC_CMD_ID_GET_AI_ALARM, BC_CMD_ID_SET_AI_ALARM, BC_CMD_ID_GET_AUDIO_ALARM, BC_CMD_ID_AUDIO_ALARM_PLAY, BC_CMD_ID_GET_WHITE_LED, BC_CMD_ID_SET_WHITE_LED_STATE, BC_CMD_ID_SET_WHITE_LED_TASK, BC_CMD_ID_FLOODLIGHT_STATUS_LIST, BC_CMD_ID_ABILITY_INFO, BC_CMD_ID_SUPPORT, BC_CMD_ID_PING, BC_CMD_ID_CHANNEL_INFO_ALL, BC_CMD_ID_GET_OSD_DATETIME, BC_CMD_ID_GET_RECORD_CFG, BC_CMD_ID_GET_ABILITY_SUPPORT, BC_CMD_ID_GET_FTP_TASK, BC_CMD_ID_GET_RECORD, BC_CMD_ID_GET_HDD_INFO_LIST, BC_CMD_ID_GET_WIFI_SIGNAL, BC_CMD_ID_GET_WIFI, BC_CMD_ID_GET_ONLINE_USER_LIST, BC_CMD_ID_GET_DAY_RECORDS, BC_CMD_ID_GET_STREAM_INFO_LIST, BC_CMD_ID_GET_LED_STATE, BC_CMD_ID_GET_EMAIL_TASK, BC_CMD_ID_GET_AUDIO_TASK, BC_CMD_ID_GET_AUDIO_CFG, BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_TIMELAPSE_CFG, BC_CMD_ID_GET_AI_DENOISE, BC_CMD_ID_GET_KIT_AP_CFG, BC_CMD_ID_GET_REC_ENC_CFG, BC_CMD_ID_GET_ACCESS_USER_LIST, BC_CMD_ID_GET_SLEEP_STATE, BC_CMD_ID_GET_VIDEO_INPUT, BC_CMD_ID_GET_SYSTEM_GENERAL, BC_CMD_ID_GET_SUPPORT, BC_CMD_ID_GET_AI_CFG, BC_CMD_ID_SET_AI_CFG, BC_CMD_ID_GET_SIREN_STATUS, BC_CMD_ID_SET_AUDIO_TASK, BC_CMD_ID_SET_VIDEO_INPUT, BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD, BC_CMD_ID_GET_ENC, BC_CMD_ID_SET_ENC, BC_CMD_ID_GET_PRIVACY_MASK, BC_CMD_ID_SET_PRIVACY_MASK, BC_CMD_ID_SET_AI_DENOISE, BC_CMD_ID_SET_LED_STATE, BC_CMD_ID_SET_AUDIO_CFG, BC_CMD_ID_SET_RECORD, BC_CMD_ID_SET_RECORD_CFG, BC_CMD_ID_SET_EMAIL_TASK, BC_CMD_ID_GET_PUSH_TASK, BC_CMD_ID_SET_PUSH_TASK, BC_CMD_ID_GET_AUTO_FOCUS, BC_CMD_ID_SET_AUTO_FOCUS, BC_CMD_ID_CMD_123, BC_CMD_ID_CMD_209, BC_CMD_ID_CMD_265, BC_CMD_ID_CMD_440, BC_CMD_ID_PUSH_VIDEO_INPUT, BC_CMD_ID_PUSH_SERIAL, BC_CMD_ID_PUSH_NET_INFO, BC_CMD_ID_PUSH_DINGDONG_LIST, BC_CMD_ID_PUSH_SLEEP_STATUS, BC_CMD_ID_PUSH_COORDINATE_POINT_LIST, BC_CMD_ID_DING_DONG_CTRL, BC_CMD_ID_GET_DING_DONG_LIST, BC_CMD_ID_DING_DONG_OPT, BC_CMD_ID_GET_DING_DONG_CFG, BC_CMD_ID_SET_DING_DONG_CFG, BC_CMD_ID_QUICK_REPLY_PLAY, BC_CMD_ID_GET_DING_DONG_SILENT, BC_CMD_ID_SET_DING_DONG_SILENT;
|
|
38
38
|
var init_constants = __esm({
|
|
39
39
|
"src/protocol/constants.ts"() {
|
|
40
40
|
"use strict";
|
|
@@ -137,6 +137,22 @@ var init_constants = __esm({
|
|
|
137
137
|
BC_CMD_ID_SET_AI_CFG = 300;
|
|
138
138
|
BC_CMD_ID_GET_SIREN_STATUS = 547;
|
|
139
139
|
BC_CMD_ID_SET_AUDIO_TASK = 231;
|
|
140
|
+
BC_CMD_ID_SET_VIDEO_INPUT = 25;
|
|
141
|
+
BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD = 297;
|
|
142
|
+
BC_CMD_ID_GET_ENC = 56;
|
|
143
|
+
BC_CMD_ID_SET_ENC = 57;
|
|
144
|
+
BC_CMD_ID_GET_PRIVACY_MASK = 52;
|
|
145
|
+
BC_CMD_ID_SET_PRIVACY_MASK = 53;
|
|
146
|
+
BC_CMD_ID_SET_AI_DENOISE = 440;
|
|
147
|
+
BC_CMD_ID_SET_LED_STATE = 209;
|
|
148
|
+
BC_CMD_ID_SET_AUDIO_CFG = 265;
|
|
149
|
+
BC_CMD_ID_SET_RECORD = 82;
|
|
150
|
+
BC_CMD_ID_SET_RECORD_CFG = 55;
|
|
151
|
+
BC_CMD_ID_SET_EMAIL_TASK = 216;
|
|
152
|
+
BC_CMD_ID_GET_PUSH_TASK = 219;
|
|
153
|
+
BC_CMD_ID_SET_PUSH_TASK = 218;
|
|
154
|
+
BC_CMD_ID_GET_AUTO_FOCUS = 224;
|
|
155
|
+
BC_CMD_ID_SET_AUTO_FOCUS = 225;
|
|
140
156
|
BC_CMD_ID_CMD_123 = 123;
|
|
141
157
|
BC_CMD_ID_CMD_209 = 209;
|
|
142
158
|
BC_CMD_ID_CMD_265 = 265;
|
|
@@ -452,6 +468,59 @@ function buildFloodlightManualXml(channelId, status, durationSeconds = 180) {
|
|
|
452
468
|
function buildWhiteLedStateXml(channelId, state) {
|
|
453
469
|
return buildFloodlightManualXml(channelId, state ? 1 : 0);
|
|
454
470
|
}
|
|
471
|
+
function ensureXmlHeader(xml) {
|
|
472
|
+
const trimmed = xml.trimStart();
|
|
473
|
+
if (trimmed.startsWith("<?xml")) return xml;
|
|
474
|
+
return `${XML_HEADER}
|
|
475
|
+
${xml}`;
|
|
476
|
+
}
|
|
477
|
+
function applyXmlTagPatch(xml, tag, value) {
|
|
478
|
+
if (value === void 0) return xml;
|
|
479
|
+
const v = typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
480
|
+
const re = new RegExp(`<${tag}>[^<]*</${tag}>`);
|
|
481
|
+
return xml.replace(re, `<${tag}>${v}</${tag}>`);
|
|
482
|
+
}
|
|
483
|
+
function patchNestedTag(xml, parent, child, value) {
|
|
484
|
+
if (value === void 0) return xml;
|
|
485
|
+
const v = typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
486
|
+
const re = new RegExp(
|
|
487
|
+
`(<${parent}[^>]*>[\\s\\S]*?<${child}>)[^<]*(</${child}>[\\s\\S]*?</${parent}>)`
|
|
488
|
+
);
|
|
489
|
+
return xml.replace(re, `$1${v}$2`);
|
|
490
|
+
}
|
|
491
|
+
function applyStreamPatch(xml, streamTag, patch) {
|
|
492
|
+
if (!patch) return xml;
|
|
493
|
+
const re = new RegExp(
|
|
494
|
+
`(<${streamTag}[^>]*>)([\\s\\S]*?)(</${streamTag}>)`
|
|
495
|
+
);
|
|
496
|
+
return xml.replace(re, (_match, open, body, close) => {
|
|
497
|
+
let next = body;
|
|
498
|
+
if (patch.bitRate !== void 0) {
|
|
499
|
+
next = applyXmlTagPatch(next, "bitRate", patch.bitRate);
|
|
500
|
+
}
|
|
501
|
+
if (patch.frameRate !== void 0) {
|
|
502
|
+
next = applyXmlTagPatch(next, "frameRate", patch.frameRate);
|
|
503
|
+
next = applyXmlTagPatch(next, "frame", patch.frameRate);
|
|
504
|
+
}
|
|
505
|
+
if (patch.videoEncType !== void 0) {
|
|
506
|
+
const intVal = patch.videoEncType === "h265" ? 1 : 0;
|
|
507
|
+
next = applyXmlTagPatch(next, "videoEncType", intVal);
|
|
508
|
+
}
|
|
509
|
+
return `${open}${next}${close}`;
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function normalizeDayNightMode(input) {
|
|
513
|
+
const stripped = String(input).replace(/&/g, "And");
|
|
514
|
+
if (!stripped) return stripped;
|
|
515
|
+
const first = stripped[0];
|
|
516
|
+
if (first === void 0) return stripped;
|
|
517
|
+
return first.toLowerCase() + stripped.slice(1);
|
|
518
|
+
}
|
|
519
|
+
function normalizeOpenClose(input) {
|
|
520
|
+
const v = String(input).toLowerCase();
|
|
521
|
+
if (v === "on" || v === "open" || v === "1" || v === "true") return "open";
|
|
522
|
+
return "close";
|
|
523
|
+
}
|
|
455
524
|
function buildAbilityInfoExtensionXml(username) {
|
|
456
525
|
return `<?xml version="1.0" encoding="UTF-8" ?>
|
|
457
526
|
<Extension version="1.1">
|
|
@@ -459,9 +528,11 @@ function buildAbilityInfoExtensionXml(username) {
|
|
|
459
528
|
<token>system, streaming, PTZ, IO, security, replay, disk, network, alarm, record, video, image</token>
|
|
460
529
|
</Extension>`;
|
|
461
530
|
}
|
|
531
|
+
var XML_HEADER;
|
|
462
532
|
var init_xml = __esm({
|
|
463
533
|
"src/protocol/xml.ts"() {
|
|
464
534
|
"use strict";
|
|
535
|
+
XML_HEADER = `<?xml version="1.0" encoding="UTF-8" ?>`;
|
|
465
536
|
}
|
|
466
537
|
});
|
|
467
538
|
|
|
@@ -2462,7 +2533,7 @@ var init_BaichuanVideoStream = __esm({
|
|
|
2462
2533
|
const allowMsgNum0Fallback = this.acceptAnyStreamType && frame.header.msgNum === 0;
|
|
2463
2534
|
if (!allowMsgNum0Fallback) {
|
|
2464
2535
|
const frameCount = this._msgNumMismatchCount = (this._msgNumMismatchCount || 0) + 1;
|
|
2465
|
-
if (frameCount <= 5) {
|
|
2536
|
+
if (frameCount <= 5 && this.client.getDebugConfig().general) {
|
|
2466
2537
|
this.logger?.log(
|
|
2467
2538
|
`[BaichuanVideoStream] Frame msgNum mismatch: received=${frame.header.msgNum}, expected=${this.activeMsgNum}, channel=${this.channel}, profile=${this.profile}, variant=${this.variant} (frame discarded)`
|
|
2468
2539
|
);
|
|
@@ -2472,7 +2543,7 @@ var init_BaichuanVideoStream = __esm({
|
|
|
2472
2543
|
}
|
|
2473
2544
|
if (!this.acceptAnyStreamType && !this.expectedStreamTypes.has(frame.header.streamType)) {
|
|
2474
2545
|
const frameCount = this._streamTypeMismatchCount = (this._streamTypeMismatchCount || 0) + 1;
|
|
2475
|
-
if (frameCount <= 5) {
|
|
2546
|
+
if (frameCount <= 5 && this.client.getDebugConfig().general) {
|
|
2476
2547
|
this.logger?.log(
|
|
2477
2548
|
`[BaichuanVideoStream] Frame streamType mismatch: received=${frame.header.streamType}, expectedAny=[${[
|
|
2478
2549
|
...this.expectedStreamTypes
|
|
@@ -6409,6 +6480,137 @@ var init_ReolinkCgiApi = __esm({
|
|
|
6409
6480
|
const param = channel == null ? {} : { channel };
|
|
6410
6481
|
return await this.call("GetPtzPreset", param, 1);
|
|
6411
6482
|
}
|
|
6483
|
+
// ── Isp / Image (colour, flip, day-night, exposure) ──────────────
|
|
6484
|
+
async GetIsp(channel) {
|
|
6485
|
+
const param = channel == null ? {} : { channel };
|
|
6486
|
+
return await this.call("GetIsp", param, 1);
|
|
6487
|
+
}
|
|
6488
|
+
async SetIsp(isp) {
|
|
6489
|
+
return await this.call("SetIsp", isp, 0);
|
|
6490
|
+
}
|
|
6491
|
+
async GetImage(channel) {
|
|
6492
|
+
const param = channel == null ? {} : { channel };
|
|
6493
|
+
return await this.call("GetImage", param, 1);
|
|
6494
|
+
}
|
|
6495
|
+
async SetImage(image) {
|
|
6496
|
+
return await this.call("SetImage", image, 0);
|
|
6497
|
+
}
|
|
6498
|
+
// ── AudioCfg (mute / volume) ─────────────────────────────────────
|
|
6499
|
+
async GetAudioCfg(channel) {
|
|
6500
|
+
const param = channel == null ? {} : { channel };
|
|
6501
|
+
return await this.call("GetAudioCfg", param, 1);
|
|
6502
|
+
}
|
|
6503
|
+
async SetAudioCfg(audio) {
|
|
6504
|
+
return await this.call("SetAudioCfg", audio, 0);
|
|
6505
|
+
}
|
|
6506
|
+
// ── Enc setter (Get already exists above) ────────────────────────
|
|
6507
|
+
async SetEnc(enc) {
|
|
6508
|
+
return await this.call("SetEnc", enc, 0);
|
|
6509
|
+
}
|
|
6510
|
+
// ── MdAlarm (motion detection sensitivity / regions) ─────────────
|
|
6511
|
+
async GetMdAlarm(channel) {
|
|
6512
|
+
const param = channel == null ? {} : { channel };
|
|
6513
|
+
return await this.call("GetMdAlarm", param, 1);
|
|
6514
|
+
}
|
|
6515
|
+
async SetMdAlarm(md) {
|
|
6516
|
+
return await this.call("SetMdAlarm", md, 0);
|
|
6517
|
+
}
|
|
6518
|
+
// ── IrLights ─────────────────────────────────────────────────────
|
|
6519
|
+
async GetIrLights(channel) {
|
|
6520
|
+
const param = channel == null ? {} : { channel };
|
|
6521
|
+
return await this.call("GetIrLights", param, 1);
|
|
6522
|
+
}
|
|
6523
|
+
async SetIrLights(ir) {
|
|
6524
|
+
return await this.call("SetIrLights", ir, 0);
|
|
6525
|
+
}
|
|
6526
|
+
// ── AiCfg (smart-detection enable + class filter) ────────────────
|
|
6527
|
+
async GetAiCfg(channel) {
|
|
6528
|
+
const param = channel == null ? {} : { channel };
|
|
6529
|
+
return await this.call("GetAiCfg", param, 1);
|
|
6530
|
+
}
|
|
6531
|
+
async SetAiCfg(ai) {
|
|
6532
|
+
return await this.call("SetAiCfg", ai, 0);
|
|
6533
|
+
}
|
|
6534
|
+
// ── Mask (privacy-mask zones) ────────────────────────────────────
|
|
6535
|
+
async GetMask(channel) {
|
|
6536
|
+
return await this.call("GetMask", { channel }, 1);
|
|
6537
|
+
}
|
|
6538
|
+
async SetMask(mask) {
|
|
6539
|
+
return await this.call("SetMask", mask, 0);
|
|
6540
|
+
}
|
|
6541
|
+
// ── AudioNoise (input noise reduction) ───────────────────────────
|
|
6542
|
+
async GetAudioNoise(channel) {
|
|
6543
|
+
return await this.call("GetAudioNoise", { channel }, 1);
|
|
6544
|
+
}
|
|
6545
|
+
async SetAudioNoise(noise) {
|
|
6546
|
+
return await this.call("SetAudioNoise", noise, 0);
|
|
6547
|
+
}
|
|
6548
|
+
// ── Rec / RecV20 (recording schedule) ────────────────────────────
|
|
6549
|
+
async GetRec(channel) {
|
|
6550
|
+
return await this.call("GetRec", { channel }, 1);
|
|
6551
|
+
}
|
|
6552
|
+
async SetRec(rec) {
|
|
6553
|
+
return await this.call("SetRec", rec, 0);
|
|
6554
|
+
}
|
|
6555
|
+
/** Newer firmwares advertise `GetRecV20` / `SetRecV20` with the
|
|
6556
|
+
* weekly-schedule `table` field. Same payload shape as `Rec`. */
|
|
6557
|
+
async GetRecV20(channel) {
|
|
6558
|
+
return await this.call("GetRecV20", { channel }, 1);
|
|
6559
|
+
}
|
|
6560
|
+
async SetRecV20(rec) {
|
|
6561
|
+
return await this.call("SetRecV20", rec, 0);
|
|
6562
|
+
}
|
|
6563
|
+
// ── Email (SMTP alert) ───────────────────────────────────────────
|
|
6564
|
+
async GetEmail(channel) {
|
|
6565
|
+
return await this.call("GetEmail", { channel }, 1);
|
|
6566
|
+
}
|
|
6567
|
+
async SetEmail(email) {
|
|
6568
|
+
return await this.call("SetEmail", email, 0);
|
|
6569
|
+
}
|
|
6570
|
+
/** V20 variant on newer firmwares with weekly-schedule `table`. */
|
|
6571
|
+
async GetEmailV20(channel) {
|
|
6572
|
+
return await this.call("GetEmailV20", { channel }, 1);
|
|
6573
|
+
}
|
|
6574
|
+
async SetEmailV20(email) {
|
|
6575
|
+
return await this.call("SetEmailV20", email, 0);
|
|
6576
|
+
}
|
|
6577
|
+
// ── Push (Reolink-cloud push notifications) ──────────────────────
|
|
6578
|
+
async GetPush(channel) {
|
|
6579
|
+
return await this.call("GetPush", { channel }, 1);
|
|
6580
|
+
}
|
|
6581
|
+
async SetPush(push) {
|
|
6582
|
+
return await this.call("SetPush", push, 0);
|
|
6583
|
+
}
|
|
6584
|
+
async GetPushV20(channel) {
|
|
6585
|
+
return await this.call("GetPushV20", { channel }, 1);
|
|
6586
|
+
}
|
|
6587
|
+
async SetPushV20(push) {
|
|
6588
|
+
return await this.call("SetPushV20", push, 0);
|
|
6589
|
+
}
|
|
6590
|
+
// ── AudioAlarm (siren-on-event) ──────────────────────────────────
|
|
6591
|
+
async GetAudioAlarm(channel) {
|
|
6592
|
+
return await this.call("GetAudioAlarm", { channel }, 1);
|
|
6593
|
+
}
|
|
6594
|
+
async SetAudioAlarm(audio) {
|
|
6595
|
+
return await this.call("SetAudioAlarm", audio, 0);
|
|
6596
|
+
}
|
|
6597
|
+
async SetAudioAlarmV20(audio) {
|
|
6598
|
+
return await this.call("SetAudioAlarmV20", audio, 0);
|
|
6599
|
+
}
|
|
6600
|
+
// ── AutoFocus (PTZ AF) ───────────────────────────────────────────
|
|
6601
|
+
async GetAutoFocus(channel) {
|
|
6602
|
+
return await this.call("GetAutoFocus", { channel }, 1);
|
|
6603
|
+
}
|
|
6604
|
+
async SetAutoFocus(af) {
|
|
6605
|
+
return await this.call("SetAutoFocus", af, 0);
|
|
6606
|
+
}
|
|
6607
|
+
// ── AiAlarm (per-class smart-detection thresholds) ───────────────
|
|
6608
|
+
async GetAiAlarm(channel, aiType) {
|
|
6609
|
+
return await this.call("GetAiAlarm", { channel, ai_type: aiType }, 1);
|
|
6610
|
+
}
|
|
6611
|
+
async SetAiAlarm(ai) {
|
|
6612
|
+
return await this.call("SetAiAlarm", ai, 0);
|
|
6613
|
+
}
|
|
6412
6614
|
async GetAudioAlarmV20(channel) {
|
|
6413
6615
|
const param = channel == null ? {} : { channel };
|
|
6414
6616
|
return await this.call("GetAudioAlarmV20", param, 0);
|
|
@@ -7807,6 +8009,7 @@ __export(index_exports, {
|
|
|
7807
8009
|
BC_CMD_ID_GET_AUDIO_ALARM: () => BC_CMD_ID_GET_AUDIO_ALARM,
|
|
7808
8010
|
BC_CMD_ID_GET_AUDIO_CFG: () => BC_CMD_ID_GET_AUDIO_CFG,
|
|
7809
8011
|
BC_CMD_ID_GET_AUDIO_TASK: () => BC_CMD_ID_GET_AUDIO_TASK,
|
|
8012
|
+
BC_CMD_ID_GET_AUTO_FOCUS: () => BC_CMD_ID_GET_AUTO_FOCUS,
|
|
7810
8013
|
BC_CMD_ID_GET_BATTERY_INFO: () => BC_CMD_ID_GET_BATTERY_INFO,
|
|
7811
8014
|
BC_CMD_ID_GET_BATTERY_INFO_LIST: () => BC_CMD_ID_GET_BATTERY_INFO_LIST,
|
|
7812
8015
|
BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD: () => BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
|
|
@@ -7815,6 +8018,7 @@ __export(index_exports, {
|
|
|
7815
8018
|
BC_CMD_ID_GET_DING_DONG_LIST: () => BC_CMD_ID_GET_DING_DONG_LIST,
|
|
7816
8019
|
BC_CMD_ID_GET_DING_DONG_SILENT: () => BC_CMD_ID_GET_DING_DONG_SILENT,
|
|
7817
8020
|
BC_CMD_ID_GET_EMAIL_TASK: () => BC_CMD_ID_GET_EMAIL_TASK,
|
|
8021
|
+
BC_CMD_ID_GET_ENC: () => BC_CMD_ID_GET_ENC,
|
|
7818
8022
|
BC_CMD_ID_GET_FTP_TASK: () => BC_CMD_ID_GET_FTP_TASK,
|
|
7819
8023
|
BC_CMD_ID_GET_HDD_INFO_LIST: () => BC_CMD_ID_GET_HDD_INFO_LIST,
|
|
7820
8024
|
BC_CMD_ID_GET_KIT_AP_CFG: () => BC_CMD_ID_GET_KIT_AP_CFG,
|
|
@@ -7823,8 +8027,10 @@ __export(index_exports, {
|
|
|
7823
8027
|
BC_CMD_ID_GET_ONLINE_USER_LIST: () => BC_CMD_ID_GET_ONLINE_USER_LIST,
|
|
7824
8028
|
BC_CMD_ID_GET_OSD_DATETIME: () => BC_CMD_ID_GET_OSD_DATETIME,
|
|
7825
8029
|
BC_CMD_ID_GET_PIR_INFO: () => BC_CMD_ID_GET_PIR_INFO,
|
|
8030
|
+
BC_CMD_ID_GET_PRIVACY_MASK: () => BC_CMD_ID_GET_PRIVACY_MASK,
|
|
7826
8031
|
BC_CMD_ID_GET_PTZ_POSITION: () => BC_CMD_ID_GET_PTZ_POSITION,
|
|
7827
8032
|
BC_CMD_ID_GET_PTZ_PRESET: () => BC_CMD_ID_GET_PTZ_PRESET,
|
|
8033
|
+
BC_CMD_ID_GET_PUSH_TASK: () => BC_CMD_ID_GET_PUSH_TASK,
|
|
7828
8034
|
BC_CMD_ID_GET_RECORD: () => BC_CMD_ID_GET_RECORD,
|
|
7829
8035
|
BC_CMD_ID_GET_RECORD_CFG: () => BC_CMD_ID_GET_RECORD_CFG,
|
|
7830
8036
|
BC_CMD_ID_GET_REC_ENC_CFG: () => BC_CMD_ID_GET_REC_ENC_CFG,
|
|
@@ -7853,11 +8059,23 @@ __export(index_exports, {
|
|
|
7853
8059
|
BC_CMD_ID_QUICK_REPLY_PLAY: () => BC_CMD_ID_QUICK_REPLY_PLAY,
|
|
7854
8060
|
BC_CMD_ID_SET_AI_ALARM: () => BC_CMD_ID_SET_AI_ALARM,
|
|
7855
8061
|
BC_CMD_ID_SET_AI_CFG: () => BC_CMD_ID_SET_AI_CFG,
|
|
8062
|
+
BC_CMD_ID_SET_AI_DENOISE: () => BC_CMD_ID_SET_AI_DENOISE,
|
|
8063
|
+
BC_CMD_ID_SET_AUDIO_CFG: () => BC_CMD_ID_SET_AUDIO_CFG,
|
|
7856
8064
|
BC_CMD_ID_SET_AUDIO_TASK: () => BC_CMD_ID_SET_AUDIO_TASK,
|
|
8065
|
+
BC_CMD_ID_SET_AUTO_FOCUS: () => BC_CMD_ID_SET_AUTO_FOCUS,
|
|
8066
|
+
BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD: () => BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD,
|
|
7857
8067
|
BC_CMD_ID_SET_DING_DONG_CFG: () => BC_CMD_ID_SET_DING_DONG_CFG,
|
|
7858
8068
|
BC_CMD_ID_SET_DING_DONG_SILENT: () => BC_CMD_ID_SET_DING_DONG_SILENT,
|
|
8069
|
+
BC_CMD_ID_SET_EMAIL_TASK: () => BC_CMD_ID_SET_EMAIL_TASK,
|
|
8070
|
+
BC_CMD_ID_SET_ENC: () => BC_CMD_ID_SET_ENC,
|
|
8071
|
+
BC_CMD_ID_SET_LED_STATE: () => BC_CMD_ID_SET_LED_STATE,
|
|
7859
8072
|
BC_CMD_ID_SET_MOTION_ALARM: () => BC_CMD_ID_SET_MOTION_ALARM,
|
|
7860
8073
|
BC_CMD_ID_SET_PIR_INFO: () => BC_CMD_ID_SET_PIR_INFO,
|
|
8074
|
+
BC_CMD_ID_SET_PRIVACY_MASK: () => BC_CMD_ID_SET_PRIVACY_MASK,
|
|
8075
|
+
BC_CMD_ID_SET_PUSH_TASK: () => BC_CMD_ID_SET_PUSH_TASK,
|
|
8076
|
+
BC_CMD_ID_SET_RECORD: () => BC_CMD_ID_SET_RECORD,
|
|
8077
|
+
BC_CMD_ID_SET_RECORD_CFG: () => BC_CMD_ID_SET_RECORD_CFG,
|
|
8078
|
+
BC_CMD_ID_SET_VIDEO_INPUT: () => BC_CMD_ID_SET_VIDEO_INPUT,
|
|
7861
8079
|
BC_CMD_ID_SET_WHITE_LED_STATE: () => BC_CMD_ID_SET_WHITE_LED_STATE,
|
|
7862
8080
|
BC_CMD_ID_SET_WHITE_LED_TASK: () => BC_CMD_ID_SET_WHITE_LED_TASK,
|
|
7863
8081
|
BC_CMD_ID_SET_ZOOM_FOCUS: () => BC_CMD_ID_SET_ZOOM_FOCUS,
|
|
@@ -7896,6 +8114,7 @@ __export(index_exports, {
|
|
|
7896
8114
|
HlsSessionManager: () => HlsSessionManager,
|
|
7897
8115
|
Intercom: () => Intercom,
|
|
7898
8116
|
MjpegTransformer: () => MjpegTransformer,
|
|
8117
|
+
MpegTsMuxer: () => MpegTsMuxer,
|
|
7899
8118
|
NVR_HUB_EXACT_TYPES: () => NVR_HUB_EXACT_TYPES,
|
|
7900
8119
|
NVR_HUB_MODEL_PATTERNS: () => NVR_HUB_MODEL_PATTERNS,
|
|
7901
8120
|
ReolinkBaichuanApi: () => ReolinkBaichuanApi,
|
|
@@ -7905,6 +8124,8 @@ __export(index_exports, {
|
|
|
7905
8124
|
abilitiesHasAny: () => abilitiesHasAny,
|
|
7906
8125
|
aesDecrypt: () => aesDecrypt,
|
|
7907
8126
|
aesEncrypt: () => aesEncrypt,
|
|
8127
|
+
applyStreamPatch: () => applyStreamPatch,
|
|
8128
|
+
applyXmlTagPatch: () => applyXmlTagPatch,
|
|
7908
8129
|
asLogger: () => asLogger,
|
|
7909
8130
|
autoDetectDeviceType: () => autoDetectDeviceType,
|
|
7910
8131
|
bcDecrypt: () => bcDecrypt,
|
|
@@ -7954,6 +8175,7 @@ __export(index_exports, {
|
|
|
7954
8175
|
createRfc4571TcpServerForReplay: () => createRfc4571TcpServerForReplay,
|
|
7955
8176
|
createRtspProxyServer: () => createRtspProxyServer,
|
|
7956
8177
|
createTaggedLogger: () => createTaggedLogger,
|
|
8178
|
+
decideSleepInferenceTransition: () => decideSleepInferenceTransition,
|
|
7957
8179
|
decideVideoclipTranscodeMode: () => decideVideoclipTranscodeMode,
|
|
7958
8180
|
decodeHeader: () => decodeHeader,
|
|
7959
8181
|
deriveAesKey: () => deriveAesKey,
|
|
@@ -7968,6 +8190,7 @@ __export(index_exports, {
|
|
|
7968
8190
|
discoverViaUdpBroadcast: () => discoverViaUdpBroadcast,
|
|
7969
8191
|
discoverViaUdpDirect: () => discoverViaUdpDirect,
|
|
7970
8192
|
encodeHeader: () => encodeHeader,
|
|
8193
|
+
ensureXmlHeader: () => ensureXmlHeader,
|
|
7971
8194
|
extractH264ParamSetsFromAccessUnit: () => extractH264ParamSetsFromAccessUnit,
|
|
7972
8195
|
extractH265ParamSetsFromAccessUnit: () => extractH265ParamSetsFromAccessUnit,
|
|
7973
8196
|
extractPpsFromAnnexB: () => extractPpsFromAnnexB,
|
|
@@ -7996,6 +8219,8 @@ __export(index_exports, {
|
|
|
7996
8219
|
maskUid: () => maskUid,
|
|
7997
8220
|
md5HexUpper: () => md5HexUpper,
|
|
7998
8221
|
md5StrModern: () => md5StrModern,
|
|
8222
|
+
normalizeDayNightMode: () => normalizeDayNightMode,
|
|
8223
|
+
normalizeOpenClose: () => normalizeOpenClose,
|
|
7999
8224
|
normalizeUid: () => normalizeUid,
|
|
8000
8225
|
packetizeAacAdtsFrame: () => packetizeAacAdtsFrame,
|
|
8001
8226
|
packetizeAacRawFrame: () => packetizeAacRawFrame,
|
|
@@ -8005,6 +8230,7 @@ __export(index_exports, {
|
|
|
8005
8230
|
parseBcMedia: () => parseBcMedia,
|
|
8006
8231
|
parseRecordingFileName: () => parseRecordingFileName,
|
|
8007
8232
|
parseSupportXml: () => parseSupportXml,
|
|
8233
|
+
patchNestedTag: () => patchNestedTag,
|
|
8008
8234
|
printNvrDiagnostics: () => printNvrDiagnostics,
|
|
8009
8235
|
runAllDiagnosticsConsecutively: () => runAllDiagnosticsConsecutively,
|
|
8010
8236
|
runMultifocalDiagnosticsConsecutively: () => runMultifocalDiagnosticsConsecutively,
|
|
@@ -13234,19 +13460,34 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
13234
13460
|
}
|
|
13235
13461
|
});
|
|
13236
13462
|
streamStarted = true;
|
|
13237
|
-
|
|
13463
|
+
const signal = options?.signal;
|
|
13464
|
+
while (!closed && !signal?.aborted) {
|
|
13238
13465
|
if (frameQueue.length > 0) {
|
|
13239
13466
|
const frame = frameQueue.shift();
|
|
13240
13467
|
yield frame;
|
|
13241
13468
|
} else {
|
|
13242
13469
|
await new Promise((resolve) => {
|
|
13243
13470
|
frameResolve = resolve;
|
|
13244
|
-
setTimeout(() => {
|
|
13471
|
+
const timer = setTimeout(() => {
|
|
13245
13472
|
if (frameResolve === resolve) {
|
|
13246
13473
|
frameResolve = null;
|
|
13247
13474
|
resolve();
|
|
13248
13475
|
}
|
|
13249
13476
|
}, 1e3);
|
|
13477
|
+
if (signal) {
|
|
13478
|
+
const onAbort = () => {
|
|
13479
|
+
clearTimeout(timer);
|
|
13480
|
+
if (frameResolve === resolve) frameResolve = null;
|
|
13481
|
+
resolve();
|
|
13482
|
+
};
|
|
13483
|
+
if (signal.aborted) {
|
|
13484
|
+
clearTimeout(timer);
|
|
13485
|
+
frameResolve = null;
|
|
13486
|
+
resolve();
|
|
13487
|
+
} else {
|
|
13488
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
13489
|
+
}
|
|
13490
|
+
}
|
|
13250
13491
|
});
|
|
13251
13492
|
}
|
|
13252
13493
|
}
|
|
@@ -13419,13 +13660,14 @@ var NativeStreamFanout = class {
|
|
|
13419
13660
|
source = null;
|
|
13420
13661
|
running = false;
|
|
13421
13662
|
pumpPromise = null;
|
|
13663
|
+
abort = new AbortController();
|
|
13422
13664
|
constructor(opts) {
|
|
13423
13665
|
this.opts = opts;
|
|
13424
13666
|
}
|
|
13425
13667
|
start() {
|
|
13426
13668
|
if (this.running) return;
|
|
13427
13669
|
this.running = true;
|
|
13428
|
-
this.source = this.opts.createSource();
|
|
13670
|
+
this.source = this.opts.createSource(this.abort.signal);
|
|
13429
13671
|
this.pumpPromise = (async () => {
|
|
13430
13672
|
try {
|
|
13431
13673
|
for await (const frame of this.source) {
|
|
@@ -13471,6 +13713,7 @@ var NativeStreamFanout = class {
|
|
|
13471
13713
|
this.source = null;
|
|
13472
13714
|
for (const q of this.queues.values()) q.close();
|
|
13473
13715
|
this.queues.clear();
|
|
13716
|
+
this.abort.abort();
|
|
13474
13717
|
try {
|
|
13475
13718
|
await src?.return(void 0);
|
|
13476
13719
|
} catch {
|
|
@@ -13509,9 +13752,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13509
13752
|
requireAuth;
|
|
13510
13753
|
authNonces = /* @__PURE__ */ new Map();
|
|
13511
13754
|
// Track nonces per client
|
|
13512
|
-
AUTH_REALM
|
|
13755
|
+
AUTH_REALM;
|
|
13513
13756
|
NONCE_TIMEOUT_MS = 3e5;
|
|
13514
13757
|
// 5 minutes
|
|
13758
|
+
lazyMetadata;
|
|
13515
13759
|
// Client tracking
|
|
13516
13760
|
connectedClients = /* @__PURE__ */ new Set();
|
|
13517
13761
|
// Set of client IDs (IP:port)
|
|
@@ -13523,8 +13767,15 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13523
13767
|
// Track all client resources for cleanup
|
|
13524
13768
|
clientResources = /* @__PURE__ */ new Map();
|
|
13525
13769
|
isRtspDebugEnabled() {
|
|
13526
|
-
|
|
13527
|
-
|
|
13770
|
+
try {
|
|
13771
|
+
if (this.api.isClosed) {
|
|
13772
|
+
return envBool(process.env.BAICHUAN_DEBUG_RTSP, false);
|
|
13773
|
+
}
|
|
13774
|
+
const dbg = this.api.client.getDebugConfig();
|
|
13775
|
+
return dbg.debugRtsp || envBool(process.env.BAICHUAN_DEBUG_RTSP, false);
|
|
13776
|
+
} catch {
|
|
13777
|
+
return envBool(process.env.BAICHUAN_DEBUG_RTSP, false);
|
|
13778
|
+
}
|
|
13528
13779
|
}
|
|
13529
13780
|
rtspDebugLog(message) {
|
|
13530
13781
|
if (!this.isRtspDebugEnabled()) return;
|
|
@@ -13546,10 +13797,20 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13546
13797
|
// Shared native stream fan-out (single camera stream, multiple RTSP clients)
|
|
13547
13798
|
nativeFanout = null;
|
|
13548
13799
|
noClientAutoStopTimer;
|
|
13800
|
+
/** Fires if camera never sends frames after stream start (sleeping), even with clients connected. */
|
|
13801
|
+
noFrameDeadlineTimer;
|
|
13549
13802
|
/** After last RTSP client; 0 = never auto-stop native stream. */
|
|
13550
13803
|
nativeStreamIdleStopMs;
|
|
13551
13804
|
/** Primed-but-no-PLAY timeout; 0 = disabled. */
|
|
13552
13805
|
nativeStreamPrimeIdleStopMs;
|
|
13806
|
+
/**
|
|
13807
|
+
* Max time to wait for the first camera frame after stream start.
|
|
13808
|
+
* If no frames arrive within this window, the native stream is stopped
|
|
13809
|
+
* (camera is sleeping). Prevents the BaichuanVideoStream watchdog from
|
|
13810
|
+
* firing and waking the camera when no real viewer is watching.
|
|
13811
|
+
* 0 = disabled. Defaults to nativeStreamPrimeIdleStopMs * 2 when > 0.
|
|
13812
|
+
*/
|
|
13813
|
+
nativeStreamNoFrameDeadlineMs;
|
|
13553
13814
|
// Prebuffer: rolling ring of recent video frames for IDR-aligned fast startup.
|
|
13554
13815
|
// When a new client connects while the stream is already running it does not need
|
|
13555
13816
|
// to wait up to one full GOP interval for the next keyframe — we replay frames
|
|
@@ -13675,14 +13936,25 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13675
13936
|
this.logger = options.logger ?? console;
|
|
13676
13937
|
this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
|
|
13677
13938
|
this.deviceId = options.deviceId;
|
|
13678
|
-
this.externalListener = options.externalListener ?? false;
|
|
13939
|
+
this.externalListener = (options.externalListener ?? false) || (options.muxMode ?? false);
|
|
13679
13940
|
this.nativeStreamIdleStopMs = options.nativeStreamIdleStopMs ?? 3e4;
|
|
13680
13941
|
this.nativeStreamPrimeIdleStopMs = options.nativeStreamPrimeIdleStopMs ?? (this.nativeStreamIdleStopMs > 0 ? 15e3 : 0);
|
|
13681
|
-
this.
|
|
13942
|
+
this.nativeStreamNoFrameDeadlineMs = this.nativeStreamPrimeIdleStopMs > 0 ? Math.min(this.nativeStreamPrimeIdleStopMs * 2, 3e4) : 0;
|
|
13943
|
+
this.authCredentials = (options.credentials ?? []).map((c) => ({
|
|
13944
|
+
username: c.username,
|
|
13945
|
+
...c.password !== void 0 ? { password: c.password } : {},
|
|
13946
|
+
...c.ha1 !== void 0 ? { ha1: c.ha1 } : {}
|
|
13947
|
+
}));
|
|
13682
13948
|
this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
|
|
13949
|
+
this.AUTH_REALM = options.authRealm ?? "BaichuanRtspServer";
|
|
13950
|
+
this.lazyMetadata = options.lazyMetadata ?? false;
|
|
13683
13951
|
const transport = this.api.client.getTransport();
|
|
13684
13952
|
this.flow = createRtspFlow(transport, "H264");
|
|
13685
13953
|
}
|
|
13954
|
+
/** Number of currently connected RTSP clients. */
|
|
13955
|
+
get clientCount() {
|
|
13956
|
+
return this.connectedClients.size;
|
|
13957
|
+
}
|
|
13686
13958
|
// --- Authentication helpers ---
|
|
13687
13959
|
/**
|
|
13688
13960
|
* Generate a new nonce for Digest authentication
|
|
@@ -13743,9 +14015,16 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13743
14015
|
this.rtspDebugLog(`Auth failed: nonce mismatch for client ${clientId}`);
|
|
13744
14016
|
return false;
|
|
13745
14017
|
}
|
|
14018
|
+
if (realm !== this.AUTH_REALM) {
|
|
14019
|
+
this.rtspDebugLog(
|
|
14020
|
+
`Auth failed: realm mismatch (client="${realm}", server="${this.AUTH_REALM}")`
|
|
14021
|
+
);
|
|
14022
|
+
return false;
|
|
14023
|
+
}
|
|
13746
14024
|
for (const cred of this.authCredentials) {
|
|
13747
14025
|
if (username !== cred.username) continue;
|
|
13748
|
-
const ha1 = this.md5(`${cred.username}:${
|
|
14026
|
+
const ha1 = cred.ha1 ?? (cred.password !== void 0 ? this.md5(`${cred.username}:${this.AUTH_REALM}:${cred.password}`) : void 0);
|
|
14027
|
+
if (!ha1) continue;
|
|
13749
14028
|
const ha2 = this.md5(`${method}:${authUri || uri}`);
|
|
13750
14029
|
const expectedResponse = this.md5(`${ha1}:${nonce}:${ha2}`);
|
|
13751
14030
|
if (response === expectedResponse) {
|
|
@@ -13774,6 +14053,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13774
14053
|
this.noClientAutoStopTimer = void 0;
|
|
13775
14054
|
}
|
|
13776
14055
|
}
|
|
14056
|
+
clearNoFrameDeadlineTimer() {
|
|
14057
|
+
if (this.noFrameDeadlineTimer) {
|
|
14058
|
+
clearTimeout(this.noFrameDeadlineTimer);
|
|
14059
|
+
this.noFrameDeadlineTimer = void 0;
|
|
14060
|
+
}
|
|
14061
|
+
}
|
|
13777
14062
|
setFlowVideoType(videoType, reason) {
|
|
13778
14063
|
if (this.flow.videoType === videoType) return;
|
|
13779
14064
|
const transport = this.api.client.getTransport();
|
|
@@ -13788,25 +14073,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13788
14073
|
if (this.active) {
|
|
13789
14074
|
throw new Error("RTSP server is already active");
|
|
13790
14075
|
}
|
|
13791
|
-
|
|
13792
|
-
|
|
13793
|
-
|
|
13794
|
-
if (stream) {
|
|
13795
|
-
this.streamMetadata = {
|
|
13796
|
-
frameRate: stream.frameRate || 25,
|
|
13797
|
-
width: stream.width,
|
|
13798
|
-
height: stream.height
|
|
13799
|
-
};
|
|
13800
|
-
const enc = String(stream.videoEncType ?? "").trim().toLowerCase();
|
|
13801
|
-
const metaVideoType = enc.includes("265") || enc.includes("hevc") ? "H265" : "H264";
|
|
13802
|
-
this.setFlowVideoType(metaVideoType, "metadata");
|
|
13803
|
-
}
|
|
13804
|
-
} catch (error) {
|
|
13805
|
-
this.logger.warn(
|
|
13806
|
-
`[BaichuanRtspServer] Could not get stream metadata: ${error}`
|
|
14076
|
+
if (this.lazyMetadata) {
|
|
14077
|
+
this.logger.info(
|
|
14078
|
+
`[BaichuanRtspServer] lazy metadata: skipping initial getStreamMetadata; will fetch on first DESCRIBE`
|
|
13807
14079
|
);
|
|
13808
|
-
|
|
13809
|
-
|
|
14080
|
+
} else {
|
|
14081
|
+
try {
|
|
14082
|
+
const metadata = await this.api.getStreamMetadata(this.channel);
|
|
14083
|
+
const stream = metadata.streams.find((s) => s.profile === this.profile);
|
|
14084
|
+
if (stream) {
|
|
14085
|
+
this.streamMetadata = {
|
|
14086
|
+
frameRate: stream.frameRate || 25,
|
|
14087
|
+
width: stream.width,
|
|
14088
|
+
height: stream.height
|
|
14089
|
+
};
|
|
14090
|
+
const enc = String(stream.videoEncType ?? "").trim().toLowerCase();
|
|
14091
|
+
const metaVideoType = enc.includes("265") || enc.includes("hevc") ? "H265" : "H264";
|
|
14092
|
+
this.setFlowVideoType(metaVideoType, "metadata");
|
|
14093
|
+
}
|
|
14094
|
+
} catch (error) {
|
|
14095
|
+
this.logger.warn(
|
|
14096
|
+
`[BaichuanRtspServer] Could not get stream metadata: ${error}`
|
|
14097
|
+
);
|
|
14098
|
+
this.streamMetadata = { frameRate: 25 };
|
|
14099
|
+
this.setFlowVideoType("H264", "metadata unavailable");
|
|
14100
|
+
}
|
|
13810
14101
|
}
|
|
13811
14102
|
if (!this.externalListener) {
|
|
13812
14103
|
this.clientConnectionServer = net2.createServer((socket) => {
|
|
@@ -13847,6 +14138,30 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
13847
14138
|
}
|
|
13848
14139
|
this.handleRtspConnection(socket, initialBuffer);
|
|
13849
14140
|
}
|
|
14141
|
+
/**
|
|
14142
|
+
* Inject an already-accepted client socket from a multiplexer
|
|
14143
|
+
* (e.g. `LocalRtspMux`) that owns the listening port.
|
|
14144
|
+
*
|
|
14145
|
+
* The mux reads the first RTSP request line to determine the target path,
|
|
14146
|
+
* then hands the socket over. Any bytes already consumed during routing
|
|
14147
|
+
* are replayed back onto the socket via `unshift()` so the RTSP parser in
|
|
14148
|
+
* `handleRtspConnection` sees the complete original request.
|
|
14149
|
+
*
|
|
14150
|
+
* @param socket - Client TCP socket, already accepted by the mux.
|
|
14151
|
+
* @param preReadData - Bytes the mux has already pulled off the socket
|
|
14152
|
+
* while parsing the request line. Replayed via `socket.unshift()`
|
|
14153
|
+
* before any further reads.
|
|
14154
|
+
*/
|
|
14155
|
+
injectSocket(socket, preReadData) {
|
|
14156
|
+
if (!this.active) {
|
|
14157
|
+
socket.end("RTSP/1.0 503 Service Unavailable\r\n\r\n");
|
|
14158
|
+
return;
|
|
14159
|
+
}
|
|
14160
|
+
if (preReadData && preReadData.length > 0) {
|
|
14161
|
+
socket.unshift(preReadData);
|
|
14162
|
+
}
|
|
14163
|
+
this.handleRtspConnection(socket);
|
|
14164
|
+
}
|
|
13850
14165
|
/**
|
|
13851
14166
|
* Handle RTSP connection from a client.
|
|
13852
14167
|
*/
|
|
@@ -14011,6 +14326,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14011
14326
|
Public: "DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS"
|
|
14012
14327
|
});
|
|
14013
14328
|
} else if (method === "DESCRIBE") {
|
|
14329
|
+
if (!this.api.isClosed && !this.api.isReady && !this.nativeStreamActive) {
|
|
14330
|
+
void this.api.ensureConnected().catch(() => {
|
|
14331
|
+
});
|
|
14332
|
+
}
|
|
14014
14333
|
if (!this.flow.getFmtp().hasParamSets && this.connectedClients.size === 0) {
|
|
14015
14334
|
try {
|
|
14016
14335
|
if (!this.nativeStreamActive) {
|
|
@@ -14048,6 +14367,27 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14048
14367
|
}
|
|
14049
14368
|
}
|
|
14050
14369
|
}
|
|
14370
|
+
if (!this.hasAudio && this.firstAudioPromise) {
|
|
14371
|
+
const audioPrimingMs = this.api.client.getTransport() === "udp" ? 3e3 : 2e3;
|
|
14372
|
+
const audioPrimingStart = Date.now();
|
|
14373
|
+
try {
|
|
14374
|
+
await Promise.race([
|
|
14375
|
+
this.firstAudioPromise,
|
|
14376
|
+
new Promise((resolve) => setTimeout(resolve, audioPrimingMs))
|
|
14377
|
+
]);
|
|
14378
|
+
} catch {
|
|
14379
|
+
}
|
|
14380
|
+
const audioPrimingElapsed = Date.now() - audioPrimingStart;
|
|
14381
|
+
if (this.hasAudio) {
|
|
14382
|
+
this.logger.info(
|
|
14383
|
+
`[rebroadcast] DESCRIBE audio priming: AAC detected after ${audioPrimingElapsed}ms client=${clientId} path=${this.path}`
|
|
14384
|
+
);
|
|
14385
|
+
} else {
|
|
14386
|
+
this.logger.info(
|
|
14387
|
+
`[rebroadcast] DESCRIBE audio priming: no audio after ${audioPrimingElapsed}ms \u2014 SDP will be video-only client=${clientId} path=${this.path}`
|
|
14388
|
+
);
|
|
14389
|
+
}
|
|
14390
|
+
}
|
|
14051
14391
|
{
|
|
14052
14392
|
const { fmtp, hasParamSets: hasParamSets2 } = this.flow.getFmtp();
|
|
14053
14393
|
const fmtpPreview = fmtp.length > 160 ? `${fmtp.slice(0, 160)}...` : fmtp;
|
|
@@ -14056,12 +14396,13 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14056
14396
|
);
|
|
14057
14397
|
}
|
|
14058
14398
|
const sdp = this.generateSdp();
|
|
14399
|
+
const contentHost = (socket.localAddress && socket.localAddress !== "0.0.0.0" && socket.localAddress !== "::" ? socket.localAddress.replace(/^::ffff:/, "") : null) ?? this.listenHost;
|
|
14059
14400
|
sendResponse(
|
|
14060
14401
|
200,
|
|
14061
14402
|
"OK",
|
|
14062
14403
|
{
|
|
14063
14404
|
"Content-Type": "application/sdp",
|
|
14064
|
-
"Content-Base": `rtsp://${
|
|
14405
|
+
"Content-Base": `rtsp://${contentHost}:${this.listenPort}${this.path}/`
|
|
14065
14406
|
},
|
|
14066
14407
|
sdp
|
|
14067
14408
|
);
|
|
@@ -14084,7 +14425,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14084
14425
|
this.emit("client", clientId);
|
|
14085
14426
|
this.clearNoClientAutoStopTimer();
|
|
14086
14427
|
if (this.connectedClients.size === 1 && !this.nativeStreamActive) {
|
|
14087
|
-
|
|
14428
|
+
void this.startNativeStream();
|
|
14088
14429
|
}
|
|
14089
14430
|
const transportMatch = requestText.match(/Transport:\s*([^\r\n]+)/i);
|
|
14090
14431
|
const transport = (transportMatch?.[1] ?? "").trim();
|
|
@@ -14218,12 +14559,23 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14218
14559
|
}
|
|
14219
14560
|
}
|
|
14220
14561
|
};
|
|
14562
|
+
const runProcessBuffer = () => {
|
|
14563
|
+
processBuffer().catch((err) => {
|
|
14564
|
+
this.logger.debug(
|
|
14565
|
+
`[BaichuanRtspServer] processBuffer failed for ${clientId}: ${err?.message ?? err}`
|
|
14566
|
+
);
|
|
14567
|
+
try {
|
|
14568
|
+
socket.destroy();
|
|
14569
|
+
} catch {
|
|
14570
|
+
}
|
|
14571
|
+
});
|
|
14572
|
+
};
|
|
14221
14573
|
socket.on("data", (data) => {
|
|
14222
14574
|
buffer = Buffer.concat([buffer, data]);
|
|
14223
|
-
|
|
14575
|
+
runProcessBuffer();
|
|
14224
14576
|
});
|
|
14225
14577
|
if (buffer.includes("\r\n\r\n")) {
|
|
14226
|
-
|
|
14578
|
+
runProcessBuffer();
|
|
14227
14579
|
}
|
|
14228
14580
|
}
|
|
14229
14581
|
/**
|
|
@@ -15091,6 +15443,25 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15091
15443
|
if (this.nativeStreamActive) {
|
|
15092
15444
|
return;
|
|
15093
15445
|
}
|
|
15446
|
+
if (!this.api.isReady) {
|
|
15447
|
+
if (this.api.isClosed) {
|
|
15448
|
+
this.logger.warn?.(
|
|
15449
|
+
`[rebroadcast] API has been explicitly closed \u2014 stream cannot start profile=${this.profile}`
|
|
15450
|
+
);
|
|
15451
|
+
return;
|
|
15452
|
+
}
|
|
15453
|
+
try {
|
|
15454
|
+
this.logger.info?.(
|
|
15455
|
+
`[rebroadcast] API not ready (idle disconnect?), calling ensureConnected profile=${this.profile}`
|
|
15456
|
+
);
|
|
15457
|
+
await this.api.ensureConnected();
|
|
15458
|
+
} catch (e) {
|
|
15459
|
+
this.logger.warn?.(
|
|
15460
|
+
`[rebroadcast] ensureConnected failed, aborting stream start: ${e}`
|
|
15461
|
+
);
|
|
15462
|
+
return;
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15094
15465
|
this.nativeStreamActive = true;
|
|
15095
15466
|
this.firstFrameReceived = false;
|
|
15096
15467
|
this.firstAudioDetected = false;
|
|
@@ -15125,13 +15496,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15125
15496
|
await this.flow.startKeepAlive(this.api);
|
|
15126
15497
|
this.nativeFanout = new NativeStreamFanout({
|
|
15127
15498
|
maxQueueItems: 200,
|
|
15128
|
-
createSource: () => createNativeStream(this.api, this.channel, this.profile, {
|
|
15499
|
+
createSource: (signal) => createNativeStream(this.api, this.channel, this.profile, {
|
|
15129
15500
|
variant: this.variant,
|
|
15130
|
-
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
15501
|
+
...dedicatedClient ? { client: dedicatedClient } : {},
|
|
15502
|
+
signal
|
|
15131
15503
|
}),
|
|
15132
15504
|
onFrame: (frame) => {
|
|
15133
15505
|
if (frame.audio) {
|
|
15134
|
-
if (!this.hasAudio &&
|
|
15506
|
+
if (!this.hasAudio && _BaichuanRtspServer.isAdtsAacFrame(frame.data)) {
|
|
15135
15507
|
const info = _BaichuanRtspServer.parseAdtsSamplingInfo(frame.data);
|
|
15136
15508
|
if (info) {
|
|
15137
15509
|
this.hasAudio = true;
|
|
@@ -15180,6 +15552,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15180
15552
|
onEnd: () => {
|
|
15181
15553
|
if (!this.nativeStreamActive) return;
|
|
15182
15554
|
this.nativeStreamActive = false;
|
|
15555
|
+
this.clearNoFrameDeadlineTimer();
|
|
15556
|
+
const hadFrames = this.firstFrameReceived;
|
|
15183
15557
|
this.firstFrameReceived = false;
|
|
15184
15558
|
this.firstFramePromise = null;
|
|
15185
15559
|
this.firstFrameResolve = null;
|
|
@@ -15204,7 +15578,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15204
15578
|
} catch {
|
|
15205
15579
|
}
|
|
15206
15580
|
}
|
|
15207
|
-
if (this.connectedClients.size > 0) {
|
|
15581
|
+
if (this.connectedClients.size > 0 && hadFrames) {
|
|
15208
15582
|
this.logger.info(
|
|
15209
15583
|
`[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
|
|
15210
15584
|
);
|
|
@@ -15216,6 +15590,19 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15216
15590
|
}
|
|
15217
15591
|
});
|
|
15218
15592
|
this.nativeFanout.start();
|
|
15593
|
+
this.clearNoFrameDeadlineTimer();
|
|
15594
|
+
if (this.nativeStreamNoFrameDeadlineMs > 0) {
|
|
15595
|
+
this.noFrameDeadlineTimer = setTimeout(() => {
|
|
15596
|
+
this.noFrameDeadlineTimer = void 0;
|
|
15597
|
+
if (!this.firstFrameReceived && this.nativeStreamActive) {
|
|
15598
|
+
this.logger.info(
|
|
15599
|
+
`[rebroadcast] no frames within ${this.nativeStreamNoFrameDeadlineMs}ms \u2014 camera sleeping, stopping stream profile=${this.profile} channel=${this.channel}`
|
|
15600
|
+
);
|
|
15601
|
+
void this.stopNativeStream();
|
|
15602
|
+
}
|
|
15603
|
+
}, this.nativeStreamNoFrameDeadlineMs);
|
|
15604
|
+
this.noFrameDeadlineTimer?.unref?.();
|
|
15605
|
+
}
|
|
15219
15606
|
this.clearNoClientAutoStopTimer();
|
|
15220
15607
|
if (this.nativeStreamPrimeIdleStopMs > 0) {
|
|
15221
15608
|
this.noClientAutoStopTimer = setTimeout(() => {
|
|
@@ -15232,6 +15619,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15232
15619
|
markFirstFrameReceived() {
|
|
15233
15620
|
if (!this.firstFrameReceived && this.firstFrameResolve) {
|
|
15234
15621
|
this.firstFrameReceived = true;
|
|
15622
|
+
this.clearNoFrameDeadlineTimer();
|
|
15235
15623
|
this.rtspDebugLog(
|
|
15236
15624
|
`First frame received from camera for profile ${this.profile}`
|
|
15237
15625
|
);
|
|
@@ -15258,6 +15646,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
15258
15646
|
);
|
|
15259
15647
|
this.flow.stopKeepAlive();
|
|
15260
15648
|
this.clearNoClientAutoStopTimer();
|
|
15649
|
+
this.clearNoFrameDeadlineTimer();
|
|
15261
15650
|
this.nativeStreamActive = false;
|
|
15262
15651
|
this.firstFrameReceived = false;
|
|
15263
15652
|
this.firstFramePromise = null;
|
|
@@ -15474,149 +15863,17 @@ init_BcMediaAnnexBDecoder();
|
|
|
15474
15863
|
// src/baichuan/stream/MpegTsMuxer.ts
|
|
15475
15864
|
var TS_PACKET_SIZE = 188;
|
|
15476
15865
|
var TS_SYNC_BYTE = 71;
|
|
15477
|
-
var
|
|
15478
|
-
var
|
|
15479
|
-
var
|
|
15866
|
+
var TS_PAYLOAD_SIZE = TS_PACKET_SIZE - 4;
|
|
15867
|
+
var PID_PAT = 0;
|
|
15868
|
+
var PID_PMT = 4096;
|
|
15869
|
+
var PID_VIDEO = 256;
|
|
15870
|
+
var PID_AUDIO = 257;
|
|
15480
15871
|
var STREAM_TYPE_H264 = 27;
|
|
15481
15872
|
var STREAM_TYPE_H265 = 36;
|
|
15482
|
-
var
|
|
15483
|
-
var
|
|
15484
|
-
var
|
|
15485
|
-
|
|
15486
|
-
const packet = Buffer.alloc(TS_PACKET_SIZE, 255);
|
|
15487
|
-
packet[0] = TS_SYNC_BYTE;
|
|
15488
|
-
packet[1] = 64 | PAT_PID >> 8 & 31;
|
|
15489
|
-
packet[2] = PAT_PID & 255;
|
|
15490
|
-
packet[3] = 16 | patCc & 15;
|
|
15491
|
-
patCc = patCc + 1 & 15;
|
|
15492
|
-
packet[4] = 0;
|
|
15493
|
-
let idx = 5;
|
|
15494
|
-
packet[idx++] = 0;
|
|
15495
|
-
packet[idx++] = 176;
|
|
15496
|
-
packet[idx++] = 13;
|
|
15497
|
-
packet[idx++] = 0;
|
|
15498
|
-
packet[idx++] = 1;
|
|
15499
|
-
packet[idx++] = 193;
|
|
15500
|
-
packet[idx++] = 0;
|
|
15501
|
-
packet[idx++] = 0;
|
|
15502
|
-
packet[idx++] = 0;
|
|
15503
|
-
packet[idx++] = 1;
|
|
15504
|
-
packet[idx++] = 224 | PMT_PID >> 8 & 31;
|
|
15505
|
-
packet[idx++] = PMT_PID & 255;
|
|
15506
|
-
const crc = crc32Mpeg(packet.subarray(5, idx));
|
|
15507
|
-
packet.writeUInt32BE(crc, idx);
|
|
15508
|
-
return packet;
|
|
15509
|
-
}
|
|
15510
|
-
function createPmt(streamType) {
|
|
15511
|
-
const packet = Buffer.alloc(TS_PACKET_SIZE, 255);
|
|
15512
|
-
packet[0] = TS_SYNC_BYTE;
|
|
15513
|
-
packet[1] = 64 | PMT_PID >> 8 & 31;
|
|
15514
|
-
packet[2] = PMT_PID & 255;
|
|
15515
|
-
packet[3] = 16 | pmtCc & 15;
|
|
15516
|
-
pmtCc = pmtCc + 1 & 15;
|
|
15517
|
-
packet[4] = 0;
|
|
15518
|
-
let idx = 5;
|
|
15519
|
-
packet[idx++] = 2;
|
|
15520
|
-
packet[idx++] = 176;
|
|
15521
|
-
packet[idx++] = 18;
|
|
15522
|
-
packet[idx++] = 0;
|
|
15523
|
-
packet[idx++] = 1;
|
|
15524
|
-
packet[idx++] = 193;
|
|
15525
|
-
packet[idx++] = 0;
|
|
15526
|
-
packet[idx++] = 0;
|
|
15527
|
-
packet[idx++] = 224 | VIDEO_PID >> 8 & 31;
|
|
15528
|
-
packet[idx++] = VIDEO_PID & 255;
|
|
15529
|
-
packet[idx++] = 240;
|
|
15530
|
-
packet[idx++] = 0;
|
|
15531
|
-
packet[idx++] = streamType;
|
|
15532
|
-
packet[idx++] = 224 | VIDEO_PID >> 8 & 31;
|
|
15533
|
-
packet[idx++] = VIDEO_PID & 255;
|
|
15534
|
-
packet[idx++] = 240;
|
|
15535
|
-
packet[idx++] = 0;
|
|
15536
|
-
const crc = crc32Mpeg(packet.subarray(5, idx));
|
|
15537
|
-
packet.writeUInt32BE(crc, idx);
|
|
15538
|
-
return packet;
|
|
15539
|
-
}
|
|
15540
|
-
function createVideoPes(data, pts, isKeyframe) {
|
|
15541
|
-
const packets = [];
|
|
15542
|
-
const pts90k = Math.floor(pts * 9e4 / 1e6);
|
|
15543
|
-
const pesHeaderLen = 14;
|
|
15544
|
-
const pesHeader = Buffer.alloc(pesHeaderLen);
|
|
15545
|
-
let idx = 0;
|
|
15546
|
-
pesHeader[idx++] = 0;
|
|
15547
|
-
pesHeader[idx++] = 0;
|
|
15548
|
-
pesHeader[idx++] = 1;
|
|
15549
|
-
pesHeader[idx++] = 224;
|
|
15550
|
-
pesHeader[idx++] = 0;
|
|
15551
|
-
pesHeader[idx++] = 0;
|
|
15552
|
-
pesHeader[idx++] = 128;
|
|
15553
|
-
pesHeader[idx++] = 128;
|
|
15554
|
-
pesHeader[idx++] = 5;
|
|
15555
|
-
pesHeader[idx++] = 33 | pts90k >> 29 & 14;
|
|
15556
|
-
pesHeader[idx++] = pts90k >> 22 & 255;
|
|
15557
|
-
pesHeader[idx++] = 1 | pts90k >> 14 & 254;
|
|
15558
|
-
pesHeader[idx++] = pts90k >> 7 & 255;
|
|
15559
|
-
pesHeader[idx++] = 1 | pts90k << 1 & 254;
|
|
15560
|
-
const pesData = Buffer.concat([pesHeader, data]);
|
|
15561
|
-
let pesOffset = 0;
|
|
15562
|
-
let isFirst = true;
|
|
15563
|
-
while (pesOffset < pesData.length) {
|
|
15564
|
-
const packet = Buffer.alloc(TS_PACKET_SIZE, 255);
|
|
15565
|
-
let pktIdx = 0;
|
|
15566
|
-
packet[pktIdx++] = TS_SYNC_BYTE;
|
|
15567
|
-
packet[pktIdx++] = (isFirst ? 64 : 0) | VIDEO_PID >> 8 & 31;
|
|
15568
|
-
packet[pktIdx++] = VIDEO_PID & 255;
|
|
15569
|
-
const remaining = pesData.length - pesOffset;
|
|
15570
|
-
const maxPayload = TS_PACKET_SIZE - 4;
|
|
15571
|
-
if (remaining >= maxPayload) {
|
|
15572
|
-
packet[pktIdx++] = 16 | videoCc & 15;
|
|
15573
|
-
videoCc = videoCc + 1 & 15;
|
|
15574
|
-
pesData.copy(packet, pktIdx, pesOffset, pesOffset + maxPayload);
|
|
15575
|
-
pesOffset += maxPayload;
|
|
15576
|
-
} else {
|
|
15577
|
-
const adaptLen = maxPayload - remaining - 1;
|
|
15578
|
-
if (adaptLen < 0) {
|
|
15579
|
-
packet[pktIdx++] = 48 | videoCc & 15;
|
|
15580
|
-
videoCc = videoCc + 1 & 15;
|
|
15581
|
-
packet[pktIdx++] = TS_PACKET_SIZE - 4 - 1 - remaining;
|
|
15582
|
-
if (isFirst && isKeyframe) {
|
|
15583
|
-
packet[pktIdx++] = 64;
|
|
15584
|
-
for (let i = pktIdx; i < TS_PACKET_SIZE - remaining; i++) {
|
|
15585
|
-
packet[i] = 255;
|
|
15586
|
-
}
|
|
15587
|
-
} else {
|
|
15588
|
-
packet[pktIdx++] = 0;
|
|
15589
|
-
for (let i = pktIdx; i < TS_PACKET_SIZE - remaining; i++) {
|
|
15590
|
-
packet[i] = 255;
|
|
15591
|
-
}
|
|
15592
|
-
}
|
|
15593
|
-
pesData.copy(packet, TS_PACKET_SIZE - remaining, pesOffset);
|
|
15594
|
-
pesOffset += remaining;
|
|
15595
|
-
} else {
|
|
15596
|
-
packet[pktIdx++] = 48 | videoCc & 15;
|
|
15597
|
-
videoCc = videoCc + 1 & 15;
|
|
15598
|
-
if (adaptLen === 0) {
|
|
15599
|
-
packet[pktIdx++] = 0;
|
|
15600
|
-
} else {
|
|
15601
|
-
packet[pktIdx++] = adaptLen;
|
|
15602
|
-
if (isFirst && isKeyframe) {
|
|
15603
|
-
packet[pktIdx++] = 64;
|
|
15604
|
-
} else {
|
|
15605
|
-
packet[pktIdx++] = 0;
|
|
15606
|
-
}
|
|
15607
|
-
for (let i = 0; i < adaptLen - 1; i++) {
|
|
15608
|
-
packet[pktIdx++] = 255;
|
|
15609
|
-
}
|
|
15610
|
-
}
|
|
15611
|
-
pesData.copy(packet, pktIdx, pesOffset, pesOffset + remaining);
|
|
15612
|
-
pesOffset += remaining;
|
|
15613
|
-
}
|
|
15614
|
-
}
|
|
15615
|
-
packets.push(packet);
|
|
15616
|
-
isFirst = false;
|
|
15617
|
-
}
|
|
15618
|
-
return packets;
|
|
15619
|
-
}
|
|
15873
|
+
var STREAM_TYPE_AAC = 15;
|
|
15874
|
+
var PES_STREAM_ID_VIDEO = 224;
|
|
15875
|
+
var PES_STREAM_ID_AUDIO = 192;
|
|
15876
|
+
var PAT_PMT_INTERVAL = 40;
|
|
15620
15877
|
function crc32Mpeg(data) {
|
|
15621
15878
|
let crc = 4294967295;
|
|
15622
15879
|
for (let i = 0; i < data.length; i++) {
|
|
@@ -15631,45 +15888,218 @@ function crc32Mpeg(data) {
|
|
|
15631
15888
|
}
|
|
15632
15889
|
return crc >>> 0;
|
|
15633
15890
|
}
|
|
15891
|
+
function usToPts(us) {
|
|
15892
|
+
return Math.floor(us * 90 / 1e3) & 8589934591;
|
|
15893
|
+
}
|
|
15894
|
+
function encodePts(buf, offset, pts, prefix) {
|
|
15895
|
+
buf[offset + 0] = prefix << 4 | (pts >>> 30 & 7) << 1 | 1;
|
|
15896
|
+
buf[offset + 1] = pts >>> 22 & 255;
|
|
15897
|
+
buf[offset + 2] = (pts >>> 15 & 127) << 1 | 1;
|
|
15898
|
+
buf[offset + 3] = pts >>> 7 & 255;
|
|
15899
|
+
buf[offset + 4] = (pts & 127) << 1 | 1;
|
|
15900
|
+
}
|
|
15901
|
+
function writeTsHeader(buf, pid, pusi, cc, hasAdapt, hasPayload) {
|
|
15902
|
+
buf[0] = TS_SYNC_BYTE;
|
|
15903
|
+
buf[1] = (pusi ? 64 : 0) | pid >> 8 & 31;
|
|
15904
|
+
buf[2] = pid & 255;
|
|
15905
|
+
buf[3] = (hasAdapt ? 32 : 0) | (hasPayload ? 16 : 0) | cc & 15;
|
|
15906
|
+
}
|
|
15907
|
+
function pesToTsPackets(pesData, pid, ccRef, isKeyframe) {
|
|
15908
|
+
const totalPackets = Math.ceil(pesData.length / TS_PAYLOAD_SIZE);
|
|
15909
|
+
const out = Buffer.allocUnsafe(totalPackets * TS_PACKET_SIZE);
|
|
15910
|
+
let pesOffset = 0;
|
|
15911
|
+
let outOffset = 0;
|
|
15912
|
+
let isFirst = true;
|
|
15913
|
+
while (pesOffset < pesData.length) {
|
|
15914
|
+
const remaining = pesData.length - pesOffset;
|
|
15915
|
+
const packet = out.subarray(outOffset, outOffset + TS_PACKET_SIZE);
|
|
15916
|
+
outOffset += TS_PACKET_SIZE;
|
|
15917
|
+
if (remaining >= TS_PAYLOAD_SIZE) {
|
|
15918
|
+
writeTsHeader(packet, pid, isFirst, ccRef.cc, false, true);
|
|
15919
|
+
ccRef.cc = ccRef.cc + 1 & 15;
|
|
15920
|
+
pesData.copy(packet, 4, pesOffset, pesOffset + TS_PAYLOAD_SIZE);
|
|
15921
|
+
pesOffset += TS_PAYLOAD_SIZE;
|
|
15922
|
+
} else {
|
|
15923
|
+
const paddingNeeded = TS_PAYLOAD_SIZE - remaining;
|
|
15924
|
+
if (paddingNeeded === 1) {
|
|
15925
|
+
writeTsHeader(packet, pid, isFirst, ccRef.cc, true, true);
|
|
15926
|
+
ccRef.cc = ccRef.cc + 1 & 15;
|
|
15927
|
+
packet[4] = 0;
|
|
15928
|
+
pesData.copy(packet, 5, pesOffset, pesOffset + remaining);
|
|
15929
|
+
} else {
|
|
15930
|
+
writeTsHeader(packet, pid, isFirst, ccRef.cc, true, true);
|
|
15931
|
+
ccRef.cc = ccRef.cc + 1 & 15;
|
|
15932
|
+
const adaptLen = paddingNeeded - 1;
|
|
15933
|
+
packet[4] = adaptLen;
|
|
15934
|
+
packet[5] = isFirst && isKeyframe ? 64 : 0;
|
|
15935
|
+
packet.fill(255, 6, 4 + paddingNeeded);
|
|
15936
|
+
pesData.copy(packet, 4 + paddingNeeded, pesOffset, pesOffset + remaining);
|
|
15937
|
+
}
|
|
15938
|
+
pesOffset += remaining;
|
|
15939
|
+
}
|
|
15940
|
+
isFirst = false;
|
|
15941
|
+
}
|
|
15942
|
+
return out;
|
|
15943
|
+
}
|
|
15944
|
+
function buildPat(cc) {
|
|
15945
|
+
const pkt = Buffer.alloc(TS_PACKET_SIZE, 255);
|
|
15946
|
+
pkt[0] = TS_SYNC_BYTE;
|
|
15947
|
+
pkt[1] = 64 | PID_PAT >> 8 & 31;
|
|
15948
|
+
pkt[2] = PID_PAT & 255;
|
|
15949
|
+
pkt[3] = 16 | cc & 15;
|
|
15950
|
+
pkt[4] = 0;
|
|
15951
|
+
const sectionStart = 5;
|
|
15952
|
+
let i = sectionStart;
|
|
15953
|
+
pkt[i++] = 0;
|
|
15954
|
+
pkt[i++] = 176;
|
|
15955
|
+
pkt[i++] = 13;
|
|
15956
|
+
pkt[i++] = 0;
|
|
15957
|
+
pkt[i++] = 1;
|
|
15958
|
+
pkt[i++] = 193;
|
|
15959
|
+
pkt[i++] = 0;
|
|
15960
|
+
pkt[i++] = 0;
|
|
15961
|
+
pkt[i++] = 0;
|
|
15962
|
+
pkt[i++] = 1;
|
|
15963
|
+
pkt[i++] = 224 | PID_PMT >> 8 & 31;
|
|
15964
|
+
pkt[i++] = PID_PMT & 255;
|
|
15965
|
+
const crc = crc32Mpeg(pkt.subarray(sectionStart, i));
|
|
15966
|
+
pkt.writeUInt32BE(crc, i);
|
|
15967
|
+
return pkt;
|
|
15968
|
+
}
|
|
15969
|
+
function buildPmt(videoStreamType, includeAudio, cc) {
|
|
15970
|
+
const pkt = Buffer.alloc(TS_PACKET_SIZE, 255);
|
|
15971
|
+
pkt[0] = TS_SYNC_BYTE;
|
|
15972
|
+
pkt[1] = 64 | PID_PMT >> 8 & 31;
|
|
15973
|
+
pkt[2] = PID_PMT & 255;
|
|
15974
|
+
pkt[3] = 16 | cc & 15;
|
|
15975
|
+
pkt[4] = 0;
|
|
15976
|
+
const sectionStart = 5;
|
|
15977
|
+
let i = sectionStart;
|
|
15978
|
+
pkt[i++] = 2;
|
|
15979
|
+
pkt[i++] = 176;
|
|
15980
|
+
const sectionLenPos = i;
|
|
15981
|
+
i += 1;
|
|
15982
|
+
pkt[i++] = 0;
|
|
15983
|
+
pkt[i++] = 1;
|
|
15984
|
+
pkt[i++] = 193;
|
|
15985
|
+
pkt[i++] = 0;
|
|
15986
|
+
pkt[i++] = 0;
|
|
15987
|
+
pkt[i++] = 224 | PID_VIDEO >> 8 & 31;
|
|
15988
|
+
pkt[i++] = PID_VIDEO & 255;
|
|
15989
|
+
pkt[i++] = 240;
|
|
15990
|
+
pkt[i++] = 0;
|
|
15991
|
+
pkt[i++] = videoStreamType;
|
|
15992
|
+
pkt[i++] = 224 | PID_VIDEO >> 8 & 31;
|
|
15993
|
+
pkt[i++] = PID_VIDEO & 255;
|
|
15994
|
+
pkt[i++] = 240;
|
|
15995
|
+
pkt[i++] = 0;
|
|
15996
|
+
if (includeAudio) {
|
|
15997
|
+
pkt[i++] = STREAM_TYPE_AAC;
|
|
15998
|
+
pkt[i++] = 224 | PID_AUDIO >> 8 & 31;
|
|
15999
|
+
pkt[i++] = PID_AUDIO & 255;
|
|
16000
|
+
pkt[i++] = 240;
|
|
16001
|
+
pkt[i++] = 0;
|
|
16002
|
+
}
|
|
16003
|
+
const sectionLen = i - sectionStart - 3 + 4;
|
|
16004
|
+
pkt[sectionLenPos] = sectionLen;
|
|
16005
|
+
const crc = crc32Mpeg(pkt.subarray(sectionStart, i));
|
|
16006
|
+
pkt.writeUInt32BE(crc, i);
|
|
16007
|
+
return pkt;
|
|
16008
|
+
}
|
|
16009
|
+
function buildVideoPes(annexBData, ptsUs, isKeyframe) {
|
|
16010
|
+
const pts = usToPts(ptsUs);
|
|
16011
|
+
const pesHeader = Buffer.allocUnsafe(14);
|
|
16012
|
+
pesHeader[0] = 0;
|
|
16013
|
+
pesHeader[1] = 0;
|
|
16014
|
+
pesHeader[2] = 1;
|
|
16015
|
+
pesHeader[3] = PES_STREAM_ID_VIDEO;
|
|
16016
|
+
pesHeader[4] = 0;
|
|
16017
|
+
pesHeader[5] = 0;
|
|
16018
|
+
pesHeader[6] = 128 | (isKeyframe ? 4 : 0);
|
|
16019
|
+
pesHeader[7] = 128;
|
|
16020
|
+
pesHeader[8] = 5;
|
|
16021
|
+
encodePts(pesHeader, 9, pts, 2);
|
|
16022
|
+
return Buffer.concat([pesHeader, annexBData]);
|
|
16023
|
+
}
|
|
16024
|
+
function buildAudioPes(adtsData, ptsUs) {
|
|
16025
|
+
const pts = usToPts(ptsUs);
|
|
16026
|
+
const pesPayloadLen = 8 + adtsData.length;
|
|
16027
|
+
const pesHeader = Buffer.allocUnsafe(14);
|
|
16028
|
+
pesHeader[0] = 0;
|
|
16029
|
+
pesHeader[1] = 0;
|
|
16030
|
+
pesHeader[2] = 1;
|
|
16031
|
+
pesHeader[3] = PES_STREAM_ID_AUDIO;
|
|
16032
|
+
pesHeader[4] = pesPayloadLen >> 8 & 255;
|
|
16033
|
+
pesHeader[5] = pesPayloadLen & 255;
|
|
16034
|
+
pesHeader[6] = 128;
|
|
16035
|
+
pesHeader[7] = 128;
|
|
16036
|
+
pesHeader[8] = 5;
|
|
16037
|
+
encodePts(pesHeader, 9, pts, 2);
|
|
16038
|
+
return Buffer.concat([pesHeader, adtsData]);
|
|
16039
|
+
}
|
|
15634
16040
|
var MpegTsMuxer = class {
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
16041
|
+
videoStreamType;
|
|
16042
|
+
includeAudio;
|
|
16043
|
+
// Per-instance continuity counters (4-bit, wrap at 16)
|
|
16044
|
+
patCc = 0;
|
|
16045
|
+
pmtCc = 0;
|
|
16046
|
+
videoCc = 0;
|
|
16047
|
+
audioCc = 0;
|
|
16048
|
+
framesSinceTableSend = 0;
|
|
16049
|
+
tablesSent = false;
|
|
15641
16050
|
constructor(options) {
|
|
15642
|
-
this.
|
|
16051
|
+
this.videoStreamType = options.videoType === "H265" ? STREAM_TYPE_H265 : STREAM_TYPE_H264;
|
|
16052
|
+
this.includeAudio = options.includeAudio ?? true;
|
|
15643
16053
|
}
|
|
15644
16054
|
/**
|
|
15645
|
-
*
|
|
15646
|
-
|
|
15647
|
-
static resetCounters() {
|
|
15648
|
-
patCc = 0;
|
|
15649
|
-
pmtCc = 0;
|
|
15650
|
-
videoCc = 0;
|
|
15651
|
-
}
|
|
15652
|
-
/**
|
|
15653
|
-
* Mux a video frame into MPEG-TS packets.
|
|
16055
|
+
* Mux a video frame (Annex-B H.264 or H.265) into MPEG-TS packets.
|
|
16056
|
+
* PAT and PMT are emitted before keyframes and periodically.
|
|
15654
16057
|
*
|
|
15655
|
-
* @param
|
|
15656
|
-
* @param
|
|
15657
|
-
* @param isKeyframe - Whether this is
|
|
15658
|
-
* @returns Buffer containing all TS packets for this frame
|
|
16058
|
+
* @param annexBData - Annex-B video data (with start codes)
|
|
16059
|
+
* @param ptsUs - Presentation timestamp in microseconds
|
|
16060
|
+
* @param isKeyframe - Whether this is an IDR / IRAP frame
|
|
15659
16061
|
*/
|
|
15660
|
-
|
|
15661
|
-
const
|
|
15662
|
-
|
|
15663
|
-
|
|
15664
|
-
|
|
15665
|
-
this.
|
|
15666
|
-
this.
|
|
15667
|
-
this.
|
|
15668
|
-
|
|
15669
|
-
|
|
15670
|
-
|
|
15671
|
-
|
|
15672
|
-
|
|
16062
|
+
muxVideo(annexBData, ptsUs, isKeyframe) {
|
|
16063
|
+
const chunks = [];
|
|
16064
|
+
const needTables = !this.tablesSent || isKeyframe || this.framesSinceTableSend >= PAT_PMT_INTERVAL;
|
|
16065
|
+
if (needTables) {
|
|
16066
|
+
chunks.push(buildPat(this.patCc));
|
|
16067
|
+
this.patCc = this.patCc + 1 & 15;
|
|
16068
|
+
chunks.push(buildPmt(this.videoStreamType, this.includeAudio, this.pmtCc));
|
|
16069
|
+
this.pmtCc = this.pmtCc + 1 & 15;
|
|
16070
|
+
this.tablesSent = true;
|
|
16071
|
+
this.framesSinceTableSend = 0;
|
|
16072
|
+
}
|
|
16073
|
+
this.framesSinceTableSend++;
|
|
16074
|
+
const pes = buildVideoPes(annexBData, ptsUs, isKeyframe);
|
|
16075
|
+
const ccRef = { cc: this.videoCc };
|
|
16076
|
+
chunks.push(pesToTsPackets(pes, PID_VIDEO, ccRef, isKeyframe));
|
|
16077
|
+
this.videoCc = ccRef.cc;
|
|
16078
|
+
return Buffer.concat(chunks);
|
|
16079
|
+
}
|
|
16080
|
+
/**
|
|
16081
|
+
* Mux an audio frame (ADTS AAC) into MPEG-TS packets.
|
|
16082
|
+
* Returns an empty Buffer when includeAudio is false.
|
|
16083
|
+
*
|
|
16084
|
+
* @param adtsData - Raw ADTS AAC frame (starting with 0xFF 0xF1/0xF9 syncword)
|
|
16085
|
+
* @param ptsUs - Presentation timestamp in microseconds
|
|
16086
|
+
*/
|
|
16087
|
+
muxAudio(adtsData, ptsUs) {
|
|
16088
|
+
if (!this.includeAudio || adtsData.length === 0) return Buffer.alloc(0);
|
|
16089
|
+
const pes = buildAudioPes(adtsData, ptsUs);
|
|
16090
|
+
const ccRef = { cc: this.audioCc };
|
|
16091
|
+
const result = pesToTsPackets(pes, PID_AUDIO, ccRef, false);
|
|
16092
|
+
this.audioCc = ccRef.cc;
|
|
16093
|
+
return result;
|
|
16094
|
+
}
|
|
16095
|
+
/** Reset all continuity counters and table state (e.g. after stream restart). */
|
|
16096
|
+
reset() {
|
|
16097
|
+
this.patCc = 0;
|
|
16098
|
+
this.pmtCc = 0;
|
|
16099
|
+
this.videoCc = 0;
|
|
16100
|
+
this.audioCc = 0;
|
|
16101
|
+
this.framesSinceTableSend = 0;
|
|
16102
|
+
this.tablesSent = false;
|
|
15673
16103
|
}
|
|
15674
16104
|
};
|
|
15675
16105
|
|
|
@@ -16103,6 +16533,53 @@ var getAiStateViaGetAiAlarm = async (params) => {
|
|
|
16103
16533
|
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr ?? "getAiState failed"));
|
|
16104
16534
|
};
|
|
16105
16535
|
|
|
16536
|
+
// src/reolink/baichuan/utils/sleepInference.ts
|
|
16537
|
+
function decideSleepInferenceTransition(input) {
|
|
16538
|
+
const { inferred, committed, pending, hysteresisPolls } = input;
|
|
16539
|
+
if (committed === void 0) {
|
|
16540
|
+
return {
|
|
16541
|
+
emit: inferred === "sleeping" ? "sleeping" : null,
|
|
16542
|
+
nextCommitted: inferred,
|
|
16543
|
+
nextPending: void 0
|
|
16544
|
+
};
|
|
16545
|
+
}
|
|
16546
|
+
if (inferred === committed) {
|
|
16547
|
+
return {
|
|
16548
|
+
emit: null,
|
|
16549
|
+
nextCommitted: committed,
|
|
16550
|
+
nextPending: void 0
|
|
16551
|
+
};
|
|
16552
|
+
}
|
|
16553
|
+
const effectivePolls = Math.max(1, hysteresisPolls);
|
|
16554
|
+
if (!pending || pending.state !== inferred) {
|
|
16555
|
+
if (effectivePolls <= 1) {
|
|
16556
|
+
return {
|
|
16557
|
+
emit: inferred === "sleeping" ? "sleeping" : "awake",
|
|
16558
|
+
nextCommitted: inferred,
|
|
16559
|
+
nextPending: void 0
|
|
16560
|
+
};
|
|
16561
|
+
}
|
|
16562
|
+
return {
|
|
16563
|
+
emit: null,
|
|
16564
|
+
nextCommitted: committed,
|
|
16565
|
+
nextPending: { state: inferred, count: 1 }
|
|
16566
|
+
};
|
|
16567
|
+
}
|
|
16568
|
+
const nextCount = pending.count + 1;
|
|
16569
|
+
if (nextCount < effectivePolls) {
|
|
16570
|
+
return {
|
|
16571
|
+
emit: null,
|
|
16572
|
+
nextCommitted: committed,
|
|
16573
|
+
nextPending: { state: inferred, count: nextCount }
|
|
16574
|
+
};
|
|
16575
|
+
}
|
|
16576
|
+
return {
|
|
16577
|
+
emit: inferred === "sleeping" ? "sleeping" : "awake",
|
|
16578
|
+
nextCommitted: inferred,
|
|
16579
|
+
nextPending: void 0
|
|
16580
|
+
};
|
|
16581
|
+
}
|
|
16582
|
+
|
|
16106
16583
|
// src/reolink/baichuan/utils/channelInfoPush.ts
|
|
16107
16584
|
init_xml();
|
|
16108
16585
|
var parseOptionalInt = (value) => {
|
|
@@ -18379,7 +18856,17 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18379
18856
|
statePollingInterval;
|
|
18380
18857
|
udpSleepInferenceInterval;
|
|
18381
18858
|
udpLastInferredSleepStateByChannel = /* @__PURE__ */ new Map();
|
|
18859
|
+
/**
|
|
18860
|
+
* Per-channel pending sleep-state candidate for hysteresis.
|
|
18861
|
+
* When the inference flips to a new state we require N consecutive polls
|
|
18862
|
+
* of that same state before committing it — this filters out transient
|
|
18863
|
+
* flapping caused by non-waking traffic drifting in/out of the 10 s
|
|
18864
|
+
* getSleepStatus() observation window during stream teardown.
|
|
18865
|
+
*/
|
|
18866
|
+
udpPendingSleepStateByChannel = /* @__PURE__ */ new Map();
|
|
18382
18867
|
udpSleepInferenceIntervalMs = 2e3;
|
|
18868
|
+
/** Consecutive inference polls required to commit a new sleeping/awake state. */
|
|
18869
|
+
udpSleepInferenceHysteresisPolls = 2;
|
|
18383
18870
|
lastMotionState;
|
|
18384
18871
|
lastAiState;
|
|
18385
18872
|
aiStatePollingDisabled = false;
|
|
@@ -18846,6 +19333,8 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18846
19333
|
*/
|
|
18847
19334
|
attachD2cDiscListener(client) {
|
|
18848
19335
|
client.on("d2c_disc", () => this.notifyD2cDisc());
|
|
19336
|
+
client.on("error", () => {
|
|
19337
|
+
});
|
|
18849
19338
|
}
|
|
18850
19339
|
/**
|
|
18851
19340
|
* Acquire a socket from the pool by tag.
|
|
@@ -18964,6 +19453,11 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18964
19453
|
const clientOpts = log ? { ...this.clientOptions, logger: log } : this.clientOptions;
|
|
18965
19454
|
const newClient = new BaichuanClient(clientOpts);
|
|
18966
19455
|
this.attachD2cDiscListener(newClient);
|
|
19456
|
+
newClient.on("error", (err) => {
|
|
19457
|
+
log?.debug?.(
|
|
19458
|
+
`[SocketPool] tag=${tag} client error: ${err?.message ?? err}`
|
|
19459
|
+
);
|
|
19460
|
+
});
|
|
18967
19461
|
await newClient.login();
|
|
18968
19462
|
const existingCooldown = this.socketPoolCooldowns.get(this.host);
|
|
18969
19463
|
if (existingCooldown) {
|
|
@@ -19479,6 +19973,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19479
19973
|
* Only counts sessions from our own IP address.
|
|
19480
19974
|
*/
|
|
19481
19975
|
async maybeRebootOnTooManySessions() {
|
|
19976
|
+
if (!this.client.isSocketConnected?.()) return;
|
|
19482
19977
|
const threshold = this.maxDedicatedSessionsBeforeReboot ?? 10;
|
|
19483
19978
|
if (this.sessionGuardRebootInFlight) return;
|
|
19484
19979
|
const cooldownMs = 10 * 6e4;
|
|
@@ -19920,6 +20415,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
19920
20415
|
}
|
|
19921
20416
|
async renewSimpleEventSubscription() {
|
|
19922
20417
|
if (this.simpleEventListeners.size === 0) return;
|
|
20418
|
+
if (!this.client.isSocketConnected?.()) return;
|
|
19923
20419
|
if (this.simpleEventResubscribeInFlight)
|
|
19924
20420
|
return await this.simpleEventResubscribeInFlight;
|
|
19925
20421
|
this.simpleEventResubscribeInFlight = (async () => {
|
|
@@ -23687,23 +24183,32 @@ ${stderr}`)
|
|
|
23687
24183
|
return;
|
|
23688
24184
|
}
|
|
23689
24185
|
const channel = this.client.getConfiguredChannel?.() ?? 0;
|
|
24186
|
+
if (!this.client.isSocketConnected?.()) {
|
|
24187
|
+
this.udpPendingSleepStateByChannel.delete(channel);
|
|
24188
|
+
return;
|
|
24189
|
+
}
|
|
23690
24190
|
const status = this.getSleepStatus({ channel });
|
|
23691
24191
|
if (status.state === "unknown") return;
|
|
23692
|
-
const
|
|
23693
|
-
this.
|
|
23694
|
-
|
|
23695
|
-
|
|
23696
|
-
|
|
23697
|
-
|
|
23698
|
-
|
|
23699
|
-
|
|
23700
|
-
|
|
23701
|
-
|
|
23702
|
-
|
|
24192
|
+
const committed = this.udpLastInferredSleepStateByChannel.get(channel);
|
|
24193
|
+
const pending = this.udpPendingSleepStateByChannel.get(channel);
|
|
24194
|
+
const decision = decideSleepInferenceTransition({
|
|
24195
|
+
inferred: status.state,
|
|
24196
|
+
committed,
|
|
24197
|
+
pending,
|
|
24198
|
+
hysteresisPolls: this.udpSleepInferenceHysteresisPolls
|
|
24199
|
+
});
|
|
24200
|
+
this.udpLastInferredSleepStateByChannel.set(
|
|
24201
|
+
channel,
|
|
24202
|
+
decision.nextCommitted
|
|
24203
|
+
);
|
|
24204
|
+
if (decision.nextPending === void 0) {
|
|
24205
|
+
this.udpPendingSleepStateByChannel.delete(channel);
|
|
24206
|
+
} else {
|
|
24207
|
+
this.udpPendingSleepStateByChannel.set(channel, decision.nextPending);
|
|
23703
24208
|
}
|
|
23704
|
-
if (
|
|
24209
|
+
if (decision.emit) {
|
|
23705
24210
|
this.dispatchSimpleEvent({
|
|
23706
|
-
type:
|
|
24211
|
+
type: decision.emit,
|
|
23707
24212
|
channel,
|
|
23708
24213
|
timestamp: Date.now()
|
|
23709
24214
|
});
|
|
@@ -23726,6 +24231,7 @@ ${stderr}`)
|
|
|
23726
24231
|
this.udpSleepInferenceInterval = void 0;
|
|
23727
24232
|
}
|
|
23728
24233
|
this.udpLastInferredSleepStateByChannel.clear();
|
|
24234
|
+
this.udpPendingSleepStateByChannel.clear();
|
|
23729
24235
|
}
|
|
23730
24236
|
/**
|
|
23731
24237
|
* GetEvents via Baichuan (legacy - use subscribeEvents for real-time events).
|
|
@@ -26156,6 +26662,373 @@ ${xml}`
|
|
|
26156
26662
|
await this.cgiApi.login();
|
|
26157
26663
|
return await this.cgiApi.getAllChannelsEvents(options);
|
|
26158
26664
|
}
|
|
26665
|
+
// ====================================================================
|
|
26666
|
+
// Native Baichuan tunable-settings setters
|
|
26667
|
+
//
|
|
26668
|
+
// Replace the CGI passthroughs above with on-wire Baichuan binary
|
|
26669
|
+
// calls. Mirrors the @http_cmd-decorated methods in reolink_aio's
|
|
26670
|
+
// baichuan.py — every command has a documented `cmd_id` (read) and
|
|
26671
|
+
// `cmd_id` (write) pair. The pattern is:
|
|
26672
|
+
//
|
|
26673
|
+
// 1. read XML via `sendXml({ cmdId: GET, channel })`
|
|
26674
|
+
// 2. patch fields via regex (camera firmware is XML-strict; using
|
|
26675
|
+
// the parser would force us to rebuild the document and risk
|
|
26676
|
+
// losing unmodified attributes / element order).
|
|
26677
|
+
// 3. write back via `sendXml({ cmdId: SET, channel, payloadXml })`
|
|
26678
|
+
//
|
|
26679
|
+
// All getters parse via `parseXmlFragmentToJson` so the consumer gets
|
|
26680
|
+
// a clean JSON object instead of XML.
|
|
26681
|
+
// ====================================================================
|
|
26682
|
+
/**
|
|
26683
|
+
* GetEnc via Baichuan (cmdId=56). Returns the `<Compression>` block:
|
|
26684
|
+
* per-stream `mainStream` / `subStream` / `thirdStream` with `audio`
|
|
26685
|
+
* flag, `width`, `height`, `frame` (NOT `frameRate`), `bitRate`,
|
|
26686
|
+
* `videoEncType` (0=h264, 1=h265), `encoderProfile`, `gop`. Mirrors
|
|
26687
|
+
* reolink_aio's `GetEnc` — note the wire payload wraps everything
|
|
26688
|
+
* in `Compression`, not `Enc`.
|
|
26689
|
+
*/
|
|
26690
|
+
async getEnc(channel, options) {
|
|
26691
|
+
const xml = await this.sendPcapDerivedSettingsGetXml({
|
|
26692
|
+
cmdId: BC_CMD_ID_GET_ENC,
|
|
26693
|
+
...channel != null ? { channel } : {},
|
|
26694
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26695
|
+
});
|
|
26696
|
+
return parseXmlFragmentToJson(xml);
|
|
26697
|
+
}
|
|
26698
|
+
/**
|
|
26699
|
+
* SetEnc via Baichuan (cmdId=57). Read-modify-write — preserves
|
|
26700
|
+
* unspecified fields. Mirrors reolink_aio's `SetEnc`.
|
|
26701
|
+
*
|
|
26702
|
+
* @param channel - Channel number (0-based)
|
|
26703
|
+
* @param patch - Fields to update on `mainStream` and/or `subStream`,
|
|
26704
|
+
* plus a top-level `audio` toggle (0/1). Pass only what you want
|
|
26705
|
+
* to change.
|
|
26706
|
+
*/
|
|
26707
|
+
async setEnc(channel, patch, options) {
|
|
26708
|
+
const ch = this.normalizeChannel(channel);
|
|
26709
|
+
let xml = await this.sendXml({
|
|
26710
|
+
cmdId: BC_CMD_ID_GET_ENC,
|
|
26711
|
+
channel: ch,
|
|
26712
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26713
|
+
});
|
|
26714
|
+
if (patch.audio !== void 0) {
|
|
26715
|
+
xml = xml.replace(
|
|
26716
|
+
/<audio>[^<]*<\/audio>/g,
|
|
26717
|
+
`<audio>${patch.audio}</audio>`
|
|
26718
|
+
);
|
|
26719
|
+
}
|
|
26720
|
+
xml = applyStreamPatch(xml, "mainStream", patch.mainStream);
|
|
26721
|
+
xml = applyStreamPatch(xml, "subStream", patch.subStream);
|
|
26722
|
+
await this.sendXml({
|
|
26723
|
+
cmdId: BC_CMD_ID_SET_ENC,
|
|
26724
|
+
channel: ch,
|
|
26725
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26726
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26727
|
+
});
|
|
26728
|
+
}
|
|
26729
|
+
/**
|
|
26730
|
+
* SetImage via Baichuan (cmdId=25, read via cmdId=26). Patches the
|
|
26731
|
+
* `<VideoInput>` block: bright / contrast / saturation / hue /
|
|
26732
|
+
* sharpen. Mirrors reolink_aio's `SetImage`.
|
|
26733
|
+
*/
|
|
26734
|
+
async setImage(channel, patch, options) {
|
|
26735
|
+
const ch = this.normalizeChannel(channel);
|
|
26736
|
+
let xml = await this.sendXml({
|
|
26737
|
+
cmdId: BC_CMD_ID_GET_VIDEO_INPUT,
|
|
26738
|
+
channel: ch,
|
|
26739
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26740
|
+
});
|
|
26741
|
+
xml = applyXmlTagPatch(xml, "bright", patch.bright);
|
|
26742
|
+
xml = applyXmlTagPatch(xml, "contrast", patch.contrast);
|
|
26743
|
+
xml = applyXmlTagPatch(xml, "saturation", patch.saturation);
|
|
26744
|
+
xml = applyXmlTagPatch(xml, "hue", patch.hue);
|
|
26745
|
+
xml = applyXmlTagPatch(xml, "sharpen", patch.sharpen);
|
|
26746
|
+
await this.sendXml({
|
|
26747
|
+
cmdId: BC_CMD_ID_SET_VIDEO_INPUT,
|
|
26748
|
+
channel: ch,
|
|
26749
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26750
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26751
|
+
});
|
|
26752
|
+
}
|
|
26753
|
+
/**
|
|
26754
|
+
* SetIsp via Baichuan (cmdId=25 for image side, cmdId=297 for
|
|
26755
|
+
* dayNightThreshold). Patches the `<InputAdvanceCfg>` block:
|
|
26756
|
+
* `DayNight/mode`, `Exposure/mode`, `binning_mode`, `hdrSwitch`.
|
|
26757
|
+
* Mirrors reolink_aio's `SetIsp`.
|
|
26758
|
+
*
|
|
26759
|
+
* @param channel - Channel number (0-based)
|
|
26760
|
+
* @param patch - Fields to update. `dayNight` accepts the camera's
|
|
26761
|
+
* raw enum (`color`, `auto`, `blackAndWhite`, …) — pass it as the
|
|
26762
|
+
* camera reports it (PascalCase / dotted forms get normalized
|
|
26763
|
+
* server-side).
|
|
26764
|
+
*/
|
|
26765
|
+
async setIsp(channel, patch, options) {
|
|
26766
|
+
const ch = this.normalizeChannel(channel);
|
|
26767
|
+
const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
|
|
26768
|
+
const wantsImageWrite = patch.dayNight !== void 0 || patch.exposure !== void 0 || patch.binningMode !== void 0 || patch.hdr !== void 0;
|
|
26769
|
+
if (wantsImageWrite) {
|
|
26770
|
+
let xml = await this.sendXml({
|
|
26771
|
+
cmdId: BC_CMD_ID_GET_VIDEO_INPUT,
|
|
26772
|
+
channel: ch,
|
|
26773
|
+
...timeoutOpts
|
|
26774
|
+
});
|
|
26775
|
+
if (patch.dayNight !== void 0) {
|
|
26776
|
+
const normalized = normalizeDayNightMode(patch.dayNight);
|
|
26777
|
+
xml = patchNestedTag(xml, "DayNight", "mode", normalized);
|
|
26778
|
+
}
|
|
26779
|
+
if (patch.exposure !== void 0) {
|
|
26780
|
+
xml = patchNestedTag(
|
|
26781
|
+
xml,
|
|
26782
|
+
"Exposure",
|
|
26783
|
+
"mode",
|
|
26784
|
+
patch.exposure.toLowerCase()
|
|
26785
|
+
);
|
|
26786
|
+
}
|
|
26787
|
+
if (patch.binningMode !== void 0) {
|
|
26788
|
+
xml = applyXmlTagPatch(xml, "binning_mode", patch.binningMode);
|
|
26789
|
+
}
|
|
26790
|
+
if (patch.hdr !== void 0) {
|
|
26791
|
+
xml = applyXmlTagPatch(xml, "hdrSwitch", patch.hdr);
|
|
26792
|
+
}
|
|
26793
|
+
await this.sendXml({
|
|
26794
|
+
cmdId: BC_CMD_ID_SET_VIDEO_INPUT,
|
|
26795
|
+
channel: ch,
|
|
26796
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26797
|
+
...timeoutOpts
|
|
26798
|
+
});
|
|
26799
|
+
}
|
|
26800
|
+
if (patch.dayNightThreshold !== void 0) {
|
|
26801
|
+
let xml = await this.sendXml({
|
|
26802
|
+
cmdId: BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
|
|
26803
|
+
channel: ch,
|
|
26804
|
+
...timeoutOpts
|
|
26805
|
+
});
|
|
26806
|
+
xml = applyXmlTagPatch(xml, "cur", patch.dayNightThreshold);
|
|
26807
|
+
await this.sendXml({
|
|
26808
|
+
cmdId: BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD,
|
|
26809
|
+
channel: ch,
|
|
26810
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26811
|
+
...timeoutOpts
|
|
26812
|
+
});
|
|
26813
|
+
}
|
|
26814
|
+
}
|
|
26815
|
+
/**
|
|
26816
|
+
* GetIsp via Baichuan (cmdId=26). Convenience alias of
|
|
26817
|
+
* `getVideoInput()` so callers that switched from CGI keep the
|
|
26818
|
+
* familiar name. Both return the merged VideoInput +
|
|
26819
|
+
* InputAdvanceCfg blob.
|
|
26820
|
+
*/
|
|
26821
|
+
async getIsp(channel, options) {
|
|
26822
|
+
return this.getVideoInput(channel, options);
|
|
26823
|
+
}
|
|
26824
|
+
/** GetImage via Baichuan (cmdId=26). Same payload as `getIsp` —
|
|
26825
|
+
* Reolink merged VideoInput + InputAdvanceCfg under one cmdId. */
|
|
26826
|
+
async getImage(channel, options) {
|
|
26827
|
+
return this.getVideoInput(channel, options);
|
|
26828
|
+
}
|
|
26829
|
+
/**
|
|
26830
|
+
* GetIrLights via Baichuan (cmdId=208). Returns LedState block:
|
|
26831
|
+
* `IRLedBrightness`, `state` (ir on/off), `lightState` (status LED
|
|
26832
|
+
* open/close), `doorbellLightState`. Mirrors reolink_aio's
|
|
26833
|
+
* `get_status_led`.
|
|
26834
|
+
*/
|
|
26835
|
+
async getIrLights(channel, options) {
|
|
26836
|
+
const xml = await this.sendPcapDerivedSettingsGetXml({
|
|
26837
|
+
cmdId: BC_CMD_ID_GET_LED_STATE,
|
|
26838
|
+
...channel != null ? { channel } : {},
|
|
26839
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26840
|
+
});
|
|
26841
|
+
return parseXmlFragmentToJson(xml);
|
|
26842
|
+
}
|
|
26843
|
+
/**
|
|
26844
|
+
* SetIrLights via Baichuan (cmdId=209, read via cmdId=208). Patches
|
|
26845
|
+
* IR LED + status LED + doorbell LED + IR brightness. Mirrors
|
|
26846
|
+
* reolink_aio's `set_status_led`.
|
|
26847
|
+
*
|
|
26848
|
+
* @param channel - Channel number (0-based)
|
|
26849
|
+
* @param patch - `irState` ("On" | "Off" | "Auto"), `lightState`
|
|
26850
|
+
* (status LED), `doorbellLightState`, `irBrightness` (0..255).
|
|
26851
|
+
* Camera-side accepts lowercase strings (`open`/`close`); the
|
|
26852
|
+
* helper normalizes from the friendly variants.
|
|
26853
|
+
*/
|
|
26854
|
+
async setIrLights(channel, patch, options) {
|
|
26855
|
+
const ch = this.normalizeChannel(channel);
|
|
26856
|
+
const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
|
|
26857
|
+
let xml = await this.sendXml({
|
|
26858
|
+
cmdId: BC_CMD_ID_GET_LED_STATE,
|
|
26859
|
+
channel: ch,
|
|
26860
|
+
...timeoutOpts
|
|
26861
|
+
});
|
|
26862
|
+
if (patch.lightState !== void 0) {
|
|
26863
|
+
xml = applyXmlTagPatch(
|
|
26864
|
+
xml,
|
|
26865
|
+
"lightState",
|
|
26866
|
+
patch.lightState === "On" ? "open" : "close"
|
|
26867
|
+
);
|
|
26868
|
+
}
|
|
26869
|
+
if (patch.doorbellLightState !== void 0) {
|
|
26870
|
+
xml = applyXmlTagPatch(
|
|
26871
|
+
xml,
|
|
26872
|
+
"doorbellLightState",
|
|
26873
|
+
normalizeOpenClose(patch.doorbellLightState)
|
|
26874
|
+
);
|
|
26875
|
+
}
|
|
26876
|
+
if (patch.irState !== void 0) {
|
|
26877
|
+
const v = String(patch.irState);
|
|
26878
|
+
const out = v === "Off" ? "close" : v.toLowerCase();
|
|
26879
|
+
xml = applyXmlTagPatch(xml, "state", out);
|
|
26880
|
+
}
|
|
26881
|
+
if (patch.irBrightness !== void 0) {
|
|
26882
|
+
xml = applyXmlTagPatch(xml, "IRLedBrightness", patch.irBrightness);
|
|
26883
|
+
}
|
|
26884
|
+
await this.sendXml({
|
|
26885
|
+
cmdId: BC_CMD_ID_SET_LED_STATE,
|
|
26886
|
+
channel: ch,
|
|
26887
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26888
|
+
...timeoutOpts
|
|
26889
|
+
});
|
|
26890
|
+
}
|
|
26891
|
+
/**
|
|
26892
|
+
* SetAudioCfg via Baichuan (cmdId=265, read via cmdId=264). Patches
|
|
26893
|
+
* volume / talk-and-reply / visitor settings. Mirrors reolink_aio's
|
|
26894
|
+
* `SetAudioCfg`.
|
|
26895
|
+
*/
|
|
26896
|
+
async setAudioCfg(channel, patch, options) {
|
|
26897
|
+
const ch = this.normalizeChannel(channel);
|
|
26898
|
+
const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
|
|
26899
|
+
let xml = await this.sendXml({
|
|
26900
|
+
cmdId: BC_CMD_ID_GET_AUDIO_CFG,
|
|
26901
|
+
channel: ch,
|
|
26902
|
+
...timeoutOpts
|
|
26903
|
+
});
|
|
26904
|
+
xml = applyXmlTagPatch(xml, "volume", patch.volume);
|
|
26905
|
+
xml = applyXmlTagPatch(
|
|
26906
|
+
xml,
|
|
26907
|
+
"talkAndReplyVolume",
|
|
26908
|
+
patch.talkAndReplyVolume
|
|
26909
|
+
);
|
|
26910
|
+
xml = applyXmlTagPatch(xml, "visitorVolume", patch.visitorVolume);
|
|
26911
|
+
xml = applyXmlTagPatch(xml, "visitorLoudspeaker", patch.visitorLoudspeaker);
|
|
26912
|
+
await this.sendXml({
|
|
26913
|
+
cmdId: BC_CMD_ID_SET_AUDIO_CFG,
|
|
26914
|
+
channel: ch,
|
|
26915
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26916
|
+
...timeoutOpts
|
|
26917
|
+
});
|
|
26918
|
+
}
|
|
26919
|
+
/**
|
|
26920
|
+
* GetMask (privacy mask) via Baichuan (cmdId=52). Returns the
|
|
26921
|
+
* `<Shelter>` block — `enable` flag + `shelterList`. Mirrors
|
|
26922
|
+
* reolink_aio's `GetMask`.
|
|
26923
|
+
*/
|
|
26924
|
+
async getMask(channel, options) {
|
|
26925
|
+
const xml = await this.sendPcapDerivedSettingsGetXml({
|
|
26926
|
+
cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
|
|
26927
|
+
...channel != null ? { channel } : {},
|
|
26928
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26929
|
+
});
|
|
26930
|
+
return parseXmlFragmentToJson(xml);
|
|
26931
|
+
}
|
|
26932
|
+
/**
|
|
26933
|
+
* SetMask (privacy mask) via Baichuan (cmdId=53, read via cmdId=52).
|
|
26934
|
+
* Toggles the `<Shelter><enable>` flag. Mirrors reolink_aio's
|
|
26935
|
+
* `SetMask` (which only touches enable too — shelter zone editing
|
|
26936
|
+
* goes through a separate flow).
|
|
26937
|
+
*/
|
|
26938
|
+
async setMask(channel, patch, options) {
|
|
26939
|
+
const ch = this.normalizeChannel(channel);
|
|
26940
|
+
const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
|
|
26941
|
+
let xml = await this.sendXml({
|
|
26942
|
+
cmdId: BC_CMD_ID_GET_PRIVACY_MASK,
|
|
26943
|
+
channel: ch,
|
|
26944
|
+
...timeoutOpts
|
|
26945
|
+
});
|
|
26946
|
+
if (patch.enable !== void 0) {
|
|
26947
|
+
xml = applyXmlTagPatch(xml, "enable", patch.enable ? 1 : 0);
|
|
26948
|
+
}
|
|
26949
|
+
await this.sendXml({
|
|
26950
|
+
cmdId: BC_CMD_ID_SET_PRIVACY_MASK,
|
|
26951
|
+
channel: ch,
|
|
26952
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26953
|
+
...timeoutOpts
|
|
26954
|
+
});
|
|
26955
|
+
}
|
|
26956
|
+
/**
|
|
26957
|
+
* GetAudioNoise via Baichuan (cmdId=439). Reads `enable` + `level`
|
|
26958
|
+
* from the aiDenoise block. Mirrors reolink_aio's `GetAudioNoise`.
|
|
26959
|
+
*
|
|
26960
|
+
* Note: `getAiDenoise` already returns the same payload typed as
|
|
26961
|
+
* `AiDenoiseConfig`. This getter exists for naming parity with
|
|
26962
|
+
* reolink_aio + the reolink CGI.
|
|
26963
|
+
*/
|
|
26964
|
+
async getAudioNoise(channel, options) {
|
|
26965
|
+
const xml = await this.sendPcapDerivedSettingsGetXml({
|
|
26966
|
+
cmdId: BC_CMD_ID_GET_AI_DENOISE,
|
|
26967
|
+
...channel != null ? { channel } : {},
|
|
26968
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
26969
|
+
});
|
|
26970
|
+
return parseXmlFragmentToJson(xml);
|
|
26971
|
+
}
|
|
26972
|
+
/**
|
|
26973
|
+
* SetAudioNoise via Baichuan (cmdId=440, read via cmdId=439).
|
|
26974
|
+
* Mirrors reolink_aio's `SetAudioNoise` — `level <= 0` flips the
|
|
26975
|
+
* enable flag off; positive values turn it on and update the level.
|
|
26976
|
+
*/
|
|
26977
|
+
async setAudioNoise(channel, level, options) {
|
|
26978
|
+
const ch = this.normalizeChannel(channel);
|
|
26979
|
+
const timeoutOpts = options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {};
|
|
26980
|
+
let xml = await this.sendXml({
|
|
26981
|
+
cmdId: BC_CMD_ID_GET_AI_DENOISE,
|
|
26982
|
+
channel: ch,
|
|
26983
|
+
...timeoutOpts
|
|
26984
|
+
});
|
|
26985
|
+
xml = applyXmlTagPatch(xml, "enable", level > 0 ? 1 : 0);
|
|
26986
|
+
if (level > 0) {
|
|
26987
|
+
xml = applyXmlTagPatch(xml, "level", level);
|
|
26988
|
+
}
|
|
26989
|
+
await this.sendXml({
|
|
26990
|
+
cmdId: BC_CMD_ID_SET_AI_DENOISE,
|
|
26991
|
+
channel: ch,
|
|
26992
|
+
payloadXml: ensureXmlHeader(xml),
|
|
26993
|
+
...timeoutOpts
|
|
26994
|
+
});
|
|
26995
|
+
}
|
|
26996
|
+
/**
|
|
26997
|
+
* GetAutoFocus via Baichuan (cmdId=224). Returns the `<AutoFocus>`
|
|
26998
|
+
* block — only `disable` (0 = AF on, 1 = AF off). Mirrors
|
|
26999
|
+
* reolink_aio's `GetAutoFocus`.
|
|
27000
|
+
*/
|
|
27001
|
+
async getAutoFocus(channel, options) {
|
|
27002
|
+
const ch = this.normalizeChannel(channel);
|
|
27003
|
+
const xml = await this.sendPcapDerivedSettingsGetXml({
|
|
27004
|
+
cmdId: BC_CMD_ID_GET_AUTO_FOCUS,
|
|
27005
|
+
channel: ch,
|
|
27006
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
27007
|
+
});
|
|
27008
|
+
return parseXmlFragmentToJson(xml);
|
|
27009
|
+
}
|
|
27010
|
+
/**
|
|
27011
|
+
* SetAutoFocus via Baichuan (cmdId=225). Mirrors reolink_aio's
|
|
27012
|
+
* `SetAutoFocus`. Note: write-only command — the payload is built
|
|
27013
|
+
* from scratch (no read-modify-write needed).
|
|
27014
|
+
*/
|
|
27015
|
+
async setAutoFocus(channel, disable, options) {
|
|
27016
|
+
const ch = this.normalizeChannel(channel);
|
|
27017
|
+
const disableVal = disable ? 1 : 0;
|
|
27018
|
+
const payloadXml = `<?xml version="1.0" encoding="UTF-8" ?>
|
|
27019
|
+
<body>
|
|
27020
|
+
<AutoFocus version="1.1">
|
|
27021
|
+
<channelId>${ch}</channelId>
|
|
27022
|
+
<disable>${disableVal}</disable>
|
|
27023
|
+
</AutoFocus>
|
|
27024
|
+
</body>`;
|
|
27025
|
+
await this.sendXml({
|
|
27026
|
+
cmdId: BC_CMD_ID_SET_AUTO_FOCUS,
|
|
27027
|
+
channel: ch,
|
|
27028
|
+
payloadXml,
|
|
27029
|
+
...options?.timeoutMs != null ? { timeoutMs: options.timeoutMs } : {}
|
|
27030
|
+
});
|
|
27031
|
+
}
|
|
26159
27032
|
/**
|
|
26160
27033
|
* Passthrough to ReolinkCgiApi.getAllChannelsBatteryInfo.
|
|
26161
27034
|
* Fetches battery info for all channels via CGI (merged with channel status sleep flag).
|
|
@@ -27324,8 +28197,8 @@ ${scheduleItems}
|
|
|
27324
28197
|
);
|
|
27325
28198
|
let args;
|
|
27326
28199
|
if (useMpegTsMuxer) {
|
|
27327
|
-
MpegTsMuxer
|
|
27328
|
-
tsMuxer
|
|
28200
|
+
tsMuxer = new MpegTsMuxer({ videoType, includeAudio: false });
|
|
28201
|
+
tsMuxer.reset();
|
|
27329
28202
|
args = [
|
|
27330
28203
|
"-hide_banner",
|
|
27331
28204
|
"-loglevel",
|
|
@@ -27489,7 +28362,7 @@ ${scheduleItems}
|
|
|
27489
28362
|
startFfmpeg(videoType);
|
|
27490
28363
|
frameCount++;
|
|
27491
28364
|
if (useMpegTsMuxer && tsMuxer) {
|
|
27492
|
-
const tsData = tsMuxer.
|
|
28365
|
+
const tsData = tsMuxer.muxVideo(data, microseconds, isKeyframe);
|
|
27493
28366
|
input.write(tsData);
|
|
27494
28367
|
} else {
|
|
27495
28368
|
if (videoType === "H264") input.write(H264_AUD);
|
|
@@ -27778,8 +28651,8 @@ ${scheduleItems}
|
|
|
27778
28651
|
logger?.log?.(
|
|
27779
28652
|
`[createRecordingReplayHlsSession] Starting ffmpeg HLS with videoType=${videoType}, transcode=${needsTranscode}, hlsTime=${hlsSegmentDuration}s, fileName=${params.fileName}`
|
|
27780
28653
|
);
|
|
27781
|
-
MpegTsMuxer
|
|
27782
|
-
tsMuxer
|
|
28654
|
+
tsMuxer = new MpegTsMuxer({ videoType, includeAudio: false });
|
|
28655
|
+
tsMuxer.reset();
|
|
27783
28656
|
const args = [
|
|
27784
28657
|
"-hide_banner",
|
|
27785
28658
|
"-loglevel",
|
|
@@ -27944,7 +28817,7 @@ ${scheduleItems}
|
|
|
27944
28817
|
startFfmpeg(videoType);
|
|
27945
28818
|
frameCount++;
|
|
27946
28819
|
if (tsMuxer) {
|
|
27947
|
-
const tsData = tsMuxer.
|
|
28820
|
+
const tsData = tsMuxer.muxVideo(data, microseconds, isKeyframe);
|
|
27948
28821
|
input.write(tsData);
|
|
27949
28822
|
}
|
|
27950
28823
|
if (frameCount === 1) {
|
|
@@ -33736,6 +34609,9 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33736
34609
|
totalVideoFramesWritten = 0;
|
|
33737
34610
|
// Prebuffer
|
|
33738
34611
|
prebuffer = [];
|
|
34612
|
+
// Audio metadata — populated on first valid ADTS AAC frame.
|
|
34613
|
+
// Exposed via getAudioInfo() for the stream-diagnostics feature.
|
|
34614
|
+
audioInfo = null;
|
|
33739
34615
|
constructor(options) {
|
|
33740
34616
|
super();
|
|
33741
34617
|
this.api = options.api;
|
|
@@ -33819,6 +34695,45 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33819
34695
|
return this.connectedClients.size;
|
|
33820
34696
|
}
|
|
33821
34697
|
// -----------------------------------------------------------------------
|
|
34698
|
+
// Diagnostic subscription API (implements DiagnosticStreamServer)
|
|
34699
|
+
//
|
|
34700
|
+
// Matches the shape of BaichuanRtspServer's diagnostic API so the
|
|
34701
|
+
// stream-diagnostic feature in the Manager app can drive either backend
|
|
34702
|
+
// with identical code.
|
|
34703
|
+
// -----------------------------------------------------------------------
|
|
34704
|
+
/**
|
|
34705
|
+
* Subscribe to the raw native stream for diagnostic purposes.
|
|
34706
|
+
* The subscriber receives the same frames the MPEG-TS muxer consumes
|
|
34707
|
+
* (pre-muxing). Counts as a "consumer" so the native stream is kept alive
|
|
34708
|
+
* for the lifetime of the subscription. If the stream is not already
|
|
34709
|
+
* running (battery camera, prestart=false), this starts it.
|
|
34710
|
+
*/
|
|
34711
|
+
async subscribeDiagnostic(id) {
|
|
34712
|
+
this.connectedClients.add(`diag:${id}`);
|
|
34713
|
+
if (!this.nativeStreamActive) {
|
|
34714
|
+
await this.startNativeStream();
|
|
34715
|
+
}
|
|
34716
|
+
if (!this.nativeFanout) {
|
|
34717
|
+
this.connectedClients.delete(`diag:${id}`);
|
|
34718
|
+
throw new Error(
|
|
34719
|
+
"Go2rtcTcpServer: native stream failed to start \u2014 cannot subscribe diagnostic"
|
|
34720
|
+
);
|
|
34721
|
+
}
|
|
34722
|
+
return this.nativeFanout.subscribe(`diag:${id}`);
|
|
34723
|
+
}
|
|
34724
|
+
/** Unsubscribe a diagnostic session and release its consumer slot. */
|
|
34725
|
+
unsubscribeDiagnostic(id) {
|
|
34726
|
+
this.removeClient(`diag:${id}`, "diagnostic unsubscribe");
|
|
34727
|
+
}
|
|
34728
|
+
/**
|
|
34729
|
+
* Returns ADTS AAC audio metadata detected from the native stream, or
|
|
34730
|
+
* null if no audio frame has been observed yet (e.g. video-only cameras
|
|
34731
|
+
* or before the first audio packet arrives).
|
|
34732
|
+
*/
|
|
34733
|
+
getAudioInfo() {
|
|
34734
|
+
return this.audioInfo;
|
|
34735
|
+
}
|
|
34736
|
+
// -----------------------------------------------------------------------
|
|
33822
34737
|
// Client handling
|
|
33823
34738
|
// -----------------------------------------------------------------------
|
|
33824
34739
|
handleClient(socket) {
|
|
@@ -33863,6 +34778,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33863
34778
|
}
|
|
33864
34779
|
if (!this.active || !this.nativeFanout) return;
|
|
33865
34780
|
const subscription = this.nativeFanout.subscribe(clientId);
|
|
34781
|
+
let muxer = null;
|
|
33866
34782
|
const prebufferSnap = this.prebuffer.slice();
|
|
33867
34783
|
let lastIdrIdx = -1;
|
|
33868
34784
|
for (let i = prebufferSnap.length - 1; i >= 0; i--) {
|
|
@@ -33876,9 +34792,21 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33876
34792
|
this.logger.info?.(
|
|
33877
34793
|
`[Go2rtcTcpServer] prebuffer replay client=${clientId} frames=${replay.length}`
|
|
33878
34794
|
);
|
|
34795
|
+
if (!muxer) {
|
|
34796
|
+
muxer = new MpegTsMuxer({
|
|
34797
|
+
videoType: this.detectedVideoType ?? "H264",
|
|
34798
|
+
includeAudio: true
|
|
34799
|
+
});
|
|
34800
|
+
}
|
|
33879
34801
|
for (const entry of replay) {
|
|
33880
34802
|
if (socket.destroyed) return;
|
|
33881
|
-
|
|
34803
|
+
let ts;
|
|
34804
|
+
if (!entry.audio) {
|
|
34805
|
+
ts = muxer.muxVideo(entry.data, entry.pts, entry.isKeyframe);
|
|
34806
|
+
} else {
|
|
34807
|
+
ts = muxer.muxAudio(entry.data, entry.pts);
|
|
34808
|
+
}
|
|
34809
|
+
if (ts.length > 0) socket.write(ts);
|
|
33882
34810
|
}
|
|
33883
34811
|
}
|
|
33884
34812
|
let seenKeyframe = lastIdrIdx >= 0;
|
|
@@ -33897,16 +34825,33 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33897
34825
|
break;
|
|
33898
34826
|
}
|
|
33899
34827
|
liveFrameCount++;
|
|
33900
|
-
|
|
34828
|
+
if (frame.audio) {
|
|
34829
|
+
if (muxer) {
|
|
34830
|
+
const pts2 = frame.microseconds ?? Date.now() * 1e3;
|
|
34831
|
+
const ts2 = muxer.muxAudio(frame.data, pts2);
|
|
34832
|
+
if (ts2.length > 0) socket.write(ts2);
|
|
34833
|
+
}
|
|
34834
|
+
continue;
|
|
34835
|
+
}
|
|
34836
|
+
const annexB = this.convertVideoFrame(frame);
|
|
33901
34837
|
if (!annexB) continue;
|
|
34838
|
+
const isKf = this.isAnnexBKeyframe(annexB, frame.videoType);
|
|
33902
34839
|
if (!seenKeyframe) {
|
|
33903
|
-
if (!
|
|
34840
|
+
if (!isKf) continue;
|
|
33904
34841
|
seenKeyframe = true;
|
|
33905
34842
|
this.logger.info?.(
|
|
33906
34843
|
`[Go2rtcTcpServer] first live keyframe client=${clientId} after ${liveFrameCount} frames`
|
|
33907
34844
|
);
|
|
34845
|
+
if (!muxer) {
|
|
34846
|
+
muxer = new MpegTsMuxer({
|
|
34847
|
+
videoType: frame.videoType ?? this.detectedVideoType ?? "H264",
|
|
34848
|
+
includeAudio: true
|
|
34849
|
+
});
|
|
34850
|
+
}
|
|
33908
34851
|
}
|
|
33909
|
-
|
|
34852
|
+
const pts = frame.microseconds ?? Date.now() * 1e3;
|
|
34853
|
+
const ts = muxer.muxVideo(annexB, pts, isKf);
|
|
34854
|
+
socket.write(ts);
|
|
33910
34855
|
liveVideoWritten++;
|
|
33911
34856
|
this.totalVideoFramesWritten++;
|
|
33912
34857
|
if (Date.now() - lastLogAt > 1e4) {
|
|
@@ -33935,14 +34880,11 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
33935
34880
|
// Frame conversion
|
|
33936
34881
|
// -----------------------------------------------------------------------
|
|
33937
34882
|
/**
|
|
33938
|
-
* Convert a native frame to
|
|
33939
|
-
*
|
|
33940
|
-
* go2rtc auto-detects the codec from SPS/PPS/VPS NALUs.
|
|
34883
|
+
* Convert a native video frame to Annex-B.
|
|
34884
|
+
* Returns null for audio frames (handled separately by muxAudio).
|
|
33941
34885
|
*/
|
|
33942
|
-
|
|
33943
|
-
if (frame.audio)
|
|
33944
|
-
return null;
|
|
33945
|
-
}
|
|
34886
|
+
convertVideoFrame(frame) {
|
|
34887
|
+
if (frame.audio) return null;
|
|
33946
34888
|
if (frame.data.length === 0) return null;
|
|
33947
34889
|
try {
|
|
33948
34890
|
if (frame.videoType === "H264") {
|
|
@@ -34006,10 +34948,71 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
34006
34948
|
return nals;
|
|
34007
34949
|
}
|
|
34008
34950
|
// -----------------------------------------------------------------------
|
|
34951
|
+
// ADTS AAC parsing (used for audio metadata exposed via getAudioInfo)
|
|
34952
|
+
// -----------------------------------------------------------------------
|
|
34953
|
+
/** True if `b` starts with an ADTS AAC syncword (0xFFF). */
|
|
34954
|
+
static isAdtsAacFrame(b) {
|
|
34955
|
+
return b.length >= 2 && b[0] === 255 && (b[1] & 240) === 240;
|
|
34956
|
+
}
|
|
34957
|
+
/**
|
|
34958
|
+
* Parse an ADTS header into {sampleRate, channels, AudioSpecificConfig hex}.
|
|
34959
|
+
* Returns null when the buffer is not a valid ADTS frame.
|
|
34960
|
+
*/
|
|
34961
|
+
static parseAdtsSamplingInfo(b) {
|
|
34962
|
+
if (b.length < 7) return null;
|
|
34963
|
+
if (!_Go2rtcTcpServer.isAdtsAacFrame(b)) return null;
|
|
34964
|
+
const samplingIndex = b[2] >> 2 & 15;
|
|
34965
|
+
const sampleRates = [
|
|
34966
|
+
96e3,
|
|
34967
|
+
88200,
|
|
34968
|
+
64e3,
|
|
34969
|
+
48e3,
|
|
34970
|
+
44100,
|
|
34971
|
+
32e3,
|
|
34972
|
+
24e3,
|
|
34973
|
+
22050,
|
|
34974
|
+
16e3,
|
|
34975
|
+
12e3,
|
|
34976
|
+
11025,
|
|
34977
|
+
8e3,
|
|
34978
|
+
7350
|
|
34979
|
+
];
|
|
34980
|
+
const sampleRate = sampleRates[samplingIndex] ?? null;
|
|
34981
|
+
if (!sampleRate) return null;
|
|
34982
|
+
const channelConfig = (b[2] & 1) << 2 | b[3] >> 6 & 3;
|
|
34983
|
+
const channels = channelConfig === 0 ? 1 : channelConfig;
|
|
34984
|
+
const profile = b[2] >> 6 & 3;
|
|
34985
|
+
const audioObjectType = profile + 1;
|
|
34986
|
+
const asc = audioObjectType << 11 | samplingIndex << 7 | channelConfig << 3;
|
|
34987
|
+
const configHex = Buffer.from([asc >> 8 & 255, asc & 255]).toString(
|
|
34988
|
+
"hex"
|
|
34989
|
+
);
|
|
34990
|
+
return { sampleRate, channels, configHex };
|
|
34991
|
+
}
|
|
34992
|
+
// -----------------------------------------------------------------------
|
|
34009
34993
|
// Native stream management
|
|
34010
34994
|
// -----------------------------------------------------------------------
|
|
34011
34995
|
async startNativeStream() {
|
|
34012
34996
|
if (this.nativeStreamActive) return;
|
|
34997
|
+
if (!this.api.isReady) {
|
|
34998
|
+
if (this.api.isClosed) {
|
|
34999
|
+
this.logger.warn?.(
|
|
35000
|
+
`[Go2rtcTcpServer] API has been explicitly closed \u2014 stream cannot start`
|
|
35001
|
+
);
|
|
35002
|
+
return;
|
|
35003
|
+
}
|
|
35004
|
+
try {
|
|
35005
|
+
this.logger.info?.(
|
|
35006
|
+
`[Go2rtcTcpServer] API not ready (idle disconnect?), calling ensureConnected`
|
|
35007
|
+
);
|
|
35008
|
+
await this.api.ensureConnected();
|
|
35009
|
+
} catch (e) {
|
|
35010
|
+
this.logger.warn?.(
|
|
35011
|
+
`[Go2rtcTcpServer] ensureConnected failed, aborting stream start: ${e}`
|
|
35012
|
+
);
|
|
35013
|
+
return;
|
|
35014
|
+
}
|
|
35015
|
+
}
|
|
34013
35016
|
this.nativeStreamActive = true;
|
|
34014
35017
|
let dedicatedClient;
|
|
34015
35018
|
if (this.deviceId) {
|
|
@@ -34028,6 +35031,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
34028
35031
|
this.logger.info?.(
|
|
34029
35032
|
`[Go2rtcTcpServer] native stream starting channel=${this.channel} profile=${this.profile} dedicated=${!!dedicatedClient}`
|
|
34030
35033
|
);
|
|
35034
|
+
let hadFrames = false;
|
|
34031
35035
|
this.nativeFanout = new NativeStreamFanout2({
|
|
34032
35036
|
maxQueueItems: 200,
|
|
34033
35037
|
createSource: () => createNativeStream(this.api, this.channel, this.profile, {
|
|
@@ -34035,19 +35039,37 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
34035
35039
|
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
34036
35040
|
}),
|
|
34037
35041
|
onFrame: (frame) => {
|
|
35042
|
+
hadFrames = true;
|
|
34038
35043
|
this.lastFrameAt = Date.now();
|
|
34039
35044
|
this.totalFramesReceived++;
|
|
34040
35045
|
if (!frame.audio && (frame.videoType === "H264" || frame.videoType === "H265")) {
|
|
34041
35046
|
this.detectedVideoType = frame.videoType;
|
|
34042
35047
|
}
|
|
34043
|
-
|
|
34044
|
-
|
|
34045
|
-
|
|
35048
|
+
let prebufData;
|
|
35049
|
+
let isKeyframe;
|
|
35050
|
+
if (frame.audio) {
|
|
35051
|
+
if (frame.data.length === 0) return;
|
|
35052
|
+
if (!this.audioInfo) {
|
|
35053
|
+
const parsed = _Go2rtcTcpServer.parseAdtsSamplingInfo(frame.data);
|
|
35054
|
+
if (parsed) {
|
|
35055
|
+
this.audioInfo = { codec: "aac-adts", ...parsed };
|
|
35056
|
+
}
|
|
35057
|
+
}
|
|
35058
|
+
prebufData = frame.data;
|
|
35059
|
+
isKeyframe = false;
|
|
35060
|
+
} else {
|
|
35061
|
+
const annexB = this.convertVideoFrame(frame);
|
|
35062
|
+
if (!annexB || annexB.length === 0) return;
|
|
35063
|
+
prebufData = annexB;
|
|
35064
|
+
isKeyframe = this.isAnnexBKeyframe(annexB, frame.videoType);
|
|
35065
|
+
}
|
|
35066
|
+
const pts = frame.microseconds ?? Date.now() * 1e3;
|
|
34046
35067
|
this.prebuffer.push({
|
|
34047
|
-
data: Buffer.from(
|
|
35068
|
+
data: Buffer.from(prebufData),
|
|
34048
35069
|
time: Date.now(),
|
|
34049
35070
|
isKeyframe,
|
|
34050
|
-
audio: frame.audio
|
|
35071
|
+
audio: frame.audio,
|
|
35072
|
+
pts
|
|
34051
35073
|
});
|
|
34052
35074
|
const cutoff = Date.now() - this.prebufferMaxMs;
|
|
34053
35075
|
let trimIdx = 0;
|
|
@@ -34074,7 +35096,23 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEm
|
|
|
34074
35096
|
});
|
|
34075
35097
|
this.dedicatedSessionRelease = void 0;
|
|
34076
35098
|
}
|
|
34077
|
-
if (this.
|
|
35099
|
+
if (!this.prestartStream) {
|
|
35100
|
+
this.logger.info?.(
|
|
35101
|
+
`[Go2rtcTcpServer] battery native stream ended hadFrames=${hadFrames} channel=${this.channel} profile=${this.profile} \u2014 dropping ${this.connectedClients.size} client(s) to prevent wake loop`
|
|
35102
|
+
);
|
|
35103
|
+
for (const [, sock] of this.clientSockets) {
|
|
35104
|
+
sock.destroy();
|
|
35105
|
+
}
|
|
35106
|
+
} else if (this.active) {
|
|
35107
|
+
if (typeof this.api.isStreamProfileRejected === "function" && this.api.isStreamProfileRejected(this.channel, this.profile)) {
|
|
35108
|
+
this.logger.warn?.(
|
|
35109
|
+
`[Go2rtcTcpServer] profile rejected by device channel=${this.channel} profile=${this.profile} \u2014 not restarting`
|
|
35110
|
+
);
|
|
35111
|
+
for (const [, sock] of this.clientSockets) {
|
|
35112
|
+
sock.destroy();
|
|
35113
|
+
}
|
|
35114
|
+
return;
|
|
35115
|
+
}
|
|
34078
35116
|
this.logger.info?.(
|
|
34079
35117
|
`[Go2rtcTcpServer] restarting native stream (clients=${this.connectedClients.size}, prestart=${this.prestartStream})`
|
|
34080
35118
|
);
|
|
@@ -36570,9 +37608,10 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36570
37608
|
const msg = fmtErr(e);
|
|
36571
37609
|
return msg.includes("Not running") || msg.includes("Baichuan UDP stream closed") || msg.includes("Baichuan socket closed") || msg.includes("ETIMEDOUT") || msg.toLowerCase().includes("timeout");
|
|
36572
37610
|
};
|
|
36573
|
-
const withRetries = async (label, max, op, shouldRetry) => {
|
|
37611
|
+
const withRetries = async (label, max, op, shouldRetry, isAborted) => {
|
|
36574
37612
|
let lastErr;
|
|
36575
37613
|
for (let attempt = 1; attempt <= max; attempt++) {
|
|
37614
|
+
if (isAborted?.()) throw new Error(`${label}: aborted (race won by another method)`);
|
|
36576
37615
|
try {
|
|
36577
37616
|
if (attempt > 1) {
|
|
36578
37617
|
logger?.log?.(`[AutoDetect] ${label}: retry ${attempt}/${max}...`);
|
|
@@ -36581,7 +37620,7 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36581
37620
|
} catch (e) {
|
|
36582
37621
|
lastErr = e;
|
|
36583
37622
|
const msg = fmtErr(e);
|
|
36584
|
-
const retryable = attempt < max && shouldRetry(e);
|
|
37623
|
+
const retryable = attempt < max && !isAborted?.() && shouldRetry(e);
|
|
36585
37624
|
logger?.log?.(
|
|
36586
37625
|
`[AutoDetect] ${label} attempt ${attempt}/${max} failed: ${msg}${retryable ? " (will retry)" : ""}`
|
|
36587
37626
|
);
|
|
@@ -36591,6 +37630,31 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36591
37630
|
}
|
|
36592
37631
|
throw lastErr instanceof Error ? lastErr : new Error(String(lastErr ?? `${label} failed`));
|
|
36593
37632
|
};
|
|
37633
|
+
const runUdpMethodsParallel = async (methods, loginAndDetect, errorPrefix) => {
|
|
37634
|
+
let raceWon = false;
|
|
37635
|
+
const methodErrors = /* @__PURE__ */ new Map();
|
|
37636
|
+
try {
|
|
37637
|
+
return await Promise.any(
|
|
37638
|
+
methods.map(async (m) => {
|
|
37639
|
+
try {
|
|
37640
|
+
const result = await loginAndDetect(m, () => raceWon);
|
|
37641
|
+
raceWon = true;
|
|
37642
|
+
return result;
|
|
37643
|
+
} catch (e) {
|
|
37644
|
+
if (!raceWon) methodErrors.set(m, fmtErr(e));
|
|
37645
|
+
logger?.log?.(`[AutoDetect] UDP (${m}) failed: ${fmtErr(e)}`);
|
|
37646
|
+
throw e;
|
|
37647
|
+
}
|
|
37648
|
+
})
|
|
37649
|
+
);
|
|
37650
|
+
} catch (e) {
|
|
37651
|
+
if (e instanceof AggregateError) {
|
|
37652
|
+
const msgs = methods.map((m) => `${m}: ${methodErrors.get(m) ?? "unknown"}`);
|
|
37653
|
+
throw new Error(`${errorPrefix} ${msgs.join(" | ")}`);
|
|
37654
|
+
}
|
|
37655
|
+
throw e;
|
|
37656
|
+
}
|
|
37657
|
+
};
|
|
36594
37658
|
const effectiveUid = normalizeUid(uid);
|
|
36595
37659
|
logger?.log?.(`[AutoDetect] Pinging ${host}...`);
|
|
36596
37660
|
const isReachable = await pingHost(host);
|
|
@@ -36620,9 +37684,9 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36620
37684
|
normalizedUid = normalizedDiscovered;
|
|
36621
37685
|
}
|
|
36622
37686
|
const methodsToTry = inputs.udpDiscoveryMethod ? [inputs.udpDiscoveryMethod] : ["local-direct", "local-broadcast", "remote", "relay", "map"];
|
|
36623
|
-
|
|
36624
|
-
|
|
36625
|
-
|
|
37687
|
+
return await runUdpMethodsParallel(
|
|
37688
|
+
methodsToTry,
|
|
37689
|
+
async (m, isAborted) => {
|
|
36626
37690
|
logger?.log?.(`[AutoDetect] Trying UDP discovery method: ${m}...`);
|
|
36627
37691
|
const udpApi = await withRetries(
|
|
36628
37692
|
`UDP(${m})`,
|
|
@@ -36645,11 +37709,14 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36645
37709
|
throw e;
|
|
36646
37710
|
}
|
|
36647
37711
|
},
|
|
36648
|
-
shouldRetryUdp
|
|
37712
|
+
shouldRetryUdp,
|
|
37713
|
+
isAborted
|
|
36649
37714
|
);
|
|
36650
|
-
const deviceInfo = await
|
|
36651
|
-
|
|
36652
|
-
|
|
37715
|
+
const [deviceInfo, capabilities, hostNetworkInfo] = await Promise.all([
|
|
37716
|
+
udpApi.getInfo(),
|
|
37717
|
+
udpApi.getDeviceCapabilities(),
|
|
37718
|
+
udpApi.getNetworkInfo(void 0, { timeoutMs: 1200 }).catch(() => void 0)
|
|
37719
|
+
]);
|
|
36653
37720
|
const channelNum = capabilities?.support?.channelNum ?? 1;
|
|
36654
37721
|
const model = deviceInfo.type?.trim();
|
|
36655
37722
|
const normalizedModel = model ? model.trim() : void 0;
|
|
@@ -36688,14 +37755,8 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36688
37755
|
channelNum: 1,
|
|
36689
37756
|
api: udpApi
|
|
36690
37757
|
};
|
|
36691
|
-
}
|
|
36692
|
-
|
|
36693
|
-
udpErrors.push(`${m}: ${msg}`);
|
|
36694
|
-
logger?.log?.(`[AutoDetect] UDP (${m}) failed: ${msg}`);
|
|
36695
|
-
}
|
|
36696
|
-
}
|
|
36697
|
-
throw new Error(
|
|
36698
|
-
`Forced UDP autodetect failed for all methods. ${udpErrors.join(" | ")}`
|
|
37758
|
+
},
|
|
37759
|
+
"Forced UDP autodetect failed for all methods."
|
|
36699
37760
|
);
|
|
36700
37761
|
}
|
|
36701
37762
|
let tcpApi;
|
|
@@ -36748,54 +37809,57 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36748
37809
|
}
|
|
36749
37810
|
return void 0;
|
|
36750
37811
|
};
|
|
36751
|
-
const infoProbe = await
|
|
36752
|
-
|
|
36753
|
-
|
|
37812
|
+
const [infoProbe, supportProbe] = await Promise.all([
|
|
37813
|
+
runProbeVariants(
|
|
37814
|
+
"getInfo",
|
|
37815
|
+
[
|
|
37816
|
+
{
|
|
37817
|
+
variant: "cmd80 class=0x6414",
|
|
37818
|
+
op: () => api.getInfo(void 0, {
|
|
37819
|
+
timeoutMs: 2500,
|
|
37820
|
+
messageClass: BC_CLASS_MODERN_24
|
|
37821
|
+
})
|
|
37822
|
+
},
|
|
37823
|
+
{
|
|
37824
|
+
variant: "cmd80 class=0x6614",
|
|
37825
|
+
op: () => api.getInfo(void 0, {
|
|
37826
|
+
timeoutMs: 3e3,
|
|
37827
|
+
messageClass: BC_CLASS_MODERN_20
|
|
37828
|
+
})
|
|
37829
|
+
},
|
|
37830
|
+
{
|
|
37831
|
+
variant: "cmd318(ch0) class=0x6414",
|
|
37832
|
+
op: () => api.getInfo(0, {
|
|
37833
|
+
timeoutMs: 3e3,
|
|
37834
|
+
messageClass: BC_CLASS_MODERN_24
|
|
37835
|
+
})
|
|
37836
|
+
},
|
|
37837
|
+
{
|
|
37838
|
+
variant: "cmd318(ch0) class=0x6614",
|
|
37839
|
+
op: () => api.getInfo(0, {
|
|
37840
|
+
timeoutMs: 3500,
|
|
37841
|
+
messageClass: BC_CLASS_MODERN_20
|
|
37842
|
+
})
|
|
37843
|
+
}
|
|
37844
|
+
]
|
|
37845
|
+
),
|
|
37846
|
+
// Support probes (cmd 199). Some firmwares may not support it or are slow.
|
|
37847
|
+
runProbeVariants("getSupportInfo", [
|
|
36754
37848
|
{
|
|
36755
|
-
variant: "
|
|
36756
|
-
op: () => api.
|
|
37849
|
+
variant: "cmd199 class=0x6414",
|
|
37850
|
+
op: () => api.getSupportInfo({
|
|
36757
37851
|
timeoutMs: 2500,
|
|
36758
37852
|
messageClass: BC_CLASS_MODERN_24
|
|
36759
37853
|
})
|
|
36760
37854
|
},
|
|
36761
37855
|
{
|
|
36762
|
-
variant: "
|
|
36763
|
-
op: () => api.
|
|
36764
|
-
timeoutMs: 3e3,
|
|
36765
|
-
messageClass: BC_CLASS_MODERN_20
|
|
36766
|
-
})
|
|
36767
|
-
},
|
|
36768
|
-
{
|
|
36769
|
-
variant: "cmd318(ch0) class=0x6414",
|
|
36770
|
-
op: () => api.getInfo(0, {
|
|
36771
|
-
timeoutMs: 3e3,
|
|
36772
|
-
messageClass: BC_CLASS_MODERN_24
|
|
36773
|
-
})
|
|
36774
|
-
},
|
|
36775
|
-
{
|
|
36776
|
-
variant: "cmd318(ch0) class=0x6614",
|
|
36777
|
-
op: () => api.getInfo(0, {
|
|
37856
|
+
variant: "cmd199 class=0x6614",
|
|
37857
|
+
op: () => api.getSupportInfo({
|
|
36778
37858
|
timeoutMs: 3500,
|
|
36779
37859
|
messageClass: BC_CLASS_MODERN_20
|
|
36780
37860
|
})
|
|
36781
37861
|
}
|
|
36782
|
-
]
|
|
36783
|
-
);
|
|
36784
|
-
const supportProbe = await runProbeVariants("getSupportInfo", [
|
|
36785
|
-
{
|
|
36786
|
-
variant: "cmd199 class=0x6414",
|
|
36787
|
-
op: () => api.getSupportInfo({
|
|
36788
|
-
timeoutMs: 2500,
|
|
36789
|
-
messageClass: BC_CLASS_MODERN_24
|
|
36790
|
-
})
|
|
36791
|
-
},
|
|
36792
|
-
{
|
|
36793
|
-
variant: "cmd199 class=0x6614",
|
|
36794
|
-
op: () => api.getSupportInfo({
|
|
36795
|
-
timeoutMs: 3500,
|
|
36796
|
-
messageClass: BC_CLASS_MODERN_20
|
|
36797
|
-
})
|
|
36798
|
-
}
|
|
37862
|
+
])
|
|
36799
37863
|
]);
|
|
36800
37864
|
const deviceInfo = infoProbe?.value;
|
|
36801
37865
|
const support = supportProbe?.value;
|
|
@@ -36887,9 +37951,11 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36887
37951
|
}
|
|
36888
37952
|
try {
|
|
36889
37953
|
const detectOverUdpApi = async (udpApi, udpDiscoveryMethod) => {
|
|
36890
|
-
const deviceInfo = await
|
|
36891
|
-
|
|
36892
|
-
|
|
37954
|
+
const [deviceInfo, capabilities, hostNetworkInfo] = await Promise.all([
|
|
37955
|
+
udpApi.getInfo(),
|
|
37956
|
+
udpApi.getDeviceCapabilities(),
|
|
37957
|
+
udpApi.getNetworkInfo(void 0, { timeoutMs: 1200 }).catch(() => void 0)
|
|
37958
|
+
]);
|
|
36893
37959
|
const channelNum = capabilities?.support?.channelNum ?? 1;
|
|
36894
37960
|
const model = deviceInfo.type?.trim();
|
|
36895
37961
|
const normalizedModel = model ? model.trim() : void 0;
|
|
@@ -36933,21 +37999,17 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36933
37999
|
};
|
|
36934
38000
|
};
|
|
36935
38001
|
const methodsToTry = ["local-direct", "local-broadcast", "remote", "relay", "map"];
|
|
36936
|
-
const
|
|
36937
|
-
|
|
36938
|
-
|
|
38002
|
+
const viableMethods = normalizedUid ? methodsToTry : methodsToTry.filter((m) => m === "local-direct" || m === "local-broadcast");
|
|
38003
|
+
return await runUdpMethodsParallel(
|
|
38004
|
+
viableMethods,
|
|
38005
|
+
async (m, isAborted) => {
|
|
36939
38006
|
logger?.log?.(`[AutoDetect] Trying UDP discovery method: ${m}...`);
|
|
36940
38007
|
const udpApi = await withRetries(
|
|
36941
38008
|
`UDP(${m})`,
|
|
36942
38009
|
maxRetries,
|
|
36943
38010
|
async (attempt) => {
|
|
36944
|
-
const apiInputs = {
|
|
36945
|
-
|
|
36946
|
-
udpDiscoveryMethod: m
|
|
36947
|
-
};
|
|
36948
|
-
if (normalizedUid) {
|
|
36949
|
-
apiInputs.uid = normalizedUid;
|
|
36950
|
-
}
|
|
38011
|
+
const apiInputs = { ...inputs, udpDiscoveryMethod: m };
|
|
38012
|
+
if (normalizedUid) apiInputs.uid = normalizedUid;
|
|
36951
38013
|
const api = createBaichuanApi(apiInputs, "udp");
|
|
36952
38014
|
try {
|
|
36953
38015
|
await api.login();
|
|
@@ -36962,20 +38024,12 @@ async function autoDetectDeviceType(inputs) {
|
|
|
36962
38024
|
throw e;
|
|
36963
38025
|
}
|
|
36964
38026
|
},
|
|
36965
|
-
shouldRetryUdp
|
|
38027
|
+
shouldRetryUdp,
|
|
38028
|
+
isAborted
|
|
36966
38029
|
);
|
|
36967
|
-
return
|
|
36968
|
-
}
|
|
36969
|
-
|
|
36970
|
-
udpErrors.push(`${m}: ${msg}`);
|
|
36971
|
-
try {
|
|
36972
|
-
} catch {
|
|
36973
|
-
}
|
|
36974
|
-
logger?.log?.(`[AutoDetect] UDP (${m}) failed: ${msg}`);
|
|
36975
|
-
}
|
|
36976
|
-
}
|
|
36977
|
-
throw new Error(
|
|
36978
|
-
`UDP discovery failed for all methods. ${udpErrors.join(" | ")}`
|
|
38030
|
+
return detectOverUdpApi(udpApi, m);
|
|
38031
|
+
},
|
|
38032
|
+
"UDP discovery failed for all methods."
|
|
36979
38033
|
);
|
|
36980
38034
|
} catch (udpError) {
|
|
36981
38035
|
logger?.log?.(
|
|
@@ -37251,6 +38305,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37251
38305
|
BC_CMD_ID_GET_AUDIO_ALARM,
|
|
37252
38306
|
BC_CMD_ID_GET_AUDIO_CFG,
|
|
37253
38307
|
BC_CMD_ID_GET_AUDIO_TASK,
|
|
38308
|
+
BC_CMD_ID_GET_AUTO_FOCUS,
|
|
37254
38309
|
BC_CMD_ID_GET_BATTERY_INFO,
|
|
37255
38310
|
BC_CMD_ID_GET_BATTERY_INFO_LIST,
|
|
37256
38311
|
BC_CMD_ID_GET_DAY_NIGHT_THRESHOLD,
|
|
@@ -37259,6 +38314,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37259
38314
|
BC_CMD_ID_GET_DING_DONG_LIST,
|
|
37260
38315
|
BC_CMD_ID_GET_DING_DONG_SILENT,
|
|
37261
38316
|
BC_CMD_ID_GET_EMAIL_TASK,
|
|
38317
|
+
BC_CMD_ID_GET_ENC,
|
|
37262
38318
|
BC_CMD_ID_GET_FTP_TASK,
|
|
37263
38319
|
BC_CMD_ID_GET_HDD_INFO_LIST,
|
|
37264
38320
|
BC_CMD_ID_GET_KIT_AP_CFG,
|
|
@@ -37267,8 +38323,10 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37267
38323
|
BC_CMD_ID_GET_ONLINE_USER_LIST,
|
|
37268
38324
|
BC_CMD_ID_GET_OSD_DATETIME,
|
|
37269
38325
|
BC_CMD_ID_GET_PIR_INFO,
|
|
38326
|
+
BC_CMD_ID_GET_PRIVACY_MASK,
|
|
37270
38327
|
BC_CMD_ID_GET_PTZ_POSITION,
|
|
37271
38328
|
BC_CMD_ID_GET_PTZ_PRESET,
|
|
38329
|
+
BC_CMD_ID_GET_PUSH_TASK,
|
|
37272
38330
|
BC_CMD_ID_GET_RECORD,
|
|
37273
38331
|
BC_CMD_ID_GET_RECORD_CFG,
|
|
37274
38332
|
BC_CMD_ID_GET_REC_ENC_CFG,
|
|
@@ -37297,11 +38355,23 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37297
38355
|
BC_CMD_ID_QUICK_REPLY_PLAY,
|
|
37298
38356
|
BC_CMD_ID_SET_AI_ALARM,
|
|
37299
38357
|
BC_CMD_ID_SET_AI_CFG,
|
|
38358
|
+
BC_CMD_ID_SET_AI_DENOISE,
|
|
38359
|
+
BC_CMD_ID_SET_AUDIO_CFG,
|
|
37300
38360
|
BC_CMD_ID_SET_AUDIO_TASK,
|
|
38361
|
+
BC_CMD_ID_SET_AUTO_FOCUS,
|
|
38362
|
+
BC_CMD_ID_SET_DAY_NIGHT_THRESHOLD,
|
|
37301
38363
|
BC_CMD_ID_SET_DING_DONG_CFG,
|
|
37302
38364
|
BC_CMD_ID_SET_DING_DONG_SILENT,
|
|
38365
|
+
BC_CMD_ID_SET_EMAIL_TASK,
|
|
38366
|
+
BC_CMD_ID_SET_ENC,
|
|
38367
|
+
BC_CMD_ID_SET_LED_STATE,
|
|
37303
38368
|
BC_CMD_ID_SET_MOTION_ALARM,
|
|
37304
38369
|
BC_CMD_ID_SET_PIR_INFO,
|
|
38370
|
+
BC_CMD_ID_SET_PRIVACY_MASK,
|
|
38371
|
+
BC_CMD_ID_SET_PUSH_TASK,
|
|
38372
|
+
BC_CMD_ID_SET_RECORD,
|
|
38373
|
+
BC_CMD_ID_SET_RECORD_CFG,
|
|
38374
|
+
BC_CMD_ID_SET_VIDEO_INPUT,
|
|
37305
38375
|
BC_CMD_ID_SET_WHITE_LED_STATE,
|
|
37306
38376
|
BC_CMD_ID_SET_WHITE_LED_TASK,
|
|
37307
38377
|
BC_CMD_ID_SET_ZOOM_FOCUS,
|
|
@@ -37340,6 +38410,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37340
38410
|
HlsSessionManager,
|
|
37341
38411
|
Intercom,
|
|
37342
38412
|
MjpegTransformer,
|
|
38413
|
+
MpegTsMuxer,
|
|
37343
38414
|
NVR_HUB_EXACT_TYPES,
|
|
37344
38415
|
NVR_HUB_MODEL_PATTERNS,
|
|
37345
38416
|
ReolinkBaichuanApi,
|
|
@@ -37349,6 +38420,8 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37349
38420
|
abilitiesHasAny,
|
|
37350
38421
|
aesDecrypt,
|
|
37351
38422
|
aesEncrypt,
|
|
38423
|
+
applyStreamPatch,
|
|
38424
|
+
applyXmlTagPatch,
|
|
37352
38425
|
asLogger,
|
|
37353
38426
|
autoDetectDeviceType,
|
|
37354
38427
|
bcDecrypt,
|
|
@@ -37398,6 +38471,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37398
38471
|
createRfc4571TcpServerForReplay,
|
|
37399
38472
|
createRtspProxyServer,
|
|
37400
38473
|
createTaggedLogger,
|
|
38474
|
+
decideSleepInferenceTransition,
|
|
37401
38475
|
decideVideoclipTranscodeMode,
|
|
37402
38476
|
decodeHeader,
|
|
37403
38477
|
deriveAesKey,
|
|
@@ -37412,6 +38486,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37412
38486
|
discoverViaUdpBroadcast,
|
|
37413
38487
|
discoverViaUdpDirect,
|
|
37414
38488
|
encodeHeader,
|
|
38489
|
+
ensureXmlHeader,
|
|
37415
38490
|
extractH264ParamSetsFromAccessUnit,
|
|
37416
38491
|
extractH265ParamSetsFromAccessUnit,
|
|
37417
38492
|
extractPpsFromAnnexB,
|
|
@@ -37440,6 +38515,8 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37440
38515
|
maskUid,
|
|
37441
38516
|
md5HexUpper,
|
|
37442
38517
|
md5StrModern,
|
|
38518
|
+
normalizeDayNightMode,
|
|
38519
|
+
normalizeOpenClose,
|
|
37443
38520
|
normalizeUid,
|
|
37444
38521
|
packetizeAacAdtsFrame,
|
|
37445
38522
|
packetizeAacRawFrame,
|
|
@@ -37449,6 +38526,7 @@ var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
|
37449
38526
|
parseBcMedia,
|
|
37450
38527
|
parseRecordingFileName,
|
|
37451
38528
|
parseSupportXml,
|
|
38529
|
+
patchNestedTag,
|
|
37452
38530
|
printNvrDiagnostics,
|
|
37453
38531
|
runAllDiagnosticsConsecutively,
|
|
37454
38532
|
runMultifocalDiagnosticsConsecutively,
|