Skip to content
Permalink
Browse files

Reworked ffmpeg exporters to work with mythffmpeg

Although there is doubtless more cleanup to do as a result of this, nuvexport
now uses the ffmpeg build provided by mythtv itself as part of it builds.  This
is as of MythTV/mythtv@8c6de46
All of the ffmpeg exporters have been tested with this setup.

Additionally, sox is now used to downsample 6-channel audio to 2-channel before
encoding.  This should fix the reported problems of encoding from AC-3 audio.
I may refactor the dependency later so it only checks for sox if it's needed
for the particular job at hand.

Split the "MP4 (iPod)" into two exporters: MP4 and H.264, as it is confusing
to look for H.264 buried inside MP4.

Fixes #9199.  Fixes #9205.
  • Loading branch information
Beirdo committed Dec 13, 2010
1 parent 240785b commit c18508f20448077762d23ab9207c5eb643324dc0
Showing with 356 additions and 126 deletions.
  1. +1 −0 Makefile
  2. +59 −19 export/ffmpeg.pm
  3. +265 −0 export/ffmpeg/H264.pm
  4. +1 −0 export/ffmpeg/MP3.pm
  5. +16 −86 export/ffmpeg/MP4.pm
  6. +10 −7 export/ffmpeg/PSP.pm
  7. +4 −2 nuvexport
  8. +0 −12 nuvexportrc
