@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.
@@ -2389,14 +2389,6 @@ var init_BaichuanVideoStream = __esm({
2389
2389
  this.profile,
2390
2390
  { variant: this.variant, client: this.client }
2391
2391
  );
2392
- if (this.client.getTransport?.() === "udp") {
2393
- await startPromise;
2394
- } else {
2395
- await Promise.race([
2396
- startPromise,
2397
- new Promise((resolve) => setTimeout(resolve, 400))
2398
- ]);
2399
- }
2400
2392
  const updateActiveMsgNum = () => {
2401
2393
  try {
2402
2394
  const getMsgNum = this.api.getActiveVideoMsgNumWithVariant;
@@ -2406,6 +2398,15 @@ var init_BaichuanVideoStream = __esm({
2406
2398
  }
2407
2399
  };
2408
2400
  updateActiveMsgNum();
2401
+ if (this.client.getTransport?.() === "udp") {
2402
+ await startPromise;
2403
+ } else {
2404
+ await Promise.race([
2405
+ startPromise,
2406
+ new Promise((resolve) => setTimeout(resolve, 400))
2407
+ ]);
2408
+ }
2409
+ updateActiveMsgNum();
2409
2410
  void startPromise.then(() => updateActiveMsgNum()).catch((e) => {
2410
2411
  const err = e instanceof Error ? e : new Error(String(e));
2411
2412
  this.emitSafeError(err);
@@ -7242,8 +7243,24 @@ var crypto = __toESM(require("crypto"), 1);
7242
7243
  // src/rfc/helpers.ts
7243
7244
  init_BaichuanVideoStream();
7244
7245
  async function* createNativeStream(api, channel, profile, options) {
7246
+ let client = options?.client;
7247
+ let dedicatedRelease;
7248
+ if (!client) {
7249
+ const variantSuffix = options?.variant && options.variant !== "default" ? `:${options.variant}` : "";
7250
+ const sessionKey = `live:native${variantSuffix}:ch${channel}:${profile}`;
7251
+ try {
7252
+ api.logger?.info?.(`[createNativeStream] acquiring dedicated session key=${sessionKey}`);
7253
+ const session = await api.createDedicatedSession(sessionKey);
7254
+ client = session.client;
7255
+ dedicatedRelease = session.release;
7256
+ api.logger?.info?.(`[createNativeStream] dedicated session acquired key=${sessionKey}`);
7257
+ } catch (e) {
7258
+ api.logger?.warn?.(`[createNativeStream] dedicated session failed, using shared client: ${e instanceof Error ? e.message : e}`);
7259
+ client = api.client;
7260
+ }
7261
+ }
7245
7262
  const videoStream = new BaichuanVideoStream({
7246
- client: api.client,
7263
+ client,
7247
7264
  api,
7248
7265
  channel,
7249
7266
  profile,
@@ -7257,9 +7274,15 @@ async function* createNativeStream(api, channel, profile, options) {
7257
7274
  let closed = false;
7258
7275
  const onError = (_error) => {
7259
7276
  closed = true;
7277
+ api.logger?.warn?.(
7278
+ `[createNativeStream] stream error \u2192 closed channel=${channel} profile=${profile} error=${_error?.message ?? _error}`
7279
+ );
7260
7280
  };
7261
7281
  const onClose = () => {
7262
7282
  closed = true;
7283
+ api.logger?.warn?.(
7284
+ `[createNativeStream] stream close \u2192 closed channel=${channel} profile=${profile}`
7285
+ );
7263
7286
  };
7264
7287
  try {
7265
7288
  videoStream.on("error", onError);
@@ -7370,6 +7393,10 @@ async function* createNativeStream(api, channel, profile, options) {
7370
7393
  }
7371
7394
  videoStream.removeListener("error", onError);
7372
7395
  videoStream.removeListener("close", onClose);
7396
+ if (dedicatedRelease) {
7397
+ dedicatedRelease().catch(() => {
7398
+ });
7399
+ }
7373
7400
  }
7374
7401
  }
7375
7402
 
@@ -7609,6 +7636,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
7609
7636
  tcpRtpFraming;
7610
7637
  active = false;
7611
7638
  flow;
7639
+ deviceId;
7640
+ dedicatedSessionRelease;
7612
7641
  // Authentication
7613
7642
  authCredentials = [];
7614
7643
  requireAuth;
@@ -7775,6 +7804,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
7775
7804
  this.path = options.path ?? `/stream/${this.profile}`;
7776
7805
  this.logger = options.logger ?? console;
7777
7806
  this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
7807
+ this.deviceId = options.deviceId;
7778
7808
  this.authCredentials = options.credentials ?? [];
7779
7809
  this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
7780
7810
  const transport = this.api.client.getTransport();
@@ -9160,14 +9190,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
9160
9190
  this.firstAudioPromise = new Promise((resolve) => {
9161
9191
  this.firstAudioResolve = resolve;
9162
9192
  });
9193
+ let dedicatedClient;
9194
+ const variantSuffix = this.variant && this.variant !== "default" ? `:${this.variant}` : "";
9195
+ const deviceIdPart = this.deviceId ?? "rtsp-server";
9196
+ const sessionKey = `live:${deviceIdPart}:ch${this.channel}:${this.profile}${variantSuffix}`;
9197
+ try {
9198
+ const session = await this.api.createDedicatedSession(sessionKey, this.logger);
9199
+ dedicatedClient = session.client;
9200
+ this.dedicatedSessionRelease = session.release;
9201
+ this.logger.info(
9202
+ `[rebroadcast] dedicated session acquired sessionKey=${sessionKey}`
9203
+ );
9204
+ } catch (e) {
9205
+ this.logger.warn(
9206
+ `[rebroadcast] failed to acquire dedicated session, falling back to shared socket: ${e}`
9207
+ );
9208
+ }
9163
9209
  this.logger.info(
9164
- `[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
9210
+ `[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size} dedicated=${!!dedicatedClient}`
9165
9211
  );
9166
9212
  await this.flow.startKeepAlive(this.api);
9167
9213
  this.nativeFanout = new NativeStreamFanout({
9168
9214
  maxQueueItems: 200,
9169
9215
  createSource: () => createNativeStream(this.api, this.channel, this.profile, {
9170
- variant: this.variant
9216
+ variant: this.variant,
9217
+ ...dedicatedClient ? { client: dedicatedClient } : {}
9171
9218
  }),
9172
9219
  onFrame: (frame) => {
9173
9220
  if (frame.audio) {
@@ -9225,6 +9272,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
9225
9272
  this.firstFrameResolve = null;
9226
9273
  this.nativeFanout = null;
9227
9274
  this.prebuffer = [];
9275
+ if (this.dedicatedSessionRelease) {
9276
+ const release = this.dedicatedSessionRelease;
9277
+ this.dedicatedSessionRelease = void 0;
9278
+ release().catch(() => {
9279
+ });
9280
+ }
9228
9281
  this.logger.info(
9229
9282
  `[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
9230
9283
  );
@@ -9294,6 +9347,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
9294
9347
  await fanout.stop();
9295
9348
  }
9296
9349
  this.prebuffer = [];
9350
+ if (this.dedicatedSessionRelease) {
9351
+ const release = this.dedicatedSessionRelease;
9352
+ this.dedicatedSessionRelease = void 0;
9353
+ try {
9354
+ await release();
9355
+ } catch {
9356
+ }
9357
+ }
9297
9358
  if (this.tempStreamGenerator) {
9298
9359
  try {
9299
9360
  await this.tempStreamGenerator.return(void 0);
@@ -9303,14 +9364,22 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events2.E
9303
9364
  }
9304
9365
  }
9305
9366
  /**
9306
- * Remove a client and stop native stream if no clients remain.
9367
+ * Remove a client and schedule native stream stop if no clients remain.
9368
+ * Uses a grace period so rapid reconnects (e.g. Frigate polling) reuse the running stream
9369
+ * and benefit from the prebuffer instead of waiting for a fresh keyframe.
9307
9370
  */
9308
9371
  removeClient(clientId) {
9309
9372
  if (this.connectedClients.has(clientId)) {
9310
9373
  this.connectedClients.delete(clientId);
9311
9374
  this.emit("clientDisconnected", clientId);
9312
9375
  if (this.connectedClients.size === 0) {
9313
- void this.stopNativeStream();
9376
+ this.clearNoClientAutoStopTimer();
9377
+ this.noClientAutoStopTimer = setTimeout(() => {
9378
+ if (this.connectedClients.size === 0) {
9379
+ void this.stopNativeStream();
9380
+ }
9381
+ }, 3e4);
9382
+ this.noClientAutoStopTimer?.unref?.();
9314
9383
  }
9315
9384
  }
9316
9385
  }