mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-07 01:56:04 +00:00
fix(engine): default to codec-based alpha capability instead of relying on tags
Tag-based alpha detection (alpha_mode / ALPHA_MODE / pix_fmt yuva*) is fundamentally brittle. Failure modes seen in the wild: - case-sensitivity across ffmpeg versions (alpha_mode vs ALPHA_MODE) - older muxers that omit the sidecar tag entirely - mp4-as-webm rewraps that drop the tag - ffprobe reporting yuv420p for VP9-with-alpha because the alpha plane lives in a Matroska BlockAdditional sidecar, not the main pix_fmt Each of those silently strips alpha at extraction time. The bug doesn't surface until the rendered output is missing layers — frustrating to debug, silent in stdout. The previous case-insensitive fix patched one of the failure modes; this commit removes the class. The robust alternative is codec-based: any bitstream that CAN carry alpha (VP9, VP8, ProRes 4444) gets the alpha-aware decoder and PNG output by default, regardless of what the tag says. The cost is a small file-size increase on opaque VP9/VP8 sources (cached PNGs vs JPGs); the benefit is no class of silent alpha loss from tag misdetection. - Adds codecMayHaveAlpha() + decoderForCodec() helpers and exports them. - Updates extractVideoFramesRange to force libvpx-vp9 / libvpx for VP9 / VP8 unconditionally (was: only when metadata.hasAlpha). - Updates resolveFrameFormat to default to PNG for any alpha-capable codec (was: only when metadata.hasAlpha). - +4 unit tests covering the codec table. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -230,8 +230,17 @@ export async function extractVideoFramesRange(
|
||||
if (isHdr && isMacOS) {
|
||||
args.push("-hwaccel", "videotoolbox");
|
||||
}
|
||||
if (metadata.hasAlpha && metadata.videoCodec === "vp9") {
|
||||
args.push("-c:v", "libvpx-vp9");
|
||||
// Always force the alpha-aware decoder on codecs that can carry alpha. The
|
||||
// alternative — gating on `metadata.hasAlpha` — relies on tag detection that
|
||||
// has at least three known failure modes: case-sensitivity across ffmpeg
|
||||
// versions (`alpha_mode` vs `ALPHA_MODE`), missing tags from older muxers,
|
||||
// and mp4-as-webm rewraps that drop the sidecar. A wrong negative there
|
||||
// silently strips alpha during decode and the bug doesn't surface until
|
||||
// the rendered video is missing layers. Codec-based default has no such
|
||||
// ambiguity: libvpx-vp9 reads the alpha sidecar when present and decodes
|
||||
// normally when it isn't.
|
||||
if (codecMayHaveAlpha(metadata.videoCodec)) {
|
||||
args.push("-c:v", decoderForCodec(metadata.videoCodec));
|
||||
}
|
||||
args.push("-ss", String(startTime), "-i", videoPath, "-t", String(duration));
|
||||
|
||||
@@ -398,9 +407,31 @@ function resolveSegmentDuration(
|
||||
return sourceRemaining > 0 ? sourceRemaining : metadata.durationSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Codecs whose bitstream is allowed to carry an alpha channel. Default the
|
||||
* extraction path to PNG output for these regardless of `metadata.hasAlpha`
|
||||
* so a missed sidecar tag doesn't silently strip transparency. Opaque content
|
||||
* encoded in one of these codecs pays a small file-size cost on the cached
|
||||
* frames but stays correct on the rare case where alpha IS present and the
|
||||
* tag was missed.
|
||||
*/
|
||||
const ALPHA_CAPABLE_CODECS = new Set(["vp9", "vp8", "prores"]);
|
||||
|
||||
export function codecMayHaveAlpha(codec: string | undefined): boolean {
|
||||
return ALPHA_CAPABLE_CODECS.has((codec ?? "").toLowerCase());
|
||||
}
|
||||
|
||||
export function decoderForCodec(codec: string | undefined): string {
|
||||
const c = (codec ?? "").toLowerCase();
|
||||
if (c === "vp9") return "libvpx-vp9";
|
||||
if (c === "vp8") return "libvpx";
|
||||
return c;
|
||||
}
|
||||
|
||||
function resolveFrameFormat(metadata: VideoMetadata, requested?: "jpg" | "png"): CacheFrameFormat {
|
||||
if (requested) return requested;
|
||||
return metadata.hasAlpha ? "png" : "jpg";
|
||||
if (metadata.hasAlpha || codecMayHaveAlpha(metadata.videoCodec)) return "png";
|
||||
return "jpg";
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user