MythTV Transcoding With Handbrake

2013-06-18 (Paul Saletan)

MythTV is great for capturing MPEG2 broadcast streams with a TV tuner card. However, its built-in software transcoder leaves a lot to be desired. The format (NuppelVideo, or .nuv) uses an older MPEG4 codec that’s inferior to the latest ones. Here’s how to produce better and smaller conversions in Ubuntu, by replacing it with Handbrake.


Start by adding the Handbrake launchpad PPA, then refresh your list of software sources. Install both the GUI (handbrake-gtk) and command line (handbrake-cli) binaries:

sudo add-apt-repository ppa:stebbins/handbrake-releases
sudo apt-get update
sudo apt-get install handbrake-gtk handbrake-cli

Run handbrake-gtk to get comfortable with Handbrake and select the best compromise of video/audio quality and storage space. Handbrake provides a number of presets for different resolutions. I prefer the Universal preset. It’s 720x404, lower resolution than HD’s 1280x720, but produces much smaller files – good enough for casual or mobile viewing. Also, most of my MythTV episodes have two audio tracks, an AAC and and an AC3. I only need one of them, so I discard the latter.


Before you can convert an original (MPEG2) video to H.264, you’ll probably want to remove commercials with MythTV’s tools. Use mythcommflag to generate a “cut list” from the segments that MythTV has already flagged as commercials. The first number is the channel ID. The second is the starting date and time of the episode. For example:

mythcommflag -c 1071 -s 20130612010000 --gencutlist

These values can be found in the filenames MythTV creates, which have the format CCCC_YYYYDDMMHHMMSS, where CCCC is the channel, and YYYYDDDMMHHSS is the starting date and time (down to the second). Of course, you can also obtain them from MythTV’s MySQL database, in the recorded table.

Next, run mythtranscode. It makes a lossless MPEG2 copy that filters out the commercial segments we flagged.

mythtranscode --chanid 1071 --starttime 20130612010000 --mpeg2 --honorcutlist -o no_commercials.mpg

Now have Handbrake convert the edited video:

/usr/bin/HandBrakeCLI -i "$OUTDIR/$TVFILENAME.mpg" -o "$OUTDIR/$TVTITLE-$1_$2.m4v" --audio 1 --aencoder copy:aac --audio-fallback faac --audio-copy-mask aac --preset="Universal"

The parameters in the above command tell Handbrake:

  • -i: the input filename of the MPEG2 file we just finished creating.
  • -o: the name of the MP4 file Handbrake will produce.
  • --audio: which audio track to include.
  • aencoder: which audio encoder to use; we’ll copy the AAC track, unmodified.
  • preset: a Handbrake preset to use for all other values (e..g, video resolution, quality, etc.)


I use steps like the above in a shell script, which automatically runs after MythTV flags commercials on a new recording:

# My MythTV user job to transcode a video to mp4

# this script expects 2 parameters to identify the recording:
#   4-digit channel ID
#   and 14-digit datetime code (YYYYMMDDDHHMMSS)

# path to MythTV transcoding tools
# a temporary working directory (must be writable by mythtv user)
# MySQL database login information (from mythconverg database)
# PID of this process. We'll create a working directory named with this ID
# intermediate MPEG2 file after cutting commercials will be XXXX__YYYYMMDDHHMMSS
# where XXXX is the channel ID and YYYYMMDDDHHMMSS is the datetime

# play nice with other processes
renice 19 $MYPID
ionice -c 3 -p $MYPID

# make working dir, go inside
mkdir $TEMPDIR/mythtmp-$MYPID
cd $TEMPDIR/mythtmp-$MYPID
# create and save an SQL query to get the specific title and subtitle of the show
echo "SELECT CONCAT(title,'-',programid,'-',subtitle) FROM recorded WHERE basename='$1_$2.mpg';" > get-tv-title_$MYPID.sql
# run the SQL query and capture the output
mysql --user=$DATABASEUSER --password=$DATABASEPASSWORD mythconverg < get-tv-title_$MYPID.sql > tv-title_$MYPID.txt
# ignore the first line (column heading);  parse only the second line (result)
TVTITLE=`sed -n '2,2p' tv-title_$MYPID.txt`
# clean up the title_subtitle  by deleting non-standard characters 
TVTITLE=`echo "$TVTITLE" | awk -F/ '{print $NF}' | sed 's/ /_/g' | sed 's/://g' | sed 's/?//g' | sed s/"'"/""/g | sed 's/,//g'`
# print the title_subtitle for logging purposes
echo $1 $2 $TVTITLE

# flag the commercials in the MythTV (.mpg) recording: build  a cutlist)
# Recent releases of MythTV use mythutil instead of mythcommflag to generate cutlists
# $INSTALLPREFIX/mythcommflag -c "$1" -s "$2" --gencutlist
$INSTALLPREFIX/mythutil --chanid "$1" --starttime "$2" --gencutlist
$INSTALLPREFIX/mythutil --chanid "$1" --starttime "$2" --getcutlist

# transcode #1: remove the commercials, using the cutlist (lossless transcode to MPEG2)
$INSTALLPREFIX/mythtranscode --chanid "$1" --starttime "$2" --mpeg2 --honorcutlist -o $OUTDIR/$TVFILENAME.mpg
# transcode #2: re-encode the MPEG2 video to MP4, using Handbrake command-line app
# the Universal preset produces 720x404 video with constant quality = 20
/usr/bin/HandBrakeCLI -i "$OUTDIR/$TVFILENAME.mpg" -o "$OUTDIR/$TVTITLE-$1_$2.m4v" --audio 1 --aencoder copy:aac --audio-fallback faac --audio-copy-mask aac --preset="Universal"
# check if the transcode exited with an error; if not, delete the intermediate MPEG file and map
if [ $? != 0 ]; then
  echo "Error occurred running Handbrake: input=$OUTDIR/$TVFILENAME output=$OUTDIR/$TVTITLE"
  rm "$OUTDIR/$"

Put the full path to your script in MythTV’s MySQL database, in the settings table as the value for one of the four user job records. I use the MythWeb package, which lets me make these edits through my web browser. Include the arguments for channel ID and starting time, using the MythTV formats for these variables explained in the user jobs documentation. Here’s my entry for UserJob1:

/home/paul/scripts/ default "%CHANID%" "%STARTTIMEUTC%"        # Use %STARTTIMEUTC% instead of %STARTTIME% in newer releases of MythTV

I also edited the corresponding UserJobDesc1 field with a label describing it (“Transcode to MP4”). If you want the script to run by default on all new recordings, set the value of AutoRunUserJob1 to 1 (and set AutoTranscode to zero, to prevent MythTV from running its own encoder).

UPDATE 2015/01/17: handbrake-cli no longer allows the --large-file parameter. The documentation says it’s not needed. So ignore my update comment from 2013/06/21, below.

UPDATE 2014/10/03: replaced %STARTTIME% with %STARTTIMEUTC% in UserJob1 command line, thanks to suggestion by Phil Maidlow.

UPDATE 2014/09/02: replaced mythcommflag with mythutil in script. Recent releases of MythTV use mythutil. Thanks to Jeffrey Palladino for pointing this out in the YouTube comments.

UPDATE 2013/06/21: handbrake-cli will abort when its output file reaches 4 gigabytes. To allow such huge files, add the parameter --large-file. See the handbrake-cli documentation linked below.