Skip to content

Instantly share code, notes, and snippets.

@fritschy
Last active June 1, 2021 13:47
Show Gist options
  • Save fritschy/ea8e944469433ed78fe3f3af98fb1042 to your computer and use it in GitHub Desktop.
Save fritschy/ea8e944469433ed78fe3f3af98fb1042 to your computer and use it in GitHub Desktop.
Wrapper around my mostly-used-part of ffmpeg ...
#!/usr/bin/env zsh
set -e
FFMPEG="$HOME/Downloads/ffmpeg-4.2.1-amd64-static/ffmpeg"
CRF=20
VCODEC=libx264
PRESET=
DEBUG=x
FORCE=()
BASENAME=0
MINSIZE=0
AC=()
AB=512000
TUNE=()
CROP="w=in_w:h=in_h"
RESAMPLE=()
PARAMS=()
RATE=()
SUSPEND=0
SUSPENDER=
VF=()
MERGE=()
EXTRA=()
EXTRANAME=()
DESHAKE1=()
DESHAKE2=()
ALL=(-map 0)
CHPST=()
ME=()
CROPDETECT=0
SIZE=""
FMT=""
x() {
echo "> $*"
eval "$@"
}
suspend_on_user_logon() {
W=(who --ips)
W=(w -shuoif)
U=$(whoami)
exec 2>/dev/null
echo "Suspending process(es) $* when a user logs on..."
pid_exists() {
kill -0 $1 2>/dev/null
}
stopped=
while sleep 0.5
do
existing=()
for i in $*
do
if pid_exists $i
then
existing=($i $existing)
else # replace program names with PIDs
p=$(pidof $i)
pid_exists $p && existing=($p $existing)
fi
done
if [ -z "$existing" ]
then
continue
fi
if $W | grep -qv "^$U "
then
if [ -z "$stopped" ]
then
kill -STOP $existing
echo
echo "$(date +%F+%T): Suspended $existing, $($W | grep -v "^$U " | cut -d\ -f1 | sort -u | tr '\n' ' ')"
stopped=1
fi
elif [ -n "$stopped" ]
then
echo "$(date +%F+%T): Continued $existing"
kill -CONT $existing
stopped=
fi
done
}
do_nice() {
nice -0 "$@"
}
append_vf() {
if [ "$VF" ]
then
VF=$VF,$1
else
VF=$1
fi
}
prepend_vf() {
if [ "$VF" ]
then
VF=$1,$VF
else
VF=$1
fi
}
while [ "$1" ] # BEGIN OF ARGUMENT PARSING
do
case "$1" in
-ffmpeg|-ff) #HELP# choose ffmpeg executable #
FFMPEG=$2
shift
;;
-deshake) #HELP# apply deshake filter #
DESHAKE1=vidstabdetect=shakiness=10:show=2:mincontrast=0.7
DESHAKE2=vidstabtransform=crop=black:smoothing=180:optzoom=0:interpol=bicubic
;;
-size) #HELP# set size of output video, keep aspect reatio in mind! #
SIZE=$2
shift
;;
-me) #HELP# show motion estimate #
case $2 in
-*)
ME=mestimate=epzs:mb_size=32:search_param=64
;;
*)
ME=$2
shift
;;
esac
;;
-deshake1) #HELP# fine tune deshake1 #
DESHAKE1=$2
shift
;;
-deshake2) #HELP# find tune deshake2 #
DESHAKE2=$2
shift
;;
-watch|-suspend) #HELP# watch for user login and suspend if anyone comes looking #
SUSPEND=1
;;
-x|-extra) #HELP# extra arguments to ffmpeg, e.g. -x -r -x 20 (set rate to 20 fps) #
EXTRA=($EXTRA $2)
shift
;;
-rate|-r|-fps) #HELP# set fps #
RATE=(-r $2)
shift
;;
-params) #HELP# set encode params #
PARAMS=$2
shift
;;
-crop) #HELP# set cropping #
prepend_vf $2
shift
;;
-debug) #HELP# set debug to display what is happening #
DEBUG=echo
;;
-aac) #HELP# re-encode audio to aac, might need -notall #
AC=($AC -acodec aac)
;;
-ac3) #HELP# re-encode audio to ac3, might need -notall #
AC=($AC -acodec ac3)
;;
-merge) #HELP# merge another file into final MKV #
MERGE=($MERGE $2)
shift
;;
-resample) #HELP# resample audio to 44.1kHz #
RESAMPLE=(-af aresample=resampler=soxr -ar 44100)
;;
-ab) #HELP# set audio bitrate #
AC=($AC -ab $(($2 * 1000)))
shift
;;
-codec|-vcodec) #HELP# set video codec #
VCODEC=$2
shift
;;
-acodec) #HELP# set audio codec #
AC=($AC -acodec $2)
shift
;;
-nice) #HELP# run encoder job nice #
do_nice() {
nice "$@"
}
;;
-crf) #HELP# set crf #
CRF=$2
shift
;;
-x26?) #HELP# use -x264 or -x265 encoder #
VCODEC=libx26${1#-x26}
;;
-basename) #HELP# output filename will be in CWD #
BASENAME=1
;;
-f|-force|--force) #HELP# overwrite output file #
FORCE=-y
;;
-preset) #HELP# set encode preset #
PRESET=$2
shift
;;
-tune) #HELP# set encoder tune #
TUNE=(-tune $2)
shift
;;
-vf) #HELP# set ffmpet video filter #
append_vf $2
shift
;;
-notall) #HELP# do not handle all streams of source file (only e..g first audio stream) #
ALL=()
;;
-10bit) #HELP# encode using 10bits, e.g. with x265 (although x264 can do this as well...) #
EXTRA=($EXTRA -pix_fmt yuv420p10le)
EXTRANAME=($EXTRANAME 10bit)
;;
-cropdetect) #HELP# try detecting crop of source video, use with caution, needs to pre-process #
CROPDETECT=1
;;
-downmix5.1|-stereo) #HELP# downmix to stereo #
ALL=()
#
# with lfe
#prepend_af "pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL + 0.5*LFE|FR < 1.0*FR + 0.707*FC + 0.707*BR + 0.5*LFE"
# with back and surround
#prepend_af "pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL + 0.707*SL|FR < 1.0*FR + 0.707*FC + 0.707*BR + 0.707*SR"
# with back, surround and lfe
#prepend_af "pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*BL + 0.707*SL + 0.5*LFE|FR < 1.0*FR + 0.707*FC + 0.707*BR + 0.707*SR + 0.5*LFE"
#prepend_af "pan=stereo|FL < 1.414*FL + 0.707*FC + 0.707*SL|FR < 1.414*FR + 0.707*FC + 0.707*SR"
#prepend_af "pan=stereo|FL < 1.0*FL + 0.707*FC + 0.707*SL|FR < 1.0*FR + 0.707*FC + 0.707*SR"
EXTRA=($EXTRA -ac 2)
EXTRANAME=($EXTRANAME stereo)
;;
-fmt) #HELP# output specific format, i.e. avi, mkv, mp4
FMT="$2"
shift
;;
-psvita)
SIZE=960x540
FMT=mp4
# this is not needed, level and aac audio however are vital
# PRESET=veryfast
VCODEC=libx264
PARAMS=(level=31)
AC=(-acodec aac -ab 96k)
ALL=()
EXTRANAME=($EXTRANAME vita)
;;
-nosize)
SIZE=
;;
--) #HELP# signal end of options, next argument needs to be source file #
shift
break
;;
--help|-h) #HELP# show this help message #
echo "Usage: $0 [options] -- <input-file>"
echo
while read line # END OF ARGUMENT PARSING
do
case "$line" in
*"BEGIN OF ARGUMENT PARSING")
docs=1
continue
;;
*"END OF ARGUMENT PARSING")
docs=0
break
;;
*"#HELP#"*)
[ "$docs" != 1 ] && continue
opts=${line%%)*}
text=${line##*#HELP#}
text=${text% #}
printf " %-18s\t%s\n" "$opts" "$text"
;;
esac
done < "$0" | sort
echo
exit 0
;;
esac
shift
done
[ -x "$FFMPEG" ] || {
echo "ffmpeg '$FFMPEG' does not exist/is not executable, using default 'ffmpeg'"
FFMPEG=ffmpeg
}
set -u
if ! [ "$AC" ]
then
AC=(-acodec copy)
fi
if [ "$PARAMS" ]
then
case "$VCODEC" in
libx264)
PARAMS=(-x264opts $PARAMS)
;;
libx265)
PARAMS=(-x265-params $PARAMS)
;;
esac
fi
[ "$1" ] || {
echo "Usage: $0 [options] -- <input-file>" 2>&1
exit 1
}
# Actual file to encode...
i=$1
if [ "$i" != "${i//-crf/}" ] || [ "$i" != "${i//SDR/}" ]
then
continue
fi
if ! [ "$FMT" ]
then
ext=mkv
else
ext=$FMT
fi
# BUild output filename
local out="${${i//HDR/SDR}:r}-${VCODEC//lib/}$(for i in $EXTRANAME; echo ".$EXTRANAME").$([ "$PRESET" ] && echo "$PRESET" || echo default)$([ "$SIZE" ] && echo ".$SIZE").crf$CRF.$ext"
if [ "$BASENAME" = 1 ]
then
out="${out:t}"
fi
if [ -e "$out" ] && [ -z "$FORCE" ]
then
echo "Output file exists but I have no force..."
exit
fi
CMDNAME=$(basename $FFMPEG)
if command -v chpst >/dev/null
then
CMDNAME=docker-node
CHPST=(chpst -b $CMDNAME)
fi
if [ $SUSPEND = 1 ]
then
suspend_on_user_logon $CMDNAME &
trap "kill -9 $!" INT EXIT
fi
# Do we want ffmpeg to do ME?
if [ "$ME" ]
then
prepend_vf $ME
fi
if [ $CROPDETECT = 1 ]
then
echo Detecting crop...
prepend_vf $($FFMPEG -i "$i" -vf cropdetect -vcodec rawvideo -y -f matroska -an -ss 0:02:00 -t 1 /dev/null 2>&1 | tail -n5 | head -n1 | sed -e 's/^.* crop=/crop=/g')
fi
if [ "$DESHAKE1" ]
then
XVF=$VF # save actual VF
append_vf ${DESHAKE1}:result="${out}.trf"
# append_vf scale=w=16:h=16 # also rescale to copy less data?
$DEBUG do_nice $CHPST $FFMPEG $FORCE $RATE -i "$i" -vf ${VF} -vcodec rawvideo $AC -an $EXTRA -f matroska /dev/null
VF=$XVF # restore out actual VF
append_vf ${DESHAKE2}:input="${out}.trf"
fi
if [ "$i" != "${i//HDR/}" ]
then
echo Tonemapping ITU.2020 to ITU.709 for $i
# prepend_vf "zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable,zscale=t=bt709:m=bt709:r=tv,format=yuv420p"
prepend_vf scale=in_color_matrix=bt2020:out_color_matrix=bt709
fi
# Setup $VF for ffmpeg
if [ $#VF != 0 ]
then
VF=(-vf $VF)
fi
to-option() {
set +u
[ "$2" ] && echo "$1 $2"
set -u
}
$DEBUG do_nice $CHPST $FFMPEG $FORCE $RATE -i "$i" $VF -vcodec $VCODEC -scodec copy $PARAMS $TUNE $(to-option -preset $PRESET) $(to-option -s $SIZE) -crf $CRF $RESAMPLE $AC $ALL $EXTRA $(to-option -f $FMT) "$out"
if [ $#MERGE -ne 0 ]
then
echo "Merging additional streams into new mkv"
$DEBUG $CHPST mkvmerge $out $MERGE -o $(basename $out .$FMT)-merged.mkv
fi
# zsh encode-video.zsh -f -fps 60 -x264 -crf 20 -vf tmix=frames=5,"select=not(mod(n\,20)),setpts=0.05*PTS" -deshake -ff ffmpeg -preset fast -- MOV_3147.mp4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment