#162·video-use

Audio click at segment boundaries (AAC priming with -c copy concat) + automatic boundary QC

Author: axisrowCreated Sep 15, 2026Updated Sep 15, 2026

Summary

Two related improvements to the render pipeline:

  1. Bug: render.py produces an audible click at every segment boundary when the concat step uses -c copy on the audio track.
  2. Feature request: an automatic post-render QC step that detects such boundary artifacts (audio clicks, frame flashes) so they never reach the user.

Repro

  1. Render any EDL with 2+ segments: python helpers/render.py edl.json -o out.mp4
  2. Listen at each segment boundary: a click is audible ~100 ms after the join, mid-speech splices are the most noticeable.

Root cause

concat_segments() uses the concat demuxer with -c copy. Each extracted segment carries its own AAC encoder priming/padding samples; with stream copy these misalign at every joint. Sample-level analysis of a rendered 2-segment preview (48 kHz mono):

  • discontinuity sits exactly on an AAC frame boundary ~102 ms after the concat point (max |Δsample| = 2038 vs local speech p95 ≈ 400)
  • the step is already present in base_preview.mp4 (pre-loudnorm), so it is introduced by concat, not by loudnorm

Fix that worked for us

Re-encode only the audio into a single continuous stream at concat; keep the video lossless (Rule 2 is about avoiding double-encoded video, and this keeps that property):

python
cmd = [
    "ffmpeg", "-y",
    "-f", "concat", "-safe", "0",
    "-i", str(concat_list),
    "-c:v", "copy",
    "-c:a", "aac", "-b:a", "192k", "-ar", "48000",
    "-movflags", "+faststart",
    str(out_path),
]

Feature request: automatic boundary QC

Self-eval currently relies on the agent eyeballing timeline_view PNGs; the click was caught by the user, not by the pipeline. A helpers/verify_cuts.py <edl.json> <rendered.mp4> that runs after every render would catch this class of defect mechanically:

  • Audio click check — for each output-timeline boundary b (cumulative sum of end - start), decode [b-0.25, b+0.25] to 16-bit PCM mono 48 kHz, compute adjacent-sample deltas, flag a click when max(|Δ|) > ratio × p95(local deltas) (e.g. ratio 5–8, plus an absolute floor to stay quiet in near-silence). Our real click scores ~5× above local speech, so the separation is comfortable.
  • Flash/black-frame check — extract frames around each boundary, compute mean luma per frame, flag adjacent-frame luma swings beyond a threshold (catches white flashes and dropped black frames at joins).
  • Print a pass/fail line per boundary and exit non-zero on any failure, so it can gate the "show the preview to the user" step.

Environment

  • macOS (Apple Silicon), ffmpeg 9.0.1_1 (ffmpeg-full)
  • render.py from current main