@apocaliss92/nodelink-js 0.5.1-beta.7 → 0.5.1-beta.9

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.cjs CHANGED
@@ -41759,6 +41759,20 @@ var import_node_events15 = require("events");
41759
41759
  var net6 = __toESM(require("net"), 1);
41760
41760
  var crypto3 = __toESM(require("crypto"), 1);
41761
41761
  var md5Hex = (s) => crypto3.createHash("md5").update(s).digest("hex");
41762
+ var RTCP_KEEPALIVE_INTERVAL_MS = 1e4;
41763
+ function buildInterleavedRtcpRr(channel, ssrc) {
41764
+ const rtcp = Buffer.alloc(8);
41765
+ rtcp[0] = 128;
41766
+ rtcp[1] = 201;
41767
+ rtcp.writeUInt16BE(1, 2);
41768
+ rtcp.writeUInt32BE(ssrc >>> 0, 4);
41769
+ const frame = Buffer.alloc(4 + rtcp.length);
41770
+ frame[0] = 36;
41771
+ frame[1] = channel & 255;
41772
+ frame.writeUInt16BE(rtcp.length, 2);
41773
+ rtcp.copy(frame, 4);
41774
+ return frame;
41775
+ }
41762
41776
  function normalizePath(path7) {
41763
41777
  if (!path7) return "/";
41764
41778
  let p = path7;
@@ -41955,6 +41969,7 @@ var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends
41955
41969
  this.server = void 0;
41956
41970
  for (const session of this.sessionByClient.values()) {
41957
41971
  this.sessionByClient.delete(session.clientId);
41972
+ this.stopRtcpKeepalive(session);
41958
41973
  if (session.handler) {
41959
41974
  void session.handler.stop();
41960
41975
  }
@@ -41981,6 +41996,7 @@ var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends
41981
41996
  const durationMs = Date.now() - connectedAt;
41982
41997
  if (session) {
41983
41998
  this.sessionByClient.delete(clientId);
41999
+ this.stopRtcpKeepalive(session);
41984
42000
  if (session.handler) {
41985
42001
  const stats = session.handler.stats;
41986
42002
  this.logger.info?.(
@@ -42137,9 +42153,20 @@ CSeq: ${cseq}\r
42137
42153
  switch (method) {
42138
42154
  case "OPTIONS":
42139
42155
  send(200, "OK", {
42140
- Public: "OPTIONS, DESCRIBE, SETUP, PLAY, RECORD, TEARDOWN"
42156
+ Public: "OPTIONS, DESCRIBE, SETUP, PLAY, RECORD, TEARDOWN, GET_PARAMETER, SET_PARAMETER"
42141
42157
  });
42142
42158
  return;
42159
+ // RFC 2326 §10.8 keepalive. Frigate's bundled go2rtc 1.9.10 sets a
42160
+ // ~30s read deadline on the producer socket during RECORD; without
42161
+ // a periodic reply on the same TCP connection that deadline fires
42162
+ // and the client gives up with "i/o timeout". go2rtc sends an empty
42163
+ // GET_PARAMETER (no body) as the keepalive — we just need to
42164
+ // acknowledge it. SET_PARAMETER is accepted for symmetry with
42165
+ // clients that prefer it for keepalive.
42166
+ case "GET_PARAMETER":
42167
+ case "SET_PARAMETER":
42168
+ send(200, "OK", sessionId ? { Session: sessionId } : {});
42169
+ return;
42143
42170
  case "DESCRIBE": {
42144
42171
  const resolved = this.resolveRouteForRequest(url);
42145
42172
  if (!resolved) {
@@ -42274,12 +42301,14 @@ ${sdp.trimEnd()}`
42274
42301
  }
42275
42302
  session.handler = handler;
42276
42303
  send(200, "OK", { Session: session.sessionId });
42304
+ this.startRtcpKeepalive(session);
42277
42305
  return;
42278
42306
  }
42279
42307
  case "TEARDOWN": {
42280
42308
  const session = this.sessionByClient.get(clientId);
42281
42309
  if (session) {
42282
42310
  this.sessionByClient.delete(clientId);
42311
+ this.stopRtcpKeepalive(session);
42283
42312
  if (session.handler) {
42284
42313
  const stats = session.handler.stats;
42285
42314
  this.logger.info?.(
@@ -42310,6 +42339,39 @@ ${sdp.trimEnd()}`
42310
42339
  send(501, "Not Implemented");
42311
42340
  }
42312
42341
  }
42342
+ /**
42343
+ * Start emitting an interleaved RTCP RR every
42344
+ * {@link RTCP_KEEPALIVE_INTERVAL_MS}. The RR carries no report blocks —
42345
+ * its purpose is to push *some* bytes back onto the TCP connection so
42346
+ * the producer's read deadline keeps resetting. Idempotent.
42347
+ */
42348
+ startRtcpKeepalive(session) {
42349
+ if (session.rtcpKeepaliveTimer) return;
42350
+ session.rtcpSsrc = Math.floor(Math.random() * 4294967295) >>> 0;
42351
+ const frame = buildInterleavedRtcpRr(session.rtcpChannel, session.rtcpSsrc);
42352
+ const writeOne = () => {
42353
+ if (!session.socket.writable) return;
42354
+ try {
42355
+ session.socket.write(frame);
42356
+ } catch (e) {
42357
+ this.logger.debug?.(
42358
+ `[BaichuanRtspBackchannelServer] RTCP RR write failed client=${session.clientId} session=${session.sessionId} error="${e.message}"`
42359
+ );
42360
+ }
42361
+ };
42362
+ session.rtcpKeepaliveTimer = setInterval(writeOne, RTCP_KEEPALIVE_INTERVAL_MS);
42363
+ if (typeof session.rtcpKeepaliveTimer.unref === "function") {
42364
+ session.rtcpKeepaliveTimer.unref();
42365
+ }
42366
+ this.logger.debug?.(
42367
+ `[BaichuanRtspBackchannelServer] RTCP RR keepalive started client=${session.clientId} session=${session.sessionId} channel=${session.rtcpChannel} ssrc=${session.rtcpSsrc?.toString(16)} interval=${RTCP_KEEPALIVE_INTERVAL_MS}ms`
42368
+ );
42369
+ }
42370
+ stopRtcpKeepalive(session) {
42371
+ if (!session.rtcpKeepaliveTimer) return;
42372
+ clearInterval(session.rtcpKeepaliveTimer);
42373
+ delete session.rtcpKeepaliveTimer;
42374
+ }
42313
42375
  buildSdp() {
42314
42376
  return `v=0\r
42315
42377
  o=- ${Date.now()} ${Date.now()} IN IP4 ${this.listenHost}\r