Bash script that uses ffmpeg
and qtfaststart
to re-encode a directory of avi files to x264 mp4 container with streaming support (fast start).
#!/usr/bin/env bash
##
# encode.sh
#
# Bash script that uses `ffmpeg` and `qtfaststart` to re-encode a directory
# of avi files to x264 mp4 container with streaming support (fast start).
#
# Dependenices
#
# - ffmpeg (v0.7.15)
# - qtfaststart (https://github.com/danielgtaylor/qtfaststart)
#
# Usage
#
# $ ./encode.sh <INPUT_DIR> [OUTPUT_DIR]
#
# Jon LaBelle <jon@tech0.com>
# Tue Jul 30 2013 12:54:10 GMT-0500 (CDT)
##
###############################################
# CONFIG
###############################################
function show_usage
{
echo -e "\tUsage:\t$PROGNAME <INPUT_DIR> [OUTPUT_DIR]" 1>&2
}
function cleanup
{
for lf in "$CWD/"*pass.log*
do
if [ -f "$lf" ]; then
rm -rf "$lf"
fi
done
echo ""
echo "$SUCCESS_COUNT of $SOURCE_FILE_COUNT file(s) encoded."
exit $1
}
function error_exit
{
echo "ERROR: ${1:-"Unknown Error"}" 1>&2
cleanup 1
}
function get_duration
{
local infile="$1"
# ffmpeg duration output format: `00:00:00.000`
# note: using sed to strip frames (.000+) from output
$FFMPEG_CMD -i "$infile" 2>&1 | grep Duration | awk '{print $2}' | tr -d , | sed -e 's/\..*//';
}
function encode_file
{
local infile="$1"
local outfile="$2"
$FFMPEG_CMD -loglevel quiet -y \
-i "$infile" \
-vcodec libx264 -preset slow -crf 22 -profile baseline \
-x264opts level=3.0:vbv-maxrate=10000:vbv-bufsize=10000:ref=1 \
-b 700k -force_fps -acodec libfaac -ab 128k -ac 2 -pass 1 -threads 0 "$outfile" >/dev/null 2>&1;
# maintain source file's mod date
# touch -r "$infile" "$outfile"
}
function make_fast_start
{
local mp4file="$1"
# note: qtfaststart installed globally so ensure everyone can
# read file (chmod 0644)
$QTFASTSTART_CMD "$mp4file" && chmod 0644 "$mp4file"
}
################################################
# Main
################################################
DEBUG=0
PROGNAME=$(basename $0)
PROC_ID=$$
CWD=$(pwd)
INPUT_FILE_EXTENSION='avi'
OUTPUT_FILE_EXTENSION='mp4'
if [ $DEBUG -ne 0 ]; then
set -x
fi
# check dependencies
FFMPEG_CMD=`which ffmpeg 2>&1`
if [ $? -ne 0 ]; then
echo "ffmpeg not found. Please install and try again."
exit 1
fi
QTFASTSTART_CMD=`which qtfaststart 2>&1`
if [ $? -ne 0 ]; then
echo "qtfaststart not found. Please install and try again."
exit 1
fi
# input directory must be specfied and exist
if [ -z "$1" ]; then
show_usage
exit 1
fi
if [ ! -d "${1}" ]; then
echo "Input directory not found!"
show_usage
exit 1
fi
INPUTDIR="$1"
OUTPUTDIR="$INPUTDIR"
# try to set output dir if specified, with fallback as input dir
if [ ! -z "$2" ]; then
if [ -d "$2" ]; then
OUTPUTDIR="$2"
else
echo "Output directory not found."
echo "note: you can leave the output directory (arg2) option empty and encoded items created in input directory (ar1)."
exit 1
fi
fi
#
# store a count of files that require encoding
# (-maxdepth 1 = non-recursive search)
# note: using sed to remove whitespace in output
#
SOURCE_FILE_COUNT=`find "$INPUTDIR" -maxdepth 1 -type f -name *.$INPUT_FILE_EXTENSION | wc -l | sed -e 's/^[ \t]*//' 2>&1;`
if [[ $? -ne 0 || $SOURCE_FILE_COUNT -eq 0 ]]; then
echo "No $INPUT_FILE_EXTENSION files found in input directory."
exit 1
fi
ATTEMPT_COUNT=0
FAIL_COUNT=0
SUCCESS_COUNT=0
# handle kill signals (ctl+c)
trap cleanup SIGHUP SIGINT SIGTERM
echo""
echo "Encoding ${SOURCE_FILE_COUNT} $INPUT_FILE_EXTENSION file(s) to $OUTPUT_FILE_EXTENSION..."
for src_file in "$INPUTDIR/"*.$INPUT_FILE_EXTENSION; do
let 'ATTEMPT_COUNT++'
src_basename=$(basename "$src_file")
src_duration=$(get_duration "$src_file")
out_basename=$(echo "$src_basename" | sed -e s/."${INPUT_FILE_EXTENSION}"/."${OUTPUT_FILE_EXTENSION}"/g)
out_file="$OUTPUTDIR/$out_basename"
echo "-> (${ATTEMPT_COUNT}/${SOURCE_FILE_COUNT}) source: $src_basename, output: $out_basename"
encode_file "$src_file" "$out_file"
out_duration=$(get_duration "$out_file")
if [ "$src_duration" != "$out_duration" ]; then
let 'FAIL_COUNT++'
echo "${ATTEMPT_COUNT}/${SOURCE_FILE_COUNT} encoded, however the output file duration differs from the source."
echo -e "\t $src_basename (source file): '$src_duration'\n\t $out_basename (output file): '$out_duration'"
echo "This item will be marked as FAILED."
echo ""
else
let 'SUCCESS_COUNT++'
make_fast_start "$out_file"
fi
done
cleanup 0