@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.
@@ -144,7 +144,7 @@ import {
144
144
  talkTraceLog,
145
145
  traceLog,
146
146
  xmlEscape
147
- } from "./chunk-DUHWTZ7U.js";
147
+ } from "./chunk-DEOMUWBN.js";
148
148
 
149
149
  // src/protocol/framing.ts
150
150
  function encodeHeader(h) {
@@ -5518,6 +5518,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5518
5518
  flow;
5519
5519
  deviceId;
5520
5520
  dedicatedSessionRelease;
5521
+ externalListener;
5521
5522
  // Authentication
5522
5523
  authCredentials = [];
5523
5524
  requireAuth;
@@ -5560,6 +5561,10 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5560
5561
  // Shared native stream fan-out (single camera stream, multiple RTSP clients)
5561
5562
  nativeFanout = null;
5562
5563
  noClientAutoStopTimer;
5564
+ /** After last RTSP client; 0 = never auto-stop native stream. */
5565
+ nativeStreamIdleStopMs;
5566
+ /** Primed-but-no-PLAY timeout; 0 = disabled. */
5567
+ nativeStreamPrimeIdleStopMs;
5563
5568
  // Prebuffer: rolling ring of recent video frames for IDR-aligned fast startup.
5564
5569
  // When a new client connects while the stream is already running it does not need
5565
5570
  // to wait up to one full GOP interval for the next keyframe — we replay frames
@@ -5685,6 +5690,9 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5685
5690
  this.logger = options.logger ?? console;
5686
5691
  this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
5687
5692
  this.deviceId = options.deviceId;
5693
+ this.externalListener = options.externalListener ?? false;
5694
+ this.nativeStreamIdleStopMs = options.nativeStreamIdleStopMs ?? 3e4;
5695
+ this.nativeStreamPrimeIdleStopMs = options.nativeStreamPrimeIdleStopMs ?? (this.nativeStreamIdleStopMs > 0 ? 15e3 : 0);
5688
5696
  this.authCredentials = options.credentials ?? [];
5689
5697
  this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
5690
5698
  const transport = this.api.client.getTransport();
@@ -5815,41 +5823,56 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5815
5823
  this.streamMetadata = { frameRate: 25 };
5816
5824
  this.setFlowVideoType("H264", "metadata unavailable");
5817
5825
  }
