With `ffmpeg`, how to avoid in a video black segments by keeping last frame?
Yesterday 6:19pm
There's no single built-in "replace black with previous frame" filter, but you can do it in two passes: detect the black intervals with blackdetect, then cut the video around them, freeze the last non-black frame with tpad, and concat everything back. Since total duration is unchanged, audio stays in sync and can be stream-copied.
You'll get lines like black_start:4.20 black_end:6.90 black_duration:2.70. Tune d (minimum black duration) and pix_th (pixel darkness threshold) to your content.
2. Automate the replacement
bash
#!/usr/bin/env bash
in="input.mp4"; out="output.mp4"
readarray -t blacks < <(ffmpeg -hide_banner -i "$in" \
-vf blackdetect=d=0.1:pix_th=0.10 -an -f null - 2>&1 \
| sed -n 's/.*black_start:\([0-9.]*\) black_end:\([0-9.]*\).*/\1 \2/p')
n=${#blacks[@]}; echo "Found $n black segment(s)"
graph="[0:v]split=$((n+1))"
for i in $(seq 0 "$n"); do graph+="[s$i]"; done
graph+=";"
prev=0; i=0; ins=""
for seg in "${blacks[@]}"; do
b=${seg% *}; e=${seg#* }
dur=$(awk -v a="$b" -v b="$e" 'BEGIN{printf "%.3f", b-a}')
# non-black part before the gap, then freeze its last frame for the gap length
graph+="[s$i]trim=$prev:$b,setpts=PTS-STARTPTS,tpad=stop_mode=clone:stop_duration=$dur[p$i];"
ins+="[p$i]"; prev=$e; ((i++))
done
graph+="[s$n]trim=$prev,setpts=PTS-STARTPTS[p$n];"
ins+="[p$n]"
graph+="${ins}concat=n=$((n+1)):v=1:a=0[v]"
ffmpeg -i "$in" -filter_complex "$graph" -map "[v]" -map "0:a?" -c:a copy "$out"
How it works:
Each black interval (b, e) becomes: keep trim(prev → b) normally, then tpad=stop_mode=clone:stop_duration=(e−b) repeats the last frame for exactly the black duration.
setpts=PTS-STARTPTS resets each piece's timestamps so concat joins them seamlessly.
The final trim=$prev keeps everything after the last black segment.
Caveats
If the video starts with black, trim=0:0 would be empty — for that first piece use [s0]trim=0:$e,tpad=start_mode=clone:start_duration=$e instead (clones the first non-black frame backwards).
Video must be re-encoded (default libx264; add -crf 18 or similar if quality matters). Audio is copied untouched since timing is preserved.
If your black segments are actually scene transitions or intentional fades, raise d so short dark moments aren't frozen.
If instead you'd rather remove the black segments entirely (shortening the video), drop the tpad from the graph — but then you'd need to cut the audio the same way to keep sync.
Yesterday 6:19pm
This is a shared conversation and may include unverified content that doesn't reflect Mistral AI's views. Login to resume chatting.