mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-05 10:14:30 +00:00
refactor(engine): manage child process lifecycles (#2160)
* refactor(engine): manage child process lifecycles * fix(engine): preserve child reaping after runtime errors * fix(engine): untrack child processes on exit
This commit is contained in:
@@ -8,6 +8,8 @@
|
||||
|
||||
import { spawn } from "child_process";
|
||||
import { getFfmpegBinary } from "./ffmpegBinaries.js";
|
||||
import { ManagedChildProcess } from "./managedChildProcess.js";
|
||||
import { trackChildProcess } from "./processTracker.js";
|
||||
|
||||
export type ConcreteGpuEncoder = "nvenc" | "videotoolbox" | "vaapi" | "qsv" | "amf";
|
||||
export type GpuEncoder = ConcreteGpuEncoder | null;
|
||||
@@ -60,25 +62,20 @@ export async function selectUsableGpuEncoder(
|
||||
}
|
||||
|
||||
export async function detectGpuEncoder(): Promise<GpuEncoder> {
|
||||
return new Promise((resolve) => {
|
||||
const ffmpeg = spawn(getFfmpegBinary(), ["-encoders"], {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
});
|
||||
let stdout = "";
|
||||
|
||||
ffmpeg.stdout.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
|
||||
ffmpeg.on("close", () => {
|
||||
const candidates = getCompiledGpuEncoders(stdout);
|
||||
void selectUsableGpuEncoder(candidates, canUseGpuEncoder)
|
||||
.then(resolve)
|
||||
.catch(() => resolve(null));
|
||||
});
|
||||
|
||||
ffmpeg.on("error", () => resolve(null));
|
||||
const ffmpeg = spawn(getFfmpegBinary(), ["-encoders"], {
|
||||
stdio: ["pipe", "pipe", "pipe"],
|
||||
});
|
||||
trackChildProcess(ffmpeg);
|
||||
let stdout = "";
|
||||
ffmpeg.stdout.on("data", (data) => {
|
||||
stdout += data.toString();
|
||||
});
|
||||
const outcome = await new ManagedChildProcess(ffmpeg, {
|
||||
deadlineAtMs: Date.now() + 30_000,
|
||||
}).wait();
|
||||
if (outcome.reason !== "exit" || outcome.exitCode !== 0) return null;
|
||||
const candidates = getCompiledGpuEncoders(stdout);
|
||||
return selectUsableGpuEncoder(candidates, canUseGpuEncoder).catch(() => null);
|
||||
}
|
||||
|
||||
let cachedGpuEncoder: GpuEncoder | undefined = undefined;
|
||||
@@ -147,46 +144,23 @@ export function getProbeArgs(encoder: ConcreteGpuEncoder): string[] {
|
||||
}
|
||||
|
||||
async function canUseGpuEncoder(encoder: ConcreteGpuEncoder): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
let settled = false;
|
||||
let timedOut = false;
|
||||
let killTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let stderr = "";
|
||||
const finish = (usable: boolean) => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
clearTimeout(timer);
|
||||
if (killTimer) clearTimeout(killTimer);
|
||||
resolve(usable);
|
||||
};
|
||||
const ffmpeg = spawn(getFfmpegBinary(), getProbeArgs(encoder), {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
});
|
||||
|
||||
ffmpeg.stderr?.on("data", (data) => {
|
||||
stderr += data.toString();
|
||||
});
|
||||
|
||||
const timer = setTimeout(() => {
|
||||
timedOut = true;
|
||||
ffmpeg.kill("SIGTERM");
|
||||
killTimer = setTimeout(() => {
|
||||
ffmpeg.kill("SIGKILL");
|
||||
finish(false);
|
||||
}, GPU_PROBE_KILL_GRACE_MS);
|
||||
}, GPU_PROBE_TIMEOUT_MS);
|
||||
|
||||
ffmpeg.on("close", (code, signal) => {
|
||||
const usable = code === 0;
|
||||
logGpuProbeFailure(encoder, { code, signal, stderr, timedOut });
|
||||
finish(usable);
|
||||
});
|
||||
|
||||
ffmpeg.on("error", (error) => {
|
||||
logGpuProbeFailure(encoder, { error, timedOut });
|
||||
finish(false);
|
||||
});
|
||||
const ffmpeg = spawn(getFfmpegBinary(), getProbeArgs(encoder), {
|
||||
stdio: ["ignore", "ignore", "pipe"],
|
||||
});
|
||||
trackChildProcess(ffmpeg);
|
||||
const outcome = await new ManagedChildProcess(ffmpeg, {
|
||||
deadlineAtMs: Date.now() + GPU_PROBE_TIMEOUT_MS,
|
||||
terminationGraceMs: GPU_PROBE_KILL_GRACE_MS,
|
||||
}).wait();
|
||||
const usable = outcome.reason === "exit" && outcome.exitCode === 0;
|
||||
logGpuProbeFailure(encoder, {
|
||||
code: outcome.exitCode,
|
||||
signal: outcome.signal,
|
||||
stderr: outcome.stderr,
|
||||
error: outcome.error,
|
||||
timedOut: outcome.reason === "deadline",
|
||||
});
|
||||
return usable;
|
||||
}
|
||||
|
||||
function logGpuProbeFailure(
|
||||
|
||||
Reference in New Issue
Block a user