transcode 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/bin/bash
  2. # Consider this script a collection of ffmpeg settings for video transcoding.
  3. # It doesn't make much sense to share this.
  4. # It's grown from my very personal transcoding needs, it WILL require adjustment.
  5. vcodec=x265 # x265 or x264
  6. # all bitrates in kbps !!!
  7. bitrate=1500 # visually lossless for x265, 1280x720px
  8. preset=medium # the default
  9. delay=600
  10. acodec=copy
  11. noopts=""
  12. speak=0
  13. usage() {
  14. [ -n "$1" ] && echo "
  15. $1"
  16. echo "
  17. Transcodes a list of video files with ffmpeg. Uses a 2-pass algorithm. Most
  18. energy went into the x265 version, but the x264 version also yields good
  19. results! Audio tracks are not transcoded, just copied.
  20. If mediainfo is installed you get some extra statistics at the end.
  21. Options:
  22. -b int video bitrate in kilobits per second, just the number (default: $bitrate)
  23. -v str video codec (x265|x264) (default: $vcodec)
  24. -a str audio codec (default: $acodec) - pass \"default\" to let ffmpeg decide.
  25. -p str preset to pass - possible values: ultrafast, superfast, veryfast,
  26. faster, fast, medium, slow, slower, veryslow
  27. -n do not use the hardcoded string of video encoding options
  28. -d int delay between completely transcoding files in seconds (default: $delay)
  29. -s say something when transcoding is finished. Requires espeak.
  30. There's little quality control, please take care what you pass on the command
  31. line.
  32. "
  33. exit 1
  34. }
  35. # internal, don't change
  36. notify=0
  37. sep="\n###############################################################################\n"
  38. while getopts "b:d:p:v:a:ns" opt; do
  39. case $opt in
  40. b) # bitrate
  41. [[ "$OPTARG" =~ ^[0-9]+$ ]] && [ "$OPTARG" -ge 1 ] || usage "-$opt: Invalid bitrate: $OPTARG (must be a positive integer)"
  42. bitrate="$OPTARG"
  43. ;;
  44. v) # vcodec
  45. vcodec="$OPTARG"
  46. ;;
  47. a) # acodec
  48. acodec="$OPTARG"
  49. ;;
  50. p) # preset
  51. preset="$OPTARG"
  52. ;;
  53. n) # no options
  54. noopts=".noopts"
  55. ;;
  56. d) # delay
  57. [[ "$OPTARG" =~ ^[0-9]+$ ]] && [ "$OPTARG" -ge 0 ] || usage "-$opt: Invalid delay time: $OPTARG (must be a positive integer)"
  58. delay="$OPTARG"
  59. ;;
  60. s) # use espeak
  61. speak=1
  62. ;;
  63. *) usage
  64. ;;
  65. esac
  66. done
  67. shift $((OPTIND-1))
  68. #~ echo "$@
  69. #~ bitrate: $bitrate
  70. #~ delay: $delay
  71. #~ vcodec: $vcodec
  72. #~ preset: $preset"
  73. #~ exit
  74. ffmpeg_cmd="ffmpeg -nostdin -hide_banner -analyzeduration 2147483647 -probesize 2147483647"
  75. endopts="-max_muxing_queue_size 9999"
  76. # about -max_muxing_queue_size: stackoverflow.com/a/56681096
  77. # about -nostdin: stackoverflow.com/a/14866487
  78. [[ "$acodec" == "default" ]] && audio_opts="" || audio_opts="-c:a $acodec"
  79. printf "Files to transcode: $@\nChosen bitrate: ${bitrate}k\nChosen codec: $vcodec\nChosen preset: $preset\n"
  80. counter=0
  81. for file in "$@"; do
  82. [ -r "$file" ] || continue
  83. duration="$(mediainfo --Output="Video;%Duration%" "$file")"
  84. duration="${duration%.*}"
  85. duration="${duration::-3}"
  86. outfile="${file%.*} video-$vcodec.bitrate-${bitrate}k.preset-$preset${noopts} acodec-${acodec}.mkv"
  87. if [ -r "$outfile" ]; then
  88. mv "$outfile" "${outfile%.*}.$(date +%s).${outfile##*.}" || exit 1
  89. fi
  90. start="$SECONDS"
  91. case "$vcodec" in
  92. x264)
  93. if [[ "$noopts" == ".noopts" ]]; then
  94. x264opts=""
  95. else
  96. x264opts="-x264opts frameref=15:fast_pskip=0"
  97. fi
  98. printf "${sep}Command first pass:\nffmpeg -y -i $file -an -c:v libx264 -pass 1 -preset $preset $bitrate_opts $x264opts -f rawvideo /dev/null$sep"
  99. $ffmpeg_cmd -y -i "$file" -an -c:v libx264 -pass 1 -preset "$preset" $bitrate_opts $x264opts $endopts -f rawvideo /dev/null && \
  100. printf "${sep}Command second pass:\nffmpeg -i $file $audio_opts -c:v libx264 -pass 2 -preset $preset -b:v $bitratek $x264opts $endopts $outfile$sep" && \
  101. $ffmpeg_cmd -i "$file" $audio_opts -c:v libx264 -pass 2 -preset $preset -b:v "$bitrate"k $x264opts $endopts "$outfile" && \
  102. notify=1 && rm ffmpeg2pass*.log*
  103. ;;
  104. x265)
  105. params=":vbv-bufsize=1000:vbv-maxrate=$bitrate"
  106. if [[ "$noopts" != ".noopts" ]]; then
  107. # see forum.doom9.org/showthread.php?t=172458 for a nice long explain about x265 params.
  108. params=":ctu=32:max-tu-size=16:crf=20.0:tu-intra-depth=2:tu-inter-depth=2:rdpenalty=2:me=3:subme=5:merange=44:b-intra=1:amp=0:ref=5:weightb=1:keyint=360:min-keyint=1:bframes=8:aq-mode=1:aq-strength=1.0:rd=5:psy-rd=1.5:psy-rdoq=5.0:rdoq-level=1:sao=0:open-gop=0:rc-lookahead=80:scenecut=40:max-merge=4:qcomp=0.8:strong-intra-smoothing=0:deblock=-2:qg-size=16:pbratio=1.2$params"
  109. fi
  110. printf "${sep}Command first pass:\nffmpeg -y -i $file -an -c:v libx265 -preset $preset -x265-params pass=1$params -f matroska /dev/null$sep"
  111. $ffmpeg_cmd -y -i "$file" -an -c:v libx265 -preset "$preset" -x265-params "pass=1$params" $endopts -f matroska /dev/null && \
  112. printf "${sep}Command second pass:\nffmpeg -i $file $audio_opts -c:v libx265 -preset $preset -x265-params pass=2$params $outfile$sep" && \
  113. $ffmpeg_cmd -i "$file" $audio_opts -c:v libx265 -preset $preset -x265-params "pass=2$params" $endopts "$outfile" && \
  114. notify=1 && rm x265_2pass.log*
  115. ;;
  116. *)
  117. echo "Invalid video codec $vcodec"
  118. exit 1
  119. ;;
  120. esac
  121. end="$SECONDS"
  122. [[ "$notify" == 1 ]] && printf "$sep\nTranscoding %s (duration: %ds) took %ds in two passes.\nThat is %ds per one second of video.\nThe output file is smaller than the original by a factor of %d\n" "$outfile" "$duration" "$((end - start))" "$(( (end - start) / duration ))" "$(( $(du -b "$file" | cut -f1 ) / $(du -b "$outfile" | cut -f1 ) ))"
  123. # if this isn't the last file, give the CPU time to cool down between transcoding
  124. ((++counter<$#)) && sleep $delay
  125. done
  126. if [[ "$notify" == 1 ]]; then
  127. [[ "$speak" == 1 ]] && espeak "${0##*/}. Finished."
  128. notify-send -t 0 "${0##*/}" "Finished."
  129. fi