Pre-delivery QA: black frames, silence, and a clean cut
A clip can look fine on a casual watch and still ship with a dead 2-second black hole or a silent gap where narration should be. This is the scan that catches both, the cut method that avoids the most common keyframe trap, and the habit of writing down what you shipped.
Scan for black frames and silence
Run blackdetect and silencedetect as null-output filters and grep
the log for their markers:
ffmpeg -hide_banner -loglevel info -i final.mp4 \
-vf "blackdetect=d=0.1:pix_th=0.10" -af "silencedetect=n=-45dB:d=0.4" -f null - 2>&1 \
| grep -E "black_start|silence_start|silence_end" \
|| echo "clean: no black frames or silent gaps flagged."
blackdetect=d=0.1:pix_th=0.10 flags any run of frames at least 0.1s long where
at least 90% of pixels read as near-black (pix_th is the per-pixel darkness
threshold). silencedetect=n=-45dB:d=0.4 flags any stretch of audio at least
0.4s long that stays below −45dB. Both thresholds are tunable per source — a dark
cinematic shot needs a stricter pix_th than a bright product demo, and a
noisier recording needs a lower n floor than a clean studio track. The toolkit
script prints a "clean" line only when neither filter reports anything, and returns each
black_start/silence_start/silence_end marker it found
otherwise so you can line them up against your edit.
Frame-accurate cut (and why it re-encodes)
Trim to the final duration with a re-encode, not a stream copy:
ffmpeg -hide_banner -y -ss 00:00:00.000 -to 00:00:05.000 -i shot01_captioned.mp4 \
-c:v libx264 -preset veryfast -crf 18 -c:a aac -b:a 192k -avoid_negative_ts make_zero shot01_final.mp4
START/END are on the source timeline (seconds like
2.5, or HH:MM:SS.mmm).
The keyframe-snap trap
A stream-copy cut (-c copy, no re-encode) can only start a new file at an
existing keyframe — it cannot decode-and-re-encode a partial GOP, so ffmpeg snaps the cut
point to the nearest keyframe at or before your requested timestamp. On footage with sparse
keyframes that snap can land a full second or more off, silently, with no error. Re-encoding
with -c:v libx264 -preset veryfast -crf 18 forces ffmpeg to actually decode and
re-encode from the exact requested frame, at the cost of encode time and a fresh compression
pass instead of a lossless copy. -avoid_negative_ts make_zero resets the output
timestamps to start at zero instead of inheriting the source's original offset, which avoids
a common sync glitch in some players.
Delivery-receipt habit
Once QA is clean, write down what actually shipped instead of trusting memory. An
ffprobe pass gives you the hard facts for the receipt:
ffprobe -v error -show_format -show_streams -of json shot01_final.mp4
A minimal receipt records: duration, resolution/fps, video codec and pixel format, audio codec/bitrate/sample rate, the integrated loudness and true peak from your loudnorm verification pass, subtitle burn-in status, source provenance, and QA status (clean scan or explained exceptions). It takes two minutes and turns "I think this is fine" into something checkable later, by you or anyone else on the delivery.
Related
Read the free preview for the SOP Pack's table of contents and one full script-gate check.
The full pack ($19) has the worked example, prompts and QA receipts; the $9 toolkit is just the scripts.