-
-
Save jablair/7853511 to your computer and use it in GitHub Desktop.
From source: Transcode video file (works best with Blu-ray or DVD rip) into MP4 (or optionally Matroska) format, with configuration and at bitrate similar to popluar online downloads. Adds a dry-run option to print out the HandbrakeCLI command so it can be customized - for instance, including additional audio tracks. A stop-gap to adding better …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# | |
# transcode-video.sh | |
# | |
# Copyright (c) 2013 Don Melton | |
# | |
# This script is a wrapper for `HandBrakeCLI` (see: <http://handbrake.fr/>). | |
# The purpose of this script is to transcode a video file into a format and | |
# size similar to a video available for download from the iTunes Store. | |
# | |
# While this script works best with a Blu-ray Disc or DVD rip extracted using | |
# `MakeMKV` (see: <http://www.makemkv.com/>), it will work with any reasonable | |
# video file as input. | |
# | |
# The goal of this script is NOT to make an exact pixel-for-pixel, | |
# wave-for-wave copy of the input. That's not practical. Instead, the goal is | |
# to create a portable version of that input as quickly as possible at a | |
# reasonable quality --- something that can be enjoyed without prejudice on | |
# both a small tablet computer and a large, wide-screen home entertainment | |
# system with 5.1 surround sound. | |
# | |
# Also, it should be possible for the output of this script to be served by | |
# media management systems like Plex (see: <http://www.plexapp.com/>) without | |
# further dynamic transcoding. In other words, transcode a video file with | |
# this script and never have to explicitly or implicitly transcode it again. | |
# | |
# By default this script will try to create an MP4 format file with dual audio | |
# tracks (like iTunes video), but it can also output Matroska format (i.e. | |
# MKV) with a single audio track. | |
# | |
# For Blu-ray Disc input, this script will transcode the video with an average | |
# bitrate of 5000 kbps, varying 500-600 kbps either way. Again, this is like | |
# iTunes video. | |
# | |
# Please note that this script does NOT use the default x264 single-pass | |
# average bitrate (ABR) mode. Instead, it modifies that mode using the x264 | |
# `ratetol=inf` option. This causes ABR mode to behave more like constant rate | |
# factor (CRF) mode, varying the bitrate to maintain quality. | |
# | |
# However, while CRF mode can create wildly different output bitrates and file | |
# sizes depending on input, the modified ABR mode used here is much more | |
# predictable. And for some input, can create better output quality than, for | |
# example, using CRF mode with a quality setting of `20`. | |
# | |
# Also note that this modified ABR mode requires usage by this script of the | |
# x264 video buffer verifier (VBV) system. Otherwise the output video might | |
# not play on some output devices. | |
# | |
# For DVD input, this script will transcode the video with an average bitrate | |
# of 1800 kbps for PAL format discs and 1500 kbps for NTSC format. | |
# | |
# For all other video input, this script will choose an output average bitrate | |
# based on the input width, height and bitrate. That chosen output bitrate | |
# will never be greater that 80% of the input, nor will it exceed targets for | |
# similarly-dimensioned Blu-rays and DVDs. | |
# | |
# If faster transcoding time or a smaller output file size is desired for | |
# Blu-ray Disc and other 1080p video input, this script can scale that input | |
# to fit within a 1280x720 pixel bounds, targeting an average output bitrate | |
# of 4000 kbps. Again, much like 720p iTunes video. | |
# | |
# By default this script uses the x264 `fast` preset to transcode all video. | |
# This is done to achieve both the performance AND quality goals. Transcoding | |
# with the `fast` preset still results in high-quality output. | |
# | |
# However, if even higher quality video is desired, access to both the | |
# `medium` and `slow` x264 presets are available. Please note that using these | |
# other presets does NOT guarantee higher-quality output. Also, even though | |
# x264 has still more presets, faster settings are undesirable and slower ones | |
# are unnecessary. | |
# | |
# For input with multi-channel audio, two audio tracks will be created for MP4 | |
# file format output. The first track will be stereo AAC audio and the second | |
# track will be multi-channel AC-3 audio. For MKV file format output, only the | |
# AC-3 surround track will be created. | |
# | |
# For stereo or mono audio input, only a single AAC audio track is created for | |
# both MP4 and MKV file formats. | |
# | |
# Multi-channel audio is transcoded at 384 kbps, stereo at 160 kbps and mono | |
# at 80 kbps. | |
# | |
# For input with Blu-ray- or DVD-compatible subtitles (PGS or VobSub formats), | |
# this script will automatically burn the first subtitle into the video if | |
# that subtitle has its forced flag set. | |
# | |
# For other subtitle manipulation, it's best to use another program on this | |
# script's output after transcoding is complete. | |
# | |
# Some of this script's various behaviors can be changed or disabled via | |
# command line options. | |
# | |
# This script does NOT facilitate the automatic cropping behavior of | |
# HandBrakeCLI due to reliability problems with that feature. Instead, | |
# cropping bounds must be passed explicitly at the command line. Use the | |
# `detect-crop.sh` script to aid in determining these parameters. | |
# | |
# WARNING: This script was designed to work on OS X. There's no guarantee it | |
# will work on a different operating system. | |
# | |
about() { | |
cat <<EOF | |
$program 1.2 of December 6, 2013 | |
Copyright (c) 2013 Don Melton | |
EOF | |
exit 0 | |
} | |
usage() { | |
cat <<EOF | |
Transcode video file (works best with Blu-ray or DVD rip) into MP4 | |
(or optionally Matroska) format, with configuration and at bitrate | |
similar to popluar online downloads. | |
Usage: $program [OPTION]... [FILE] | |
--mkv output Matroska format with single audio track | |
--preset NAME use x264 fast|medium|slow preset (default: fast) | |
--abr BITRATE set average video bitrate target | |
(default: based on input) | |
--crf FACTOR use constant rate factor mode instead of ABR | |
--resize resize video to fit within 1280x720 pixel bounds | |
--rate FPS force video frame rate (default: based on input) | |
--no-ac3 don't output multi-channel AC-3 audio | |
--ac3 BITRATE set AC-3 audio bitrate to 384|448|640 kbps | |
(default: 384) | |
--crop T:B:L:R set video croping bounds (default: 0:0:0:0) | |
--no-auto-burn don't automatically burn first "forced" subtitle | |
--dry-run show the HandbreakCLI command that would be run | |
but don't execute | |
--help display this help and exit | |
--version output version information and exit | |
Requires \`HandBrakeCLI\` and \`mediainfo\` executables in \$PATH. | |
Output and log file are written to current working directory. | |
EOF | |
exit 0 | |
} | |
syntax_error() { | |
echo "$program: $1" >&2 | |
echo "Try \`$program --help\` for more information." >&2 | |
exit 1 | |
} | |
die() { | |
echo "$program: $1" >&2 | |
exit ${2:-1} | |
} | |
readonly program="$(basename "$0")" | |
case $1 in | |
--help) | |
usage | |
;; | |
--version) | |
about | |
;; | |
esac | |
container_format='mp4' | |
container_format_options='--large-file --optimize' | |
preset_options='--x264-preset fast' | |
reference_frames_option='' | |
rate_tolerance_option='' | |
bitrate='' | |
rate_factor='' | |
resize='' | |
frame_rate_options='' | |
ac3_bitrate='384' | |
crop='0:0:0:0' | |
size_options='--strict-anamorphic' | |
auto_burn='yes' | |
while [ "$1" ]; do | |
case $1 in | |
--mkv) | |
container_format='mkv' | |
container_format_options='' | |
;; | |
--preset) | |
preset="$2" | |
shift | |
case $preset in | |
fast|slow) | |
preset_options="--x264-preset $preset" | |
;; | |
medium) | |
preset_options='' | |
;; | |
*) | |
syntax_error "unsupported preset: $preset" | |
;; | |
esac | |
;; | |
--abr) | |
bitrate="$2" | |
rate_factor='' | |
shift | |
;; | |
--crf) | |
rate_factor="$2" | |
bitrate='' | |
shift | |
;; | |
--resize) | |
resize='yes' | |
;; | |
--rate) | |
frame_rate_options="--rate $2" | |
shift | |
;; | |
--no-ac3|--no-surround) | |
ac3_bitrate='' | |
;; | |
--ac3) | |
ac3_bitrate="$2" | |
shift | |
case $ac3_bitrate in | |
384|448|640) | |
;; | |
*) | |
syntax_error "unsupported AC-3 audio bitrate: $ac3_bitrate" | |
;; | |
esac | |
;; | |
--crop) | |
crop="$2" | |
shift | |
;; | |
--no-auto-burn) | |
auto_burn='' | |
;; | |
--dry-run) | |
dry_run='yes' | |
;; | |
-*) | |
syntax_error "unrecognized option: $1" | |
;; | |
*) | |
break | |
;; | |
esac | |
shift | |
done | |
readonly input="$1" | |
if [ ! "$input" ]; then | |
syntax_error 'too few arguments' | |
fi | |
if [ ! -f "$input" ]; then | |
die "input file not found: $input" | |
fi | |
readonly output="$(basename "$input" | sed 's/\.[^.]*$//').$container_format" | |
if [ -e "$output" ] && [ ! "$dry_run" ]; then | |
die "output file already exists: $output" | |
fi | |
if ! $(which HandBrakeCLI >/dev/null); then | |
die 'executable not in $PATH: HandBrakeCLI' | |
fi | |
if ! $(which mediainfo >/dev/null); then | |
die 'executable not in $PATH: mediainfo' | |
fi | |
# Determine maximum output video bitrate based on input size: | |
# 5000 kbps for Blu-ray or other content larger than 1280x720 pixels. | |
# 4000 kbps for resized or other content larger than 720x576 pixels. | |
# 1800 kbps for PAL DVD or other content taller than 480 pixels. | |
# 1500 kbps for NTSC DVD or other content. | |
# Limit output video bitrate to 80% of input video bitrate. But also allow | |
# user to set a specific average video bitrate target. | |
# Set `x264` video buffer verifier (VBV) size and maximum rate to values | |
# appropriate for H.264 level with High profile: | |
# 25000 for level 4.0 (e.g. Blu-ray input) | |
# 17500 for level 3.1 (e.g. 720p input) | |
# 12500 for level 3.0 (e.g. DVD input) | |
# When using `slow` preset for output larger than 1280x720 pixels, limit | |
# reference frames to 4 to maintain compatibility with H.264 level 4.0. | |
readonly width="$(mediainfo --Inform='Video;%Width%' "$input")" | |
readonly height="$(mediainfo --Inform='Video;%Height%' "$input")" | |
if (($width > 1280)) || (($height > 720)); then | |
if [ ! "$resize" ]; then | |
vbv_value='25000' | |
max_bitrate='5000' | |
if [ "$preset" == 'slow' ]; then | |
reference_frames_option='ref=4:' | |
fi | |
else | |
vbv_value='17500' | |
max_bitrate='4000' | |
size_options='--maxWidth 1280 --maxHeight 720 --loose-anamorphic' | |
fi | |
elif (($width > 720)) || (($height > 576)); then | |
vbv_value='17500' | |
max_bitrate='4000' | |
else | |
vbv_value='12500' | |
if (($height > 480)); then | |
max_bitrate='1800' | |
else | |
max_bitrate='1500' | |
fi | |
fi | |
# Allow user to explicitly use constant rate factor (CRF) mode instead of this | |
# script's modified average bitrate (ABR) mode. | |
if [ "$rate_factor" ]; then | |
rate_control_options="--quality $rate_factor" | |
else | |
rate_tolerance_option=':ratetol=inf' | |
if [ "$bitrate" ]; then | |
if (($bitrate > $vbv_value)); then | |
bitrate="$vbv_value" | |
fi | |
else | |
readonly min_bitrate="$((max_bitrate / 2))" | |
bitrate="$(mediainfo --Inform='Video;%BitRate%' "$input")" | |
if [ ! "$bitrate" ]; then | |
bitrate="$(mediainfo --Inform='General;%OverallBitRate%' "$input")" | |
bitrate="$(((bitrate / 10) * 9))" | |
fi | |
if [ "$bitrate" ]; then | |
bitrate="$(((bitrate / 5) * 4))" | |
bitrate="$((bitrate / 1000))" | |
bitrate="$(((bitrate / 100) * 100))" | |
if (($bitrate > $max_bitrate)); then | |
bitrate="$max_bitrate" | |
elif (($bitrate < $min_bitrate)); then | |
bitrate="$min_bitrate" | |
fi | |
else | |
bitrate="$min_bitrate" | |
fi | |
fi | |
rate_control_options="--vb $bitrate" | |
fi | |
# Set peak video frame rate to 30 fps so `HandBrakeCLI` can dynamically | |
# determine output frame rate, but force output frame rate of `23.976` if | |
# input frame rate is exactly `29.970`, or if user has indicated a specific | |
# output frame rate. | |
frame_rate="$(mediainfo --Inform='Video;%FrameRate_Original%' "$input")" | |
if [ ! "$frame_rate" ]; then | |
frame_rate="$(mediainfo --Inform='Video;%FrameRate%' "$input")" | |
fi | |
if [ ! "$frame_rate_options" ]; then | |
if [ "$frame_rate" == '29.970' ]; then | |
frame_rate_options='--rate 23.976' | |
else | |
frame_rate_options='--rate 30 --pfr' | |
fi | |
fi | |
# Transcode multi-channel audio at 384 kbps in Dolby Digital (AC-3) format. | |
# Use existing audio if it's already in that format and at or below that | |
# bitrate. | |
# Transcode stereo or mono audio using `HandBrakeCLI` default behavior, | |
# Advanced Audio Coding (AAC) format at 160 or 80 kbps. Use existing audio if | |
# it's already in that format. | |
# For MP4 output, transcode multi-channel audio input first in stereo AAC | |
# format and then add a second audio track in multi-channel AC-3 format. | |
# Allow user to decide whether to output multi-channel AC-3 audio. | |
# Also, allow user to increase AC-3 audio bitrate to 448 or 640 kbps. | |
readonly audio_channels="$(mediainfo --Inform='Audio;%Channels%' "$input" | sed 's/^\([0-9]\).*$/\1/')" | |
readonly audio_format="$(mediainfo --Inform='General;%Audio_Format_List%' "$input" | sed 's| /.*||')" | |
if [ "$ac3_bitrate" ] && (($audio_channels > 2)); then | |
readonly audio_bitrate="$(mediainfo --Inform='Audio;%BitRate%' "$input")" | |
if [ "$audio_format" == 'AC-3' ] && (($audio_bitrate <= ($ac3_bitrate * 1000))); then | |
if [ "$container_format" == 'mp4' ]; then | |
audio_options='--aencoder ca_aac,copy:ac3' | |
else | |
audio_options='--aencoder copy:ac3' | |
fi | |
elif [ "$container_format" == 'mp4' ]; then | |
audio_options="--aencoder ca_aac,ac3 --ab ,$ac3_bitrate" | |
else | |
audio_options="--aencoder ac3 --ab $ac3_bitrate" | |
fi | |
elif [ "$audio_format" == 'AAC' ]; then | |
audio_options='--aencoder copy:aac' | |
else | |
audio_options='' | |
fi | |
# Detelecine video if input frame rate is exactly `29.970` fps. This forces | |
# consistent output frame rate for NTSC DVD input, smoothing playback. | |
if [ "$frame_rate" == '29.970' ]; then | |
filter_options='--detelecine' | |
else | |
filter_options='' | |
fi | |
# Automatically burn first subtitle into video output if that subtitle has its | |
# forced flag set and it's a supported format for burning. But allow user to | |
# disable this behavior. | |
readonly subtitle_forced="$(mediainfo --Inform='Text;%Forced%' "$input" | sed 's/^\(Yes\).*$/\1/')" | |
readonly subtitle_format="$(mediainfo --Inform='Text;%Format%' "$input" | sed 's/^\(PGS\).*$/\1/;s/^\(VobSub\).*$/\1/')" | |
if [ "$auto_burn" ] && [ "$subtitle_forced" == 'Yes' ] && ( [ "$subtitle_format" == 'PGS' ] || [ "$subtitle_format" == 'VobSub' ] ); then | |
subtitle_options='--subtitle 1 --subtitle-burned' | |
else | |
subtitle_options='' | |
fi | |
echo "Transcoding: $input" >&2 | |
# Transcode video in single-pass average bitrate (ABR) mode, but set rate | |
# tolerance to maximum (using `ratetol=inf` option) so behavior is more like | |
# constant rate factor (CRF) mode. But also allow user to explicitly use CRF | |
# instead of this modified ABR mode. | |
if [ ! "$dry_run" ]; then | |
time HandBrakeCLI \ | |
--markers \ | |
$container_format_options \ | |
--encoder x264 \ | |
$preset_options \ | |
--encopts ${reference_frames_option}vbv-maxrate=$vbv_value:vbv-bufsize=$vbv_value${rate_tolerance_option} \ | |
$rate_control_options \ | |
$frame_rate_options \ | |
$audio_options \ | |
--crop $crop \ | |
$size_options \ | |
$filter_options \ | |
$subtitle_options \ | |
--input "$input" \ | |
--output "$output" \ | |
2>&1 | tee -a "${output}.log" | |
else | |
echo HandBrakeCLI \ | |
--markers \ | |
$container_format_options \ | |
--encoder x264 \ | |
$preset_options \ | |
--encopts ${reference_frames_option}vbv-maxrate=$vbv_value:vbv-bufsize=$vbv_value${rate_tolerance_option} \ | |
$rate_control_options \ | |
$frame_rate_options \ | |
$audio_options \ | |
--crop $crop \ | |
$size_options \ | |
$filter_options \ | |
$subtitle_options \ | |
--input "$input" \ | |
--output "$output" | |
fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment