#!/usr/bin/perl -w
# ============================================================================
# = NAME
# x264_transcode_high.pl
#
# = PURPOSE
# Convert mpeg2 file from myth to h264 with aac audio.
#
# = USAGE
my $usage = 'Usage:
x264_transcode_high.pl -j %JOBID%
x264_transcode_high.pl -f %FILE%
';
# ============================================================================
use strict;
use MythTV;
use XML::Simple;
# What file are we copying/transcoding?
my $file = '';
my $jobid = -1;
# do nothing?
my $noexec = 0;
# extra console output?
my $DEBUG = 1;
# some globals
my ($chanid, $command, $query, $ref, $starttime, $showtitle, $episodetitle);
my ($seasonnumber, $episodenumber, $episodedetails);
my ($newfilename, $newstarttime);
my $xmlparser = new XML::Simple;
my $xmlstring;
# globals for stream and resolution mapping
my ($output, $videostream, $audiostreamsurround, $audiostreamstereo, $framerate);
# transcode options
my $videocodec = "x264";
my $videoquality = "22"; # target quality
my $videobitrate = "1500k"; # target bitrate - not used if using videoquality
my $audiostream = 1; # default audio channel
my $audiocodec = "copy:ac3";
my $audiobitrate = "256";
my $audiochannels = "auto";
my $audiosamplerate = "Auto";
my $audiodrc = "1.0";
my $ftype = "mkv";
my $detelecine = "--detelecine";
my $decomb = "--decomb";
my $looseanamorphic = "--loose-anamorphic";
my $chapters = "-m";
# x264 options
my $badapt = "b-adapt=2";
my $rclookahead = "rc-lookahead=50";
my $x264options = "$badapt:$rclookahead";
# ref=2:bframes=2:subme=6:mixed-refs=0:weightb=0:8x8dct=0:trellis=0
my $mt = '';
my $db = '';
sub Reconnect()
{
$mt = new MythTV();
$db = $mt->{'dbh'};
}
# ============================================================================
sub Die($)
{
print STDERR "@_\n";
exit -1;
}
# ============================================================================
# Parse command-line arguments, check there is something to do:
#
if ( ! @ARGV )
{ 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 )
{
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 || Die "Unable to query jobqueue table";
$ref = $query->fetchrow_hashref;
$chanid = $ref->{'chanid'};
$starttime = $ref->{'starttime'};
$query->finish;
if ( ! $chanid || ! $starttime )
{ Die "Cannot find details for job $jobid" }
$query = $db->prepare("SELECT basename FROM recorded " .
"WHERE chanid=$chanid AND starttime='$starttime';");
$query->execute || Die "Unable to query recorded table";
($file) = $query->fetchrow_array;
$query->finish;
if ( ! $file )
{ Die "Cannot find recording for chan $chanid, starttime $starttime" }
if ( $DEBUG )
{
print "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
{
print "File $file has a strange name. Searching in recorded table\n";
$query = $db->prepare("SELECT chanid, starttime " .
"FROM recorded WHERE basename='$file';");
$query->execute || Die "Unable to query recorded table";
($chanid,$starttime) = $query->fetchrow_array;
$query->finish;
if ( ! $chanid || ! $starttime )
{ 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 )
{ Die "Cannot find directory $dir that contains recordings" }
if ( -e "$d/$file" )
{
$dir = $d;
last
}
else
{ print "$d/$file does not exist\n" }
}
if ( ! $dir )
{ Die "Cannot find recording" }
# ============================================================================
# Do you want to implement the deinterlace feature?
#my $dowedeinterlace = " ";
# ============================================================================
# Use ffmpeg to find if we need to deinterlace
my $deinterlace = " ";
#if ( $dowedeinterlace == '1')
# {
# $command = "ffmpeg -i $dir/$file ";
# open(FF_info, "$command 2>&1 |");
# while ( defined(my $line = ) ) {
# chomp($line);
# if ( $line =~ /^\s*Stream.*#(\S\.\S).*:\sVideo.*\s(\S*)\stbr/ )
# {
# $framerate = $2;
# next;
# }
# }
# if ( $framerate <= 30.0 ) { $deinterlace = "--deinterlace" } elsif ( $framerate > 30 && $framerate <= 60 ) { $deinterlace = "" }
#}
# ============================================================================
# First, generate a new filename,
#
$query = $db->prepare("SELECT title FROM recorded $whereChanAndStarttime;");
$query->execute || Die "Unable to query recorded table";
$showtitle = $query->fetchrow_array;
$query->finish;
$query = $db->prepare("SELECT subtitle FROM recorded $whereChanAndStarttime;");
$query->execute || 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, $month, $day, $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 && print "$dir/$newfilename seems unique\n";
# ============================================================================
# Third, do the transcode
#
# $audiochannels = 6;
# $audiostream = $audiostreamsurround;
# if ( $audiostreamsurround eq "" )
# {
# $audiochannels = 2;
# $audiostream = $audiostreamstereo;
# }
$command = "/usr/bin/HandBrakeCLI -i $file";
$command = "$command -o $newfilename";
$command = "$command -e $videocodec";
$command = "$command -q $videoquality";
$command = "$command -a $audiostream";
$command = "$command -E $audiocodec";
$command = "$command -B $audiobitrate";
$command = "$command -6 $audiochannels";
$command = "$command -R $audiosamplerate";
$command = "$command -D $audiodrc";
$command = "$command -f $ftype";
$command = "$command $detelecine";
$command = "$command $decomb";
$command = "$command $looseanamorphic";
$command = "$command $chapters";
$command = "$command $deinterlace";
$command = "$command -x $x264options";
$DEBUG && print "Executing: $command\n";
chdir $dir;
system $command;
if ( ! -e "$dir/$newfilename" )
{ 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 || Die "Unable to query recorded table";
$ref = $query->fetchrow_hashref;
$query->finish;
$ref->{'starttime'} = $newstarttime;
$ref->{'basename'} = $newfilename;
if ( $DEBUG && ! $noexec )
{
print 'Old file size = ' . (-s "$dir/$file") . "\n";
print '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) || 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
}
# ============================================================================
$db->disconnect;
1;
Monday, March 12, 2012
High quality user job transcode script for MythTV using Handbrake
A high quality user job script for MythTV that transcodes a recording to .mkv format and gives the resulting filename a human readable name.
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment