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
@yashchellani
Copy link

yashchellani commented Feb 1, 2021

hey @davidwebca! I'd like to use this script to split my video by black-screen detection, but I'm pretty new to shell scripting. I tried running a downloaded copy of your script with my .mp4 file in Windows, but I haven't had any success doing so.

I have a .mp4 file stored in the same directory as this script. Could you please give me some steps to follow for running your script with my own file?

@davidwebca
Copy link
Author

Hi! First there’s two things to know: 1) the script is dependent on ffmpeg which is the most popular cli to edit video and audio. You need to have it installed. 2) This bash script was written on Mac, should be compatible on Linux but I can’t say for sure about windows. I know there’s a way now to run bash in windows ( https://www.howtogeek.com/261591/how-to-create-and-run-bash-shell-scripts-on-windows-10/ ) but it’s mostly about rewriting the syntax for windows specifics. Since ffmpeg is also distributed on windows, everything else should stay the same (filters logic etc).

Let me know if you manage to make it work. I’m pretty busy these days but I could probably find time in the next few days to test it on windows.

@yashchellani
Copy link

thanks so much for your reply!
Okay so I tried running the script on Ubuntu WSL, with the following command:
./split-black-scenes.sh -d 0.5 -f new.mp4
new.mp4 is the name of video file that I'm trying to split.
Running this results in an ffout file with the following output:
./split-black-scenes.sh: line 99: ffmpeg: command not found

I'm not able to understand what I've done wrong. here. Any inputs would help!

@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