@apocaliss92/nodelink-js 0.2.3 → 0.2.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/dist/index.d.cts CHANGED
@@ -3323,6 +3323,12 @@ interface BaichuanRtspServerOptions {
3323
3323
  }>;
3324
3324
  /** Require authentication for RTSP connections (default: false if no credentials set) */
3325
3325
  requireAuth?: boolean;
3326
+ /**
3327
+ * External identifier for dedicated socket session.
3328
+ * When provided, a dedicated BaichuanClient is created for the stream,
3329
+ * isolating it from other streams on the shared socket (avoids streamType mismatch).
3330
+ */
3331
+ deviceId?: string;
3326
3332
  }
3327
3333
  /**
3328
3334
  * BaichuanRtspServer - RTSP server that serves a Baichuan video stream.
@@ -3352,6 +3358,8 @@ declare class BaichuanRtspServer extends EventEmitter<{
3352
3358
  private tcpRtpFraming;
3353
3359
  private active;
3354
3360
  private flow;
3361
+ private deviceId;
3362
+ private dedicatedSessionRelease;
3355
3363
  private authCredentials;
3356
3364
  private requireAuth;
3357
3365
  private authNonces;
@@ -3440,7 +3448,9 @@ declare class BaichuanRtspServer extends EventEmitter<{
3440
3448
  */
3441
3449
  private stopNativeStream;
3442
3450
  /**
3443
- * Remove a client and stop native stream if no clients remain.
3451
+ * Remove a client and schedule native stream stop if no clients remain.
3452
+ * Uses a grace period so rapid reconnects (e.g. Frigate polling) reuse the running stream
3453
+ * and benefit from the prebuffer instead of waiting for a fresh keyframe.
3444
3454
  */
3445
3455
  private removeClient;
3446
3456
  /**
@@ -7416,6 +7426,8 @@ declare class BaichuanEventEmitter {
7416
7426
  */
7417
7427
  declare function createNativeStream(api: ReolinkBaichuanApi, channel: number, profile: StreamProfile, options?: {
7418
7428
  variant?: NativeVideoStreamVariant;
7429
+ /** Optional dedicated BaichuanClient for stream isolation. When omitted, uses api.client (shared). */
7430
+ client?: BaichuanClient;
7419
7431
  }): AsyncGenerator<{
7420
7432
  audio: boolean;
7421
7433
  data: Buffer;
@@ -8169,6 +8181,13 @@ interface BaichuanHlsServerOptions {
8169
8181
  playlistSize?: number;
8170
8182
  /** ffmpeg binary path (default: "ffmpeg") */
8171
8183
  ffmpegPath?: string;
8184
+ /**
8185
+ * External video stream from a shared pool (e.g. createRfc4571TcpServer).
8186
+ * When provided, the HLS server subscribes to this stream's events instead
8187
+ * of creating its own via createNativeStream. This allows multiple outputs
8188
+ * (MJPEG, HLS, WebRTC) to share a single camera streaming session.
8189
+ */
8190
+ externalVideoStream?: BaichuanVideoStream;
8172
8191
  /** Logger callback */
8173
8192
  logger?: (level: "debug" | "info" | "warn" | "error", message: string) => void;
8174
8193
  }
@@ -8190,6 +8209,7 @@ declare class BaichuanHlsServer extends EventEmitter {
8190
8209
  private readonly segmentDuration;
8191
8210
  private readonly playlistSize;
8192
8211
  private readonly ffmpegPath;
8212
+ private readonly externalVideoStream;
8193
8213
  private readonly log;
8194
8214
  private outputDir;
8195
8215
  private createdTempDir;
@@ -8235,6 +8255,11 @@ declare class BaichuanHlsServer extends EventEmitter {
8235
8255
  data: Buffer;
8236
8256
  contentType: string;
8237
8257
  } | null>;
8258
+ /**
8259
+ * Wrap a BaichuanVideoStream's videoAccessUnit events into an async generator
8260
+ * compatible with createNativeStream's output format.
8261
+ */
8262
+ private wrapVideoStreamAsGenerator;
8238
8263
  private pumpNativeToFfmpeg;
8239
8264
  private spawnFfmpeg;
8240
8265
  }
package/dist/index.d.ts CHANGED
@@ -994,6 +994,7 @@ export declare class BaichuanHlsServer extends EventEmitter {
994
994
  private readonly segmentDuration;
995
995
  private readonly playlistSize;
996
996
  private readonly ffmpegPath;
997
+ private readonly externalVideoStream;
997
998
  private readonly log;
998
999
  private outputDir;
999
1000
  private createdTempDir;
@@ -1039,6 +1040,11 @@ export declare class BaichuanHlsServer extends EventEmitter {
1039
1040
  data: Buffer;
1040
1041
  contentType: string;
1041
1042
  } | null>;
1043
+ /**
1044
+ * Wrap a BaichuanVideoStream's videoAccessUnit events into an async generator
1045
+ * compatible with createNativeStream's output format.
1046
+ */
1047
+ private wrapVideoStreamAsGenerator;
1042
1048
  private pumpNativeToFfmpeg;
1043
1049
  private spawnFfmpeg;
1044
1050
  }
@@ -1060,6 +1066,13 @@ export declare interface BaichuanHlsServerOptions {
1060
1066
  playlistSize?: number;
1061
1067
  /** ffmpeg binary path (default: "ffmpeg") */
1062
1068
  ffmpegPath?: string;
1069
+ /**
1070
+ * External video stream from a shared pool (e.g. createRfc4571TcpServer).
1071
+ * When provided, the HLS server subscribes to this stream's events instead
1072
+ * of creating its own via createNativeStream. This allows multiple outputs
1073
+ * (MJPEG, HLS, WebRTC) to share a single camera streaming session.
1074
+ */
1075
+ externalVideoStream?: BaichuanVideoStream;
1063
1076
  /** Logger callback */
1064
1077
  logger?: (level: "debug" | "info" | "warn" | "error", message: string) => void;
1065
1078
  }
@@ -1298,6 +1311,8 @@ export declare class BaichuanRtspServer extends EventEmitter<{
1298
1311
  private tcpRtpFraming;
1299
1312
  private active;
1300
1313
  private flow;
1314
+ private deviceId;
1315
+ private dedicatedSessionRelease;
1301
1316
  private authCredentials;
1302
1317
  private requireAuth;
1303
1318
  private authNonces;
@@ -1386,7 +1401,9 @@ export declare class BaichuanRtspServer extends EventEmitter<{
1386
1401
  */
1387
1402
  private stopNativeStream;
1388
1403
  /**
1389
- * Remove a client and stop native stream if no clients remain.
1404
+ * Remove a client and schedule native stream stop if no clients remain.
1405
+ * Uses a grace period so rapid reconnects (e.g. Frigate polling) reuse the running stream
1406
+ * and benefit from the prebuffer instead of waiting for a fresh keyframe.
1390
1407
  */
1391
1408
  private removeClient;
1392
1409
  /**
@@ -1440,6 +1457,12 @@ export declare interface BaichuanRtspServerOptions {
1440
1457
  }>;
1441
1458
  /** Require authentication for RTSP connections (default: false if no credentials set) */
1442
1459
  requireAuth?: boolean;
1460
+ /**
1461
+ * External identifier for dedicated socket session.
1462
+ * When provided, a dedicated BaichuanClient is created for the stream,
1463
+ * isolating it from other streams on the shared socket (avoids streamType mismatch).
1464
+ */
1465
+ deviceId?: string;
1443
1466
  }
1444
1467
 
1445
1468
  export declare type BaichuanSerialPush = {
@@ -3290,6 +3313,8 @@ export declare function createMjpegBoundary(): string;
3290
3313
  */
3291
3314
  export declare function createNativeStream(api: ReolinkBaichuanApi, channel: number, profile: StreamProfile, options?: {
3292
3315
  variant?: NativeVideoStreamVariant;
3316
+ /** Optional dedicated BaichuanClient for stream isolation. When omitted, uses api.client (shared). */
3317
+ client?: BaichuanClient;
3293
3318
  }): AsyncGenerator<{
3294
3319
  audio: boolean;
3295
3320
  data: Buffer;
package/dist/index.js CHANGED
@@ -38,7 +38,7 @@ import {
38
38
  normalizeUid,
39
39
  parseSupportXml,
40
40
  setGlobalLogger
41
- } from "./chunk-RWYEGEWG.js";
41
+ } from "./chunk-EG5IY3CM.js";
42
42
  import {
43
43
  AesStreamDecryptor,
44
44
  BC_AES_IV,
@@ -215,7 +215,7 @@ import {
215
215
  testChannelStreams,
216
216
  xmlEscape,
217
217
  zipDirectory
218
- } from "./chunk-NLTB7GTA.js";
218
+ } from "./chunk-APEEZ4UN.js";
219
219
 
220
220
  // src/reolink/baichuan/HlsSessionManager.ts
221
221
  var withTimeout = async (p, ms, label) => {
@@ -6494,6 +6494,7 @@ var BaichuanHlsServer = class extends EventEmitter6 {
6494
6494
  segmentDuration;
6495
6495
  playlistSize;
6496
6496
  ffmpegPath;
6497
+ externalVideoStream;
6497
6498
  log;
6498
6499
  outputDir = null;
6499
6500
  createdTempDir = false;
@@ -6520,6 +6521,7 @@ var BaichuanHlsServer = class extends EventEmitter6 {
6520
6521
  this.outputDir = options.outputDir;
6521
6522
  this.createdTempDir = false;
6522
6523
  }
6524
+ this.externalVideoStream = options.externalVideoStream;
6523
6525
  this.log = options.logger ?? (() => {
6524
6526
  });
6525
6527
  }
@@ -6544,12 +6546,16 @@ var BaichuanHlsServer = class extends EventEmitter6 {
6544
6546
  this.playlistPath = path.join(this.outputDir, "playlist.m3u8");
6545
6547
  this.segmentPattern = path.join(this.outputDir, "segment_%05d.ts");
6546
6548
  this.log("info", `Starting HLS stream to ${this.outputDir}`);
6547
- this.nativeStream = createNativeStream(
6548
- this.api,
6549
- this.channel,
6550
- this.profile,
6551
- this.variant ? { variant: this.variant } : void 0
6552
- );
6549
+ if (this.externalVideoStream) {
6550
+ this.nativeStream = this.wrapVideoStreamAsGenerator(this.externalVideoStream);
6551
+ } else {
6552
+ this.nativeStream = createNativeStream(
6553
+ this.api,
6554
+ this.channel,
6555
+ this.profile,
6556
+ this.variant ? { variant: this.variant } : void 0
6557
+ );
6558
+ }
6553
6559
  this.pumpPromise = this.pumpNativeToFfmpeg();
6554
6560
  this.startedAt = /* @__PURE__ */ new Date();
6555
6561
  this.state = "running";
@@ -6668,6 +6674,56 @@ var BaichuanHlsServer = class extends EventEmitter6 {
6668
6674
  // ============================================================================
6669
6675
  // Private Methods
6670
6676
  // ============================================================================
6677
+ /**
6678
+ * Wrap a BaichuanVideoStream's videoAccessUnit events into an async generator
6679
+ * compatible with createNativeStream's output format.
6680
+ */
6681
+ async *wrapVideoStreamAsGenerator(videoStream) {
6682
+ const queue = [];
6683
+ let resolve = null;
6684
+ let done = false;
6685
+ const onFrame = (au) => {
6686
+ queue.push(au);
6687
+ resolve?.();
6688
+ resolve = null;
6689
+ };
6690
+ const onClose = () => {
6691
+ done = true;
6692
+ resolve?.();
6693
+ resolve = null;
6694
+ };
6695
+ const onError = () => {
6696
+ done = true;
6697
+ resolve?.();
6698
+ resolve = null;
6699
+ };
6700
+ videoStream.on("videoAccessUnit", onFrame);
6701
+ videoStream.on("close", onClose);
6702
+ videoStream.on("error", onError);
6703
+ try {
6704
+ while (!done) {
6705
+ if (queue.length === 0) {
6706
+ await new Promise((r) => {
6707
+ resolve = r;
6708
+ });
6709
+ }
6710
+ while (queue.length > 0) {
6711
+ const frame = queue.shift();
6712
+ yield {
6713
+ audio: false,
6714
+ data: frame.data,
6715
+ videoType: frame.videoType,
6716
+ isKeyframe: frame.isKeyframe,
6717
+ microseconds: frame.microseconds
6718
+ };
6719
+ }
6720
+ }
6721
+ } finally {
6722
+ videoStream.removeListener("videoAccessUnit", onFrame);
6723
+ videoStream.removeListener("close", onClose);
6724
+ videoStream.removeListener("error", onError);
6725
+ }
6726
+ }
6671
6727
  async pumpNativeToFfmpeg() {
6672
6728
  if (!this.nativeStream || !this.playlistPath || !this.segmentPattern) {
6673
6729
  return;