Skip to content

Instantly share code, notes, and snippets.

@achesco
Last active October 25, 2025 04:38
Show Gist options
  • Select an option

  • Save achesco/4dc2ebf13378a0a61fc26c7fe01f539e to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown

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

@davidwebca
Copy link
Copy Markdown

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
Copy Markdown

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

@davidwebca
Copy link
Copy Markdown

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
Copy Markdown

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

@jazzyjackson
Copy link
Copy Markdown

jazzyjackson commented Dec 23, 2024

Thanks folks, I'm happily slicing up old home video tapes with this script.

Thanks to nielsbom for the hardware acceleration tip, I found the equivalent to h264_videotoolbox for my nvidia-GPU available in WSL is h264_nvenc and get about an 8x speedup.

With each tape there's usually a few scenes that I want to merge back together, so I wrote a script to run after this one; if I've identified that, say, all the videos from 0010_xyz.mp4 until 0018_xyz.mp4 should be one video, I can run the following:

./merge.sh 0010_xyz.mp4 0018_xyz.mp4

and the script will produce the intermediate list.txt of filenames to pass to ffmpeg's concat and write to file 0010-0018_xyz.mp4. it doesn't delete the input files of course, I do that manually after confirming the merge looks good.

https://gist.github.com/jazzyjackson/bf9282df0a40d7ef471e6676f282831e

And in case the scene splitting script missed something I need to split up manually, I can recommend this fun little utility that lets you seek, set markers, and split scenes without leaving your terminal: https://github.com/wong-justin/vic

Cheers

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