@apocaliss92/nodelink-js 0.4.1 → 0.4.3
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 +56 -603
- package/dist/{DiagnosticsTools-XIIYZDXL.js → DiagnosticsTools-55PR4WFD.js} +2 -2
- package/dist/{chunk-DUHWTZ7U.js → chunk-DEOMUWBN.js} +2 -2
- package/dist/chunk-DEOMUWBN.js.map +1 -0
- package/dist/{chunk-SDRNJQ5U.js → chunk-UHFJPQA4.js} +116 -52
- package/dist/chunk-UHFJPQA4.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +113 -49
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +113 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -9
- package/dist/index.d.ts +66 -7
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-DUHWTZ7U.js.map +0 -1
- package/dist/chunk-SDRNJQ5U.js.map +0 -1
- /package/dist/{DiagnosticsTools-XIIYZDXL.js.map → DiagnosticsTools-55PR4WFD.js.map} +0 -0
package/dist/cli/rtsp-server.cjs
CHANGED
|
@@ -7450,7 +7450,7 @@ async function captureModelFixtures(params) {
|
|
|
7450
7450
|
() => api.getDualLensChannelInfo(channel),
|
|
7451
7451
|
(v) => writeJsonSafe(path4.join(outDir, "dual-lens-info.json"), v)
|
|
7452
7452
|
);
|
|
7453
|
-
await capture("streamCombinationTest", async () => {
|
|
7453
|
+
if (!params.skipStreamCombinationTest) await capture("streamCombinationTest", async () => {
|
|
7454
7454
|
let dualLensInfo;
|
|
7455
7455
|
try {
|
|
7456
7456
|
dualLensInfo = await api.getDualLensChannelInfo(channel);
|
|
@@ -8070,6 +8070,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8070
8070
|
flow;
|
|
8071
8071
|
deviceId;
|
|
8072
8072
|
dedicatedSessionRelease;
|
|
8073
|
+
externalListener;
|
|
8073
8074
|
// Authentication
|
|
8074
8075
|
authCredentials = [];
|
|
8075
8076
|
requireAuth;
|
|
@@ -8112,6 +8113,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8112
8113
|
// Shared native stream fan-out (single camera stream, multiple RTSP clients)
|
|
8113
8114
|
nativeFanout = null;
|
|
8114
8115
|
noClientAutoStopTimer;
|
|
8116
|
+
/** After last RTSP client; 0 = never auto-stop native stream. */
|
|
8117
|
+
nativeStreamIdleStopMs;
|
|
8118
|
+
/** Primed-but-no-PLAY timeout; 0 = disabled. */
|
|
8119
|
+
nativeStreamPrimeIdleStopMs;
|
|
8115
8120
|
// Prebuffer: rolling ring of recent video frames for IDR-aligned fast startup.
|
|
8116
8121
|
// When a new client connects while the stream is already running it does not need
|
|
8117
8122
|
// to wait up to one full GOP interval for the next keyframe — we replay frames
|
|
@@ -8237,6 +8242,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8237
8242
|
this.logger = options.logger ?? console;
|
|
8238
8243
|
this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
|
|
8239
8244
|
this.deviceId = options.deviceId;
|
|
8245
|
+
this.externalListener = options.externalListener ?? false;
|
|
8246
|
+
this.nativeStreamIdleStopMs = options.nativeStreamIdleStopMs ?? 3e4;
|
|
8247
|
+
this.nativeStreamPrimeIdleStopMs = options.nativeStreamPrimeIdleStopMs ?? (this.nativeStreamIdleStopMs > 0 ? 15e3 : 0);
|
|
8240
8248
|
this.authCredentials = options.credentials ?? [];
|
|
8241
8249
|
this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
|
|
8242
8250
|
const transport = this.api.client.getTransport();
|
|
@@ -8367,41 +8375,56 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8367
8375
|
this.streamMetadata = { frameRate: 25 };
|
|
8368
8376
|
this.setFlowVideoType("H264", "metadata unavailable");
|
|
8369
8377
|
}
|
|
8370
|
-
this.
|
|
8371
|
-
this.
|
|
8372
|
-
|
|
8373
|
-
|
|
8374
|
-
|
|
8375
|
-
this.
|
|
8376
|
-
|
|
8377
|
-
|
|
8378
|
-
|
|
8379
|
-
|
|
8380
|
-
|
|
8378
|
+
if (!this.externalListener) {
|
|
8379
|
+
this.clientConnectionServer = net.createServer((socket) => {
|
|
8380
|
+
this.handleRtspConnection(socket);
|
|
8381
|
+
});
|
|
8382
|
+
await new Promise((resolve, reject) => {
|
|
8383
|
+
this.clientConnectionServer.listen(
|
|
8384
|
+
this.listenPort,
|
|
8385
|
+
this.listenHost,
|
|
8386
|
+
() => {
|
|
8387
|
+
const address = this.clientConnectionServer.address();
|
|
8388
|
+
if (address && typeof address === "object" && "port" in address) {
|
|
8389
|
+
this.listenPort = address.port;
|
|
8390
|
+
}
|
|
8391
|
+
resolve();
|
|
8381
8392
|
}
|
|
8382
|
-
|
|
8383
|
-
|
|
8384
|
-
|
|
8385
|
-
|
|
8386
|
-
reject(error);
|
|
8393
|
+
);
|
|
8394
|
+
this.clientConnectionServer.on("error", (error) => {
|
|
8395
|
+
reject(error);
|
|
8396
|
+
});
|
|
8387
8397
|
});
|
|
8388
|
-
}
|
|
8398
|
+
}
|
|
8389
8399
|
this.active = true;
|
|
8390
8400
|
this.logger.info(
|
|
8391
8401
|
`[BaichuanRtspServer] RTSP server started on ${this.listenHost}:${this.listenPort}, path: ${this.path}`
|
|
8392
8402
|
);
|
|
8393
8403
|
}
|
|
8404
|
+
/**
|
|
8405
|
+
* Accept an externally-routed RTSP connection.
|
|
8406
|
+
* Used in directHandoff mode where RtspProxyServer routes sockets here.
|
|
8407
|
+
* @param socket - The client TCP socket (already authenticated by proxy)
|
|
8408
|
+
* @param initialBuffer - Any bytes already read during path parsing/auth
|
|
8409
|
+
*/
|
|
8410
|
+
acceptConnection(socket, initialBuffer) {
|
|
8411
|
+
if (!this.active) {
|
|
8412
|
+
socket.end("RTSP/1.0 503 Service Unavailable\r\n\r\n");
|
|
8413
|
+
return;
|
|
8414
|
+
}
|
|
8415
|
+
this.handleRtspConnection(socket, initialBuffer);
|
|
8416
|
+
}
|
|
8394
8417
|
/**
|
|
8395
8418
|
* Handle RTSP connection from a client.
|
|
8396
8419
|
*/
|
|
8397
|
-
handleRtspConnection(socket) {
|
|
8420
|
+
handleRtspConnection(socket, initialBuffer) {
|
|
8398
8421
|
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
8399
8422
|
const connectTime = Date.now();
|
|
8400
8423
|
this.logger.info(
|
|
8401
8424
|
`[rebroadcast] client connected client=${clientId} path=${this.path} profile=${this.profile} channel=${this.channel}`
|
|
8402
8425
|
);
|
|
8403
8426
|
let sessionId = "";
|
|
8404
|
-
let buffer = Buffer.alloc(0);
|
|
8427
|
+
let buffer = initialBuffer ?? Buffer.alloc(0);
|
|
8405
8428
|
let clientFfmpeg;
|
|
8406
8429
|
let useTcpInterleaved = false;
|
|
8407
8430
|
let clientUdpSocket = null;
|
|
@@ -8486,8 +8509,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8486
8509
|
}
|
|
8487
8510
|
cleanup();
|
|
8488
8511
|
});
|
|
8489
|
-
|
|
8490
|
-
buffer = Buffer.concat([buffer, data]);
|
|
8512
|
+
const processBuffer = async () => {
|
|
8491
8513
|
while (buffer.includes("\r\n\r\n")) {
|
|
8492
8514
|
const endIndex = buffer.indexOf("\r\n\r\n");
|
|
8493
8515
|
const requestText = buffer.subarray(0, endIndex).toString();
|
|
@@ -8762,7 +8784,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
8762
8784
|
sendResponse(501, "Not Implemented");
|
|
8763
8785
|
}
|
|
8764
8786
|
}
|
|
8787
|
+
};
|
|
8788
|
+
socket.on("data", (data) => {
|
|
8789
|
+
buffer = Buffer.concat([buffer, data]);
|
|
8790
|
+
void processBuffer();
|
|
8765
8791
|
});
|
|
8792
|
+
if (buffer.includes("\r\n\r\n")) {
|
|
8793
|
+
void processBuffer();
|
|
8794
|
+
}
|
|
8766
8795
|
}
|
|
8767
8796
|
/**
|
|
8768
8797
|
* Generate SDP (Session Description Protocol) for RTSP DESCRIBE.
|
|
@@ -9730,34 +9759,42 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
9730
9759
|
res.seenFirstVideoKeyframe = false;
|
|
9731
9760
|
res.rtpSentVideoConfig = false;
|
|
9732
9761
|
}
|
|
9733
|
-
if (this.dedicatedSessionRelease) {
|
|
9734
|
-
const release = this.dedicatedSessionRelease;
|
|
9735
|
-
this.dedicatedSessionRelease = void 0;
|
|
9736
|
-
release().catch(() => {
|
|
9737
|
-
});
|
|
9738
|
-
}
|
|
9739
9762
|
this.logger.info(
|
|
9740
9763
|
`[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
|
|
9741
9764
|
);
|
|
9742
|
-
|
|
9743
|
-
this.
|
|
9744
|
-
|
|
9745
|
-
|
|
9746
|
-
|
|
9747
|
-
|
|
9765
|
+
const releaseAndRestart = async () => {
|
|
9766
|
+
if (this.dedicatedSessionRelease) {
|
|
9767
|
+
const release = this.dedicatedSessionRelease;
|
|
9768
|
+
this.dedicatedSessionRelease = void 0;
|
|
9769
|
+
try {
|
|
9770
|
+
await release();
|
|
9771
|
+
} catch {
|
|
9772
|
+
}
|
|
9773
|
+
}
|
|
9774
|
+
if (this.connectedClients.size > 0) {
|
|
9775
|
+
this.logger.info(
|
|
9776
|
+
`[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
|
|
9777
|
+
);
|
|
9778
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
9779
|
+
void this.startNativeStream();
|
|
9780
|
+
}
|
|
9781
|
+
};
|
|
9782
|
+
void releaseAndRestart();
|
|
9748
9783
|
}
|
|
9749
9784
|
});
|
|
9750
9785
|
this.nativeFanout.start();
|
|
9751
9786
|
this.clearNoClientAutoStopTimer();
|
|
9752
|
-
this.
|
|
9753
|
-
|
|
9754
|
-
this.
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9787
|
+
if (this.nativeStreamPrimeIdleStopMs > 0) {
|
|
9788
|
+
this.noClientAutoStopTimer = setTimeout(() => {
|
|
9789
|
+
if (this.connectedClients.size === 0) {
|
|
9790
|
+
this.rtspDebugLog(
|
|
9791
|
+
`Auto-stopping primed native stream (no clients connected)`
|
|
9792
|
+
);
|
|
9793
|
+
void this.stopNativeStream();
|
|
9794
|
+
}
|
|
9795
|
+
}, this.nativeStreamPrimeIdleStopMs);
|
|
9796
|
+
this.noClientAutoStopTimer?.unref?.();
|
|
9797
|
+
}
|
|
9761
9798
|
}
|
|
9762
9799
|
markFirstFrameReceived() {
|
|
9763
9800
|
if (!this.firstFrameReceived && this.firstFrameResolve) {
|
|
@@ -9832,12 +9869,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
9832
9869
|
this.emit("clientDisconnected", clientId);
|
|
9833
9870
|
if (this.connectedClients.size === 0) {
|
|
9834
9871
|
this.clearNoClientAutoStopTimer();
|
|
9835
|
-
this.
|
|
9836
|
-
|
|
9837
|
-
|
|
9838
|
-
|
|
9839
|
-
|
|
9840
|
-
|
|
9872
|
+
if (this.nativeStreamIdleStopMs > 0) {
|
|
9873
|
+
this.noClientAutoStopTimer = setTimeout(() => {
|
|
9874
|
+
if (this.connectedClients.size === 0) {
|
|
9875
|
+
void this.stopNativeStream();
|
|
9876
|
+
}
|
|
9877
|
+
}, this.nativeStreamIdleStopMs);
|
|
9878
|
+
this.noClientAutoStopTimer?.unref?.();
|
|
9879
|
+
}
|
|
9841
9880
|
}
|
|
9842
9881
|
}
|
|
9843
9882
|
}
|
|
@@ -9968,6 +10007,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
|
|
|
9968
10007
|
getClientCount() {
|
|
9969
10008
|
return this.connectedClients.size;
|
|
9970
10009
|
}
|
|
10010
|
+
/**
|
|
10011
|
+
* Subscribe to the raw native stream for diagnostic purposes.
|
|
10012
|
+
* The subscriber receives the same frames as RTSP clients.
|
|
10013
|
+
* Counts as a "consumer" for lifecycle — prevents auto-stop while subscribed.
|
|
10014
|
+
* If the native stream is not active, starts it automatically.
|
|
10015
|
+
*/
|
|
10016
|
+
async subscribeDiagnostic(id) {
|
|
10017
|
+
this.connectedClients.add(`diag:${id}`);
|
|
10018
|
+
if (!this.nativeStreamActive) {
|
|
10019
|
+
await this.startNativeStream();
|
|
10020
|
+
}
|
|
10021
|
+
return this.nativeFanout.subscribe(`diag:${id}`);
|
|
10022
|
+
}
|
|
10023
|
+
/**
|
|
10024
|
+
* Unsubscribe a diagnostic session.
|
|
10025
|
+
*/
|
|
10026
|
+
unsubscribeDiagnostic(id) {
|
|
10027
|
+
this.removeClient(`diag:${id}`);
|
|
10028
|
+
}
|
|
10029
|
+
/**
|
|
10030
|
+
* Returns detected audio metadata (available after first audio frame).
|
|
10031
|
+
*/
|
|
10032
|
+
getAudioInfo() {
|
|
10033
|
+
return this.audioInfo;
|
|
10034
|
+
}
|
|
9971
10035
|
};
|
|
9972
10036
|
|
|
9973
10037
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|