I've often wanted to transcode recordings with x264 and have them remain as a recording. Recently I finally wrote a script which seems to work well.
It uses a bitrate of 1000, I don't know if this is optimal, but a 30 minute show once commercials a removed results in a file of ~170mb (for me about 1/5th the size of the original). My original plan had been to use matroska as the container, however the MythTV playback seemed much better using mp4. I find the script requires approximately 1.5x the original runtime for transcoding (X2 4050+ 1gb RAM).
A few things to note:
- I don't believe the script removes commercials based on commercial flagging, a cutlist must be created.
- Temporary files are stored in /myth/tmp/ if a cutlist is present you will need space for the original mpeg too
- I've attempted to incorporate error handling, but I don't know if I've been successful (it hasn't failed, yet)
- Since this is an early version, the script does not remove the old file, it is simply renamed to ${CHAN}_${START}.mpg.old (see above, I didn't want it to fail, and take a recording with it)
I'm curious to know if this works for others, or if anyone has suggestions.
Code:
#!/bin/sh
# Transcode a MythTV recording to
# Usage: transcode_mp4 %STARTTIME% %CHANID% "%DIR%/%FILE%" %JOBID%
# Constants?
DBUSERNAME="mythtv"
DBPASSWORD="mythtv"
DBHOST="localhost"
TEMP="/myth/tmp/"
TVDIR="/myth/tv/"
starttime="$(date +%s)"
# Variables to improve readability
# Don't change these!
CHAN=$1
START=$2
FILE=$3
JOBID=$4
ERROR=0
BASENAME="${CHAN}_${START}"
STATUSFILE="${TEMP}${BASENAME}_status.log"
sql_command()
# Arg 1 - command to execute
{
mysql -u $DBUSERNAME --password=$DBPASSWORD -h $DBHOST mythconverg -e "$1"
}
update_comment()
# Arg 1 -comment to set
{
sql_command "update jobqueue set comment=\"$1\" where id=\"$JOBID\";"
}
set_status()
# Arg 1 - status
{
sql_command "update jobqueue set status=\"$1\" where id=\"$JOBID\";"
}
# -- Slightly modified from myth2x264 --
check_background_progress()
# check mencoder progress in background
# Arg_1 = PROGRESS CALCULATION
{
while [ `tail -1 $STATUSFILE | grep -c "^x264 \[info\]: kb/s:"` = 0 ]
do
sleep 10
current_status=`tail -1 $STATUSFILE | grep "([ 0-9]\{1,\}%)"`
prog_percent=`echo "$current_status" | sed 's_.*(\([ 0-9][ 0-9]\)%).*_\1_'`
current_FPS=`echo "$current_status" | sed 's_.*\([ 0-9][ 0-9].[ 0-9][ 0-9]\)fps.*_\1_'`
if [ -n "$prog_percent" ]; then
prog_percent=`expr $prog_percent / $1`
update_comment "$prog_percent% Completed @ $current_FPS fps"
fi
sleep 10
done
}
cut_commercials()
{
update_comment "Removing commercials"
CUTLIST=`mythcommflag --getcutlist -c $CHAN -s $START | grep 'Cutlist:' | cut -d \ -f 2`
if [ -n "$CUTLIST" ]; then
/usr/bin/nice -n19 mythtranscode -c $CHAN -s $START --outfile "${TEMP}${BASENAME}.mpg" --mpeg2 --honorcutlist
else
ln -s "${FILE}" "${TEMP}${BASENAME}.mpg"
fi
}
transcode_video()
{
(/usr/bin/nice -n19 mencoder "${TEMP}${BASENAME}.mpg" -o /dev/null -vf softskip,harddup -oac mp3lame -lameopts abr:br=92 -ovc x264 -x264encopts pass=1:bitrate=1000:turbo=2:me=umh:me_range=16:nodct_decimate:nointerlaced:8x8dct:nofast_pskip:trellis=1:partitions=p8x8,b8x8,i8x8,i4x4:mixed_refs:bime:keyint=300:keyint_min=30:frameref=3:bframes=14:b_adapt:b_pyramid:weight_b:direct_pred=auto:subq=5:nobrdo:chroma_me:cabac:deblock:nossim:nopsnr:threads=auto -passlogfile "${TEMP}${BASENAME}.log" > $STATUSFILE 2>&1; ERROR=$?) &
check_background_progress "2"
cat /dev/null > $STATUSFILE
if [ $ERROR -eq 0 ]; then
(/usr/bin/nice -n19 mencoder "${TEMP}${BASENAME}.mpg" -o "${TEMP}${BASENAME}.mp4" -vf softskip,harddup -oac mp3lame -lameopts abr:br=92 -ovc x264 -x264encopts pass=2:bitrate=1000:me=umh:me_range=16:nodct_decimate:nointerlaced:8x8dct:nofast_pskip:trellis=1:partitions=p8x8,b8x8,i8x8,i4x4:mixed_refs:keyint=300:keyint_min=30:frameref=3:bframes=14:bime:b_adapt:b_pyramid:weight_b:direct_pred=auto:subq=5:nobrdo:chroma_me:cabac:deblock:nossim:nopsnr:threads=auto -passlogfile "${TEMP}${BASENAME}.log" > $STATUSFILE 2>&1; ERROR=$?) &
check_background_progress "2 + 50"
fi
}
switch_files()
{
FILESIZE="${TEMP}${BASENAME}.mp4"
mv "${TEMP}${BASENAME}.mp4" "${TVDIR}${BASENAME}.mp4"
sql_command "UPDATE recorded SET basename='${BASENAME}.mp4', filesize = '${FILESIZE}' WHERE chanid = '${CHAN}' AND starttime = '${START}';"
mv "${FILE}" "${FILE}.old"
/usr/bin/nice -n19 mythcommflag --clearcutlist -c $CHAN -s $START
/usr/bin/nice -n19 mythcommflag --rebuild -c $CHAN -s $START
}
cleanup()
{
update_comment "Cleanup"
rm "${TEMP}${BASENAME}.log"
rm "${TEMP}${BASENAME}.mpg"
rm "${TEMP}${BASENAME}.mpg.map"
rm "${STATUSFILE}"
}
#----- Main ------
cut_commercials
transcode_video
# stop timer
endtime="$(date +%s)"
seconds="$(expr $aftertime - $starttime)"
if [ $ERROR -eq 0 ]; then
hours=$((seconds / 3600))
seconds=$((seconds % 3600))
minutes=$((seconds / 60))
seconds=$((seconds % 60))
switch_files
set_status 272
update_comment "Encode Successful. Encoding Time: $hours hour(s) $minutes minute(s) $seconds second(s)"
else
set_status 304
update_comment "Encode Failed. Exit status: $ERROR"
fi
cleanup