@apocaliss92/nodelink-js 0.2.5 → 0.3.4

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
@@ -7511,7 +7511,11 @@ __export(index_exports, {
7511
7511
  detectIosClient: () => detectIosClient,
7512
7512
  detectVideoCodecFromNal: () => detectVideoCodecFromNal,
7513
7513
  discoverReolinkDevices: () => discoverReolinkDevices,
7514
+ discoverViaArpTable: () => discoverViaArpTable,
7515
+ discoverViaDhcpListener: () => discoverViaDhcpListener,
7514
7516
  discoverViaHttpScan: () => discoverViaHttpScan,
7517
+ discoverViaOnvif: () => discoverViaOnvif,
7518
+ discoverViaTcpPortScan: () => discoverViaTcpPortScan,
7515
7519
  discoverViaUdpBroadcast: () => discoverViaUdpBroadcast,
7516
7520
  discoverViaUdpDirect: () => discoverViaUdpDirect,
7517
7521
  encodeHeader: () => encodeHeader,
@@ -13379,14 +13383,16 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
13379
13383
  `;
13380
13384
  }
13381
13385
  if (body) {
13382
- response += `Content-Length: ${Buffer.byteLength(body, "utf8")}\r
13386
+ const bodyBuf = Buffer.from(body, "utf8");
13387
+ response += `Content-Length: ${bodyBuf.length}\r
13383
13388
  `;
13389
+ response += "\r\n";
13390
+ socket.write(response);
13391
+ socket.write(bodyBuf);
13392
+ } else {
13393
+ response += "\r\n";
13394
+ socket.write(response);
13384
13395
  }
13385
- response += "\r\n";
13386
- if (body) {
13387
- response += body;
13388
- }
13389
- socket.write(response);
13390
13396
  };
13391
13397
  this.rtspDebugLog(`RTSP ${method} ${url}`);
13392
13398
  if (this.requireAuth) {
@@ -13596,10 +13602,25 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
13596
13602
  );
13597
13603
  }
13598
13604
  }
13599
- sendResponse(200, "OK", {
13600
- Session: sessionId,
13601
- Range: "npt=0.000-"
13602
- });
13605
+ {
13606
+ const baseUrl = `rtsp://${this.listenHost}:${this.listenPort}${this.path}`;
13607
+ const resources = this.clientResources.get(clientId);
13608
+ const rtpInfoParts = [];
13609
+ if (resources?.setupTrack0) {
13610
+ rtpInfoParts.push(`url=${baseUrl}/track0`);
13611
+ }
13612
+ if (resources?.setupTrack1) {
13613
+ rtpInfoParts.push(`url=${baseUrl}/track1`);
13614
+ }
13615
+ const playHeaders = {
13616
+ Session: sessionId,
13617
+ Range: "npt=now-"
13618
+ };
13619
+ if (rtpInfoParts.length > 0) {
13620
+ playHeaders["RTP-Info"] = rtpInfoParts.join(",");
13621
+ }
13622
+ sendResponse(200, "OK", playHeaders);
13623
+ }
13603
13624
  } else if (method === "TEARDOWN") {
13604
13625
  this.logger.info(
13605
13626
  `[rebroadcast] TEARDOWN client=${clientId} session=${sessionId}`
@@ -13629,6 +13650,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
13629
13650
  sdp += `c=IN IP4 ${this.listenHost}\r
13630
13651
  `;
13631
13652
  sdp += "t=0 0\r\n";
13653
+ sdp += "a=range:npt=now-\r\n";
13654
+ sdp += "a=control:*\r\n";
13632
13655
  sdp += `m=video 0 RTP/AVP ${videoPayloadType}\r
13633
13656
  `;
13634
13657
  sdp += `a=rtpmap:${videoPayloadType} ${codec}/90000\r
@@ -14571,7 +14594,14 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
14571
14594
  this.firstFramePromise = null;
14572
14595
  this.firstFrameResolve = null;
14573
14596
  this.nativeFanout = null;
14574
- this.prebuffer = [];
14597
+ for (const [, resources] of this.clientResources) {
14598
+ const res = resources;
14599
+ res.rtpVideoBaseMicroseconds = void 0;
14600
+ res.rtpVideoBaseTimestamp = void 0;
14601
+ res.rtpVideoLastTimestamp = void 0;
14602
+ res.seenFirstVideoKeyframe = false;
14603
+ res.rtpSentVideoConfig = false;
14604
+ }
14575
14605
  if (this.dedicatedSessionRelease) {
14576
14606
  const release = this.dedicatedSessionRelease;
14577
14607
  this.dedicatedSessionRelease = void 0;
@@ -22571,13 +22601,13 @@ ${stderr}`)
22571
22601
  */
22572
22602
  async muxToMp4(params) {
22573
22603
  const { spawn: spawn12 } = await import("child_process");
22574
- const { randomUUID: randomUUID2 } = await import("crypto");
22604
+ const { randomUUID: randomUUID3 } = await import("crypto");
22575
22605
  const fs6 = await import("fs/promises");
22576
22606
  const os2 = await import("os");
22577
22607
  const path6 = await import("path");
22578
22608
  const ffmpeg = params.ffmpegPath ?? "ffmpeg";
22579
22609
  const tmpDir = os2.tmpdir();
22580
- const id = randomUUID2();
22610
+ const id = randomUUID3();
22581
22611
  const videoFormat = params.videoCodec === "H265" ? "hevc" : "h264";
22582
22612
  const videoPath = path6.join(tmpDir, `reolink-${id}.${videoFormat}`);
22583
22613
  const outputPath = path6.join(tmpDir, `reolink-${id}.mp4`);
@@ -27918,9 +27948,14 @@ function buildHlsRedirectUrl(originalUrl) {
27918
27948
  }
27919
27949
 
27920
27950
  // src/reolink/discovery.ts
27951
+ var import_node_child_process4 = require("child_process");
27952
+ var import_node_crypto3 = require("crypto");
27921
27953
  var import_node_dgram2 = __toESM(require("dgram"), 1);
27954
+ var net3 = __toESM(require("net"), 1);
27922
27955
  var import_node_os2 = require("os");
27956
+ var import_node_util = require("util");
27923
27957
  init_ReolinkCgiApi();
27958
+ var execFileAsync = (0, import_node_util.promisify)(import_node_child_process4.execFile);
27924
27959
  async function discoverViaUdpDirect(host, options) {
27925
27960
  if (!options.enableUdpDiscovery) return [];
27926
27961
  const logger = options.logger;
@@ -28282,6 +28317,348 @@ async function discoverViaUdpBroadcast(options) {
28282
28317
  });
28283
28318
  });
28284
28319
  }
