[mythtv-users] Share your transcode settings!

Jeremy Smith mythtv at belynnandjeremy.xohost.com
Thu Jan 12 16:51:35 UTC 2012


I use Perl.
It's kicked off via a user job.
I use the recording group to determine the hardcoded transcode settings.
I transcode using ffmpeg.  
The resulting mpeg4 files work with mythtv, ASUS Transformer and WDTV.  
I check source file using https://metacpan.org/release/FFprobe
Issues:  No commercial editing.  ACC audio will sometimes become out of
sync.  No CC.  

#!/usr/bin/perl -w

# Config Perl
use strict;
use MythTV;
use File::Basename;
# Very handy! - https://metacpan.org/release/FFprobe
use FFprobe;

# This is usual Die function
sub Die($)
{
   print STDERR "@_\n";
   exit -1;
}

# Run a command
sub runcmd($) {
    open(CMD,"@_ |") || die "Failed: $!\n";
    while ( <CMD> )
    {
        print $_ . "\n";
    }
}

# Begin main program here.

# Need good config in config.xml.  
# If Perl binding not picking up config.xml, add line breaks after each end
tag.
my $mt = new MythTV();
my $db = $mt->{'dbh'};

# Check params and parse
if ( ! @ARGV )
{   Die "Missing --starttime and --chanid"  }

my ($chanid, $starttime) = 
    (-1,'');

while ( @ARGV && $ARGV[0] =~ m/^-/ )
{
    my $arg = shift;

    if ( $arg eq '-s' || $arg eq '--starttime' )
    {   $starttime = shift  }    
    elsif ( $arg eq '-c' || $arg eq '--chanid' )
    {   $chanid = shift  }
    else
    {
        unshift @ARGV, $arg;
        last;
    }
}

# We'll need there
my ($file, $storage, $recgroup);

# Open the recording
my $show = $mt->new_recording($chanid,$starttime);

# Print the info
my ($key, $role);
print "\ninfo:\n\n";
foreach $key (sort keys %{$show}) {
    next unless ($key && defined $show->{$key});
    next if ($key eq '_mythtv' || $key eq 'channel');
    print ' ' x (23 - length($key)) if (length($key) < 23);
    if ($key eq 'credits') {
        print "$key: ";
        my $i = 0;
        foreach $role (sort keys %{$show->{$key}}) {
            $i++;
            if ($i > 1) {
                print ';';
            }
            print ' ', ucfirst($role),
                ' (',
                join(', ', @{$show->{$key}{$role}}),
                ')';
        }
        print "\n";
    }
    else {
        print "$key: $show->{$key}\n";
    }
}

print "\nmythtv done\n\n";
if (-e $show->{'local_path'}) {
    print $show->{'local_path'}, " exists.\n";
}
else {
    Die $show->{'local_path'} . " not found.\n";    
}

# Probe the video info
my $probe = FFprobe->probe_file($show->{'local_path'});
print "\nprobe done\n\n";
print $probe->{'format'}->{'filename'}, "\n";
use Data::Dumper;  warn Dumper \$probe;
print "\n";

# Collect info for transcode
my $stream;
my ($width, $height, $aspect, $videostream, $videocodec) = 
  (0,0,'4:3',0,'');
my ($audiostream51, $audiostreamstereo, $found51, $foundstereo,
$audio51codec, $audiostereocodec) = 
  (0,0,0,0,'','');
my ($foundcc, $ccstream) = (0, 0);
my $lang = 'eng';
my $duration_bad = $probe->{'format'}->{'duration'};
my $duration = $show->{'endtime'} - $show->{'starttime'};
print "Duration is $duration seconds per MythTV, $duration_bad per
ffProbe\n\n";

foreach $stream (@{$probe->{'stream'}}) {
    print "Stream: ", $stream->{'index'}, "\n";
    $lang = $stream->{'TAG:language'};
    $lang = 'eng' unless defined $lang;
    if ( $stream->{'codec_type'} eq 'video' ) {
        if ($width < $stream->{'width'}) { 
            $width = $stream->{'width'};
            $height = $stream->{'height'};
            $aspect = $stream->{'display_aspect_ratio'};
            $aspect = '4:3' unless defined $aspect;
            $videostream = $stream->{'index'};
            $videocodec = $stream->{'codec_name'};
            print "  Found $aspect video, codec $videocodec,
$width"."x$height.\n";
        }
    } elsif ( $stream->{'codec_type'} eq 'audio' and ($lang eq 'eng' or
$lang eq 'und')) {
        if ( $stream->{'channels'} == 2 ) {
            $audiostreamstereo = $stream->{'index'};
            $foundstereo = 1;
            $audiostereocodec = $stream->{'codec_name'};
            print "  Found stereo audio, stream $audiostreamstereo, codec
$audiostereocodec.\n";
        } elsif ( $stream->{'channels'} == 6 ) {
            $audiostream51 = $stream->{'index'};
            $found51 = 1;
            $audio51codec = $stream->{'codec_name'};
            print "  Found 5.1 audio, stream $audiostream51, codec
$audio51codec.\n";
        } else {
            print "WARNING: Odd number of audio channels, index
$stream->{'index'}.\n";
        }
    } elsif ( $stream->{'codec_type'} eq 'data' ) {
        $foundcc = 1;
        $ccstream = $stream->{'index'};
        print "  Found closed caption.\n";
    }
}

