diff --git a/README.md b/README.md deleted file mode 100644 index 64907eb..0000000 --- a/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# burnSubs -You found my burnSubs tool. The goal for this tool is to ease the -conversion of arbitrary video files with soft-subtitles in the SSA/ASS -Substation Alpha format into simple stereo hard-subtitled video files. - -## Features -* softsub to hardsub conversion -* embedded font files -* selecting specific audio streams -* selecting specific video streams -* automatic selection of language preferences -* default surround sound to stereo down-mixing - * opt out CLI flag available -* anti-clobbering default behavior -* auto-cleanup on error - -## Prerequisite Tools -Firstly, the tool will yell at you if the tools it needs don't exist. -Feel free to just run the tool, and it will let you know what you're -missing. - -* [`ffmpeg`](https://ffmpeg.org/) - the one and only - * `ffprobe` - usually comes with ffmpeg -* [`jq`](https://stedolan.github.io/jq/) - file/pipe based JSON processor - - -## How does it work? -`burnSubs` takes an input video file and tries to figure out what -audio streams and subtitle streams exist within the file. It stores -metadata in `/tmp` while it runs. When running it will pull the -streams within the input file, and try to select Japanese language -audio streams, and a non-signs subtitle stream (i.e. a full language -subtitle stream) to add to the output video. - -Before transcoding it will then extract the subtitle file to pass into -`ffmpeg`'s subtitle burn in filter, and will try to down-mix any -surround sound input streams to stereo. Downmixing, track selection, -clobbering behavior, and verbosity can all be controlled to a limited -extent by CLI flags. - -## Can you make it do *XYZ*. -Give me an enhancement request in github and I'll take a look. \ No newline at end of file diff --git a/burnSubs b/burnSubs index 0feca62..40a27c2 100755 --- a/burnSubs +++ b/burnSubs @@ -5,18 +5,18 @@ set -o errexit ################################################################################ # burnSubs -# version 0.13.3 -################# +# version 0.11.1 +#################3 # 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=18 - function machineSetup() { # Default setup export FF_ENC="libx264" @@ -24,7 +24,7 @@ function machineSetup() { export FILT_PFX="" export FILT_SFX="" - CRF=${OPTS_CRF:-$DEFAULTS_OPTS_CRF} + CRF=${OPTS_CRF:-23} export FF_STD="-preset myTranscoderPreset -crf $CRF -tune animation \ -preset medium -movflags +faststart" export FF_EXT="-profile:v high -level 4.0" @@ -55,7 +55,7 @@ function machineSetup() { #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 + elif [[ "$(hostname)" == "grad-heo-lappy" ]]; then echo " > Hostname: $(hostname)" export OPTS_ENC="vaapi" else @@ -64,6 +64,20 @@ function machineSetup() { export FF_EXT="${FF_EXT} -pix_fmt yuv420p" fi 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 [[ "$(hostname)" == "Ram-the-Red" ]]; then + FILT_AUDIO="${FILT_AUDIO} -strict -2" + fi + else + FILT_AUDIO="-c:a copy" + fi # Configure the encoder based upon the specific encoder chain required ## NVIDIA GPU Encode/Decode @@ -137,9 +151,9 @@ function setupBins() { setAndValidateBin "ffmpeg" "FFMPEG" setAndValidateBin "ffprobe" "FFPROBE" setAndValidateBin "jq" "JQ" - #setAndValidateBin "python3" "PYTHON3" - #setAndValidateBin "awk" "AWK" - #setAndValidateBin "date" "DATE" + setAndValidateBin "python3" "PYTHON3" + setAndValidateBin "awk" "AWK" + setAndValidateBin "date" "DATE" } # Used by the above function to evaluate overrides if they are set, and @@ -173,7 +187,6 @@ function doCleanup() { echo "=> Cleaning up." rm -r "$TMP" else - set +x echo "tmp dir: $TMP" fi @@ -281,45 +294,10 @@ function parseStreams() { "$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}" + "$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}]) | reduce .[] as $item ([]; [.[],{t:($item.t // ($item.lang + "-" + ($item.i | tostring))),i:$item.i,lang:$item.lang,disposition:$item.disposition}])' "${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() { @@ -368,29 +346,18 @@ function selectSubs() { export SUBTITLE_INDEX=-1 else if [[ $OPTS_SELSUB -lt 0 && $SUB_COUNT -gt 1 ]]; then + OPTS_SELSUB_LANG="${OPTS_SELSUB_LANG:-eng}" + LANG_TEST=$($JQ '[.[].lang] | index("'${OPTS_SELSUB_LANG}'")' "$STREAMS_SUB") 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 + if [[ "$LANG_TEST" == "null" ]]; 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") + else # 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")) or ($trk.t | test("dub";"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")) or ($trk.t | test("dub";"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 + export SUBTITLE_INDEX=$($JQ '.['$LANG_TEST'].i' "$STREAMS_SUB") fi else if [[ $SUB_COUNT -eq 1 ]]; then @@ -412,14 +379,12 @@ function selectSubs() { printf " Using default selection rules... " if [[ "$LANG_TEST" == "null" ]]; then echo "Japanese audio not found!" - echo " ==> Reverting to first audio stream." + echo " ==> Reverting to first subtitle file." 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 @@ -448,21 +413,17 @@ function extractSubs() { function doTranscode() { echo "=> Starting transcode:" # shellcheck disable=SC2086 - echo "$FFMPEG" ${FF_VERBOSITY} ${FF_HW} -i "${INPUT_VIDEO}" \ - -sn ${LIM_TIME} \ + echo "$FFMPEG" ${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} \ + ${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} \ + "$FFMPEG" ${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} \ + ${FILT_AUDIO} -c:v "${FF_ENC}" ${FF_STD} ${FF_EXT} ${FF_AUDIO} \ "${OUTPUT_VIDEO}" export FINAL_STATUS=$? } @@ -520,8 +481,6 @@ 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" @@ -541,8 +500,7 @@ FINAL_STATUS=1 ###### # 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' -- "$@") +OPT_STRING=$(getopt -o 'hkls:a:dt:' --long 'help,psoft,soft,dry,crf:,audio,audiofix,alocale:,slocale:' -- "$@") # reassign them as positional arguments eval set -- "$OPT_STRING" @@ -618,13 +576,6 @@ while true; do 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." @@ -650,22 +601,6 @@ while true; do 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 @@ -677,31 +612,27 @@ while true; do #echo "TODO: HELP!" # Display HELP cat << _EOT ---------------------------------------------------------------------------- - -k auto-klobber when ffmpeg asks + -k auto-klobber when ffmpeg asks - -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 + -t ffmpeg encoding time limit + --crf <#> override CRF setting + --soft force software decode and encode + --psoft use software encoding (allow hardware decode when available) + --audiofix transcode audio + --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 + -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) + -d debug (no cleanup) + --dry dry run (no encoding) _EOT exit ;; @@ -727,19 +658,6 @@ else 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 - -if [[ "$OPTS_DEBUG" == "true" ]]; then - set -x -fi - ############### # Configure the encoder based upon the hostname machineSetup @@ -768,10 +686,6 @@ fi 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"