@apocaliss92/nodelink-js 0.2.5 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -82
- package/dist/{chunk-EG5IY3CM.js → chunk-UDS2UR4S.js} +444 -20
- package/dist/chunk-UDS2UR4S.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +78 -16
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +1071 -210
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -70
- package/dist/index.d.ts +193 -69
- package/dist/index.js +580 -142
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
- package/dist/chunk-EG5IY3CM.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -22,7 +22,11 @@ import {
|
|
|
22
22
|
createTaggedLogger,
|
|
23
23
|
decodeHeader,
|
|
24
24
|
discoverReolinkDevices,
|
|
25
|
+
discoverViaArpTable,
|
|
26
|
+
discoverViaDhcpListener,
|
|
25
27
|
discoverViaHttpScan,
|
|
28
|
+
discoverViaOnvif,
|
|
29
|
+
discoverViaTcpPortScan,
|
|
26
30
|
discoverViaUdpBroadcast,
|
|
27
31
|
discoverViaUdpDirect,
|
|
28
32
|
encodeHeader,
|
|
@@ -38,7 +42,7 @@ import {
|
|
|
38
42
|
normalizeUid,
|
|
39
43
|
parseSupportXml,
|
|
40
44
|
setGlobalLogger
|
|
41
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-UDS2UR4S.js";
|
|
42
46
|
import {
|
|
43
47
|
AesStreamDecryptor,
|
|
44
48
|
BC_AES_IV,
|
|
@@ -586,51 +590,18 @@ var AutodiscoveryClient = class {
|
|
|
586
590
|
scanTimer = null;
|
|
587
591
|
isRunning = false;
|
|
588
592
|
currentScanPromise = null;
|
|
589
|
-
/**
|
|
590
|
-
* Costruttore del client di autodiscovery.
|
|
591
|
-
*
|
|
592
|
-
* @param options - Opzioni di configurazione per il discovery
|
|
593
|
-
*/
|
|
594
593
|
constructor(options = {}) {
|
|
595
594
|
this.options = {
|
|
596
|
-
|
|
597
|
-
|
|
595
|
+
...options,
|
|
596
|
+
scanIntervalMs: options.scanIntervalMs ?? 12e4,
|
|
598
597
|
autoStart: options.autoStart ?? false
|
|
599
598
|
};
|
|
600
|
-
if (options.networkCidr !== void 0) {
|
|
601
|
-
this.options.networkCidr = options.networkCidr;
|
|
602
|
-
}
|
|
603
|
-
if (options.username !== void 0) {
|
|
604
|
-
this.options.username = options.username;
|
|
605
|
-
}
|
|
606
|
-
if (options.password !== void 0) {
|
|
607
|
-
this.options.password = options.password;
|
|
608
|
-
}
|
|
609
|
-
if (options.httpProbeTimeoutMs !== void 0) {
|
|
610
|
-
this.options.httpProbeTimeoutMs = options.httpProbeTimeoutMs;
|
|
611
|
-
}
|
|
612
|
-
if (options.maxConcurrentProbes !== void 0) {
|
|
613
|
-
this.options.maxConcurrentProbes = options.maxConcurrentProbes;
|
|
614
|
-
}
|
|
615
|
-
if (options.logger !== void 0) {
|
|
616
|
-
this.options.logger = options.logger;
|
|
617
|
-
}
|
|
618
|
-
if (options.httpPorts !== void 0) {
|
|
619
|
-
this.options.httpPorts = options.httpPorts;
|
|
620
|
-
}
|
|
621
|
-
if (options.discoveryMethod !== void 0) {
|
|
622
|
-
this.options.discoveryMethod = options.discoveryMethod;
|
|
623
|
-
}
|
|
624
|
-
if (options.udpBroadcastTimeoutMs !== void 0) {
|
|
625
|
-
this.options.udpBroadcastTimeoutMs = options.udpBroadcastTimeoutMs;
|
|
626
|
-
}
|
|
627
599
|
if (this.options.autoStart) {
|
|
628
600
|
this.start();
|
|
629
601
|
}
|
|
630
602
|
}
|
|
631
603
|
/**
|
|
632
|
-
*
|
|
633
|
-
* Se già in esecuzione, non fa nulla.
|
|
604
|
+
* Start continuous discovery. If already running, does nothing.
|
|
634
605
|
*/
|
|
635
606
|
start() {
|
|
636
607
|
if (this.isRunning) {
|
|
@@ -645,8 +616,7 @@ var AutodiscoveryClient = class {
|
|
|
645
616
|
this.scheduleNextScan();
|
|
646
617
|
}
|
|
647
618
|
/**
|
|
648
|
-
*
|
|
649
|
-
* Se non è in esecuzione, non fa nulla.
|
|
619
|
+
* Stop continuous discovery. If not running, does nothing.
|
|
650
620
|
*/
|
|
651
621
|
stop() {
|
|
652
622
|
if (!this.isRunning) {
|
|
@@ -661,50 +631,41 @@ var AutodiscoveryClient = class {
|
|
|
661
631
|
this.options.logger?.log?.("[Autodiscovery] Discovery stopped");
|
|
662
632
|
}
|
|
663
633
|
/**
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
* @returns Array di dispositivi discoverati, ordinati per host
|
|
634
|
+
* Returns the current list of discovered devices, sorted by host IP.
|
|
667
635
|
*/
|
|
668
636
|
getDiscoveredDevices() {
|
|
669
|
-
return Array.from(this.discoveredDevices.values()).sort(
|
|
670
|
-
|
|
671
|
-
|
|
637
|
+
return Array.from(this.discoveredDevices.values()).sort(
|
|
638
|
+
(a, b) => a.host.localeCompare(b.host)
|
|
639
|
+
);
|
|
672
640
|
}
|
|
673
641
|
/**
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
* @returns Numero di dispositivi discoverati
|
|
642
|
+
* Returns the number of currently discovered devices.
|
|
677
643
|
*/
|
|
678
644
|
getDeviceCount() {
|
|
679
645
|
return this.discoveredDevices.size;
|
|
680
646
|
}
|
|
681
647
|
/**
|
|
682
|
-
*
|
|
683
|
-
*
|
|
684
|
-
* @returns `true` se il discovery è in esecuzione, `false` altrimenti
|
|
648
|
+
* Returns whether continuous discovery is currently running.
|
|
685
649
|
*/
|
|
686
650
|
isActive() {
|
|
687
651
|
return this.isRunning;
|
|
688
652
|
}
|
|
689
653
|
/**
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
* @returns Promise che si risolve quando lo scan è completato
|
|
654
|
+
* Force an immediate scan (doesn't wait for the scheduled interval).
|
|
655
|
+
* If a scan is already in progress, waits for it to complete.
|
|
694
656
|
*/
|
|
695
657
|
async scanNow() {
|
|
696
658
|
if (this.currentScanPromise) {
|
|
697
|
-
this.options.logger?.log?.(
|
|
659
|
+
this.options.logger?.log?.(
|
|
660
|
+
"[Autodiscovery] Scan already in progress, waiting for completion..."
|
|
661
|
+
);
|
|
698
662
|
await this.currentScanPromise;
|
|
699
663
|
return;
|
|
700
664
|
}
|
|
701
665
|
await this.performScan();
|
|
702
666
|
}
|
|
703
667
|
/**
|
|
704
|
-
*
|
|
705
|
-
*
|
|
706
|
-
* @param host - Indirizzo IP del dispositivo da rimuovere
|
|
707
|
-
* @returns `true` se il dispositivo è stato rimosso, `false` se non era presente
|
|
668
|
+
* Remove a device from the discovered list.
|
|
708
669
|
*/
|
|
709
670
|
removeDevice(host) {
|
|
710
671
|
const removed = this.discoveredDevices.delete(host);
|
|
@@ -714,59 +675,20 @@ var AutodiscoveryClient = class {
|
|
|
714
675
|
return removed;
|
|
715
676
|
}
|
|
716
677
|
/**
|
|
717
|
-
*
|
|
678
|
+
* Clear all discovered devices.
|
|
718
679
|
*/
|
|
719
680
|
clearDevices() {
|
|
720
681
|
const count = this.discoveredDevices.size;
|
|
721
682
|
this.discoveredDevices.clear();
|
|
722
|
-
this.options.logger?.log?.(
|
|
683
|
+
this.options.logger?.log?.(
|
|
684
|
+
`[Autodiscovery] Removed ${count} device(s) from list`
|
|
685
|
+
);
|
|
723
686
|
}
|
|
724
|
-
/**
|
|
725
|
-
* Esegue un singolo scan della rete.
|
|
726
|
-
*/
|
|
727
687
|
async performScan() {
|
|
728
688
|
const scanPromise = (async () => {
|
|
729
689
|
try {
|
|
730
690
|
this.options.logger?.log?.("[Autodiscovery] Starting scan...");
|
|
731
|
-
const
|
|
732
|
-
const discoveryOptions = {
|
|
733
|
-
enableHttpScanning: discoveryMethod === "http" || discoveryMethod === "both",
|
|
734
|
-
enableUdpDiscovery: discoveryMethod === "udp" || discoveryMethod === "both"
|
|
735
|
-
};
|
|
736
|
-
if (this.options.networkCidr !== void 0) {
|
|
737
|
-
discoveryOptions.networkCidr = this.options.networkCidr;
|
|
738
|
-
}
|
|
739
|
-
if (this.options.username !== void 0) {
|
|
740
|
-
discoveryOptions.username = this.options.username;
|
|
741
|
-
}
|
|
742
|
-
if (this.options.password !== void 0) {
|
|
743
|
-
discoveryOptions.password = this.options.password;
|
|
744
|
-
}
|
|
745
|
-
if (this.options.httpProbeTimeoutMs !== void 0) {
|
|
746
|
-
discoveryOptions.httpProbeTimeoutMs = this.options.httpProbeTimeoutMs;
|
|
747
|
-
}
|
|
748
|
-
if (this.options.maxConcurrentProbes !== void 0) {
|
|
749
|
-
discoveryOptions.maxConcurrentProbes = this.options.maxConcurrentProbes;
|
|
750
|
-
}
|
|
751
|
-
if (this.options.logger !== void 0) {
|
|
752
|
-
discoveryOptions.logger = this.options.logger;
|
|
753
|
-
}
|
|
754
|
-
if (this.options.httpPorts !== void 0) {
|
|
755
|
-
discoveryOptions.httpPorts = this.options.httpPorts;
|
|
756
|
-
}
|
|
757
|
-
if (this.options.udpBroadcastTimeoutMs !== void 0) {
|
|
758
|
-
discoveryOptions.udpBroadcastTimeoutMs = this.options.udpBroadcastTimeoutMs;
|
|
759
|
-
}
|
|
760
|
-
let discovered = [];
|
|
761
|
-
if (discoveryMethod === "http" || discoveryMethod === "both") {
|
|
762
|
-
const httpDevices = await discoverViaHttpScan(discoveryOptions);
|
|
763
|
-
discovered.push(...httpDevices);
|
|
764
|
-
}
|
|
765
|
-
if (discoveryMethod === "udp" || discoveryMethod === "both") {
|
|
766
|
-
const udpDevices = await discoverViaUdpBroadcast(discoveryOptions);
|
|
767
|
-
discovered.push(...udpDevices);
|
|
768
|
-
}
|
|
769
|
-
const beforeCount = this.discoveredDevices.size;
|
|
691
|
+
const discovered = await discoverReolinkDevices(this.options);
|
|
770
692
|
const newDevices = [];
|
|
771
693
|
const updatedDevices = [];
|
|
772
694
|
for (const device of discovered) {
|
|
@@ -781,38 +703,35 @@ var AutodiscoveryClient = class {
|
|
|
781
703
|
newDevices.push(device);
|
|
782
704
|
}
|
|
783
705
|
}
|
|
784
|
-
const afterCount = this.discoveredDevices.size;
|
|
785
706
|
this.options.logger?.log?.(
|
|
786
|
-
`[Autodiscovery] Scan completed: ${newDevices.length} new, ${updatedDevices.length} updated, total: ${
|
|
707
|
+
`[Autodiscovery] Scan completed: ${newDevices.length} new, ${updatedDevices.length} updated, total: ${this.discoveredDevices.size}`
|
|
787
708
|
);
|
|
788
709
|
for (const device of newDevices) {
|
|
789
|
-
const details = [];
|
|
790
|
-
if (device.model) details.push(`Model: ${device.model}`);
|
|
791
|
-
if (device.name) details.push(`Name: ${device.name}`);
|
|
792
|
-
if (device.uid) details.push(`UID: ${device.uid}`);
|
|
793
|
-
if (device.firmwareVersion) details.push(`Firmware: ${device.firmwareVersion}`);
|
|
794
|
-
if (device.httpPort) details.push(`HTTP Port: ${device.httpPort}`);
|
|
795
|
-
if (device.httpsPort) details.push(`HTTPS Port: ${device.httpsPort}`);
|
|
796
|
-
details.push(`Discovery Method: ${device.discoveryMethod}`);
|
|
797
|
-
if (device.supportsHttps !== void 0) details.push(`HTTPS: ${device.supportsHttps}`);
|
|
798
|
-
if (device.httpAccessible !== void 0) details.push(`HTTP Accessible: ${device.httpAccessible}`);
|
|
799
710
|
this.options.logger?.log?.(
|
|
800
|
-
`[Autodiscovery]
|
|
711
|
+
`[Autodiscovery] NEW DEVICE - ${device.host} | ${device.model ?? "unknown"} | ${device.name ?? ""} | via ${device.discoveryMethod}`
|
|
801
712
|
);
|
|
713
|
+
try {
|
|
714
|
+
this.options.onDeviceDiscovered?.(device);
|
|
715
|
+
} catch {
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
for (const device of updatedDevices) {
|
|
719
|
+
try {
|
|
720
|
+
this.options.onDeviceUpdated?.(device);
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
802
723
|
}
|
|
803
724
|
} catch (error) {
|
|
804
725
|
const msg = error instanceof Error ? error.message : String(error);
|
|
805
|
-
this.options.logger?.error?.(
|
|
726
|
+
this.options.logger?.error?.(
|
|
727
|
+
`[Autodiscovery] Error during scan: ${msg}`
|
|
728
|
+
);
|
|
806
729
|
}
|
|
807
730
|
})();
|
|
808
731
|
this.currentScanPromise = scanPromise;
|
|
809
732
|
await scanPromise;
|
|
810
733
|
this.currentScanPromise = null;
|
|
811
734
|
}
|
|
812
|
-
/**
|
|
813
|
-
* Unisce le informazioni di un dispositivo esistente con quelle di un nuovo scan.
|
|
814
|
-
* Restituisce il dispositivo aggiornato se ci sono state modifiche, altrimenti `null`.
|
|
815
|
-
*/
|
|
816
735
|
mergeDeviceInfo(existing, updated) {
|
|
817
736
|
let hasChanges = false;
|
|
818
737
|
if (!existing.model && updated.model) {
|
|
@@ -849,13 +768,8 @@ var AutodiscoveryClient = class {
|
|
|
849
768
|
}
|
|
850
769
|
return hasChanges ? existing : null;
|
|
851
770
|
}
|
|
852
|
-
/**
|
|
853
|
-
* Programma il prossimo scan.
|
|
854
|
-
*/
|
|
855
771
|
scheduleNextScan() {
|
|
856
|
-
if (!this.isRunning)
|
|
857
|
-
return;
|
|
858
|
-
}
|
|
772
|
+
if (!this.isRunning) return;
|
|
859
773
|
this.scanTimer = setTimeout(() => {
|
|
860
774
|
this.scanTimer = null;
|
|
861
775
|
if (this.isRunning) {
|
|
@@ -4671,8 +4585,527 @@ async function createReplayHttpServer(options) {
|
|
|
4671
4585
|
};
|
|
4672
4586
|
}
|
|
4673
4587
|
|
|
4674
|
-
// src/baichuan/stream/
|
|
4588
|
+
// src/baichuan/stream/Go2rtcTcpServer.ts
|
|
4675
4589
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
4590
|
+
import * as net from "net";
|
|
4591
|
+
var AsyncBoundedQueue = class {
|
|
4592
|
+
maxItems;
|
|
4593
|
+
queue = [];
|
|
4594
|
+
waiting;
|
|
4595
|
+
closed = false;
|
|
4596
|
+
constructor(maxItems) {
|
|
4597
|
+
this.maxItems = Math.max(1, maxItems | 0);
|
|
4598
|
+
}
|
|
4599
|
+
push(item) {
|
|
4600
|
+
if (this.closed) return;
|
|
4601
|
+
if (this.waiting) {
|
|
4602
|
+
const { resolve } = this.waiting;
|
|
4603
|
+
this.waiting = void 0;
|
|
4604
|
+
resolve({ value: item, done: false });
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4607
|
+
this.queue.push(item);
|
|
4608
|
+
if (this.queue.length > this.maxItems) {
|
|
4609
|
+
this.queue.splice(0, this.queue.length - this.maxItems);
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
close() {
|
|
4613
|
+
if (this.closed) return;
|
|
4614
|
+
this.closed = true;
|
|
4615
|
+
if (this.waiting) {
|
|
4616
|
+
const { resolve } = this.waiting;
|
|
4617
|
+
this.waiting = void 0;
|
|
4618
|
+
resolve({ value: void 0, done: true });
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
async next() {
|
|
4622
|
+
if (this.closed) return { value: void 0, done: true };
|
|
4623
|
+
const item = this.queue.shift();
|
|
4624
|
+
if (item !== void 0) return { value: item, done: false };
|
|
4625
|
+
return await new Promise((resolve) => {
|
|
4626
|
+
this.waiting = { resolve };
|
|
4627
|
+
});
|
|
4628
|
+
}
|
|
4629
|
+
};
|
|
4630
|
+
var NativeStreamFanout = class {
|
|
4631
|
+
opts;
|
|
4632
|
+
queues = /* @__PURE__ */ new Map();
|
|
4633
|
+
source = null;
|
|
4634
|
+
running = false;
|
|
4635
|
+
pumpPromise = null;
|
|
4636
|
+
constructor(opts) {
|
|
4637
|
+
this.opts = opts;
|
|
4638
|
+
}
|
|
4639
|
+
start() {
|
|
4640
|
+
if (this.running) return;
|
|
4641
|
+
this.running = true;
|
|
4642
|
+
this.source = this.opts.createSource();
|
|
4643
|
+
this.pumpPromise = (async () => {
|
|
4644
|
+
try {
|
|
4645
|
+
for await (const frame of this.source) {
|
|
4646
|
+
try {
|
|
4647
|
+
this.opts.onFrame?.(frame);
|
|
4648
|
+
} catch {
|
|
4649
|
+
}
|
|
4650
|
+
for (const q of this.queues.values()) {
|
|
4651
|
+
q.push(frame);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
} catch (e) {
|
|
4655
|
+
this.opts.onError?.(e);
|
|
4656
|
+
} finally {
|
|
4657
|
+
for (const q of this.queues.values()) q.close();
|
|
4658
|
+
this.queues.clear();
|
|
4659
|
+
this.running = false;
|
|
4660
|
+
this.opts.onEnd?.();
|
|
4661
|
+
}
|
|
4662
|
+
})();
|
|
4663
|
+
}
|
|
4664
|
+
subscribe(id) {
|
|
4665
|
+
const q = new AsyncBoundedQueue(this.opts.maxQueueItems);
|
|
4666
|
+
this.queues.set(id, q);
|
|
4667
|
+
const self = this;
|
|
4668
|
+
return (async function* () {
|
|
4669
|
+
try {
|
|
4670
|
+
while (true) {
|
|
4671
|
+
const r = await q.next();
|
|
4672
|
+
if (r.done) return;
|
|
4673
|
+
yield r.value;
|
|
4674
|
+
}
|
|
4675
|
+
} finally {
|
|
4676
|
+
q.close();
|
|
4677
|
+
self.queues.delete(id);
|
|
4678
|
+
}
|
|
4679
|
+
})();
|
|
4680
|
+
}
|
|
4681
|
+
async stop() {
|
|
4682
|
+
if (!this.running) return;
|
|
4683
|
+
this.running = false;
|
|
4684
|
+
const src = this.source;
|
|
4685
|
+
this.source = null;
|
|
4686
|
+
for (const q of this.queues.values()) q.close();
|
|
4687
|
+
this.queues.clear();
|
|
4688
|
+
try {
|
|
4689
|
+
await src?.return(void 0);
|
|
4690
|
+
} catch {
|
|
4691
|
+
}
|
|
4692
|
+
try {
|
|
4693
|
+
await this.pumpPromise;
|
|
4694
|
+
} catch {
|
|
4695
|
+
}
|
|
4696
|
+
this.pumpPromise = null;
|
|
4697
|
+
}
|
|
4698
|
+
};
|
|
4699
|
+
var Go2rtcTcpServer = class _Go2rtcTcpServer extends EventEmitter2 {
|
|
4700
|
+
api;
|
|
4701
|
+
channel;
|
|
4702
|
+
profile;
|
|
4703
|
+
variant;
|
|
4704
|
+
listenHost;
|
|
4705
|
+
listenPort;
|
|
4706
|
+
logger;
|
|
4707
|
+
deviceId;
|
|
4708
|
+
gracePeriodMs;
|
|
4709
|
+
prebufferMaxMs;
|
|
4710
|
+
maxBufferBytes;
|
|
4711
|
+
prestartStream;
|
|
4712
|
+
active = false;
|
|
4713
|
+
server;
|
|
4714
|
+
resolvedPort;
|
|
4715
|
+
// Native stream
|
|
4716
|
+
nativeFanout = null;
|
|
4717
|
+
nativeStreamActive = false;
|
|
4718
|
+
dedicatedSessionRelease;
|
|
4719
|
+
detectedVideoType;
|
|
4720
|
+
// Client tracking
|
|
4721
|
+
connectedClients = /* @__PURE__ */ new Set();
|
|
4722
|
+
clientSockets = /* @__PURE__ */ new Map();
|
|
4723
|
+
stopGraceTimer;
|
|
4724
|
+
// Prebuffer
|
|
4725
|
+
prebuffer = [];
|
|
4726
|
+
constructor(options) {
|
|
4727
|
+
super();
|
|
4728
|
+
this.api = options.api;
|
|
4729
|
+
this.channel = options.channel;
|
|
4730
|
+
this.profile = options.profile;
|
|
4731
|
+
this.variant = options.variant ?? "default";
|
|
4732
|
+
this.listenHost = options.listenHost ?? "127.0.0.1";
|
|
4733
|
+
this.listenPort = options.listenPort ?? 0;
|
|
4734
|
+
this.logger = options.logger ?? console;
|
|
4735
|
+
this.deviceId = options.deviceId;
|
|
4736
|
+
this.gracePeriodMs = options.gracePeriodMs ?? 3e4;
|
|
4737
|
+
this.prebufferMaxMs = options.prebufferMs ?? 3e3;
|
|
4738
|
+
this.maxBufferBytes = options.maxBufferBytes ?? 1e8;
|
|
4739
|
+
this.prestartStream = options.prestartStream ?? true;
|
|
4740
|
+
}
|
|
4741
|
+
// -----------------------------------------------------------------------
|
|
4742
|
+
// Public API
|
|
4743
|
+
// -----------------------------------------------------------------------
|
|
4744
|
+
/** Start listening. Resolves once the TCP server is bound. */
|
|
4745
|
+
async start() {
|
|
4746
|
+
if (this.active) return;
|
|
4747
|
+
this.active = true;
|
|
4748
|
+
this.server = net.createServer((socket) => this.handleClient(socket));
|
|
4749
|
+
this.server.on("error", (err) => {
|
|
4750
|
+
this.logger.error?.(`[Go2rtcTcpServer] server error: ${err.message}`);
|
|
4751
|
+
this.emit("error", err);
|
|
4752
|
+
});
|
|
4753
|
+
await new Promise((resolve, reject) => {
|
|
4754
|
+
this.server.listen(this.listenPort, this.listenHost, () => {
|
|
4755
|
+
const addr = this.server.address();
|
|
4756
|
+
this.resolvedPort = addr.port;
|
|
4757
|
+
this.logger.info?.(
|
|
4758
|
+
`[Go2rtcTcpServer] listening on ${addr.address}:${addr.port} channel=${this.channel} profile=${this.profile}`
|
|
4759
|
+
);
|
|
4760
|
+
this.emit("listening", { host: addr.address, port: addr.port });
|
|
4761
|
+
resolve();
|
|
4762
|
+
});
|
|
4763
|
+
this.server.once("error", reject);
|
|
4764
|
+
});
|
|
4765
|
+
if (this.prestartStream) {
|
|
4766
|
+
this.logger.info?.(
|
|
4767
|
+
`[Go2rtcTcpServer] pre-starting native stream channel=${this.channel} profile=${this.profile}`
|
|
4768
|
+
);
|
|
4769
|
+
this.startNativeStream();
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
/** Stop the server and all active streams. */
|
|
4773
|
+
async stop() {
|
|
4774
|
+
if (!this.active) return;
|
|
4775
|
+
this.active = false;
|
|
4776
|
+
clearTimeout(this.stopGraceTimer);
|
|
4777
|
+
for (const [id, sock] of this.clientSockets) {
|
|
4778
|
+
sock.destroy();
|
|
4779
|
+
this.connectedClients.delete(id);
|
|
4780
|
+
}
|
|
4781
|
+
this.clientSockets.clear();
|
|
4782
|
+
await this.stopNativeStream();
|
|
4783
|
+
if (this.server) {
|
|
4784
|
+
await new Promise((resolve) => {
|
|
4785
|
+
this.server.close(() => resolve());
|
|
4786
|
+
});
|
|
4787
|
+
this.server = void 0;
|
|
4788
|
+
}
|
|
4789
|
+
this.prebuffer = [];
|
|
4790
|
+
this.resolvedPort = void 0;
|
|
4791
|
+
this.emit("close");
|
|
4792
|
+
}
|
|
4793
|
+
/** The actual port the server is listening on (available after start()). */
|
|
4794
|
+
get port() {
|
|
4795
|
+
return this.resolvedPort;
|
|
4796
|
+
}
|
|
4797
|
+
/** The go2rtc-compatible source URL. */
|
|
4798
|
+
get go2rtcSourceUrl() {
|
|
4799
|
+
if (this.resolvedPort == null) return void 0;
|
|
4800
|
+
return `tcp://127.0.0.1:${this.resolvedPort}`;
|
|
4801
|
+
}
|
|
4802
|
+
/** Number of currently connected clients. */
|
|
4803
|
+
get clientCount() {
|
|
4804
|
+
return this.connectedClients.size;
|
|
4805
|
+
}
|
|
4806
|
+
// -----------------------------------------------------------------------
|
|
4807
|
+
// Client handling
|
|
4808
|
+
// -----------------------------------------------------------------------
|
|
4809
|
+
handleClient(socket) {
|
|
4810
|
+
const clientId = `${socket.remoteAddress}:${socket.remotePort}`;
|
|
4811
|
+
socket.setNoDelay(true);
|
|
4812
|
+
this.connectedClients.add(clientId);
|
|
4813
|
+
this.clientSockets.set(clientId, socket);
|
|
4814
|
+
this.logger.info?.(
|
|
4815
|
+
`[Go2rtcTcpServer] client connected id=${clientId} total=${this.connectedClients.size}`
|
|
4816
|
+
);
|
|
4817
|
+
this.emit("client", clientId);
|
|
4818
|
+
if (this.stopGraceTimer) {
|
|
4819
|
+
clearTimeout(this.stopGraceTimer);
|
|
4820
|
+
this.stopGraceTimer = void 0;
|
|
4821
|
+
}
|
|
4822
|
+
if (!this.nativeStreamActive) {
|
|
4823
|
+
this.startNativeStream();
|
|
4824
|
+
}
|
|
4825
|
+
this.feedClient(clientId, socket).catch((err) => {
|
|
4826
|
+
this.logger.warn?.(
|
|
4827
|
+
`[Go2rtcTcpServer] feedClient error id=${clientId}: ${err}`
|
|
4828
|
+
);
|
|
4829
|
+
});
|
|
4830
|
+
const cleanup = () => {
|
|
4831
|
+
this.removeClient(clientId);
|
|
4832
|
+
socket.destroy();
|
|
4833
|
+
};
|
|
4834
|
+
socket.on("error", cleanup);
|
|
4835
|
+
socket.on("close", cleanup);
|
|
4836
|
+
}
|
|
4837
|
+
async feedClient(clientId, socket) {
|
|
4838
|
+
const fanoutDeadline = Date.now() + 3e4;
|
|
4839
|
+
while (this.active && !this.nativeFanout) {
|
|
4840
|
+
if (socket.destroyed) return;
|
|
4841
|
+
if (Date.now() > fanoutDeadline) {
|
|
4842
|
+
this.logger.warn?.(
|
|
4843
|
+
`[Go2rtcTcpServer] fanout not ready after 30s, dropping client ${clientId}`
|
|
4844
|
+
);
|
|
4845
|
+
return;
|
|
4846
|
+
}
|
|
4847
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
4848
|
+
}
|
|
4849
|
+
if (!this.active || !this.nativeFanout) return;
|
|
4850
|
+
const subscription = this.nativeFanout.subscribe(clientId);
|
|
4851
|
+
const prebufferSnap = this.prebuffer.slice();
|
|
4852
|
+
let lastIdrIdx = -1;
|
|
4853
|
+
for (let i = prebufferSnap.length - 1; i >= 0; i--) {
|
|
4854
|
+
if (prebufferSnap[i].isKeyframe) {
|
|
4855
|
+
lastIdrIdx = i;
|
|
4856
|
+
break;
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
if (lastIdrIdx >= 0) {
|
|
4860
|
+
const replay = prebufferSnap.slice(lastIdrIdx);
|
|
4861
|
+
this.logger.info?.(
|
|
4862
|
+
`[Go2rtcTcpServer] prebuffer replay client=${clientId} frames=${replay.length}`
|
|
4863
|
+
);
|
|
4864
|
+
for (const entry of replay) {
|
|
4865
|
+
if (socket.destroyed) return;
|
|
4866
|
+
socket.write(entry.data);
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
let seenKeyframe = lastIdrIdx >= 0;
|
|
4870
|
+
let liveFrameCount = 0;
|
|
4871
|
+
let liveVideoWritten = 0;
|
|
4872
|
+
let lastLogAt = Date.now();
|
|
4873
|
+
try {
|
|
4874
|
+
this.logger.info?.(
|
|
4875
|
+
`[Go2rtcTcpServer] entering live loop client=${clientId} seenKeyframe=${seenKeyframe}`
|
|
4876
|
+
);
|
|
4877
|
+
for await (const frame of subscription) {
|
|
4878
|
+
if (socket.destroyed || !this.active) {
|
|
4879
|
+
this.logger.info?.(
|
|
4880
|
+
`[Go2rtcTcpServer] live loop exit client=${clientId} destroyed=${socket.destroyed} active=${this.active}`
|
|
4881
|
+
);
|
|
4882
|
+
break;
|
|
4883
|
+
}
|
|
4884
|
+
liveFrameCount++;
|
|
4885
|
+
const annexB = this.convertFrame(frame);
|
|
4886
|
+
if (!annexB) continue;
|
|
4887
|
+
if (!seenKeyframe) {
|
|
4888
|
+
if (!this.isAnnexBKeyframe(annexB, frame.videoType)) continue;
|
|
4889
|
+
seenKeyframe = true;
|
|
4890
|
+
this.logger.info?.(
|
|
4891
|
+
`[Go2rtcTcpServer] first live keyframe client=${clientId} after ${liveFrameCount} frames`
|
|
4892
|
+
);
|
|
4893
|
+
}
|
|
4894
|
+
socket.write(annexB);
|
|
4895
|
+
liveVideoWritten++;
|
|
4896
|
+
if (Date.now() - lastLogAt > 1e4) {
|
|
4897
|
+
this.logger.info?.(
|
|
4898
|
+
`[Go2rtcTcpServer] live stats client=${clientId} received=${liveFrameCount} written=${liveVideoWritten} bufLen=${socket.writableLength}`
|
|
4899
|
+
);
|
|
4900
|
+
lastLogAt = Date.now();
|
|
4901
|
+
}
|
|
4902
|
+
if (socket.writableLength > this.maxBufferBytes) {
|
|
4903
|
+
this.logger.warn?.(
|
|
4904
|
+
`[Go2rtcTcpServer] buffer overflow (${socket.writableLength} bytes), dropping client ${clientId}`
|
|
4905
|
+
);
|
|
4906
|
+
socket.destroy();
|
|
4907
|
+
break;
|
|
4908
|
+
}
|
|
4909
|
+
}
|
|
4910
|
+
this.logger.info?.(
|
|
4911
|
+
`[Go2rtcTcpServer] live loop ended naturally client=${clientId} received=${liveFrameCount} written=${liveVideoWritten}`
|
|
4912
|
+
);
|
|
4913
|
+
} finally {
|
|
4914
|
+
await subscription.return(void 0).catch(() => {
|
|
4915
|
+
});
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
// -----------------------------------------------------------------------
|
|
4919
|
+
// Frame conversion
|
|
4920
|
+
// -----------------------------------------------------------------------
|
|
4921
|
+
/**
|
|
4922
|
+
* Convert a native frame to wire-ready Annex-B.
|
|
4923
|
+
* Audio frames are skipped — raw TCP carries only video (Annex-B).
|
|
4924
|
+
* go2rtc auto-detects the codec from SPS/PPS/VPS NALUs.
|
|
4925
|
+
*/
|
|
4926
|
+
convertFrame(frame) {
|
|
4927
|
+
if (frame.audio) {
|
|
4928
|
+
return null;
|
|
4929
|
+
}
|
|
4930
|
+
if (frame.data.length === 0) return null;
|
|
4931
|
+
try {
|
|
4932
|
+
if (frame.videoType === "H264") {
|
|
4933
|
+
return convertToAnnexB(frame.data);
|
|
4934
|
+
}
|
|
4935
|
+
if (frame.videoType === "H265") {
|
|
4936
|
+
return convertToAnnexB2(frame.data);
|
|
4937
|
+
}
|
|
4938
|
+
} catch {
|
|
4939
|
+
}
|
|
4940
|
+
return frame.data;
|
|
4941
|
+
}
|
|
4942
|
+
/** Check if an Annex-B buffer contains a keyframe (IDR for H.264, IRAP for H.265). */
|
|
4943
|
+
isAnnexBKeyframe(annexB, videoType) {
|
|
4944
|
+
try {
|
|
4945
|
+
if (videoType === "H264") {
|
|
4946
|
+
const nals = _Go2rtcTcpServer.splitAnnexBNals(annexB);
|
|
4947
|
+
return nals.some((n) => n.length >= 1 && (n[0] & 31) === 5);
|
|
4948
|
+
}
|
|
4949
|
+
if (videoType === "H265") {
|
|
4950
|
+
const nals = splitAnnexBToNalPayloads2(annexB);
|
|
4951
|
+
return nals.some(
|
|
4952
|
+
(n) => n.length >= 2 && isH265Irap(n[0] >> 1 & 63)
|
|
4953
|
+
);
|
|
4954
|
+
}
|
|
4955
|
+
} catch {
|
|
4956
|
+
}
|
|
4957
|
+
return false;
|
|
4958
|
+
}
|
|
4959
|
+
/** Split Annex-B byte stream into individual NAL units. */
|
|
4960
|
+
static splitAnnexBNals(buf) {
|
|
4961
|
+
const nals = [];
|
|
4962
|
+
let i = 0;
|
|
4963
|
+
while (i < buf.length) {
|
|
4964
|
+
if (i + 2 < buf.length && buf[i] === 0 && buf[i + 1] === 0) {
|
|
4965
|
+
let scLen;
|
|
4966
|
+
if (buf[i + 2] === 1) {
|
|
4967
|
+
scLen = 3;
|
|
4968
|
+
} else if (i + 3 < buf.length && buf[i + 2] === 0 && buf[i + 3] === 1) {
|
|
4969
|
+
scLen = 4;
|
|
4970
|
+
} else {
|
|
4971
|
+
i++;
|
|
4972
|
+
continue;
|
|
4973
|
+
}
|
|
4974
|
+
const nalStart = i + scLen;
|
|
4975
|
+
let nalEnd = buf.length;
|
|
4976
|
+
for (let j = nalStart; j < buf.length - 2; j++) {
|
|
4977
|
+
if (buf[j] === 0 && buf[j + 1] === 0 && (buf[j + 2] === 1 || j + 3 < buf.length && buf[j + 2] === 0 && buf[j + 3] === 1)) {
|
|
4978
|
+
nalEnd = j;
|
|
4979
|
+
break;
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
if (nalEnd > nalStart) {
|
|
4983
|
+
nals.push(buf.subarray(nalStart, nalEnd));
|
|
4984
|
+
}
|
|
4985
|
+
i = nalEnd;
|
|
4986
|
+
} else {
|
|
4987
|
+
i++;
|
|
4988
|
+
}
|
|
4989
|
+
}
|
|
4990
|
+
return nals;
|
|
4991
|
+
}
|
|
4992
|
+
// -----------------------------------------------------------------------
|
|
4993
|
+
// Native stream management
|
|
4994
|
+
// -----------------------------------------------------------------------
|
|
4995
|
+
async startNativeStream() {
|
|
4996
|
+
if (this.nativeStreamActive) return;
|
|
4997
|
+
this.nativeStreamActive = true;
|
|
4998
|
+
let dedicatedClient;
|
|
4999
|
+
if (this.deviceId) {
|
|
5000
|
+
try {
|
|
5001
|
+
const session = await this.api.createDedicatedSession(
|
|
5002
|
+
`live:${this.deviceId}:ch${this.channel}:${this.profile}`
|
|
5003
|
+
);
|
|
5004
|
+
dedicatedClient = session.client;
|
|
5005
|
+
this.dedicatedSessionRelease = session.release;
|
|
5006
|
+
} catch (e) {
|
|
5007
|
+
this.logger.warn?.(
|
|
5008
|
+
`[Go2rtcTcpServer] failed to acquire dedicated session, using shared socket: ${e}`
|
|
5009
|
+
);
|
|
5010
|
+
}
|
|
5011
|
+
}
|
|
5012
|
+
this.logger.info?.(
|
|
5013
|
+
`[Go2rtcTcpServer] native stream starting channel=${this.channel} profile=${this.profile} dedicated=${!!dedicatedClient}`
|
|
5014
|
+
);
|
|
5015
|
+
this.nativeFanout = new NativeStreamFanout({
|
|
5016
|
+
maxQueueItems: 200,
|
|
5017
|
+
createSource: () => createNativeStream(this.api, this.channel, this.profile, {
|
|
5018
|
+
variant: this.variant,
|
|
5019
|
+
...dedicatedClient ? { client: dedicatedClient } : {}
|
|
5020
|
+
}),
|
|
5021
|
+
onFrame: (frame) => {
|
|
5022
|
+
if (!frame.audio && (frame.videoType === "H264" || frame.videoType === "H265")) {
|
|
5023
|
+
this.detectedVideoType = frame.videoType;
|
|
5024
|
+
}
|
|
5025
|
+
const wireData = this.convertFrame(frame);
|
|
5026
|
+
if (!wireData || wireData.length === 0) return;
|
|
5027
|
+
const isKeyframe = !frame.audio && this.isAnnexBKeyframe(wireData, frame.videoType);
|
|
5028
|
+
this.prebuffer.push({
|
|
5029
|
+
data: Buffer.from(wireData),
|
|
5030
|
+
time: Date.now(),
|
|
5031
|
+
isKeyframe,
|
|
5032
|
+
audio: frame.audio
|
|
5033
|
+
});
|
|
5034
|
+
const cutoff = Date.now() - this.prebufferMaxMs;
|
|
5035
|
+
let trimIdx = 0;
|
|
5036
|
+
while (trimIdx < this.prebuffer.length && this.prebuffer[trimIdx].time < cutoff) {
|
|
5037
|
+
trimIdx++;
|
|
5038
|
+
}
|
|
5039
|
+
if (trimIdx > 0) this.prebuffer.splice(0, trimIdx);
|
|
5040
|
+
},
|
|
5041
|
+
onError: (error) => {
|
|
5042
|
+
this.logger.warn?.(`[Go2rtcTcpServer] native stream error: ${error}`);
|
|
5043
|
+
},
|
|
5044
|
+
onEnd: () => {
|
|
5045
|
+
if (!this.nativeStreamActive) return;
|
|
5046
|
+
this.nativeStreamActive = false;
|
|
5047
|
+
this.nativeFanout = null;
|
|
5048
|
+
if (this.dedicatedSessionRelease) {
|
|
5049
|
+
this.dedicatedSessionRelease().catch(() => {
|
|
5050
|
+
});
|
|
5051
|
+
this.dedicatedSessionRelease = void 0;
|
|
5052
|
+
}
|
|
5053
|
+
if (this.active && (this.connectedClients.size > 0 || this.prestartStream)) {
|
|
5054
|
+
this.logger.info?.(
|
|
5055
|
+
`[Go2rtcTcpServer] native stream ended, restarting (clients=${this.connectedClients.size}, prestart=${this.prestartStream})`
|
|
5056
|
+
);
|
|
5057
|
+
this.startNativeStream();
|
|
5058
|
+
}
|
|
5059
|
+
}
|
|
5060
|
+
});
|
|
5061
|
+
this.nativeFanout.start();
|
|
5062
|
+
}
|
|
5063
|
+
async stopNativeStream() {
|
|
5064
|
+
this.nativeStreamActive = false;
|
|
5065
|
+
const fanout = this.nativeFanout;
|
|
5066
|
+
this.nativeFanout = null;
|
|
5067
|
+
if (fanout) {
|
|
5068
|
+
await fanout.stop();
|
|
5069
|
+
}
|
|
5070
|
+
this.prebuffer = [];
|
|
5071
|
+
if (this.dedicatedSessionRelease) {
|
|
5072
|
+
await this.dedicatedSessionRelease().catch(() => {
|
|
5073
|
+
});
|
|
5074
|
+
this.dedicatedSessionRelease = void 0;
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5077
|
+
// -----------------------------------------------------------------------
|
|
5078
|
+
// Client lifecycle
|
|
5079
|
+
// -----------------------------------------------------------------------
|
|
5080
|
+
removeClient(clientId) {
|
|
5081
|
+
if (!this.connectedClients.has(clientId)) return;
|
|
5082
|
+
this.connectedClients.delete(clientId);
|
|
5083
|
+
this.clientSockets.delete(clientId);
|
|
5084
|
+
this.logger.info?.(
|
|
5085
|
+
`[Go2rtcTcpServer] client disconnected id=${clientId} remaining=${this.connectedClients.size}`
|
|
5086
|
+
);
|
|
5087
|
+
this.emit("clientDisconnected", clientId);
|
|
5088
|
+
if (this.connectedClients.size === 0 && !this.prestartStream) {
|
|
5089
|
+
this.scheduleStop();
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
scheduleStop() {
|
|
5093
|
+
if (this.stopGraceTimer) return;
|
|
5094
|
+
this.logger.info?.(
|
|
5095
|
+
`[Go2rtcTcpServer] no clients, scheduling stream stop in ${this.gracePeriodMs}ms`
|
|
5096
|
+
);
|
|
5097
|
+
this.stopGraceTimer = setTimeout(async () => {
|
|
5098
|
+
this.stopGraceTimer = void 0;
|
|
5099
|
+
if (this.connectedClients.size === 0 && this.nativeStreamActive) {
|
|
5100
|
+
this.logger.info?.("[Go2rtcTcpServer] grace period expired, stopping native stream");
|
|
5101
|
+
await this.stopNativeStream();
|
|
5102
|
+
}
|
|
5103
|
+
}, this.gracePeriodMs);
|
|
5104
|
+
}
|
|
5105
|
+
};
|
|
5106
|
+
|
|
5107
|
+
// src/baichuan/stream/BaichuanHttpStreamServer.ts
|
|
5108
|
+
import { EventEmitter as EventEmitter3 } from "events";
|
|
4676
5109
|
import { spawn as spawn5 } from "child_process";
|
|
4677
5110
|
import * as http4 from "http";
|
|
4678
5111
|
var NAL_START_CODE_4B = Buffer.from([0, 0, 0, 1]);
|
|
@@ -4719,7 +5152,7 @@ function isH264KeyframeFromAnnexB(annexB) {
|
|
|
4719
5152
|
}
|
|
4720
5153
|
return false;
|
|
4721
5154
|
}
|
|
4722
|
-
var BaichuanHttpStreamServer = class extends
|
|
5155
|
+
var BaichuanHttpStreamServer = class extends EventEmitter3 {
|
|
4723
5156
|
videoStream;
|
|
4724
5157
|
listenPort;
|
|
4725
5158
|
path;
|
|
@@ -4986,15 +5419,15 @@ var BaichuanHttpStreamServer = class extends EventEmitter2 {
|
|
|
4986
5419
|
};
|
|
4987
5420
|
|
|
4988
5421
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
4989
|
-
import { EventEmitter as
|
|
5422
|
+
import { EventEmitter as EventEmitter5 } from "events";
|
|
4990
5423
|
import * as http5 from "http";
|
|
4991
5424
|
|
|
4992
5425
|
// src/baichuan/stream/MjpegTransformer.ts
|
|
4993
|
-
import { EventEmitter as
|
|
5426
|
+
import { EventEmitter as EventEmitter4 } from "events";
|
|
4994
5427
|
import { spawn as spawn6 } from "child_process";
|
|
4995
5428
|
var JPEG_SOI = Buffer.from([255, 216]);
|
|
4996
5429
|
var JPEG_EOI = Buffer.from([255, 217]);
|
|
4997
|
-
var MjpegTransformer = class extends
|
|
5430
|
+
var MjpegTransformer = class extends EventEmitter4 {
|
|
4998
5431
|
options;
|
|
4999
5432
|
ffmpeg = null;
|
|
5000
5433
|
started = false;
|
|
@@ -5191,7 +5624,7 @@ Content-Length: ${frame.length}\r
|
|
|
5191
5624
|
}
|
|
5192
5625
|
|
|
5193
5626
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
5194
|
-
var BaichuanMjpegServer = class extends
|
|
5627
|
+
var BaichuanMjpegServer = class extends EventEmitter5 {
|
|
5195
5628
|
options;
|
|
5196
5629
|
clients = /* @__PURE__ */ new Map();
|
|
5197
5630
|
httpServer = null;
|
|
@@ -5472,7 +5905,7 @@ var BaichuanMjpegServer = class extends EventEmitter4 {
|
|
|
5472
5905
|
};
|
|
5473
5906
|
|
|
5474
5907
|
// src/baichuan/stream/BaichuanWebRTCServer.ts
|
|
5475
|
-
import { EventEmitter as
|
|
5908
|
+
import { EventEmitter as EventEmitter6 } from "events";
|
|
5476
5909
|
function parseAnnexBNalUnits(annexB) {
|
|
5477
5910
|
const nalUnits = [];
|
|
5478
5911
|
let offset = 0;
|
|
@@ -5507,7 +5940,7 @@ function getH264NalType(nalUnit) {
|
|
|
5507
5940
|
function getH265NalType2(nalUnit) {
|
|
5508
5941
|
return nalUnit[0] >> 1 & 63;
|
|
5509
5942
|
}
|
|
5510
|
-
var BaichuanWebRTCServer = class extends
|
|
5943
|
+
var BaichuanWebRTCServer = class extends EventEmitter6 {
|
|
5511
5944
|
options;
|
|
5512
5945
|
sessions = /* @__PURE__ */ new Map();
|
|
5513
5946
|
sessionIdCounter = 0;
|
|
@@ -6409,7 +6842,7 @@ Error: ${err}`
|
|
|
6409
6842
|
};
|
|
6410
6843
|
|
|
6411
6844
|
// src/baichuan/stream/BaichuanHlsServer.ts
|
|
6412
|
-
import { EventEmitter as
|
|
6845
|
+
import { EventEmitter as EventEmitter7 } from "events";
|
|
6413
6846
|
import fs from "fs";
|
|
6414
6847
|
import fsp from "fs/promises";
|
|
6415
6848
|
import os from "os";
|
|
@@ -6486,7 +6919,7 @@ function getNalTypes(codec, annexB) {
|
|
|
6486
6919
|
}
|
|
6487
6920
|
});
|
|
6488
6921
|
}
|
|
6489
|
-
var BaichuanHlsServer = class extends
|
|
6922
|
+
var BaichuanHlsServer = class extends EventEmitter7 {
|
|
6490
6923
|
api;
|
|
6491
6924
|
channel;
|
|
6492
6925
|
profile;
|
|
@@ -6904,10 +7337,10 @@ var BaichuanHlsServer = class extends EventEmitter6 {
|
|
|
6904
7337
|
};
|
|
6905
7338
|
|
|
6906
7339
|
// src/multifocal/compositeRtspServer.ts
|
|
6907
|
-
import { EventEmitter as
|
|
7340
|
+
import { EventEmitter as EventEmitter8 } from "events";
|
|
6908
7341
|
import { spawn as spawn8 } from "child_process";
|
|
6909
|
-
import * as
|
|
6910
|
-
var CompositeRtspServer = class extends
|
|
7342
|
+
import * as net2 from "net";
|
|
7343
|
+
var CompositeRtspServer = class extends EventEmitter8 {
|
|
6911
7344
|
options;
|
|
6912
7345
|
compositeStream = null;
|
|
6913
7346
|
rtspServer = null;
|
|
@@ -6973,7 +7406,7 @@ var CompositeRtspServer = class extends EventEmitter7 {
|
|
|
6973
7406
|
const width = widerStreamInfo?.width ?? 1920;
|
|
6974
7407
|
const height = widerStreamInfo?.height ?? 1080;
|
|
6975
7408
|
const fps = widerStreamInfo?.frameRate ?? 25;
|
|
6976
|
-
this.rtspServer =
|
|
7409
|
+
this.rtspServer = net2.createServer((socket) => {
|
|
6977
7410
|
this.handleRtspConnection(socket);
|
|
6978
7411
|
});
|
|
6979
7412
|
await new Promise((resolve, reject) => {
|
|
@@ -7248,6 +7681,7 @@ export {
|
|
|
7248
7681
|
DUAL_LENS_DUAL_MOTION_MODELS,
|
|
7249
7682
|
DUAL_LENS_MODELS,
|
|
7250
7683
|
DUAL_LENS_SINGLE_MOTION_MODELS,
|
|
7684
|
+
Go2rtcTcpServer,
|
|
7251
7685
|
H264RtpDepacketizer,
|
|
7252
7686
|
H265RtpDepacketizer,
|
|
7253
7687
|
HlsSessionManager,
|
|
@@ -7315,7 +7749,11 @@ export {
|
|
|
7315
7749
|
detectIosClient,
|
|
7316
7750
|
detectVideoCodecFromNal,
|
|
7317
7751
|
discoverReolinkDevices,
|
|
7752
|
+
discoverViaArpTable,
|
|
7753
|
+
discoverViaDhcpListener,
|
|
7318
7754
|
discoverViaHttpScan,
|
|
7755
|
+
discoverViaOnvif,
|
|
7756
|
+
discoverViaTcpPortScan,
|
|
7319
7757
|
discoverViaUdpBroadcast,
|
|
7320
7758
|
discoverViaUdpDirect,
|
|
7321
7759
|
encodeHeader,
|