Purpose
Take a silent (or near-silent) video master and a music file, mix them with sane defaults, and write a final video with audible, well-balanced music. Loudness is normalized so the output sits at roughly -14 LUFS (platform-friendly for social and YouTube). Fades in and out are applied automatically, and music duration is matched to video duration by trimming or looping with a crossfade.
Inputs
--video <path>to a silent video (any common container).--music <path>to a music file (mp3 or wav). Must be a real music track fromfind-music,create-programmatic-music-bed, or user-supplied with a documented source. Synthesized tones (ffmpeg -f lavfi -i sine=..., white noise, oscillator output) are rejected by default.--out <path>for the mixed output.- Optional
--target-lufs <float>(default -14). - Optional
--fade-in <seconds>and--fade-out <seconds>(defaults: 0.5 and 1.0). - Optional
--duck-vo <path>to a voiceover track to mix on top with side-chain ducking. - Optional
--allow-placeholder-toneflag (off by default) for explicit smoke-test runs where the upstream user has acknowledged the music is a placeholder.
Workflow
- Verify music is real. Probe the music file with
ffprobe. Reject if the metadata or filename suggests a tone (lavfi,sine,tone,placeholder,synth), or if a spectral check shows a sustained pure-tone profile. Reject also if the run's caller manifest recordsmusic_source: placeholder. The run isblockedunless--allow-placeholder-toneis set. - Probe video duration with
ffprobeand music duration. - If music is shorter than video, loop with a crossfade so the join is seamless. If longer, trim with a fade-out at the cut.
- Apply loudnorm to bring music to the target LUFS. Apply fade-in at the start and fade-out near the end of the video.
- If
--duck-vois provided, mix the VO at -16 LUFS and side-chain compress the music when VO is active. - Run a single ffmpeg invocation that combines the silent video stream with the processed music (and optional VO) and writes the output with H.264 video and AAC audio.
- Write
manifest.jsonwith input paths, durations, target LUFS, music provenance (real | placeholder-tone-allowed), and the final output path. Writeverification.md.
Output
- A muxed video file at the requested output path.
manifest.jsonandverification.md.
Quality Checks
- Output duration matches the input video duration exactly.
- Output has an audible audio stream (probe confirms a non-silent track).
- Music provenance is recorded in the manifest and is not
placeholderfor production runs. - Measured loudness is within ±1 LUFS of the target.
- No clipping in peaks (true peak below -1 dBTP).
Failure Modes
- ffmpeg or ffprobe not installed — surface the error and mark blocked.
- Music file is corrupt or unreadable — surface the error.
- Music file is a synthesized tone or placeholder — mark
blockedand direct the caller to re-runfind-music. Do not silently mix a tone under a delivery video. - Video already has an audio stream — by default, replace it; surface the replacement in the manifest.
- Loudness target cannot be hit because the music is too quiet — re-encode at higher gain and re-measure.