zefie

ztranscode.sh (primitive x264 to x265 encoder script which grew into a monster)

Nov 30th, 2020 (edited)
559
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #!/bin/bash
  2.  
  3. ffmpeg="$(command -v ffmpeg)"
  4. dest="/home/zefie/downloads/manual"
  5. ext=mkv # output ext
  6. fps=24 # video fps for tuning
  7. olddir="old" # where source files go when we are done (relative to $workdir)
  8. outdir="zefie" # where new files go when we are done (relative to $workdir)
  9. baddir="bad" # where files with decode errors end up (relative)
  10. ffmpeg_avg_br=5M
  11. ffmpeg_buf=8M
  12. ffmpeg_max_br=12M
  13. ffmpeg_aud_opts=(-b:a 320k)
  14. ffmpeg_mov_avg_br=10M
  15. ffmpeg_mov_buf=10M
  16. ffmpeg_mov_max_br=20M
  17. ffmpeg_mov_aud_opts=("${ffmpeg_aud_opts[@]}")
  18.  
  19. vcodec=hevc
  20. acodec=aac
  21. ffmpeg_threads=4
  22. preset=hq
  23. twopass=true
  24. errchk=1
  25. delete=1
  26. use_nvidia=1 # comment/unset to disable
  27. use_nvidia_encoder=1 # ignored if use_nvidia unset
  28. use_nvidia_decoder=1 # ignored if use_nvidia unset
  29.  
  30. REPLACE=( \
  31.     "H.264|HEVC" \
  32.     "x264|HEVC" \
  33.     "NTb|zefie" \
  34.     "QOQ|zefie" \
  35.     "DDP5.1|AAC5.1" \
  36.     "DD+2.0|AAC2.0" \
  37. )
  38.  
  39.  
  40. # end config
  41.  
  42.  
  43. ffmpeg_opts=()
  44. processed_count=0
  45. FILES=()
  46.  
  47. if [ "${1}" == "movie" ]; then
  48.     echo " [!!!] Executing in movie mode, using higher bitrates."
  49.     ffmpeg_avg_br="${ffmpeg_mov_avg_br}";
  50.     ffmpeg_buf="${ffmpeg_mov_buf}";
  51.     ffmpeg_max_br="${ffmpeg_mov_max_br}";
  52.     ffmpeg_aud_opts=("${ffmpeg_mov_aud_opts[@]}")
  53.     shift;
  54. fi
  55.  
  56. if [ "${1}" == "noerr" ]; then
  57.     echo " [!!!] Executing in noerr mode, ffmpeg will NOT fail on minor errors!"
  58.     errchk=
  59.     shift;
  60. fi
  61.  
  62. if [ "${1}" == "nomove" ]; then
  63.     echo " [!!!] Executing in nomove mode, will not move failed files to ${baddir} !"
  64.     nomove=1
  65.     shift;
  66. fi
  67.  
  68. if [ "${1}" == "nodel" ]; then
  69.     echo " [!!!] Executing in old mode, will not delete files, will place them in ${olddir}"
  70.     delete=
  71.     shift;
  72. fi
  73.  
  74. if [ -z "${1}" ] || [ ! -d "${1}" ]; then
  75.     if [ ! -z "${1}" ] && [ -f "${1}" ]; then
  76.         workdir="$(realpath "$(dirname "${1}")")"
  77.         singlefile="$(realpath "${1}")"
  78.         echo " [***] Single File Mode: ${singlefile} ..."
  79.     else
  80.         workdir="$(realpath "${PWD}")"
  81.     fi
  82. else
  83.     workdir="$(realpath "${1}")"
  84. fi
  85.  
  86. echo " [***] Working in ${workdir} ..."
  87.  
  88. if [ -z "${delete}" ]; then
  89.     olddir="${workdir}/${olddir}";
  90. fi
  91. outdir="${workdir}/${outdir}"
  92. baddir="${workdir}/${baddir}"
  93.  
  94.  
  95. function detect_hw_encoder() {
  96.     if [ -e "/dev/nvidia0" ] && [ ! -z "${use_nvidia}" ] && [ ! -z "${use_nvidia_encoder}" ]; then
  97.         case "${vcodec}" in
  98.                 hevc)
  99.                     echo hevc_nvenc;
  100.                     ;;
  101.                 h264)
  102.                     echo h264_nvenc;
  103.                     ;;
  104.                 *)
  105.                     echo "${vcodec}";
  106.                     ;;
  107.         esac
  108.     else
  109.         case "${vcodec}" in
  110.                 hevc)
  111.                     echo libx265;
  112.                     ;;
  113.                 h264)
  114.                     echo libx264;
  115.                     ;;
  116.                 *)
  117.                     echo "${vcodec}";
  118.                     ;;
  119.         esac
  120.     fi
  121. }
  122.  
  123.  
  124. function detect_hw_decoder() {
  125.     local codec_data codec_res;
  126.     codec_data="$(mediainfo --Inform="Video;%Format%" "${1}")"
  127.     if [ -e "/dev/nvidia0" ] && [ ! -z "${use_nvidia}" ] && [ ! -z "${use_nvidia_decoder}" ]; then
  128.         case "${codec_data}" in
  129.                     AVC)
  130.                         echo " [***] Using NVidia Accelerated H.264 Decoder" 1>&2
  131.                         codec_res=(-hwaccel cuvid -c:v h264_cuvid);
  132.                         ;;
  133.                     HEVC)
  134.                         echo " [***] Using NVidia Accelerated HEVC Decoder" 1>&2
  135.                         codec_res=(-hwaccel cuvid -c:v henc_cuvid);
  136.                         ;;
  137.                     MJPEG)
  138.                         echo " [***] Using NVidia Accelerated MJPEG Decoder" 1>&2
  139.                         codec_res=(-hwaccel cuvid -c:v mjpeg_cuvid);
  140.                         ;;
  141.                     MPEG1)
  142.                         echo " [***] Using NVidia Accelerated MPEG1 Decoder" 1>&2
  143.                         codec_res=(-hwaccel cuvid -c:v mpeg1_cuvid);
  144.                         ;;
  145.                     MPEG2)
  146.                         echo " [***] Using NVidia Accelerated MPEG2 Decoder" 1>&2
  147.                         codec_res=(-hwaccel cuvid -c:v mpeg2_cuvid);
  148.                         ;;
  149.                     MPEG4)
  150.                         echo " [***] Using NVidia Accelerated MPEG4 Decoder" 1>&2
  151.                         codec_res=(-hwaccel cuvid -c:v mpeg4_cuvid);
  152.                         ;;
  153.                     VC1)
  154.                         echo " [***] Using NVidia Accelerated VC1 Decoder" 1>&2
  155.                         codec_res=(-hwaccel cuvid -c:v vc1_cuvid);
  156.                         ;;
  157.                     VP8)
  158.                         echo " [***] Using NVidia Accelerated VP8 Decoder" 1>&2
  159.                         codec_res=(-hwaccel cuvid -c:v vp8_cuvid);
  160.                         ;;
  161.                     VP9)
  162.                         echo " [***] Using NVidia Accelerated VP9 Decoder" 1>&2
  163.                         codec_res=(-hwaccel cuvid -c:v vp9_cuvid);
  164.                         ;;
  165.                     *)
  166.                         echo " [***] Using Software Video Decoder" 1>&2
  167.                         codec_res=();
  168.                         ;;
  169.         esac
  170.     else
  171.         echo " [***] Using Software Video Decoder" 1>&2
  172.         codec_res=();
  173.     fi
  174.     echo "${codec_res[@]}"
  175. }
  176.  
  177.  
  178. function prepare_ffmpeg_opts() {
  179.     local cuvid_dec ffmpeg_opts_in ffmpeg_opts_qual ffmpeg_opts_out codec ffmpeg_opts_audio_out ffmpeg_opts_in_generic;
  180.  
  181.     ffmpeg_opts_audio_out=(-c:a "${acodec}" "${ffmpeg_aud_opts[@]}")
  182.     ffmpeg_opts_in_generic=(-y -loglevel warning -stats -threads "${ffmpeg_threads}")
  183.     if [ ! -z "${errchk}" ]; then
  184.         ffmpeg_opts_in_generic=("${ffmpeg_opts_in_generic[@]}" -xerror)
  185.     fi
  186.  
  187.  
  188.     codec=$(detect_hw_encoder);
  189.     # shellcheck disable=SC2207
  190.     cuvid_dec=($(detect_hw_decoder "${1}"));
  191.     if [ "${codec}" = "hevc_nvenc" ]; then
  192.         echo " [***] Using NVidia Accelerated HEVC Encoder" 1>&2
  193.         ffmpeg_opts_in=("${ffmpeg_opts_in_generic[@]}" "${cuvid_dec[@]}")
  194.         ffmpeg_opts_qual=(-preset "${preset}" -profile:v main -level:v 5.1 -2pass "${twopass}" -tier high -rc:v vbr_hq -b:v "${ffmpeg_avg_br}" -maxrate:v "${ffmpeg_max_br}" -bufsize:v "${ffmpeg_buf}" -g $((fps * 10)) -map 0 -movflags use_metadata_tags -movflags faststart)
  195.         ffmpeg_opts_out=("${ffmpeg_opts_audio_out[@]}" -c:v "${codec}" "${ffmpeg_opts_qual[@]}" -f matroska)
  196.     fi
  197.  
  198.     if [ "${codec}" = "libx265" ]; then
  199.         echo " [***] Using Software x265 Encoder" 1>&2
  200.         ffmpeg_opts_in=("${ffmpeg_opts_in_generic[@]}" "${cuvid_dec[@]}")
  201.         ffmpeg_opts_qual=(-preset "${preset}" -tune grain -x265-params "'-no-scenecut=1:-2pass=${twopass}:-rc=vbr_hq:-rc-lookahead=${fps}:-surfaces=56'" -b:v "${ffmpeg_avg_br}" -maxrate:v "${ffmpeg_max_br}" -bufsize:v "${ffmpeg_buf}" -g $((fps * 10)) -map 0 -movflags use_metadata_tags -movflags faststart)
  202.         ffmpeg_opts_out=("${ffmpeg_opts_audio_out[@]}" -c:v "${codec}" "${ffmpeg_opts_qual[@]}" -f matroska)
  203.     fi
  204.     ffmpeg_opts=("${ffmpeg_opts_in[@]}" -i "${1}" "${ffmpeg_opts_out[@]}" "${2}")
  205. }
  206.  
  207. zcmd() {
  208.     while [ ! -z "${1}" ]; do
  209.         case "${1}" in
  210.                 silent)
  211.                     local SILENT=1;
  212.                     shift
  213.                     ;;
  214.                 errchk)
  215.                     local ERRCHK=1;
  216.                     shift
  217.                     ;;
  218.                 *)
  219.                     break;
  220.                     ;;
  221.         esac
  222.     done
  223.  
  224.     if [ -z ${SILENT} ]; then
  225.         echo " [***] Executing: ${*}"
  226.     fi
  227.  
  228.     "${@}";
  229.     local RES=$?
  230.     if [ ${RES} != 0 ] && [ ! -z ${ERRCHK} ]; then
  231.             echo "exiting with error ${RES}"
  232.             exit "${RES}"
  233.     fi
  234.     return ${RES}
  235. }
  236.  
  237.  
  238. function process_file() {
  239.     local fname res;
  240.     if [ -z "${ext}" ]; then
  241.         ext="$(echo "${f}" | rev | cut -d'.' -f1 | rev)"
  242.     fi
  243.     fname="$(basename "${f}" | rev | cut -d'.' -f2- | rev)"
  244.  
  245.  
  246.     # renaming for output
  247.     for rep in "${REPLACE[@]}"; do
  248.         rep1=$(echo "${rep}" | cut -d'|' -f1)
  249.         rep2=$(echo "${rep}" | cut -d'|' -f2)
  250.         # shellcheck disable=SC2001
  251.         fname="$(echo "${fname}" | sed "s|${rep1}|${rep2}|")"
  252.         unset rep1 rep2;
  253.     done
  254.  
  255.     if [ ! -d "${outdir}" ]; then
  256.         zcmd errchk mkdir "${outdir}"
  257.     fi
  258.  
  259.    # shellcheck disable=SC2207
  260.     prepare_ffmpeg_opts "${1}" "${outdir}/${fname}.${ext}"
  261.     zcmd "${ffmpeg}" "${ffmpeg_opts[@]}"
  262.     res=$?
  263.     if [ ${res} -eq 0 ] && [ -f "${outdir}/${fname}.${ext}" ]; then
  264.         if [ -z "${delete}" ]; then
  265.             if [ ! -d "${olddir}" ]; then
  266.                 zcmd errchk mkdir "${olddir}"
  267.             fi
  268.             zcmd errchk mv -v "${f}" "${olddir}/$(basename "${f}")"
  269.         else
  270.             zcmd errchk rm -v "${f}"
  271.         fi
  272.         if [ ! -z "${dest}" ] && [ ! -z "${errchk}" ]; then
  273.             # move to dest, if defined, and if not running in noerr mode
  274.             mv -v "${outdir}/${fname}.${ext}" "$(realpath "${dest}")/${fname}.${ext}"
  275.         fi
  276.    else
  277.         if [ "${res}" -ne 255 ]; then
  278.             echo " [!!!] errors while encoding ${f}!"
  279.             if [ -z "${nomove}" ]; then
  280.                 if [ ! -d "${baddir}" ]; then
  281.                     zcmd errchk mkdir "${baddir}"
  282.                 fi
  283.                 echo "      Moving ${f} to ${baddir} ..."
  284.                 zcmd silent errchk mv "${f}" "${baddir}/$(basename "${f}")"
  285.             fi
  286.             echo "      Removing incomplete encoding at ${outdir}/${fname}.${ext} ..."
  287.             zcmd silent rm -f "${outdir}/${fname}.${ext}"
  288.         else
  289.             echo "exiting with error ${res}"
  290.             exit ${res}
  291.         fi
  292.     fi
  293.     processed_count=$((processed_count+1))
  294. }
  295.  
  296.  
  297. if [ -z "${singlefile}" ]; then
  298.     while IFS= read -r -d '' file; do
  299.         if [ -f "${file}" ]; then
  300.             if [ "$(echo "${file}" | grep 'zefie\.' -c)" -eq 0 ]; then
  301.                 FILES=("${FILES[@]}" "${file}")
  302.             fi
  303.         fi
  304.     done < <(find "${workdir}" -maxdepth 1 -regextype posix-extended -regex '.*\.(mp4|mkv)' -print0);
  305. else
  306.     FILES=("${singlefile}")
  307. fi
  308.  
  309. if [ "${#FILES[@]}" -gt 0 ]; then
  310.     if [ -z "${singlefile}" ]; then
  311.         # shellcheck disable=SC2207
  312.         IFS=$'\n' sorted=($(sort <<<"${FILES[*]}")); unset IFS;
  313.         FILES=("${sorted[@]}")
  314.         unset sorted;
  315.         echo -n " [***] Found ${#FILES[@]} file"
  316.         if [ "${#FILES[@]}" -ne 1 ]; then
  317.             echo -n "s";
  318.         fi
  319.         echo " ... processing now."
  320.     fi
  321.  
  322.     for f in "${FILES[@]}"; do
  323.             echo " [***] Processing ${f} (file $((processed_count+1)) of ${#FILES[@]}, $((${#FILES[@]}-processed_count-1)) remaining) ..."
  324.             process_file "${f}"
  325.     done
  326. else
  327.     echo " [!!!] Did not find any files :("
  328.     exit 1
  329. fi
  330.  
RAW Paste Data