-
-
Save achesco/4dc2ebf13378a0a61fc26c7fe01f539e to your computer and use it in GitHub Desktop.
| # 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 |
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
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
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