@@ -17,6 +17,7 @@ EXPORT_MODULES = export/generic.pm \
export/ffmpeg/DVD.pm \
export/ffmpeg/PSP.pm \
export/ffmpeg/MP4.pm \
export/ffmpeg/H264.pm \
export/mencoder/XviD.pm \
export/mencoder/H264MP3.pm \
export/mencoder/H264AAC.pm
@@ -47,6 +47,9 @@ package export::ffmpeg;
# Make sure we have yuvdenoise
my $yuvdenoise = find_program('yuvdenoise')
or push @{$self->{'errors'}}, 'You need yuvdenoise (part of mjpegtools) to use this exporter.';
# Make sure we have sox
my $sox = find_program('sox')
or push @{$self->{'errors'}}, 'You need sox to use this exporter.';
# Check the yuvdenoise version
if (!defined $self->{'denoise_vmaj'}) {
$data = `cat /dev/null | yuvdenoise 2>&1`;
@@ -191,23 +194,28 @@ package export::ffmpeg;
$mythtranscode .= ' --honorcutlist' if ($self->{'use_cutlist'});
$mythtranscode .= ' --fifosync' if ($self->{'audioonly'} || $firstpass);

my $audiofifo = "/tmp/fifodir_$$/audout";
my $videofifo = "/tmp/fifodir_$$/vidout";
my $videotype = 'rawvideo -pix_fmt yuv420p';
my $crop_w;
my $crop_h;
my $pad_w;
my $pad_h;
my $scale_w;
my $scale_h;
my $height;
my $width;
my @filters;
my $sox;

# Standard encodes
if (!$self->{'audioonly'}) {
# Do noise reduction -- ffmpeg's -nr flag doesn't seem to do anything other than prevent denoise from working
if ($self->{'noise_reduction'}) {
$ffmpeg .= "$NICE ffmpeg -f rawvideo";
$ffmpeg .= "$NICE mythffmpeg -f rawvideo";
$ffmpeg .= ' -s ' . $episode->{'finfo'}{'width'} . 'x' . $episode->{'finfo'}{'height'};
$ffmpeg .= ' -r ' . $episode->{'finfo'}{'fps'};
$ffmpeg .= " -i /tmp/fifodir_$$/vidout -f yuv4mpegpipe -";
$ffmpeg .= " -i $videofifo -f yuv4mpegpipe -";
$ffmpeg .= ' 2> /dev/null | ';
$ffmpeg .= "$NICE yuvdenoise";
if ($self->{'denoise_vmaj'} < 1.6 || ($self->{'denoise_vmaj'} == 1.6 && $self->{'denoise_vmin'} < 3)) {
@@ -226,16 +234,33 @@ package export::ffmpeg;
}
}

# If we have 6-channel audio, and want 2-channel, we need to use sox as
# ffmpeg is retarded and won't/can't do it
if ((!$firstpass || $self->{'audioonly'}) &&
$episode->{'finfo'}{'audio_channels'} > 2) {
my $newaudiofifo = "/tmp/fifodir_$$/audout2";
$sox = "$NICE sox --single-threaded -t raw -e signed -2"
. " -c " . $episode->{'finfo'}{'audio_channels'}
. " -r " . $episode->{'finfo'}{'audio_sample_rate'}
. " $audiofifo -t raw -e signed -2 -c 2"
. " -r " . $episode->{'finfo'}{'audio_sample_rate'}
. " $newaudiofifo"
. " remix -m 1,4v0.5,2v0.7 3,5v0.5,2v0.7";
$audiofifo = $newaudiofifo;
}

# Start the ffmpeg command
$ffmpeg .= "$NICE ffmpeg";
$ffmpeg .= "$NICE mythffmpeg";
if ($num_cpus > 1) {
$ffmpeg .= ' -threads '.($num_cpus);
}
$ffmpeg .= ' -y -f '.($Config{'byteorder'} == 4321 ? 's16be' : 's16le')
.' -ar ' . $episode->{'finfo'}{'audio_sample_rate'}
.' -ac ' . $episode->{'finfo'}{'audio_channels'};
$ffmpeg .= ' -y';
if (!$firstpass) {
$ffmpeg .= " -i /tmp/fifodir_$$/audout";
$ffmpeg .=' -f '.($Config{'byteorder'} == 4321 ? 's16be' : 's16le')
.' -ar ' . $episode->{'finfo'}{'audio_sample_rate'}
# .' -ac ' . $episode->{'finfo'}{'audio_channels'};
.' -ac 2';
$ffmpeg .= " -i $audiofifo";
}
if (!$self->{'audioonly'}) {
$ffmpeg .= " -f $videotype"
@@ -246,6 +271,9 @@ package export::ffmpeg;

$self->{'out_aspect'} ||= $episode->{'finfo'}{'aspect_f'};

$ffmpeg .= ' -aspect ' . $self->{'out_aspect'}
.' -r ' . $self->{'out_fps'};

# The output is actually a stretched/anamorphic aspect ratio
# (like 480x480 for SVCD, which is 4:3)
if ($self->{'aspect_stretched'}) {
@@ -274,8 +302,8 @@ package export::ffmpeg;
}
}

$ffmpeg .= ' -aspect ' . $self->{'out_aspect'}
.' -r ' . $self->{'out_fps'};
$scale_w = $width - (2 * $pad_w);
$scale_h = $height - (2 * $pad_h);

# Deinterlace in ffmpeg only if the user wants to
if ($self->val('deinterlace') && !($self->val('noise_reduction') && $self->val('deint_in_yuvdenoise'))) {
@@ -293,23 +321,25 @@ package export::ffmpeg;
$b-- if ($b > 0 && $b % 2);
$l-- if ($l > 0 && $l % 2);
# crop
$ffmpeg .= " -croptop $t -cropright $r"
." -cropbottom $b -cropleft $l" if ($t || $r || $b || $l);
$crop_w = $episode->{'finfo'}{'width'} - $r - $l;
$crop_h = $episode->{'finfo'}{'height'} - $b - $t;
push @filters, "crop=$l:$t:$crop_w:$crop_h" if ($t || $r || $b || $l);
}

# Letter/Pillarboxing as appropriate
if ($pad_h) {
$ffmpeg .= " -padtop $pad_h -padbottom $pad_h";
}
if ($pad_w) {
$ffmpeg .= " -padleft $pad_w -padright $pad_w";
}
$ffmpeg .= " -s ${width}x$height";
push @filters, "scale=$scale_w:$scale_h";
push @filters, "pad=$width:$height:$pad_w:$pad_h:black" if ($pad_h | $pad_w);
push @filters, "pixelaspect=1";
push @filters, "slicify";

# Add in the filters
$ffmpeg .= " -vf " . join(",", @filters) if ($#filters != -1);
}

# Add any additional settings from the child module
$ffmpeg .= ' '.$self->{'ffmpeg_xtra'};


# Output directory set to null means the first pass of a multipass
if ($firstpass || !$self->{'path'} || $self->{'path'} =~ /^\/dev\/null\b/) {
$ffmpeg .= ' /dev/null';
@@ -319,7 +349,8 @@ package export::ffmpeg;
$ffmpeg .= ' '.shell_escape($self->get_outfile($episode, $suffix));
}
# ffmpeg pids
my ($mythtrans_pid, $ffmpeg_pid, $mythtrans_h, $ffmpeg_h);
my ($mythtrans_pid, $sox_pid, $ffmpeg_pid, $mythtrans_h, $sox_h,
$ffmpeg_h);

# Create a directory for mythtranscode's fifo's
mkdir("/tmp/fifodir_$$/", 0755) or die "Can't create /tmp/fifodir_$$/: $!\n\n";
@@ -328,6 +359,15 @@ package export::ffmpeg;
fifos_wait("/tmp/fifodir_$$/", $mythtrans_pid, $mythtrans_h);
push @tmpfiles, "/tmp/fifodir_$$", "/tmp/fifodir_$$/audout", "/tmp/fifodir_$$/vidout";

# Run sox if needed
if ((!$firstpass || $self->{'audioonly'}) &&
($episode->{'finfo'}{'audio_channels'} > 2)) {
POSIX::mkfifo( "/tmp/fifodir_$$/audout2", 0644 );
push @tmpfiles, "/tmp/fifodir_$$/audout2";
($sox_pid, $sox_h) = fork_command("$sox 2>&1");
$children{$sox_pid} = 'sox' if ($sox_pid);
}

# For multipass encodes, we don't need the audio on the first pass
if ($self->{'audioonly'}) {
my ($cat_pid, $cat_h) = fork_command("cat /tmp/fifodir_$$/vidout > /dev/null");

0 comments on commit c18508f

Please sign in to comment.
You can’t perform that action at this time.