mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-11 06:30:03 +00:00
refactor: simplify review fixes for WebM PR
- Use static import for copyFileSync (was unnecessary dynamic import) - Shallow-copy config before mutating forceScreenshot (prevents caller-provided config from being permanently modified) - Consolidate isWebm/isWebmRender/outputFormat into single early declaration in renderOrchestrator - Fix debug output extension for WebM (was hardcoded .mp4) - Log unexpected audio extraction errors instead of silently swallowing Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "fs";
|
import { copyFileSync, existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from "fs";
|
||||||
import { join, dirname } from "path";
|
import { join, dirname } from "path";
|
||||||
import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
|
import { DEFAULT_CONFIG, type EngineConfig } from "../config.js";
|
||||||
import { type GpuEncoder, getCachedGpuEncoder, getGpuEncoderName } from "../utils/gpuEncoder.js";
|
import { type GpuEncoder, getCachedGpuEncoder, getGpuEncoderName } from "../utils/gpuEncoder.js";
|
||||||
@@ -414,8 +414,6 @@ export async function applyFaststart(
|
|||||||
): Promise<MuxResult> {
|
): Promise<MuxResult> {
|
||||||
// faststart is MP4-only (moves moov atom to file start for streaming)
|
// faststart is MP4-only (moves moov atom to file start for streaming)
|
||||||
if (outputPath.endsWith(".webm")) {
|
if (outputPath.endsWith(".webm")) {
|
||||||
// For WebM, just copy the file as-is
|
|
||||||
const { copyFileSync } = await import("fs");
|
|
||||||
if (inputPath !== outputPath) copyFileSync(inputPath, outputPath);
|
if (inputPath !== outputPath) copyFileSync(inputPath, outputPath);
|
||||||
return { success: true, outputPath, durationMs: 0 };
|
return { success: true, outputPath, durationMs: 0 };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,8 +333,12 @@ function extractMonoPcm16(videoPath: string): Int16Array {
|
|||||||
return new Int16Array(0);
|
return new Int16Array(0);
|
||||||
}
|
}
|
||||||
return new Int16Array(stdout.buffer, stdout.byteOffset, Math.floor(stdout.byteLength / 2));
|
return new Int16Array(stdout.buffer, stdout.byteOffset, Math.floor(stdout.byteLength / 2));
|
||||||
} catch {
|
} catch (err) {
|
||||||
// No audio stream in the video (e.g., WebM without audio)
|
// No audio stream (e.g., WebM without audio) — log but don't fail
|
||||||
|
const msg = err instanceof Error ? err.message : String(err);
|
||||||
|
if (!msg.includes("does not contain any stream")) {
|
||||||
|
logPretty(`Audio extraction warning: ${msg.slice(0, 200)}`, "⚠️");
|
||||||
|
}
|
||||||
return new Int16Array(0);
|
return new Int16Array(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -300,9 +300,11 @@ export async function executeRenderJob(
|
|||||||
let restoreLogger: (() => void) | null = null;
|
let restoreLogger: (() => void) | null = null;
|
||||||
const perfStages: Record<string, number> = {};
|
const perfStages: Record<string, number> = {};
|
||||||
const perfOutputPath = join(workDir, "perf-summary.json");
|
const perfOutputPath = join(workDir, "perf-summary.json");
|
||||||
const cfg = job.config.producerConfig ?? resolveConfig();
|
const cfg = { ...(job.config.producerConfig ?? resolveConfig()) };
|
||||||
|
const outputFormat = (job.config.format ?? "mp4") as "mp4" | "webm";
|
||||||
|
const isWebm = outputFormat === "webm";
|
||||||
// WebM/transparency requires screenshot mode — beginFrame doesn't support alpha channel
|
// WebM/transparency requires screenshot mode — beginFrame doesn't support alpha channel
|
||||||
if (job.config.format === "webm") {
|
if (isWebm) {
|
||||||
cfg.forceScreenshot = true;
|
cfg.forceScreenshot = true;
|
||||||
}
|
}
|
||||||
const enableChunkedEncode = cfg.enableChunkedEncode;
|
const enableChunkedEncode = cfg.enableChunkedEncode;
|
||||||
@@ -363,7 +365,6 @@ export async function executeRenderJob(
|
|||||||
});
|
});
|
||||||
assertNotAborted();
|
assertNotAborted();
|
||||||
|
|
||||||
const isWebm = job.config.format === "webm";
|
|
||||||
const captureOpts: CaptureOptions = {
|
const captureOpts: CaptureOptions = {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
@@ -600,19 +601,17 @@ export async function executeRenderJob(
|
|||||||
const framesDir = join(workDir, "captured-frames");
|
const framesDir = join(workDir, "captured-frames");
|
||||||
if (!existsSync(framesDir)) mkdirSync(framesDir, { recursive: true });
|
if (!existsSync(framesDir)) mkdirSync(framesDir, { recursive: true });
|
||||||
|
|
||||||
const outputFormat = job.config.format ?? "mp4";
|
|
||||||
const isWebmRender = outputFormat === "webm";
|
|
||||||
const captureOptions: CaptureOptions = {
|
const captureOptions: CaptureOptions = {
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fps: job.config.fps,
|
fps: job.config.fps,
|
||||||
format: isWebmRender ? "png" : "jpeg",
|
format: isWebm ? "png" : "jpeg",
|
||||||
quality: isWebmRender ? undefined : job.config.quality === "draft" ? 80 : 95,
|
quality: isWebm ? undefined : job.config.quality === "draft" ? 80 : 95,
|
||||||
};
|
};
|
||||||
|
|
||||||
const workerCount = calculateOptimalWorkers(job.totalFrames!, job.config.workers, cfg);
|
const workerCount = calculateOptimalWorkers(job.totalFrames!, job.config.workers, cfg);
|
||||||
|
|
||||||
const videoExt = isWebmRender ? ".webm" : ".mp4";
|
const videoExt = isWebm ? ".webm" : ".mp4";
|
||||||
const videoOnlyPath = join(workDir, `video-only${videoExt}`);
|
const videoOnlyPath = join(workDir, `video-only${videoExt}`);
|
||||||
const preset = getEncoderPreset(job.config.quality, outputFormat);
|
const preset = getEncoderPreset(job.config.quality, outputFormat);
|
||||||
|
|
||||||
@@ -846,7 +845,7 @@ export async function executeRenderJob(
|
|||||||
const stage5Start = Date.now();
|
const stage5Start = Date.now();
|
||||||
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
updateJobStatus(job, "encoding", "Encoding video", 75, onProgress);
|
||||||
|
|
||||||
const frameExt = isWebmRender ? "png" : "jpg";
|
const frameExt = isWebm ? "png" : "jpg";
|
||||||
const framePattern = `frame_%06d.${frameExt}`;
|
const framePattern = `frame_%06d.${frameExt}`;
|
||||||
const encoderOpts = {
|
const encoderOpts = {
|
||||||
fps: job.config.fps,
|
fps: job.config.fps,
|
||||||
@@ -962,7 +961,7 @@ export async function executeRenderJob(
|
|||||||
if (job.config.debug) {
|
if (job.config.debug) {
|
||||||
// Copy output MP4 into debug dir for easy access
|
// Copy output MP4 into debug dir for easy access
|
||||||
if (existsSync(outputPath)) {
|
if (existsSync(outputPath)) {
|
||||||
const debugOutput = join(workDir, "output.mp4");
|
const debugOutput = join(workDir, isWebm ? "output.webm" : "output.mp4");
|
||||||
copyFileSync(outputPath, debugOutput);
|
copyFileSync(outputPath, debugOutput);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user