fix: use VP9 codec for webm recording output (#779)

* fix: use VP9 codec for webm recording output

The recording command hardcoded libx264 (H.264) which is incompatible
with the WebM container format. WebM only supports VP8/VP9/AV1 codecs,
causing ffmpeg to fail when users specify a .webm output file.

Select codec based on output file extension: libvpx-vp9 for .webm,
libx264 for other formats.

Fixes #778

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* refactor: use CRF mode for VP9 webm encoding

Switch from bitrate target (-b:v 2M) to constant quality mode (-crf 30),
which is the standard approach for screen recording (used by Puppeteer
and recommended by ffmpeg VP9 guide). CRF adapts bitrate to scene
complexity for more consistent quality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: add -b:v 0 for true constant quality VP9 encoding

Without -b:v 0, libvpx-vp9 uses its default bitrate target alongside
-crf, resulting in constrained quality mode instead of true constant
quality mode.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: pad video dimensions to even numbers for h264 compatibility

libx264 requires width and height to be divisible by 2, but CDP
screencast can capture frames with odd dimensions (e.g. 1280x577).
Add pad filter to ensure even dimensions for all codecs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: hyunjinee <leehj0110@kakao.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
jin.2
2026-03-14 09:27:33 -05:00
committed by GitHub
co-authored by Claude Opus 4.6 hyunjinee
parent 67986adffc
commit 44ce24fb48
+11 -15
View File
@@ -73,22 +73,18 @@ pub fn recording_stop(state: &mut RecordingState) -> Result<Value, String> {
let output = &state.output_path;
// Encode with ffmpeg
let codec_args: &[&str] = if output.ends_with(".webm") {
&["-c:v", "libvpx-vp9", "-crf", "30", "-b:v", "0"]
} else {
&["-c:v", "libx264", "-preset", "fast"]
};
let result = Command::new("ffmpeg")
.args([
"-y",
"-framerate",
"30",
"-i",
&frame_pattern,
"-c:v",
"libx264",
"-pix_fmt",
"yuv420p",
"-preset",
"fast",
output,
])
.args(["-y", "-framerate", "30", "-i", &frame_pattern])
.args(["-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2"])
.args(codec_args)
.args(["-pix_fmt", "yuv420p"])
.arg(output)
.output();
let _ = std::fs::remove_dir_all(&state.temp_dir);