burnSubs/burnSubs

780 lines
24 KiB
Bash
Executable file

#!/bin/bash
set -e
set -o nounset
set -o errexit
################################################################################
# burnSubs
# version 0.13.2
#################
# Wishlist:
# queue encodes
# finish TODOs
# finish help flag
# audio recode flag
#
# Changes
# automatically select JPN audio if more than one audio channel found.
################################################################################
DEFAULTS_OPTS_CRF=20
function machineSetup() {
# Default setup
export FF_ENC="libx264"
export FF_HW=""
export FILT_PFX=""
export FILT_SFX=""
CRF=${OPTS_CRF:-$DEFAULTS_OPTS_CRF}
export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \
-preset medium -movflags +faststart"
export FF_EXT="-profile:v high -level 4.0"
# Handle the time limit argument used for testing if it was passed
if [[ "${OPTS_TIMELIMIT}" != "null" ]]; then
export LIM_TIME="-t ${OPTS_TIMELIMIT}"
else
export LIM_TIME=""
fi
# The default extraProc function. This should be overridden by each encoder
# as required. For example, to set bitrate after a video has been parsed.
function extraProc() {
return
}
# Use the machine hostname to set the default encoding options based upon
# my preferences.
echo "Machine identification:"
if [[ "${OPTS_FORCESOFT}" == "true" ]]; then
echo " > Hostname: $(hostname)"
export OPTS_ENC="allsoft"
else
if [[ "$(hostname)" == "Kusanagi" ]]; then
echo " > Hostname: 草薙素子"
export OPTS_ENC="nvidia"
echo "---> FIX FFMPEG!"
#export FFMPEG="/opt/ffmpeg-nvenc/bin/ffmpeg"
export LD_LIBRARY_PATH="/opt/ffmpeg-nvenc/lib"
export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
elif [[ "$(hostname)" == "random-vaapi-intel-laptop" ]]; then
echo " > Hostname: $(hostname)"
export OPTS_ENC="vaapi"
else
echo " > Hostname: $(hostname)"
export OPTS_ENC="allsoft"
export FF_EXT="${FF_EXT} -pix_fmt yuv420p"
fi
fi
# Configure the encoder based upon the specific encoder chain required
## NVIDIA GPU Encode/Decode
if [[ "$OPTS_ENC" == "nvidia" ]]; then
echo " > Using nVidia Encoder"
echo " >>> full hardware"
# NVIDIA GPU encoding options
export FF_ENC="h264_nvenc"
# Assuming h264 input video
# TODO: Handle non h264 input streams
#export FF_HW="-hwaccel cuvid -c:v h264_cuvid"
#export FF_HW="-hwaccel cuvid"
# Frame count lookahead
export FF_EXT="${FF_EXT} -rc-lookahead 30"
## Intel CPU Encode/Decode
elif [[ "$OPTS_ENC" == "vaapi" ]]; then
echo " > Using VAAPI."
# apt install libvdpau-va-gl1
#export FF_BIN="/opt/ffmpeg/bin/ffmpeg"
# Full hardware
if [[ "${OPTS_FORCEPARTSOFT}" == "false" ]]; then
echo " >>> full hardware"
# Full hardware decode/encode
export FF_HW="-hwaccel vaapi -hwaccel_device \
/dev/dri/renderD128 -hwaccel_output_format vaapi"
export FF_ENC="h264_vaapi"
export FILT_PFX="scale_vaapi,hwmap=mode=read+write+direct,format=nv12,"
export FILT_SFX=",hwmap"
# Override the extra options used for this encoder
export FF_EXT="-profile:v 100 -level 40"
# Mixed software hardware
else
echo " >>> hardware decode"
echo " > software encode"
# software encode, hardware decode only
export FF_HW="-hwaccel vaapi -hwaccel_device /dev/dri/renderD128"
fi
# Disable this shit
# set the bitrate based upon the old video size.
if [[ $(false) ]]; then
function extraProc() {
echo -n " > determine video size: "
LEN_COMPLEX=$("$JQ" '.streams[0].tags.DURATION' \
"${STREAMS_ALL}" | tr -d '"')
LEN_SECONDS=$("$DATE" -u +'%s' -d "1970-01-01 $LEN_COMPLEX")
# shellcheck disable=SC2016
VIDEO_SIZE_KBYTES=$("$FFPROBE" -select_streams v -show_entries \
packet=size -of default=nokey=1:noprint_wrappers=1 \
-i "${INPUT_VIDEO}" | "$AWK" '{s+=$1} END {print s/1024.0}')
KBITRATE=$("$PYTHON3" -c \
"print('%.0f' % (1.0*8.0*$VIDEO_SIZE_KBYTES/$LEN_SECONDS))")
export FF_EXT="-b:v ${KBITRATE}K "
}
fi
# Full Software encoding/decoding. No hardware assistance.
else
echo " > Using default full software chain."
fi
sleep 1
echo "====> Starting <===="
}
function setupBins() {
# The first argument is the "default" binary name, and the second
# is the variable where that binary's path is stored. Using *export* to
# override the variable before "setupBins" is called will let you specify
# a specific instance of the binary to use instead.
setAndValidateBin "ffmpeg" "FFMPEG"
setAndValidateBin "ffprobe" "FFPROBE"
setAndValidateBin "jq" "JQ"
#setAndValidateBin "python3" "PYTHON3"
#setAndValidateBin "awk" "AWK"
#setAndValidateBin "date" "DATE"
}
# Used by the above function to evaluate overrides if they are set, and
# otherwise to simply return the existing binary as an absolute path.
function setAndValidateBin() {
binaryVariable="$2"
# shellcheck disable=SC2086
eval binaryCurrentValue="\${$binaryVariable:-}"
if [[ "" == "$binaryCurrentValue" ]]; then
binaryCurrentValue="$1"
fi
# Try to extract the results of the current binary as an absolute path
binaryCurrentValue=$(readlink -f "$(which "$binaryCurrentValue")") || /bin/true
# And fail if we don't have it
if [[ ! -x "$binaryCurrentValue" ]]; then
echo "ERROR: Required binary '$1' not found."
exit
fi
# shellcheck disable=SC2086
eval export $binaryVariable="$binaryCurrentValue"
}
# The fall back cleanup function to remove the shit in temp. Set to trap in
# the setup temp function further on.
function doCleanup() {
# On eject return to where we started, and frag the cleanup directory
cd "$WD"
if [[ "$OPTS_DEBUG" == "false" ]]; then
echo "=> Cleaning up."
rm -r "$TMP"
else
echo "tmp dir: $TMP"
fi
alertUser
}
# All we do is note that we'll have to move around, and this helps us keep our
# bearings and avoid polluting the working directory.
function setupTemp() {
echo "=> Performing Setup:"
# The FFMPEG calls require all of the files to actually exist on disk. To
# accommodate this, we setup a temporary working directory to dump the files
# and we will remove it's contents when things are done. This provides a
# location for the font files to be placed, as well as a location for the
# raw subtitles files.
trap doCleanup EXIT
export WD="$PWD"
export TMP=$(mktemp -d /tmp/transFont.XXXX)
}
function dumpFonts() {
# Dumb font dump
cd "$FONTDIR"
"$FFMPEG" -dump_attachment:t "" -i "${INPUT_VIDEO}" -vn -an \
-f null -y /dev/null 2>/dev/null || /bin/true
cd "$WD"
}
function setupFonts() {
echo " > setting up fonts..."
# Simply setup an encode specific font configuration setup. This will direct
# the system to use any font files that we have provided in $TMP/font_files
# then fall back to the system configuration afterwards.
export FONTDIR="${TMP}/font_files"
FC_DIR="${TMP}/fontconfig"
FC_FILE="${FC_DIR}/fonts.conf"
mkdir "${FONTDIR}"
mkdir "${FC_DIR}"
{
echo '<?xml version="1.0"?>';
echo '<!DOCTYPE fontconfig SYSTEM "fonts.dtd">';
echo '<fontconfig>';
echo ' <dir>'"${FONTDIR}"'</dir>';
echo ' <include>/etc/fonts/fonts.conf</include>';
echo '</fontconfig>'
} > "${FC_FILE}"
# This export line presents the configuration override to FFMPEG later on.
export FONTCONFIG_FILE="${FC_FILE}"
dumpFonts
}
function setupPreset() {
echo " > setting up preset..."
# The preset system provides a way to specify specific encoding options.
# This can easily be removed as desired.
presetName="$1"
# And include the ffmpeg preset
export FFMPEG_DATADIR="${TMP}/ffmpeg"
FFMPEG_PRESET_FILE="${FFMPEG_DATADIR}/${FF_ENC}-${presetName}.preset"
mkdir "${FFMPEG_DATADIR}"
{
# This preset is for the roku
echo 'coder=1'
echo 'flags=+loop'
echo 'cmp=+chroma'
echo 'partitions=+parti8x8+parti4x4+partp8x8+partb8x8'
echo 'me_method=umh'
echo 'subq=8'
echo 'me_range=16'
echo 'g=250'
echo 'keyint_min=25'
echo 'sc_threshold=40'
echo 'i_qfactor=0.71'
echo 'b_strategy=2'
echo 'qcomp=0.6'
echo 'qmin=10'
echo 'qmax=51'
echo 'qdiff=4'
echo 'bf=4'
echo 'refs=4'
echo 'directpred=3'
echo 'trellis=1'
echo 'flags2=+wpred+mixed_refs+dct8x8+fastpskip'
} > "${FFMPEG_PRESET_FILE}"
}
function parseStreams() {
# Most MKV files will only have a single subtitle file. In the case we have
# multiple subtitles we wish to handle conversion gracefully. To do so, we
# extract all of the track information (the first ffprobe call), then use
# the json parsing tool jq to extract subtitle data for styled subtitles.
#
#
STREAMS_ALL="${TMP}/streams.json"
export STREAMS_SUB="${TMP}/subs.json"
export STREAMS_AUDIO="${TMP}/audio.json"
"$FFPROBE" -v error -of json -show_streams "${INPUT_VIDEO}" 2>/dev/null > "${STREAMS_ALL}"
# Extract subtitles
#$item.codec_type == "subtitle" \&\&
# shellcheck disable=SC2016
"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_name == "ass" or $item.codec_name == "dvd_subtitle") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, codec:($item.codec_name)}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition,codec:$item.codec}])' "${STREAMS_ALL}" > "${STREAMS_SUB}"
export SUB_COUNT=$("$JQ" 'length' "${STREAMS_SUB}")
# shellcheck disable=SC2016
"$JQ" 'reduce .streams[] as $item ([]; if ($item.codec_type == "audio") then [.[],$item] else . end) | reduce .[] as $item ([]; [.[],{t:($item.tags.title),i:($item.index),lang:$item.tags.language, disposition:$item.disposition, chan:{channels:$item.channels, channel_layout:$item.channel_layout}}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition, chan:$item.chan}])' "${STREAMS_ALL}" > "${STREAMS_AUDIO}"
export AUDIO_COUNT=$("$JQ" 'length' "${STREAMS_AUDIO}")
}
function setupAudioTranscode() {
if [[ $AUDIO_COUNT -ne 1 ]]; then
CHANNEL_COUNT=$($JQ '.[] | select(.i == '$AUDIO_INDEX') | .chan.channels' "$STREAMS_AUDIO")
CHANNEL_LAYOUT=$($JQ '.[] | select(.i == '$AUDIO_INDEX') | .chan.channel_layout' "$STREAMS_AUDIO")
else
CHANNEL_COUNT=$($JQ '.[] | .chan.channels' "$STREAMS_AUDIO")
CHANNEL_LAYOUT=$($JQ '.[] | .chan.channel_layout' "$STREAMS_AUDIO")
fi
if [[ "${CHANNEL_COUNT}" == "6" && "${CHANNEL_LAYOUT}" == '"5.1"' ]]; then
# check if we're 5.1 and if so flag transcode.
export OPTS_TRANS_AUDIO=true
elif [[ "${CHANNEL_COUNT}" != "2" && "${CHANNEL_LAYOUT}" != '"5.1"' ]]; then
echo "ERROR: Trying to enocde non 5.1 and non-stereo audio stream."
exit 1
fi
# Configure audio filtergraph if needed.
if [[ "${OPTS_TRANS_AUDIO}" == true ]]; then
FILT_AUDIO="-c:a aac"
if [[ "${OPTS_LPF_AUDIO}" == true ]]; then
FILT_AUDIO="-filter:a highpass=f=7 ${FILT_AUDIO}"
fi
if [[ "${OPTS_SURROUND_PRESERVE}" == false ]]; then
# From https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg
# Nightmode Formula
FILT_AUDIO="-filter:a pan=stereo|FL<FC+0.30*FL+0.30*BL|FR<FC+0.30*FR+0.30*BR ${FILT_AUDIO}"
fi
else
FILT_AUDIO="-c:a copy"
fi
}
function listSubtitles() {
# TODO:
echo ""
echo "available subtitles:"
printf "Num: %4s %s\n" "LANG" "Subtitle Title String"
echo "---------------------------------"
for iSUB in $(seq 0 $(($SUB_COUNT-1))); do
X_TITLE=$($JQ '.['$iSUB'].t' $STREAMS_SUB | tr -d '"')
X_INDEX=$($JQ '.['$iSUB'].i' $STREAMS_SUB | tr -d '"')
X_LANG=$($JQ '.['$iSUB'].lang' $STREAMS_SUB)
X_CODEC=$($JQ '.['$iSUB'].codec' $STREAMS_SUB)
printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE"
if [[ "$X_CODEC" != "ass" ]]; then
printf " format: %s\n" $X_CODEC
fi
done
echo ""
return
}
function listAudioTracks() {
# TODO:
echo ""
echo "available audio tracks:"
printf "Num: %4s %s\n" "LANG" "Title String"
echo "---------------------------------"
for iAUDIO in $(seq 0 $(($AUDIO_COUNT-1))); do
X_TITLE=$($JQ '.['$iAUDIO'].t' $STREAMS_AUDIO | tr -d '"')
X_INDEX=$($JQ '.['$iAUDIO'].i' $STREAMS_AUDIO | tr -d '"')
X_LANG=$($JQ '.['$iAUDIO'].lang' $STREAMS_AUDIO)
printf " %2d: %4s %s\n" $X_INDEX $X_LANG "$X_TITLE"
done
echo ""
return
}
function selectSubs() {
# TODO: handle multiple subtitle files
# TODO: verify the the subtitle index is legal
if [[ $SUB_COUNT -eq 0 ]]; then
echo " > ERROR: No subtitles!"
echo " > Reverting to a dry run."
export OPTS_DRYRUN=true
export SUBTITLE_INDEX=-1
else
if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then
echo " > WARNING: Multiple subtitles!"
printf " Using default selection rules... "
OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}"
LANG_TEST=$($JQ '[.[].lang | match("'${OPTS_SELSUB_LANG}'")] | length' "$STREAMS_SUB")
if [[ "$LANG_TEST" == "0" ]]; then
echo "English not found!"
echo " ==> Reverting to first subtitle file."
export SUBTITLE_INDEX=$($JQ '.[0].i' "$STREAMS_SUB")
elif [[ "$LANG_TEST" == "1" ]]; then
LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
# we found english
echo "English found"
export SUBTITLE_INDEX=$($JQ '.['$LANG_SINGLE_SELECT'].i' "$STREAMS_SUB")
else
# try to avoid a signs and lyrics track
LANG_SINGLE_SELECT=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB")
echo "Multiple english tracks found."
export SUBTITLE_INDEX=$($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) | not ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].i' "$STREAMS_SUB")
# And display rejected subtitles too.
SUBTITLE_REJECT_LIST=($($JQ 'reduce .[] as $trk ([]; if ($trk.lang == "'${OPTS_SELSUB_LANG}'" and ( ($trk.t | test("lyrics";"i") or ($trk.t | test("signs";"i")) ) ) ) then [.[],{t:$trk.t,i:$trk.i}] else . end) | .[].t' "$STREAMS_SUB"))
for REJECT_SUB in ${SUBTITLE_REJECT_LIST[@]}; do
echo " > rejecting ${REJECT_SUB}"
done
fi
else
if [[ $SUB_COUNT -eq 1 ]]; then
export SUBTITLE_INDEX=$($JQ '.[].i' "$STREAMS_SUB")
else
export SUBTITLE_INDEX=$OPTS_SELSUB
fi
fi
SUBTITLE_ARRAY_INDEX=$($JQ '[.[].i] | index('$SUBTITLE_INDEX')' "$STREAMS_SUB")
SUBTITLE_NAME=$($JQ '.['$SUBTITLE_ARRAY_INDEX'].t' "$STREAMS_SUB")
echo " > subtitles: [${SUBTITLE_INDEX}] ${SUBTITLE_NAME}"
fi
if [[ $AUDIO_COUNT -gt 1 ]]; then
if [[ $OPTS_SELAUDIO -lt 0 ]]; then
OPTS_SELAUDIO_LANG="${OPTS_SELAUDIO_LANG:-jpn}"
LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELAUDIO_LANG}'")' "$STREAMS_AUDIO")
echo " > WARNING: Multiple audio streams!"
printf " Using default selection rules... "
if [[ "$LANG_TEST" == "null" ]]; then
echo "Japanese audio not found!"
echo " ==> Reverting to first audio stream."
export AUDIO_INDEX=$($JQ '.[0].i' "$STREAMS_AUDIO")
else
# we found english
echo "Japanese audio found"
export AUDIO_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_AUDIO")
AUDIO_STREAM_TITLE=$($JQ '.['$LANG_TEST'].t' "$STREAMS_AUDIO")
echo " ==> stream has title ${AUDIO_STREAM_TITLE}"
fi
else
AUDIO_INDEX=$OPTS_SELAUDIO
fi
export FF_AUDIO="-map 0:$AUDIO_INDEX -map 0:v:0"
else
export FF_AUDIO=""
fi
}
function extractSubs() {
export SUBTITLE_FILE="${TMP}/ripped.ass"
if [[ $SUBTITLE_INDEX -lt 0 ]]; then
echo " > skipping subtitles (TODO: tidy cleanup)"
return
else
echo " > extracting subtitles"
fi
extractIndex="$1"
"$FFMPEG" -i "${INPUT_VIDEO}" -map 0:"${extractIndex}" -vn -an -c:s copy -c:a copy \
"$SUBTITLE_FILE" 2>/dev/null
}
function doTranscode() {
echo "=> Starting transcode:"
# shellcheck disable=SC2086
echo "$FFMPEG" ${FF_VERBOSITY} ${FF_HW} -i "${INPUT_VIDEO}" \
-sn ${LIM_TIME} \
-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
${FILT_AUDIO} \
-c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO} \
"${OUTPUT_VIDEO}"
if [[ "$OPTS_DRYRUN" == true ]]; then
return
fi
# shellcheck disable=SC2086
"$FFMPEG" ${FF_VERBOSITY} ${FF_HW} -i "${INPUT_VIDEO}" \
-sn ${LIM_TIME} \
-filter:v "${FILT_PFX}ass=${SUBTITLE_FILE}${FILT_SFX}" \
${FILT_AUDIO} \
-c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO} \
"${OUTPUT_VIDEO}"
export FINAL_STATUS=$?
}
function runExtraProc() {
if [[ "$FF_ENC" == "$1" ]]; then
extraProc
fi
}
function klobberCheck() {
if [[ -e "${OUTPUT_VIDEO}" ]]; then
if [[ "$OPTS_KLOBBER" == "true" ]]; then
echo " > klobbering old file '${OUTPUT_VIDEO}'"
rm -v "${OUTPUT_VIDEO}"
else
echo " > refusing to klobber old file '${OUTPUT_VIDEO}'"
exit 1
fi
fi
}
function alertUser() {
# if the function isn't available, then define an empty function to
# reject the message into the void
if ! which notify-send >/dev/null; then
function notify-send() { return; }
fi
if [[ $FINAL_STATUS -ne 0 ]]; then
notify-send -u normal --icon error "🗙 Transcode failed!" \
"$(basename "${INPUT_VIDEO}")\n🞩🞩🞩🞩🞩\n$(basename "${OUTPUT_VIDEO}")"
else
notify-send -u normal --icon ${NOTIFY_ICON} "✔ Transcode finished" \
"$(basename "${INPUT_VIDEO}")\n↓↓↓↓↓\n$(basename "${OUTPUT_VIDEO}")"
fi
}
# And verify arguments
######
# Defaults
OPTS_KLOBBER=false
OPTS_LISTSUBS=false
OPTS_SELSUB=-1
OPTS_SELAUDIO=-1
OPTS_LISTSUBS=false
OPTS_FORCESOFT=false
OPTS_FORCEPARTSOFT=false
OPTS_ENC="allsoft"
OPTS_TIMELIMIT=null
OPTS_DRYRUN=false
OPTS_DEBUG=false
OPTS_LPF_AUDIO=false
OPTS_TRANS_AUDIO=false
OPTS_derived_NO_OUTPUT=false
OPTS_VERBOSITY=1
OPTS_SURROUND_PRESERVE=false
unset OPT_CRF
# this is the --icon flag passed to notify-send at the end of the transcode
NOTIFY_ICON="face-tired"
# preinitalized final ffmpeg status to assumed error
FINAL_STATUS=1
### Argument Parsing
# Input/Output videos (non-flagged arguments)
# optional:
# -k do clobber output
# -s # subtitle track
# -l list subtitle tracks of input files
# --soft force software encoder
# -d debug, don't cleanup
###
######
# Reformat and organize the input strings
OPT_STRING=$(getopt -o 'hkls:a:dt:vq' \
--long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:,verbose,quiet,keep-surround' -- "$@")
# reassign them as positional arguments
eval set -- "$OPT_STRING"
while true; do
case "$1" in
"-t")
OPTS_TIMELIMIT="$2"
echo ">> !! limiting encode to time '$OPTS_TIMELIMIT'"
shift 2
continue
;;
"-k")
OPTS_KLOBBER=true
echo ">> !! klobber when encoding time comes."
shift
continue
;;
"-l")
OPTS_LISTSUBS=true
OPTS_derived_NO_OUTPUT=true
echo ">> !! list subtitles and exit"
shift
continue
;;
"-s")
OPTS_SELSUB="$2"
#TODO: verify legal subtitle track number convention"
echo "TODO: verify legal subtitle track number convention"
echo ">> !! Selecting subtitle track #${OPTS_SELSUB}"
shift 2
continue
;;
"--slocale")
OPTS_SELSUB_LANG="$2"
#TODO: verify legal subtitle track number convention"
echo "TODO: verify legal subtitle track number convention"
echo ">> !! Selecting subtitle track #${OPTS_SELSUB_LANG}"
shift 2
continue
;;
"-a")
OPTS_SELAUDIO="$2"
#TODO: verify legal subtitle track number convention"
echo "TODO: verify legal audio track number convention"
echo ">> !! Selecting audio track #${OPTS_SELAUDIO}"
shift 2
continue
;;
"--alocale")
OPTS_SELAUDIO_LANG="$2"
#TODO: verify legal subtitle track number convention"
echo "TODO: verify legal audio track number convention"
echo ">> !! Selecting audio track with locale #${OPTS_SELAUDIO_LANG}"
shift 2
continue
;;
"--psoft")
OPTS_FORCEPARTSOFT=true
echo ">> !! forcing software encoding."
shift
continue
;;
"--lpf")
OPTS_LPF_AUDIO=true
OPTS_TRANS_AUDIO=true
echo ">> !! low pass filter audio to AAC."
shift
continue
;;
"--audiofix")
OPTS_TRANS_AUDIO=true
echo ">> !! no filter audio to AAC."
shift
continue
;;
"--keep-surround")
OPTS_SURROUND_PRESERVE=true
OPTS_TRANS_AUDIO=true
echo ">> !! preserving 5.1/7.1 surround sound if available."
shift
continue
;;
"--soft")
OPTS_FORCESOFT=true
echo ">> !! forcing software decoding/encoding."
shift
continue
;;
"--crf")
OPTS_CRF="$2"
echo ">> !! CRF Override CRF='$OPTS_CRF'"
shift 2
continue
;;
"-d")
OPTS_DEBUG=true
echo ">> !! debug enabled."
echo ">> temp files will not be cleaned up."
shift
continue
;;
"--dry")
OPTS_DRYRUN=true
echo ">> !! dry run. No encode."
shift
continue
;;
"--verbose"|"-v")
if [[ ${OPTS_VERBOSITY} -ne 0 ]]; then
OPTS_VERBOSITY=2
echo ">> !! print ffmpeg header."
else
echo ">> !! Ignoring verbosity flag. Quiet takes precidence."
fi
shift
continue
;;
"--quiet"|"-q")
OPTS_VERBOSITY=0
echo ">> !! print ffmpeg header."
shift
continue
;;
"--")
shift # all arguments parsed
break
;;
"-h" | "--help")
# TODO: Display help
shift # all arguments parsed
echo "$(basename $0) [args] <input> <output>"
#echo "TODO: HELP!" # Display HELP
cat << _EOT
----------------------------------------------------------------------------
-k auto-klobber when ffmpeg asks
-t <t> ffmpeg encoding time limit
--crf <#> override CRF setting
default: $DEFAULTS_OPTS_CRF
--soft force software decode and encode
--psoft use software encoding (allow hardware decode when available)
--audiofix transcode audio
--keep-surround try to preserve surround sound rather than downmixing to stereo.
--lpf transcode audio, and low-pass filter as well
-l list subtitles and audio tracks (no encoding)
-s <#> select specific subtitle track number
#TODO: verify legal subtitle track number convention
-a <#> select specific audio track number
#TODO: verify legal audio track number convention
--slocale 'eng' select specific subtitle track number
#TODO: verify legal subtitle track number convention
--alocale 'jpn' select specific audio track number
#TODO: verify legal audio track number convention
-d debug (no cleanup)
--dry dry run (no encoding)
-q, --quiet make ffmpeg shutup
-v, --verbose show all FFMPEG details (except that ruddy header)
_EOT
exit
;;
*)
echo "Arg: $1"
echo "Internal Error!" >&2
exit
;;
esac
done
# Now parse POSITIONAL ARGUMENTS
if [[ $# -eq 2 || $OPTS_derived_NO_OUTPUT == true ]]; then
INPUT_VIDEO="$(readlink -f "$1")"
if [[ $# -eq 2 ]]; then
OUTPUT_VIDEO="$2"
else
OUTPUT_VIDEO="/dev/null"
fi
else
echo "ERROR: Incorrect number of positional arguments. Expected 2, got $#"
echo " $(basename $0) [args] <input> <output>"
exit
fi
# Actual verbosity parsing
if [[ ${OPTS_VERBOSITY} -le 0 ]]; then
FF_VERBOSITY="-hide_banner -loglevel error"
elif [[ ${OPTS_VERBOSITY} -eq 1 ]]; then
FF_VERBOSITY="-hide_banner -loglevel error -stats"
else # ie [[ ${OPTS_VERBOSITY} -ge 2 ]]; then
FF_VERBOSITY="-hide_banner"
fi
###############
# Configure the encoder based upon the hostname
machineSetup
###############
# Check and validate the binaries we use to parse and setup the encoding chain
setupBins
# Setup the temp space for working files
setupTemp
# Extract embedded fonts and configure fontconfig
setupFonts
# Export the preset file used for the Roku
setupPreset myTranscoderPreset
###############
# Parse stream data to identify subtitles
parseStreams
# Now! If OPTS_LISTSUBS is defined, then we branch to list subs and exit.
if [[ "$OPTS_LISTSUBS" == "true" ]]; then
listSubtitles
listAudioTracks
exit
fi
# ask the user for the subtitle file if more than one is available
# Also selects audio stream.
selectSubs
# extract the selected subtitle file
extractSubs $SUBTITLE_INDEX
# Configure the audio straem
setupAudioTranscode
# Set the bitrate if that function wasn't disabled
runExtraProc "h264_vaapi"
# If we're clobbering, now is the time to do the clobbering
klobberCheck
# Kickoff the transcode
doTranscode
# cleanup automatically executes after the done message is cleared
echo "done."