@apocaliss92/nodelink-js 0.6.3 → 0.6.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 +26 -0
- package/dist/{chunk-Q4AXRV2G.js → chunk-WQ2TQCYP.js} +539 -31
- package/dist/chunk-WQ2TQCYP.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +554 -43
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +645 -82
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +233 -48
- package/dist/index.d.ts +195 -2
- package/dist/index.js +61 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-Q4AXRV2G.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -7678,12 +7678,12 @@ var init_ReolinkCgiApi = __esm({
|
|
|
7678
7678
|
"getVideoclipThumbnailJpeg",
|
|
7679
7679
|
`Extracting thumbnail from VOD URL (FLV): ${vodUrl.substring(0, 100)}... (seek=${seekSeconds}s)`
|
|
7680
7680
|
);
|
|
7681
|
-
const { spawn:
|
|
7681
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
7682
7682
|
return new Promise((resolve, reject) => {
|
|
7683
7683
|
const chunks = [];
|
|
7684
7684
|
let stderr = "";
|
|
7685
7685
|
let timedOut = false;
|
|
7686
|
-
const ffmpeg =
|
|
7686
|
+
const ffmpeg = spawn14(ffmpegPath, [
|
|
7687
7687
|
"-y",
|
|
7688
7688
|
"-analyzeduration",
|
|
7689
7689
|
"10000000",
|
|
@@ -8267,7 +8267,9 @@ var init_ReolinkCgiApi = __esm({
|
|
|
8267
8267
|
var index_exports = {};
|
|
8268
8268
|
__export(index_exports, {
|
|
8269
8269
|
ALL_UDP_DISCOVERY_METHODS: () => ALL_UDP_DISCOVERY_METHODS,
|
|
8270
|
+
ALWAYS_ON_DEFAULTS: () => ALWAYS_ON_DEFAULTS,
|
|
8270
8271
|
AesStreamDecryptor: () => AesStreamDecryptor,
|
|
8272
|
+
AlwaysOnController: () => AlwaysOnController,
|
|
8271
8273
|
AutodiscoveryClient: () => AutodiscoveryClient,
|
|
8272
8274
|
BC_AES_IV: () => BC_AES_IV,
|
|
8273
8275
|
BC_CLASS_FILE_DOWNLOAD: () => BC_CLASS_FILE_DOWNLOAD,
|
|
@@ -8424,6 +8426,7 @@ __export(index_exports, {
|
|
|
8424
8426
|
BcUdpStream: () => BcUdpStream,
|
|
8425
8427
|
CompositeRtspServer: () => CompositeRtspServer,
|
|
8426
8428
|
CompositeStream: () => CompositeStream,
|
|
8429
|
+
ContinuousVideoStream: () => ContinuousVideoStream,
|
|
8427
8430
|
DEFAULT_SHELTER_CANVAS: () => DEFAULT_SHELTER_CANVAS,
|
|
8428
8431
|
DUAL_LENS_DUAL_MOTION_MODELS: () => DUAL_LENS_DUAL_MOTION_MODELS,
|
|
8429
8432
|
DUAL_LENS_MODELS: () => DUAL_LENS_MODELS,
|
|
@@ -8437,6 +8440,7 @@ __export(index_exports, {
|
|
|
8437
8440
|
MpegTsMuxer: () => MpegTsMuxer,
|
|
8438
8441
|
NVR_HUB_EXACT_TYPES: () => NVR_HUB_EXACT_TYPES,
|
|
8439
8442
|
NVR_HUB_MODEL_PATTERNS: () => NVR_HUB_MODEL_PATTERNS,
|
|
8443
|
+
PlaceholderRenderer: () => PlaceholderRenderer,
|
|
8440
8444
|
ReolinkBaichuanApi: () => ReolinkBaichuanApi,
|
|
8441
8445
|
ReolinkCgiApi: () => ReolinkCgiApi,
|
|
8442
8446
|
ReolinkHttpClient: () => ReolinkHttpClient,
|
|
@@ -9126,6 +9130,7 @@ function readCache(uid, now) {
|
|
|
9126
9130
|
}
|
|
9127
9131
|
async function getServerBinding(uid, options = {}) {
|
|
9128
9132
|
if (!uid || typeof uid !== "string") return void 0;
|
|
9133
|
+
uid = uid.toUpperCase();
|
|
9129
9134
|
const now = Date.now();
|
|
9130
9135
|
const cached = readCache(uid, now);
|
|
9131
9136
|
if (cached?.kind === "ok") return cached.response;
|
|
@@ -14067,14 +14072,14 @@ init_ReolinkHttpClient();
|
|
|
14067
14072
|
init_ReolinkCgiApi();
|
|
14068
14073
|
|
|
14069
14074
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
14070
|
-
var
|
|
14075
|
+
var import_node_child_process4 = require("child_process");
|
|
14071
14076
|
var import_promises2 = require("fs/promises");
|
|
14072
14077
|
var import_node_path2 = require("path");
|
|
14073
14078
|
var import_node_stream = require("stream");
|
|
14074
14079
|
|
|
14075
14080
|
// src/baichuan/stream/BaichuanRtspServer.ts
|
|
14076
|
-
var
|
|
14077
|
-
var
|
|
14081
|
+
var import_node_events5 = require("events");
|
|
14082
|
+
var import_node_child_process3 = require("child_process");
|
|
14078
14083
|
var net2 = __toESM(require("net"), 1);
|
|
14079
14084
|
var dgram2 = __toESM(require("dgram"), 1);
|
|
14080
14085
|
var crypto = __toESM(require("crypto"), 1);
|
|
@@ -14435,6 +14440,358 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
14435
14440
|
}
|
|
14436
14441
|
}
|
|
14437
14442
|
|
|
14443
|
+
// src/baichuan/stream/BaichuanRtspServer.ts
|
|
14444
|
+
init_BaichuanVideoStream();
|
|
14445
|
+
|
|
14446
|
+
// src/baichuan/stream/ContinuousVideoStream.ts
|
|
14447
|
+
var import_node_events4 = require("events");
|
|
14448
|
+
|
|
14449
|
+
// src/baichuan/stream/PlaceholderRenderer.ts
|
|
14450
|
+
var import_node_child_process2 = require("child_process");
|
|
14451
|
+
var import_jimp = require("jimp");
|
|
14452
|
+
var import_fonts = require("jimp/fonts");
|
|
14453
|
+
|
|
14454
|
+
// src/baichuan/stream/alwaysOnTypes.ts
|
|
14455
|
+
var ALWAYS_ON_DEFAULTS = {
|
|
14456
|
+
triggers: ["motion", "doorbell"],
|
|
14457
|
+
windowMs: 15e3,
|
|
14458
|
+
idleFps: 1,
|
|
14459
|
+
primeOnStart: true,
|
|
14460
|
+
placeholder: { enabled: true, text: "Sleeping", opacity: 0.5 }
|
|
14461
|
+
};
|
|
14462
|
+
|
|
14463
|
+
// src/baichuan/stream/PlaceholderRenderer.ts
|
|
14464
|
+
function ffmpegCodec(videoType) {
|
|
14465
|
+
if (videoType === "H265") {
|
|
14466
|
+
return {
|
|
14467
|
+
inputFormat: "hevc",
|
|
14468
|
+
encoder: "libx265",
|
|
14469
|
+
outputFormat: "hevc"
|
|
14470
|
+
};
|
|
14471
|
+
}
|
|
14472
|
+
return {
|
|
14473
|
+
inputFormat: "h264",
|
|
14474
|
+
encoder: "libx264",
|
|
14475
|
+
outputFormat: "h264"
|
|
14476
|
+
};
|
|
14477
|
+
}
|
|
14478
|
+
function runFfmpeg(args, input) {
|
|
14479
|
+
return new Promise((resolve, reject) => {
|
|
14480
|
+
const proc = (0, import_node_child_process2.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
14481
|
+
const stdoutChunks = [];
|
|
14482
|
+
const stderrChunks = [];
|
|
14483
|
+
proc.stdout?.on("data", (chunk) => stdoutChunks.push(chunk));
|
|
14484
|
+
proc.stderr?.on("data", (chunk) => stderrChunks.push(chunk));
|
|
14485
|
+
proc.on("error", (error) => reject(error));
|
|
14486
|
+
proc.on("close", (code) => {
|
|
14487
|
+
if (code === 0) {
|
|
14488
|
+
resolve(Buffer.concat(stdoutChunks));
|
|
14489
|
+
return;
|
|
14490
|
+
}
|
|
14491
|
+
const stderr = Buffer.concat(stderrChunks).toString("utf8").trim();
|
|
14492
|
+
reject(new Error(`ffmpeg exited with code ${code}: ${stderr}`));
|
|
14493
|
+
});
|
|
14494
|
+
const stdin = proc.stdin;
|
|
14495
|
+
if (!stdin) {
|
|
14496
|
+
reject(new Error("ffmpeg stdin not available"));
|
|
14497
|
+
return;
|
|
14498
|
+
}
|
|
14499
|
+
stdin.on("error", (error) => reject(error));
|
|
14500
|
+
stdin.end(input);
|
|
14501
|
+
});
|
|
14502
|
+
}
|
|
14503
|
+
var PlaceholderRenderer = class {
|
|
14504
|
+
opts;
|
|
14505
|
+
logger;
|
|
14506
|
+
constructor(args) {
|
|
14507
|
+
this.opts = { ...ALWAYS_ON_DEFAULTS.placeholder, ...args.placeholder ?? {} };
|
|
14508
|
+
this.logger = args.logger;
|
|
14509
|
+
}
|
|
14510
|
+
/** Returns the access unit bytes to emit as placeholder, or null if none available. */
|
|
14511
|
+
async render(keyframe) {
|
|
14512
|
+
if (!keyframe) return null;
|
|
14513
|
+
if (!this.opts.enabled) return keyframe.data;
|
|
14514
|
+
try {
|
|
14515
|
+
const jpeg = await this.decodeToJpeg(keyframe);
|
|
14516
|
+
const decorated = await this.decorate(jpeg);
|
|
14517
|
+
const idr = await this.encodeIdr(decorated, keyframe.videoType);
|
|
14518
|
+
if (!idr || idr.length === 0) {
|
|
14519
|
+
throw new Error("ffmpeg produced empty IDR output");
|
|
14520
|
+
}
|
|
14521
|
+
return idr;
|
|
14522
|
+
} catch (error) {
|
|
14523
|
+
this.logger?.warn?.(
|
|
14524
|
+
"PlaceholderRenderer: decoration failed, falling back to raw keyframe",
|
|
14525
|
+
error instanceof Error ? error.message : error
|
|
14526
|
+
);
|
|
14527
|
+
return keyframe.data;
|
|
14528
|
+
}
|
|
14529
|
+
}
|
|
14530
|
+
/** Decodes the cached keyframe access unit into a single JPEG still via ffmpeg. */
|
|
14531
|
+
async decodeToJpeg(keyframe) {
|
|
14532
|
+
const { inputFormat } = ffmpegCodec(keyframe.videoType);
|
|
14533
|
+
return runFfmpeg(
|
|
14534
|
+
[
|
|
14535
|
+
"-hide_banner",
|
|
14536
|
+
"-loglevel",
|
|
14537
|
+
"error",
|
|
14538
|
+
"-f",
|
|
14539
|
+
inputFormat,
|
|
14540
|
+
"-i",
|
|
14541
|
+
"pipe:0",
|
|
14542
|
+
"-frames:v",
|
|
14543
|
+
"1",
|
|
14544
|
+
"-f",
|
|
14545
|
+
"mjpeg",
|
|
14546
|
+
"pipe:1"
|
|
14547
|
+
],
|
|
14548
|
+
keyframe.data
|
|
14549
|
+
);
|
|
14550
|
+
}
|
|
14551
|
+
/** Dims the still and prints the overlay text using jimp, returning a JPEG buffer. */
|
|
14552
|
+
async decorate(jpeg) {
|
|
14553
|
+
const image = await import_jimp.Jimp.read(jpeg);
|
|
14554
|
+
const op = Math.max(0, Math.min(1, this.opts.opacity));
|
|
14555
|
+
if (op < 1) {
|
|
14556
|
+
const data = image.bitmap.data;
|
|
14557
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
14558
|
+
data[i] = data[i] * op;
|
|
14559
|
+
data[i + 1] = data[i + 1] * op;
|
|
14560
|
+
data[i + 2] = data[i + 2] * op;
|
|
14561
|
+
}
|
|
14562
|
+
}
|
|
14563
|
+
const fontDef = image.width >= 1280 ? import_fonts.SANS_128_WHITE : image.width >= 640 ? import_fonts.SANS_64_WHITE : import_fonts.SANS_32_WHITE;
|
|
14564
|
+
const font = await (0, import_jimp.loadFont)(fontDef);
|
|
14565
|
+
const text = this.opts.text;
|
|
14566
|
+
const textWidth = (0, import_jimp.measureText)(font, text);
|
|
14567
|
+
const textHeight = (0, import_jimp.measureTextHeight)(font, text, image.width);
|
|
14568
|
+
const x = Math.max(0, Math.round((image.width - textWidth) / 2));
|
|
14569
|
+
const y = Math.max(0, Math.round((image.height - textHeight) / 2));
|
|
14570
|
+
image.print({ font, x, y, text });
|
|
14571
|
+
return image.getBuffer(import_jimp.JimpMime.jpeg);
|
|
14572
|
+
}
|
|
14573
|
+
/** Encodes the decorated JPEG into a single IDR access unit in the target codec. */
|
|
14574
|
+
async encodeIdr(jpeg, videoType) {
|
|
14575
|
+
const { encoder, outputFormat } = ffmpegCodec(videoType);
|
|
14576
|
+
return runFfmpeg(
|
|
14577
|
+
[
|
|
14578
|
+
"-hide_banner",
|
|
14579
|
+
"-loglevel",
|
|
14580
|
+
"error",
|
|
14581
|
+
"-f",
|
|
14582
|
+
"image2pipe",
|
|
14583
|
+
"-i",
|
|
14584
|
+
"pipe:0",
|
|
14585
|
+
"-frames:v",
|
|
14586
|
+
"1",
|
|
14587
|
+
"-c:v",
|
|
14588
|
+
encoder,
|
|
14589
|
+
"-pix_fmt",
|
|
14590
|
+
"yuv420p",
|
|
14591
|
+
"-f",
|
|
14592
|
+
outputFormat,
|
|
14593
|
+
"pipe:1"
|
|
14594
|
+
],
|
|
14595
|
+
jpeg
|
|
14596
|
+
);
|
|
14597
|
+
}
|
|
14598
|
+
};
|
|
14599
|
+
|
|
14600
|
+
// src/baichuan/stream/ContinuousVideoStream.ts
|
|
14601
|
+
var ContinuousVideoStream = class extends import_node_events4.EventEmitter {
|
|
14602
|
+
constructor(opts) {
|
|
14603
|
+
super();
|
|
14604
|
+
this.opts = opts;
|
|
14605
|
+
this.idleFps = Math.max(0.1, opts.idleFps ?? ALWAYS_ON_DEFAULTS.idleFps);
|
|
14606
|
+
this.logger = opts.logger;
|
|
14607
|
+
const rendererArgs = {};
|
|
14608
|
+
if (opts.placeholder !== void 0) rendererArgs.placeholder = opts.placeholder;
|
|
14609
|
+
if (opts.logger !== void 0) rendererArgs.logger = opts.logger;
|
|
14610
|
+
this.renderer = opts.renderer ?? new PlaceholderRenderer(rendererArgs);
|
|
14611
|
+
}
|
|
14612
|
+
live = null;
|
|
14613
|
+
lastKeyframe = null;
|
|
14614
|
+
lastMicroseconds = 0;
|
|
14615
|
+
idleFps;
|
|
14616
|
+
renderer;
|
|
14617
|
+
logger;
|
|
14618
|
+
stopped = false;
|
|
14619
|
+
starting = false;
|
|
14620
|
+
idleTimer = null;
|
|
14621
|
+
idlePlaceholder = null;
|
|
14622
|
+
hasCachedKeyframe() {
|
|
14623
|
+
return this.lastKeyframe !== null;
|
|
14624
|
+
}
|
|
14625
|
+
async goLive() {
|
|
14626
|
+
if (this.stopped || this.live || this.starting) return;
|
|
14627
|
+
this.starting = true;
|
|
14628
|
+
try {
|
|
14629
|
+
this.stopIdleLoop();
|
|
14630
|
+
const stream = await this.opts.createLiveStream();
|
|
14631
|
+
this.live = stream;
|
|
14632
|
+
stream.on("videoAccessUnit", this.onLiveAccessUnit);
|
|
14633
|
+
stream.on("additionalHeader", this.onAdditionalHeader);
|
|
14634
|
+
stream.on("audioFrame", this.onAudioFrame);
|
|
14635
|
+
stream.on("error", this.onLiveError);
|
|
14636
|
+
await stream.start().catch((e) => this.emit("error", e));
|
|
14637
|
+
} finally {
|
|
14638
|
+
this.starting = false;
|
|
14639
|
+
}
|
|
14640
|
+
}
|
|
14641
|
+
async goIdle() {
|
|
14642
|
+
if (!this.live) return;
|
|
14643
|
+
const s = this.live;
|
|
14644
|
+
this.live = null;
|
|
14645
|
+
s.off("videoAccessUnit", this.onLiveAccessUnit);
|
|
14646
|
+
s.off("additionalHeader", this.onAdditionalHeader);
|
|
14647
|
+
s.off("audioFrame", this.onAudioFrame);
|
|
14648
|
+
s.off("error", this.onLiveError);
|
|
14649
|
+
await s.stop().catch(() => {
|
|
14650
|
+
});
|
|
14651
|
+
await this.startIdleLoop();
|
|
14652
|
+
}
|
|
14653
|
+
async stop() {
|
|
14654
|
+
this.stopped = true;
|
|
14655
|
+
await this.goIdle();
|
|
14656
|
+
this.stopIdleLoop();
|
|
14657
|
+
this.emit("close");
|
|
14658
|
+
}
|
|
14659
|
+
async startIdleLoop() {
|
|
14660
|
+
if (this.stopped) return;
|
|
14661
|
+
this.idlePlaceholder = await this.renderer.render(this.lastKeyframe);
|
|
14662
|
+
if (!this.idlePlaceholder || !this.lastKeyframe) {
|
|
14663
|
+
this.logger?.debug?.("[ContinuousVideoStream] no keyframe yet; idle loop deferred");
|
|
14664
|
+
return;
|
|
14665
|
+
}
|
|
14666
|
+
const stepUs = Math.round(1e6 / this.idleFps);
|
|
14667
|
+
const videoType = this.lastKeyframe.videoType;
|
|
14668
|
+
this.idleTimer = setInterval(() => {
|
|
14669
|
+
if (!this.idlePlaceholder) return;
|
|
14670
|
+
this.lastMicroseconds += stepUs;
|
|
14671
|
+
this.emit("videoAccessUnit", {
|
|
14672
|
+
data: this.idlePlaceholder,
|
|
14673
|
+
isKeyframe: true,
|
|
14674
|
+
videoType,
|
|
14675
|
+
microseconds: this.lastMicroseconds
|
|
14676
|
+
});
|
|
14677
|
+
}, Math.round(1e3 / this.idleFps));
|
|
14678
|
+
}
|
|
14679
|
+
stopIdleLoop() {
|
|
14680
|
+
if (this.idleTimer) {
|
|
14681
|
+
clearInterval(this.idleTimer);
|
|
14682
|
+
this.idleTimer = null;
|
|
14683
|
+
}
|
|
14684
|
+
this.idlePlaceholder = null;
|
|
14685
|
+
}
|
|
14686
|
+
onLiveAccessUnit = (au) => {
|
|
14687
|
+
if (au.isKeyframe) {
|
|
14688
|
+
this.lastKeyframe = { data: au.data, videoType: au.videoType };
|
|
14689
|
+
}
|
|
14690
|
+
this.lastMicroseconds = au.microseconds;
|
|
14691
|
+
this.emit("videoAccessUnit", au);
|
|
14692
|
+
};
|
|
14693
|
+
onAdditionalHeader = (h) => this.emit("additionalHeader", h);
|
|
14694
|
+
onAudioFrame = (a) => this.emit("audioFrame", a);
|
|
14695
|
+
onLiveError = (e) => this.emit("error", e);
|
|
14696
|
+
};
|
|
14697
|
+
|
|
14698
|
+
// src/baichuan/stream/AlwaysOnController.ts
|
|
14699
|
+
var AlwaysOnController = class {
|
|
14700
|
+
constructor(o) {
|
|
14701
|
+
this.o = o;
|
|
14702
|
+
this.triggers = new Set(o.options.triggers ?? ALWAYS_ON_DEFAULTS.triggers);
|
|
14703
|
+
this.windowMs = o.options.windowMs ?? ALWAYS_ON_DEFAULTS.windowMs;
|
|
14704
|
+
this.primeOnStart = o.options.primeOnStart ?? ALWAYS_ON_DEFAULTS.primeOnStart;
|
|
14705
|
+
this.logger = o.logger;
|
|
14706
|
+
}
|
|
14707
|
+
triggers;
|
|
14708
|
+
windowMs;
|
|
14709
|
+
primeOnStart;
|
|
14710
|
+
logger;
|
|
14711
|
+
windowTimer = null;
|
|
14712
|
+
live = false;
|
|
14713
|
+
started = false;
|
|
14714
|
+
handler = (e) => void this.onEvent(e);
|
|
14715
|
+
get windowSeconds() {
|
|
14716
|
+
return Math.round(this.windowMs / 1e3);
|
|
14717
|
+
}
|
|
14718
|
+
async start() {
|
|
14719
|
+
if (this.started) return;
|
|
14720
|
+
this.started = true;
|
|
14721
|
+
await this.o.api.onSimpleEvent(this.handler);
|
|
14722
|
+
this.logger?.info?.(
|
|
14723
|
+
`[AlwaysOnController] started ch${this.o.channel} \u2014 triggers=[${[...this.triggers].join(", ")}], window=${this.windowSeconds}s, primeOnStart=${this.primeOnStart}`
|
|
14724
|
+
);
|
|
14725
|
+
if (this.primeOnStart) {
|
|
14726
|
+
await this.openWindow("prime");
|
|
14727
|
+
}
|
|
14728
|
+
}
|
|
14729
|
+
async stop() {
|
|
14730
|
+
if (!this.started) return;
|
|
14731
|
+
this.started = false;
|
|
14732
|
+
if (this.windowTimer) {
|
|
14733
|
+
clearTimeout(this.windowTimer);
|
|
14734
|
+
this.windowTimer = null;
|
|
14735
|
+
}
|
|
14736
|
+
await this.o.api.offSimpleEvent(this.handler).catch(() => {
|
|
14737
|
+
});
|
|
14738
|
+
if (this.live) {
|
|
14739
|
+
this.live = false;
|
|
14740
|
+
await this.o.goIdle().catch(() => {
|
|
14741
|
+
});
|
|
14742
|
+
}
|
|
14743
|
+
this.logger?.info?.(`[AlwaysOnController] stopped ch${this.o.channel}`);
|
|
14744
|
+
}
|
|
14745
|
+
async onEvent(e) {
|
|
14746
|
+
if (e.channel !== this.o.channel) return;
|
|
14747
|
+
if (!this.triggers.has(e.type)) {
|
|
14748
|
+
this.logger?.debug?.(
|
|
14749
|
+
`[AlwaysOnController] event '${e.type}' ch${e.channel} ignored (not a configured trigger)`
|
|
14750
|
+
);
|
|
14751
|
+
return;
|
|
14752
|
+
}
|
|
14753
|
+
await this.openWindow(e.type);
|
|
14754
|
+
}
|
|
14755
|
+
async openWindow(reason) {
|
|
14756
|
+
if (this.windowTimer) clearTimeout(this.windowTimer);
|
|
14757
|
+
if (!this.live) {
|
|
14758
|
+
this.live = true;
|
|
14759
|
+
try {
|
|
14760
|
+
await this.o.api.wakeUp(this.o.channel).catch(() => {
|
|
14761
|
+
});
|
|
14762
|
+
await this.o.goLive();
|
|
14763
|
+
this.logger?.info?.(
|
|
14764
|
+
`[AlwaysOnController] live window OPENED (trigger=${reason}) \u2014 streaming real frames; will sleep in ${this.windowSeconds}s without new events`
|
|
14765
|
+
);
|
|
14766
|
+
} catch (err) {
|
|
14767
|
+
this.live = false;
|
|
14768
|
+
this.logger?.warn?.(
|
|
14769
|
+
`[AlwaysOnController] goLive failed: ${err?.message}`
|
|
14770
|
+
);
|
|
14771
|
+
return;
|
|
14772
|
+
}
|
|
14773
|
+
} else {
|
|
14774
|
+
this.logger?.info?.(
|
|
14775
|
+
`[AlwaysOnController] live window EXTENDED (trigger=${reason}) \u2014 sleep timer reset to ${this.windowSeconds}s`
|
|
14776
|
+
);
|
|
14777
|
+
}
|
|
14778
|
+
this.windowTimer = setTimeout(() => void this.closeWindow(), this.windowMs);
|
|
14779
|
+
}
|
|
14780
|
+
async closeWindow() {
|
|
14781
|
+
this.windowTimer = null;
|
|
14782
|
+
if (!this.live) return;
|
|
14783
|
+
this.live = false;
|
|
14784
|
+
this.logger?.info?.(
|
|
14785
|
+
`[AlwaysOnController] live window CLOSED \u2014 going idle (placeholder); camera can sleep`
|
|
14786
|
+
);
|
|
14787
|
+
await this.o.goIdle().catch(
|
|
14788
|
+
(err) => this.logger?.warn?.(
|
|
14789
|
+
`[AlwaysOnController] goIdle failed: ${err?.message}`
|
|
14790
|
+
)
|
|
14791
|
+
);
|
|
14792
|
+
}
|
|
14793
|
+
};
|
|
14794
|
+
|
|
14438
14795
|
// src/baichuan/stream/rtspFlow.ts
|
|
14439
14796
|
init_H264Converter();
|
|
14440
14797
|
init_H265Converter();
|
|
@@ -14661,7 +15018,7 @@ function envBool(value, defaultValue) {
|
|
|
14661
15018
|
if (v === "0" || v === "false" || v === "no" || v === "off") return false;
|
|
14662
15019
|
return defaultValue;
|
|
14663
15020
|
}
|
|
14664
|
-
var BaichuanRtspServer = class _BaichuanRtspServer extends
|
|
15021
|
+
var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events5.EventEmitter {
|
|
14665
15022
|
api;
|
|
14666
15023
|
channel;
|
|
14667
15024
|
profile;
|
|
@@ -14676,6 +15033,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14676
15033
|
deviceId;
|
|
14677
15034
|
dedicatedSessionRelease;
|
|
14678
15035
|
externalListener;
|
|
15036
|
+
// Always-on continuous stream (battery cameras). Populated only when
|
|
15037
|
+
// `options.alwaysOn?.enabled`; the default (non-alwaysOn) path leaves these
|
|
15038
|
+
// null/undefined and is byte-for-byte equivalent in behaviour.
|
|
15039
|
+
alwaysOnOptions;
|
|
15040
|
+
continuousStream = null;
|
|
15041
|
+
alwaysOnController = null;
|
|
14679
15042
|
// Authentication
|
|
14680
15043
|
authCredentials = [];
|
|
14681
15044
|
requireAuth;
|
|
@@ -14690,6 +15053,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14690
15053
|
// Set of client IDs (IP:port)
|
|
14691
15054
|
nativeStreamActive = false;
|
|
14692
15055
|
// Whether the native stream is currently active
|
|
15056
|
+
tearingDown = false;
|
|
15057
|
+
// True while stop() is running; suppresses onEnd-driven restarts
|
|
14693
15058
|
clientConnectionServer;
|
|
14694
15059
|
// TCP server to track connections
|
|
14695
15060
|
streamMetadata = null;
|
|
@@ -14877,6 +15242,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14877
15242
|
this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
|
|
14878
15243
|
this.AUTH_REALM = options.authRealm ?? "BaichuanRtspServer";
|
|
14879
15244
|
this.lazyMetadata = options.lazyMetadata ?? false;
|
|
15245
|
+
this.alwaysOnOptions = options.alwaysOn;
|
|
14880
15246
|
const transport = this.api.client.getTransport();
|
|
14881
15247
|
this.flow = createRtspFlow(transport, "H264");
|
|
14882
15248
|
}
|
|
@@ -16000,7 +16366,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16000
16366
|
this.rtspDebugLog(
|
|
16001
16367
|
`Spawning ffmpeg for client ${clientId}: ffmpeg ${ffmpegArgs.join(" ")}`
|
|
16002
16368
|
);
|
|
16003
|
-
ffmpeg = (0,
|
|
16369
|
+
ffmpeg = (0, import_node_child_process3.spawn)("ffmpeg", ffmpegArgs, {
|
|
16004
16370
|
stdio
|
|
16005
16371
|
});
|
|
16006
16372
|
try {
|
|
@@ -16364,6 +16730,141 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16364
16730
|
}
|
|
16365
16731
|
});
|
|
16366
16732
|
}
|
|
16733
|
+
/**
|
|
16734
|
+
* Always-on source: bridge a {@link ContinuousVideoStream} into the existing
|
|
16735
|
+
* fanout. Yields the same frame shape that `createNativeStream` produces, so
|
|
16736
|
+
* the rest of the pipeline (prebuffer, param-set extraction, per-client
|
|
16737
|
+
* subscribe, ffmpeg/direct-RTP) is unchanged.
|
|
16738
|
+
*
|
|
16739
|
+
* The CVS itself is long-lived (created once, reused across native-stream
|
|
16740
|
+
* restarts) and is driven by the {@link AlwaysOnController}, which opens/closes
|
|
16741
|
+
* live windows from camera events. Each fanout source generator only forwards
|
|
16742
|
+
* CVS events to the fanout pump for as long as `signal` is not aborted.
|
|
16743
|
+
*/
|
|
16744
|
+
async *createContinuousSource(dedicatedClient, signal) {
|
|
16745
|
+
const cvs = this.ensureContinuousStream(dedicatedClient);
|
|
16746
|
+
const queue = [];
|
|
16747
|
+
const MAX_QUEUE = 200;
|
|
16748
|
+
let wake = null;
|
|
16749
|
+
let done = false;
|
|
16750
|
+
const push = (frame) => {
|
|
16751
|
+
queue.push(frame);
|
|
16752
|
+
if (queue.length > MAX_QUEUE) {
|
|
16753
|
+
queue.splice(0, queue.length - MAX_QUEUE);
|
|
16754
|
+
}
|
|
16755
|
+
if (wake) {
|
|
16756
|
+
const w = wake;
|
|
16757
|
+
wake = null;
|
|
16758
|
+
w();
|
|
16759
|
+
}
|
|
16760
|
+
};
|
|
16761
|
+
const onVideo = (au) => {
|
|
16762
|
+
push({
|
|
16763
|
+
audio: false,
|
|
16764
|
+
data: au.data,
|
|
16765
|
+
codec: null,
|
|
16766
|
+
sampleRate: null,
|
|
16767
|
+
microseconds: au.microseconds,
|
|
16768
|
+
videoType: au.videoType,
|
|
16769
|
+
isKeyframe: au.isKeyframe
|
|
16770
|
+
});
|
|
16771
|
+
};
|
|
16772
|
+
const onAudio = (frame) => {
|
|
16773
|
+
push({
|
|
16774
|
+
audio: true,
|
|
16775
|
+
data: frame,
|
|
16776
|
+
codec: "aac",
|
|
16777
|
+
sampleRate: 8e3,
|
|
16778
|
+
microseconds: null
|
|
16779
|
+
});
|
|
16780
|
+
};
|
|
16781
|
+
const finish = () => {
|
|
16782
|
+
done = true;
|
|
16783
|
+
if (wake) {
|
|
16784
|
+
const w = wake;
|
|
16785
|
+
wake = null;
|
|
16786
|
+
w();
|
|
16787
|
+
}
|
|
16788
|
+
};
|
|
16789
|
+
const onAbort = () => finish();
|
|
16790
|
+
cvs.on("videoAccessUnit", onVideo);
|
|
16791
|
+
cvs.on("audioFrame", onAudio);
|
|
16792
|
+
cvs.on("close", finish);
|
|
16793
|
+
if (signal.aborted) {
|
|
16794
|
+
done = true;
|
|
16795
|
+
} else {
|
|
16796
|
+
signal.addEventListener("abort", onAbort);
|
|
16797
|
+
}
|
|
16798
|
+
try {
|
|
16799
|
+
while (!done && !signal.aborted) {
|
|
16800
|
+
if (queue.length > 0) {
|
|
16801
|
+
yield queue.shift();
|
|
16802
|
+
} else {
|
|
16803
|
+
await new Promise((resolve) => {
|
|
16804
|
+
wake = resolve;
|
|
16805
|
+
if (done || signal.aborted) {
|
|
16806
|
+
wake = null;
|
|
16807
|
+
resolve();
|
|
16808
|
+
}
|
|
16809
|
+
});
|
|
16810
|
+
}
|
|
16811
|
+
}
|
|
16812
|
+
while (queue.length > 0 && !signal.aborted) {
|
|
16813
|
+
yield queue.shift();
|
|
16814
|
+
}
|
|
16815
|
+
} finally {
|
|
16816
|
+
cvs.off("videoAccessUnit", onVideo);
|
|
16817
|
+
cvs.off("audioFrame", onAudio);
|
|
16818
|
+
cvs.off("close", finish);
|
|
16819
|
+
signal.removeEventListener("abort", onAbort);
|
|
16820
|
+
}
|
|
16821
|
+
}
|
|
16822
|
+
/**
|
|
16823
|
+
* Lazily build the long-lived {@link ContinuousVideoStream} +
|
|
16824
|
+
* {@link AlwaysOnController} for always-on mode. Both are created once and
|
|
16825
|
+
* reused for the lifetime of the server (across native-stream restarts).
|
|
16826
|
+
*/
|
|
16827
|
+
ensureContinuousStream(dedicatedClient) {
|
|
16828
|
+
if (this.continuousStream) return this.continuousStream;
|
|
16829
|
+
const createLiveStream = async () => {
|
|
16830
|
+
const client = dedicatedClient ?? this.api.client;
|
|
16831
|
+
return new BaichuanVideoStream({
|
|
16832
|
+
client,
|
|
16833
|
+
api: this.api,
|
|
16834
|
+
channel: this.channel,
|
|
16835
|
+
profile: this.profile,
|
|
16836
|
+
...this.variant !== "default" ? { variant: this.variant } : {},
|
|
16837
|
+
...this.logger ? { logger: this.logger } : {}
|
|
16838
|
+
});
|
|
16839
|
+
};
|
|
16840
|
+
const cvsOptions = {
|
|
16841
|
+
createLiveStream,
|
|
16842
|
+
...this.alwaysOnOptions?.idleFps !== void 0 ? { idleFps: this.alwaysOnOptions.idleFps } : {},
|
|
16843
|
+
...this.alwaysOnOptions?.placeholder !== void 0 ? { placeholder: this.alwaysOnOptions.placeholder } : {},
|
|
16844
|
+
...this.logger ? { logger: this.logger } : {}
|
|
16845
|
+
};
|
|
16846
|
+
const cvs = new ContinuousVideoStream(cvsOptions);
|
|
16847
|
+
cvs.on("error", (e) => {
|
|
16848
|
+
this.logger.warn(
|
|
16849
|
+
`[BaichuanRtspServer] ContinuousVideoStream error: ${e?.message ?? e}`
|
|
16850
|
+
);
|
|
16851
|
+
});
|
|
16852
|
+
this.continuousStream = cvs;
|
|
16853
|
+
this.alwaysOnController = new AlwaysOnController({
|
|
16854
|
+
api: this.api,
|
|
16855
|
+
channel: this.channel,
|
|
16856
|
+
options: this.alwaysOnOptions,
|
|
16857
|
+
goLive: () => cvs.goLive(),
|
|
16858
|
+
goIdle: () => cvs.goIdle(),
|
|
16859
|
+
...this.logger ? { logger: this.logger } : {}
|
|
16860
|
+
});
|
|
16861
|
+
void this.alwaysOnController.start().catch((e) => {
|
|
16862
|
+
this.logger.warn(
|
|
16863
|
+
`[BaichuanRtspServer] AlwaysOnController start failed: ${e?.message ?? e}`
|
|
16864
|
+
);
|
|
16865
|
+
});
|
|
16866
|
+
return cvs;
|
|
16867
|
+
}
|
|
16367
16868
|
/**
|
|
16368
16869
|
* Start native stream (mark as active).
|
|
16369
16870
|
* Each client will create its own generator, so we just track that the stream is active.
|
|
@@ -16425,7 +16926,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16425
16926
|
await this.flow.startKeepAlive(this.api);
|
|
16426
16927
|
this.nativeFanout = new NativeStreamFanout({
|
|
16427
16928
|
maxQueueItems: 200,
|
|
16428
|
-
createSource: (signal) => createNativeStream(this.api, this.channel, this.profile, {
|
|
16929
|
+
createSource: (signal) => this.alwaysOnOptions?.enabled ? this.createContinuousSource(dedicatedClient, signal) : createNativeStream(this.api, this.channel, this.profile, {
|
|
16429
16930
|
variant: this.variant,
|
|
16430
16931
|
...dedicatedClient ? { client: dedicatedClient } : {},
|
|
16431
16932
|
signal
|
|
@@ -16508,6 +17009,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16508
17009
|
} catch {
|
|
16509
17010
|
}
|
|
16510
17011
|
}
|
|
17012
|
+
if (this.tearingDown) return;
|
|
16511
17013
|
if (this.connectedClients.size > 0 && hadFrames) {
|
|
16512
17014
|
this.logger.info(
|
|
16513
17015
|
`[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
|
|
@@ -16521,7 +17023,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16521
17023
|
});
|
|
16522
17024
|
this.nativeFanout.start();
|
|
16523
17025
|
this.clearNoFrameDeadlineTimer();
|
|
16524
|
-
if (this.nativeStreamNoFrameDeadlineMs > 0) {
|
|
17026
|
+
if (this.nativeStreamNoFrameDeadlineMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16525
17027
|
this.noFrameDeadlineTimer = setTimeout(() => {
|
|
16526
17028
|
this.noFrameDeadlineTimer = void 0;
|
|
16527
17029
|
if (!this.firstFrameReceived && this.nativeStreamActive) {
|
|
@@ -16534,7 +17036,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16534
17036
|
this.noFrameDeadlineTimer?.unref?.();
|
|
16535
17037
|
}
|
|
16536
17038
|
this.clearNoClientAutoStopTimer();
|
|
16537
|
-
if (this.nativeStreamPrimeIdleStopMs > 0) {
|
|
17039
|
+
if (this.nativeStreamPrimeIdleStopMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16538
17040
|
this.noClientAutoStopTimer = setTimeout(() => {
|
|
16539
17041
|
if (this.connectedClients.size === 0) {
|
|
16540
17042
|
this.rtspDebugLog(
|
|
@@ -16621,7 +17123,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16621
17123
|
this.emit("clientDisconnected", clientId);
|
|
16622
17124
|
if (this.connectedClients.size === 0) {
|
|
16623
17125
|
this.clearNoClientAutoStopTimer();
|
|
16624
|
-
if (this.nativeStreamIdleStopMs > 0) {
|
|
17126
|
+
if (this.nativeStreamIdleStopMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16625
17127
|
this.noClientAutoStopTimer = setTimeout(() => {
|
|
16626
17128
|
if (this.connectedClients.size === 0) {
|
|
16627
17129
|
void this.stopNativeStream();
|
|
@@ -16692,9 +17194,22 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16692
17194
|
if (!this.active) {
|
|
16693
17195
|
return;
|
|
16694
17196
|
}
|
|
17197
|
+
this.tearingDown = true;
|
|
16695
17198
|
this.logger.info(
|
|
16696
17199
|
`[BaichuanRtspServer] Stopping RTSP server on ${this.listenHost}:${this.listenPort}...`
|
|
16697
17200
|
);
|
|
17201
|
+
if (this.alwaysOnController) {
|
|
17202
|
+
const controller = this.alwaysOnController;
|
|
17203
|
+
this.alwaysOnController = null;
|
|
17204
|
+
await controller.stop().catch(() => {
|
|
17205
|
+
});
|
|
17206
|
+
}
|
|
17207
|
+
if (this.continuousStream) {
|
|
17208
|
+
const cvs = this.continuousStream;
|
|
17209
|
+
this.continuousStream = null;
|
|
17210
|
+
await cvs.stop().catch(() => {
|
|
17211
|
+
});
|
|
17212
|
+
}
|
|
16698
17213
|
await this.stopNativeStream();
|
|
16699
17214
|
const clientIds = Array.from(this.connectedClients);
|
|
16700
17215
|
for (const clientId of clientIds) {
|
|
@@ -17368,7 +17883,7 @@ function buildSetSystemGeneralXml(patch) {
|
|
|
17368
17883
|
}
|
|
17369
17884
|
|
|
17370
17885
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
17371
|
-
var
|
|
17886
|
+
var import_jimp2 = require("jimp");
|
|
17372
17887
|
init_ReolinkCgiApi();
|
|
17373
17888
|
init_ReolinkHttpClient();
|
|
17374
17889
|
|
|
@@ -20328,8 +20843,8 @@ var parseSirenStatusListPushXml = (xml) => {
|
|
|
20328
20843
|
};
|
|
20329
20844
|
|
|
20330
20845
|
// src/emailPush/bus.ts
|
|
20331
|
-
var
|
|
20332
|
-
var emitter = new
|
|
20846
|
+
var import_node_events6 = require("events");
|
|
20847
|
+
var emitter = new import_node_events6.EventEmitter();
|
|
20333
20848
|
var cameraResolver = () => void 0;
|
|
20334
20849
|
var lastEventByCamera = /* @__PURE__ */ new Map();
|
|
20335
20850
|
var MAX_GLOBAL_EVENTS = 300;
|
|
@@ -24208,12 +24723,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
24208
24723
|
let wideImg;
|
|
24209
24724
|
let teleImg;
|
|
24210
24725
|
try {
|
|
24211
|
-
wideImg = await
|
|
24726
|
+
wideImg = await import_jimp2.Jimp.read(wide);
|
|
24212
24727
|
} catch {
|
|
24213
24728
|
return wide;
|
|
24214
24729
|
}
|
|
24215
24730
|
try {
|
|
24216
|
-
teleImg = await
|
|
24731
|
+
teleImg = await import_jimp2.Jimp.read(tele);
|
|
24217
24732
|
} catch {
|
|
24218
24733
|
return wide;
|
|
24219
24734
|
}
|
|
@@ -24247,7 +24762,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
24247
24762
|
});
|
|
24248
24763
|
teleImg.resize({ w: pipW, h: pipH });
|
|
24249
24764
|
wideImg.composite(teleImg, left, top);
|
|
24250
|
-
return await wideImg.getBuffer(
|
|
24765
|
+
return await wideImg.getBuffer(import_jimp2.JimpMime.jpeg, { quality: 80 });
|
|
24251
24766
|
}
|
|
24252
24767
|
const ch = channel !== void 0 ? this.normalizeChannel(channel) : 0;
|
|
24253
24768
|
const variant = options?.variant ?? "default";
|
|
@@ -25205,7 +25720,7 @@ ${xml}`);
|
|
|
25205
25720
|
const chunks = [];
|
|
25206
25721
|
let stderr = "";
|
|
25207
25722
|
let timedOut = false;
|
|
25208
|
-
const ff = (0,
|
|
25723
|
+
const ff = (0, import_node_child_process4.spawn)(params.ffmpegPath, [
|
|
25209
25724
|
"-hide_banner",
|
|
25210
25725
|
"-loglevel",
|
|
25211
25726
|
"error",
|
|
@@ -25290,7 +25805,7 @@ ${xml}`);
|
|
|
25290
25805
|
const chunks = [];
|
|
25291
25806
|
let stderr = "";
|
|
25292
25807
|
let timedOut = false;
|
|
25293
|
-
const ff = (0,
|
|
25808
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25294
25809
|
"-hide_banner",
|
|
25295
25810
|
"-loglevel",
|
|
25296
25811
|
"error",
|
|
@@ -25406,7 +25921,7 @@ ${xml}`);
|
|
|
25406
25921
|
ensureEnabled: true
|
|
25407
25922
|
});
|
|
25408
25923
|
await new Promise((resolve, reject) => {
|
|
25409
|
-
const ff = (0,
|
|
25924
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25410
25925
|
"-hide_banner",
|
|
25411
25926
|
"-loglevel",
|
|
25412
25927
|
"error",
|
|
@@ -25462,7 +25977,7 @@ ${stderr}`));
|
|
|
25462
25977
|
const atSeconds = Number.isFinite(params.atSeconds) && params.atSeconds >= 0 ? params.atSeconds : 0;
|
|
25463
25978
|
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(params.outputPath), { recursive: true });
|
|
25464
25979
|
await new Promise((resolve, reject) => {
|
|
25465
|
-
const ff = (0,
|
|
25980
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25466
25981
|
"-hide_banner",
|
|
25467
25982
|
"-loglevel",
|
|
25468
25983
|
"error",
|
|
@@ -26027,7 +26542,7 @@ ${stderr}`)
|
|
|
26027
26542
|
* Convert a raw video keyframe to JPEG using ffmpeg.
|
|
26028
26543
|
*/
|
|
26029
26544
|
async convertFrameToJpeg(params) {
|
|
26030
|
-
const { spawn:
|
|
26545
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
26031
26546
|
const ffmpeg = params.ffmpegPath ?? "ffmpeg";
|
|
26032
26547
|
const inputFormat = params.videoCodec === "H265" ? "hevc" : "h264";
|
|
26033
26548
|
return new Promise((resolve, reject) => {
|
|
@@ -26049,7 +26564,7 @@ ${stderr}`)
|
|
|
26049
26564
|
"2",
|
|
26050
26565
|
"pipe:1"
|
|
26051
26566
|
];
|
|
26052
|
-
const proc =
|
|
26567
|
+
const proc = spawn14(ffmpeg, args, {
|
|
26053
26568
|
stdio: ["pipe", "pipe", "pipe"]
|
|
26054
26569
|
});
|
|
26055
26570
|
const chunks = [];
|
|
@@ -26192,7 +26707,7 @@ ${stderr}`)
|
|
|
26192
26707
|
* Internal helper to mux video+audio into MP4 using ffmpeg.
|
|
26193
26708
|
*/
|
|
26194
26709
|
async muxToMp4(params) {
|
|
26195
|
-
const { spawn:
|
|
26710
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
26196
26711
|
const { randomUUID: randomUUID3 } = await import("crypto");
|
|
26197
26712
|
const fs7 = await import("fs/promises");
|
|
26198
26713
|
const os2 = await import("os");
|
|
@@ -26244,7 +26759,7 @@ ${stderr}`)
|
|
|
26244
26759
|
outputPath
|
|
26245
26760
|
);
|
|
26246
26761
|
await new Promise((resolve, reject) => {
|
|
26247
|
-
const p =
|
|
26762
|
+
const p = spawn14(ffmpeg, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
26248
26763
|
let stderr = "";
|
|
26249
26764
|
p.stderr.on("data", (d) => {
|
|
26250
26765
|
stderr += d.toString();
|
|
@@ -31231,7 +31746,7 @@ ${scheduleItems}
|
|
|
31231
31746
|
"mjpeg",
|
|
31232
31747
|
"pipe:1"
|
|
31233
31748
|
];
|
|
31234
|
-
const ff = (0,
|
|
31749
|
+
const ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31235
31750
|
const chunks = [];
|
|
31236
31751
|
let stderr = "";
|
|
31237
31752
|
ff.stdout.on("data", (d) => chunks.push(Buffer.from(d)));
|
|
@@ -31355,7 +31870,7 @@ ${scheduleItems}
|
|
|
31355
31870
|
"pipe:1"
|
|
31356
31871
|
];
|
|
31357
31872
|
}
|
|
31358
|
-
ff = (0,
|
|
31873
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31359
31874
|
if (!ff.stdin || !ff.stdout || !ff.stderr) {
|
|
31360
31875
|
throw new Error("ffmpeg stdio streams not available");
|
|
31361
31876
|
}
|
|
@@ -31602,7 +32117,7 @@ ${scheduleItems}
|
|
|
31602
32117
|
"mp4",
|
|
31603
32118
|
"pipe:1"
|
|
31604
32119
|
];
|
|
31605
|
-
ff = (0,
|
|
32120
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31606
32121
|
if (!ff.stdin || !ff.stdout || !ff.stderr) {
|
|
31607
32122
|
throw new Error("ffmpeg stdio streams not available");
|
|
31608
32123
|
}
|
|
@@ -31811,7 +32326,7 @@ ${scheduleItems}
|
|
|
31811
32326
|
"independent_segments+temp_file",
|
|
31812
32327
|
playlistPath
|
|
31813
32328
|
];
|
|
31814
|
-
ff = (0,
|
|
32329
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31815
32330
|
if (!ff.stdin || !ff.stderr) {
|
|
31816
32331
|
throw new Error("ffmpeg stdio streams not available");
|
|
31817
32332
|
}
|
|
@@ -32783,14 +33298,14 @@ function buildHlsRedirectUrl(originalUrl) {
|
|
|
32783
33298
|
}
|
|
32784
33299
|
|
|
32785
33300
|
// src/reolink/discovery.ts
|
|
32786
|
-
var
|
|
33301
|
+
var import_node_child_process5 = require("child_process");
|
|
32787
33302
|
var import_node_crypto4 = require("crypto");
|
|
32788
33303
|
var import_node_dgram2 = __toESM(require("dgram"), 1);
|
|
32789
33304
|
var net3 = __toESM(require("net"), 1);
|
|
32790
33305
|
var import_node_os2 = require("os");
|
|
32791
33306
|
var import_node_util = require("util");
|
|
32792
33307
|
init_ReolinkCgiApi();
|
|
32793
|
-
var execFileAsync = (0, import_node_util.promisify)(
|
|
33308
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process5.execFile);
|
|
32794
33309
|
async function discoverViaUdpDirect(host, options) {
|
|
32795
33310
|
if (!options.enableUdpDiscovery) return [];
|
|
32796
33311
|
const logger = options.logger;
|
|
@@ -33832,7 +34347,7 @@ init_recordingFileName();
|
|
|
33832
34347
|
|
|
33833
34348
|
// src/reolink/baichuan/endpoints-server.ts
|
|
33834
34349
|
var import_node_http = __toESM(require("http"), 1);
|
|
33835
|
-
var
|
|
34350
|
+
var import_node_child_process6 = require("child_process");
|
|
33836
34351
|
function parseIntParam(v, def) {
|
|
33837
34352
|
if (v == null) return def;
|
|
33838
34353
|
const n = Number.parseInt(v, 10);
|
|
@@ -34071,7 +34586,7 @@ function createBaichuanEndpointsServer(opts) {
|
|
|
34071
34586
|
"Cache-Control": "no-cache",
|
|
34072
34587
|
Connection: "close"
|
|
34073
34588
|
});
|
|
34074
|
-
const ff2 = (0,
|
|
34589
|
+
const ff2 = (0, import_node_child_process6.spawn)("ffmpeg", [
|
|
34075
34590
|
"-hide_banner",
|
|
34076
34591
|
"-loglevel",
|
|
34077
34592
|
"error",
|
|
@@ -34104,7 +34619,7 @@ function createBaichuanEndpointsServer(opts) {
|
|
|
34104
34619
|
);
|
|
34105
34620
|
res.setHeader("Cache-Control", "no-cache");
|
|
34106
34621
|
res.setHeader("Connection", "close");
|
|
34107
|
-
const ff = (0,
|
|
34622
|
+
const ff = (0, import_node_child_process6.spawn)("ffmpeg", [
|
|
34108
34623
|
"-hide_banner",
|
|
34109
34624
|
"-loglevel",
|
|
34110
34625
|
"error",
|
|
@@ -34215,7 +34730,7 @@ init_urls();
|
|
|
34215
34730
|
|
|
34216
34731
|
// src/rtsp/server.ts
|
|
34217
34732
|
var import_node_http2 = __toESM(require("http"), 1);
|
|
34218
|
-
var
|
|
34733
|
+
var import_node_child_process7 = require("child_process");
|
|
34219
34734
|
init_urls();
|
|
34220
34735
|
function createRtspProxyServer(opts) {
|
|
34221
34736
|
return import_node_http2.default.createServer((req, res) => {
|
|
@@ -34256,7 +34771,7 @@ function createRtspProxyServer(opts) {
|
|
|
34256
34771
|
Connection: "close"
|
|
34257
34772
|
});
|
|
34258
34773
|
const rtspTransport = opts.rtspTransport ?? "tcp";
|
|
34259
|
-
const ff = (0,
|
|
34774
|
+
const ff = (0, import_node_child_process7.spawn)("ffmpeg", [
|
|
34260
34775
|
"-hide_banner",
|
|
34261
34776
|
"-loglevel",
|
|
34262
34777
|
"error",
|
|
@@ -35128,9 +35643,9 @@ var import_node_net2 = __toESM(require("net"), 1);
|
|
|
35128
35643
|
init_BaichuanVideoStream();
|
|
35129
35644
|
|
|
35130
35645
|
// src/multifocal/compositeStream.ts
|
|
35131
|
-
var
|
|
35646
|
+
var import_node_child_process8 = require("child_process");
|
|
35132
35647
|
var import_node_crypto6 = require("crypto");
|
|
35133
|
-
var
|
|
35648
|
+
var import_node_events7 = require("events");
|
|
35134
35649
|
function calculateOverlayPosition(position, mainWidth, mainHeight, pipWidth, pipHeight, margin) {
|
|
35135
35650
|
const pipW = Math.floor(pipWidth);
|
|
35136
35651
|
const pipH = Math.floor(pipHeight);
|
|
@@ -35158,7 +35673,7 @@ function calculateOverlayPosition(position, mainWidth, mainHeight, pipWidth, pip
|
|
|
35158
35673
|
return { x: m, y: m };
|
|
35159
35674
|
}
|
|
35160
35675
|
}
|
|
35161
|
-
var CompositeStream = class extends
|
|
35676
|
+
var CompositeStream = class extends import_node_events7.EventEmitter {
|
|
35162
35677
|
options;
|
|
35163
35678
|
widerStream = null;
|
|
35164
35679
|
teleStream = null;
|
|
@@ -35483,7 +35998,7 @@ var CompositeStream = class extends import_node_events6.EventEmitter {
|
|
|
35483
35998
|
this.logger.log?.(
|
|
35484
35999
|
`[CompositeStream] Starting ffmpeg (rtsp inputs): bin=${ffmpegBin} args=${ffmpegArgs.join(" ")}`
|
|
35485
36000
|
);
|
|
35486
|
-
this.ffmpegProcess = (0,
|
|
36001
|
+
this.ffmpegProcess = (0, import_node_child_process8.spawn)(ffmpegBin, ffmpegArgs, {
|
|
35487
36002
|
stdio: ["ignore", "pipe", "pipe"]
|
|
35488
36003
|
});
|
|
35489
36004
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -35613,7 +36128,7 @@ var CompositeStream = class extends import_node_events6.EventEmitter {
|
|
|
35613
36128
|
this.logger.log?.(
|
|
35614
36129
|
`[CompositeStream] Starting ffmpeg: bin=${ffmpegBin} args=${ffmpegArgs.join(" ")}`
|
|
35615
36130
|
);
|
|
35616
|
-
this.ffmpegProcess = (0,
|
|
36131
|
+
this.ffmpegProcess = (0, import_node_child_process8.spawn)(ffmpegBin, ffmpegArgs, {
|
|
35617
36132
|
stdio: ["pipe", "pipe", "pipe", "pipe"]
|
|
35618
36133
|
});
|
|
35619
36134
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -36191,7 +36706,8 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36191
36706
|
apisToClose.add(resolvedCompositeApis.widerApi);
|
|
36192
36707
|
if (resolvedCompositeApis?.teleApi)
|
|
36193
36708
|
apisToClose.add(resolvedCompositeApis.teleApi);
|
|
36194
|
-
const
|
|
36709
|
+
const alwaysOnEnabled = Boolean(options.alwaysOn?.enabled) && !isComposite;
|
|
36710
|
+
const uptimeRestartMs = alwaysOnEnabled ? 0 : uptimeRestartMsOpt ?? (isComposite ? 6e4 : 1e4);
|
|
36195
36711
|
const variantSuffix = variant && variant !== "default" ? ` variant=${variant}` : "";
|
|
36196
36712
|
const logPrefix = isComposite ? `[native-rfc4571 composite profile=${profile}${variantSuffix}${requestedId ? ` id=${requestedId}` : ""}]` : `[native-rfc4571 ch=${channel} profile=${profile}${variantSuffix}]`;
|
|
36197
36713
|
const log = (message) => {
|
|
@@ -36215,6 +36731,7 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36215
36731
|
);
|
|
36216
36732
|
let videoStream;
|
|
36217
36733
|
let isCompositeStream = false;
|
|
36734
|
+
let alwaysOnController;
|
|
36218
36735
|
if (isComposite) {
|
|
36219
36736
|
const widerChannel = compositeOptions?.widerChannel ?? 0;
|
|
36220
36737
|
const teleChannel = compositeOptions?.teleChannel ?? 1;
|
|
@@ -36357,7 +36874,7 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36357
36874
|
} else {
|
|
36358
36875
|
streamClient = baseApi.client;
|
|
36359
36876
|
}
|
|
36360
|
-
|
|
36877
|
+
const createLiveStream = async () => new BaichuanVideoStream({
|
|
36361
36878
|
client: streamClient,
|
|
36362
36879
|
api: baseApi,
|
|
36363
36880
|
channel: ch,
|
|
@@ -36365,10 +36882,39 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36365
36882
|
variant,
|
|
36366
36883
|
logger
|
|
36367
36884
|
});
|
|
36368
|
-
|
|
36369
|
-
|
|
36370
|
-
|
|
36371
|
-
|
|
36885
|
+
if (options.alwaysOn?.enabled) {
|
|
36886
|
+
const cvsOpts = {
|
|
36887
|
+
// ContinuousVideoStream owns the lifecycle: it calls createLiveStream
|
|
36888
|
+
// (which returns a started stream) and re-starts it internally on goLive.
|
|
36889
|
+
createLiveStream,
|
|
36890
|
+
logger
|
|
36891
|
+
};
|
|
36892
|
+
if (options.alwaysOn.idleFps !== void 0)
|
|
36893
|
+
cvsOpts.idleFps = options.alwaysOn.idleFps;
|
|
36894
|
+
if (options.alwaysOn.placeholder !== void 0)
|
|
36895
|
+
cvsOpts.placeholder = options.alwaysOn.placeholder;
|
|
36896
|
+
const cvs = new ContinuousVideoStream(cvsOpts);
|
|
36897
|
+
alwaysOnController = new AlwaysOnController({
|
|
36898
|
+
api: baseApi,
|
|
36899
|
+
channel: ch,
|
|
36900
|
+
options: options.alwaysOn,
|
|
36901
|
+
goLive: () => cvs.goLive(),
|
|
36902
|
+
goIdle: () => cvs.goIdle(),
|
|
36903
|
+
logger
|
|
36904
|
+
});
|
|
36905
|
+
await alwaysOnController.start();
|
|
36906
|
+
videoStream = cvs;
|
|
36907
|
+
log(
|
|
36908
|
+
`always-on stream started (ch=${ch} profile=${profile}${deviceId ? ` dedicated=${deviceId}` : ""})`
|
|
36909
|
+
);
|
|
36910
|
+
} else {
|
|
36911
|
+
const live = await createLiveStream();
|
|
36912
|
+
await live.start();
|
|
36913
|
+
videoStream = live;
|
|
36914
|
+
log(
|
|
36915
|
+
`stream started (ch=${ch} profile=${profile}${deviceId ? ` dedicated=${deviceId}` : ""})`
|
|
36916
|
+
);
|
|
36917
|
+
}
|
|
36372
36918
|
}
|
|
36373
36919
|
const waitForKeyframe = async () => {
|
|
36374
36920
|
if (isCompositeStream) {
|
|
@@ -36496,6 +37042,12 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36496
37042
|
try {
|
|
36497
37043
|
keyframe = await waitForKeyframe();
|
|
36498
37044
|
} catch (e) {
|
|
37045
|
+
if (alwaysOnController) {
|
|
37046
|
+
try {
|
|
37047
|
+
await alwaysOnController.stop();
|
|
37048
|
+
} catch {
|
|
37049
|
+
}
|
|
37050
|
+
}
|
|
36499
37051
|
try {
|
|
36500
37052
|
await videoStream.stop();
|
|
36501
37053
|
} catch {
|
|
@@ -36744,12 +37296,13 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36744
37296
|
} catch {
|
|
36745
37297
|
}
|
|
36746
37298
|
muxer = makeMuxer();
|
|
37299
|
+
const restartable = videoStream;
|
|
36747
37300
|
try {
|
|
36748
|
-
await
|
|
37301
|
+
await restartable.stop();
|
|
36749
37302
|
} catch {
|
|
36750
37303
|
}
|
|
36751
37304
|
try {
|
|
36752
|
-
await
|
|
37305
|
+
await restartable.start();
|
|
36753
37306
|
} catch (e) {
|
|
36754
37307
|
restarting = false;
|
|
36755
37308
|
close(e).catch(() => {
|
|
@@ -36770,6 +37323,12 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36770
37323
|
cancelIdleTeardown();
|
|
36771
37324
|
const reasonStr = reason?.message || reason?.toString?.() || reason || "requested";
|
|
36772
37325
|
muxer.close();
|
|
37326
|
+
if (alwaysOnController) {
|
|
37327
|
+
try {
|
|
37328
|
+
await alwaysOnController.stop();
|
|
37329
|
+
} catch {
|
|
37330
|
+
}
|
|
37331
|
+
}
|
|
36773
37332
|
try {
|
|
36774
37333
|
await videoStream.stop();
|
|
36775
37334
|
} catch {
|
|
@@ -37369,7 +37928,7 @@ async function createRfc4571TcpServerForReplay(options) {
|
|
|
37369
37928
|
|
|
37370
37929
|
// src/rfc/replay-http-server.ts
|
|
37371
37930
|
var import_node_http3 = __toESM(require("http"), 1);
|
|
37372
|
-
var
|
|
37931
|
+
var import_node_child_process9 = require("child_process");
|
|
37373
37932
|
var import_node_stream2 = require("stream");
|
|
37374
37933
|
async function createReplayHttpServer(options) {
|
|
37375
37934
|
const {
|
|
@@ -37523,7 +38082,7 @@ async function createReplayHttpServer(options) {
|
|
|
37523
38082
|
"pipe:1"
|
|
37524
38083
|
];
|
|
37525
38084
|
log(`spawning ffmpeg: ${ffmpegPath} ${ffmpegArgs.join(" ")}`);
|
|
37526
|
-
ffmpegProcess = (0,
|
|
38085
|
+
ffmpegProcess = (0, import_node_child_process9.spawn)(ffmpegPath, ffmpegArgs, {
|
|
37527
38086
|
stdio: ["pipe", "pipe", "pipe"]
|
|
37528
38087
|
});
|
|
37529
38088
|
ffmpegProcess.stdout?.pipe(outputStream).pipe(res);
|
|
@@ -37624,7 +38183,7 @@ async function createReplayHttpServer(options) {
|
|
|
37624
38183
|
init_BaichuanVideoStream();
|
|
37625
38184
|
|
|
37626
38185
|
// src/baichuan/stream/Go2rtcTcpServer.ts
|
|
37627
|
-
var
|
|
38186
|
+
var import_node_events8 = require("events");
|
|
37628
38187
|
var net4 = __toESM(require("net"), 1);
|
|
37629
38188
|
init_H264Converter();
|
|
37630
38189
|
init_H265Converter();
|
|
@@ -37736,7 +38295,7 @@ var NativeStreamFanout2 = class {
|
|
|
37736
38295
|
this.pumpPromise = null;
|
|
37737
38296
|
}
|
|
37738
38297
|
};
|
|
37739
|
-
var Go2rtcTcpServer = class _Go2rtcTcpServer extends
|
|
38298
|
+
var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events8.EventEmitter {
|
|
37740
38299
|
api;
|
|
37741
38300
|
channel;
|
|
37742
38301
|
profile;
|
|
@@ -38427,8 +38986,8 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events7.EventEm
|
|
|
38427
38986
|
};
|
|
38428
38987
|
|
|
38429
38988
|
// src/baichuan/stream/BaichuanHttpStreamServer.ts
|
|
38430
|
-
var
|
|
38431
|
-
var
|
|
38989
|
+
var import_node_events9 = require("events");
|
|
38990
|
+
var import_node_child_process10 = require("child_process");
|
|
38432
38991
|
var http4 = __toESM(require("http"), 1);
|
|
38433
38992
|
var NAL_START_CODE_4B4 = Buffer.from([0, 0, 0, 1]);
|
|
38434
38993
|
var NAL_START_CODE_3B3 = Buffer.from([0, 0, 1]);
|
|
@@ -38474,7 +39033,7 @@ function isH264KeyframeFromAnnexB(annexB) {
|
|
|
38474
39033
|
}
|
|
38475
39034
|
return false;
|
|
38476
39035
|
}
|
|
38477
|
-
var BaichuanHttpStreamServer = class extends
|
|
39036
|
+
var BaichuanHttpStreamServer = class extends import_node_events9.EventEmitter {
|
|
38478
39037
|
videoStream;
|
|
38479
39038
|
listenPort;
|
|
38480
39039
|
path;
|
|
@@ -38538,7 +39097,7 @@ var BaichuanHttpStreamServer = class extends import_node_events8.EventEmitter {
|
|
|
38538
39097
|
this.httpServer.on("error", reject);
|
|
38539
39098
|
});
|
|
38540
39099
|
this.logger.info(`[BaichuanHttpStreamServer] Starting ffmpeg for H.264 -> MPEG-TS conversion...`);
|
|
38541
|
-
const ffmpeg = (0,
|
|
39100
|
+
const ffmpeg = (0, import_node_child_process10.spawn)("ffmpeg", [
|
|
38542
39101
|
"-hide_banner",
|
|
38543
39102
|
// ffmpeg warnings often include non-fatal decode messages (e.g. decode_slice_header),
|
|
38544
39103
|
// which we don't want to treat as application errors.
|
|
@@ -38746,15 +39305,15 @@ var BaichuanHttpStreamServer = class extends import_node_events8.EventEmitter {
|
|
|
38746
39305
|
};
|
|
38747
39306
|
|
|
38748
39307
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
38749
|
-
var
|
|
39308
|
+
var import_node_events11 = require("events");
|
|
38750
39309
|
var http5 = __toESM(require("http"), 1);
|
|
38751
39310
|
|
|
38752
39311
|
// src/baichuan/stream/MjpegTransformer.ts
|
|
38753
|
-
var
|
|
38754
|
-
var
|
|
39312
|
+
var import_node_events10 = require("events");
|
|
39313
|
+
var import_node_child_process11 = require("child_process");
|
|
38755
39314
|
var JPEG_SOI = Buffer.from([255, 216]);
|
|
38756
39315
|
var JPEG_EOI = Buffer.from([255, 217]);
|
|
38757
|
-
var MjpegTransformer = class extends
|
|
39316
|
+
var MjpegTransformer = class extends import_node_events10.EventEmitter {
|
|
38758
39317
|
options;
|
|
38759
39318
|
ffmpeg = null;
|
|
38760
39319
|
started = false;
|
|
@@ -38812,7 +39371,7 @@ var MjpegTransformer = class extends import_node_events9.EventEmitter {
|
|
|
38812
39371
|
"pipe:1"
|
|
38813
39372
|
);
|
|
38814
39373
|
this.log("debug", `Starting FFmpeg with args: ${args.join(" ")}`);
|
|
38815
|
-
this.ffmpeg = (0,
|
|
39374
|
+
this.ffmpeg = (0, import_node_child_process11.spawn)("ffmpeg", args, {
|
|
38816
39375
|
stdio: ["pipe", "pipe", "pipe"]
|
|
38817
39376
|
});
|
|
38818
39377
|
this.ffmpeg.stdout.on("data", (data) => {
|
|
@@ -38953,7 +39512,7 @@ Content-Length: ${frame.length}\r
|
|
|
38953
39512
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
38954
39513
|
init_H264Converter();
|
|
38955
39514
|
init_H265Converter();
|
|
38956
|
-
var BaichuanMjpegServer = class extends
|
|
39515
|
+
var BaichuanMjpegServer = class extends import_node_events11.EventEmitter {
|
|
38957
39516
|
options;
|
|
38958
39517
|
clients = /* @__PURE__ */ new Map();
|
|
38959
39518
|
httpServer = null;
|
|
@@ -39234,14 +39793,14 @@ var BaichuanMjpegServer = class extends import_node_events10.EventEmitter {
|
|
|
39234
39793
|
};
|
|
39235
39794
|
|
|
39236
39795
|
// src/baichuan/stream/BaichuanWebRTCServer.ts
|
|
39237
|
-
var
|
|
39796
|
+
var import_node_events13 = require("events");
|
|
39238
39797
|
init_BcMediaAnnexBDecoder();
|
|
39239
39798
|
|
|
39240
39799
|
// src/baichuan/stream/AacToOpusTranscoder.ts
|
|
39241
|
-
var
|
|
39800
|
+
var import_node_child_process12 = require("child_process");
|
|
39242
39801
|
var import_node_dgram3 = require("dgram");
|
|
39243
|
-
var
|
|
39244
|
-
var AacToOpusTranscoder = class extends
|
|
39802
|
+
var import_node_events12 = require("events");
|
|
39803
|
+
var AacToOpusTranscoder = class extends import_node_events12.EventEmitter {
|
|
39245
39804
|
opts;
|
|
39246
39805
|
socket = null;
|
|
39247
39806
|
ffmpeg = null;
|
|
@@ -39318,7 +39877,7 @@ var AacToOpusTranscoder = class extends import_node_events11.EventEmitter {
|
|
|
39318
39877
|
`rtp://127.0.0.1:${this.port}`
|
|
39319
39878
|
];
|
|
39320
39879
|
this.log("info", `spawning ffmpeg with: ${this.opts.ffmpegPath} ${args.join(" ")}`);
|
|
39321
|
-
this.ffmpeg = (0,
|
|
39880
|
+
this.ffmpeg = (0, import_node_child_process12.spawn)(this.opts.ffmpegPath, args, {
|
|
39322
39881
|
stdio: ["pipe", "ignore", "pipe"]
|
|
39323
39882
|
});
|
|
39324
39883
|
this.ffmpeg.on("error", (err) => {
|
|
@@ -39458,7 +40017,7 @@ function getH264NalType(nalUnit) {
|
|
|
39458
40017
|
function getH265NalType2(nalUnit) {
|
|
39459
40018
|
return nalUnit[0] >> 1 & 63;
|
|
39460
40019
|
}
|
|
39461
|
-
var BaichuanWebRTCServer = class extends
|
|
40020
|
+
var BaichuanWebRTCServer = class extends import_node_events13.EventEmitter {
|
|
39462
40021
|
options;
|
|
39463
40022
|
sessions = /* @__PURE__ */ new Map();
|
|
39464
40023
|
sessionIdCounter = 0;
|
|
@@ -40450,12 +41009,12 @@ Error: ${err}`
|
|
|
40450
41009
|
};
|
|
40451
41010
|
|
|
40452
41011
|
// src/baichuan/stream/BaichuanHlsServer.ts
|
|
40453
|
-
var
|
|
41012
|
+
var import_node_events14 = require("events");
|
|
40454
41013
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
40455
41014
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
40456
41015
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
40457
41016
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
40458
|
-
var
|
|
41017
|
+
var import_node_child_process13 = require("child_process");
|
|
40459
41018
|
init_BcMediaAnnexBDecoder();
|
|
40460
41019
|
init_H264Converter();
|
|
40461
41020
|
init_H265Converter();
|
|
@@ -40530,7 +41089,7 @@ function getNalTypes(codec, annexB) {
|
|
|
40530
41089
|
}
|
|
40531
41090
|
});
|
|
40532
41091
|
}
|
|
40533
|
-
var BaichuanHlsServer = class extends
|
|
41092
|
+
var BaichuanHlsServer = class extends import_node_events14.EventEmitter {
|
|
40534
41093
|
api;
|
|
40535
41094
|
channel;
|
|
40536
41095
|
profile;
|
|
@@ -40932,7 +41491,7 @@ var BaichuanHlsServer = class extends import_node_events13.EventEmitter {
|
|
|
40932
41491
|
this.segmentPattern,
|
|
40933
41492
|
this.playlistPath
|
|
40934
41493
|
);
|
|
40935
|
-
const p = (0,
|
|
41494
|
+
const p = (0, import_node_child_process13.spawn)(this.ffmpegPath, args, {
|
|
40936
41495
|
stdio: ["pipe", "ignore", "pipe"]
|
|
40937
41496
|
});
|
|
40938
41497
|
p.on("error", (err) => {
|
|
@@ -40977,7 +41536,7 @@ function selectViableUdpMethods(hasUid, methods = ALL_UDP_DISCOVERY_METHODS) {
|
|
|
40977
41536
|
return methods.filter((m) => m === "local-direct");
|
|
40978
41537
|
}
|
|
40979
41538
|
function normalizeUid(uid) {
|
|
40980
|
-
const v = uid?.trim();
|
|
41539
|
+
const v = uid?.trim().toUpperCase();
|
|
40981
41540
|
return v ? v : void 0;
|
|
40982
41541
|
}
|
|
40983
41542
|
function maskUid(uid) {
|
|
@@ -41056,13 +41615,13 @@ async function pingHost(host, timeoutMs = 3e3) {
|
|
|
41056
41615
|
}
|
|
41057
41616
|
return ["-c", "1", "-W", String(Math.max(1, Math.floor(timeoutMs / 1e3))), host];
|
|
41058
41617
|
};
|
|
41059
|
-
const { spawn:
|
|
41618
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
41060
41619
|
for (const bin of pingCandidates) {
|
|
41061
41620
|
const ranOk = await new Promise((resolve) => {
|
|
41062
41621
|
let settled = false;
|
|
41063
41622
|
let child;
|
|
41064
41623
|
try {
|
|
41065
|
-
child =
|
|
41624
|
+
child = spawn14(bin, pingArgs(bin), { stdio: "ignore" });
|
|
41066
41625
|
} catch {
|
|
41067
41626
|
resolve("spawn-failed");
|
|
41068
41627
|
return;
|
|
@@ -41695,10 +42254,10 @@ async function autoDetectDeviceType(inputs) {
|
|
|
41695
42254
|
}
|
|
41696
42255
|
|
|
41697
42256
|
// src/multifocal/compositeRtspServer.ts
|
|
41698
|
-
var
|
|
41699
|
-
var
|
|
42257
|
+
var import_node_events15 = require("events");
|
|
42258
|
+
var import_node_child_process14 = require("child_process");
|
|
41700
42259
|
var net5 = __toESM(require("net"), 1);
|
|
41701
|
-
var CompositeRtspServer = class extends
|
|
42260
|
+
var CompositeRtspServer = class extends import_node_events15.EventEmitter {
|
|
41702
42261
|
options;
|
|
41703
42262
|
compositeStream = null;
|
|
41704
42263
|
rtspServer = null;
|
|
@@ -41803,7 +42362,7 @@ var CompositeRtspServer = class extends import_node_events14.EventEmitter {
|
|
|
41803
42362
|
this.logger.log?.(
|
|
41804
42363
|
`[CompositeRtspServer] Starting ffmpeg RTSP server: ${ffmpegArgs.join(" ")}`
|
|
41805
42364
|
);
|
|
41806
|
-
this.ffmpegProcess = (0,
|
|
42365
|
+
this.ffmpegProcess = (0, import_node_child_process14.spawn)("ffmpeg", ffmpegArgs, {
|
|
41807
42366
|
stdio: ["pipe", "pipe", "pipe"]
|
|
41808
42367
|
});
|
|
41809
42368
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -42474,7 +43033,7 @@ var RtspBackchannel = class _RtspBackchannel {
|
|
|
42474
43033
|
};
|
|
42475
43034
|
|
|
42476
43035
|
// src/baichuan/stream/BaichuanRtspBackchannelServer.ts
|
|
42477
|
-
var
|
|
43036
|
+
var import_node_events16 = require("events");
|
|
42478
43037
|
var net6 = __toESM(require("net"), 1);
|
|
42479
43038
|
var crypto3 = __toESM(require("crypto"), 1);
|
|
42480
43039
|
var md5Hex = (s) => crypto3.createHash("md5").update(s).digest("hex");
|
|
@@ -42531,7 +43090,7 @@ function extractPublicEndpoint(url, requestText) {
|
|
|
42531
43090
|
if (hostHeader) return hostHeader;
|
|
42532
43091
|
return null;
|
|
42533
43092
|
}
|
|
42534
|
-
var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends
|
|
43093
|
+
var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends import_node_events16.EventEmitter {
|
|
42535
43094
|
listenHost;
|
|
42536
43095
|
listenPort;
|
|
42537
43096
|
logger;
|
|
@@ -43512,7 +44071,9 @@ function buildInitialStatus(config) {
|
|
|
43512
44071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
43513
44072
|
0 && (module.exports = {
|
|
43514
44073
|
ALL_UDP_DISCOVERY_METHODS,
|
|
44074
|
+
ALWAYS_ON_DEFAULTS,
|
|
43515
44075
|
AesStreamDecryptor,
|
|
44076
|
+
AlwaysOnController,
|
|
43516
44077
|
AutodiscoveryClient,
|
|
43517
44078
|
BC_AES_IV,
|
|
43518
44079
|
BC_CLASS_FILE_DOWNLOAD,
|
|
@@ -43669,6 +44230,7 @@ function buildInitialStatus(config) {
|
|
|
43669
44230
|
BcUdpStream,
|
|
43670
44231
|
CompositeRtspServer,
|
|
43671
44232
|
CompositeStream,
|
|
44233
|
+
ContinuousVideoStream,
|
|
43672
44234
|
DEFAULT_SHELTER_CANVAS,
|
|
43673
44235
|
DUAL_LENS_DUAL_MOTION_MODELS,
|
|
43674
44236
|
DUAL_LENS_MODELS,
|
|
@@ -43682,6 +44244,7 @@ function buildInitialStatus(config) {
|
|
|
43682
44244
|
MpegTsMuxer,
|
|
43683
44245
|
NVR_HUB_EXACT_TYPES,
|
|
43684
44246
|
NVR_HUB_MODEL_PATTERNS,
|
|
44247
|
+
PlaceholderRenderer,
|
|
43685
44248
|
ReolinkBaichuanApi,
|
|
43686
44249
|
ReolinkCgiApi,
|
|
43687
44250
|
ReolinkHttpClient,
|