From fe92af30d664664fd8ab7655ab0b4602e0dda923 Mon Sep 17 00:00:00 2001 From: Boris Batkin Date: Sat, 6 Jun 2026 16:25:47 -0700 Subject: [PATCH] convert_recording: add --crf and --preset knobs Surfaced recording dasImguiImplot's realtime_scroll: a full-motion clip (the whole plot scrolls every frame) defeats H.264 inter-frame compression and ran 2.77 MB at the defaults, ~2x the other tutorials. The fix is to compress harder WITHOUT touching quality: a slower x264 preset. crf 23 + --preset veryslow lands the same clip at 1.54 MB with visually identical lines (no mosquito noise on the dark background - raising crf to 32 to hit the size would have introduced exactly that). - --crf (default 23): libx264 quality, unchanged default so existing recordings re-encode the same. - --preset (default medium): libx264 preset; veryslow for full-motion clips. skills/recording.md documents both and the "shrink via --preset, not --crf" rule. Co-Authored-By: Claude Opus 4.8 (1M context) --- skills/recording.md | 7 +++++++ utils/convert_recording.das | 8 +++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/skills/recording.md b/skills/recording.md index 71235b2b..4da598d9 100644 --- a/skills/recording.md +++ b/skills/recording.md @@ -89,6 +89,13 @@ Needs a running TTS server at `--base-url` (default `http://127.0.0.1:8880/v1`). --apng /doc/source/_static/tutorials/buttons.apng ``` +Mostly-static recordings (a plot with a brief gesture) land ~0.5–0.8 MB at the defaults. A +**full-motion** clip (e.g. a realtime scroll, where every frame is a full-frame change) defeats +H.264 inter-frame compression and runs several MB. Shrink it the lossless way — add +`--preset veryslow` (better compression at the **same** quality), **not** by raising `--crf` +(which trades away the quality the clip needs). `--crf` stays at the clean default `23`; both are +knobs only for this case. + Only the resulting `.mp4` is tracked. The `.apng`, `voiceover/*.wav`, `*.manifest.json`, `*.sidecar.json`, `*_music.wav`, and `*.mp4.ffmpeg.txt` are all intermediates and gitignored. ## Mental model diff --git a/utils/convert_recording.das b/utils/convert_recording.das index 1b7d5b27..5ccde374 100644 --- a/utils/convert_recording.das +++ b/utils/convert_recording.das @@ -36,6 +36,12 @@ struct Config { @clarg_doc = "Fade in/out length, milliseconds" fade_ms : int = 100 + @clarg_doc = "libx264 quality (lower = better/larger). Default 23 - keep it here for excellent quality; shrink full-motion clips with --preset, not by raising this" + crf : int = 23 + + @clarg_doc = "libx264 preset (slower = smaller at the SAME quality). Default medium; use veryslow for full-motion clips (e.g. realtime scroll) to cut size without touching crf" + preset : string = "medium" + @clarg_doc = "daScript root (find_sf2 + default daslang exe). Default: this exe's das-root" das_root : string @@ -158,7 +164,7 @@ def main : int { var fargs <- ["ffmpeg", "-y", "-r", "{fps_eff}", "-i", cfg.apng, "-i", music_wav] fargs |> push_from(vo_inputs) fargs |> push_from(["-filter_complex", fg, "-map", "0:v", "-map", "[aout]", - "-c:v", "libx264", "-crf", "23", "-pix_fmt", "yuv420p", + "-c:v", "libx264", "-preset", cfg.preset, "-crf", "{cfg.crf}", "-pix_fmt", "yuv420p", "-movflags", "+faststart", "-shortest", out_mp4]) // --- save the command, then run it ---