-
-
Save davidwebca/e26186b8f4c6795b19c043fffb6f9861 to your computer and use it in GitHub Desktop.
# 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 |
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!
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/
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
- 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 - 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.
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 👌
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
Ooh nice catch, thanks!
Any way we are getting a Windows Command line version?
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.
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.
Nice, thanks for the input!
nice script!
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).
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.