From 44ce24fb484167cc34485b88ead512503bd8d4d8 Mon Sep 17 00:00:00 2001 From: "jin.2" Date: Sat, 14 Mar 2026 23:27:33 +0900 Subject: [PATCH] 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) * 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) * 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) * 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) --------- Co-authored-by: hyunjinee Co-authored-by: Claude Opus 4.6 (1M context) --- cli/src/native/recording.rs | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/cli/src/native/recording.rs b/cli/src/native/recording.rs index f9614cd..e4cbd68 100644 --- a/cli/src/native/recording.rs +++ b/cli/src/native/recording.rs @@ -73,22 +73,18 @@ pub fn recording_stop(state: &mut RecordingState) -> Result { 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);