@apocaliss92/nodelink-js 0.3.4 → 0.3.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/README.md +48 -82
- package/dist/{DiagnosticsTools-2JQRV5FE.js → DiagnosticsTools-DQDDBRM6.js} +4 -2
- package/dist/{chunk-APEEZ4UN.js → chunk-6Q6MK4WG.js} +172 -1
- package/dist/chunk-6Q6MK4WG.js.map +1 -0
- package/dist/{chunk-YSEFEQYV.js → chunk-OGIKBDON.js} +49 -8
- package/dist/chunk-OGIKBDON.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +215 -4
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +2 -2
- package/dist/index.cjs +756 -18
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +184 -1
- package/dist/index.d.ts +165 -0
- package/dist/index.js +541 -17
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-APEEZ4UN.js.map +0 -1
- package/dist/chunk-YSEFEQYV.js.map +0 -1
- /package/dist/{DiagnosticsTools-2JQRV5FE.js.map → DiagnosticsTools-DQDDBRM6.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -3103,6 +3103,7 @@ var init_urls = __esm({
|
|
|
3103
3103
|
// src/debug/DiagnosticsTools.ts
|
|
3104
3104
|
var DiagnosticsTools_exports = {};
|
|
3105
3105
|
__export(DiagnosticsTools_exports, {
|
|
3106
|
+
captureModelFixtures: () => captureModelFixtures,
|
|
3106
3107
|
collectCgiDiagnostics: () => collectCgiDiagnostics,
|
|
3107
3108
|
collectMultifocalDiagnostics: () => collectMultifocalDiagnostics,
|
|
3108
3109
|
collectNativeDiagnostics: () => collectNativeDiagnostics,
|
|
@@ -3136,6 +3137,10 @@ function writeJson(filePath, obj) {
|
|
|
3136
3137
|
mkdirp(path4.dirname(filePath));
|
|
3137
3138
|
fs4.writeFileSync(filePath, JSON.stringify(obj, null, 2));
|
|
3138
3139
|
}
|
|
3140
|
+
function writeText(filePath, text) {
|
|
3141
|
+
mkdirp(path4.dirname(filePath));
|
|
3142
|
+
fs4.writeFileSync(filePath, text);
|
|
3143
|
+
}
|
|
3139
3144
|
function appendNdjson(filePath, obj) {
|
|
3140
3145
|
mkdirp(path4.dirname(filePath));
|
|
3141
3146
|
fs4.appendFileSync(filePath, JSON.stringify(obj) + "\n");
|
|
@@ -5399,6 +5404,172 @@ async function runAllDiagnosticsConsecutively(params) {
|
|
|
5399
5404
|
streamsDir
|
|
5400
5405
|
};
|
|
5401
5406
|
}
|
|
5407
|
+
async function captureModelFixtures(params) {
|
|
5408
|
+
const { api, channel, outDir } = params;
|
|
5409
|
+
const log = params.log ?? console.log;
|
|
5410
|
+
mkdirp(outDir);
|
|
5411
|
+
const calls = {};
|
|
5412
|
+
const errors = [];
|
|
5413
|
+
async function capture(name, fn, writer) {
|
|
5414
|
+
try {
|
|
5415
|
+
const value = await fn();
|
|
5416
|
+
calls[name] = { ok: true, value };
|
|
5417
|
+
if (writer && value !== void 0 && value !== null) {
|
|
5418
|
+
writer(value);
|
|
5419
|
+
}
|
|
5420
|
+
log(` \u2713 ${name}`);
|
|
5421
|
+
return value;
|
|
5422
|
+
} catch (e) {
|
|
5423
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
5424
|
+
calls[name] = { ok: false, error: msg };
|
|
5425
|
+
errors.push(`${name}: ${msg}`);
|
|
5426
|
+
log(` \u2717 ${name}: ${msg}`);
|
|
5427
|
+
return void 0;
|
|
5428
|
+
}
|
|
5429
|
+
}
|
|
5430
|
+
const info = await capture(
|
|
5431
|
+
"getInfo",
|
|
5432
|
+
() => api.getInfo(channel),
|
|
5433
|
+
(v) => writeJson(path4.join(outDir, "device-info.json"), v)
|
|
5434
|
+
);
|
|
5435
|
+
const support = await capture(
|
|
5436
|
+
"getSupportInfo",
|
|
5437
|
+
() => api.getSupportInfo(),
|
|
5438
|
+
(v) => writeJson(path4.join(outDir, "support-info.json"), v)
|
|
5439
|
+
);
|
|
5440
|
+
const abilities = await capture(
|
|
5441
|
+
"getAbilityInfo",
|
|
5442
|
+
() => api.getAbilityInfo(),
|
|
5443
|
+
(v) => writeJson(path4.join(outDir, "ability-info.json"), v)
|
|
5444
|
+
);
|
|
5445
|
+
await capture(
|
|
5446
|
+
"getDeviceCapabilities",
|
|
5447
|
+
() => api.getDeviceCapabilities(channel),
|
|
5448
|
+
(v) => writeJson(path4.join(outDir, "capabilities.json"), v)
|
|
5449
|
+
);
|
|
5450
|
+
await capture("cmd289-WhiteLed", () => api.sendXml({
|
|
5451
|
+
cmdId: BC_CMD_ID_GET_WHITE_LED,
|
|
5452
|
+
channel,
|
|
5453
|
+
timeoutMs: 3e3
|
|
5454
|
+
}), (v) => writeText(path4.join(outDir, "cmd289-white-led.xml"), v));
|
|
5455
|
+
await capture(
|
|
5456
|
+
"getStreamMetadata",
|
|
5457
|
+
() => api.getStreamMetadata(channel),
|
|
5458
|
+
(v) => writeJson(path4.join(outDir, "stream-metadata.json"), v)
|
|
5459
|
+
);
|
|
5460
|
+
await capture(
|
|
5461
|
+
"getEncXml",
|
|
5462
|
+
() => api.getEncXml(channel),
|
|
5463
|
+
(v) => writeText(path4.join(outDir, "enc-config.xml"), v)
|
|
5464
|
+
);
|
|
5465
|
+
await capture(
|
|
5466
|
+
"getPorts",
|
|
5467
|
+
() => api.getPorts(),
|
|
5468
|
+
(v) => writeJson(path4.join(outDir, "ports.json"), v)
|
|
5469
|
+
);
|
|
5470
|
+
await capture(
|
|
5471
|
+
"getTalkAbility",
|
|
5472
|
+
() => api.getTalkAbility(channel),
|
|
5473
|
+
(v) => writeJson(path4.join(outDir, "talk-ability.json"), v)
|
|
5474
|
+
);
|
|
5475
|
+
await capture(
|
|
5476
|
+
"getTwoWayAudioConfig",
|
|
5477
|
+
() => api.getTwoWayAudioConfig(channel),
|
|
5478
|
+
(v) => writeJson(path4.join(outDir, "two-way-audio-config.json"), v)
|
|
5479
|
+
);
|
|
5480
|
+
await capture(
|
|
5481
|
+
"getAiState",
|
|
5482
|
+
() => api.getAiState(channel),
|
|
5483
|
+
(v) => writeJson(path4.join(outDir, "ai-state.json"), v)
|
|
5484
|
+
);
|
|
5485
|
+
await capture(
|
|
5486
|
+
"getAiCfg",
|
|
5487
|
+
() => api.getAiCfg(channel),
|
|
5488
|
+
(v) => writeJson(path4.join(outDir, "ai-cfg.json"), v)
|
|
5489
|
+
);
|
|
5490
|
+
await capture(
|
|
5491
|
+
"getOsd",
|
|
5492
|
+
() => api.getOsd(channel),
|
|
5493
|
+
(v) => writeJson(path4.join(outDir, "osd.json"), v)
|
|
5494
|
+
);
|
|
5495
|
+
await capture(
|
|
5496
|
+
"getMotionAlarm",
|
|
5497
|
+
() => api.getMotionAlarm(channel),
|
|
5498
|
+
(v) => writeJson(path4.join(outDir, "motion-alarm.json"), v)
|
|
5499
|
+
);
|
|
5500
|
+
await capture(
|
|
5501
|
+
"getRecordCfg",
|
|
5502
|
+
() => api.getRecordCfg(channel),
|
|
5503
|
+
(v) => writeJson(path4.join(outDir, "record-cfg.json"), v)
|
|
5504
|
+
);
|
|
5505
|
+
await capture(
|
|
5506
|
+
"getVideoInput",
|
|
5507
|
+
() => api.getVideoInput(channel),
|
|
5508
|
+
(v) => writeJson(path4.join(outDir, "video-input.json"), v)
|
|
5509
|
+
);
|
|
5510
|
+
await capture(
|
|
5511
|
+
"getPtzPresets",
|
|
5512
|
+
() => api.getPtzPresets(channel),
|
|
5513
|
+
(v) => writeJson(path4.join(outDir, "ptz-presets.json"), v)
|
|
5514
|
+
);
|
|
5515
|
+
await capture(
|
|
5516
|
+
"getNetworkInfo",
|
|
5517
|
+
() => api.getNetworkInfo(),
|
|
5518
|
+
(v) => writeJson(path4.join(outDir, "network-info.json"), v)
|
|
5519
|
+
);
|
|
5520
|
+
await capture(
|
|
5521
|
+
"getSystemGeneral",
|
|
5522
|
+
() => api.getSystemGeneral(),
|
|
5523
|
+
(v) => writeJson(path4.join(outDir, "system-general.json"), v)
|
|
5524
|
+
);
|
|
5525
|
+
await capture(
|
|
5526
|
+
"getWifiSignal",
|
|
5527
|
+
() => api.getWifiSignal(channel),
|
|
5528
|
+
(v) => writeJson(path4.join(outDir, "wifi-signal.json"), v)
|
|
5529
|
+
);
|
|
5530
|
+
await capture(
|
|
5531
|
+
"getWhiteLedState",
|
|
5532
|
+
() => api.getWhiteLedState(channel),
|
|
5533
|
+
(v) => writeJson(path4.join(outDir, "white-led-state.json"), v)
|
|
5534
|
+
);
|
|
5535
|
+
await capture(
|
|
5536
|
+
"getFloodlightOnMotion",
|
|
5537
|
+
() => api.getFloodlightOnMotion(channel),
|
|
5538
|
+
(v) => writeJson(path4.join(outDir, "floodlight-on-motion.json"), v)
|
|
5539
|
+
);
|
|
5540
|
+
await capture(
|
|
5541
|
+
"buildVideoStreamOptions",
|
|
5542
|
+
() => api.buildVideoStreamOptions({ channel }),
|
|
5543
|
+
(v) => writeJson(path4.join(outDir, "video-stream-options.json"), v)
|
|
5544
|
+
);
|
|
5545
|
+
const total = Object.keys(calls).length;
|
|
5546
|
+
const ok = Object.values(calls).filter((c) => c.ok).length;
|
|
5547
|
+
const failed = total - ok;
|
|
5548
|
+
const summary = { total, ok, failed, errors };
|
|
5549
|
+
writeJson(path4.join(outDir, "_summary.json"), {
|
|
5550
|
+
collectedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5551
|
+
model: info?.type ?? "unknown",
|
|
5552
|
+
itemNo: info?.itemNo ?? "unknown",
|
|
5553
|
+
firmwareVersion: info?.firmwareVersion ?? "unknown",
|
|
5554
|
+
channel,
|
|
5555
|
+
...summary,
|
|
5556
|
+
calls: Object.fromEntries(
|
|
5557
|
+
Object.entries(calls).map(([k, v]) => [
|
|
5558
|
+
k,
|
|
5559
|
+
v.ok ? "ok" : `FAILED: ${v.error}`
|
|
5560
|
+
])
|
|
5561
|
+
)
|
|
5562
|
+
});
|
|
5563
|
+
log(`
|
|
5564
|
+
Summary: ${ok}/${total} ok, ${failed} failed`);
|
|
5565
|
+
if (errors.length) {
|
|
5566
|
+
log(` Errors:`);
|
|
5567
|
+
for (const err of errors) {
|
|
5568
|
+
log(` - ${err}`);
|
|
5569
|
+
}
|
|
5570
|
+
}
|
|
5571
|
+
return { calls, outDir, summary };
|
|
5572
|
+
}
|
|
5402
5573
|
var fs4, path4, import_node_child_process, import_node_path;
|
|
5403
5574
|
var init_DiagnosticsTools = __esm({
|
|
5404
5575
|
"src/debug/DiagnosticsTools.ts"() {
|
|
@@ -7444,6 +7615,7 @@ __export(index_exports, {
|
|
|
7444
7615
|
DUAL_LENS_DUAL_MOTION_MODELS: () => DUAL_LENS_DUAL_MOTION_MODELS,
|
|
7445
7616
|
DUAL_LENS_MODELS: () => DUAL_LENS_MODELS,
|
|
7446
7617
|
DUAL_LENS_SINGLE_MOTION_MODELS: () => DUAL_LENS_SINGLE_MOTION_MODELS,
|
|
7618
|
+
Go2rtcTcpServer: () => Go2rtcTcpServer,
|
|
7447
7619
|
H264RtpDepacketizer: () => H264RtpDepacketizer,
|
|
7448
7620
|
H265RtpDepacketizer: () => H265RtpDepacketizer,
|
|
7449
7621
|
HlsSessionManager: () => HlsSessionManager,
|
|
@@ -7485,6 +7657,7 @@ __export(index_exports, {
|
|
|
7485
7657
|
buildSirenTimesXml: () => buildSirenTimesXml,
|
|
7486
7658
|
buildStartZoomFocusXml: () => buildStartZoomFocusXml,
|
|
7487
7659
|
buildWhiteLedStateXml: () => buildWhiteLedStateXml,
|
|
7660
|
+
captureModelFixtures: () => captureModelFixtures,
|
|
7488
7661
|
collectCgiDiagnostics: () => collectCgiDiagnostics,
|
|
7489
7662
|
collectMultifocalDiagnostics: () => collectMultifocalDiagnostics,
|
|
7490
7663
|
collectNativeDiagnostics: () => collectNativeDiagnostics,
|
|
@@ -7566,6 +7739,7 @@ __export(index_exports, {
|
|
|
7566
7739
|
splitH265AnnexBToNalPayloads: () => splitAnnexBToNalPayloads2,
|
|
7567
7740
|
testChannelStreams: () => testChannelStreams,
|
|
7568
7741
|
xmlEscape: () => xmlEscape,
|
|
7742
|
+
xmlIndicatesFloodlight: () => xmlIndicatesFloodlight,
|
|
7569
7743
|
zipDirectory: () => zipDirectory
|
|
7570
7744
|
});
|
|
7571
7745
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -9261,6 +9435,22 @@ var BaichuanClient = class _BaichuanClient extends import_node_events2.EventEmit
|
|
|
9261
9435
|
static coverPreviewBackoffMs = /* @__PURE__ */ new Map();
|
|
9262
9436
|
static COVER_PREVIEW_INITIAL_BACKOFF_MS = 1e3;
|
|
9263
9437
|
static COVER_PREVIEW_MAX_BACKOFF_MS = 3e4;
|
|
9438
|
+
/**
|
|
9439
|
+
* Per-client snapshot (cmd_id=109) serialization queue.
|
|
9440
|
+
*
|
|
9441
|
+
* WHY: On NVR/multi-camera devices sharing one socket, concurrent snapshot requests
|
|
9442
|
+
* can cause JPEG data to mix (even with per-request msgNum filtering):
|
|
9443
|
+
* - Camera A and B both send frames on same socket
|
|
9444
|
+
* - Frame listener is global per socket
|
|
9445
|
+
* - Timing quirks can cause chunk reordering or listener confusion
|
|
9446
|
+
*
|
|
9447
|
+
* FIX: Serialize all cmd_id=109 requests on THIS client instance.
|
|
9448
|
+
* Each snapshot waits for previous one to complete before starting.
|
|
9449
|
+
* This ensures clean frame sequences per request, zero data corruption.
|
|
9450
|
+
*
|
|
9451
|
+
* Impact: Snapshots are ~0–50ms slower per camera (negligible for users).
|
|
9452
|
+
*/
|
|
9453
|
+
snapshotQueueTail = Promise.resolve();
|
|
9264
9454
|
opts;
|
|
9265
9455
|
debugCfg;
|
|
9266
9456
|
logger;
|
|
@@ -11742,6 +11932,20 @@ var BaichuanClient = class _BaichuanClient extends import_node_events2.EventEmit
|
|
|
11742
11932
|
});
|
|
11743
11933
|
}
|
|
11744
11934
|
async sendBinarySnapshot109(params) {
|
|
11935
|
+
const prevTail = this.snapshotQueueTail;
|
|
11936
|
+
let resolve;
|
|
11937
|
+
const newTail = new Promise((r) => {
|
|
11938
|
+
resolve = r;
|
|
11939
|
+
});
|
|
11940
|
+
this.snapshotQueueTail = newTail;
|
|
11941
|
+
try {
|
|
11942
|
+
await prevTail;
|
|
11943
|
+
return await this.sendBinarySnapshot109Impl(params);
|
|
11944
|
+
} finally {
|
|
11945
|
+
resolve();
|
|
11946
|
+
}
|
|
11947
|
+
}
|
|
11948
|
+
async sendBinarySnapshot109Impl(params) {
|
|
11745
11949
|
await this.connect();
|
|
11746
11950
|
const channel = params.channel ?? this.opts.channel ?? 0;
|
|
11747
11951
|
const channelId = params.channelIdOverride ?? (params.channel == null ? this.hostChannelId : channel + 1);
|
|
@@ -11801,7 +12005,8 @@ var BaichuanClient = class _BaichuanClient extends import_node_events2.EventEmit
|
|
|
11801
12005
|
};
|
|
11802
12006
|
const onFrame = (frame) => {
|
|
11803
12007
|
if (frame.header.cmdId !== cmdId) return;
|
|
11804
|
-
if (frame.header.msgNum
|
|
12008
|
+
if (frame.header.msgNum !== msgNum) return;
|
|
12009
|
+
if (frame.header.responseCode >= 400) {
|
|
11805
12010
|
fail(
|
|
11806
12011
|
new Error(
|
|
11807
12012
|
`Baichuan snapshot request rejected (cmdId=${cmdId} msgNum=${msgNum} responseCode=${frame.header.responseCode})`
|
|
@@ -15285,6 +15490,17 @@ function computeDeviceCapabilities(params) {
|
|
|
15285
15490
|
if (ptzMode !== void 0) result.ptzMode = ptzMode;
|
|
15286
15491
|
return result;
|
|
15287
15492
|
}
|
|
15493
|
+
function xmlIndicatesFloodlight(xml) {
|
|
15494
|
+
if (/(<FloodlightTask\b|<FloodlightManual\b|<FloodlightStatusList\b)/i.test(
|
|
15495
|
+
xml
|
|
15496
|
+
)) {
|
|
15497
|
+
return true;
|
|
15498
|
+
}
|
|
15499
|
+
if (/<WhiteLed\b/i.test(xml)) {
|
|
15500
|
+
return /(<brightness_cur>|<bright>|<LightingSchedule\b)/i.test(xml);
|
|
15501
|
+
}
|
|
15502
|
+
return false;
|
|
15503
|
+
}
|
|
15288
15504
|
|
|
15289
15505
|
// src/reolink/baichuan/utils/abilityInfo.ts
|
|
15290
15506
|
init_xml();
|
|
@@ -24321,9 +24537,7 @@ ${stderr}`)
|
|
|
24321
24537
|
`probeFloodlightSupportByCmd289: received XML for channel ${ch}:
|
|
24322
24538
|
${xml}`
|
|
24323
24539
|
);
|
|
24324
|
-
return
|
|
24325
|
-
xml
|
|
24326
|
-
);
|
|
24540
|
+
return xmlIndicatesFloodlight(xml);
|
|
24327
24541
|
} catch {
|
|
24328
24542
|
return false;
|
|
24329
24543
|
}
|
|
@@ -32724,8 +32938,529 @@ async function createReplayHttpServer(options) {
|
|
|
32724
32938
|
// src/index.ts
|
|
32725
32939
|
init_BaichuanVideoStream();
|
|
32726
32940
|
|
|
32727
|
-
// src/baichuan/stream/
|
|
32941
|
+
// src/baichuan/stream/Go2rtcTcpServer.ts
|
|
32728
32942
|
var import_node_events6 = require("events");
|
|
32943
|
+
var net4 = __toESM(require("net"), 1);
|
|
32944
|
+
init_H264Converter();
|
|
32945
|
+
init_H265Converter();
|
|
32946
|
+
var AsyncBoundedQueue2 = class {
|
|
32947
|
+
maxItems;
|
|
32948
|
+
queue = [];
|
|
32949
|
+
waiting;
|
|
32950
|
+
closed = false;
|
|
32951
|
+
constructor(maxItems) {
|
|
32952
|
+
this.maxItems = Math.max(1, maxItems | 0);
|
|
32953
|
+
}
|
|
32954
|
+
push(item) {
|
|
32955
|
+
if (this.closed) return;
|
|
32956
|
+
if (this.waiting) {
|
|
32957
|
+
const { resolve } = this.waiting;
|
|
32958
|
+
this.waiting = void 0;
|
|
32959
|
+
resolve({ value: item, done: false });
|
|
32960
|
+
return;
|
|
32961
|
+
}
|
|
32962
|
+
this.queue.push(item);
|
|
32963
|
+
if (this.queue.length > this.maxItems) {
|
|
32964
|
+
this.queue.splice(0, this.queue.length - this.maxItems);
|
|
32965
|
+
}
|
|
32966
|
+
}
|
|
32967
|
+
close() {
|
|
32968
|
+
if (this.closed) return;
|
|
32969
|
+
this.closed = true;
|
|
32970
|
+
if (this.waiting) {
|
|
32971
|
+
const { resolve } = this.waiting;
|
|
32972
|
+
this.waiting = void 0;
|
|
32973
|
+
resolve({ value: void 0, done: true });
|
|
32974
|
+
}
|
|
32975
|
+
}
|
|
32976
|
+
async next() {
|
|
32977
|
+
if (this.closed) return { value: void 0, done: true };
|
|
32978
|
+
const item = this.queue.shift();
|
|
32979
|
+
if (item !== void 0) return { value: item, done: false };
|
|
32980
|
+
return await new Promise((resolve) => {
|
|
32981
|
+
this.waiting = { resolve };
|
|
32982
|
+
});
|
|
32983
|
+
}
|
|
32984
|
+
};
|
|
32985
|
+
var NativeStreamFanout2 = class {
|
|
32986
|
+
opts;
|
|
32987
|
+
queues = /* @__PURE__ */ new Map();
|
|
32988
|
+
source = null;
|
|
32989
|
+
running = false;
|
|
32990
|
+
pumpPromise = null;
|
|
32991
|
+
constructor(opts) {
|
|
32992
|
+
this.opts = opts;
|
|
32993
|
+
}
|
|
32994
|
+
start() {
|
|
32995
|
+
if (this.running) return;
|
|
32996
|
+
this.running = true;
|
|
32997
|
+
this.source = this.opts.createSource();
|
|
32998
|
+
this.pumpPromise = (async () => {
|
|
32999
|
+
try {
|
|
33000
|
+
for await (const frame of this.source) {
|
|
33001
|
+
try {
|
|
33002
|
+
this.opts.onFrame?.(frame);
|
|
33003
|
+
} catch {
|
|
33004
|
+
}
|
|
33005
|
+
for (const q of this.queues.values()) {
|
|
33006
|
+
q.push(frame);
|
|
33007
|
+
}
|
|
33008
|
+
}
|
|
33009
|
+
} catch (e) {
|
|
33010
|
+
this.opts.onError?.(e);
|
|
33011
|
+
} finally {
|
|
33012
|
+
for (const q of this.queues.values()) q.close();
|
|
33013
|
+
this.queues.clear();
|
|
33014
|
+
this.running = false;
|
|
33015
|
+
this.opts.onEnd?.();
|
|
33016
|
+
}
|
|
33017
|
+
})();
|
|
33018
|
+
}
|
|
33019
|
+
subscribe(id) {
|
|
33020
|
+
const q = new AsyncBoundedQueue2(this.opts.maxQueueItems);
|
|
33021
|
+
this.queues.set(id, q);
|
|
33022
|
+
const self = this;
|
|
33023
|
+
return (async function* () {
|
|
33024
|
+
try {
|
|
33025
|
+
while (true) {
|
|
33026
|
+
const r = await q.next();
|
|
33027
|
+
if (r.done) return;
|
|
33028
|
+
yield r.value;
|
|
33029
|
+
}
|
|
33030
|
+
} finally {
|
|
33031
|
+
q.close();
|
|
33032
|
+
self.queues.delete(id);
|
|
33033
|
+
}
|
|
33034
|
+
})();
|
|
33035
|
+
}
|
|
33036
|
+
async stop() {
|
|
33037
|
+
if (!this.running) return;
|
|
33038
|
+
this.running = false;
|
|
33039
|
+
const src = this.source;
|
|
33040
|
+
this.source = null;
|
|
33041
|
+
for (const q of this.queues.values()) q.close();
|
|
33042
|
+
this.queues.clear();
|
|
33043
|
+
try {
|
|
33044
|
+
await src?.return(void 0);
|
|
33045
|
+
} catch {
|
|
33046
|
+
}
|
|
33047
|
+
try {
|
|
33048
|
+
await this.pumpPromise;
|
|
33049
|
+
} catch {
|
|
33050
|
+
}
|
|
33051
|
+
this.pumpPromise = null;
|
|
33052
|
+
}
|
|
33053
|
+
};
|
|
33054
|
+
var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events6.EventEmitter {
|
|
33055
|
+
api;
|
|
33056
|
+
channel;
|
|
33057
|
+
profile;
|
|
33058
|
+
variant;
|
|
33059
|
+
listenHost;
|
|
33060
|
+
listenPort;
|
|
33061
|
+
logger;
|
|
33062
|
+
deviceId;
|
|
33063
|
+
gracePeriodMs;
|
|
33064
|
+
prebufferMaxMs;
|
|
33065
|
+
maxBufferBytes;
|
|
33066
|
+
prestartStream;
|
|
33067
|
+
active = false;
|
|
33068
|
+
server;
|
|
33069
|
+
resolvedPort;
|
|
33070
|
+
// Native stream
|
|
33071
|
+
nativeFanout = null;
|
|
33072
|
+
nativeStreamActive = false;
|
|
33073
|
+
dedicatedSessionRelease;
|
|
33074
|
+
detectedVideoType;
|
|
33075
|
+
// Client tracking
|
|
33076
|
+
connectedClients = /* @__PURE__ */ new Set();
|
|
33077
|
+
clientSockets = /* @__PURE__ */ new Map();
|
|
33078
|
+
stopGraceTimer;
|
|
33079
|
+
// Prebuffer
|
|
33080
|
+
prebuffer = [];
|
|
33081
|
+
constructor(options) {
|
|
33082
|
+
super();
|
|
33083
|
+
this.api = options.api;
|
|
33084
|
+
this.channel = options.channel;
|
|
33085
|
+
this.profile = options.profile;
|
|
33086
|
+
this.variant = options.variant ?? "default";
|
|
33087
|
+
this.listenHost = options.listenHost ?? "127.0.0.1";
|
|
33088
|
+
this.listenPort = options.listenPort ?? 0;
|
|
33089
|
+
this.logger = options.logger ?? console;
|
|
33090
|
+
this.deviceId = options.deviceId;
|
|
33091
|
+
this.gracePeriodMs = options.gracePeriodMs ?? 3e4;
|
|
33092
|
+
this.prebufferMaxMs = options.prebufferMs ?? 3e3;
|
|
33093
|
+
this.maxBufferBytes = options.maxBufferBytes ?? 1e8;
|
|
33094
|
+
this.prestartStream = options.prestartStream ?? true;
|
|
33095
|
+
}
|
|
33096
|
+
// -----------------------------------------------------------------------
|
|
33097
|
+
// Public API
|
|
33098
|
+
// -----------------------------------------------------------------------
|
|
33099
|
+
/** Start listening. Resolves once the TCP server is bound. */
|
|
33100
|
+
async start() {
|
|
33101
|
+
if (this.active) return;
|
|
33102
|
+
this.active = true;
|
|
33103
|
+
this.server = net4.createServer((socket) => this.handleClient(socket));
|
|
33104
|
+
this.server.on("error", (err) => {
|
|
33105
|
+
this.logger.error?.(`[Go2rtcTcpServer] server error: ${err.message}`);
|
|
33106
|
+
this.emit("error", err);
|
|
33107
|
+
});
|
|
33108
|
+
await new Promise((resolve, reject) => {
|
|
33109
|
+
this.server.listen(this.listenPort, this.listenHost, () => {
|
|
33110
|
+
const addr = this.server.address();
|
|
33111
|
+
this.resolvedPort = addr.port;
|
|
33112
|
+
this.logger.info?.(
|
|
33113
|
+
`[Go2rtcTcpServer] listening on ${addr.address}:${addr.port} channel=${this.channel} profile=${this.profile}`
|
|
33114
|
+
);
|
|
33115
|
+
this.emit("listening", { host: addr.address, port: addr.port });
|
|
33116
|
+
resolve();
|
|
33117
|
+
});
|
|
33118
|
+
this.server.once("error", reject);
|
|
33119
|
+
});
|
|
33120
|
+
if (this.prestartStream) {
|
|
33121
|
+
this.logger.info?.(
|
|
33122
|
+
`[Go2rtcTcpServer] pre-starting native stream channel=${this.channel} profile=${this.profile}`
|
|
33123
|
+
);
|
|
33124
|
+
this.startNativeStream();
|
|
33125
|
+
}
|
|
33126
|
+
}
|
|
33127
|
+
/** Stop the server and all active streams. */
|
|
33128
|
+
async stop() {
|
|
33129
|
+
if (!this.active) return;
|
|
33130
|
+
this.active = false;
|
|
33131
|
+
clearTimeout(this.stopGraceTimer);
|
|
33132
|
+
for (const [id, sock] of this.clientSockets) {
|
|
33133
|
+
sock.destroy();
|
|
33134
|
+
this.connectedClients.delete(id);
|
|
33135
|
+
}
|
|
33136
|
+
this.clientSockets.clear();
|
|
33137
|
+
await this.stopNativeStream();
|
|
33138
|
+
if (this.server) {
|
|
33139
|
+
await new Promise((resolve) => {
|
|
33140
|
+
this.server.close(() => resolve());
|
|
33141
|
+
});
|
|
33142
|
+
this.server = void 0;
|
|
33143
|
+
}
|
|
33144
|
+
this.prebuffer = [];
|
|
33145
|
+
this.resolvedPort = void 0;
|
|
33146
|
+
this.emit("close");
|
|
33147
|
+
}
|
|
33148
|
+
/** The actual port the server is listening on (available after start()). */
|
|
33149
|
+
get port() {
|
|
33150
|
+
return this.resolvedPort;
|
|
33151
|
+
}
|
|
33152
|
+
/** The go2rtc-compatible source URL. */
|
|
33153
|
+
get go2rtcSourceUrl() {
|
|
33154
|
+
if (this.resolvedPort == null) return void 0;
|
|
33155
|
+
return `tcp://127.0.0.1:${this.resolvedPort}`;
|
|
33156
|
+
}
|
|
33157
|
+
/** Number of currently connected clients. */
|
|
33158
|
+
get clientCount() {
|
|
33159
|
+
return this.connectedClients.size;
|
|
33160
|
+
}
|
|
33161
|
+
// -----------------------------------------------------------------------
|
|
33162
|
+
// Client handling
|
|
33163
|
+
// -----------------------------------------------------------------------
|
|
33164
|
+
handleClient(socket) {
|
|
33165
|
+
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
33166
|
+
socket.setNoDelay(true);
|
|
33167
|
+
this.connectedClients.add(clientId);
|
|
33168
|
+
this.clientSockets.set(clientId, socket);
|
|
33169
|
+
this.logger.info?.(
|
|
33170
|
+
`[Go2rtcTcpServer] client connected id=${clientId} total=${this.connectedClients.size}`
|
|
33171
|
+
);
|
|
33172
|
+
this.emit("client", clientId);
|
|
33173
|
+
if (this.stopGraceTimer) {
|
|
33174
|
+
clearTimeout(this.stopGraceTimer);
|
|
33175
|
+
this.stopGraceTimer = void 0;
|
|
33176
|
+
}
|
|
33177
|
+
if (!this.nativeStreamActive) {
|
|
33178
|
+
this.startNativeStream();
|
|
33179
|
+
}
|
|
33180
|
+
this.feedClient(clientId, socket).catch((err) => {
|
|
33181
|
+
this.logger.warn?.(
|
|
33182
|
+
`[Go2rtcTcpServer] feedClient error id=${clientId}: ${err}`
|
|
33183
|
+
);
|
|
33184
|
+
});
|
|
33185
|
+
const cleanup = () => {
|
|
33186
|
+
this.removeClient(clientId);
|
|
33187
|
+
socket.destroy();
|
|
33188
|
+
};
|
|
33189
|
+
socket.on("error", cleanup);
|
|
33190
|
+
socket.on("close", cleanup);
|
|
33191
|
+
}
|
|
33192
|
+
async feedClient(clientId, socket) {
|
|
33193
|
+
const fanoutDeadline = Date.now() + 3e4;
|
|
33194
|
+
while (this.active && !this.nativeFanout) {
|
|
33195
|
+
if (socket.destroyed) return;
|
|
33196
|
+
if (Date.now() > fanoutDeadline) {
|
|
33197
|
+
this.logger.warn?.(
|
|
33198
|
+
`[Go2rtcTcpServer] fanout not ready after 30s, dropping client ${clientId}`
|
|
33199
|
+
);
|
|
33200
|
+
return;
|
|
33201
|
+
}
|
|
33202
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
33203
|
+
}
|
|
33204
|
+
if (!this.active || !this.nativeFanout) return;
|
|
33205
|
+
const subscription = this.nativeFanout.subscribe(clientId);
|
|
33206
|
+
const prebufferSnap = this.prebuffer.slice();
|
|
33207
|
+
let lastIdrIdx = -1;
|
|
33208
|
+
for (let i = prebufferSnap.length - 1; i >= 0; i--) {
|
|
33209
|
+
if (prebufferSnap[i].isKeyframe) {
|
|
33210
|
+
lastIdrIdx = i;
|
|
33211
|
+
break;
|
|
33212
|
+
}
|
|
33213
|
+
}
|
|
33214
|
+
if (lastIdrIdx >= 0) {
|
|
33215
|
+
const replay = prebufferSnap.slice(lastIdrIdx);
|
|
33216
|
+
this.logger.info?.(
|
|
33217
|
+
`[Go2rtcTcpServer] prebuffer replay client=${clientId} frames=${replay.length}`
|
|
33218
|
+
);
|
|
33219
|
+
for (const entry of replay) {
|
|
33220
|
+
if (socket.destroyed) return;
|
|
33221
|
+
socket.write(entry.data);
|
|
33222
|
+
}
|
|
33223
|
+
}
|
|
33224
|
+
let seenKeyframe = lastIdrIdx >= 0;
|
|
33225
|
+
let liveFrameCount = 0;
|
|
33226
|
+
let liveVideoWritten = 0;
|
|
33227
|
+
let lastLogAt = Date.now();
|
|
33228
|
+
try {
|
|
33229
|
+
this.logger.info?.(
|
|
33230
|
+
`[Go2rtcTcpServer] entering live loop client=${clientId} seenKeyframe=${seenKeyframe}`
|
|
33231
|
+
);
|
|
33232
|
+
for await (const frame of subscription) {
|
|
33233
|
+
if (socket.destroyed || !this.active) {
|
|
33234
|
+
this.logger.info?.(
|
|
33235
|
+
`[Go2rtcTcpServer] live loop exit client=${clientId} destroyed=${socket.destroyed} active=${this.active}`
|
|
33236
|
+
);
|
|
33237
|
+
break;
|
|
33238
|
+
}
|
|
33239
|
+
liveFrameCount++;
|
|
33240
|
+
const annexB = this.convertFrame(frame);
|
|
33241
|
+
if (!annexB) continue;
|
|
33242
|
+
if (!seenKeyframe) {
|
|
33243
|
+
if (!this.isAnnexBKeyframe(annexB, frame.videoType)) continue;
|
|
33244
|
+
seenKeyframe = true;
|
|
33245
|
+
this.logger.info?.(
|
|
33246
|
+
`[Go2rtcTcpServer] first live keyframe client=${clientId} after ${liveFrameCount} frames`
|
|
33247
|
+
);
|
|
33248
|
+
}
|
|
33249
|
+
socket.write(annexB);
|
|
33250
|
+
liveVideoWritten++;
|
|
33251
|
+
if (Date.now() - lastLogAt > 1e4) {
|
|
33252
|
+
this.logger.info?.(
|
|
33253
|
+
`[Go2rtcTcpServer] live stats client=${clientId} received=${liveFrameCount} written=${liveVideoWritten} bufLen=${socket.writableLength}`
|
|
33254
|
+
);
|
|
33255
|
+
lastLogAt = Date.now();
|
|
33256
|
+
}
|
|
33257
|
+
if (socket.writableLength > this.maxBufferBytes) {
|
|
33258
|
+
this.logger.warn?.(
|
|
33259
|
+
`[Go2rtcTcpServer] buffer overflow (${socket.writableLength} bytes), dropping client ${clientId}`
|
|
33260
|
+
);
|
|
33261
|
+
socket.destroy();
|
|
33262
|
+
break;
|
|
33263
|
+
}
|
|
33264
|
+
}
|
|
33265
|
+
this.logger.info?.(
|
|
33266
|
+
`[Go2rtcTcpServer] live loop ended naturally client=${clientId} received=${liveFrameCount} written=${liveVideoWritten}`
|
|
33267
|
+
);
|
|
33268
|
+
} finally {
|
|
33269
|
+
await subscription.return(void 0).catch(() => {
|
|
33270
|
+
});
|
|
33271
|
+
}
|
|
33272
|
+
}
|
|
33273
|
+
// -----------------------------------------------------------------------
|
|
33274
|
+
// Frame conversion
|
|
33275
|
+
// -----------------------------------------------------------------------
|
|
33276
|
+
/**
|
|
33277
|
+
* Convert a native frame to wire-ready Annex-B.
|
|
33278
|
+
* Audio frames are skipped — raw TCP carries only video (Annex-B).
|
|
33279
|
+
* go2rtc auto-detects the codec from SPS/PPS/VPS NALUs.
|
|
33280
|
+
*/
|
|
33281
|
+
convertFrame(frame) {
|
|
33282
|
+
if (frame.audio) {
|
|
33283
|
+
return null;
|
|
33284
|
+
}
|
|
33285
|
+
if (frame.data.length === 0) return null;
|
|
33286
|
+
try {
|
|
33287
|
+
if (frame.videoType === "H264") {
|
|
33288
|
+
return convertToAnnexB(frame.data);
|
|
33289
|
+
}
|
|
33290
|
+
if (frame.videoType === "H265") {
|
|
33291
|
+
return convertToAnnexB2(frame.data);
|
|
33292
|
+
}
|
|
33293
|
+
} catch {
|
|
33294
|
+
}
|
|
33295
|
+
return frame.data;
|
|
33296
|
+
}
|
|
33297
|
+
/** Check if an Annex-B buffer contains a keyframe (IDR for H.264, IRAP for H.265). */
|
|
33298
|
+
isAnnexBKeyframe(annexB, videoType) {
|
|
33299
|
+
try {
|
|
33300
|
+
if (videoType === "H264") {
|
|
33301
|
+
const nals = _Go2rtcTcpServer.splitAnnexBNals(annexB);
|
|
33302
|
+
return nals.some((n) => n.length >= 1 && (n[0] & 31) === 5);
|
|
33303
|
+
}
|
|
33304
|
+
if (videoType === "H265") {
|
|
33305
|
+
const nals = splitAnnexBToNalPayloads2(annexB);
|
|
33306
|
+
return nals.some(
|
|
33307
|
+
(n) => n.length >= 2 && isH265Irap(n[0] >> 1 & 63)
|
|
33308
|
+
);
|
|
33309
|
+
}
|
|
33310
|
+
} catch {
|
|
33311
|
+
}
|
|
33312
|
+
return false;
|
|
33313
|
+
}
|
|
33314
|
+
/** Split Annex-B byte stream into individual NAL units. */
|
|
33315
|
+
static splitAnnexBNals(buf) {
|
|
33316
|
+
const nals = [];
|
|
33317
|
+
let i = 0;
|
|
33318
|
+
while (i < buf.length) {
|
|
33319
|
+
if (i + 2 < buf.length && buf[i] === 0 && buf[i + 1] === 0) {
|
|
33320
|
+
let scLen;
|
|
33321
|
+
if (buf[i + 2] === 1) {
|
|
33322
|
+
scLen = 3;
|
|
33323
|
+
} else if (i + 3 < buf.length && buf[i + 2] === 0 && buf[i + 3] === 1) {
|
|
33324
|
+
scLen = 4;
|
|
33325
|
+
} else {
|
|
33326
|
+
i++;
|
|
33327
|
+
continue;
|
|
33328
|
+
}
|
|
33329
|
+
const nalStart = i + scLen;
|
|
33330
|
+
let nalEnd = buf.length;
|
|
33331
|
+
for (let j = nalStart; j < buf.length - 2; j++) {
|
|
33332
|
+
if (buf[j] === 0 && buf[j + 1] === 0 && (buf[j + 2] === 1 || j + 3 < buf.length && buf[j + 2] === 0 && buf[j + 3] === 1)) {
|
|
33333
|
+
nalEnd = j;
|
|
33334
|
+
break;
|
|
33335
|
+
}
|
|
33336
|
+
}
|
|
33337
|
+
if (nalEnd > nalStart) {
|
|
33338
|
+
nals.push(buf.subarray(nalStart, nalEnd));
|
|
33339
|
+
}
|
|
33340
|
+
i = nalEnd;
|
|
33341
|
+
} else {
|
|
33342
|
+
i++;
|
|
33343
|
+
}
|
|
33344
|
+
}
|
|
33345
|
+
return nals;
|
|
33346
|
+
}
|
|
33347
|
+
// -----------------------------------------------------------------------
|
|
33348
|
+
// Native stream management
|
|
33349
|
+
// -----------------------------------------------------------------------
|
|
33350
|
+
async startNativeStream() {
|
|
33351
|
+
if (this.nativeStreamActive) return;
|
|
33352
|
+
this.nativeStreamActive = true;
|
|
33353
|
+
let dedicatedClient;
|
|
33354
|
+
if (this.deviceId) {
|
|
33355
|
+
try {
|
|
33356
|
+
const session = await this.api.createDedicatedSession(
|
|
33357
|
+
`live:${this.deviceId}:ch${this.channel}:${this.profile}`
|
|
33358
|
+
);
|
|
33359
|
+
dedicatedClient = session.client;
|
|
33360
|
+
this.dedicatedSessionRelease = session.release;
|
|
33361
|
+
} catch (e) {
|
|
33362
|
+
this.logger.warn?.(
|
|
33363
|
+
`[Go2rtcTcpServer] failed to acquire dedicated session, using shared socket: ${e}`
|
|
33364
|
+
);
|
|
33365
|
+
}
|
|
33366
|
+
}
|
|
33367
|
+
this.logger.info?.(
|
|
33368
|
+
`[Go2rtcTcpServer] native stream starting channel=${this.channel} profile=${this.profile} dedicated=${!!dedicatedClient}`
|
|
33369
|
+
);
|
|
33370
|
+
this.nativeFanout = new NativeStreamFanout2({
|
|
33371
|
+
maxQueueItems: 200,
|
|
33372
|
+
createSource: () => createNativeStream(this.api, this.channel, this.profile, {
|
|
33373
|
+
variant: this.variant,
|
|
33374
|
+
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
33375
|
+
}),
|
|
33376
|
+
onFrame: (frame) => {
|
|
33377
|
+
if (!frame.audio && (frame.videoType === "H264" || frame.videoType === "H265")) {
|
|
33378
|
+
this.detectedVideoType = frame.videoType;
|
|
33379
|
+
}
|
|
33380
|
+
const wireData = this.convertFrame(frame);
|
|
33381
|
+
if (!wireData || wireData.length === 0) return;
|
|
33382
|
+
const isKeyframe = !frame.audio && this.isAnnexBKeyframe(wireData, frame.videoType);
|
|
33383
|
+
this.prebuffer.push({
|
|
33384
|
+
data: Buffer.from(wireData),
|
|
33385
|
+
time: Date.now(),
|
|
33386
|
+
isKeyframe,
|
|
33387
|
+
audio: frame.audio
|
|
33388
|
+
});
|
|
33389
|
+
const cutoff = Date.now() - this.prebufferMaxMs;
|
|
33390
|
+
let trimIdx = 0;
|
|
33391
|
+
while (trimIdx < this.prebuffer.length && this.prebuffer[trimIdx].time < cutoff) {
|
|
33392
|
+
trimIdx++;
|
|
33393
|
+
}
|
|
33394
|
+
if (trimIdx > 0) this.prebuffer.splice(0, trimIdx);
|
|
33395
|
+
},
|
|
33396
|
+
onError: (error) => {
|
|
33397
|
+
this.logger.warn?.(`[Go2rtcTcpServer] native stream error: ${error}`);
|
|
33398
|
+
},
|
|
33399
|
+
onEnd: () => {
|
|
33400
|
+
if (!this.nativeStreamActive) return;
|
|
33401
|
+
this.nativeStreamActive = false;
|
|
33402
|
+
this.nativeFanout = null;
|
|
33403
|
+
if (this.dedicatedSessionRelease) {
|
|
33404
|
+
this.dedicatedSessionRelease().catch(() => {
|
|
33405
|
+
});
|
|
33406
|
+
this.dedicatedSessionRelease = void 0;
|
|
33407
|
+
}
|
|
33408
|
+
if (this.active && (this.connectedClients.size > 0 || this.prestartStream)) {
|
|
33409
|
+
this.logger.info?.(
|
|
33410
|
+
`[Go2rtcTcpServer] native stream ended, restarting (clients=${this.connectedClients.size}, prestart=${this.prestartStream})`
|
|
33411
|
+
);
|
|
33412
|
+
this.startNativeStream();
|
|
33413
|
+
}
|
|
33414
|
+
}
|
|
33415
|
+
});
|
|
33416
|
+
this.nativeFanout.start();
|
|
33417
|
+
}
|
|
33418
|
+
async stopNativeStream() {
|
|
33419
|
+
this.nativeStreamActive = false;
|
|
33420
|
+
const fanout = this.nativeFanout;
|
|
33421
|
+
this.nativeFanout = null;
|
|
33422
|
+
if (fanout) {
|
|
33423
|
+
await fanout.stop();
|
|
33424
|
+
}
|
|
33425
|
+
this.prebuffer = [];
|
|
33426
|
+
if (this.dedicatedSessionRelease) {
|
|
33427
|
+
await this.dedicatedSessionRelease().catch(() => {
|
|
33428
|
+
});
|
|
33429
|
+
this.dedicatedSessionRelease = void 0;
|
|
33430
|
+
}
|
|
33431
|
+
}
|
|
33432
|
+
// -----------------------------------------------------------------------
|
|
33433
|
+
// Client lifecycle
|
|
33434
|
+
// -----------------------------------------------------------------------
|
|
33435
|
+
removeClient(clientId) {
|
|
33436
|
+
if (!this.connectedClients.has(clientId)) return;
|
|
33437
|
+
this.connectedClients.delete(clientId);
|
|
33438
|
+
this.clientSockets.delete(clientId);
|
|
33439
|
+
this.logger.info?.(
|
|
33440
|
+
`[Go2rtcTcpServer] client disconnected id=${clientId} remaining=${this.connectedClients.size}`
|
|
33441
|
+
);
|
|
33442
|
+
this.emit("clientDisconnected", clientId);
|
|
33443
|
+
if (this.connectedClients.size === 0 && !this.prestartStream) {
|
|
33444
|
+
this.scheduleStop();
|
|
33445
|
+
}
|
|
33446
|
+
}
|
|
33447
|
+
scheduleStop() {
|
|
33448
|
+
if (this.stopGraceTimer) return;
|
|
33449
|
+
this.logger.info?.(
|
|
33450
|
+
`[Go2rtcTcpServer] no clients, scheduling stream stop in ${this.gracePeriodMs}ms`
|
|
33451
|
+
);
|
|
33452
|
+
this.stopGraceTimer = setTimeout(async () => {
|
|
33453
|
+
this.stopGraceTimer = void 0;
|
|
33454
|
+
if (this.connectedClients.size === 0 && this.nativeStreamActive) {
|
|
33455
|
+
this.logger.info?.("[Go2rtcTcpServer] grace period expired, stopping native stream");
|
|
33456
|
+
await this.stopNativeStream();
|
|
33457
|
+
}
|
|
33458
|
+
}, this.gracePeriodMs);
|
|
33459
|
+
}
|
|
33460
|
+
};
|
|
33461
|
+
|
|
33462
|
+
// src/baichuan/stream/BaichuanHttpStreamServer.ts
|
|
33463
|
+
var import_node_events7 = require("events");
|
|
32729
33464
|
var import_node_child_process9 = require("child_process");
|
|
32730
33465
|
var http4 = __toESM(require("http"), 1);
|
|
32731
33466
|
var NAL_START_CODE_4B4 = Buffer.from([0, 0, 0, 1]);
|
|
@@ -32772,7 +33507,7 @@ function isH264KeyframeFromAnnexB(annexB) {
|
|
|
32772
33507
|
}
|
|
32773
33508
|
return false;
|
|
32774
33509
|
}
|
|
32775
|
-
var BaichuanHttpStreamServer = class extends
|
|
33510
|
+
var BaichuanHttpStreamServer = class extends import_node_events7.EventEmitter {
|
|
32776
33511
|
videoStream;
|
|
32777
33512
|
listenPort;
|
|
32778
33513
|
path;
|
|
@@ -33039,15 +33774,15 @@ var BaichuanHttpStreamServer = class extends import_node_events6.EventEmitter {
|
|
|
33039
33774
|
};
|
|
33040
33775
|
|
|
33041
33776
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
33042
|
-
var
|
|
33777
|
+
var import_node_events9 = require("events");
|
|
33043
33778
|
var http5 = __toESM(require("http"), 1);
|
|
33044
33779
|
|
|
33045
33780
|
// src/baichuan/stream/MjpegTransformer.ts
|
|
33046
|
-
var
|
|
33781
|
+
var import_node_events8 = require("events");
|
|
33047
33782
|
var import_node_child_process10 = require("child_process");
|
|
33048
33783
|
var JPEG_SOI = Buffer.from([255, 216]);
|
|
33049
33784
|
var JPEG_EOI = Buffer.from([255, 217]);
|
|
33050
|
-
var MjpegTransformer = class extends
|
|
33785
|
+
var MjpegTransformer = class extends import_node_events8.EventEmitter {
|
|
33051
33786
|
options;
|
|
33052
33787
|
ffmpeg = null;
|
|
33053
33788
|
started = false;
|
|
@@ -33246,7 +33981,7 @@ Content-Length: ${frame.length}\r
|
|
|
33246
33981
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
33247
33982
|
init_H264Converter();
|
|
33248
33983
|
init_H265Converter();
|
|
33249
|
-
var BaichuanMjpegServer = class extends
|
|
33984
|
+
var BaichuanMjpegServer = class extends import_node_events9.EventEmitter {
|
|
33250
33985
|
options;
|
|
33251
33986
|
clients = /* @__PURE__ */ new Map();
|
|
33252
33987
|
httpServer = null;
|
|
@@ -33527,7 +34262,7 @@ var BaichuanMjpegServer = class extends import_node_events8.EventEmitter {
|
|
|
33527
34262
|
};
|
|
33528
34263
|
|
|
33529
34264
|
// src/baichuan/stream/BaichuanWebRTCServer.ts
|
|
33530
|
-
var
|
|
34265
|
+
var import_node_events10 = require("events");
|
|
33531
34266
|
init_BcMediaAnnexBDecoder();
|
|
33532
34267
|
init_H264Converter();
|
|
33533
34268
|
function parseAnnexBNalUnits(annexB) {
|
|
@@ -33564,7 +34299,7 @@ function getH264NalType(nalUnit) {
|
|
|
33564
34299
|
function getH265NalType2(nalUnit) {
|
|
33565
34300
|
return nalUnit[0] >> 1 & 63;
|
|
33566
34301
|
}
|
|
33567
|
-
var BaichuanWebRTCServer = class extends
|
|
34302
|
+
var BaichuanWebRTCServer = class extends import_node_events10.EventEmitter {
|
|
33568
34303
|
options;
|
|
33569
34304
|
sessions = /* @__PURE__ */ new Map();
|
|
33570
34305
|
sessionIdCounter = 0;
|
|
@@ -34466,7 +35201,7 @@ Error: ${err}`
|
|
|
34466
35201
|
};
|
|
34467
35202
|
|
|
34468
35203
|
// src/baichuan/stream/BaichuanHlsServer.ts
|
|
34469
|
-
var
|
|
35204
|
+
var import_node_events11 = require("events");
|
|
34470
35205
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
34471
35206
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
34472
35207
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
@@ -34546,7 +35281,7 @@ function getNalTypes(codec, annexB) {
|
|
|
34546
35281
|
}
|
|
34547
35282
|
});
|
|
34548
35283
|
}
|
|
34549
|
-
var BaichuanHlsServer = class extends
|
|
35284
|
+
var BaichuanHlsServer = class extends import_node_events11.EventEmitter {
|
|
34550
35285
|
api;
|
|
34551
35286
|
channel;
|
|
34552
35287
|
profile;
|
|
@@ -35547,10 +36282,10 @@ async function autoDetectDeviceType(inputs) {
|
|
|
35547
36282
|
}
|
|
35548
36283
|
|
|
35549
36284
|
// src/multifocal/compositeRtspServer.ts
|
|
35550
|
-
var
|
|
36285
|
+
var import_node_events12 = require("events");
|
|
35551
36286
|
var import_node_child_process12 = require("child_process");
|
|
35552
|
-
var
|
|
35553
|
-
var CompositeRtspServer = class extends
|
|
36287
|
+
var net5 = __toESM(require("net"), 1);
|
|
36288
|
+
var CompositeRtspServer = class extends import_node_events12.EventEmitter {
|
|
35554
36289
|
options;
|
|
35555
36290
|
compositeStream = null;
|
|
35556
36291
|
rtspServer = null;
|
|
@@ -35616,7 +36351,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
35616
36351
|
const width = widerStreamInfo?.width ?? 1920;
|
|
35617
36352
|
const height = widerStreamInfo?.height ?? 1080;
|
|
35618
36353
|
const fps = widerStreamInfo?.frameRate ?? 25;
|
|
35619
|
-
this.rtspServer =
|
|
36354
|
+
this.rtspServer = net5.createServer((socket) => {
|
|
35620
36355
|
this.handleRtspConnection(socket);
|
|
35621
36356
|
});
|
|
35622
36357
|
await new Promise((resolve, reject) => {
|
|
@@ -35892,6 +36627,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
35892
36627
|
DUAL_LENS_DUAL_MOTION_MODELS,
|
|
35893
36628
|
DUAL_LENS_MODELS,
|
|
35894
36629
|
DUAL_LENS_SINGLE_MOTION_MODELS,
|
|
36630
|
+
Go2rtcTcpServer,
|
|
35895
36631
|
H264RtpDepacketizer,
|
|
35896
36632
|
H265RtpDepacketizer,
|
|
35897
36633
|
HlsSessionManager,
|
|
@@ -35933,6 +36669,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
35933
36669
|
buildSirenTimesXml,
|
|
35934
36670
|
buildStartZoomFocusXml,
|
|
35935
36671
|
buildWhiteLedStateXml,
|
|
36672
|
+
captureModelFixtures,
|
|
35936
36673
|
collectCgiDiagnostics,
|
|
35937
36674
|
collectMultifocalDiagnostics,
|
|
35938
36675
|
collectNativeDiagnostics,
|
|
@@ -36014,6 +36751,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
|
|
|
36014
36751
|
splitH265AnnexBToNalPayloads,
|
|
36015
36752
|
testChannelStreams,
|
|
36016
36753
|
xmlEscape,
|
|
36754
|
+
xmlIndicatesFloodlight,
|
|
36017
36755
|
zipDirectory
|
|
36018
36756
|
});
|
|
36019
36757
|
//# sourceMappingURL=index.cjs.map
|