print "\n";

# Determine new file name
my ($filename, $directories, $suffix) =
fileparse($show->{'local_path'},qr/\.[^.]*/);
print " name = ", $filename, " dir = ", $directories, " suff = ", $suffix,
"\n";
my $newfilename = $filename . ".mp4";
if (-e $directories . $newfilename) {
    for (0 .. 100) {
        $newfilename = $filename . $_ . ".mp4";
        unless (-e $directories . $newfilename) {
            last;
        }
    }
}
print "New file = $newfilename\n";

# Convert it
my $needtranscode = 0;
my $ffmpeg = "ffmpeg -er 1 -threads 2 -y -i $show->{'local_path'} ";

if ($suffix eq '.nuv') {
    $ffmpeg = $ffmpeg . " -vsync 1 -async 10";
} else {
#    $ffmpeg = $ffmpeg . " -vsync 0 ";
}
my $est_size = 0;

# Different converts for different recording groups
if ( $show->{'recgroup'} eq 'HD-5.1' or $show->{'recgroup'} eq 'Default') {
    if ( $aspect ne '16:9' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    if ( $width != 1280 or $height != 800 or $videocodec ne 'h264' or
$audiostereocodec ne 'aac' or $foundstereo = 0 ) { 
        $needtranscode = 1; 
    }
    # stream 0.0 is video
    $ffmpeg = $ffmpeg . " -b 5M -map 0.$videostream -vcodec mpeg4 -s
1280x720 -aspect $aspect ";
    $est_size = 5 * 1000 * 1000 * $duration / 8;
    if ($found51) {
        if ($foundstereo) {
            # Found eng 5.1 and stereo
            $ffmpeg = $ffmpeg . " -map 0.$audiostreamstereo -strict
experimental -acodec aac -ab 128k ";
            if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
            $ffmpeg = $ffmpeg . " $directories$newfilename ";
            $ffmpeg = $ffmpeg . " -map 0.$audiostream51 -acodec copy
-newaudio ";
        } else {
            # Found eng 5.1 only
            $ffmpeg = $ffmpeg . " -map 0.$audiostream51 -strict experimental
-acodec aac -ac 2 -ab 128k ";
            if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
            $ffmpeg = $ffmpeg . " $directories$newfilename ";
            $ffmpeg = $ffmpeg . " -map 0.$audiostream51 -acodec copy
-newaudio ";
        }
    } else {
        # No eng 5.1, stereo only
        $ffmpeg = $ffmpeg . " -map 0.$audiostreamstereo -strict experimental
-acodec aac -ab 128k ";
        if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
        $ffmpeg = $ffmpeg . " $directories$newfilename ";
    }
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'HD-Stereo' ) {
    if ( $aspect ne '16:9' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    if ( $width != 1280 or $height != 800 or $videocodec ne 'h264' or
$audiostereocodec ne 'aac' or $foundstereo = 0 ) { 
        $needtranscode = 1; 
    }
    $ffmpeg = $ffmpeg . " -b 5m -map 0.$videostream -vcodec mpeg4 -s
1280x720 -aspect $aspect ";
    $est_size = 5 * 1000 * 1000 * $duration / 8;
    if ($foundstereo) {
        $ffmpeg = $ffmpeg . " -map 0.$audiostreamstereo -strict experimental
-acodec aac -ab 128k ";
    } else {
        $ffmpeg = $ffmpeg . " -map 0.$audiostream51 -strict experimental
-acodec aac -ac 2 -ab 128k ";
    }
    if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
    $ffmpeg = $ffmpeg . " $directories$newfilename ";
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'SD-Stereo' ) {
    if ( $aspect ne '4:3' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    # 720 width does not work
    if ( $width != 640 or $height != 480 or $videocodec ne 'h264' or
$audiostereocodec ne 'aac' or $foundstereo = 0 ) { 
        $needtranscode = 1; 
    }
    $ffmpeg = $ffmpeg . " -b 2500k -vcodec mpeg4 -s 640x480 -aspect $aspect
";
    $est_size = 2500 * 1000 * $duration / 8;
    $ffmpeg = $ffmpeg . " -strict experimental -acodec aac -ac 2 -ab 128k ";
    if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
    $ffmpeg = $ffmpeg . " $directories$newfilename ";
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'Music' ) {
    if ( $aspect ne '4:3' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    # 720 width does not work
    if ( $width != 320 or $height != 240 or $videocodec ne 'h264' or
$audiostereocodec ne 'aac' or $foundstereo = 0 ) { 
        $needtranscode = 1; 
    }
    $ffmpeg = $ffmpeg . " -b 300k -vcodec mpeg4 -s 320x240 -aspect $aspect
";
    $est_size = 300 * 1000 * $duration / 8;
    $ffmpeg = $ffmpeg . " -strict experimental -acodec aac -ac 2 -ab 128k ";
    if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
    $ffmpeg = $ffmpeg . " $directories$newfilename ";
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'HD-Low' ) {
    if ( $aspect ne '16:9' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    $needtranscode = 1; 
    $ffmpeg = $ffmpeg . " -b 1700k -map 0.$videostream -vcodec mpeg4 -s
960x540 -aspect $aspect ";
    $est_size = 1700 * 1000 * $duration / 8;
    if ($foundstereo) {
        $ffmpeg = $ffmpeg . " -map 0.$audiostreamstereo -strict experimental
-acodec aac -ac 1 -ab 64k ";
    } else {
        $ffmpeg = $ffmpeg . " -map 0.$audiostream51 -strict experimental
-acodec aac -ac 1 -ab 64k ";
    }
#    if ($foundcc) { $ffmpeg = $ffmpeg . " -scodec copy "; }
    $ffmpeg = $ffmpeg . " $directories$newfilename ";
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'SD-Low' ) {
    if ( $aspect ne '4:3' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
    $needtranscode = 1; 
    $ffmpeg = $ffmpeg . " -b 1500k -vcodec mpeg4 -s 480x360 -aspect $aspect
";
    $est_size = 1500 * 1000 * $duration / 8;
    $ffmpeg = $ffmpeg . " -strict experimental -acodec aac -ac 1 -ab 64k ";
    $ffmpeg = $ffmpeg . " $directories$newfilename ";
    print $ffmpeg, "\n";
} elsif ( $show->{'recgroup'} eq 'HD-Very-Low' ) {
    if ( $aspect ne '16:9' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
} elsif ( $show->{'recgroup'} eq 'SD-Very-Low' ) {
    if ( $aspect ne '4:3' ) { Die "Recording Group $show->{'recgroup'} but
video $aspect."; }
}

print "New estimated size is $est_size bytes\n";

if ($needtranscode) {
    runcmd $ffmpeg; 
} else {
    Die "No transcoding needed";
}

# Check size
my $filesize = -s $directories . $newfilename;
my $percent = $filesize / $est_size * 100;
printf "New file is %.1f percent of estimate\n", $percent;
if ($percent < 95 ) { Die "Transcoding Failed\n"; } 

# Update the database
my ($query, $query_handle);
$query = "update recorded set transcoded=1, filesize=?, basename=? where
chanid=? and starttime=?";
$query_handle = $db->prepare($query);
$query_handle->execute($filesize,$newfilename,$chanid,$starttime)  || die
"Unable to update recorded table";

# Rename files
# rename $show->{'local_path'},$show->{'local_path'}.'.old';
rename $show->{'local_path'} . '.png',$directories . $newfilename . '.png';
rename $show->{'local_path'} . '.-1.100x56.png',$directories . $newfilename
. '.-1.100x56.png';
rename $show->{'local_path'} . '.-1.100x75.png',$directories . $newfilename
. '.-1.100x75.png';
rename $show->{'local_path'} . '.-1.160x90.png',$directories . $newfilename
. '.-1.160x90.png';
unlink $show->{'local_path'};

# Rebuild the seek index 
runcmd "mythcommflag --rebuild -s $starttime -c $chanid"; 

# And a pretty link
runcmd "/usr/share/doc/mythtv-backend/contrib/user_jobs/mythlink.pl --link
/var/lib/mythtv/pretty --chanid $chanid --starttime $starttime --format
\%Y\%m\%d-\%H\%i-\%T-\%S"


-----Original Message-----
From: mythtv-users-bounces at mythtv.org
[mailto:mythtv-users-bounces at mythtv.org] On Behalf Of Josh Rosenberg
Sent: Wednesday, January 11, 2012 11:26 AM
To: mythtv-users at mythtv.org
Subject: [mythtv-users] Share your transcode settings!

All,

I'm still struggling a lot with setting up transcoding.  But rather
than ask for specific help, I'm going to try a different approach.

Those of you who do any sort of lossy or format-converting transcoding
beyond just stripping commercials, share how you do it!  Built-in
transcoding, a user job, or what?  If you use a script someone posted
online, have you modified it?  If you use a standard utility like
ffmpeg, or a script with multiple modes, what options do you call it
with?  Also, how long does the conversion take and how well does it
work for you (that is, how much space does it save and how is the
quality impacted)?

Bonus points for explaining why you chose what you chose, or what
alternatives you've discarded along the way.

Google searching for transcode help seems to mostly reveal people who
are struggling, and I'd like to hear some examples from people who are
succeeding!  Regardless of whether your use case exactly matches mine
or not.

Thanks!

Josh
_______________________________________________
mythtv-users mailing list
mythtv-users at mythtv.org
http://www.mythtv.org/mailman/listinfo/mythtv-users




More information about the mythtv-users mailing list