Skip to content

Commit 6218b60

Browse files
authored
recorder: pad odd source dimensions before libx264 encode (#246)
## Summary libx264 with `yuv420p` requires both width and height to be divisible by 2 (4:2:0 chroma subsampling). When the captured display has an odd vertical (or horizontal) pixel count, ffmpeg fails to open the encoder with `width/height not divisible by 2` and exits almost immediately — but `Start()` only waits 250ms for a startup error, so the API still returns 200 and the caller is left with a zero-byte mp4. This change adds a `-vf pad=ceil(iw/2)*2:ceil(ih/2)*2` filter so odd source dimensions are padded by a single pixel of black before encode. Even dimensions are unchanged. ## Repro ``` $ ffmpeg -f lavfi -i color=red:size=641x481:rate=5 -t 1 -c:v libx264 -pix_fmt yuv420p -y out.mp4 [libx264] width not divisible by 2 (641x481) [enc:libx264] Could not open encoder before EOF $ ls -la out.mp4 -rw-r--r-- 1 user user 0 ... out.mp4 ``` With the filter: ``` $ ffmpeg -f lavfi -i color=red:size=641x481:rate=5 -t 1 -vf "pad=ceil(iw/2)*2:ceil(ih/2)*2" -c:v libx264 -pix_fmt yuv420p -y out.mp4 $ ffprobe out.mp4 width=642 height=482 ``` ## Test plan - [x] `go build ./...` - [x] `go test ./lib/recorder/... -v` (existing tests pass, new `TestFFmpegArgs_PadsOddDimensions` covers the args) <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Touches ffmpeg encoding arguments for all recordings, which could subtly affect output dimensions/compatibility if the filter order or padding behavior differs across inputs/platforms. > > **Overview** > **Prevents recordings from failing on odd-sized displays** by adding `-vf pad=ceil(iw/2)*2:ceil(ih/2)*2` to the ffmpeg output args before `libx264`/`yuv420p`, ensuring width/height are even. > > Adds `TestFFmpegArgs_PadsOddDimensions` to assert the new `-vf` filter is present in the generated argument list. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 72b0e42. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> Co-authored-by: Sayan- <1415138+Sayan-@users.noreply.github.com>
1 parent 08fbd6d commit 6218b60

2 files changed

Lines changed: 19 additions & 0 deletions

File tree

server/lib/recorder/ffmeg_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,21 @@ func TestFFmpegRecorder_Params(t *testing.T) {
6868
assert.Equal(t, *params.OutputDir, *got.OutputDir)
6969
}
7070

71+
func TestFFmpegArgs_PadsOddDimensions(t *testing.T) {
72+
tempDir := t.TempDir()
73+
args, err := ffmpegArgs(defaultParams(tempDir), filepath.Join(tempDir, "out.mp4"))
74+
require.NoError(t, err)
75+
76+
var vf string
77+
for i, a := range args {
78+
if a == "-vf" && i+1 < len(args) {
79+
vf = args[i+1]
80+
break
81+
}
82+
}
83+
assert.Equal(t, "pad=ceil(iw/2)*2:ceil(ih/2)*2", vf)
84+
}
85+
7186
func TestFFmpegRecorder_ForceStop(t *testing.T) {
7287
tempDir := t.TempDir()
7388
rec := &FFmpegRecorder{

server/lib/recorder/ffmpeg.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,10 @@ func ffmpegArgs(params FFmpegRecordingParams, outputPath string) ([]string, erro
498498

499499
// Output options next
500500
args = append(args, []string{
501+
// yuv420p requires even width and height; pad odd source dimensions by one pixel
502+
// so libx264 doesn't fail to open the encoder.
503+
"-vf", "pad=ceil(iw/2)*2:ceil(ih/2)*2",
504+
501505
// Video encoding
502506
"-c:v", "libx264",
503507
"-profile:v", "high", // Explicit web-compatible profile

0 commit comments

Comments
 (0)