Skip to content

Instantly share code, notes, and snippets.

@nielsbom
Forked from achesco/split-to-scenes.sh
Last active January 15, 2024 01:50
Show Gist options
  • Save nielsbom/c86c504fa5fd61ae9530caec654c6ae6 to your computer and use it in GitHub Desktop.
Save nielsbom/c86c504fa5fd61ae9530caec654c6ae6 to your computer and use it in GitHub Desktop.
Detect and split video to scenes with ffmpeg
#!/bin/bash
# set -Eeuo pipefail
# Splits video to separate scenes files
# Source: https://gist.github.com/achesco/4dc2ebf13378a0a61fc26c7fe01f539e
# Inspired by https://stackoverflow.com/a/38205105
# The "-c:v h264_videotoolbox \" argument makes it work faster on Apple Silicon
# computers.
# ❗The bitrate argument is overriden in this version, we look at the original bitrate.
file=""
out="./"
diff=0.4
bitrate="512k"
trim=0
stripaudio=""
script_name=$(basename "$0")
usage () {
echo "Usage: $script_name [[[-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
output_file="$out"/$(printf "%04d_%s" "$2" "$filename_without_spaces")
echo "Creating ${output_file}"
ffmpeg \
-loglevel error \
-hide_banner \
-ss "$1" \
$duration_flag "$3" \
-i "$file" \
-vcodec libx264 \
-c:v h264_videotoolbox \
-movflags faststart \
-b "$bitrate_ffprobe" \
$stripaudio \
-y "$output_file" < /dev/null
}
filename=$(basename "$file")
filename_without_spaces="${filename// /_}"
bitrate_ffprobe=$(ffprobe -v quiet -select_streams v:0 -show_entries stream=bit_rate -of default=noprint_wrappers=1 "$filename" | cut -d"=" -f 2-)
mkdir -p "$out"
timefrom=0
i=1
echo "finding parts..."
while read -r timestamp; do
duration=$(bc <<< "$timestamp-$timefrom-$trim" | awk '{printf "%f", $0}')
cut_part "$timefrom" "$i" "$duration"
timefrom=$timestamp
echo from "$timefrom" for "$duration" seconds
i=$(( i + 1))
done < <(
ffmpeg -i "$file" -c:v h264_videotoolbox -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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment