Skip to content

Instantly share code, notes, and snippets.

@xarblu
Created March 16, 2024 16:19
Show Gist options
  • Select an option

  • Save xarblu/b6beccad6bdb96b181633284f00770b3 to your computer and use it in GitHub Desktop.

Select an option

Save xarblu/b6beccad6bdb96b181633284f00770b3 to your computer and use it in GitHub Desktop.
FFMPEG Movie Encode Script
#!/usr/bin/env bash
# Usage:
# movenc.sh <infile> [<outfile>]
#
# User variables/args:
# CROP - ffmpeg crop filter of format xx:xx:xx:xx
# ASTREAMS - ffmpeg audio stream
# SSTREAMS - ffmpeg subtitle stream
# TUNE - x264 tune
#
# Dependencies:
# ffmpeg with libfdk_aac and x264
# jq
for arg in "${@}"; do
case "${arg}" in
--astreams=*)
ASTREAMS="${arg#*=}"
shift
;;
--sstreams=*)
SSTREAMS="${arg#*=}"
shift
;;
--crop=*)
CROP="${arg#*=}"
shift
;;
--tune=*)
TUNE="${arg#*=}"
shift
;;
esac
done
# needs 1 arg, a file that exists
[[ ! -f "${1}" ]] && echo "File \"${1}\" doesn't exist." && exit 1
# arg $1 is input file
infile="${1}"
# arg $2 is output file (defaults to ${infile%.*}+done.mkv)
outfile="${2:-${infile%.*}+done.mkv}"
# get ffprobe attribute for v:0
get_vattr() {
ffprobe -i "${infile}" -v quiet -print_format json \
-show_streams -select_streams v:0 \
| jq --raw-output ".streams[0].${1}"
}
# get sample_aspect_ratio via ffprobe
get_sar() {
get_vattr 'sample_aspect_ratio' | sed -e 's|:|/|'
}
# get resolution via ffprobe if not cropped, else use crop resolution
get_width() {
if [[ -z "${CROP}" ]]; then
echo -n "$(get_vattr 'width')"
else
echo -n "${CROP}" | awk -F ':' '{ print $1 }'
fi
}
get_height() {
if [[ -z "${CROP}" ]]; then
echo -n "$(get_vattr 'height')"
else
echo -n "${CROP}" | awk -F ':' '{ print $2 }'
fi
}
# get crop filter string
get_crop() {
[[ -n "${CROP}" ]] && echo -n ",crop=${CROP}"
}
# $1: stream_spec $2: tag
get_tag_for() {
ffprobe -i "${infile}" -v quiet -print_format json \
-show_streams -select_streams "${1#0:}" \
| jq --raw-output ".streams[0].tags.${2}"
}
add_vflags() {
local fps gop
fps="$(get_vattr 'r_frame_rate' | bc)"
_FFMPEG_ARGS+=( -c:v libx264 )
# set preset based on res (576 is PAL DVD)
if (( $(get_height) > 576 )); then
_FFMPEG_ARGS+=( -preset slower )
else
_FFMPEG_ARGS+=( -preset veryslow )
fi
# considered "visually lossless"
_FFMPEG_ARGS+=( -crf 17 )
# optionally add a tune
if [[ -n "${TUNE}" ]]; then
_FFMPEG_ARGS+=( -tune "${TUNE}" )
fi
# GOP is 10*fps capped at 300
gop="$(( "${fps}" * 10 ))"
if (( ${gop} >= 300 )); then
_FFMPEG_ARGS+=( -g 300 )
else
_FFMPEG_ARGS+=( -g "${gop}" )
fi
# always apply sar filter to get correct aspect ratio
# conditionally add crop filter
_FFMPEG_ARGS+=( -vf "setsar=$(get_sar)$(get_crop)" )
}
# unconditional libfdk_aac with -vbr 5
add_aflags() {
_FFMPEG_ARGS+=(
-c:a libfdk_aac
-vbr 5
)
}
# for now nothing fancy, just copy
add_sflags() {
_FFMPEG_ARGS+=(
-c:s copy
)
}
# map streams and some metadata
add_mappings() {
local i val
# map global metadata
_FFMPEG_ARGS+=( -map_metadata 0 )
# map video
_FFMPEG_ARGS+=( -map 0:v:0 )
# no default metadata mappings
_FFMPEG_ARGS+=( -map_metadata:s:v -1 )
_FFMPEG_ARGS+=( -map_metadata:s:a -1 )
_FFMPEG_ARGS+=( -map_metadata:s:s -1 )
# map audio and subtitles only keeping language and title metadata
# audio
i=0
for s in ${ASTREAMS}; do
_FFMPEG_ARGS+=( -map "${s}" )
# make first track the default
_FFMPEG_ARGS+=( "-disposition:a:${i}" "$([[ "${i}" -eq 0 ]] && echo -n default || echo -n 0)" )
for tag in language title; do
val="$(get_tag_for "${s}" "${tag}")"
[[ "${val}" != "null" ]] && _FFMPEG_ARGS+=( "-metadata:s:a:${i}" "${tag}=${val}" )
done
i=$(( ${i} + 1 ))
done
# subtitles
i=0
for s in ${SSTREAMS}; do
_FFMPEG_ARGS+=( -map "${s}" )
for tag in language title; do
val="$(get_tag_for "${s}" "${tag}")"
[[ "${val}" != "null" ]] && _FFMPEG_ARGS+=( "-metadata:s:s:${i}" "${tag}=${val}" )
done
i=$(( ${i} + 1 ))
done
}
# build ffmpeg args
declare -a _FFMPEG_ARGS
add_vflags
add_aflags
add_sflags
add_mappings
# form ffmpeg command for encode, PRETEND only echos command
$([[ -n "${PRETEND}" ]] && echo echo) \
ffmpeg -i "${infile}" "${_FFMPEG_ARGS[@]}" "${outfile}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment