123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- #!/bin/sh
- ## TODO: handle srt encoding?
- usage () {
- cat <<EOF>&2
- Usage: ${0##*/} [OPTIONS] FILES|FOLDERS
- Transcode FILES or files found in FOLDERS to x264 and ogg vorbis in a MKV container.
- Black stripes are *not* cropped by default since it may be sometimes inaccurate.
- A time stamp is appended to the output filenames.
- CRF encoding is performed by default. It targets a desired quality. CRF 20 is
- usually a good factor, 18 is higher quality, 22 is lower. The 18-22 range is
- reasonable. The quality loss is usually noticeable above 22 and the size
- increase is significant below 18 for a barely noticeable quality change.
- 2-pass encoding can achieve best compression, if the given bitrate is
- appropriate. A sample in CRF encoding can give a good bitrate estimate for the
- desired quality. Bitrates for 1080p movies are found within the 1000k-10000k
- range.
- You can get a list of supported codecs with
- $ ffmpeg -codecs
- User options are read from the TC_VIDEO_OPT variable. This can be useful if you
- often use the same options.
- Options:
- -a CODEC: Audio codec supported by ffmpeg. (Default: libvorbis).
- -b: Default bitrate for audio stream with unidentifiable bitrate.
- If 0, copy stream (default).
- -c: Copy streams (no reencoding).
- -C: Enable auto-crop (needs video reencoding).
- -d BR: Perform a 2-pass video encoding at bitrate BR and set '-tune film'.
- -f: Remove source when done.
- -h: Display this help.
- -o OPT: Additional options.
- -p: Preview changes, do not encode.
- -P: Generate two sequence of thumbnails, one cropped, the other not.
- Enable auto-crop, do not encode.
- -q QUAL: x264 CRF quality (default 20).
- -s: Sample of 5 minutes.
- -S MIN: Sample of MIN minutes.
- -t: Remove all "title" metadata.
- Examples:
- * Get a preview of changes.
- ${0##*/} -p input.video
- * Proceed with default options over folders and files.
- ${0##*/} input-folder input.video
- * Preview black stripes cropping.
- ${0##*/} -P input.video
- * Process with default options and remove black stripes.
- ${0##*/} -C input.video
- * Exchange streams 1 and 2 by first removing them, then adding them in the
- desired order.
- ${0##*/} -o '-map -0:1 -map -0:2 -map 0:2 -map 0:1' input.video
- * Change audio stream 1 title and remove audio stream 2 title:
- ${0##*/} -o '-metadata:s:a:0 title="FR: OGG Stereo" -metadata:s:a:1 title=' input.video
- See <https://trac.ffmpeg.org/wiki/Encode/H.264>, 'ffmpeg -h encoder=libx264' and 'x264 --fullhelp'.
- EOF
- }
- EXT="mkv"
- AUDIO_CODEC="libvorbis"
- VIDEO_PARAM="-c:v libx264 -preset slower -crf 20"
- AUDIO_PARAM=""
- VIDEO_FILTER=""
- AUDIO_DEFAULT_RATE=0
- OVERWRITE="-n"
- SAMPLE=""
- OPT_OVERWRITE=false
- OPT_REMOVE_TITLE=false
- OPT_CROP=false
- OPT_CROPPREVIEW=false
- OPT_PREVIEW=false
- OPT_COPY=false
- OPT_2PASS=false
- while getopts ":a:b:cCd:fho:pPq:sS:t" opt; do
- case $opt in
- a)
- AUDIO_CODEC=$OPTARG ;;
- b)
- AUDIO_DEFAULT_RATE=$OPTARG ;;
- c)
- VIDEO_PARAM="-c:v copy"
- AUDIO_PARAM="-c:a copy"
- OPT_COPY=true;;
- C)
- OPT_CROP=true;;
- d)
- VIDEO_PARAM="-c:v libx264 -preset slower -b:v ${OPTARG}k"
- OPT_2PASS=true;;
- f)
- OVERWRITE="-y"
- OPT_OVERWRITE=true;;
- h)
- usage
- exit 1 ;;
- o)
- TC_VIDEO_OPT="$OPTARG" ;;
- p)
- OPT_PREVIEW=true ;;
- P)
- OPT_CROP=true
- OPT_PREVIEW=true
- OPT_CROPPREVIEW=true ;;
- q)
- VIDEO_PARAM="-c:v libx264 -preset slower -crf $OPTARG";;
- s)
- SAMPLE="-ss 60 -t 300" ;;
- S)
- SAMPLE="-ss 60 -t $((60*OPTARG))" ;;
- t)
- OPT_REMOVE_TITLE=true ;;
- \?)
- usage
- exit 1 ;;
- esac
- done
- shift $((OPTIND - 1))
- if [ $# -eq 0 ]; then
- usage
- exit
- fi
- if ! command -v ffmpeg >/dev/null; then
- echo "ffmpeg required."
- exit
- fi
- highfreq () {
- awk 'BEGIN{max=0} /crop=/ {t[$NF]++; if (t[$NF]>max) {max=t[$NF]; val=$NF}} END{print val}'
- }
- ## Usage: cropvalue FILE STEP
- ## Return the crop values as ffmpeg output them.
- cropvalue () {
- local step=$2
- for i in $(seq "$step" "$step" $((5*step))); do
- ffmpeg -nostdin -ss "$i" -i "$1" -t 10 -vf "cropdetect=24:2:0" -f null - 2>&1
- done | highfreq
- }
- ## Return the audio encoding parameters. For instance
- ## -c:2 libvorbis -b:2 384k -c:5 copy
- ## If input codec is $AUDIO_CODEC, we copy. If some stream bitrates are missing
- ## or 0, we encode it to a default value. If default value is 0, then we copy
- ## stream.
- audiobitrate () {
- local bitrate
- for i in $(seq 0 $((format_nb_streams-1))); do
- ## Skip non audio tracks.
- [ "$(eval echo \$streams_stream_"$i"_codec_type)" != "audio" ] && continue
- bitrate=$(eval echo \$streams_stream_"$i"_bit_rate)
- if [ -n "$bitrate" ] && [ "$bitrate" -gt 0 ] 2>/dev/null; then
- ## If non-empty and a positive number.
- bitrate=$((bitrate / 1000))
- else
- bitrate="$AUDIO_DEFAULT_RATE"
- fi
- if [ "$bitrate" -le 0 ] || \
- [ "$AUDIO_CODEC" = "$(eval echo \$streams_stream_"$i"_codec_name)" ] || \
- [ "$AUDIO_CODEC" = "lib$(eval echo \$streams_stream_"$i"_codec_name)" ]; then
- printf -- "-c:%s copy " "$i"
- else
- [ "$bitrate" -gt 500 ] && bitrate=500
- printf -- "-c:%s %s -b:%s %sk " "$i" "$AUDIO_CODEC" "$i" "$bitrate"
- fi
- done
- }
- transcode () {
- echo "==> [$1]"
- OUTPUT="${1%.*}.$EXT"
- [ -e "$OUTPUT" ] && OUTPUT="${1%.*}-$(date '+%F-%H%M%S').$EXT"
- ## Metadata (i.e. tags + technical data).
- buffer="$(ffprobe -v quiet -print_format flat=s=_ -show_streams -show_format "$1")"
- if [ $? -ne 0 ]; then
- echo "File [$1] is unsupported by FFmpeg."
- return
- fi
- ## The following 'eval' defines variables such as format_nb_streams
- eval "$buffer"
- unset buffer
- STREAM_TITLE=""
- if $OPT_REMOVE_TITLE; then
- for i in $(seq 0 $((format_nb_streams-1)) ); do
- STREAM_TITLE="${STREAM_TITLE}-metadata:s:$i title= "
- done
- fi
- if $OPT_CROP; then
- echo "Computing crop values... "
- ## For 5 different timeslices of 1 second at every 1/6th of the video,
- ## we sample the crop values. We keep the values with highest
- ## frequency. This is much faster than encoding in one pass with low
- ## framerate.
- STEP=${format_duration:-$streams_stream_0_duration}
- STEP="${STEP%%.*}"
- STEP=$((STEP/6))
- VIDEO_FILTER="-vf $(cropvalue "$1" "$STEP")"
- if $OPT_CROPPREVIEW; then
- echo "Generating preview... "
- for i in $(seq $STEP $STEP $((5*STEP))); do
- ffmpeg -nostdin -v warning -y -ss "$i" -i "$1" \
- -f image2 -vframes 1 "$VIDEO_FILTER" "${1%.*}-preview-$i-cropped.png" \
- -f image2 -vframes 1 "${1%.*}-preview-$i-uncropped.png"
- done
- fi
- fi
- ## WARNING: We mix down audio to 2 channels with '-ac 2'. This greatly reduce
- ## file size and avoid any confusion for playback, which is often the case
- ## when converting DTS to any other format. (DTS has embedded channel
- ## description which is not always available in other formats.)
- ! $OPT_COPY && AUDIO_PARAM="$(audiobitrate "$1") -ac 2"
- cat <<EOF>&2
- User options: ${TC_VIDEO_OPT:-none}
- Sample: ${SAMPLE:-no}
- Clear tags: $OPT_REMOVE_TITLE
- In place: $OPT_OVERWRITE
- Crop: $OPT_CROP
- Video params: $VIDEO_PARAM
- Video filter: $VIDEO_FILTER
- Audio params: $AUDIO_PARAM
- Output file: $OUTPUT
- EOF
- $OPT_PREVIEW && return
- if $OPT_2PASS; then
- echo ":: FIRST PASS"
- ffmpeg -nostdin -y "$SAMPLE" -i "$1" \
- "$VIDEO_PARAM" -pass 1 -tune film "$VIDEO_FILTER" \
- "$AUDIO_PARAM" \
- -c:s copy \
- -dn \
- -map 0 "$STREAM_TITLE" \
- "$TC_VIDEO_OPT" -f matroska /dev/null
- VIDEO_PARAM="$VIDEO_PARAM -pass 2 -tune film"
- echo
- echo ":: SECOND PASS"
- fi
- ffmpeg -nostdin "$OVERWRITE" -i "$1" \
- "$VIDEO_PARAM" "$VIDEO_FILTER" \
- "$AUDIO_PARAM" \
- -c:s copy \
- -dn \
- -map 0 "$STREAM_TITLE" \
- "$SAMPLE" "$TC_VIDEO_OPT" "$OUTPUT"
- $OPT_2PASS && rm -v ffmpeg2pass-0.log ffmpeg2pass-0.log.mbtree
- if $OPT_OVERWRITE; then
- rm "$1"
- mv -f "$OUTPUT" "${1%.*}.$EXT"
- fi
- echo
- }
- for i; do
- transcode "$i"
- done
|