Main Contents

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.

Filed under: Linux, MythTV |

Sorry, the comment form is closed at this time.