@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.
@@ -144,7 +144,7 @@ import {
144
144
  talkTraceLog,
145
145
  traceLog,
146
146
  xmlEscape
147
- } from "./chunk-NLTB7GTA.js";
147
+ } from "./chunk-APEEZ4UN.js";
148
148
 
149
149
  // src/protocol/framing.ts
150
150
  function encodeHeader(h) {
@@ -5089,8 +5089,24 @@ var BaichuanEventEmitter = class {
5089
5089
  }
5090
5090
  };
5091
5091
  async function* createNativeStream(api, channel, profile, options) {
5092
+ let client = options?.client;
5093
+ let dedicatedRelease;
5094
+ if (!client) {
5095
+ const variantSuffix = options?.variant && options.variant !== "default" ? `:${options.variant}` : "";
5096
+ const sessionKey = `live:native${variantSuffix}:ch${channel}:${profile}`;
5097
+ try {
5098
+ api.logger?.info?.(`[createNativeStream] acquiring dedicated session key=${sessionKey}`);
5099
+ const session = await api.createDedicatedSession(sessionKey);
5100
+ client = session.client;
5101
+ dedicatedRelease = session.release;
5102
+ api.logger?.info?.(`[createNativeStream] dedicated session acquired key=${sessionKey}`);
5103
+ } catch (e) {
5104
+ api.logger?.warn?.(`[createNativeStream] dedicated session failed, using shared client: ${e instanceof Error ? e.message : e}`);
5105
+ client = api.client;
5106
+ }
5107
+ }
5092
5108
  const videoStream = new BaichuanVideoStream({
5093
- client: api.client,
5109
+ client,
5094
5110
  api,
5095
5111
  channel,
5096
5112
  profile,
@@ -5104,9 +5120,15 @@ async function* createNativeStream(api, channel, profile, options) {
5104
5120
  let closed = false;
5105
5121
  const onError = (_error) => {
5106
5122
  closed = true;
5123
+ api.logger?.warn?.(
5124
+ `[createNativeStream] stream error \u2192 closed channel=${channel} profile=${profile} error=${_error?.message ?? _error}`
5125
+ );
5107
5126
  };
5108
5127
  const onClose = () => {
5109
5128
  closed = true;
5129
+ api.logger?.warn?.(
5130
+ `[createNativeStream] stream close \u2192 closed channel=${channel} profile=${profile}`
5131
+ );
5110
5132
  };
5111
5133
  try {
5112
5134
  videoStream.on("error", onError);
@@ -5217,6 +5239,10 @@ async function* createNativeStream(api, channel, profile, options) {
5217
5239
  }
5218
5240
  videoStream.removeListener("error", onError);
5219
5241
  videoStream.removeListener("close", onClose);
5242
+ if (dedicatedRelease) {
5243
+ dedicatedRelease().catch(() => {
5244
+ });
5245
+ }
5220
5246
  }
5221
5247
  }
5222
5248
 
@@ -5459,6 +5485,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5459
5485
  tcpRtpFraming;
5460
5486
  active = false;
5461
5487
  flow;
5488
+ deviceId;
5489
+ dedicatedSessionRelease;
5462
5490
  // Authentication
5463
5491
  authCredentials = [];
5464
5492
  requireAuth;
@@ -5625,6 +5653,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
5625
5653
  this.path = options.path ?? `/stream/${this.profile}`;
5626
5654
  this.logger = options.logger ?? console;
5627
5655
  this.tcpRtpFraming = options.tcpRtpFraming ?? "rfc4571";
5656
+ this.deviceId = options.deviceId;
5628
5657
  this.authCredentials = options.credentials ?? [];
5629
5658
  this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
5630
5659
  const transport = this.api.client.getTransport();
@@ -7010,14 +7039,31 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7010
7039
  this.firstAudioPromise = new Promise((resolve) => {
7011
7040
  this.firstAudioResolve = resolve;
7012
7041
  });
7042
+ let dedicatedClient;
7043
+ const variantSuffix = this.variant && this.variant !== "default" ? `:${this.variant}` : "";
7044
+ const deviceIdPart = this.deviceId ?? "rtsp-server";
7045
+ const sessionKey = `live:${deviceIdPart}:ch${this.channel}:${this.profile}${variantSuffix}`;
7046
+ try {
7047
+ const session = await this.api.createDedicatedSession(sessionKey, this.logger);
7048
+ dedicatedClient = session.client;
7049
+ this.dedicatedSessionRelease = session.release;
7050
+ this.logger.info(
7051
+ `[rebroadcast] dedicated session acquired sessionKey=${sessionKey}`
7052
+ );
7053
+ } catch (e) {
7054
+ this.logger.warn(
7055
+ `[rebroadcast] failed to acquire dedicated session, falling back to shared socket: ${e}`
7056
+ );
7057
+ }
7013
7058
  this.logger.info(
7014
- `[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
7059
+ `[rebroadcast] native stream starting profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size} dedicated=${!!dedicatedClient}`
7015
7060
  );
7016
7061
  await this.flow.startKeepAlive(this.api);
7017
7062
  this.nativeFanout = new NativeStreamFanout({
7018
7063
  maxQueueItems: 200,
7019
7064
  createSource: () => createNativeStream(this.api, this.channel, this.profile, {
7020
- variant: this.variant
7065
+ variant: this.variant,
7066
+ ...dedicatedClient ? { client: dedicatedClient } : {}
7021
7067
  }),
7022
7068
  onFrame: (frame) => {
7023
7069
  if (frame.audio) {
@@ -7075,6 +7121,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7075
7121
  this.firstFrameResolve = null;
7076
7122
  this.nativeFanout = null;
7077
7123
  this.prebuffer = [];
7124
+ if (this.dedicatedSessionRelease) {
7125
+ const release = this.dedicatedSessionRelease;
7126
+ this.dedicatedSessionRelease = void 0;
7127
+ release().catch(() => {
7128
+ });
7129
+ }
7078
7130
  this.logger.info(
7079
7131
  `[rebroadcast] native stream ended (camera sleeping or connection lost) profile=${this.profile} channel=${this.channel} clients=${this.connectedClients.size}`
7080
7132
  );
@@ -7144,6 +7196,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7144
7196
  await fanout.stop();
7145
7197
  }
7146
7198
  this.prebuffer = [];
7199
+ if (this.dedicatedSessionRelease) {
7200
+ const release = this.dedicatedSessionRelease;
7201
+ this.dedicatedSessionRelease = void 0;
7202
+ try {
7203
+ await release();
7204
+ } catch {
7205
+ }
7206
+ }
7147
7207
  if (this.tempStreamGenerator) {
7148
7208
  try {
7149
7209
  await this.tempStreamGenerator.return(void 0);
@@ -7153,14 +7213,22 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends EventEmitter3 {
7153
7213
  }
7154
7214
  }
7155
7215
  /**
7156
- * Remove a client and stop native stream if no clients remain.
7216
+ * Remove a client and schedule native stream stop if no clients remain.
7217
+ * Uses a grace period so rapid reconnects (e.g. Frigate polling) reuse the running stream
7218
+ * and benefit from the prebuffer instead of waiting for a fresh keyframe.
7157
7219
  */
7158
7220
  removeClient(clientId) {
7159
7221
  if (this.connectedClients.has(clientId)) {
7160
7222
  this.connectedClients.delete(clientId);
7161
7223
  this.emit("clientDisconnected", clientId);
7162
7224
  if (this.connectedClients.size === 0) {
7163
- void this.stopNativeStream();
7225
+ this.clearNoClientAutoStopTimer();
7226
+ this.noClientAutoStopTimer = setTimeout(() => {
7227
+ if (this.connectedClients.size === 0) {
7228
+ void this.stopNativeStream();
7229
+ }
7230
+ }, 3e4);
7231
+ this.noClientAutoStopTimer?.unref?.();
7164
7232
  }
7165
7233
  }
7166
7234
  }
@@ -17684,7 +17752,7 @@ ${xml}`
17684
17752
  * @returns Test results for all stream types and profiles
17685
17753
  */
17686
17754
  async testChannelStreams(channel, logger) {
17687
- const { testChannelStreams } = await import("./DiagnosticsTools-FNLGCOVA.js");
17755
+ const { testChannelStreams } = await import("./DiagnosticsTools-2JQRV5FE.js");
17688
17756
  return await testChannelStreams({
17689
17757
  api: this,
17690
17758
  channel: this.normalizeChannel(channel),
@@ -17700,7 +17768,7 @@ ${xml}`
17700
17768
  * @returns Complete diagnostics for all channels and streams
17701
17769
  */
17702
17770
  async collectMultifocalDiagnostics(logger) {
17703
- const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-FNLGCOVA.js");
17771
+ const { collectMultifocalDiagnostics } = await import("./DiagnosticsTools-2JQRV5FE.js");
17704
17772
  return await collectMultifocalDiagnostics({
17705
17773
  api: this,
17706
17774
  logger
@@ -21014,4 +21082,4 @@ export {
21014
21082
  isTcpFailureThatShouldFallbackToUdp,
21015
21083
  autoDetectDeviceType
21016
21084
  };
21017
- //# sourceMappingURL=chunk-RWYEGEWG.js.map
21085
+ //# sourceMappingURL=chunk-EG5IY3CM.js.map