28320
+ var REOLINK_MAC_PREFIXES = [
28321
+ "EC:71:DB",
28322
+ // Most common Reolink OUI
28323
+ "2C:1B:3A",
28324
+ // WiFi cameras (E1 Zoom, etc.)
28325
+ "18:2C:65",
28326
+ // Battery cameras (Video Doorbell, Argus, etc.)
28327
+ "DC:E5:37",
28328
+ // Some newer models
28329
+ "9C:8E:CD",
28330
+ // Some WiFi models
28331
+ "B4:4B:D6",
28332
+ // Some models
28333
+ "E4:3D:1A"
28334
+ // Some models
28335
+ ];
28336
+ async function discoverViaArpTable(options) {
28337
+ if (!options.enableArpLookup) return [];
28338
+ const logger = options.logger;
28339
+ logger?.log?.("[Discovery] Starting ARP table lookup for Reolink MAC prefix...");
28340
+ const discovered = [];
28341
+ try {
28342
+ let entries = [];
28343
+ if ((0, import_node_os2.platform)() === "linux") {
28344
+ try {
28345
+ const { readFile } = await import("fs/promises");
28346
+ const content = await readFile("/proc/net/arp", "utf8");
28347
+ for (const line of content.split("\n").slice(1)) {
28348
+ const parts = line.trim().split(/\s+/);
28349
+ if (parts.length >= 4 && parts[0] && parts[3] && parts[3] !== "00:00:00:00:00:00") {
28350
+ entries.push({ ip: parts[0], mac: parts[3].toUpperCase() });
28351
+ }
28352
+ }
28353
+ } catch {
28354
+ const { stdout } = await runArpCommand();
28355
+ entries = parseArpOutput(stdout);
28356
+ }
28357
+ } else {
28358
+ const { stdout } = await runArpCommand();
28359
+ entries = parseArpOutput(stdout);
28360
+ }
28361
+ logger?.log?.(`[Discovery] ARP table has ${entries.length} entries`);
28362
+ for (const { ip, mac } of entries) {
28363
+ const isReolink = REOLINK_MAC_PREFIXES.some(
28364
+ (prefix) => mac.startsWith(prefix)
28365
+ );
28366
+ if (isReolink) {
28367
+ logger?.log?.(`[Discovery] Found Reolink device via ARP: ${ip} (MAC: ${mac})`);
28368
+ discovered.push({
28369
+ host: ip,
28370
+ discoveryMethod: "arp"
28371
+ });
28372
+ }
28373
+ }
28374
+ } catch (err) {
28375
+ const msg = err instanceof Error ? err.message : String(err);
28376
+ logger?.warn?.(`[Discovery] ARP table lookup failed: ${msg}`);
28377
+ }
28378
+ logger?.log?.(`[Discovery] ARP lookup complete. Found ${discovered.length} device(s).`);
28379
+ return discovered;
28380
+ }
28381
+ async function runArpCommand() {
28382
+ const paths = ["/usr/sbin/arp", "/sbin/arp", "/usr/bin/arp", "arp"];
28383
+ for (const arpPath of paths) {
28384
+ try {
28385
+ return await execFileAsync(arpPath, ["-an"], { timeout: 5e3 });
28386
+ } catch {
28387
+ }
28388
+ }
28389
+ throw new Error("arp command not found");
28390
+ }
28391
+ function parseArpOutput(stdout) {
28392
+ const results = [];
28393
+ for (const line of stdout.split("\n")) {
28394
+ const match = /\((\d+\.\d+\.\d+\.\d+)\)\s+at\s+([0-9a-fA-F:]+)/i.exec(line);
28395
+ if (match && match[1] && match[2] && match[2] !== "(incomplete)") {
28396
+ results.push({ ip: match[1], mac: match[2].toUpperCase() });
28397
+ }
28398
+ }
28399
+ return results;
28400
+ }
28401
+ async function discoverViaDhcpListener(options) {
28402
+ if (!options.enableDhcpListener) return [];
28403
+ const logger = options.logger;
28404
+ const timeoutMs = options.dhcpListenerTimeoutMs ?? 1e4;
28405
+ logger?.log?.(`[Discovery] Starting passive DHCP listener (${timeoutMs}ms)...`);
28406
+ const discovered = /* @__PURE__ */ new Map();
28407
+ return new Promise((resolve) => {
28408
+ let socket;
28409
+ let timeout;
28410
+ try {
28411
+ socket = import_node_dgram2.default.createSocket({ type: "udp4", reuseAddr: true });
28412
+ } catch (err) {
28413
+ logger?.warn?.(`[Discovery] DHCP: failed to create socket: ${err instanceof Error ? err.message : String(err)}`);
28414
+ resolve([]);
28415
+ return;
28416
+ }
28417
+ socket.on("message", (msg) => {
28418
+ try {
28419
+ if (msg.length < 240) return;
28420
+ const op = msg[0];
28421
+ const hlen = msg[2];
28422
+ if (hlen !== 6) return;
28423
+ const mac = [
28424
+ msg[28]?.toString(16).padStart(2, "0"),
28425
+ msg[29]?.toString(16).padStart(2, "0"),
28426
+ msg[30]?.toString(16).padStart(2, "0"),
28427
+ msg[31]?.toString(16).padStart(2, "0"),
28428
+ msg[32]?.toString(16).padStart(2, "0"),
28429
+ msg[33]?.toString(16).padStart(2, "0")
28430
+ ].join(":").toUpperCase();
28431
+ const isReolinkMac = REOLINK_MAC_PREFIXES.some((p) => mac.startsWith(p));
28432
+ let hostname = "";
28433
+ let i = 240;
28434
+ while (i < msg.length - 1) {
28435
+ const optType = msg[i];
28436
+ if (optType === 255) break;
28437
+ if (optType === 0) {
28438
+ i++;
28439
+ continue;
28440
+ }
28441
+ const optLen = msg[i + 1] ?? 0;
28442
+ if (optType === 12 && optLen > 0) {
28443
+ hostname = msg.subarray(i + 2, i + 2 + optLen).toString("ascii").toLowerCase();
28444
+ }
28445
+ i += 2 + optLen;
28446
+ }
28447
+ const isReolinkHostname = hostname.startsWith("reolink");
28448
+ if (!isReolinkMac && !isReolinkHostname) return;
28449
+ const yiaddr = `${msg[16]}.${msg[17]}.${msg[18]}.${msg[19]}`;
28450
+ const ciaddr = `${msg[12]}.${msg[13]}.${msg[14]}.${msg[15]}`;
28451
+ const ip = yiaddr !== "0.0.0.0" ? yiaddr : ciaddr;
28452
+ if (ip === "0.0.0.0" || !ip) return;
28453
+ if (!discovered.has(ip)) {
28454
+ logger?.log?.(`[Discovery] DHCP: found Reolink device ${ip} (MAC: ${mac}, hostname: ${hostname || "n/a"}, op: ${op === 1 ? "request" : "reply"})`);
28455
+ const device = {
28456
+ host: ip,
28457
+ discoveryMethod: "dhcp"
28458
+ };
28459
+ if (hostname) device.name = hostname;
28460
+ discovered.set(ip, device);
28461
+ }
28462
+ } catch {
28463
+ }
28464
+ });
28465
+ socket.on("error", (err) => {
28466
+ logger?.warn?.(`[Discovery] DHCP socket error: ${err.message}`);
28467
+ clearTimeout(timeout);
28468
+ socket.close();
28469
+ resolve(Array.from(discovered.values()));
28470
+ });
28471
+ socket.bind(67, "0.0.0.0", () => {
28472
+ logger?.log?.("[Discovery] DHCP listener bound on port 67");
28473
+ timeout = setTimeout(() => {
28474
+ socket.close();
28475
+ logger?.log?.(`[Discovery] DHCP listener complete. Found ${discovered.size} device(s).`);
28476
+ resolve(Array.from(discovered.values()));
28477
+ }, timeoutMs);
28478
+ });
28479
+ });
28480
+ }
28481
+ function probeTcpPort(ip, port, timeoutMs) {
28482
+ return new Promise((resolve) => {
28483
+ const socket = new net3.Socket();
28484
+ let settled = false;
28485
+ const done = (result) => {
28486
+ if (settled) return;
28487
+ settled = true;
28488
+ socket.destroy();
28489
+ resolve(result);
28490
+ };
28491
+ socket.setTimeout(timeoutMs);
28492
+ socket.on("connect", () => done(true));
28493
+ socket.on("timeout", () => done(false));
28494
+ socket.on("error", () => done(false));
28495
+ socket.connect(port, ip);
28496
+ });
28497
+ }
28498
+ async function discoverViaTcpPortScan(options) {
28499
+ if (!options.enableTcpPortScan) return [];
28500
+ const logger = options.logger;
28501
+ const networkCidr = options.networkCidr ?? getLocalNetworks()[0];
28502
+ const timeoutMs = options.tcpProbeTimeoutMs ?? 1500;
28503
+ const maxConcurrent = options.maxConcurrentProbes ?? 80;
28504
+ if (!networkCidr) {
28505
+ logger?.warn?.("[Discovery] No network CIDR available for TCP port scan");
28506
+ return [];
28507
+ }
28508
+ logger?.log?.(`[Discovery] Starting TCP port 9000 scan on network ${networkCidr}...`);
28509
+ const ipRange = parseCidr(networkCidr);
28510
+ if (!ipRange) {
28511
+ logger?.warn?.(`[Discovery] Invalid CIDR: ${networkCidr}`);
28512
+ return [];
28513
+ }
28514
+ const discovered = [];
28515
+ const ipAddresses = [];
28516
+ for (let ipNum = ipRange.start; ipNum <= ipRange.end && ipNum <= ipRange.start + 254; ipNum++) {
28517
+ ipAddresses.push(ipNumberToString(ipNum));
28518
+ }
28519
+ logger?.log?.(`[Discovery] Scanning ${ipAddresses.length} IPs on port 9000...`);
28520
+ for (let i = 0; i < ipAddresses.length; i += maxConcurrent) {
28521
+ const batch = ipAddresses.slice(i, i + maxConcurrent);
28522
+ const batchResults = await Promise.allSettled(
28523
+ batch.map(async (ip) => {
28524
+ const open = await probeTcpPort(ip, 9e3, timeoutMs);
28525
+ if (open) {
28526
+ logger?.log?.(`[Discovery] Found Baichuan device at ${ip}:9000`);
28527
+ return { host: ip, discoveryMethod: "tcp_port_scan" };
28528
+ }
28529
+ return null;
28530
+ })
28531
+ );
28532
+ for (const result of batchResults) {
28533
+ if (result.status === "fulfilled" && result.value) {
28534
+ discovered.push(result.value);
28535
+ }
28536
+ }
28537
+ }
28538
+ logger?.log?.(`[Discovery] TCP port scan complete. Found ${discovered.length} device(s).`);
28539
+ return discovered;
28540
+ }
28541
+ async function discoverViaOnvif(options) {
28542
+ if (!options.enableOnvifDiscovery) return [];
28543
+ const logger = options.logger;
28544
+ const timeoutMs = options.onvifDiscoveryTimeoutMs ?? 5e3;
28545
+ logger?.log?.(`[Discovery] Starting ONVIF WS-Discovery (${timeoutMs}ms)...`);
28546
+ const discovered = /* @__PURE__ */ new Map();
28547
+ const MULTICAST_ADDR = "239.255.255.250";
28548
+ const MULTICAST_PORT = 3702;
28549
+ const messageId = `uuid:${(0, import_node_crypto3.randomUUID)()}`;
28550
+ const probeMessage = [
28551
+ '<?xml version="1.0" encoding="UTF-8"?>',
28552
+ '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope"',
28553
+ ' xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing"',
28554
+ ' xmlns:d="http://schemas.xmlsoap.org/ws/2005/04/discovery"',
28555
+ ' xmlns:dn="http://www.onvif.org/ver10/network/wsdl">',
28556
+ " <s:Header>",
28557
+ ` <a:MessageID>${messageId}</a:MessageID>`,
28558
+ " <a:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>",
28559
+ " <a:Action>http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>",
28560
+ " </s:Header>",
28561
+ " <s:Body>",
28562
+ " <d:Probe>",
28563
+ " <d:Types>dn:NetworkVideoTransmitter</d:Types>",
28564
+ " </d:Probe>",
28565
+ " </s:Body>",
28566
+ "</s:Envelope>"
28567
+ ].join("\n");
28568
+ return new Promise((resolve) => {
28569
+ const socket = import_node_dgram2.default.createSocket({ type: "udp4", reuseAddr: true });
28570
+ let timeout;
28571
+ socket.on("message", (msg, rinfo) => {
28572
+ try {
28573
+ const xml = msg.toString("utf8");
28574
+ const xaddrsMatch = /<[^:]*:?XAddrs>([^<]+)<\/[^:]*:?XAddrs>/i.exec(xml);
28575
+ const scopesMatch = /<[^:]*:?Scopes>([^<]+)<\/[^:]*:?Scopes>/i.exec(xml);
28576
+ let host = rinfo.address;
28577
+ let httpPort;
28578
+ if (xaddrsMatch?.[1]) {
28579
+ const urls = xaddrsMatch[1].trim().split(/\s+/);
28580
+ for (const url of urls) {
28581
+ try {
28582
+ const parsed = new URL(url);
28583
+ if (parsed.hostname) {
28584
+ host = parsed.hostname;
28585
+ const p = Number.parseInt(parsed.port, 10);
28586
+ if (p && p !== 80) httpPort = p;
28587
+ break;
28588
+ }
28589
+ } catch {
28590
+ }
28591
+ }
28592
+ }
28593
+ if (discovered.has(host)) return;
28594
+ let model;
28595
+ let name;
28596
+ let manufacturer;
28597
+ if (scopesMatch?.[1]) {
28598
+ const scopes = scopesMatch[1].trim().split(/\s+/);
28599
+ for (const scope of scopes) {
28600
+ const hwMatch = /\/hardware\/(.+)$/i.exec(scope);
28601
+ if (hwMatch?.[1]) model = decodeURIComponent(hwMatch[1]);
28602
+ const nameMatch = /\/name\/(.+)$/i.exec(scope);
28603
+ if (nameMatch?.[1]) name = decodeURIComponent(nameMatch[1]);
28604
+ const mfgMatch = /\/manufacturer\/(.+)$/i.exec(scope);
28605
+ if (mfgMatch?.[1]) manufacturer = decodeURIComponent(mfgMatch[1]);
28606
+ }
28607
+ }
28608
+ const allText = `${manufacturer ?? ""} ${model ?? ""} ${xaddrsMatch?.[1] ?? ""}`.toLowerCase();
28609
+ const hasReolinkText = allText.includes("reolink");
28610
+ const hasReolinkModel = /^(rlc|rln|rl[ncb]|e1|cw|cx|duo|trackmix|argus|lumus|go|video doorbell|reolink)/i.test(model ?? "");
28611
+ const isReolink = hasReolinkText || hasReolinkModel;
28612
+ if (!isReolink) {
28613
+ logger?.debug?.(`[Discovery] ONVIF: skipping non-Reolink device at ${host} (${model ?? "unknown"}, manufacturer: ${manufacturer ?? "unknown"})`);
28614
+ return;
28615
+ }
28616
+ logger?.log?.(`[Discovery] ONVIF: found Reolink device at ${host}${model ? ` (${model})` : ""}${name ? ` name="${name}"` : ""}`);
28617
+ const device = {
28618
+ host,
28619
+ discoveryMethod: "onvif"
28620
+ };
28621
+ if (model) device.model = model;
28622
+ if (name && name !== "IPC") {
28623
+ device.name = name;
28624
+ } else if (model) {
28625
+ device.name = model;
28626
+ }
28627
+ if (httpPort) device.httpPort = httpPort;
28628
+ discovered.set(host, device);
28629
+ } catch {
28630
+ }
28631
+ });
28632
+ socket.on("error", (err) => {
28633
+ logger?.warn?.(`[Discovery] ONVIF socket error: ${err.message}`);
28634
+ });
28635
+ socket.bind(0, "0.0.0.0", () => {
28636
+ const buf = Buffer.from(probeMessage, "utf8");
28637
+ socket.send(buf, 0, buf.length, MULTICAST_PORT, MULTICAST_ADDR, (err) => {
28638
+ if (err) {
28639
+ logger?.warn?.(`[Discovery] ONVIF: failed to send probe: ${err.message}`);
28640
+ }
28641
+ });
28642
+ setTimeout(() => {
28643
+ try {
28644
+ socket.send(buf, 0, buf.length, MULTICAST_PORT, MULTICAST_ADDR);
28645
+ } catch {
28646
+ }
28647
+ }, 500);
28648
+ timeout = setTimeout(() => {
28649
+ try {
28650
+ socket.close();
28651
+ } catch {
28652
+ }
28653
+ logger?.log?.(`[Discovery] ONVIF WS-Discovery complete. Found ${discovered.size} device(s).`);
28654
+ resolve(Array.from(discovered.values()));
28655
+ }, timeoutMs);
28656
+ });
28657
+ socket.on("close", () => {
28658
+ if (timeout) clearTimeout(timeout);
28659
+ });
28660
+ });
28661
+ }
28285
28662
  async function discoverReolinkDevices(options = {}) {
28286
28663
  const logger = options.logger;
28287
28664
  logger?.log?.("[Discovery] Starting Reolink device discovery...");
@@ -28304,10 +28681,26 @@ async function discoverReolinkDevices(options = {}) {
28304
28681
  results.push(seenDevices.get(key));
28305
28682
  }
28306
28683
  };
