Skip to content

Instantly share code, notes, and snippets.

@davidwebca
Last active December 18, 2023 15:29
Show Gist options
  • Save davidwebca/e26186b8f4c6795b19c043fffb6f9861 to your computer and use it in GitHub Desktop.
Save davidwebca/e26186b8f4c6795b19c043fffb6f9861 to your computer and use it in GitHub Desktop.
Detect black screen and split with ffmpeg remux
# Splits video to separate scenes files when full black frames are found in the video
# Inspired by https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e
# Who got inspired by https://stackoverflow.com/a/38205105
#!/bin/bash
file=""
out="./"
dur=0.05
stripaudio=""
ratio=1.00
th=0.05
add=0.00
trim=0.00
usage () {
echo "Usage: $(basename $0) [[[-o folder] [-d black duration]] | [-h]] -f file.mp4"
echo
echo "Options:"
echo "-f, --file Input file"
echo "-o, --out Outpup files folder path, default"
echo " to current folder"
echo "-d, --dur Duration for black detection in seconds. 0.05 default (practical single frame)"
echo "-r, --ratio ffmpeg pic_th : Set the threshold for considering a picture black. 1.00 default"
echo "-th, --threshold ffmpeg pix_th : Set the threshold for considering a pixel black. 0.00 default."
echo "-t, --trim Substracts to splitting timestamp in seconds. 0 default"
echo "-a, --add Adds to splitting timestamp in seconds. 0 default"
echo "-sa, --strip-audio Strip audio"
echo "-h, --help Display this help message"
echo
echo "Example: split.sh -d 0.5 -o /tmp/parts -f file.mp4"
echo "Splits file.mp4 file to scenes with black frames during more than 0.5 second"
echo "and saves output parts to /tmp/parts folder"
}
if [ "$1" = "" ]; then
usage
fi
while [ "$1" != "" ]; do
case $1 in
-f | --file )
shift
file=$1
;;
-d | --dur )
shift
dur=$1
;;
-r | --ratio )
shift
ratio=$1
;;
-th | --threshold )
shift
th=$1
;;
-o | --out )
shift
out=$1
;;
-t | --trim )
shift
trim=$1
;;
-a | --add )
shift
add=$1
;;
-sa | --strip-audio )
stripaudio="-an"
;;
-h | --help )
usage
exit
;;
* )
usage
exit 1
esac
shift
done
cut_part () {
duration_flag=""
if [[ "$3" != "" ]]; then
duration_flag="-t"
fi
echo "cutting from $1 during $3"
printf -v fileout "$out/%04d_%s" $2 $filename
ffmpeg -y -loglevel error -hide_banner -i $file -ss $1 $stripaudio $duration_flag $3 $fileout < /dev/null
}
filename=`basename $file`
mkdir -p $out
timefrom=0
i=1
ffmpeg -i $file -vf blackdetect=d=$dur:pic_th=$ratio:pix_th=$th -f null - 2> ffout
black_start=( $(grep blackdetect ffout | grep black_start:[0-9.]* -o | grep "[0-9]*(\.[0-9]*)?" -oE) )
black_duration=( $(grep blackdetect ffout | grep black_duration:[0-9.]* -o | grep "[0-9]*(\.[0-9]*)?" -oE) )
> timestamps
for ii in "${!black_start[@]}"; do
half=$(bc -l <<< "${black_duration[$ii]}/2")
middletime=$(bc -l <<< "${black_start[$ii]} + $half")
echo $middletime | LC_ALL=en_US.UTF-8 awk '{printf "%f", $0}' >> timestamps
echo "" >> timestamps
done
while read -r timestamp; do
duration=`bc -l <<< "$timestamp-$timefrom+$add-$trim" | LC_ALL=en_US.UTF-8 awk '{printf "%f", $0}'`
cut_part $timefrom $i $duration
timefrom=`bc -l <<< "$timestamp+$add-$trim" | LC_ALL=en_US.UTF-8 awk '{printf "%f", $0}'`
i=`expr $i + 1`
done < timestamps
if [[ "$timefrom" != 0 ]]; then
cut_part $timefrom $i
fi
@davidwebca
Copy link
Author

That message just means you either didn’t install or didn’t install correctly ffmpeg. There you go. Here’s how to install it for Ubuntu : https://linuxize.com/post/how-to-install-ffmpeg-on-ubuntu-18-04/

@husim0
Copy link

husim0 commented Nov 23, 2021

First of all, I would like to thank you for your script, it's really useful for my needs. I don't know how I would have managed what I need to do without you!

I had some accuracy problem with splitting : videos had about 5 seconds from the previous one to the next. Black scenes were detected properly but splitting was wrong. Weird.

Then I managed to resolve this problem by replacing :

ffmpeg -y -loglevel error -hide_banner -ss $1 -i $file -c:v copy -c:a copy $stripaudio $duration_flag $3 $fileout < /dev/null

By :

ffmpeg -y -loglevel error -hide_banner  -i $file -ss $1 $stripaudio $duration_flag $3 $fileout < /dev/null
  1. I put -i file before the -ss option. Apparently doing this ffmpeg does not work the same that way and is more accurate : https://stackoverflow.com/questions/45004159/ffmpeg-ss-and-t-for-cutting-mp3
  2. I removed -c:v copy -c:a copy as it seems it can create inaccuracy : https://superuser.com/questions/1129396/ffmpeg-ss-t-seeking-output-is-not-accurate-any-workarounds ; that way file is re-encoded so it's slower but it works as expected.

@davidwebca
Copy link
Author

Thanks so much! I’ll give those edits a go in a project I have to do soon and I’ll update the gist after testing 👌

@VictorKern
Copy link

Hello ! That's a really nice script !

I just noticed one issue when using it: when you are getting the black_start and black_duration value, if the value is an integer grep will not catch it. (for example "7" is not recognized by grep "[0-9]*\.[0-9]*" -o since there is no ".")

I just replaced those final grep by
grep "[0-9]*(\.[0-9]*)?" -oE
so the entire lines become
black_start=( $(grep blackdetect ffout | grep black_start:[0-9.]* -o | grep "[0-9]*(\.[0-9]*)?" -oE) )
black_duration=( $(grep blackdetect ffout | grep black_duration:[0-9.]* -o | grep "[0-9]*(\.[0-9]*)?" -oE) )

and it works just fine

@davidwebca
Copy link
Author

Ooh nice catch, thanks!

@expecttheunusual
Copy link

Any way we are getting a Windows Command line version?

@davidwebca
Copy link
Author

davidwebca commented Feb 13, 2022

I can’t translate this bash script to a straight Windows version without learning about it first, but I used ffmpeg in Windows before, so that can give you a head start. The other thing I know is possible is to use WSL which allows to use bash as a sub system directly in Windows 10/11.

@leingang
Copy link

leingang commented Jul 3, 2022

Thank you for doing this! I was getting some unexpected behavior until I realized that your script's default's are different from ffmpeg's defaults for the blackdetect filter. To match the defaults, you could set

dur=2.00
ratio=0.98
th=0.10

at the beginning of the script.

@davidwebca
Copy link
Author

Nice, thanks for the input!

@hadees
Copy link

hadees commented Sep 13, 2022

nice script!

@michaelkarlcoleman
Copy link

This worked great for me. In my instance, was splitting a transfer from a VHS videotape, so had to use the ffmpeg blackdetect defaults (see above).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment