Transcode movies using MEncoder and x264
April 4, 2008
These are the steps I follow to rip a DVD and transcode it to a high-quality AVI file for TV playback. (Using MythTV, it’s just a single step and is easy enough for my kids to rip a movie using just the remote, but I’ll detail that in another post.) I start with a VOB file ripped using MythTV’s ‘Import DVD’ feature or sometimes a VOB or ISO from DVD Decrypter (copy /b or cat the VOB’s together if necessary). I grab a single audio track, usually AC-3 Dolby Digital, and a single subtitle track, always the first track (if there are additional English tracks, they are usually director’s commentary, etc.) It’s important to grab the subtitles because you don’t always know which movies use subtitles in certain scenes, for example, Star Wars uses the subtitle track for some alien dialogue, etc. With mencoder you can use the forcedsubsonly option which will only render the subtitles for scenes that need it. Transcode with this command:
mencoder MOVIE.vob -vf pullup,softskip -aid 128 -sid 0 -forcedsubsonly -ovc x264 -x264encopts threads=auto:bframes=3:weight_b:partitions=all:8x8dct:subq=6:bitrate=1100 -oac copy -ofps 24000/1001 -o MOVIE.avi
The above command works for most standard 24 fps progressive movies. When transcoding 30 fps or interlaced videos, usually straight-to-DVD, low-budget or made-for-TV movies, in order to avoid jumpy playback, you need to force 30 fps and deinterlace, like this:
mencoder MOVIE.vob -vf pp=lb,pullup,softskip -aid 128 -sid 0 -forcedsubsonly -ovc x264 -x264encopts threads=auto:bframes=3:weight_b:partitions=all:8x8dct:subq=6:bitrate=1100 -oac copy -ofps 30000/1001 -o MOVIE.avi
On my dual-processor Pentium 4 3.0 GHz with HyperThreading enabled I usually get between 20-30 fps encoding; 15-20 fps without HyperThreading. On my Intel Quadcore 2.4 GHz I get between 60-80 fps.
So I don’t have to type this in everytime, I created a simple script to save me some time. You can download it here or copy it below:
#!/bin/bash
#Author: James Dastrup
#Date: April 17, 2008
usage ()
{
echo "This script encodes a movie with x264."
echo
echo "-i [file] Input file. Required."
echo "-o [file] Output file. Optional. If not supplied, a file will"
echo " be created in the same location as the original"
echo " with an extension of .avi"
echo "-d Deinterlace. Optional. Default is auto-detect."
echo "-c [crop] Crop. Optional. [crop] should use this format:"
echo " 720:480:0:0 "
echo " or use 'auto' to autodetect. "
echo " Be careful with auto! Not perfect, always verify the results."
echo "-a [track] Audio Track. Optional Default is 128."
echo "-b [bitrate] Bitrate. Optional. Default is 1100."
echo "-p [1,2] Encoding passes. Optional. Default is 1."
echo "-f [x] Framerate. Optional. Default is auto-detect, but will"
echo " fall back to 30 fps if it is unable to detect."
echo " Can be any reasonable number."
echo " 24 will actually be 24000/1001 and"
echo " 30 will actually be 30000/1001."
echo "-t [#] Threads. Optional. Default is auto, which creates"
echo " about 1 thread per core. Keep it <= # of cores."
exit 1
}
cropDetect ()
{
echo "Please wait while doing cropdetect..."
sleep 1
mplayer -vf cropdetect -v -v -v -v -v -nosound -sb 500000000 -frames 20 $infile -quiet -vo null | grep 'crop=' > $0.tmp
exec < $0.tmp
while read line
do
cropdo=${line#*\-vf\ }
cropdo=${cropdo%\)\.}
done
unlink $0.tmp
echo
echo "Crop is ${cropdo/crop=/}"
sleep 1
}
fpsDetect ()
{
mplayer -ao null -vo null -identify $infile -ss 120 -endpos 10 -speed 4 2>/dev/null | grep -q -e 24000/1001fps -e FPS=23 -e FPS=24
if [ $? -eq 0 ]; then
frame=24000/1001
else
frame=30000/1001
deint="pp=lb"
fi
}
while getopts ":i:o:c:a:b:dp:f:t:" Option
do
case $Option in
i ) infile=$OPTARG;;
o ) outfile=$OPTARG;;
d ) deint="pp=lb";;
c ) crop=$OPTARG;;
a ) audio=$OPTARG;;
b ) bitrate=$OPTARG;;
p ) pass=$OPTARG;;
f ) frame=$OPTARG;;
t ) threads=$OPTARG;;
\? ) usage
exit 1;;
* ) usage
exit 1;;
esac
done
if [ $# -eq 0 ]; then
usage
fi
if [ "$infile" = "" ]; then
usage
fi
if [ "$crop" = "auto" ]; then
cropDetect
elif [ "$crop" = "" ]; then
crop=""
else
cropcheck=0
cropcheck=`expr index "$crop" \:`
if [ $cropcheck = 0 ]; then
usage
else
cropdo="crop=$crop"
fi
fi
if [ "$outfile" = "" ]; then
outfile=${infile/.vob/}.avi
outfile=${outfile//\ /\\\ }
else
outfile=${outfile//\ /\\\ }
fi
if [ "$audio" = "" ]; then
audio=128
fi
if [ "$bitrate" = "" ]; then
bitrate=1100
fi
if [ "$frame" = "" ]; then
fpsDetect
elif [ "$frame" = "24" ]; then
frame=24000/1001
elif [ "$frame" = "30" ]; then
frame=30000/1001
fi
if [ "$threads" = "" ]; then
threads="auto"
fi
if [ "$pass" = "1" ]; then
pass=1
elif [ "$pass" = "2" ]; then
pass=2
elif [ "$pass" = "" ]; then
pass=1
else
usage
fi
doThis="mencoder $infile "
if [ "$crop" != "" ]; then
doThis="$doThis-vf $cropdo,pullup,softskip"
if [ $deint ]; then
doThis="$doThis,$deint"
fi
else
if [ $deint ]; then
doThis="$doThis-vf $deint,pullup,softskip"
else
doThis="$doThis-vf pullup,softskip"
fi
fi
doThis="$doThis -aid $audio -sid 0 -forcedsubsonly -ovc x264 -x264encopts threads=$threads:bframes=3:weight_b"
if [ "$pass" = "2" ]; then
doThis2="$doThis:pass=2:partitions=all:8x8dct:qcomp=0.7:subq=6:b_pyramid:bitrate=${bitrate} -oac copy -ofps $frame -o $outfile"
doThis="$doThis:pass=1:partitions=none:subq=1:turbo=2:frameref=1:qcomp=0.7:bitrate=${bitrate} -nosound -ofps $frame -o /dev/null"
echo "The following commands will be executed:"
echo
echo $doThis
echo
echo $doThis2
echo
echo
echo "Press CTRL-C to cancel in the next 3 seconds..."
echo
sleep 3
nice -n 20 $doThis && nice -n 20 $doThis2
else
doThis="$doThis:partitions=all:8x8dct:subq=6:bitrate=${bitrate} -oac copy -ofps $frame -o $outfile"
echo "The following command will be executed:"
echo
echo $doThis
echo
echo
echo "Press CTRL-C to cancel in the next 3 seconds..."
echo
sleep 3
nice -n 20 $doThis
fi
Yeah, so I’m no expert in bash, but it works for me. Some notes on the commands above: I’ve found that an average bitrate of 1100 is sufficient in almost all cases, and overkill for some types of videos, like 2D animation, with which I can go down to 800. I can’t really tell a difference between 1 or 2 pass, so I save time and use 1. I always copy audio and never re-encode it - I don’t even know if it’s possible to transcode audio and keep it in 5.1 or 7.1 surround. This script assumes the audio is on track 128, which is almost always the primary English track. x264 should be using all your cores. If it’s not, get a different build or recompile it with threads support. This script also attempts to auto-detect 24 or 30 fps movies. I don’t normally crop - even if it works it often is more difficult to get the correct aspect during playback. I know the AVI container isn’t the most advanced, but until mencoder, which is the most advanced IMO, supports outputting directly to Matroska (MKV), I’ll be using AVI.