5818
- this.clientConnectionServer = net2.createServer((socket) => {
5819
- this.handleRtspConnection(socket);
5820
- });
5821
- await new Promise((resolve, reject) => {
5822
- this.clientConnectionServer.listen(
5823
- this.listenPort,
5824
- this.listenHost,
5825
- () => {
5826
- const address = this.clientConnectionServer.address();
5827
- if (address && typeof address === "object" && "port" in address) {
5828
- this.listenPort = address.port;
5826
+ if (!this.externalListener) {
5827
+ this.clientConnectionServer = net2.createServer((socket) => {
5828
+ this.handleRtspConnection(socket);
5829
+ });
5830
+ await new Promise((resolve, reject) => {
5831
+ this.clientConnectionServer.listen(
5832
+ this.listenPort,
5833
+ this.listenHost,
5834
+ () => {
5835
+ const address = this.clientConnectionServer.address();
5836
+ if (address && typeof address === "object" && "port" in address) {
5837
+ this.listenPort = address.port;
5838
+ }
5839
+ resolve();
5829
5840
  }
5830
- resolve();
5831
- }
5832
- );
5833
- this.clientConnectionServer.on("error", (error) => {
5834
- reject(error);
5841
+ );
5842
+ this.clientConnectionServer.on("error", (error) => {
5843
+ reject(error);
5844
+ });
5835
5845
  });
5836
- });
5846
+ }
5837
5847
  this.active = true;
5838
5848
  this.logger.info(
5839
5849
  `[BaichuanRtspServer] RTSP server started on ${this.listenHost}:${this.listenPort}, path: ${this.path}`
5840
5850
  );
5841
5851
  }
5852
+ /**
5853
+ * Accept an externally-routed RTSP connection.
5854
+ * Used in directHandoff mode where RtspProxyServer routes sockets here.
5855
+ * @param socket - The client TCP socket (already authenticated by proxy)
5856
+ * @param initialBuffer - Any bytes already read during path parsing/auth
5857
+ */
5858
+ acceptConnection(socket, initialBuffer) {
5859
+ if (!this.active) {
5860
+ socket.end("RTSP/1.0 503 Service Unavailable\r\n\r\n");
5861
+ return;
5862
+ }
5863
+ this.handleRtspConnection(socket, initialBuffer);
5864
+ }
5842
5865
  /**
5843
5866
  * Handle RTSP connection from a client.
5844
5867
  */
5845
- handleRtspConnection(socket) {
5868
+ handleRtspConnection(socket, initialBuffer) {
5846
5869
  const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
5847
5870
  const connectTime = Date.now();
5848
5871
  this.logger.info(
5849
5872
  `[rebroadcast] client connected client=${clientId} path=${this.path} profile=${this.profile} channel=${this.channel}`
5850
5873
  );
5851
5874
  let sessionId = "";
5852
- let buffer = Buffer.alloc(0);
5875
+ let buffer = initialBuffer ?? Buffer.alloc(0);
5853
5876
  let clientFfmpeg;
5854
5877
  let useTcpInterleaved = false;
5855
5878
  let clientUdpSocket = null;
@@ -5934,8 +5957,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5934
5957
  }
5935
5958
  cleanup();
5936
5959
  });
5937
- socket.on("data", async (data) => {
5938
- buffer = Buffer.concat([buffer, data]);
5960
+ const processBuffer = async () => {
5939
5961
  while (buffer.includes("\r\n\r\n")) {
5940
5962
  const endIndex = buffer.indexOf("\r\n\r\n");
5941
5963
  const requestText = buffer.subarray(0, endIndex).toString();
@@ -6210,7 +6232,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
6210
6232
  sendResponse(501, "Not Implemented");
6211
6233
  }
6212
6234
  }
6235
+ };
6236
+ socket.on("data", (data) => {
6237
+ buffer = Buffer.concat([buffer, data]);
6238
+ void processBuffer();
6213
6239
  });
6240
+ if (buffer.includes("\r\n\r\n")) {
6241
+ void processBuffer();
6242
+ }
6214
6243
  }
6215
6244
  /**
6216
6245
  * Generate SDP (Session Description Protocol) for RTSP DESCRIBE.
@@ -7178,34 +7207,42 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7178
7207
  res.seenFirstVideoKeyframe = false;
7179
7208
  res.rtpSentVideoConfig = false;
7180
7209
  }
7181
- if (this.dedicatedSessionRelease) {
7182
- const release = this.dedicatedSessionRelease;
7183
- this.dedicatedSessionRelease = void 0;
7184
- release().catch(() => {
7185
- });
7186
- }
7187
7210
  this.logger.info(
7188
7211
  `[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
7189
7212
  );
7190
- if (this.connectedClients.size > 0) {
7191
- this.logger.info(
7192
- `[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
7193
- );
7194
- setImmediate(() => void this.startNativeStream());
7195
- }
7213
+ const releaseAndRestart = async () => {
7214
+ if (this.dedicatedSessionRelease) {
7215
+ const release = this.dedicatedSessionRelease;
7216
+ this.dedicatedSessionRelease = void 0;
7217
+ try {
7218
+ await release();
7219
+ } catch {
7220
+ }
7221
+ }
7222
+ if (this.connectedClients.size > 0) {
7223
+ this.logger.info(
7224
+ `[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
7225
+ );
7226
+ await new Promise((r) => setTimeout(r, 500));
7227
+ void this.startNativeStream();
7228
+ }
7229
+ };
7230
+ void releaseAndRestart();
7196
7231
  }
7197
7232
  });
7198
7233
  this.nativeFanout.start();
7199
7234
  this.clearNoClientAutoStopTimer();
7200
- this.noClientAutoStopTimer = setTimeout(() => {
7201
- if (this.connectedClients.size === 0) {
7202
- this.rtspDebugLog(
7203
- `Auto-stopping primed native stream (no clients connected)`
7204
- );
7205
- void this.stopNativeStream();
7206
- }
7207
- }, 15e3);
7208
- this.noClientAutoStopTimer?.unref?.();
7235
+ if (this.nativeStreamPrimeIdleStopMs > 0) {
7236
+ this.noClientAutoStopTimer = setTimeout(() => {
7237
+ if (this.connectedClients.size === 0) {
7238
+ this.rtspDebugLog(
7239
+ `Auto-stopping primed native stream (no clients connected)`
7240
+ );
7241
+ void this.stopNativeStream();
7242
+ }
7243
+ }, this.nativeStreamPrimeIdleStopMs);
7244
+ this.noClientAutoStopTimer?.unref?.();
7245
+ }
7209
7246
  }
7210
7247
  markFirstFrameReceived() {
7211
7248
  if (!this.firstFrameReceived && this.firstFrameResolve) {
@@ -7280,12 +7317,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7280
7317
  this.emit("clientDisconnected", clientId);
7281
7318
  if (this.connectedClients.size === 0) {
7282
7319
  this.clearNoClientAutoStopTimer();
7283
- this.noClientAutoStopTimer = setTimeout(() => {
7284
- if (this.connectedClients.size === 0) {
7285
- void this.stopNativeStream();
7286
- }
7287
- }, 3e4);
7288
- this.noClientAutoStopTimer?.unref?.();
7320
+ if (this.nativeStreamIdleStopMs > 0) {
7321
+ this.noClientAutoStopTimer = setTimeout(() => {
7322
+ if (this.connectedClients.size === 0) {
7323
+ void this.stopNativeStream();
7324
+ }
7325
+ }, this.nativeStreamIdleStopMs);
7326
+ this.noClientAutoStopTimer?.unref?.();
7327
+ }
7289
7328
  }
7290
7329
  }
7291
7330
  }
@@ -7416,6 +7455,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7416
7455
  getClientCount() {
7417
7456
  return this.connectedClients.size;
7418
7457
  }
7458
+ /**
7459
+ * Subscribe to the raw native stream for diagnostic purposes.
7460
+ * The subscriber receives the same frames as RTSP clients.
7461
+ * Counts as a "consumer" for lifecycle — prevents auto-stop while subscribed.
7462
+ * If the native stream is not active, starts it automatically.
7463
+ */
7464
+ async subscribeDiagnostic(id) {
7465
+ this.connectedClients.add(`diag:${id}`);
7466
+ if (!this.nativeStreamActive) {
7467
+ await this.startNativeStream();
7468
+ }
7469
+ return this.nativeFanout.subscribe(`diag:${id}`);
7470
+ }
7471
+ /**
7472
+ * Unsubscribe a diagnostic session.
7473
+ */
7474
+ unsubscribeDiagnostic(id) {
7475
+ this.removeClient(`diag:${id}`);
7476
+ }
7477
+ /**
7478
+ * Returns detected audio metadata (available after first audio frame).
7479
+ */
7480
+ getAudioInfo() {
7481
+ return this.audioInfo;
7482
+ }
7419
7483
  };
