@apocaliss92/nodelink-js 0.2.5 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -82
- package/dist/{chunk-EG5IY3CM.js → chunk-UDS2UR4S.js} +444 -20
- package/dist/chunk-UDS2UR4S.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +78 -16
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +1071 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -70
- package/dist/index.d.ts +193 -69
- package/dist/index.js +580 -142
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-EG5IY3CM.js.map +0 -1
package/dist/cli/rtsp-server.cjs
CHANGED
|
@@ -8079,14 +8079,16 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8079
8079
|
`;
|
|
8080
8080
|
}
|
|
8081
8081
|
if (body) {
|
|
8082
|
-
|
|
8082
|
+
const bodyBuf = Buffer.from(body, "utf8");
|
|
8083
|
+
response += `Content-Length: ${bodyBuf.length}\r
|
|
8083
8084
|
`;
|
|
8085
|
+
response += "\r\n";
|
|
8086
|
+
socket.write(response);
|
|
8087
|
+
socket.write(bodyBuf);
|
|
8088
|
+
} else {
|
|
8089
|
+
response += "\r\n";
|
|
8090
|
+
socket.write(response);
|
|
8084
8091
|
}
|
|
8085
|
-
response += "\r\n";
|
|
8086
|
-
if (body) {
|
|
8087
|
-
response += body;
|
|
8088
|
-
}
|
|
8089
|
-
socket.write(response);
|
|
8090
8092
|
};
|
|
8091
8093
|
this.rtspDebugLog(`RTSP ${method} ${url}`);
|
|
8092
8094
|
if (this.requireAuth) {
|
|
@@ -8296,10 +8298,25 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8296
8298
|
);
|
|
8297
8299
|
}
|
|
8298
8300
|
}
|
|
8299
|
-
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8301
|
+
{
|
|
8302
|
+
const baseUrl = `rtsp://${this.listenHost}:${this.listenPort}${this.path}`;
|
|
8303
|
+
const resources = this.clientResources.get(clientId);
|
|
8304
|
+
const rtpInfoParts = [];
|
|
8305
|
+
if (resources?.setupTrack0) {
|
|
8306
|
+
rtpInfoParts.push(`url=${baseUrl}/track0`);
|
|
8307
|
+
}
|
|
8308
|
+
if (resources?.setupTrack1) {
|
|
8309
|
+
rtpInfoParts.push(`url=${baseUrl}/track1`);
|
|
8310
|
+
}
|
|
8311
|
+
const playHeaders = {
|
|
8312
|
+
Session: sessionId,
|
|
8313
|
+
Range: "npt=now-"
|
|
8314
|
+
};
|
|
8315
|
+
if (rtpInfoParts.length > 0) {
|
|
8316
|
+
playHeaders["RTP-Info"] = rtpInfoParts.join(",");
|
|
8317
|
+
}
|
|
8318
|
+
sendResponse(200, "OK", playHeaders);
|
|
8319
|
+
}
|
|
8303
8320
|
} else if (method === "TEARDOWN") {
|
|
8304
8321
|
this.logger.info(
|
|
8305
8322
|
`[rebroadcast] TEARDOWN client=${clientId} session=${sessionId}`
|
|
@@ -8329,6 +8346,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8329
8346
|
sdp += `c=IN IP4 ${this.listenHost}\r
|
|
8330
8347
|
`;
|
|
8331
8348
|
sdp += "t=0 0\r\n";
|
|
8349
|
+
sdp += "a=range:npt=now-\r\n";
|
|
8350
|
+
sdp += "a=control:*\r\n";
|
|
8332
8351
|
sdp += `m=video 0 RTP/AVP ${videoPayloadType}\r
|
|
8333
8352
|
`;
|
|
8334
8353
|
sdp += `a=rtpmap:${videoPayloadType} ${codec}/90000\r
|
|
@@ -9271,7 +9290,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
9271
9290
|
this.firstFramePromise = null;
|
|
9272
9291
|
this.firstFrameResolve = null;
|
|
9273
9292
|
this.nativeFanout = null;
|
|
9274
|
-
|
|
9293
|
+
for (const [, resources] of this.clientResources) {
|
|
9294
|
+
const res = resources;
|
|
9295
|
+
res.rtpVideoBaseMicroseconds = void 0;
|
|
9296
|
+
res.rtpVideoBaseTimestamp = void 0;
|
|
9297
|
+
res.rtpVideoLastTimestamp = void 0;
|
|
9298
|
+
res.seenFirstVideoKeyframe = false;
|
|
9299
|
+
res.rtpSentVideoConfig = false;
|
|
9300
|
+
}
|
|
9275
9301
|
if (this.dedicatedSessionRelease) {
|
|
9276
9302
|
const release = this.dedicatedSessionRelease;
|
|
9277
9303
|
this.dedicatedSessionRelease = void 0;
|
|
@@ -11345,6 +11371,22 @@ var BaichuanClient = class _BaichuanClient extends import_node_events4.EventEmit
|
|
|
11345
11371
|
static coverPreviewBackoffMs = /* @__PURE__ */ new Map();
|
|
11346
11372
|
static COVER_PREVIEW_INITIAL_BACKOFF_MS = 1e3;
|
|
11347
11373
|
static COVER_PREVIEW_MAX_BACKOFF_MS = 3e4;
|
|
11374
|
+
/**
|
|
11375
|
+
* Per-client snapshot (cmd_id=109) serialization queue.
|
|
11376
|
+
*
|
|
11377
|
+
* WHY: On NVR/multi-camera devices sharing one socket, concurrent snapshot requests
|
|
11378
|
+
* can cause JPEG data to mix (even with per-request msgNum filtering):
|
|
11379
|
+
* - Camera A and B both send frames on same socket
|
|
11380
|
+
* - Frame listener is global per socket
|
|
11381
|
+
* - Timing quirks can cause chunk reordering or listener confusion
|
|
11382
|
+
*
|
|
11383
|
+
* FIX: Serialize all cmd_id=109 requests on THIS client instance.
|
|
11384
|
+
* Each snapshot waits for previous one to complete before starting.
|
|
11385
|
+
* This ensures clean frame sequences per request, zero data corruption.
|
|
11386
|
+
*
|
|
11387
|
+
* Impact: Snapshots are ~0–50ms slower per camera (negligible for users).
|
|
11388
|
+
*/
|
|
11389
|
+
snapshotQueueTail = Promise.resolve();
|
|
11348
11390
|
opts;
|
|
11349
11391
|
debugCfg;
|
|
11350
11392
|
logger;
|
|
@@ -13826,6 +13868,20 @@ var BaichuanClient = class _BaichuanClient extends import_node_events4.EventEmit
|
|
|
13826
13868
|
});
|
|
13827
13869
|
}
|
|
13828
13870
|
async sendBinarySnapshot109(params) {
|
|
13871
|
+
const prevTail = this.snapshotQueueTail;
|
|
13872
|
+
let resolve;
|
|
13873
|
+
const newTail = new Promise((r) => {
|
|
13874
|
+
resolve = r;
|
|
13875
|
+
});
|
|
13876
|
+
this.snapshotQueueTail = newTail;
|
|
13877
|
+
try {
|
|
13878
|
+
await prevTail;
|
|
13879
|
+
return await this.sendBinarySnapshot109Impl(params);
|
|
13880
|
+
} finally {
|
|
13881
|
+
resolve();
|
|
13882
|
+
}
|
|
13883
|
+
}
|
|
13884
|
+
async sendBinarySnapshot109Impl(params) {
|
|
13829
13885
|
await this.connect();
|
|
13830
13886
|
const channel = params.channel ?? this.opts.channel ?? 0;
|
|
13831
13887
|
const channelId = params.channelIdOverride ?? (params.channel == null ? this.hostChannelId : channel + 1);
|
|
@@ -13885,7 +13941,8 @@ var BaichuanClient = class _BaichuanClient extends import_node_events4.EventEmit
|
|
|
13885
13941
|
};
|
|
13886
13942
|
const onFrame = (frame) => {
|
|
13887
13943
|
if (frame.header.cmdId !== cmdId) return;
|
|
13888
|
-
if (frame.header.msgNum
|
|
13944
|
+
if (frame.header.msgNum !== msgNum) return;
|
|
13945
|
+
if (frame.header.responseCode >= 400) {
|
|
13889
13946
|
fail(
|
|
13890
13947
|
new Error(
|
|
13891
13948
|
`Baichuan snapshot request rejected (cmdId=${cmdId} msgNum=${msgNum} responseCode=${frame.header.responseCode})`
|
|
@@ -22003,13 +22060,13 @@ ${stderr}`)
|
|
|
22003
22060
|
*/
|
|
22004
22061
|
async muxToMp4(params) {
|
|
22005
22062
|
const { spawn: spawn4 } = await import("child_process");
|
|
22006
|
-
const { randomUUID:
|
|
22063
|
+
const { randomUUID: randomUUID3 } = await import("crypto");
|
|
22007
22064
|
const fs5 = await import("fs/promises");
|
|
22008
22065
|
const os = await import("os");
|
|
22009
22066
|
const path5 = await import("path");
|
|
22010
22067
|
const ffmpeg = params.ffmpegPath ?? "ffmpeg";
|
|
22011
22068
|
const tmpDir = os.tmpdir();
|
|
22012
|
-
const id =
|
|
22069
|
+
const id = randomUUID3();
|
|
22013
22070
|
const videoFormat = params.videoCodec === "H265" ? "hevc" : "h264";
|
|
22014
22071
|
const videoPath = path5.join(tmpDir, `reolink-${id}.${videoFormat}`);
|
|
22015
22072
|
const outputPath = path5.join(tmpDir, `reolink-${id}.mp4`);
|
|
@@ -26991,8 +27048,13 @@ ${scheduleItems}
|
|
|
26991
27048
|
init_constants();
|
|
26992
27049
|
|
|
26993
27050
|
// src/reolink/discovery.ts
|
|
27051
|
+
var import_node_child_process4 = require("child_process");
|
|
27052
|
+
var import_node_crypto3 = require("crypto");
|
|
26994
27053
|
var import_node_dgram2 = __toESM(require("dgram"), 1);
|
|
27054
|
+
var net3 = __toESM(require("net"), 1);
|
|
26995
27055
|
var import_node_os2 = require("os");
|
|
27056
|
+
var import_node_util = require("util");
|
|
27057
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process4.execFile);
|
|
26996
27058
|
async function discoverViaUdpDirect(host, options) {
|
|
26997
27059
|
if (!options.enableUdpDiscovery) return [];
|
|
26998
27060
|
const logger = options.logger;
|
|
@@ -27234,8 +27296,8 @@ function isTcpFailureThatShouldFallbackToUdp(e) {
|
|
|
27234
27296
|
async function pingHost(host, timeoutMs = 3e3) {
|
|
27235
27297
|
return new Promise((resolve) => {
|
|
27236
27298
|
const { exec } = require("child_process");
|
|
27237
|
-
const
|
|
27238
|
-
const pingCmd =
|
|
27299
|
+
const platform2 = process.platform;
|
|
27300
|
+
const pingCmd = platform2 === "win32" ? `ping -n 1 -w ${timeoutMs} ${host}` : platform2 === "darwin" ? (
|
|
27239
27301
|
// macOS: -W is in milliseconds (Linux: seconds)
|
|
27240
27302
|
`ping -c 1 -W ${timeoutMs} ${host}`
|
|
27241
27303
|
) : (
|