I have added a few extras to the script, most notably a separate log for each transcode. Also, install mediainfo for a nice output at the end of the log.
###############################################################################################################################
#!/usr/bin/perl -w
# ============================================================================
# = NAME
# x264_transcode_576i.pl
#
# = PURPOSE
# Convert mpeg2 file from myth to h264 with aac audio.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_576i.pl -j %JOBID%
x264_transcode_576i.pl -f %FILE%
';
###############################################################################################################################
# Require and Use Clauses.
###############################################################################################################################
#
use strict;
use MythTV;
use XML::Simple;
###############################################################################################################################
# User defined quantities. Change these parameters to suit your needs
###############################################################################################################################
#
# Transcode speed, quality and size
my $howfast = "medium"; # Choices are none, ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
my $videoqualitynumber = "23"; # Sensible ranges are between 20 - 25, lower values are higher quality, bigger filesize
my $videosource = "576"; # Choices are 1080, 720, 576, Other
my $tuning = "none"; # x264 tune options are none, film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency
my $presetprofile = "none"; # x264 profiles are none, baseline, main, high, high10, high422, high444
my $audiochoice = "aac"; # Choices are copy, aac, mp3
my $subtitles = "none"; # Choices are none, english etc
###############################################################################################################################
# Prototype and Constant definitions
###############################################################################################################################
#
#Message logger so we can track what's been going on
sub logmsg(@);
# Define the Reconnect command here
sub Reconnect();
# What file are we copying/transcoding?
my $file = '';
my $jobid = -1;
# do nothing?
my $noexec = 0;
# Booleans
my $true = 1;
my $false = 0;
###############################################################################################################################
# Logging
###############################################################################################################################
# Set up a tmp log to show initial errors
# $LOG will be changed once $newfilename is created
my $failLOG = "/var/log/transcode/all_576i.log";
my $LOG = $failLOG;
my $logsize = 4096; #Maximum log size in kbytes, self pruning.
my $logdebug = $false; #Debugging default is off.
my $logverbose = "-v 1";
###############################################################################################################################
# Transcoding
###############################################################################################################################
#
# some globals
my ($chanid, $command, $query, $ref, $starttime, $showtitle, $episodetitle);
my ($seasonnumber, $episodenumber, $episodedetails);
my ($newfilename, $newstarttime);
my $xmlparser = new XML::Simple;
my $xmlstring;
my $mt = '';
my $db = '';
# globals for stream and resolution mapping
my ($videofilters,$framerate,$anamorphic,$displaysize,$x264speed,$x264tune,$x264profile,$deinterlace,$detelecine,$decomb,$denoise,$deblock);
my ($audiocodec,$audiobitrate,$audiochannels,$audiosamplerate,$audiodrc,$audiostream);
# transcode audio options
$audiostream = "-a 1";
if ( $audiochoice eq "copy" )
{
$audiocodec = "-E copy:mp3";
$audiobitrate = "";
$audiochannels = "";
$audiosamplerate = "";
$audiodrc = "";
}
elsif ( $audiochoice eq "aac" )
{
$audiocodec = "-E faac";
$audiobitrate = "-B 256";
$audiochannels = "-6 dpl2";
$audiosamplerate = "-R Auto";
$audiodrc = "-D 0.0";
}
elsif ( $audiochoice eq "mp3" )
{
$audiocodec = "-E lame";
$audiobitrate = "-B 128";
$audiochannels = "-6 dpl2";
$audiosamplerate = "-R Auto";
$audiodrc = "-D 0.0";
}
# filters
$deinterlace = "--deinterlace=\"slower\"";
$detelecine = "--detelecine";
#$decomb = "--decomb=\"bob\"";
$decomb = "--decomb";
$denoise = "--denoise";
# strict or loose anamorphic?
if ( $videosource eq "1080" )
{
$anamorphic = "--loose-anamorphic";
$displaysize = "720";
}
elsif ( $videosource eq "720" )
{
$anamorphic = "--strict-anamorphic";
$displaysize = "720";
}
else
{
$anamorphic = "--strict-anamorphic";
$displaysize = "576";
}
# transcode video options
$videofilters = "$denoise $decomb";
my $videoquality = "-q $videoqualitynumber";
my $videorate = "--vfr";
my $transcodesize="-Y $displaysize";
my $videocodec = "-e x264";
my $ftype = "-f mkv";
my $chapters = "-m";
# Subtitle options
my $subtitleoptions;
if ( $subtitles eq "none")
{ $subtitleoptions = ""}
else
{ $subtitleoptions = "-s 1" }
# Assemble video options and audio options
my $videooptions = "$videocodec $videoquality $videorate $ftype $videofilters $anamorphic $transcodesize $chapters $subtitleoptions";
my $audiooptions = "$audiostream $audiocodec $audiochannels $audiobitrate $audiosamplerate";
my $transcodeoptions = "$videooptions $audiooptions";
# --x264-preset
if ( $howfast eq "none" )
{ $x264speed = "" }
else
{ $x264speed = "--x264-preset $howfast" }
# --x264-tune
if ( $tuning eq "none" )
{ $x264tune = "" }
else
{ $x264tune= "--x264-tune $tuning" }
# --x264-profile
if ( $presetprofile eq "none" )
{ $x264profile = "" }
else
{ $x264profile= "--x264-profile $presetprofile" }
# Assemble x264options
#my $x264options = "$x264speed $x264tune $x264profile -x ref=9:subme=9:me=umh:merange=32:trellis=2:rc-lookahead=80";
my $x264options = "$x264speed $x264tune $x264profile -x ref=5:rc-lookahead=60";
###############################################################################################################################
# HandBrakeCLI Builtin commands
###############################################################################################################################
# Default x264 options
# -x ref=3:bframes=3:b-pyramid=normal:weightp=2:8x8dct=1:cabac=1:b-adapt=1:me=hex:subme=7:mixed-refs=1:weightb=1:trellis=1:rc-lookahead=40:mbtree=1:analyse=p8x8,b8x8,i8x8,i4x4:aq-mode=1:direct=spatial
#
# Normal Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4 -e x264 -q 20.0 -a 1 -E faac -B 160 -6 dpl2 -R Auto -D 0.0 -f mp4 --strict-anamorphic -m -x ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0
#
# High Profile
# HandBrakeCLI -i DVD -o ~/Movies/movie.mp4 -e x264 -q 20.0 -a 1,1 -E faac,copy:ac3 -B 160,160 -6 dpl2,auto -R Auto,Auto -D 0.0,0.0 -f mp4 --detelecine --decomb --loose-anamorphic -m -x b-adapt=2:rc-lookahead=50
# x264 presets converted to HandBrakeCLI commands
#my $ultrafast = "-x ref=1:bframes=0:cabac=0:8x8dct=0:weightp=0:me=dia:subq=0:rc-lookahead=0:mbtree=0:analyse=none:trellis=0:aq-mode=0:scenecut=0:no-deblock=1";
#my $superfast = "-x ref=1:weightp=1:me=dia:subq=1:rc-lookahead=0:mbtree=0:analyse=i4x4,i8x8:trellis=0";
#my $veryfast = "-x ref=1:weightp=1:subq=2:rc-lookahead=10:trellis=0";
#my $faster = "-x ref=2:mixed-refs=0:weightp=1:subq=4:rc-lookahead=20";
#my $fast = "-x ref=2:weightp=1:subq=6:rc-lookahead=30";
#my $medium = "-x ref=3:bframes=3:b-adapt=1:direct=spatial:me=hex:merange=16:subq=7:rc-lookahead=40:trellis=1";
#my $slow = "-x ref=5:b-adapt=2:direct=auto:me=umh:subq=8:rc-lookahead=50";
#my $slower = "-x ref=8:b-adapt=2:direct=auto:me=umh:subq=9:rc-lookahead=60:analyse=all:trellis=2";
#my $veryslow = "-x ref=16:bframes=8:b-adapt=2:direct=auto:me=umh:merange=24:subq=10:rc-lookahead=60:analyse=all:trellis=2";
#my $placebo = "-x ref=16:bframes=16:b-adapt=2:direct=auto:me=tesa:merange=24:subq=11:rc-lookahead=60:analyse=all:trellis=2:no-fast-pskip=1";
#my $film = ":psy-rd=1.0,0.15:deblock=-1,-1";
#my $x264options = "$ultrafast$film";
###############################################################################################################################
# HandBrakeCLI tested commands
###############################################################################################################################
#
# HandBrakeCLI -i ?.mpg -o ?.mkv -e x264 -q 20 -r 25 --cfr -f mkv -m -Y 576 --decomb=9:2:6:20:80:16:16:10:20:20:4:2:50:24:1:-1 --loose-anamorphic -a 1 -E copy:*
###############################################################################################################################
# Kill
###############################################################################################################################
#
my $debug = 1;
sub Die($)
{
print STDERR "@_\n";
exit -1;
}
###############################################################################################################################
# Parse command-line arguments, check there is something to do:
###############################################################################################################################
#
if ( ! @ARGV )
{ logmsg && Die "$usage" }
Reconnect;
while ( @ARGV && $ARGV[0] =~ m/^-/ )
{
my $arg = shift @ARGV;
if ( $arg eq '-d' || $arg eq '--debug' )
{ $debug = 1 }
elsif ( $arg eq '-n' || $arg eq '--noaction' )
{ $noexec = 1 }
elsif ( $arg eq '-j' || $arg eq '--jobid' )
{ $jobid = shift @ARGV }
elsif ( $arg eq '-f' || $arg eq '--file' )
{ $file = shift @ARGV }
else
{
unshift @ARGV, $arg;
last;
}
}
if ( ! $file && $jobid == -1 )
{
logmsg && Die "No file or job specified. $usage";
}
###############################################################################################################################
# If we were supplied a jobid, lookup chanid and starttime so that we can find the filename
###############################################################################################################################
#
if ( $jobid != -1 )
{
$query = $db->prepare("SELECT chanid, starttime " .
"FROM jobqueue WHERE id=$jobid;");
$query->execute || logmsg && Die "Unable to query jobqueue table";
$ref = $query->fetchrow_hashref;
$chanid = $ref->{'chanid'};
$starttime = $ref->{'starttime'};
$query->finish;
if ( ! $chanid || ! $starttime )
{ logmsg && Die "Cannot find details for job $jobid" }
$query = $db->prepare("SELECT basename FROM recorded " .
"WHERE chanid=$chanid AND starttime='$starttime';");
$query->execute || logmsg && Die "Unable to query recorded table";
($file) = $query->fetchrow_array;
$query->finish;
if ( ! $file )
{ logmsg && Die "Cannot find recording for chan $chanid, starttime $starttime" }
if ( $debug )
{
logmsg "Job $jobid refers to recording chanid=$chanid,",
" starttime=$starttime\n"
}
}
else
{
if ( $file =~ m/(\d+)_(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)/ )
{ $chanid = $1, $starttime = "$2-$3-$4 $5:$6:$7" }
else
{
logmsg "File $file has a strange name. Searching in recorded table\n";
$query = $db->prepare("SELECT chanid, starttime " .
"FROM recorded WHERE basename='$file';");
$query->execute || logmsg && Die "Unable to query recorded table";
($chanid,$starttime) = $query->fetchrow_array;
$query->finish;
if ( ! $chanid || ! $starttime )
{ logmsg && Die "Cannot find details for filename $file" }
}
}
###############################################################################################################################
# A commonly used SQL row selector:
###############################################################################################################################
my $whereChanAndStarttime = "WHERE chanid=$chanid AND starttime='$starttime'";
###############################################################################################################################
# Find the directory that contains the recordings, check the file exists
###############################################################################################################################
my $dir = undef;
my $dirs = $mt->{'video_dirs'};
foreach my $d ( @$dirs )
{
if ( ! -e $d )
{ logmsg && Die "Cannot find directory $dir that contains recordings" }
if ( -e "$d/$file" )
{
$dir = $d;
last
}
else
{ logmsg "$d/$file does not exist\n" }
}
if ( ! $dir )
{ logmsg && Die "Cannot find recording" }
$query = $db->prepare("SELECT title FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && Die "Unable to query recorded table";
$showtitle = $query->fetchrow_array;
$query->finish;
$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && Die "Unable to query recorded table";
$episodetitle = $query->fetchrow_array;
$query->finish;
if ( $episodetitle ne "" )
{
$seasonnumber = "";
$episodenumber = "";
$xmlstring = `/usr/share/mythtv/metadata/Television/ttvdb.py -N "$showtitle" "$episodetitle"`;
if ( $xmlstring ne "" ) {
$episodedetails =$xmlparser->XMLin($xmlstring);
$seasonnumber = $episodedetails->{item}->{season};
$episodenumber = $episodedetails->{item}->{episode};
}
}
my ($year,$month,$day,$hour,$mins,$secs) = split m/[- :]/, $starttime;
my $oldShortTime = sprintf "%04d%02d%02d",
$year, $month, $day;
my $iter = 0;
do {
if ( $episodetitle eq "" || $seasonnumber eq "" || $episodenumber eq "" )
{
$newfilename = sprintf "%s_%s.%s.%s", $showtitle, $day, $month, $year;
} else {
$newfilename = sprintf "%s_S%0sE%0s_%s", $showtitle, $seasonnumber, $episodenumber, $episodetitle;
}
$newfilename =~ s/\;/ AND /g;
$newfilename =~ s/\&/ AND /g;
$newfilename =~ s/\s+/ /g;
$newfilename =~ s/\s/_/g;
$newfilename =~ s/:/_/g;
$newfilename =~ s/__/_/g;
$newfilename =~ s/\(//g;
$newfilename =~ s/\)//g;
$newfilename =~ s/'//g;
$newfilename =~ s/\!//g;
$newfilename =~ s/\///g;
if ( $iter != "0" )
{ $newfilename = sprintf "%s_%d%s", $newfilename, $iter, ".mkv" } else { $newfilename = sprintf "%s%s", $newfilename, ".mkv" }
$iter ++;
$secs = $secs + $iter;
$newstarttime = sprintf "%04d-%02d-%02d %02d:%02d:%02d",
$year, $month, $day, $hour, $mins, $secs;
} while ( -e "$dir/$newfilename" );
$debug && logmsg "$dir/$newfilename seems unique\n";
###############################################################################################################################
# Logging
###############################################################################################################################
# extra console output?
$LOG = "/var/log/transcode/transcode_$newfilename.log";
###############################################################################################################################
# logmsg
# Little routine to write to the log file.
# Rotates around $logsize bytes.
###############################################################################################################################
#
sub logmsg(@)
{
my ($string)=@_;
my $time=scalar localtime;
my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks);
my (@lines,$line);
($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks)=stat("$LOG");
if (defined($size))
{
$size=$size/1024; #size in kbyte
if ($size >= $logsize)
{
unlink ("$LOG.old") if (-e("$LOG.old"));
rename ($LOG,"$LOG.old");
}
}
print "$time : $string\n" if ($logdebug==$true);
if (open (LOG,">>$LOG"))
{
if ($string =~ /\n/)
{
@lines=split(/\n/,$string);
foreach $line (@lines)
{
print LOG "$time : $line\n";
}
}
else
{
print LOG "$time : $string\n";
}
close (LOG);
}
else
{
print "Unable to open LOG $LOG : $!";
}
}
sub Reconnect()
{
$mt = new MythTV();
$db = $mt->{'dbh'};
}
###############################################################################################################################
# Build and execute command
###############################################################################################################################
#
$command = "nice /usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command $logverbose";
$command = "$command $x264options";
$command = "$command $transcodeoptions";
###############################################################################################################################
# Execute the transcode command, and log the HandBrakeCLI output
###############################################################################################################################
chdir $dir;
# Open STDERR so that HandBrakeCLI outputs to the log file
open ( STDERR, ">>$LOG" );
select( STDERR );
# First log message
logmsg "###############################################################################################################################\n";
logmsg "x264_transcode_1080i.pl has been initiated\n";
$debug && logmsg "Executing: $command\n";
logmsg "Options used are:\n";
logmsg "Preset is $howfast";
logmsg "Profile is $presetprofile";
logmsg "Tune is $tuning";
logmsg "Constant Rate Factor is $videoqualitynumber";
logmsg "Audio track is $audiochoice";
logmsg "Video filters are set to $videofilters";
# Bang!
system $command;
# Used this to debug
# my $touchcommand = "touch $newfilename";
# $debug && logmsg "Executing: $touchcommand\n";
# system $touchcommand;
# Close STDERR and re-open STDOUT
close( STDERR );
select( STDOUT );
if ( ! -e "$dir/$newfilename" )
{ logmsg && Die "Transcode failed\n" }
###############################################################################################################################
# Last, copy the existing recorded details with the new file name.
###############################################################################################################################
Reconnect;
$query = $db->prepare("SELECT * FROM recorded $whereChanAndStarttime;");
$query->execute || logmsg && Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;
$ref->{'starttime'} = $newstarttime;
$ref->{'basename'} = $newfilename;
if ( $debug && ! $noexec )
{
logmsg 'Old file size = ' . (-s "$dir/$file") . "\n";
logmsg 'New file size = ' . (-s "$dir/$newfilename") . "\n";
}
$ref->{'filesize'} = -s "$dir/$newfilename";
my $extra = 'Copy';
###############################################################################################################################
# The new recording file has no cutlist, so we don't insert that field
###############################################################################################################################
my @recKeys = grep(!/^cutlist$/, keys %$ref);
###############################################################################################################################
# Build up the SQL insert command:
###############################################################################################################################
$command = 'INSERT INTO recorded (' . join(',', @recKeys) . ') VALUES ("';
foreach my $key ( @recKeys )
{
if (defined $ref->{$key})
{ $command .= quotemeta($ref->{$key}) . '","' }
else
{ chop $command; $command .= 'NULL,"' }
}
chop $command; chop $command; # remove trailing comma quote
$command .= ');';
if ( $debug || $noexec )
{ print "# $command\n" }
if ( ! $noexec )
{ $db->do($command) || logmsg && Die "Couldn't create new recording's record, but transcoded file exists $newfilename\n" }
###############################################################################################################################
# Delete the old recording, keeping the new transcoded recording
###############################################################################################################################
if ( -e "$dir/$newfilename" )
{ $command = 'DELETE from recorded ' . join(',', $whereChanAndStarttime) . '; '; # Build up the SQL delete command:
$db->do($command); # remove the mysql entry for the recording
my $filepng = $file . join(',','.png'); # Create the file png filename
unlink($file); # remove the original file
unlink($filepng); # remove the file's png
logmsg "Transcoding $newfilename has completed\n";
logmsg "###############################################################################################################################\n";
}
###############################################################################################################################
# Finish the logfile off nicely
###############################################################################################################################
#
#if ( -e "$outputdir/$newfilename" )
$command = "/usr/bin/mediainfo $newfilename >> $LOG";
system $command;
###############################################################################################################################
# Tidy up
###############################################################################################################################
$db->disconnect;
1;