Skip to content

Instantly share code, notes, and snippets.

@achesco
Last active January 14, 2024 19:40
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save achesco/4dc2ebf13378a0a61fc26c7fe01f539e to your computer and use it in GitHub Desktop.
Save achesco/4dc2ebf13378a0a61fc26c7fe01f539e to your computer and use it in GitHub Desktop.
Detect and split video to scenes with ffmpeg
# Splits video to separate scenes files
# Inspired by https://stackoverflow.com/a/38205105
#!/bin/bash
file=""
out="./"
diff=0.4
bitrate="512k"
trim=0
stripaudio=""
usage () {
echo "Usage: $(basename $0) [[[-o folder] [-d ratio]] | [-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, --diff Number from 0 to 1, default to 0.4."
echo " Scene change difference factor"
echo "-b, --bitrate Bitrate to encode parts, default to 512k"
echo "-t, --trim Trim last given seconds number, default 0"
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 change more than 0.5"
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 | --diff )
shift
diff=$1
;;
-o | --out )
shift
out=$1
;;
-b | --bitrate )
shift
bitrate=$1
;;
-t | --trim )
shift
trim=$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
ffmpeg -loglevel error -hide_banner -ss $1 $duration_flag $3 -i $file \
-vcodec libx264 -movflags faststart -b $bitrate $stripaudio \
-y $out/`printf "%04d_%s" $2 $filename` < /dev/null
}
filename=`basename $file`
mkdir -p $out
timefrom=0
i=1
while read -r timestamp; do
duration=`bc <<< "$timestamp-$timefrom-$trim" | awk '{printf "%f", $0}'`
cut_part $timefrom $i $duration
timefrom=$timestamp
i=`expr $i + 1`
done < <(
ffmpeg -i $file -filter:v "select='gt(scene,$diff)',showinfo" -f null - 2>&1 | \
grep Parsed_showinfo | grep pts_time:[0-9.]* -o | grep "[0-9]*\.[0-9]*" -o
)
if [ $timefrom != 0 ]; then
cut_part $timefrom $i
fi
@davidwebca
Copy link

Hi! Just checking in if you ended up with a working solution for this script? I'm very interested in it. 👀

@davidwebca
Copy link

davidwebca commented Oct 6, 2020

Sorry for bothering with another comment. I needed a script like this but the scene detection from ffmpeg didn't work in my use case. Gladly, in my videos, I have fully black screens so I touched up the script and created a version that would detect these and split without encoding (remuxing). I also didn't want to bother fixing the output to feed it to the loop so I ended up writing two log files to parse. https://gist.github.com/david-treblig/e26186b8f4c6795b19c043fffb6f9861

@jeremymeyers
Copy link

@davidwebca the link you posted is borked. is there a new location?

@davidwebca
Copy link

Oh lord, must be because I changed my username. Github redirects repos but not gists... interesting 🤔 Well, here you go: https://gist.github.com/davidwebca/e26186b8f4c6795b19c043fffb6f9861

@nielsbom
Copy link

I forked:

  • following some ShellCheck advice
  • reading bitrate from original file
  • being able to split files with spaces in their name
  • using Apple Silicon hardware for some speedup

https://gist.github.com/nielsbom/c86c504fa5fd61ae9530caec654c6ae6

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