28307
- const [httpDevices, udpDevices] = await Promise.all([
28684
+ const [httpDevices, udpDevices, tcpDevices, arpDevices, dhcpDevices, onvifDevices] = await Promise.all([
28308
28685
  discoverViaHttpScan(options),
28309
- discoverViaUdpBroadcast(options)
28686
+ discoverViaUdpBroadcast(options),
28687
+ discoverViaTcpPortScan(options),
28688
+ discoverViaArpTable(options),
28689
+ discoverViaDhcpListener(options),
28690
+ discoverViaOnvif(options)
28310
28691
  ]);
28692
+ for (const device of dhcpDevices) {
28693
+ mergeDevice(device);
28694
+ }
28695
+ for (const device of arpDevices) {
28696
+ mergeDevice(device);
28697
+ }
28698
+ for (const device of tcpDevices) {
28699
+ mergeDevice(device);
28700
+ }
28701
+ for (const device of onvifDevices) {
28702
+ mergeDevice(device);
28703
+ }
28311
28704
  for (const device of httpDevices) {
28312
28705
  mergeDevice(device);
28313
28706
  }
@@ -28325,51 +28718,18 @@ var AutodiscoveryClient = class {
28325
28718
  scanTimer = null;
28326
28719
  isRunning = false;
28327
28720
  currentScanPromise = null;
28328
- /**
28329
- * Costruttore del client di autodiscovery.
28330
- *
28331
- * @param options - Opzioni di configurazione per il discovery
28332
- */
28333
28721
  constructor(options = {}) {
28334
28722
  this.options = {
28335
- scanIntervalMs: options.scanIntervalMs ?? 6e4,
28336
- // Default: 60 secondi
28723
+ ...options,
28724
+ scanIntervalMs: options.scanIntervalMs ?? 12e4,
28337
28725
  autoStart: options.autoStart ?? false
28338
28726
  };
28339
- if (options.networkCidr !== void 0) {
28340
- this.options.networkCidr = options.networkCidr;
28341
- }
28342
- if (options.username !== void 0) {
28343
- this.options.username = options.username;
28344
- }
28345
- if (options.password !== void 0) {
28346
- this.options.password = options.password;
28347
- }
28348
- if (options.httpProbeTimeoutMs !== void 0) {
28349
- this.options.httpProbeTimeoutMs = options.httpProbeTimeoutMs;
28350
- }
28351
- if (options.maxConcurrentProbes !== void 0) {
28352
- this.options.maxConcurrentProbes = options.maxConcurrentProbes;
28353
- }
28354
- if (options.logger !== void 0) {
28355
- this.options.logger = options.logger;
28356
- }
28357
- if (options.httpPorts !== void 0) {
28358
- this.options.httpPorts = options.httpPorts;
28359
- }
28360
- if (options.discoveryMethod !== void 0) {
28361
- this.options.discoveryMethod = options.discoveryMethod;
28362
- }
28363
- if (options.udpBroadcastTimeoutMs !== void 0) {
28364
- this.options.udpBroadcastTimeoutMs = options.udpBroadcastTimeoutMs;
28365
- }
28366
28727
  if (this.options.autoStart) {
28367
28728
  this.start();
28368
28729
  }
28369
28730
  }
28370
28731
  /**
28371
- * Avvia il discovery continuato.
28372
- * Se già in esecuzione, non fa nulla.
28732
+ * Start continuous discovery. If already running, does nothing.
28373
28733
  */
28374
28734
  start() {
28375
28735
  if (this.isRunning) {
@@ -28384,8 +28744,7 @@ var AutodiscoveryClient = class {
28384
28744
  this.scheduleNextScan();
28385
28745
  }
28386
28746
  /**
28387
- * Ferma il discovery continuato.
28388
- * Se non è in esecuzione, non fa nulla.
28747
+ * Stop continuous discovery. If not running, does nothing.
28389
28748
  */
28390
28749
  stop() {
28391
28750
  if (!this.isRunning) {
@@ -28400,50 +28759,41 @@ var AutodiscoveryClient = class {
28400
28759
  this.options.logger?.log?.("[Autodiscovery] Discovery stopped");
28401
28760
  }
28402
28761
  /**
28403
- * Restituisce la lista corrente delle telecamere discoverate.
28404
- *
28405
- * @returns Array di dispositivi discoverati, ordinati per host
28762
+ * Returns the current list of discovered devices, sorted by host IP.
28406
28763
  */
28407
28764
  getDiscoveredDevices() {
28408
- return Array.from(this.discoveredDevices.values()).sort((a, b) => {
28409
- return a.host.localeCompare(b.host);
28410
- });
28765
+ return Array.from(this.discoveredDevices.values()).sort(
28766
+ (a, b) => a.host.localeCompare(b.host)
28767
+ );
28411
28768
  }
28412
28769
  /**
28413
- * Restituisce il numero di telecamere attualmente discoverate.
28414
- *
28415
- * @returns Numero di dispositivi discoverati
28770
+ * Returns the number of currently discovered devices.
28416
28771
  */
28417
28772
  getDeviceCount() {
28418
28773
  return this.discoveredDevices.size;
28419
28774
  }
28420
28775
  /**
28421
- * Verifica se il discovery è attualmente in esecuzione.
28422
- *
28423
- * @returns `true` se il discovery è in esecuzione, `false` altrimenti
28776
+ * Returns whether continuous discovery is currently running.
28424
28777
  */
28425
28778
  isActive() {
28426
28779
  return this.isRunning;
28427
28780
  }
28428
28781
  /**
28429
- * Forza un scan immediato (non aspetta l'intervallo programmato).
28430
- * Se uno scan è già in corso, attende il completamento prima di avviarne uno nuovo.
28431
- *
28432
- * @returns Promise che si risolve quando lo scan è completato
28782
+ * Force an immediate scan (doesn't wait for the scheduled interval).
28783
+ * If a scan is already in progress, waits for it to complete.
28433
28784
  */
28434
28785
  async scanNow() {
28435
28786
  if (this.currentScanPromise) {
28436
- this.options.logger?.log?.("[Autodiscovery] Scan already in progress, waiting for completion...");
28787
+ this.options.logger?.log?.(
28788
+ "[Autodiscovery] Scan already in progress, waiting for completion..."
28789
+ );
28437
28790
  await this.currentScanPromise;
28438
28791
  return;
28439
28792
  }
28440
28793
  await this.performScan();
28441
28794
  }
28442
28795
  /**
28443
- * Rimuove un dispositivo dalla lista (utile se si sa che non è più disponibile).
28444
- *
28445
- * @param host - Indirizzo IP del dispositivo da rimuovere
28446
- * @returns `true` se il dispositivo è stato rimosso, `false` se non era presente
28796
+ * Remove a device from the discovered list.
28447
28797
  */
28448
28798
  removeDevice(host) {
28449
28799
  const removed = this.discoveredDevices.delete(host);
@@ -28453,59 +28803,20 @@ var AutodiscoveryClient = class {
28453
28803
  return removed;
28454
28804
  }
28455
28805
  /**
28456
- * Pulisce tutte le telecamere discoverate dalla lista.
28806
+ * Clear all discovered devices.
28457
28807
  */
28458
28808
  clearDevices() {
28459
28809
  const count = this.discoveredDevices.size;
28460
28810
  this.discoveredDevices.clear();
28461
- this.options.logger?.log?.(`[Autodiscovery] Removed ${count} device(s) from list`);
28811
+ this.options.logger?.log?.(
28812
+ `[Autodiscovery] Removed ${count} device(s) from list`
28813
+ );
28462
28814
  }
28463
- /**
28464
- * Esegue un singolo scan della rete.
28465
- */
28466
28815
  async performScan() {
28467
28816
  const scanPromise = (async () => {
28468
28817
  try {
28469
28818
  this.options.logger?.log?.("[Autodiscovery] Starting scan...");
28470
- const discoveryMethod = this.options.discoveryMethod ?? "http";
28471
- const discoveryOptions = {
28472
- enableHttpScanning: discoveryMethod === "http" || discoveryMethod === "both",
28473
- enableUdpDiscovery: discoveryMethod === "udp" || discoveryMethod === "both"
28474
- };
28475
- if (this.options.networkCidr !== void 0) {
28476
- discoveryOptions.networkCidr = this.options.networkCidr;
28477
- }
28478
- if (this.options.username !== void 0) {
28479
- discoveryOptions.username = this.options.username;
28480
- }
28481
- if (this.options.password !== void 0) {
28482
- discoveryOptions.password = this.options.password;
28483
- }
28484
- if (this.options.httpProbeTimeoutMs !== void 0) {
28485
- discoveryOptions.httpProbeTimeoutMs = this.options.httpProbeTimeoutMs;
28486
- }
28487
- if (this.options.maxConcurrentProbes !== void 0) {
28488
- discoveryOptions.maxConcurrentProbes = this.options.maxConcurrentProbes;
28489
- }
28490
- if (this.options.logger !== void 0) {
28491
- discoveryOptions.logger = this.options.logger;
28492
- }
28493
- if (this.options.httpPorts !== void 0) {
28494
- discoveryOptions.httpPorts = this.options.httpPorts;
28495
- }
28496
- if (this.options.udpBroadcastTimeoutMs !== void 0) {
28497
- discoveryOptions.udpBroadcastTimeoutMs = this.options.udpBroadcastTimeoutMs;
28498
- }
28499
- let discovered = [];
28500
- if (discoveryMethod === "http" || discoveryMethod === "both") {
28501
- const httpDevices = await discoverViaHttpScan(discoveryOptions);
28502
- discovered.push(...httpDevices);
28503
- }
28504
- if (discoveryMethod === "udp" || discoveryMethod === "both") {
28505
- const udpDevices = await discoverViaUdpBroadcast(discoveryOptions);
28506
- discovered.push(...udpDevices);
28507
- }
28508
- const beforeCount = this.discoveredDevices.size;
28819
+ const discovered = await discoverReolinkDevices(this.options);
28509
28820
  const newDevices = [];
28510
28821
  const updatedDevices = [];
28511
28822
  for (const device of discovered) {
@@ -28520,38 +28831,35 @@ var AutodiscoveryClient = class {
28520
28831
  newDevices.push(device);
28521
28832
  }
28522
28833
  }
28523
- const afterCount = this.discoveredDevices.size;
28524
28834
  this.options.logger?.log?.(
28525
- `[Autodiscovery] Scan completed: ${newDevices.length} new, ${updatedDevices.length} updated, total: ${afterCount}`
28835
+ `[Autodiscovery] Scan completed: ${newDevices.length} new, ${updatedDevices.length} updated, total: ${this.discoveredDevices.size}`
28526
28836
  );
28527
28837
  for (const device of newDevices) {
28528
- const details = [];
28529
- if (device.model) details.push(`Model: ${device.model}`);
28530
- if (device.name) details.push(`Name: ${device.name}`);
28531
- if (device.uid) details.push(`UID: ${device.uid}`);
28532
- if (device.firmwareVersion) details.push(`Firmware: ${device.firmwareVersion}`);
28533
- if (device.httpPort) details.push(`HTTP Port: ${device.httpPort}`);
28534
- if (device.httpsPort) details.push(`HTTPS Port: ${device.httpsPort}`);
28535
- details.push(`Discovery Method: ${device.discoveryMethod}`);
28536
- if (device.supportsHttps !== void 0) details.push(`HTTPS: ${device.supportsHttps}`);
28537
- if (device.httpAccessible !== void 0) details.push(`HTTP Accessible: ${device.httpAccessible}`);
28538
28838
  this.options.logger?.log?.(
28539
- `[Autodiscovery] \u{1F195} NEW DEVICE DISCOVERED - Host: ${device.host}${details.length > 0 ? ` | ${details.join(" | ")}` : ""}`
28839
+ `[Autodiscovery] NEW DEVICE - ${device.host} | ${device.model ?? "unknown"} | ${device.name ?? ""} | via ${device.discoveryMethod}`
28540
28840
  );
28841
+ try {
28842
+ this.options.onDeviceDiscovered?.(device);
28843
+ } catch {
28844
+ }
28845
+ }
28846
+ for (const device of updatedDevices) {
28847
+ try {
28848
+ this.options.onDeviceUpdated?.(device);
28849
+ } catch {
28850
+ }
28541
28851
  }
28542
28852
  } catch (error) {
28543
28853
  const msg = error instanceof Error ? error.message : String(error);
28544
- this.options.logger?.error?.(`[Autodiscovery] Error during scan: ${msg}`);
28854
+ this.options.logger?.error?.(
28855
+ `[Autodiscovery] Error during scan: ${msg}`
28856
+ );
28545
28857
  }
28546
28858
  })();
28547
28859
  this.currentScanPromise = scanPromise;
28548
28860
  await scanPromise;
28549
28861
  this.currentScanPromise = null;
28550
28862
  }
28551
- /**
28552
- * Unisce le informazioni di un dispositivo esistente con quelle di un nuovo scan.
28553
- * Restituisce il dispositivo aggiornato se ci sono state modifiche, altrimenti `null`.
28554
- */
28555
28863
  mergeDeviceInfo(existing, updated) {
28556
28864
  let hasChanges = false;
28557
28865
  if (!existing.model && updated.model) {
@@ -28588,13 +28896,8 @@ var AutodiscoveryClient = class {
28588
28896
  }
28589
28897
  return hasChanges ? existing : null;
28590
28898
  }
28591
- /**
28592
- * Programma il prossimo scan.
28593
- */
28594
28899
  scheduleNextScan() {
28595
- if (!this.isRunning) {
28596
- return;
28597
- }
28900
+ if (!this.isRunning) return;
28598
28901
  this.scanTimer = setTimeout(() => {
28599
28902
  this.scanTimer = null;
28600
28903
  if (this.isRunning) {
@@ -28633,7 +28936,7 @@ function decideVideoclipTranscodeMode(headers, forceMode) {
28633
28936
  };
28634
28937
  }
28635
28938
  const ua = (clientInfo.userAgent ?? "").toLowerCase();
28636
- const platform = (clientInfo.secChUaPlatform ?? "").toLowerCase().replace(/"/g, "");
28939
+ const platform2 = (clientInfo.secChUaPlatform ?? "").toLowerCase().replace(/"/g, "");
28637
28940
  const isIos = /iphone|ipad|ipod/.test(ua);
28638
28941
  if (isIos) {
28639
28942
  return {
@@ -28650,7 +28953,7 @@ function decideVideoclipTranscodeMode(headers, forceMode) {
28650
28953
  clientInfo
28651
28954
  };
28652
28955
  }
28653
- const isAndroid = ua.includes("android") || platform === "android";
28956
+ const isAndroid = ua.includes("android") || platform2 === "android";
28654
28957
  if (isAndroid) {
28655
28958
  return {
28656
28959
  mode: "transcode-h264",
@@ -28659,7 +28962,7 @@ function decideVideoclipTranscodeMode(headers, forceMode) {
28659
28962
  };
28660
28963
  }
28661
28964
  const isChromium = ua.includes("chrome") || ua.includes("edg");
28662
- const isMac = ua.includes("mac os") || platform === "macos";
28965
+ const isMac = ua.includes("mac os") || platform2 === "macos";
28663
28966
  if (isChromium && !isMac) {
28664
28967
  return {
28665
28968
  mode: "transcode-h264",
@@ -28686,7 +28989,7 @@ init_recordingFileName();
28686
28989
 
28687
28990
  // src/reolink/baichuan/endpoints-server.ts
28688
28991
  var import_node_http = __toESM(require("http"), 1);
28689
- var import_node_child_process4 = require("child_process");
28992
+ var import_node_child_process5 = require("child_process");
28690
28993
  function parseIntParam(v, def) {
28691
28994
  if (v == null) return def;
28692
28995
  const n = Number.parseInt(v, 10);
@@ -28925,7 +29228,7 @@ function createBaichuanEndpointsServer(opts) {
28925
29228
  "Cache-Control": "no-cache",
28926
29229
  Connection: "close"
28927
29230
  });
28928
- const ff2 = (0, import_node_child_process4.spawn)("ffmpeg", [
29231
+ const ff2 = (0, import_node_child_process5.spawn)("ffmpeg", [
28929
29232
  "-hide_banner",
28930
29233
  "-loglevel",
28931
29234
  "error",
@@ -28958,7 +29261,7 @@ function createBaichuanEndpointsServer(opts) {
28958
29261
  );
28959
29262
  res.setHeader("Cache-Control", "no-cache");
28960
29263
  res.setHeader("Connection", "close");
28961
- const ff = (0, import_node_child_process4.spawn)("ffmpeg", [
29264
+ const ff = (0, import_node_child_process5.spawn)("ffmpeg", [
28962
29265
  "-hide_banner",
28963
29266
  "-loglevel",
28964
29267
  "error",
@@ -29069,7 +29372,7 @@ init_urls();
29069
29372
 
29070
29373
  // src/rtsp/server.ts
29071
29374
  var import_node_http2 = __toESM(require("http"), 1);
29072
- var import_node_child_process5 = require("child_process");
29375
+ var import_node_child_process6 = require("child_process");
29073
29376
  init_urls();
29074
29377
  function createRtspProxyServer(opts) {
29075
29378
  return import_node_http2.default.createServer((req, res) => {
@@ -29110,7 +29413,7 @@ function createRtspProxyServer(opts) {
29110
29413
  Connection: "close"
29111
29414
  });
29112
29415
  const rtspTransport = opts.rtspTransport ?? "tcp";
29113
- const ff = (0, import_node_child_process5.spawn)("ffmpeg", [
29416
+ const ff = (0, import_node_child_process6.spawn)("ffmpeg", [
29114
29417
  "-hide_banner",
29115
29418
  "-loglevel",
29116
29419
  "error",
@@ -29140,7 +29443,7 @@ function createRtspProxyServer(opts) {
29140
29443
  }
29141
29444
 
29142
29445
  // src/rfc/rfc4571.ts
29143
- var import_node_crypto3 = __toESM(require("crypto"), 1);
29446
+ var import_node_crypto4 = __toESM(require("crypto"), 1);
29144
29447
  function buildRfc4571Sdp(video, audio) {
29145
29448
  let out = "v=0\r\n";
29146
29449
  out += "o=- 0 0 IN IP4 0.0.0.0\r\n";
@@ -29413,12 +29716,12 @@ function parseAdtsHeader(adtsFrame) {
29413
29716
  var RtpWriter = class {
29414
29717
  constructor(payloadType) {
29415
29718
  this.payloadType = payloadType;
29416
- this.seq = import_node_crypto3.default.randomBytes(2).readUInt16BE(0);
29417
- this.timestamp = import_node_crypto3.default.randomBytes(4).readUInt32BE(0);
29719
+ this.seq = import_node_crypto4.default.randomBytes(2).readUInt16BE(0);
29720
+ this.timestamp = import_node_crypto4.default.randomBytes(4).readUInt32BE(0);
29418
29721
  }
29419
29722
  seq = 0;
29420
29723
  timestamp = 0;
29421
- ssrc = import_node_crypto3.default.randomBytes(4).readUInt32BE(0);
29724
+ ssrc = import_node_crypto4.default.randomBytes(4).readUInt32BE(0);
29422
29725
  setTimestamp(ts) {
29423
29726
  this.timestamp = ts >>> 0;
29424
29727
  }
@@ -29982,8 +30285,8 @@ var import_node_net2 = __toESM(require("net"), 1);
29982
30285
  init_BaichuanVideoStream();
29983
30286
 
29984
30287
  // src/multifocal/compositeStream.ts
29985
- var import_node_child_process6 = require("child_process");
29986
- var import_node_crypto4 = require("crypto");
30288
+ var import_node_child_process7 = require("child_process");
30289
+ var import_node_crypto5 = require("crypto");
29987
30290
  var import_node_events5 = require("events");
29988
30291
  function calculateOverlayPosition(position, mainWidth, mainHeight, pipWidth, pipHeight, margin) {
29989
30292
  const pipW = Math.floor(pipWidth);
@@ -30094,7 +30397,7 @@ var CompositeStream = class extends import_node_events5.EventEmitter {
30094
30397
  if (!buf?.length) return;
30095
30398
  const head = buf.subarray(0, Math.min(12, buf.length)).toString("hex");
30096
30399
  const slice = buf.subarray(0, Math.min(256, buf.length));
30097
- const sha1 = (0, import_node_crypto4.createHash)("sha1").update(slice).digest("hex");
30400
+ const sha1 = (0, import_node_crypto5.createHash)("sha1").update(slice).digest("hex");
30098
30401
  return { len: buf.length, headHex: head, sha1_256: sha1 };
30099
30402
  }
30100
30403
  async primeForFfmpeg(gen, timeoutMs, requireKeyframe) {
@@ -30323,7 +30626,7 @@ var CompositeStream = class extends import_node_events5.EventEmitter {
30323
30626
  "pipe:1"
30324
30627
  ];
30325
30628
  this.logger.log?.(`[CompositeStream] Starting ffmpeg (rtsp inputs): ${ffmpegArgs.join(" ")}`);
30326
- this.ffmpegProcess = (0, import_node_child_process6.spawn)("ffmpeg", ffmpegArgs, {
30629
+ this.ffmpegProcess = (0, import_node_child_process7.spawn)("ffmpeg", ffmpegArgs, {
30327
30630
  stdio: ["ignore", "pipe", "pipe"]
30328
30631
  });
30329
30632
  this.ffmpegProcess.on("error", (error) => {
@@ -30438,7 +30741,7 @@ var CompositeStream = class extends import_node_events5.EventEmitter {
30438
30741
  this.logger.log?.(
30439
30742
  `[CompositeStream] Starting ffmpeg: ${ffmpegArgs.join(" ")}`
30440
30743
  );
30441
- this.ffmpegProcess = (0, import_node_child_process6.spawn)("ffmpeg", ffmpegArgs, {
30744
+ this.ffmpegProcess = (0, import_node_child_process7.spawn)("ffmpeg", ffmpegArgs, {
30442
30745
  stdio: ["pipe", "pipe", "pipe", "pipe"]
30443
30746
  });
30444
30747
  this.ffmpegProcess.on("error", (error) => {
@@ -32167,7 +32470,7 @@ async function createRfc4571TcpServerForReplay(options) {
32167
32470
 
32168
32471
  // src/rfc/replay-http-server.ts
32169
32472
  var import_node_http3 = __toESM(require("http"), 1);
32170
- var import_node_child_process7 = require("child_process");
32473
+ var import_node_child_process8 = require("child_process");
32171
32474
  var import_node_stream2 = require("stream");
32172
32475
  async function createReplayHttpServer(options) {
32173
32476
  const {
@@ -32321,7 +32624,7 @@ async function createReplayHttpServer(options) {
32321
32624
  "pipe:1"
32322
32625
  ];
32323
32626
  log(`spawning ffmpeg: ${ffmpegPath} ${ffmpegArgs.join(" ")}`);
32324
- ffmpegProcess = (0, import_node_child_process7.spawn)(ffmpegPath, ffmpegArgs, {
32627
+ ffmpegProcess = (0, import_node_child_process8.spawn)(ffmpegPath, ffmpegArgs, {
32325
32628
  stdio: ["pipe", "pipe", "pipe"]
32326
32629
  });
32327
32630
  ffmpegProcess.stdout?.pipe(outputStream).pipe(res);
@@ -32423,7 +32726,7 @@ init_BaichuanVideoStream();
32423
32726
 
32424
32727
  // src/baichuan/stream/BaichuanHttpStreamServer.ts
32425
32728
  var import_node_events6 = require("events");
32426
- var import_node_child_process8 = require("child_process");
32729
+ var import_node_child_process9 = require("child_process");
32427
32730
  var http4 = __toESM(require("http"), 1);
32428
32731
  var NAL_START_CODE_4B4 = Buffer.from([0, 0, 0, 1]);
32429
32732
  var NAL_START_CODE_3B3 = Buffer.from([0, 0, 1]);
@@ -32533,7 +32836,7 @@ var BaichuanHttpStreamServer = class extends import_node_events6.EventEmitter {
32533
32836
  this.httpServer.on("error", reject);
32534
32837
  });
32535
32838
  this.logger.info(`[BaichuanHttpStreamServer] Starting ffmpeg for H.264 -> MPEG-TS conversion...`);
32536
- const ffmpeg = (0, import_node_child_process8.spawn)("ffmpeg", [
32839
+ const ffmpeg = (0, import_node_child_process9.spawn)("ffmpeg", [
32537
32840
  "-hide_banner",
32538
32841
  // ffmpeg warnings often include non-fatal decode messages (e.g. decode_slice_header),
32539
32842
  // which we don't want to treat as application errors.
@@ -32741,7 +33044,7 @@ var http5 = __toESM(require("http"), 1);
32741
33044
 
32742
33045
  // src/baichuan/stream/MjpegTransformer.ts
32743
33046
  var import_node_events7 = require("events");
32744
- var import_node_child_process9 = require("child_process");
33047
+ var import_node_child_process10 = require("child_process");
32745
33048
  var JPEG_SOI = Buffer.from([255, 216]);
32746
33049
  var JPEG_EOI = Buffer.from([255, 217]);
32747
33050
  var MjpegTransformer = class extends import_node_events7.EventEmitter {
@@ -32802,7 +33105,7 @@ var MjpegTransformer = class extends import_node_events7.EventEmitter {
32802
33105
  "pipe:1"
32803
33106
  );
32804
33107
  this.log("debug", `Starting FFmpeg with args: ${args.join(" ")}`);
32805
- this.ffmpeg = (0, import_node_child_process9.spawn)("ffmpeg", args, {
33108
+ this.ffmpeg = (0, import_node_child_process10.spawn)("ffmpeg", args, {
32806
33109
  stdio: ["pipe", "pipe", "pipe"]
32807
33110
  });
32808
33111
  this.ffmpeg.stdout.on("data", (data) => {
@@ -34168,7 +34471,7 @@ var import_node_fs = __toESM(require("fs"), 1);
34168
34471
  var import_promises3 = __toESM(require("fs/promises"), 1);
34169
34472
  var import_node_os3 = __toESM(require("os"), 1);
34170
34473
  var import_node_path3 = __toESM(require("path"), 1);
34171
- var import_node_child_process10 = require("child_process");
34474
+ var import_node_child_process11 = require("child_process");
34172
34475
  init_BcMediaAnnexBDecoder();
34173
34476
  init_H264Converter();
34174
34477
  init_H265Converter();
@@ -34638,7 +34941,7 @@ var BaichuanHlsServer = class extends import_node_events10.EventEmitter {
34638
34941
  this.segmentPattern,
34639
34942
  this.playlistPath
34640
34943
  );
34641
- const p = (0, import_node_child_process10.spawn)(this.ffmpegPath, args, {
34944
+ const p = (0, import_node_child_process11.spawn)(this.ffmpegPath, args, {
34642
34945
  stdio: ["pipe", "ignore", "pipe"]
34643
34946
  });
34644
34947
  p.on("error", (err) => {
@@ -34736,8 +35039,8 @@ function isTcpFailureThatShouldFallbackToUdp(e) {
34736
35039
  async function pingHost(host, timeoutMs = 3e3) {
34737
35040
  return new Promise((resolve) => {
34738
35041
  const { exec } = require("child_process");
34739
- const platform = process.platform;
34740
- const pingCmd = platform === "win32" ? `ping -n 1 -w ${timeoutMs} ${host}` : platform === "darwin" ? (
35042
+ const platform2 = process.platform;
35043
+ const pingCmd = platform2 === "win32" ? `ping -n 1 -w ${timeoutMs} ${host}` : platform2 === "darwin" ? (
34741
35044
  // macOS: -W is in milliseconds (Linux: seconds)
34742
35045
  `ping -c 1 -W ${timeoutMs} ${host}`
34743
35046
  ) : (
@@ -35245,8 +35548,8 @@ async function autoDetectDeviceType(inputs) {
35245
35548
 
35246
35549
  // src/multifocal/compositeRtspServer.ts
35247
35550
  var import_node_events11 = require("events");
35248
- var import_node_child_process11 = require("child_process");
35249
- var net3 = __toESM(require("net"), 1);
35551
+ var import_node_child_process12 = require("child_process");
35552
+ var net4 = __toESM(require("net"), 1);
35250
35553
  var CompositeRtspServer = class extends import_node_events11.EventEmitter {
35251
35554
  options;
35252
35555
  compositeStream = null;
@@ -35313,7 +35616,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
35313
35616
  const width = widerStreamInfo?.width ?? 1920;
35314
35617
  const height = widerStreamInfo?.height ?? 1080;
35315
35618
  const fps = widerStreamInfo?.frameRate ?? 25;
35316
- this.rtspServer = net3.createServer((socket) => {
35619
+ this.rtspServer = net4.createServer((socket) => {
35317
35620
  this.handleRtspConnection(socket);
35318
35621
  });
35319
35622
  await new Promise((resolve, reject) => {
@@ -35352,7 +35655,7 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
35352
35655
  this.logger.log?.(
35353
35656
  `[CompositeRtspServer] Starting ffmpeg RTSP server: ${ffmpegArgs.join(" ")}`
35354
35657
  );
35355
- this.ffmpegProcess = (0, import_node_child_process11.spawn)("ffmpeg", ffmpegArgs, {
35658
+ this.ffmpegProcess = (0, import_node_child_process12.spawn)("ffmpeg", ffmpegArgs, {
35356
35659
  stdio: ["pipe", "pipe", "pipe"]
35357
35660
  });
35358
35661
  this.ffmpegProcess.on("error", (error) => {
@@ -35656,7 +35959,11 @@ var CompositeRtspServer = class extends import_node_events11.EventEmitter {
35656
35959
  detectIosClient,
35657
35960
  detectVideoCodecFromNal,
35658
35961
  discoverReolinkDevices,
35962
+ discoverViaArpTable,
35963
+ discoverViaDhcpListener,
35659
35964
  discoverViaHttpScan,
35965
+ discoverViaOnvif,
35966
+ discoverViaTcpPortScan,
35660
35967
  discoverViaUdpBroadcast,
35661
35968
  discoverViaUdpDirect,
35662
35969
  encodeHeader,