123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- #!/bin/bash
- # Consider this script a collection of ffmpeg settings for video transcoding.
- # It doesn't make much sense to share this.
- # It's grown from my very personal transcoding needs, it WILL require adjustment.
- vcodec=x265 # x265 or x264
- # all bitrates in kbps !!!
- bitrate=1500 # visually lossless for x265, 1280x720px
- preset=medium # the default
- delay=600
- acodec=copy
- noopts=""
- speak=0
- usage() {
- [ -n "$1" ] && echo "
- $1"
- echo "
- Transcodes a list of video files with ffmpeg. Uses a 2-pass algorithm. Most
- energy went into the x265 version, but the x264 version also yields good
- results! Audio tracks are not transcoded, just copied.
- If mediainfo is installed you get some extra statistics at the end.
- Options:
- -b int video bitrate in kilobits per second, just the number (default: $bitrate)
- -v str video codec (x265|x264) (default: $vcodec)
- -a str audio codec (default: $acodec) - pass \"default\" to let ffmpeg decide.
- -p str preset to pass - possible values: ultrafast, superfast, veryfast,
- faster, fast, medium, slow, slower, veryslow
- -n do not use the hardcoded string of video encoding options
- -d int delay between completely transcoding files in seconds (default: $delay)
- -s say something when transcoding is finished. Requires espeak.
- There's little quality control, please take care what you pass on the command
- line.
- "
- exit 1
- }
- # internal, don't change
- notify=0
- sep="\n###############################################################################\n"
- while getopts "b:d:p:v:a:ns" opt; do
- case $opt in
- b) # bitrate
- [[ "$OPTARG" =~ ^[0-9]+$ ]] && [ "$OPTARG" -ge 1 ] || usage "-$opt: Invalid bitrate: $OPTARG (must be a positive integer)"
- bitrate="$OPTARG"
- ;;
- v) # vcodec
- vcodec="$OPTARG"
- ;;
- a) # acodec
- acodec="$OPTARG"
- ;;
- p) # preset
- preset="$OPTARG"
- ;;
- n) # no options
- noopts=".noopts"
- ;;
- d) # delay
- [[ "$OPTARG" =~ ^[0-9]+$ ]] && [ "$OPTARG" -ge 0 ] || usage "-$opt: Invalid delay time: $OPTARG (must be a positive integer)"
- delay="$OPTARG"
- ;;
- s) # use espeak
- speak=1
- ;;
- *) usage
- ;;
- esac
- done
- shift $((OPTIND-1))
- #~ echo "$@
- #~ bitrate: $bitrate
- #~ delay: $delay
- #~ vcodec: $vcodec
- #~ preset: $preset"
- #~ exit
- ffmpeg_cmd="ffmpeg -nostdin -hide_banner -analyzeduration 2147483647 -probesize 2147483647"
- endopts="-max_muxing_queue_size 9999"
- # about -max_muxing_queue_size: stackoverflow.com/a/56681096
- # about -nostdin: stackoverflow.com/a/14866487
- [[ "$acodec" == "default" ]] && audio_opts="" || audio_opts="-c:a $acodec"
- printf "Files to transcode: $@\nChosen bitrate: ${bitrate}k\nChosen codec: $vcodec\nChosen preset: $preset\n"
- counter=0
- for file in "$@"; do
- [ -r "$file" ] || continue
- duration="$(mediainfo --Output="Video;%Duration%" "$file")"
- duration="${duration%.*}"
- duration="${duration::-3}"
- outfile="${file%.*} video-$vcodec.bitrate-${bitrate}k.preset-$preset${noopts} acodec-${acodec}.mkv"
- if [ -r "$outfile" ]; then
- mv "$outfile" "${outfile%.*}.$(date +%s).${outfile##*.}" || exit 1
- fi
- start="$SECONDS"
- case "$vcodec" in
- x264)
- if [[ "$noopts" == ".noopts" ]]; then
- x264opts=""
- else
- x264opts="-x264opts frameref=15:fast_pskip=0"
- fi
- 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"
- $ffmpeg_cmd -y -i "$file" -an -c:v libx264 -pass 1 -preset "$preset" $bitrate_opts $x264opts $endopts -f rawvideo /dev/null && \
- printf "${sep}Command second pass:\nffmpeg -i $file $audio_opts -c:v libx264 -pass 2 -preset $preset -b:v $bitratek $x264opts $endopts $outfile$sep" && \
- $ffmpeg_cmd -i "$file" $audio_opts -c:v libx264 -pass 2 -preset $preset -b:v "$bitrate"k $x264opts $endopts "$outfile" && \
- notify=1 && rm ffmpeg2pass*.log*
- ;;
- x265)
- params=":vbv-bufsize=1000:vbv-maxrate=$bitrate"
- if [[ "$noopts" != ".noopts" ]]; then
- # see forum.doom9.org/showthread.php?t=172458 for a nice long explain about x265 params.
- 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"
- fi
- 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"
- $ffmpeg_cmd -y -i "$file" -an -c:v libx265 -preset "$preset" -x265-params "pass=1$params" $endopts -f matroska /dev/null && \
- printf "${sep}Command second pass:\nffmpeg -i $file $audio_opts -c:v libx265 -preset $preset -x265-params pass=2$params $outfile$sep" && \
- $ffmpeg_cmd -i "$file" $audio_opts -c:v libx265 -preset $preset -x265-params "pass=2$params" $endopts "$outfile" && \
- notify=1 && rm x265_2pass.log*
- ;;
- *)
- echo "Invalid video codec $vcodec"
- exit 1
- ;;
- esac
- end="$SECONDS"
- [[ "$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 ) ))"
- # if this isn't the last file, give the CPU time to cool down between transcoding
- ((++counter<$#)) && sleep $delay
- done
- if [[ "$notify" == 1 ]]; then
- [[ "$speak" == 1 ]] && espeak "${0##*/}. Finished."
- notify-send -t 0 "${0##*/}" "Finished."
- fi
|