7420
7484
 
7421
7485
  // src/reolink/baichuan/capabilities.ts
@@ -17818,7 +17882,7 @@ ${xml}`
17818
17882
  * @returns Test results for all stream types and profiles
17819
17883
  */
17820
17884
  async testChannelStreams(channel, logger) {
17821
- const { testChannelStreams } = await import("./DiagnosticsTools-XIIYZDXL.js");
17885
+ const { testChannelStreams } = await import("./DiagnosticsTools-55PR4WFD.js");
17822
17886
  return await testChannelStreams({
17823
17887
  api: this,
17824
17888
  channel: this.normalizeChannel(channel),
@@ -17834,7 +17898,7 @@ ${xml}`
17834
17898
  * @returns Complete diagnostics for all channels and streams
17835
17899
  */
17836
17900
  async collectMultifocalDiagnostics(logger) {
17837
- const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-XIIYZDXL.js");
17901
+ const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-55PR4WFD.js");
17838
17902
  return await collectMultifocalDiagnostics({
17839
17903
  api: this,
17840
17904
  logger
@@ -21516,4 +21580,4 @@ export {
21516
21580
  isTcpFailureThatShouldFallbackToUdp,
21517
21581
  autoDetectDeviceType
21518
21582
  };
21519
- //# sourceMappingURL=chunk-SDRNJQ5U.js.map
21583
+ //# sourceMappingURL=chunk-UHFJPQA4.js.map