@apocaliss92/nodelink-js 0.4.6 → 0.4.7
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/{chunk-F2Y5U3YP.js → chunk-GKLOJJ34.js} +92 -17
- package/dist/chunk-GKLOJJ34.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +91 -16
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +153 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +63 -8
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-F2Y5U3YP.js.map +0 -1
package/dist/index.d.cts
CHANGED
|
@@ -4239,6 +4239,13 @@ declare class ReolinkBaichuanApi {
|
|
|
4239
4239
|
* - "replay:XXX" - dedicated per replay session
|
|
4240
4240
|
*/
|
|
4241
4241
|
private readonly socketPool;
|
|
4242
|
+
/**
|
|
4243
|
+
* Consecutive stream-start (cmdId=3) timeout counter per socket tag.
|
|
4244
|
+
* When a streaming socket has N consecutive timeouts, the socket is force-closed
|
|
4245
|
+
* so the next attempt creates a fresh connection. Resets on success.
|
|
4246
|
+
*/
|
|
4247
|
+
private readonly consecutiveStreamTimeouts;
|
|
4248
|
+
private static readonly MAX_CONSECUTIVE_STREAM_TIMEOUTS;
|
|
4242
4249
|
/** BaichuanClientOptions to use when creating new sockets */
|
|
4243
4250
|
private readonly clientOptions;
|
|
4244
4251
|
/**
|
|
@@ -4383,6 +4390,14 @@ declare class ReolinkBaichuanApi {
|
|
|
4383
4390
|
*/
|
|
4384
4391
|
private readonly deviceCapabilitiesCache;
|
|
4385
4392
|
private static readonly CAPABILITIES_CACHE_TTL_MS;
|
|
4393
|
+
/**
|
|
4394
|
+
* Dedupe key for battery push events (cmd_id 252), per channel.
|
|
4395
|
+
* Cameras emit BatteryInfoList frequently while streaming (every few
|
|
4396
|
+
* seconds). We only forward an event when the meaningful fields change
|
|
4397
|
+
* (percent, chargeStatus, adapterStatus) to avoid flooding SSE/MQTT
|
|
4398
|
+
* consumers and the UI event log.
|
|
4399
|
+
*/
|
|
4400
|
+
private readonly lastBatteryPushKey;
|
|
4386
4401
|
/** Keep replay/streaming sockets warm briefly to reduce clip switch latency. */
|
|
4387
4402
|
private static readonly SOCKET_POOL_KEEPALIVE_MS;
|
|
4388
4403
|
/**
|
|
@@ -5722,6 +5737,22 @@ declare class ReolinkBaichuanApi {
|
|
|
5722
5737
|
* 2. **Storm**: ≥3 D2C_DISCs within 60 s triggers extended cooldown (120 s).
|
|
5723
5738
|
*/
|
|
5724
5739
|
private notifyD2cDisc;
|
|
5740
|
+
/**
|
|
5741
|
+
* Find the socket pool tag for a given BaichuanClient instance.
|
|
5742
|
+
* Returns undefined if the client is not in the pool (e.g. it's the general socket used directly).
|
|
5743
|
+
*/
|
|
5744
|
+
private findSocketTagForClient;
|
|
5745
|
+
/**
|
|
5746
|
+
* Reset the consecutive stream-start timeout counter for a streaming socket.
|
|
5747
|
+
* Called on successful stream start.
|
|
5748
|
+
*/
|
|
5749
|
+
private resetStreamTimeoutCounter;
|
|
5750
|
+
/**
|
|
5751
|
+
* Track a stream-start timeout on a streaming socket.
|
|
5752
|
+
* After MAX_CONSECUTIVE_STREAM_TIMEOUTS consecutive timeouts, force-close the
|
|
5753
|
+
* socket so the next attempt creates a fresh connection.
|
|
5754
|
+
*/
|
|
5755
|
+
private trackStreamTimeout;
|
|
5725
5756
|
/**
|
|
5726
5757
|
* Best-effort sleeping inference for battery/BCUDP cameras.
|
|
5727
5758
|
*
|
|
@@ -8046,6 +8077,13 @@ interface Go2rtcTcpServerOptions {
|
|
|
8046
8077
|
prebufferMs?: number;
|
|
8047
8078
|
/** Maximum write buffer per client before dropping the connection (default: 100 MB). */
|
|
8048
8079
|
maxBufferBytes?: number;
|
|
8080
|
+
/**
|
|
8081
|
+
* Stream inactivity timeout in ms. If no frames arrive from the native
|
|
8082
|
+
* stream for this duration, the stream is considered dead and will be
|
|
8083
|
+
* torn down + restarted (similar to go2rtc's per-packet read deadline).
|
|
8084
|
+
* Default: 15 000 (15s). Set to 0 to disable.
|
|
8085
|
+
*/
|
|
8086
|
+
streamTimeoutMs?: number;
|
|
8049
8087
|
/**
|
|
8050
8088
|
* When true, the native camera stream is started immediately on start()
|
|
8051
8089
|
* rather than waiting for the first TCP client. This ensures frames are
|
|
@@ -8075,6 +8113,7 @@ declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
8075
8113
|
private readonly gracePeriodMs;
|
|
8076
8114
|
private readonly prebufferMaxMs;
|
|
8077
8115
|
private readonly maxBufferBytes;
|
|
8116
|
+
private readonly streamTimeoutMs;
|
|
8078
8117
|
private readonly prestartStream;
|
|
8079
8118
|
private active;
|
|
8080
8119
|
private server;
|
|
@@ -8086,6 +8125,10 @@ declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
8086
8125
|
private connectedClients;
|
|
8087
8126
|
private clientSockets;
|
|
8088
8127
|
private stopGraceTimer;
|
|
8128
|
+
private lastFrameAt;
|
|
8129
|
+
private streamHealthTimer;
|
|
8130
|
+
private totalFramesReceived;
|
|
8131
|
+
private totalVideoFramesWritten;
|
|
8089
8132
|
private prebuffer;
|
|
8090
8133
|
constructor(options: Go2rtcTcpServerOptions);
|
|
8091
8134
|
/** Start listening. Resolves once the TCP server is bound. */
|
|
@@ -8112,6 +8155,8 @@ declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
8112
8155
|
private static splitAnnexBNals;
|
|
8113
8156
|
private startNativeStream;
|
|
8114
8157
|
private stopNativeStream;
|
|
8158
|
+
private startStreamHealthMonitor;
|
|
8159
|
+
private stopStreamHealthMonitor;
|
|
8115
8160
|
private removeClient;
|
|
8116
8161
|
private scheduleStop;
|
|
8117
8162
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -4200,6 +4200,7 @@ export declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
4200
4200
|
private readonly gracePeriodMs;
|
|
4201
4201
|
private readonly prebufferMaxMs;
|
|
4202
4202
|
private readonly maxBufferBytes;
|
|
4203
|
+
private readonly streamTimeoutMs;
|
|
4203
4204
|
private readonly prestartStream;
|
|
4204
4205
|
private active;
|
|
4205
4206
|
private server;
|
|
@@ -4211,6 +4212,10 @@ export declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
4211
4212
|
private connectedClients;
|
|
4212
4213
|
private clientSockets;
|
|
4213
4214
|
private stopGraceTimer;
|
|
4215
|
+
private lastFrameAt;
|
|
4216
|
+
private streamHealthTimer;
|
|
4217
|
+
private totalFramesReceived;
|
|
4218
|
+
private totalVideoFramesWritten;
|
|
4214
4219
|
private prebuffer;
|
|
4215
4220
|
constructor(options: Go2rtcTcpServerOptions);
|
|
4216
4221
|
/** Start listening. Resolves once the TCP server is bound. */
|
|
@@ -4237,6 +4242,8 @@ export declare class Go2rtcTcpServer extends EventEmitter<{
|
|
|
4237
4242
|
private static splitAnnexBNals;
|
|
4238
4243
|
private startNativeStream;
|
|
4239
4244
|
private stopNativeStream;
|
|
4245
|
+
private startStreamHealthMonitor;
|
|
4246
|
+
private stopStreamHealthMonitor;
|
|
4240
4247
|
private removeClient;
|
|
4241
4248
|
private scheduleStop;
|
|
4242
4249
|
}
|
|
@@ -4268,6 +4275,13 @@ export declare interface Go2rtcTcpServerOptions {
|
|
|
4268
4275
|
prebufferMs?: number;
|
|
4269
4276
|
/** Maximum write buffer per client before dropping the connection (default: 100 MB). */
|
|
4270
4277
|
maxBufferBytes?: number;
|
|
4278
|
+
/**
|
|
4279
|
+
* Stream inactivity timeout in ms. If no frames arrive from the native
|
|
4280
|
+
* stream for this duration, the stream is considered dead and will be
|
|
4281
|
+
* torn down + restarted (similar to go2rtc's per-packet read deadline).
|
|
4282
|
+
* Default: 15 000 (15s). Set to 0 to disable.
|
|
4283
|
+
*/
|
|
4284
|
+
streamTimeoutMs?: number;
|
|
4271
4285
|
/**
|
|
4272
4286
|
* When true, the native camera stream is started immediately on start()
|
|
4273
4287
|
* rather than waiting for the first TCP client. This ensures frames are
|
|
@@ -5082,6 +5096,13 @@ export declare class ReolinkBaichuanApi {
|
|
|
5082
5096
|
* - "replay:XXX" - dedicated per replay session
|
|
5083
5097
|
*/
|
|
5084
5098
|
private readonly socketPool;
|
|
5099
|
+
/**
|
|
5100
|
+
* Consecutive stream-start (cmdId=3) timeout counter per socket tag.
|
|
5101
|
+
* When a streaming socket has N consecutive timeouts, the socket is force-closed
|
|
5102
|
+
* so the next attempt creates a fresh connection. Resets on success.
|
|
5103
|
+
*/
|
|
5104
|
+
private readonly consecutiveStreamTimeouts;
|
|
5105
|
+
private static readonly MAX_CONSECUTIVE_STREAM_TIMEOUTS;
|
|
5085
5106
|
/** BaichuanClientOptions to use when creating new sockets */
|
|
5086
5107
|
private readonly clientOptions;
|
|
5087
5108
|
/**
|
|
@@ -5226,6 +5247,14 @@ export declare class ReolinkBaichuanApi {
|
|
|
5226
5247
|
*/
|
|
5227
5248
|
private readonly deviceCapabilitiesCache;
|
|
5228
5249
|
private static readonly CAPABILITIES_CACHE_TTL_MS;
|
|
5250
|
+
/**
|
|
5251
|
+
* Dedupe key for battery push events (cmd_id 252), per channel.
|
|
5252
|
+
* Cameras emit BatteryInfoList frequently while streaming (every few
|
|
5253
|
+
* seconds). We only forward an event when the meaningful fields change
|
|
5254
|
+
* (percent, chargeStatus, adapterStatus) to avoid flooding SSE/MQTT
|
|
5255
|
+
* consumers and the UI event log.
|
|
5256
|
+
*/
|
|
5257
|
+
private readonly lastBatteryPushKey;
|
|
5229
5258
|
/** Keep replay/streaming sockets warm briefly to reduce clip switch latency. */
|
|
5230
5259
|
private static readonly SOCKET_POOL_KEEPALIVE_MS;
|
|
5231
5260
|
/**
|
|
@@ -6565,6 +6594,22 @@ export declare class ReolinkBaichuanApi {
|
|
|
6565
6594
|
* 2. **Storm**: ≥3 D2C_DISCs within 60 s triggers extended cooldown (120 s).
|
|
6566
6595
|
*/
|
|
6567
6596
|
private notifyD2cDisc;
|
|
6597
|
+
/**
|
|
6598
|
+
* Find the socket pool tag for a given BaichuanClient instance.
|
|
6599
|
+
* Returns undefined if the client is not in the pool (e.g. it's the general socket used directly).
|
|
6600
|
+
*/
|
|
6601
|
+
private findSocketTagForClient;
|
|
6602
|
+
/**
|
|
6603
|
+
* Reset the consecutive stream-start timeout counter for a streaming socket.
|
|
6604
|
+
* Called on successful stream start.
|
|
6605
|
+
*/
|
|
6606
|
+
private resetStreamTimeoutCounter;
|
|
6607
|
+
/**
|
|
6608
|
+
* Track a stream-start timeout on a streaming socket.
|
|
6609
|
+
* After MAX_CONSECUTIVE_STREAM_TIMEOUTS consecutive timeouts, force-close the
|
|
6610
|
+
* socket so the next attempt creates a fresh connection.
|
|
6611
|
+
*/
|
|
6612
|
+
private trackStreamTimeout;
|
|
6568
6613
|
/**
|
|
6569
6614
|
* Best-effort sleeping inference for battery/BCUDP cameras.
|
|
6570
6615
|
*
|
package/dist/index.js
CHANGED
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
parseSupportXml,
|
|
44
44
|
setGlobalLogger,
|
|
45
45
|
xmlIndicatesFloodlight
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-GKLOJJ34.js";
|
|
47
47
|
import {
|
|
48
48
|
AesStreamDecryptor,
|
|
49
49
|
BC_AES_IV,
|
|
@@ -4725,6 +4725,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4725
4725
|
gracePeriodMs;
|
|
4726
4726
|
prebufferMaxMs;
|
|
4727
4727
|
maxBufferBytes;
|
|
4728
|
+
streamTimeoutMs;
|
|
4728
4729
|
prestartStream;
|
|
4729
4730
|
active = false;
|
|
4730
4731
|
server;
|
|
@@ -4738,6 +4739,11 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4738
4739
|
connectedClients = /* @__PURE__ */ new Set();
|
|
4739
4740
|
clientSockets = /* @__PURE__ */ new Map();
|
|
4740
4741
|
stopGraceTimer;
|
|
4742
|
+
// Stream health monitoring
|
|
4743
|
+
lastFrameAt = 0;
|
|
4744
|
+
streamHealthTimer;
|
|
4745
|
+
totalFramesReceived = 0;
|
|
4746
|
+
totalVideoFramesWritten = 0;
|
|
4741
4747
|
// Prebuffer
|
|
4742
4748
|
prebuffer = [];
|
|
4743
4749
|
constructor(options) {
|
|
@@ -4753,6 +4759,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4753
4759
|
this.gracePeriodMs = options.gracePeriodMs ?? 3e4;
|
|
4754
4760
|
this.prebufferMaxMs = options.prebufferMs ?? 3e3;
|
|
4755
4761
|
this.maxBufferBytes = options.maxBufferBytes ?? 1e8;
|
|
4762
|
+
this.streamTimeoutMs = options.streamTimeoutMs ?? 15e3;
|
|
4756
4763
|
this.prestartStream = options.prestartStream ?? true;
|
|
4757
4764
|
}
|
|
4758
4765
|
// -----------------------------------------------------------------------
|
|
@@ -4791,6 +4798,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4791
4798
|
if (!this.active) return;
|
|
4792
4799
|
this.active = false;
|
|
4793
4800
|
clearTimeout(this.stopGraceTimer);
|
|
4801
|
+
this.stopStreamHealthMonitor();
|
|
4794
4802
|
for (const [id, sock] of this.clientSockets) {
|
|
4795
4803
|
sock.destroy();
|
|
4796
4804
|
this.connectedClients.delete(id);
|
|
@@ -4844,12 +4852,12 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4844
4852
|
`[Go2rtcTcpServer] feedClient error id=${clientId}: ${err}`
|
|
4845
4853
|
);
|
|
4846
4854
|
});
|
|
4847
|
-
const cleanup = () => {
|
|
4848
|
-
this.removeClient(clientId);
|
|
4855
|
+
const cleanup = (reason) => {
|
|
4856
|
+
this.removeClient(clientId, reason);
|
|
4849
4857
|
socket.destroy();
|
|
4850
4858
|
};
|
|
4851
|
-
socket.on("error", cleanup);
|
|
4852
|
-
socket.on("close", cleanup);
|
|
4859
|
+
socket.on("error", (err) => cleanup(`error: ${err.message}`));
|
|
4860
|
+
socket.on("close", (hadError) => cleanup(hadError ? "close (with error)" : "close (clean)"));
|
|
4853
4861
|
}
|
|
4854
4862
|
async feedClient(clientId, socket) {
|
|
4855
4863
|
const fanoutDeadline = Date.now() + 3e4;
|
|
@@ -4910,6 +4918,7 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
4910
4918
|
}
|
|
4911
4919
|
socket.write(annexB);
|
|
4912
4920
|
liveVideoWritten++;
|
|
4921
|
+
this.totalVideoFramesWritten++;
|
|
4913
4922
|
if (Date.now() - lastLogAt > 1e4) {
|
|
4914
4923
|
this.logger.info?.(
|
|
4915
4924
|
`[Go2rtcTcpServer] live stats client=${clientId} received=${liveFrameCount} written=${liveVideoWritten} bufLen=${socket.writableLength}`
|
|
@@ -5036,6 +5045,8 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
5036
5045
|
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
5037
5046
|
}),
|
|
5038
5047
|
onFrame: (frame) => {
|
|
5048
|
+
this.lastFrameAt = Date.now();
|
|
5049
|
+
this.totalFramesReceived++;
|
|
5039
5050
|
if (!frame.audio && (frame.videoType === "H264" || frame.videoType === "H265")) {
|
|
5040
5051
|
this.detectedVideoType = frame.videoType;
|
|
5041
5052
|
}
|
|
@@ -5062,6 +5073,12 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
5062
5073
|
if (!this.nativeStreamActive) return;
|
|
5063
5074
|
this.nativeStreamActive = false;
|
|
5064
5075
|
this.nativeFanout = null;
|
|
5076
|
+
this.stopStreamHealthMonitor();
|
|
5077
|
+
const silenceMs = this.lastFrameAt > 0 ? Date.now() - this.lastFrameAt : -1;
|
|
5078
|
+
const diagnosis = silenceMs > this.streamTimeoutMs ? "camera stopped sending frames" : silenceMs >= 0 ? "stream source closed" : "no frames were ever received";
|
|
5079
|
+
this.logger.warn?.(
|
|
5080
|
+
`[Go2rtcTcpServer] native stream ended diagnosis="${diagnosis}" lastFrame=${silenceMs >= 0 ? `${(silenceMs / 1e3).toFixed(1)}s ago` : "never"} totalRx=${this.totalFramesReceived} clients=${this.connectedClients.size}`
|
|
5081
|
+
);
|
|
5065
5082
|
if (this.dedicatedSessionRelease) {
|
|
5066
5083
|
this.dedicatedSessionRelease().catch(() => {
|
|
5067
5084
|
});
|
|
@@ -5069,16 +5086,18 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
5069
5086
|
}
|
|
5070
5087
|
if (this.active && (this.connectedClients.size > 0 || this.prestartStream)) {
|
|
5071
5088
|
this.logger.info?.(
|
|
5072
|
-
`[Go2rtcTcpServer] native stream
|
|
5089
|
+
`[Go2rtcTcpServer] restarting native stream (clients=${this.connectedClients.size}, prestart=${this.prestartStream})`
|
|
5073
5090
|
);
|
|
5074
5091
|
this.startNativeStream();
|
|
5075
5092
|
}
|
|
5076
5093
|
}
|
|
5077
5094
|
});
|
|
5078
5095
|
this.nativeFanout.start();
|
|
5096
|
+
this.startStreamHealthMonitor();
|
|
5079
5097
|
}
|
|
5080
5098
|
async stopNativeStream() {
|
|
5081
5099
|
this.nativeStreamActive = false;
|
|
5100
|
+
this.stopStreamHealthMonitor();
|
|
5082
5101
|
const fanout = this.nativeFanout;
|
|
5083
5102
|
this.nativeFanout = null;
|
|
5084
5103
|
if (fanout) {
|
|
@@ -5092,14 +5111,50 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
|
5092
5111
|
}
|
|
5093
5112
|
}
|
|
5094
5113
|
// -----------------------------------------------------------------------
|
|
5114
|
+
// Stream health monitoring
|
|
5115
|
+
// -----------------------------------------------------------------------
|
|
5116
|
+
startStreamHealthMonitor() {
|
|
5117
|
+
this.stopStreamHealthMonitor();
|
|
5118
|
+
if (this.streamTimeoutMs <= 0) return;
|
|
5119
|
+
this.lastFrameAt = Date.now();
|
|
5120
|
+
this.streamHealthTimer = setInterval(() => {
|
|
5121
|
+
if (!this.nativeStreamActive || !this.active) {
|
|
5122
|
+
this.stopStreamHealthMonitor();
|
|
5123
|
+
return;
|
|
5124
|
+
}
|
|
5125
|
+
const silenceMs = Date.now() - this.lastFrameAt;
|
|
5126
|
+
if (silenceMs > this.streamTimeoutMs) {
|
|
5127
|
+
this.logger.warn?.(
|
|
5128
|
+
`[Go2rtcTcpServer] stream inactivity timeout: no frames for ${(silenceMs / 1e3).toFixed(1)}s (threshold=${this.streamTimeoutMs}ms), totalReceived=${this.totalFramesReceived} clients=${this.connectedClients.size} \u2014 forcing stream restart`
|
|
5129
|
+
);
|
|
5130
|
+
this.stopStreamHealthMonitor();
|
|
5131
|
+
const fanout = this.nativeFanout;
|
|
5132
|
+
if (fanout) {
|
|
5133
|
+
this.nativeStreamActive = false;
|
|
5134
|
+
this.nativeFanout = null;
|
|
5135
|
+
fanout.stop().catch(() => {
|
|
5136
|
+
});
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
}, Math.min(this.streamTimeoutMs / 2, 5e3));
|
|
5140
|
+
}
|
|
5141
|
+
stopStreamHealthMonitor() {
|
|
5142
|
+
if (this.streamHealthTimer) {
|
|
5143
|
+
clearInterval(this.streamHealthTimer);
|
|
5144
|
+
this.streamHealthTimer = void 0;
|
|
5145
|
+
}
|
|
5146
|
+
}
|
|
5147
|
+
// -----------------------------------------------------------------------
|
|
5095
5148
|
// Client lifecycle
|
|
5096
5149
|
// -----------------------------------------------------------------------
|
|
5097
|
-
removeClient(clientId) {
|
|
5150
|
+
removeClient(clientId, reason) {
|
|
5098
5151
|
if (!this.connectedClients.has(clientId)) return;
|
|
5099
5152
|
this.connectedClients.delete(clientId);
|
|
5100
5153
|
this.clientSockets.delete(clientId);
|
|
5154
|
+
const silenceMs = this.lastFrameAt > 0 ? Date.now() - this.lastFrameAt : -1;
|
|
5155
|
+
const silenceInfo = silenceMs >= 0 ? ` lastFrame=${(silenceMs / 1e3).toFixed(1)}s ago` : "";
|
|
5101
5156
|
this.logger.info?.(
|
|
5102
|
-
`[Go2rtcTcpServer] client disconnected id=${clientId} remaining=${this.connectedClients.size}`
|
|
5157
|
+
`[Go2rtcTcpServer] client disconnected id=${clientId} reason=${reason ?? "unknown"} remaining=${this.connectedClients.size} totalRx=${this.totalFramesReceived} totalTx=${this.totalVideoFramesWritten}${silenceInfo}`
|
|
5103
5158
|
);
|
|
5104
5159
|
this.emit("clientDisconnected", clientId);
|
|
5105
5160
|
if (this.connectedClients.size === 0 && !this.prestartStream) {
|