From a84e02b494d4724be00c0aa84ff157cfaf1687e4 Mon Sep 17 00:00:00 2001 From: jenstandstad Date: Sat, 25 Jul 2026 14:09:17 +0200 Subject: [PATCH] Removing infinite buffering length for a background video --- gnommo/cli.py | 5 ++++- gnommo/preprocessor.py | 4 ++++ gnommo/renderer.py | 15 +++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/gnommo/cli.py b/gnommo/cli.py index 35eb2cb..6e012e4 100644 --- a/gnommo/cli.py +++ b/gnommo/cli.py @@ -2802,7 +2802,10 @@ def cmd_preprocess( entry = dict(existing_narration.get(segment_id, {})) # Always record the plain path; the res subdir shift happens at render for low/tiny. entry["processed_file"] = f"processed/{segment_id}_processed.mov" - entry.setdefault("use_audio_channels", "auto") + # Store the RESOLVED audio channel (preprocess_video mutated "auto" → the + # detected left/right/both). This is the concrete value; render reads it and + # never re-runs the auto-detect probe. + entry["use_audio_channels"] = segment_source.use_audio_channels or "auto" entry.setdefault("defer_loudnorm", False) existing_narration[segment_id] = entry diff --git a/gnommo/preprocessor.py b/gnommo/preprocessor.py index 77d236b..f312fbc 100644 --- a/gnommo/preprocessor.py +++ b/gnommo/preprocessor.py @@ -715,6 +715,10 @@ def preprocess_video( if channel == "auto": channel = _resolve_auto_channel(current_input) print(f" Auto channel detection: using '{channel}'") + # Persist the resolved channel on the source so the caller can store it back + # to narration.json — the auto-detect (a full-file volumedetect) then runs + # ONCE here at preprocess, never again at render time. + video_source.use_audio_channels = channel elif channel in ("left", "right"): is_silent, max_vol = check_audio_channel_silent(current_input, channel) if is_silent: diff --git a/gnommo/renderer.py b/gnommo/renderer.py index 03caa9d..56f4a8c 100644 --- a/gnommo/renderer.py +++ b/gnommo/renderer.py @@ -319,12 +319,15 @@ def _build_audio_channel_filter(use_audio_channels: str) -> str: use_audio_channels: "both", "left", or "right" Returns: - Filter string (e.g., "pan=mono|c0=c1") or empty string for "both" + Filter string, or empty string for "both". + + Matches preprocess (apply_audio_normalize): a selected single channel is + duplicated to BOTH output channels (centered stereo), not collapsed to mono. """ if use_audio_channels == "left": - return "pan=mono|c0=c0" + return "pan=stereo|c0=c0|c1=c0" elif use_audio_channels == "right": - return "pan=mono|c0=c1" + return "pan=stereo|c0=c1|c1=c1" return "" # "both" - no filter needed @@ -458,9 +461,13 @@ def build_ffmpeg_command(plan: RenderPlan, output_path: Path) -> list[str]: ) image_extensions = {".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".webp"} bg_is_image = bg_path.suffix.lower() in image_extensions - # Loop background videos infinitely + # Loop background videos, but BOUND the loop to the output length. An + # unbounded -stream_loop -1 input can make ffmpeg read/buffer ahead without + # limit, which balloons memory on long renders — cap it with an input -t so + # it EOFs cleanly at total_duration. if not bg_is_image: cmd.extend(["-stream_loop", "-1"]) + cmd.extend(["-t", f"{plan.total_duration:.3f}"]) # Duration of background video is irrelevant (looped or image) — skip analysis cmd.extend(["-analyzeduration", "0", "-probesize", "1000"]) cmd.extend(["-i", str(bg_path)])