@apocaliss92/nodelink-js 0.6.4 → 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-UL34MR4L.js → chunk-WQ2TQCYP.js} +537 -30
- package/dist/chunk-WQ2TQCYP.js.map +1 -0
- package/dist/cli/rtsp-server.cjs +552 -42
- package/dist/cli/rtsp-server.cjs.map +1 -1
- package/dist/cli/rtsp-server.js +1 -1
- package/dist/index.cjs +643 -81
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +211 -47
- package/dist/index.d.ts +173 -1
- package/dist/index.js +61 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-UL34MR4L.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,
|
|
@@ -14068,14 +14072,14 @@ init_ReolinkHttpClient();
|
|
|
14068
14072
|
init_ReolinkCgiApi();
|
|
14069
14073
|
|
|
14070
14074
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
14071
|
-
var
|
|
14075
|
+
var import_node_child_process4 = require("child_process");
|
|
14072
14076
|
var import_promises2 = require("fs/promises");
|
|
14073
14077
|
var import_node_path2 = require("path");
|
|
14074
14078
|
var import_node_stream = require("stream");
|
|
14075
14079
|
|
|
14076
14080
|
// src/baichuan/stream/BaichuanRtspServer.ts
|
|
14077
|
-
var
|
|
14078
|
-
var
|
|
14081
|
+
var import_node_events5 = require("events");
|
|
14082
|
+
var import_node_child_process3 = require("child_process");
|
|
14079
14083
|
var net2 = __toESM(require("net"), 1);
|
|
14080
14084
|
var dgram2 = __toESM(require("dgram"), 1);
|
|
14081
14085
|
var crypto = __toESM(require("crypto"), 1);
|
|
@@ -14436,6 +14440,358 @@ async function* createNativeStream(api, channel, profile, options) {
|
|
|
14436
14440
|
}
|
|
14437
14441
|
}
|
|
14438
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
|
+
|
|
14439
14795
|
// src/baichuan/stream/rtspFlow.ts
|
|
14440
14796
|
init_H264Converter();
|
|
14441
14797
|
init_H265Converter();
|
|
@@ -14662,7 +15018,7 @@ function envBool(value, defaultValue) {
|
|
|
14662
15018
|
if (v === "0" || v === "false" || v === "no" || v === "off") return false;
|
|
14663
15019
|
return defaultValue;
|
|
14664
15020
|
}
|
|
14665
|
-
var BaichuanRtspServer = class _BaichuanRtspServer extends
|
|
15021
|
+
var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events5.EventEmitter {
|
|
14666
15022
|
api;
|
|
14667
15023
|
channel;
|
|
14668
15024
|
profile;
|
|
@@ -14677,6 +15033,12 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14677
15033
|
deviceId;
|
|
14678
15034
|
dedicatedSessionRelease;
|
|
14679
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;
|
|
14680
15042
|
// Authentication
|
|
14681
15043
|
authCredentials = [];
|
|
14682
15044
|
requireAuth;
|
|
@@ -14691,6 +15053,8 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14691
15053
|
// Set of client IDs (IP:port)
|
|
14692
15054
|
nativeStreamActive = false;
|
|
14693
15055
|
// Whether the native stream is currently active
|
|
15056
|
+
tearingDown = false;
|
|
15057
|
+
// True while stop() is running; suppresses onEnd-driven restarts
|
|
14694
15058
|
clientConnectionServer;
|
|
14695
15059
|
// TCP server to track connections
|
|
14696
15060
|
streamMetadata = null;
|
|
@@ -14878,6 +15242,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
14878
15242
|
this.requireAuth = options.requireAuth ?? this.authCredentials.length > 0;
|
|
14879
15243
|
this.AUTH_REALM = options.authRealm ?? "BaichuanRtspServer";
|
|
14880
15244
|
this.lazyMetadata = options.lazyMetadata ?? false;
|
|
15245
|
+
this.alwaysOnOptions = options.alwaysOn;
|
|
14881
15246
|
const transport = this.api.client.getTransport();
|
|
14882
15247
|
this.flow = createRtspFlow(transport, "H264");
|
|
14883
15248
|
}
|
|
@@ -16001,7 +16366,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16001
16366
|
this.rtspDebugLog(
|
|
16002
16367
|
`Spawning ffmpeg for client ${clientId}: ffmpeg ${ffmpegArgs.join(" ")}`
|
|
16003
16368
|
);
|
|
16004
|
-
ffmpeg = (0,
|
|
16369
|
+
ffmpeg = (0, import_node_child_process3.spawn)("ffmpeg", ffmpegArgs, {
|
|
16005
16370
|
stdio
|
|
16006
16371
|
});
|
|
16007
16372
|
try {
|
|
@@ -16365,6 +16730,141 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16365
16730
|
}
|
|
16366
16731
|
});
|
|
16367
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
|
+
}
|
|
16368
16868
|
/**
|
|
16369
16869
|
* Start native stream (mark as active).
|
|
16370
16870
|
* Each client will create its own generator, so we just track that the stream is active.
|
|
@@ -16426,7 +16926,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16426
16926
|
await this.flow.startKeepAlive(this.api);
|
|
16427
16927
|
this.nativeFanout = new NativeStreamFanout({
|
|
16428
16928
|
maxQueueItems: 200,
|
|
16429
|
-
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, {
|
|
16430
16930
|
variant: this.variant,
|
|
16431
16931
|
...dedicatedClient ? { client: dedicatedClient } : {},
|
|
16432
16932
|
signal
|
|
@@ -16509,6 +17009,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16509
17009
|
} catch {
|
|
16510
17010
|
}
|
|
16511
17011
|
}
|
|
17012
|
+
if (this.tearingDown) return;
|
|
16512
17013
|
if (this.connectedClients.size > 0 && hadFrames) {
|
|
16513
17014
|
this.logger.info(
|
|
16514
17015
|
`[rebroadcast] restarting native stream for ${this.connectedClients.size} active client(s)`
|
|
@@ -16522,7 +17023,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16522
17023
|
});
|
|
16523
17024
|
this.nativeFanout.start();
|
|
16524
17025
|
this.clearNoFrameDeadlineTimer();
|
|
16525
|
-
if (this.nativeStreamNoFrameDeadlineMs > 0) {
|
|
17026
|
+
if (this.nativeStreamNoFrameDeadlineMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16526
17027
|
this.noFrameDeadlineTimer = setTimeout(() => {
|
|
16527
17028
|
this.noFrameDeadlineTimer = void 0;
|
|
16528
17029
|
if (!this.firstFrameReceived && this.nativeStreamActive) {
|
|
@@ -16535,7 +17036,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16535
17036
|
this.noFrameDeadlineTimer?.unref?.();
|
|
16536
17037
|
}
|
|
16537
17038
|
this.clearNoClientAutoStopTimer();
|
|
16538
|
-
if (this.nativeStreamPrimeIdleStopMs > 0) {
|
|
17039
|
+
if (this.nativeStreamPrimeIdleStopMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16539
17040
|
this.noClientAutoStopTimer = setTimeout(() => {
|
|
16540
17041
|
if (this.connectedClients.size === 0) {
|
|
16541
17042
|
this.rtspDebugLog(
|
|
@@ -16622,7 +17123,7 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16622
17123
|
this.emit("clientDisconnected", clientId);
|
|
16623
17124
|
if (this.connectedClients.size === 0) {
|
|
16624
17125
|
this.clearNoClientAutoStopTimer();
|
|
16625
|
-
if (this.nativeStreamIdleStopMs > 0) {
|
|
17126
|
+
if (this.nativeStreamIdleStopMs > 0 && !this.alwaysOnOptions?.enabled) {
|
|
16626
17127
|
this.noClientAutoStopTimer = setTimeout(() => {
|
|
16627
17128
|
if (this.connectedClients.size === 0) {
|
|
16628
17129
|
void this.stopNativeStream();
|
|
@@ -16693,9 +17194,22 @@ var BaichuanRtspServer = class _BaichuanRtspServer extends import_node_events4.E
|
|
|
16693
17194
|
if (!this.active) {
|
|
16694
17195
|
return;
|
|
16695
17196
|
}
|
|
17197
|
+
this.tearingDown = true;
|
|
16696
17198
|
this.logger.info(
|
|
16697
17199
|
`[BaichuanRtspServer] Stopping RTSP server on ${this.listenHost}:${this.listenPort}...`
|
|
16698
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
|
+
}
|
|
16699
17213
|
await this.stopNativeStream();
|
|
16700
17214
|
const clientIds = Array.from(this.connectedClients);
|
|
16701
17215
|
for (const clientId of clientIds) {
|
|
@@ -17369,7 +17883,7 @@ function buildSetSystemGeneralXml(patch) {
|
|
|
17369
17883
|
}
|
|
17370
17884
|
|
|
17371
17885
|
// src/reolink/baichuan/ReolinkBaichuanApi.ts
|
|
17372
|
-
var
|
|
17886
|
+
var import_jimp2 = require("jimp");
|
|
17373
17887
|
init_ReolinkCgiApi();
|
|
17374
17888
|
init_ReolinkHttpClient();
|
|
17375
17889
|
|
|
@@ -20329,8 +20843,8 @@ var parseSirenStatusListPushXml = (xml) => {
|
|
|
20329
20843
|
};
|
|
20330
20844
|
|
|
20331
20845
|
// src/emailPush/bus.ts
|
|
20332
|
-
var
|
|
20333
|
-
var emitter = new
|
|
20846
|
+
var import_node_events6 = require("events");
|
|
20847
|
+
var emitter = new import_node_events6.EventEmitter();
|
|
20334
20848
|
var cameraResolver = () => void 0;
|
|
20335
20849
|
var lastEventByCamera = /* @__PURE__ */ new Map();
|
|
20336
20850
|
var MAX_GLOBAL_EVENTS = 300;
|
|
@@ -24209,12 +24723,12 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
24209
24723
|
let wideImg;
|
|
24210
24724
|
let teleImg;
|
|
24211
24725
|
try {
|
|
24212
|
-
wideImg = await
|
|
24726
|
+
wideImg = await import_jimp2.Jimp.read(wide);
|
|
24213
24727
|
} catch {
|
|
24214
24728
|
return wide;
|
|
24215
24729
|
}
|
|
24216
24730
|
try {
|
|
24217
|
-
teleImg = await
|
|
24731
|
+
teleImg = await import_jimp2.Jimp.read(tele);
|
|
24218
24732
|
} catch {
|
|
24219
24733
|
return wide;
|
|
24220
24734
|
}
|
|
@@ -24248,7 +24762,7 @@ var ReolinkBaichuanApi = class _ReolinkBaichuanApi {
|
|
|
24248
24762
|
});
|
|
24249
24763
|
teleImg.resize({ w: pipW, h: pipH });
|
|
24250
24764
|
wideImg.composite(teleImg, left, top);
|
|
24251
|
-
return await wideImg.getBuffer(
|
|
24765
|
+
return await wideImg.getBuffer(import_jimp2.JimpMime.jpeg, { quality: 80 });
|
|
24252
24766
|
}
|
|
24253
24767
|
const ch = channel !== void 0 ? this.normalizeChannel(channel) : 0;
|
|
24254
24768
|
const variant = options?.variant ?? "default";
|
|
@@ -25206,7 +25720,7 @@ ${xml}`);
|
|
|
25206
25720
|
const chunks = [];
|
|
25207
25721
|
let stderr = "";
|
|
25208
25722
|
let timedOut = false;
|
|
25209
|
-
const ff = (0,
|
|
25723
|
+
const ff = (0, import_node_child_process4.spawn)(params.ffmpegPath, [
|
|
25210
25724
|
"-hide_banner",
|
|
25211
25725
|
"-loglevel",
|
|
25212
25726
|
"error",
|
|
@@ -25291,7 +25805,7 @@ ${xml}`);
|
|
|
25291
25805
|
const chunks = [];
|
|
25292
25806
|
let stderr = "";
|
|
25293
25807
|
let timedOut = false;
|
|
25294
|
-
const ff = (0,
|
|
25808
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25295
25809
|
"-hide_banner",
|
|
25296
25810
|
"-loglevel",
|
|
25297
25811
|
"error",
|
|
@@ -25407,7 +25921,7 @@ ${xml}`);
|
|
|
25407
25921
|
ensureEnabled: true
|
|
25408
25922
|
});
|
|
25409
25923
|
await new Promise((resolve, reject) => {
|
|
25410
|
-
const ff = (0,
|
|
25924
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25411
25925
|
"-hide_banner",
|
|
25412
25926
|
"-loglevel",
|
|
25413
25927
|
"error",
|
|
@@ -25463,7 +25977,7 @@ ${stderr}`));
|
|
|
25463
25977
|
const atSeconds = Number.isFinite(params.atSeconds) && params.atSeconds >= 0 ? params.atSeconds : 0;
|
|
25464
25978
|
await (0, import_promises2.mkdir)((0, import_node_path2.dirname)(params.outputPath), { recursive: true });
|
|
25465
25979
|
await new Promise((resolve, reject) => {
|
|
25466
|
-
const ff = (0,
|
|
25980
|
+
const ff = (0, import_node_child_process4.spawn)(ffmpegPath, [
|
|
25467
25981
|
"-hide_banner",
|
|
25468
25982
|
"-loglevel",
|
|
25469
25983
|
"error",
|
|
@@ -26028,7 +26542,7 @@ ${stderr}`)
|
|
|
26028
26542
|
* Convert a raw video keyframe to JPEG using ffmpeg.
|
|
26029
26543
|
*/
|
|
26030
26544
|
async convertFrameToJpeg(params) {
|
|
26031
|
-
const { spawn:
|
|
26545
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
26032
26546
|
const ffmpeg = params.ffmpegPath ?? "ffmpeg";
|
|
26033
26547
|
const inputFormat = params.videoCodec === "H265" ? "hevc" : "h264";
|
|
26034
26548
|
return new Promise((resolve, reject) => {
|
|
@@ -26050,7 +26564,7 @@ ${stderr}`)
|
|
|
26050
26564
|
"2",
|
|
26051
26565
|
"pipe:1"
|
|
26052
26566
|
];
|
|
26053
|
-
const proc =
|
|
26567
|
+
const proc = spawn14(ffmpeg, args, {
|
|
26054
26568
|
stdio: ["pipe", "pipe", "pipe"]
|
|
26055
26569
|
});
|
|
26056
26570
|
const chunks = [];
|
|
@@ -26193,7 +26707,7 @@ ${stderr}`)
|
|
|
26193
26707
|
* Internal helper to mux video+audio into MP4 using ffmpeg.
|
|
26194
26708
|
*/
|
|
26195
26709
|
async muxToMp4(params) {
|
|
26196
|
-
const { spawn:
|
|
26710
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
26197
26711
|
const { randomUUID: randomUUID3 } = await import("crypto");
|
|
26198
26712
|
const fs7 = await import("fs/promises");
|
|
26199
26713
|
const os2 = await import("os");
|
|
@@ -26245,7 +26759,7 @@ ${stderr}`)
|
|
|
26245
26759
|
outputPath
|
|
26246
26760
|
);
|
|
26247
26761
|
await new Promise((resolve, reject) => {
|
|
26248
|
-
const p =
|
|
26762
|
+
const p = spawn14(ffmpeg, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
26249
26763
|
let stderr = "";
|
|
26250
26764
|
p.stderr.on("data", (d) => {
|
|
26251
26765
|
stderr += d.toString();
|
|
@@ -31232,7 +31746,7 @@ ${scheduleItems}
|
|
|
31232
31746
|
"mjpeg",
|
|
31233
31747
|
"pipe:1"
|
|
31234
31748
|
];
|
|
31235
|
-
const ff = (0,
|
|
31749
|
+
const ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31236
31750
|
const chunks = [];
|
|
31237
31751
|
let stderr = "";
|
|
31238
31752
|
ff.stdout.on("data", (d) => chunks.push(Buffer.from(d)));
|
|
@@ -31356,7 +31870,7 @@ ${scheduleItems}
|
|
|
31356
31870
|
"pipe:1"
|
|
31357
31871
|
];
|
|
31358
31872
|
}
|
|
31359
|
-
ff = (0,
|
|
31873
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31360
31874
|
if (!ff.stdin || !ff.stdout || !ff.stderr) {
|
|
31361
31875
|
throw new Error("ffmpeg stdio streams not available");
|
|
31362
31876
|
}
|
|
@@ -31603,7 +32117,7 @@ ${scheduleItems}
|
|
|
31603
32117
|
"mp4",
|
|
31604
32118
|
"pipe:1"
|
|
31605
32119
|
];
|
|
31606
|
-
ff = (0,
|
|
32120
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31607
32121
|
if (!ff.stdin || !ff.stdout || !ff.stderr) {
|
|
31608
32122
|
throw new Error("ffmpeg stdio streams not available");
|
|
31609
32123
|
}
|
|
@@ -31812,7 +32326,7 @@ ${scheduleItems}
|
|
|
31812
32326
|
"independent_segments+temp_file",
|
|
31813
32327
|
playlistPath
|
|
31814
32328
|
];
|
|
31815
|
-
ff = (0,
|
|
32329
|
+
ff = (0, import_node_child_process4.spawn)("ffmpeg", args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
31816
32330
|
if (!ff.stdin || !ff.stderr) {
|
|
31817
32331
|
throw new Error("ffmpeg stdio streams not available");
|
|
31818
32332
|
}
|
|
@@ -32784,14 +33298,14 @@ function buildHlsRedirectUrl(originalUrl) {
|
|
|
32784
33298
|
}
|
|
32785
33299
|
|
|
32786
33300
|
// src/reolink/discovery.ts
|
|
32787
|
-
var
|
|
33301
|
+
var import_node_child_process5 = require("child_process");
|
|
32788
33302
|
var import_node_crypto4 = require("crypto");
|
|
32789
33303
|
var import_node_dgram2 = __toESM(require("dgram"), 1);
|
|
32790
33304
|
var net3 = __toESM(require("net"), 1);
|
|
32791
33305
|
var import_node_os2 = require("os");
|
|
32792
33306
|
var import_node_util = require("util");
|
|
32793
33307
|
init_ReolinkCgiApi();
|
|
32794
|
-
var execFileAsync = (0, import_node_util.promisify)(
|
|
33308
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process5.execFile);
|
|
32795
33309
|
async function discoverViaUdpDirect(host, options) {
|
|
32796
33310
|
if (!options.enableUdpDiscovery) return [];
|
|
32797
33311
|
const logger = options.logger;
|
|
@@ -33833,7 +34347,7 @@ init_recordingFileName();
|
|
|
33833
34347
|
|
|
33834
34348
|
// src/reolink/baichuan/endpoints-server.ts
|
|
33835
34349
|
var import_node_http = __toESM(require("http"), 1);
|
|
33836
|
-
var
|
|
34350
|
+
var import_node_child_process6 = require("child_process");
|
|
33837
34351
|
function parseIntParam(v, def) {
|
|
33838
34352
|
if (v == null) return def;
|
|
33839
34353
|
const n = Number.parseInt(v, 10);
|
|
@@ -34072,7 +34586,7 @@ function createBaichuanEndpointsServer(opts) {
|
|
|
34072
34586
|
"Cache-Control": "no-cache",
|
|
34073
34587
|
Connection: "close"
|
|
34074
34588
|
});
|
|
34075
|
-
const ff2 = (0,
|
|
34589
|
+
const ff2 = (0, import_node_child_process6.spawn)("ffmpeg", [
|
|
34076
34590
|
"-hide_banner",
|
|
34077
34591
|
"-loglevel",
|
|
34078
34592
|
"error",
|
|
@@ -34105,7 +34619,7 @@ function createBaichuanEndpointsServer(opts) {
|
|
|
34105
34619
|
);
|
|
34106
34620
|
res.setHeader("Cache-Control", "no-cache");
|
|
34107
34621
|
res.setHeader("Connection", "close");
|
|
34108
|
-
const ff = (0,
|
|
34622
|
+
const ff = (0, import_node_child_process6.spawn)("ffmpeg", [
|
|
34109
34623
|
"-hide_banner",
|
|
34110
34624
|
"-loglevel",
|
|
34111
34625
|
"error",
|
|
@@ -34216,7 +34730,7 @@ init_urls();
|
|
|
34216
34730
|
|
|
34217
34731
|
// src/rtsp/server.ts
|
|
34218
34732
|
var import_node_http2 = __toESM(require("http"), 1);
|
|
34219
|
-
var
|
|
34733
|
+
var import_node_child_process7 = require("child_process");
|
|
34220
34734
|
init_urls();
|
|
34221
34735
|
function createRtspProxyServer(opts) {
|
|
34222
34736
|
return import_node_http2.default.createServer((req, res) => {
|
|
@@ -34257,7 +34771,7 @@ function createRtspProxyServer(opts) {
|
|
|
34257
34771
|
Connection: "close"
|
|
34258
34772
|
});
|
|
34259
34773
|
const rtspTransport = opts.rtspTransport ?? "tcp";
|
|
34260
|
-
const ff = (0,
|
|
34774
|
+
const ff = (0, import_node_child_process7.spawn)("ffmpeg", [
|
|
34261
34775
|
"-hide_banner",
|
|
34262
34776
|
"-loglevel",
|
|
34263
34777
|
"error",
|
|
@@ -35129,9 +35643,9 @@ var import_node_net2 = __toESM(require("net"), 1);
|
|
|
35129
35643
|
init_BaichuanVideoStream();
|
|
35130
35644
|
|
|
35131
35645
|
// src/multifocal/compositeStream.ts
|
|
35132
|
-
var
|
|
35646
|
+
var import_node_child_process8 = require("child_process");
|
|
35133
35647
|
var import_node_crypto6 = require("crypto");
|
|
35134
|
-
var
|
|
35648
|
+
var import_node_events7 = require("events");
|
|
35135
35649
|
function calculateOverlayPosition(position, mainWidth, mainHeight, pipWidth, pipHeight, margin) {
|
|
35136
35650
|
const pipW = Math.floor(pipWidth);
|
|
35137
35651
|
const pipH = Math.floor(pipHeight);
|
|
@@ -35159,7 +35673,7 @@ function calculateOverlayPosition(position, mainWidth, mainHeight, pipWidth, pip
|
|
|
35159
35673
|
return { x: m, y: m };
|
|
35160
35674
|
}
|
|
35161
35675
|
}
|
|
35162
|
-
var CompositeStream = class extends
|
|
35676
|
+
var CompositeStream = class extends import_node_events7.EventEmitter {
|
|
35163
35677
|
options;
|
|
35164
35678
|
widerStream = null;
|
|
35165
35679
|
teleStream = null;
|
|
@@ -35484,7 +35998,7 @@ var CompositeStream = class extends import_node_events6.EventEmitter {
|
|
|
35484
35998
|
this.logger.log?.(
|
|
35485
35999
|
`[CompositeStream] Starting ffmpeg (rtsp inputs): bin=${ffmpegBin} args=${ffmpegArgs.join(" ")}`
|
|
35486
36000
|
);
|
|
35487
|
-
this.ffmpegProcess = (0,
|
|
36001
|
+
this.ffmpegProcess = (0, import_node_child_process8.spawn)(ffmpegBin, ffmpegArgs, {
|
|
35488
36002
|
stdio: ["ignore", "pipe", "pipe"]
|
|
35489
36003
|
});
|
|
35490
36004
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -35614,7 +36128,7 @@ var CompositeStream = class extends import_node_events6.EventEmitter {
|
|
|
35614
36128
|
this.logger.log?.(
|
|
35615
36129
|
`[CompositeStream] Starting ffmpeg: bin=${ffmpegBin} args=${ffmpegArgs.join(" ")}`
|
|
35616
36130
|
);
|
|
35617
|
-
this.ffmpegProcess = (0,
|
|
36131
|
+
this.ffmpegProcess = (0, import_node_child_process8.spawn)(ffmpegBin, ffmpegArgs, {
|
|
35618
36132
|
stdio: ["pipe", "pipe", "pipe", "pipe"]
|
|
35619
36133
|
});
|
|
35620
36134
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -36192,7 +36706,8 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36192
36706
|
apisToClose.add(resolvedCompositeApis.widerApi);
|
|
36193
36707
|
if (resolvedCompositeApis?.teleApi)
|
|
36194
36708
|
apisToClose.add(resolvedCompositeApis.teleApi);
|
|
36195
|
-
const
|
|
36709
|
+
const alwaysOnEnabled = Boolean(options.alwaysOn?.enabled) && !isComposite;
|
|
36710
|
+
const uptimeRestartMs = alwaysOnEnabled ? 0 : uptimeRestartMsOpt ?? (isComposite ? 6e4 : 1e4);
|
|
36196
36711
|
const variantSuffix = variant && variant !== "default" ? ` variant=${variant}` : "";
|
|
36197
36712
|
const logPrefix = isComposite ? `[native-rfc4571 composite profile=${profile}${variantSuffix}${requestedId ? ` id=${requestedId}` : ""}]` : `[native-rfc4571 ch=${channel} profile=${profile}${variantSuffix}]`;
|
|
36198
36713
|
const log = (message) => {
|
|
@@ -36216,6 +36731,7 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36216
36731
|
);
|
|
36217
36732
|
let videoStream;
|
|
36218
36733
|
let isCompositeStream = false;
|
|
36734
|
+
let alwaysOnController;
|
|
36219
36735
|
if (isComposite) {
|
|
36220
36736
|
const widerChannel = compositeOptions?.widerChannel ?? 0;
|
|
36221
36737
|
const teleChannel = compositeOptions?.teleChannel ?? 1;
|
|
@@ -36358,7 +36874,7 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36358
36874
|
} else {
|
|
36359
36875
|
streamClient = baseApi.client;
|
|
36360
36876
|
}
|
|
36361
|
-
|
|
36877
|
+
const createLiveStream = async () => new BaichuanVideoStream({
|
|
36362
36878
|
client: streamClient,
|
|
36363
36879
|
api: baseApi,
|
|
36364
36880
|
channel: ch,
|
|
@@ -36366,10 +36882,39 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36366
36882
|
variant,
|
|
36367
36883
|
logger
|
|
36368
36884
|
});
|
|
36369
|
-
|
|
36370
|
-
|
|
36371
|
-
|
|
36372
|
-
|
|
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
|
+
}
|
|
36373
36918
|
}
|
|
36374
36919
|
const waitForKeyframe = async () => {
|
|
36375
36920
|
if (isCompositeStream) {
|
|
@@ -36497,6 +37042,12 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36497
37042
|
try {
|
|
36498
37043
|
keyframe = await waitForKeyframe();
|
|
36499
37044
|
} catch (e) {
|
|
37045
|
+
if (alwaysOnController) {
|
|
37046
|
+
try {
|
|
37047
|
+
await alwaysOnController.stop();
|
|
37048
|
+
} catch {
|
|
37049
|
+
}
|
|
37050
|
+
}
|
|
36500
37051
|
try {
|
|
36501
37052
|
await videoStream.stop();
|
|
36502
37053
|
} catch {
|
|
@@ -36745,12 +37296,13 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36745
37296
|
} catch {
|
|
36746
37297
|
}
|
|
36747
37298
|
muxer = makeMuxer();
|
|
37299
|
+
const restartable = videoStream;
|
|
36748
37300
|
try {
|
|
36749
|
-
await
|
|
37301
|
+
await restartable.stop();
|
|
36750
37302
|
} catch {
|
|
36751
37303
|
}
|
|
36752
37304
|
try {
|
|
36753
|
-
await
|
|
37305
|
+
await restartable.start();
|
|
36754
37306
|
} catch (e) {
|
|
36755
37307
|
restarting = false;
|
|
36756
37308
|
close(e).catch(() => {
|
|
@@ -36771,6 +37323,12 @@ async function createRfc4571TcpServerInternal(options) {
|
|
|
36771
37323
|
cancelIdleTeardown();
|
|
36772
37324
|
const reasonStr = reason?.message || reason?.toString?.() || reason || "requested";
|
|
36773
37325
|
muxer.close();
|
|
37326
|
+
if (alwaysOnController) {
|
|
37327
|
+
try {
|
|
37328
|
+
await alwaysOnController.stop();
|
|
37329
|
+
} catch {
|
|
37330
|
+
}
|
|
37331
|
+
}
|
|
36774
37332
|
try {
|
|
36775
37333
|
await videoStream.stop();
|
|
36776
37334
|
} catch {
|
|
@@ -37370,7 +37928,7 @@ async function createRfc4571TcpServerForReplay(options) {
|
|
|
37370
37928
|
|
|
37371
37929
|
// src/rfc/replay-http-server.ts
|
|
37372
37930
|
var import_node_http3 = __toESM(require("http"), 1);
|
|
37373
|
-
var
|
|
37931
|
+
var import_node_child_process9 = require("child_process");
|
|
37374
37932
|
var import_node_stream2 = require("stream");
|
|
37375
37933
|
async function createReplayHttpServer(options) {
|
|
37376
37934
|
const {
|
|
@@ -37524,7 +38082,7 @@ async function createReplayHttpServer(options) {
|
|
|
37524
38082
|
"pipe:1"
|
|
37525
38083
|
];
|
|
37526
38084
|
log(`spawning ffmpeg: ${ffmpegPath} ${ffmpegArgs.join(" ")}`);
|
|
37527
|
-
ffmpegProcess = (0,
|
|
38085
|
+
ffmpegProcess = (0, import_node_child_process9.spawn)(ffmpegPath, ffmpegArgs, {
|
|
37528
38086
|
stdio: ["pipe", "pipe", "pipe"]
|
|
37529
38087
|
});
|
|
37530
38088
|
ffmpegProcess.stdout?.pipe(outputStream).pipe(res);
|
|
@@ -37625,7 +38183,7 @@ async function createReplayHttpServer(options) {
|
|
|
37625
38183
|
init_BaichuanVideoStream();
|
|
37626
38184
|
|
|
37627
38185
|
// src/baichuan/stream/Go2rtcTcpServer.ts
|
|
37628
|
-
var
|
|
38186
|
+
var import_node_events8 = require("events");
|
|
37629
38187
|
var net4 = __toESM(require("net"), 1);
|
|
37630
38188
|
init_H264Converter();
|
|
37631
38189
|
init_H265Converter();
|
|
@@ -37737,7 +38295,7 @@ var NativeStreamFanout2 = class {
|
|
|
37737
38295
|
this.pumpPromise = null;
|
|
37738
38296
|
}
|
|
37739
38297
|
};
|
|
37740
|
-
var Go2rtcTcpServer = class _Go2rtcTcpServer extends
|
|
38298
|
+
var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events8.EventEmitter {
|
|
37741
38299
|
api;
|
|
37742
38300
|
channel;
|
|
37743
38301
|
profile;
|
|
@@ -38428,8 +38986,8 @@ var Go2rtcTcpServer = class _Go2rtcTcpServer extends import_node_events7.EventEm
|
|
|
38428
38986
|
};
|
|
38429
38987
|
|
|
38430
38988
|
// src/baichuan/stream/BaichuanHttpStreamServer.ts
|
|
38431
|
-
var
|
|
38432
|
-
var
|
|
38989
|
+
var import_node_events9 = require("events");
|
|
38990
|
+
var import_node_child_process10 = require("child_process");
|
|
38433
38991
|
var http4 = __toESM(require("http"), 1);
|
|
38434
38992
|
var NAL_START_CODE_4B4 = Buffer.from([0, 0, 0, 1]);
|
|
38435
38993
|
var NAL_START_CODE_3B3 = Buffer.from([0, 0, 1]);
|
|
@@ -38475,7 +39033,7 @@ function isH264KeyframeFromAnnexB(annexB) {
|
|
|
38475
39033
|
}
|
|
38476
39034
|
return false;
|
|
38477
39035
|
}
|
|
38478
|
-
var BaichuanHttpStreamServer = class extends
|
|
39036
|
+
var BaichuanHttpStreamServer = class extends import_node_events9.EventEmitter {
|
|
38479
39037
|
videoStream;
|
|
38480
39038
|
listenPort;
|
|
38481
39039
|
path;
|
|
@@ -38539,7 +39097,7 @@ var BaichuanHttpStreamServer = class extends import_node_events8.EventEmitter {
|
|
|
38539
39097
|
this.httpServer.on("error", reject);
|
|
38540
39098
|
});
|
|
38541
39099
|
this.logger.info(`[BaichuanHttpStreamServer] Starting ffmpeg for H.264 -> MPEG-TS conversion...`);
|
|
38542
|
-
const ffmpeg = (0,
|
|
39100
|
+
const ffmpeg = (0, import_node_child_process10.spawn)("ffmpeg", [
|
|
38543
39101
|
"-hide_banner",
|
|
38544
39102
|
// ffmpeg warnings often include non-fatal decode messages (e.g. decode_slice_header),
|
|
38545
39103
|
// which we don't want to treat as application errors.
|
|
@@ -38747,15 +39305,15 @@ var BaichuanHttpStreamServer = class extends import_node_events8.EventEmitter {
|
|
|
38747
39305
|
};
|
|
38748
39306
|
|
|
38749
39307
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
38750
|
-
var
|
|
39308
|
+
var import_node_events11 = require("events");
|
|
38751
39309
|
var http5 = __toESM(require("http"), 1);
|
|
38752
39310
|
|
|
38753
39311
|
// src/baichuan/stream/MjpegTransformer.ts
|
|
38754
|
-
var
|
|
38755
|
-
var
|
|
39312
|
+
var import_node_events10 = require("events");
|
|
39313
|
+
var import_node_child_process11 = require("child_process");
|
|
38756
39314
|
var JPEG_SOI = Buffer.from([255, 216]);
|
|
38757
39315
|
var JPEG_EOI = Buffer.from([255, 217]);
|
|
38758
|
-
var MjpegTransformer = class extends
|
|
39316
|
+
var MjpegTransformer = class extends import_node_events10.EventEmitter {
|
|
38759
39317
|
options;
|
|
38760
39318
|
ffmpeg = null;
|
|
38761
39319
|
started = false;
|
|
@@ -38813,7 +39371,7 @@ var MjpegTransformer = class extends import_node_events9.EventEmitter {
|
|
|
38813
39371
|
"pipe:1"
|
|
38814
39372
|
);
|
|
38815
39373
|
this.log("debug", `Starting FFmpeg with args: ${args.join(" ")}`);
|
|
38816
|
-
this.ffmpeg = (0,
|
|
39374
|
+
this.ffmpeg = (0, import_node_child_process11.spawn)("ffmpeg", args, {
|
|
38817
39375
|
stdio: ["pipe", "pipe", "pipe"]
|
|
38818
39376
|
});
|
|
38819
39377
|
this.ffmpeg.stdout.on("data", (data) => {
|
|
@@ -38954,7 +39512,7 @@ Content-Length: ${frame.length}\r
|
|
|
38954
39512
|
// src/baichuan/stream/BaichuanMjpegServer.ts
|
|
38955
39513
|
init_H264Converter();
|
|
38956
39514
|
init_H265Converter();
|
|
38957
|
-
var BaichuanMjpegServer = class extends
|
|
39515
|
+
var BaichuanMjpegServer = class extends import_node_events11.EventEmitter {
|
|
38958
39516
|
options;
|
|
38959
39517
|
clients = /* @__PURE__ */ new Map();
|
|
38960
39518
|
httpServer = null;
|
|
@@ -39235,14 +39793,14 @@ var BaichuanMjpegServer = class extends import_node_events10.EventEmitter {
|
|
|
39235
39793
|
};
|
|
39236
39794
|
|
|
39237
39795
|
// src/baichuan/stream/BaichuanWebRTCServer.ts
|
|
39238
|
-
var
|
|
39796
|
+
var import_node_events13 = require("events");
|
|
39239
39797
|
init_BcMediaAnnexBDecoder();
|
|
39240
39798
|
|
|
39241
39799
|
// src/baichuan/stream/AacToOpusTranscoder.ts
|
|
39242
|
-
var
|
|
39800
|
+
var import_node_child_process12 = require("child_process");
|
|
39243
39801
|
var import_node_dgram3 = require("dgram");
|
|
39244
|
-
var
|
|
39245
|
-
var AacToOpusTranscoder = class extends
|
|
39802
|
+
var import_node_events12 = require("events");
|
|
39803
|
+
var AacToOpusTranscoder = class extends import_node_events12.EventEmitter {
|
|
39246
39804
|
opts;
|
|
39247
39805
|
socket = null;
|
|
39248
39806
|
ffmpeg = null;
|
|
@@ -39319,7 +39877,7 @@ var AacToOpusTranscoder = class extends import_node_events11.EventEmitter {
|
|
|
39319
39877
|
`rtp://127.0.0.1:${this.port}`
|
|
39320
39878
|
];
|
|
39321
39879
|
this.log("info", `spawning ffmpeg with: ${this.opts.ffmpegPath} ${args.join(" ")}`);
|
|
39322
|
-
this.ffmpeg = (0,
|
|
39880
|
+
this.ffmpeg = (0, import_node_child_process12.spawn)(this.opts.ffmpegPath, args, {
|
|
39323
39881
|
stdio: ["pipe", "ignore", "pipe"]
|
|
39324
39882
|
});
|
|
39325
39883
|
this.ffmpeg.on("error", (err) => {
|
|
@@ -39459,7 +40017,7 @@ function getH264NalType(nalUnit) {
|
|
|
39459
40017
|
function getH265NalType2(nalUnit) {
|
|
39460
40018
|
return nalUnit[0] >> 1 & 63;
|
|
39461
40019
|
}
|
|
39462
|
-
var BaichuanWebRTCServer = class extends
|
|
40020
|
+
var BaichuanWebRTCServer = class extends import_node_events13.EventEmitter {
|
|
39463
40021
|
options;
|
|
39464
40022
|
sessions = /* @__PURE__ */ new Map();
|
|
39465
40023
|
sessionIdCounter = 0;
|
|
@@ -40451,12 +41009,12 @@ Error: ${err}`
|
|
|
40451
41009
|
};
|
|
40452
41010
|
|
|
40453
41011
|
// src/baichuan/stream/BaichuanHlsServer.ts
|
|
40454
|
-
var
|
|
41012
|
+
var import_node_events14 = require("events");
|
|
40455
41013
|
var import_node_fs = __toESM(require("fs"), 1);
|
|
40456
41014
|
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
40457
41015
|
var import_node_os3 = __toESM(require("os"), 1);
|
|
40458
41016
|
var import_node_path3 = __toESM(require("path"), 1);
|
|
40459
|
-
var
|
|
41017
|
+
var import_node_child_process13 = require("child_process");
|
|
40460
41018
|
init_BcMediaAnnexBDecoder();
|
|
40461
41019
|
init_H264Converter();
|
|
40462
41020
|
init_H265Converter();
|
|
@@ -40531,7 +41089,7 @@ function getNalTypes(codec, annexB) {
|
|
|
40531
41089
|
}
|
|
40532
41090
|
});
|
|
40533
41091
|
}
|
|
40534
|
-
var BaichuanHlsServer = class extends
|
|
41092
|
+
var BaichuanHlsServer = class extends import_node_events14.EventEmitter {
|
|
40535
41093
|
api;
|
|
40536
41094
|
channel;
|
|
40537
41095
|
profile;
|
|
@@ -40933,7 +41491,7 @@ var BaichuanHlsServer = class extends import_node_events13.EventEmitter {
|
|
|
40933
41491
|
this.segmentPattern,
|
|
40934
41492
|
this.playlistPath
|
|
40935
41493
|
);
|
|
40936
|
-
const p = (0,
|
|
41494
|
+
const p = (0, import_node_child_process13.spawn)(this.ffmpegPath, args, {
|
|
40937
41495
|
stdio: ["pipe", "ignore", "pipe"]
|
|
40938
41496
|
});
|
|
40939
41497
|
p.on("error", (err) => {
|
|
@@ -41057,13 +41615,13 @@ async function pingHost(host, timeoutMs = 3e3) {
|
|
|
41057
41615
|
}
|
|
41058
41616
|
return ["-c", "1", "-W", String(Math.max(1, Math.floor(timeoutMs / 1e3))), host];
|
|
41059
41617
|
};
|
|
41060
|
-
const { spawn:
|
|
41618
|
+
const { spawn: spawn14 } = await import("child_process");
|
|
41061
41619
|
for (const bin of pingCandidates) {
|
|
41062
41620
|
const ranOk = await new Promise((resolve) => {
|
|
41063
41621
|
let settled = false;
|
|
41064
41622
|
let child;
|
|
41065
41623
|
try {
|
|
41066
|
-
child =
|
|
41624
|
+
child = spawn14(bin, pingArgs(bin), { stdio: "ignore" });
|
|
41067
41625
|
} catch {
|
|
41068
41626
|
resolve("spawn-failed");
|
|
41069
41627
|
return;
|
|
@@ -41696,10 +42254,10 @@ async function autoDetectDeviceType(inputs) {
|
|
|
41696
42254
|
}
|
|
41697
42255
|
|
|
41698
42256
|
// src/multifocal/compositeRtspServer.ts
|
|
41699
|
-
var
|
|
41700
|
-
var
|
|
42257
|
+
var import_node_events15 = require("events");
|
|
42258
|
+
var import_node_child_process14 = require("child_process");
|
|
41701
42259
|
var net5 = __toESM(require("net"), 1);
|
|
41702
|
-
var CompositeRtspServer = class extends
|
|
42260
|
+
var CompositeRtspServer = class extends import_node_events15.EventEmitter {
|
|
41703
42261
|
options;
|
|
41704
42262
|
compositeStream = null;
|
|
41705
42263
|
rtspServer = null;
|
|
@@ -41804,7 +42362,7 @@ var CompositeRtspServer = class extends import_node_events14.EventEmitter {
|
|
|
41804
42362
|
this.logger.log?.(
|
|
41805
42363
|
`[CompositeRtspServer] Starting ffmpeg RTSP server: ${ffmpegArgs.join(" ")}`
|
|
41806
42364
|
);
|
|
41807
|
-
this.ffmpegProcess = (0,
|
|
42365
|
+
this.ffmpegProcess = (0, import_node_child_process14.spawn)("ffmpeg", ffmpegArgs, {
|
|
41808
42366
|
stdio: ["pipe", "pipe", "pipe"]
|
|
41809
42367
|
});
|
|
41810
42368
|
this.ffmpegProcess.on("error", (error) => {
|
|
@@ -42475,7 +43033,7 @@ var RtspBackchannel = class _RtspBackchannel {
|
|
|
42475
43033
|
};
|
|
42476
43034
|
|
|
42477
43035
|
// src/baichuan/stream/BaichuanRtspBackchannelServer.ts
|
|
42478
|
-
var
|
|
43036
|
+
var import_node_events16 = require("events");
|
|
42479
43037
|
var net6 = __toESM(require("net"), 1);
|
|
42480
43038
|
var crypto3 = __toESM(require("crypto"), 1);
|
|
42481
43039
|
var md5Hex = (s) => crypto3.createHash("md5").update(s).digest("hex");
|
|
@@ -42532,7 +43090,7 @@ function extractPublicEndpoint(url, requestText) {
|
|
|
42532
43090
|
if (hostHeader) return hostHeader;
|
|
42533
43091
|
return null;
|
|
42534
43092
|
}
|
|
42535
|
-
var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends
|
|
43093
|
+
var BaichuanRtspBackchannelServer = class _BaichuanRtspBackchannelServer extends import_node_events16.EventEmitter {
|
|
42536
43094
|
listenHost;
|
|
42537
43095
|
listenPort;
|
|
42538
43096
|
logger;
|
|
@@ -43513,7 +44071,9 @@ function buildInitialStatus(config) {
|
|
|
43513
44071
|
// Annotate the CommonJS export names for ESM import in node:
|
|
43514
44072
|
0 && (module.exports = {
|
|
43515
44073
|
ALL_UDP_DISCOVERY_METHODS,
|
|
44074
|
+
ALWAYS_ON_DEFAULTS,
|
|
43516
44075
|
AesStreamDecryptor,
|
|
44076
|
+
AlwaysOnController,
|
|
43517
44077
|
AutodiscoveryClient,
|
|
43518
44078
|
BC_AES_IV,
|
|
43519
44079
|
BC_CLASS_FILE_DOWNLOAD,
|
|
@@ -43670,6 +44230,7 @@ function buildInitialStatus(config) {
|
|
|
43670
44230
|
BcUdpStream,
|
|
43671
44231
|
CompositeRtspServer,
|
|
43672
44232
|
CompositeStream,
|
|
44233
|
+
ContinuousVideoStream,
|
|
43673
44234
|
DEFAULT_SHELTER_CANVAS,
|
|
43674
44235
|
DUAL_LENS_DUAL_MOTION_MODELS,
|
|
43675
44236
|
DUAL_LENS_MODELS,
|
|
@@ -43683,6 +44244,7 @@ function buildInitialStatus(config) {
|
|
|
43683
44244
|
MpegTsMuxer,
|
|
43684
44245
|
NVR_HUB_EXACT_TYPES,
|
|
43685
44246
|
NVR_HUB_MODEL_PATTERNS,
|
|
44247
|
+
PlaceholderRenderer,
|
|
43686
44248
|
ReolinkBaichuanApi,
|
|
43687
44249
|
ReolinkCgiApi,
|
|
43688
44250
|
ReolinkHttpClient,
|