@apocaliss92/nodelink-js 0.4.5 → 0.4.6
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-55PR4WFD.js → DiagnosticsTools-UMN4C7SY.js} +2 -2
- package/dist/{chunk-WDFKIHM5.js → chunk-F2Y5U3YP.js} +86 -13
- package/dist/chunk-F2Y5U3YP.js.map +1 -0
- package/dist/{chunk-DEOMUWBN.js → chunk-TR3V5FTO.js} +15 -1
- package/dist/chunk-TR3V5FTO.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +96 -9
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +125 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +31 -18
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-DEOMUWBN.js.map +0 -1
- package/dist/chunk-WDFKIHM5.js.map +0 -1
- /package/dist/{DiagnosticsTools-55PR4WFD.js.map → DiagnosticsTools-UMN4C7SY.js.map} +0 -0
package/dist/cli/rtsp-server.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
BaichuanRtspServer,
|
|
4
4
|
ReolinkBaichuanApi,
|
|
5
5
|
autoDetectDeviceType
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-F2Y5U3YP.js";
|
|
7
7
|
import {
|
|
8
8
|
__require
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-TR3V5FTO.js";
|
|
10
10
|
|
|
11
11
|
// src/cli/rtsp-server.ts
|
|
12
12
|
function parseArgs() {
|
package/dist/index.cjs
CHANGED
|
@@ -2178,6 +2178,19 @@ var init_BaichuanVideoStream = __esm({
|
|
|
2178
2178
|
// Stateful AES decryptor for fragmented BcMedia packets (full_aes mode)
|
|
2179
2179
|
// In CFB mode, continuation frames must use the cipher state from previous frames.
|
|
2180
2180
|
aesStreamDecryptor = null;
|
|
2181
|
+
/**
|
|
2182
|
+
* Pending startup error stashed when emitSafeError is called before any
|
|
2183
|
+
* "error" listener is registered (e.g. camera returns 400 during start()).
|
|
2184
|
+
* The rfc4571-server's waitForKeyframe can consume this immediately instead
|
|
2185
|
+
* of waiting for the full keyframe timeout.
|
|
2186
|
+
*/
|
|
2187
|
+
_pendingStartupError;
|
|
2188
|
+
/** Consume and clear any pending startup error. */
|
|
2189
|
+
consumePendingStartupError() {
|
|
2190
|
+
const err = this._pendingStartupError;
|
|
2191
|
+
this._pendingStartupError = void 0;
|
|
2192
|
+
return err;
|
|
2193
|
+
}
|
|
2181
2194
|
emitSafeError(err) {
|
|
2182
2195
|
if (!this.active) {
|
|
2183
2196
|
this.logger?.warn?.(
|
|
@@ -2189,6 +2202,7 @@ var init_BaichuanVideoStream = __esm({
|
|
|
2189
2202
|
this.logger?.warn?.(
|
|
2190
2203
|
`[BaichuanVideoStream] Unhandled stream error: ${err.message}`
|
|
2191
2204
|
);
|
|
2205
|
+
this._pendingStartupError = err;
|
|
2192
2206
|
return;
|
|
2193
2207
|
}
|
|
2194
2208
|
this.emit("error", err);
|
|
@@ -8527,6 +8541,9 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
8527
8541
|
resendTimer;
|
|
8528
8542
|
hbTimer;
|
|
8529
8543
|
discoveryTid;
|
|
8544
|
+
// Track discovery-phase timers so close() can cancel them even if
|
|
8545
|
+
// discovery is still in progress (prevents ERR_SOCKET_DGRAM_NOT_RUNNING).
|
|
8546
|
+
discoveryTimers = [];
|
|
8530
8547
|
acceptSent = false;
|
|
8531
8548
|
lastAcceptAtMs;
|
|
8532
8549
|
ackScheduled = false;
|
|
@@ -8575,9 +8592,31 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
8575
8592
|
});
|
|
8576
8593
|
sock.on("error", (e) => this.emit("error", e));
|
|
8577
8594
|
sock.on("close", () => this.emit("close"));
|
|
8578
|
-
|
|
8579
|
-
|
|
8580
|
-
|
|
8595
|
+
const portRange = Array.from({ length: 500 }, (_, i) => 53500 + i);
|
|
8596
|
+
for (let i = portRange.length - 1; i > 0; i--) {
|
|
8597
|
+
const j = Math.floor(Math.random() * (i + 1));
|
|
8598
|
+
[portRange[i], portRange[j]] = [portRange[j], portRange[i]];
|
|
8599
|
+
}
|
|
8600
|
+
let bound = false;
|
|
8601
|
+
for (const port of portRange) {
|
|
8602
|
+
try {
|
|
8603
|
+
await new Promise((resolve, reject) => {
|
|
8604
|
+
sock.once("error", reject);
|
|
8605
|
+
sock.bind(port, "0.0.0.0", () => {
|
|
8606
|
+
sock.removeListener("error", reject);
|
|
8607
|
+
resolve();
|
|
8608
|
+
});
|
|
8609
|
+
});
|
|
8610
|
+
bound = true;
|
|
8611
|
+
break;
|
|
8612
|
+
} catch {
|
|
8613
|
+
}
|
|
8614
|
+
}
|
|
8615
|
+
if (!bound) {
|
|
8616
|
+
await new Promise(
|
|
8617
|
+
(resolve) => sock.bind(0, "0.0.0.0", () => resolve())
|
|
8618
|
+
);
|
|
8619
|
+
}
|
|
8581
8620
|
if (this.opts.mode === "direct") {
|
|
8582
8621
|
this.remote = { host: this.opts.host, port: this.opts.port };
|
|
8583
8622
|
this.clientId = this.opts.clientId;
|
|
@@ -8997,7 +9036,24 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
8997
9036
|
BCUDP_DISCOVERY_PORT_LOCAL_ANY,
|
|
8998
9037
|
BCUDP_DISCOVERY_PORT_LOCAL_UID
|
|
8999
9038
|
];
|
|
9000
|
-
const
|
|
9039
|
+
const broadcastHosts = ["255.255.255.255"];
|
|
9040
|
+
const ifaces = (0, import_node_os.networkInterfaces)();
|
|
9041
|
+
for (const name of Object.keys(ifaces)) {
|
|
9042
|
+
const entries = ifaces[name];
|
|
9043
|
+
if (!entries) continue;
|
|
9044
|
+
for (const addr2 of entries) {
|
|
9045
|
+
if (addr2.family === "IPv4" && !addr2.internal && addr2.cidr) {
|
|
9046
|
+
const ipParts = addr2.address.split(".").map(Number);
|
|
9047
|
+
const maskParts = addr2.netmask.split(".").map(Number);
|
|
9048
|
+
if (ipParts.length === 4 && maskParts.length === 4) {
|
|
9049
|
+
const bcast = ipParts.map((octet, i) => octet | ~maskParts[i] & 255).join(".");
|
|
9050
|
+
if (!broadcastHosts.includes(bcast)) {
|
|
9051
|
+
broadcastHosts.push(bcast);
|
|
9052
|
+
}
|
|
9053
|
+
}
|
|
9054
|
+
}
|
|
9055
|
+
}
|
|
9056
|
+
}
|
|
9001
9057
|
const directHost = (this.opts.directHost ?? "").trim();
|
|
9002
9058
|
const localMode = opts?.localMode ?? "local-broadcast";
|
|
9003
9059
|
const directFirstWindowMs = localMode === "local-direct" && directHost ? 3e3 : 0;
|
|
@@ -9024,6 +9080,7 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
9024
9080
|
)
|
|
9025
9081
|
);
|
|
9026
9082
|
}, discoveryTimeout);
|
|
9083
|
+
this.discoveryTimers.push(timeout);
|
|
9027
9084
|
let retryTimer;
|
|
9028
9085
|
let retryCount = 0;
|
|
9029
9086
|
let discoveredSid;
|
|
@@ -9200,11 +9257,11 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
9200
9257
|
if (directHost) {
|
|
9201
9258
|
if (directFirstWindowMs > 0 && elapsedMs < directFirstWindowMs)
|
|
9202
9259
|
return [directHost];
|
|
9203
|
-
return [directHost,
|
|
9260
|
+
return [directHost, ...broadcastHosts];
|
|
9204
9261
|
}
|
|
9205
|
-
return
|
|
9262
|
+
return broadcastHosts;
|
|
9206
9263
|
}
|
|
9207
|
-
return
|
|
9264
|
+
return broadcastHosts;
|
|
9208
9265
|
})();
|
|
9209
9266
|
for (const host of Array.from(new Set(hosts))) {
|
|
9210
9267
|
for (const port of ports) {
|
|
@@ -9212,8 +9269,7 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
9212
9269
|
sock.send(packet, port, host);
|
|
9213
9270
|
retryCount++;
|
|
9214
9271
|
this.emit("debug", "discovery_send", { retryCount, host, port });
|
|
9215
|
-
} catch
|
|
9216
|
-
this.emit("error", e instanceof Error ? e : new Error(String(e)));
|
|
9272
|
+
} catch {
|
|
9217
9273
|
}
|
|
9218
9274
|
}
|
|
9219
9275
|
}
|
|
@@ -9222,6 +9278,7 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
9222
9278
|
retryTimer = (0, import_node_timers.setInterval)(() => {
|
|
9223
9279
|
sendDiscovery();
|
|
9224
9280
|
}, retryInterval);
|
|
9281
|
+
this.discoveryTimers.push(retryTimer);
|
|
9225
9282
|
});
|
|
9226
9283
|
this.clientId = reply.cid;
|
|
9227
9284
|
this.cameraId = reply.did;
|
|
@@ -9538,6 +9595,10 @@ var BcUdpStream = class extends import_node_events.EventEmitter {
|
|
|
9538
9595
|
this.ackTimer = void 0;
|
|
9539
9596
|
this.resendTimer = void 0;
|
|
9540
9597
|
this.hbTimer = void 0;
|
|
9598
|
+
for (const t of this.discoveryTimers) {
|
|
9599
|
+
clearInterval(t);
|
|
9600
|
+
}
|
|
9601
|
+
this.discoveryTimers = [];
|
|
9541
9602
|
const s = this.sock;
|
|
9542
9603
|
this.sock = void 0;
|
|
9543
9604
|
if (!s) return;
|
|
@@ -18654,6 +18715,20 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
18654
18715
|
const prefix = basename.substring(0, 10).toUpperCase();
|
|
18655
18716
|
return prefix.includes("S") ? "subStream" : "mainStream";
|
|
18656
18717
|
}
|
|
18718
|
+
/**
|
|
18719
|
+
* Stream profiles that the device explicitly rejected (response_code 400).
|
|
18720
|
+
* Keyed by `"ch:profile"` (e.g. `"0:ext"`). Once a profile is in this set
|
|
18721
|
+
* it is excluded from `buildVideoStreamOptions()` results and no further
|
|
18722
|
+
* start attempts are made until the API instance is recreated.
|
|
18723
|
+
*/
|
|
18724
|
+
_rejectedStreamProfiles = /* @__PURE__ */ new Set();
|
|
18725
|
+
/**
|
|
18726
|
+
* Check whether a stream profile was rejected by the device at runtime
|
|
18727
|
+
* (e.g. ext returned response_code 400).
|
|
18728
|
+
*/
|
|
18729
|
+
isStreamProfileRejected(channel, profile) {
|
|
18730
|
+
return this._rejectedStreamProfiles.has(`${channel}:${profile}`);
|
|
18731
|
+
}
|
|
18657
18732
|
/**
|
|
18658
18733
|
* Cache for buildVideoStreamOptions.
|
|
18659
18734
|
*
|
|
@@ -23778,6 +23853,16 @@ ${stderr}`)
|
|
|
23778
23853
|
}
|
|
23779
23854
|
if (!frame) frame = await targetClient.sendFrame(baseParams);
|
|
23780
23855
|
if (frame.header.responseCode !== 200) {
|
|
23856
|
+
if (frame.header.responseCode === 400) {
|
|
23857
|
+
const rejKey = `${ch}:${profile}`;
|
|
23858
|
+
if (!this._rejectedStreamProfiles.has(rejKey)) {
|
|
23859
|
+
this._rejectedStreamProfiles.add(rejKey);
|
|
23860
|
+
this.videoStreamOptionsCache.clear();
|
|
23861
|
+
this.logger?.warn?.(
|
|
23862
|
+
`[ReolinkBaichuanApi] Stream profile rejected by device: channel=${ch} profile=${profile} (response_code 400). This profile will be excluded from available streams. The camera may not support this stream profile with the current firmware.`
|
|
23863
|
+
);
|
|
23864
|
+
}
|
|
23865
|
+
}
|
|
23781
23866
|
throw new Error(
|
|
23782
23867
|
`Video stream request rejected (response_code ${frame.header.responseCode}). Expected response_code 200, camera returned ${frame.header.responseCode}`
|
|
23783
23868
|
);
|
|
@@ -25799,6 +25884,8 @@ ${xml}`
|
|
|
25799
25884
|
for (const metadata of params.metadatas) {
|
|
25800
25885
|
const profile = metadata.profile;
|
|
25801
25886
|
if (isMultiFocal && profile === "ext") continue;
|
|
25887
|
+
if (this._rejectedStreamProfiles.has(`${params.channel}:${profile}`))
|
|
25888
|
+
continue;
|
|
25802
25889
|
if (params.includeRtsp && profile !== "ext") {
|
|
25803
25890
|
const streamName = profile === "main" ? "main" : "sub";
|
|
25804
25891
|
pushRtsp({
|
|
@@ -32289,6 +32376,11 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
32289
32376
|
"videoAccessUnit",
|
|
32290
32377
|
onAu
|
|
32291
32378
|
);
|
|
32379
|
+
const pendingErr = videoStream.consumePendingStartupError?.();
|
|
32380
|
+
if (pendingErr) {
|
|
32381
|
+
cleanup();
|
|
32382
|
+
reject(pendingErr);
|
|
32383
|
+
}
|
|
32292
32384
|
});
|
|
32293
32385
|
}
|
|
32294
32386
|
};
|
|
@@ -32300,24 +32392,32 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
32300
32392
|
await videoStream.stop();
|
|
32301
32393
|
} catch {
|
|
32302
32394
|
}
|
|
32303
|
-
if (
|
|
32304
|
-
|
|
32305
|
-
|
|
32395
|
+
if (dedicatedSession) {
|
|
32396
|
+
try {
|
|
32397
|
+
await dedicatedSession.release();
|
|
32398
|
+
} catch {
|
|
32399
|
+
}
|
|
32400
|
+
}
|
|
32401
|
+
if (!dedicatedSession) {
|
|
32402
|
+
if (closeApiOnTeardown) {
|
|
32403
|
+
await Promise.allSettled(
|
|
32404
|
+
Array.from(apisToClose).map(async (a) => {
|
|
32405
|
+
try {
|
|
32406
|
+
await a.close();
|
|
32407
|
+
} catch {
|
|
32408
|
+
}
|
|
32409
|
+
})
|
|
32410
|
+
);
|
|
32411
|
+
} else {
|
|
32412
|
+
const graceMs = isComposite ? 5e3 : 0;
|
|
32413
|
+
for (const a of Array.from(apisToClose)) {
|
|
32306
32414
|
try {
|
|
32307
|
-
|
|
32415
|
+
a?.client?.requestIdleDisconnectSoon?.(
|
|
32416
|
+
"rfc4571_teardown",
|
|
32417
|
+
graceMs
|
|
32418
|
+
);
|
|
32308
32419
|
} catch {
|
|
32309
32420
|
}
|
|
32310
|
-
})
|
|
32311
|
-
);
|
|
32312
|
-
} else {
|
|
32313
|
-
const graceMs = isComposite ? 5e3 : 0;
|
|
32314
|
-
for (const a of Array.from(apisToClose)) {
|
|
32315
|
-
try {
|
|
32316
|
-
a?.client?.requestIdleDisconnectSoon?.(
|
|
32317
|
-
"rfc4571_teardown",
|
|
32318
|
-
graceMs
|
|
32319
|
-
);
|
|
32320
|
-
} catch {
|
|
32321
32421
|
}
|
|
32322
32422
|
}
|
|
32323
32423
|
}
|
|
@@ -32573,7 +32673,7 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
32573
32673
|
} catch {
|
|
32574
32674
|
}
|
|
32575
32675
|
}
|
|
32576
|
-
if (closeApiOnTeardown) {
|
|
32676
|
+
if (closeApiOnTeardown && !dedicatedSession) {
|
|
32577
32677
|
await Promise.allSettled(
|
|
32578
32678
|
Array.from(apisToClose).map(async (a) => {
|
|
32579
32679
|
try {
|