Skip to content

Commit

Permalink
Reworked ffmpeg exporters to work with mythffmpeg
Browse files Browse the repository at this point in the history
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 c18508f
Show file tree
Hide file tree
Showing 8 changed files with 356 additions and 126 deletions.
1 change: 1 addition & 0 deletions Makefile
Expand Up @@ -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
Expand Down
78 changes: 59 additions & 19 deletions export/ffmpeg.pm
Expand Up @@ -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`;
Expand Down Expand Up @@ -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)) {
Expand All @@ -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"
Expand All @@ -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'}) {
Expand Down Expand Up @@ -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'))) {
Expand All @@ -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';
Expand All @@ -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";
Expand All @@ -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");
Expand Down

0 comments on commit c18508f

Please sign in to comment.