From c18508f20448077762d23ab9207c5eb643324dc0 Mon Sep 17 00:00:00 2001 From: Gavin Hurlbut Date: Mon, 13 Dec 2010 03:59:04 -0800 Subject: [PATCH] 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@8c6de4675d6d3ba234999611d35901953074b1d2 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. --- Makefile | 1 + export/ffmpeg.pm | 78 ++++++++++--- export/ffmpeg/H264.pm | 265 ++++++++++++++++++++++++++++++++++++++++++ export/ffmpeg/MP3.pm | 1 + export/ffmpeg/MP4.pm | 102 +++------------- export/ffmpeg/PSP.pm | 17 +-- nuvexport | 6 +- nuvexportrc | 12 -- 8 files changed, 356 insertions(+), 126 deletions(-) create mode 100644 export/ffmpeg/H264.pm diff --git a/Makefile b/Makefile index 2283b64..5b9470f 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/export/ffmpeg.pm b/export/ffmpeg.pm index fd82a5c..5f8ac54 100644 --- a/export/ffmpeg.pm +++ b/export/ffmpeg.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"); diff --git a/export/ffmpeg/H264.pm b/export/ffmpeg/H264.pm new file mode 100644 index 0000000..d436c5e --- /dev/null +++ b/export/ffmpeg/H264.pm @@ -0,0 +1,265 @@ +# vim:ts=4:sw=4:ai:et:si:sts=4 +# +# ffmpeg-based H.264 (iPod) video module for nuvexport. +# +# Many thanks to cartman in #ffmpeg, and for the instructions at +# http://rob.opendot.cl/index.php?active=3&subactive=1 +# http://videotranscoding.wikispaces.com/EncodeForIPodorPSP +# + +package export::ffmpeg::H264; + use base 'export::ffmpeg'; + +# Load the myth and nuv utilities, and make sure we're connected to the database + use nuv_export::shared_utils; + use nuv_export::cli; + use nuv_export::ui; + use mythtv::recordings; + +# Load the following extra parameters from the commandline + add_arg('quantisation|q=i', 'Quantisation'); + add_arg('a_bitrate|a=i', 'Audio bitrate'); + add_arg('v_bitrate|v=i', 'Video bitrate'); + add_arg('multipass!', 'Enable two-pass encoding.'); + add_arg('mp4_fps=s', 'Framerate to use: auto, 25, 23.97, 29.97.'); + add_arg('ipod!', 'Produce ipod-compatible output.'); + + sub new { + my $class = shift; + my $self = { + 'cli' => qr/\b(?:h264|ipodh264)\b/i, + 'name' => 'Export to H.264 (including iPod)', + 'enabled' => 1, + 'errors' => [], + 'defaults' => {}, + }; + bless($self, $class); + + # Initialize the default parameters + $self->load_defaults(); + + # Verify any commandline or config file options + die "Audio bitrate must be > 0\n" unless (!defined $self->val('a_bitrate') || $self->{'a_bitrate'} > 0); + die "Video bitrate must be > 0\n" unless (!defined $self->val('v_bitrate') || $self->{'v_bitrate'} > 0); + die "Width must be > 0\n" unless (!defined $self->val('width') || $self->{'width'} =~ /^\s*\D/ || $self->{'width'} > 0); + die "Height must be > 0\n" unless (!defined $self->val('height') || $self->{'height'} =~ /^\s*\D/ || $self->{'height'} > 0); + + # VBR, multipass, etc. + if ($self->val('multipass')) { + $self->{'vbr'} = 1; + } + elsif ($self->val('quantisation')) { + die "Quantisation must be a number between 1 and 31 (lower means better quality).\n" if ($self->{'quantisation'} < 1 || $self->{'quantisation'} > 31); + $self->{'vbr'} = 1; + } + + # Initialize and check for ffmpeg + $self->init_ffmpeg(); + + # Can we even encode mp4? + if (!$self->can_encode_format('mp4')) { + push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to mp4 file formats."; + } + if (!$self->can_encode('aac') && !$self->can_encode('libfaac')) { + push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to aac audio."; + } + if (!$self->can_encode('libx264')) { + push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to h264 video."; + } + # Any errors? disable this function + $self->{'enabled'} = 0 if ($self->{'errors'} && @{$self->{'errors'}} > 0); + # Return + return $self; + } + +# Load default settings + sub load_defaults { + my $self = shift; + # Load the parent module's settings + $self->SUPER::load_defaults(); + # Default settings + $self->{'defaults'}{'v_bitrate'} = 384; + $self->{'defaults'}{'a_bitrate'} = 64; + $self->{'defaults'}{'width'} = 320; + } + +# Gather settings from the user + sub gather_settings { + my $self = shift; + # Load the parent module's settings + $self->SUPER::gather_settings(); + # Audio Bitrate + $self->{'a_bitrate'} = query_text('Audio bitrate?', + 'int', + $self->val('a_bitrate')); + # Video options + if (!$is_cli) { + # iPod compatibility mode? + $self->{'ipod'} = query_text('Enable iPod compatibility?', + 'yesno', + $self->val('ipod')); + # Video bitrate options + $self->{'vbr'} = query_text('Variable bitrate video?', + 'yesno', + $self->val('vbr')); + if ($self->{'vbr'}) { + $self->{'multipass'} = query_text('Multi-pass (slower, but better quality)?', + 'yesno', + $self->val('multipass')); + if (!$self->{'multipass'}) { + while (1) { + my $quantisation = query_text('VBR quality/quantisation (1-31)?', + 'float', + $self->val('quantisation')); + if ($quantisation < 1) { + print "Too low; please choose a number between 1 and 31.\n"; + } + elsif ($quantisation > 31) { + print "Too high; please choose a number between 1 and 31\n"; + } + else { + $self->{'quantisation'} = $quantisation; + last; + } + } + } + } else { + $self->{'multipass'} = 0; + } + # Ask the user what video bitrate he/she wants + $self->{'v_bitrate'} = query_text('Video bitrate?', + 'int', + $self->val('v_bitrate')); + } + # Loop, in case we need to verify ipod compatibility + while (1) { + # Query the resolution + $self->query_resolution(); + # Warn about ipod resolution + if ($self->val('ipod') && ($self->{'height'} > 480 || $self->{'width'} > 640)) { + my $note = "WARNING: Video larger than 640x480 will not play on an iPod.\n"; + die $note if ($is_cli); + print $note; + next; + } + # Done looping + last; + } + } + + sub export { + my $self = shift; + my $episode = shift; + # Make sure this is set to anamorphic mode + $self->{'aspect_stretched'} = 1; + # Framerate + my $standard = ($episode->{'finfo'}{'fps'} =~ /^2(?:5|4\.9)/) ? 'PAL' : 'NTSC'; + + if (!defined $self->val('mp4_fps')) { + $self->{'mp4_fps'} = "auto"; + } + + if ($standard eq 'PAL') { + $self->{'out_fps'} = 25; + } + elsif ($self->val('mp4_fps') =~ /^23/) { + $self->{'out_fps'} = 23.97; + } + elsif ($self->val('mp4_fps') =~ /^29/) { + $self->{'out_fps'} = 29.97; + } + else { + $self->{'out_fps'} = ($self->{'width'} > 320 || $self->{'height'} > 288) ? 29.97 : 23.97; + } + # Embed the title + $safe_title = $episode->{'title'}; + if ($episode->{'subtitle'} ne 'Untitled') { + $safe_title .= ' - '.$episode->{'subtitle'}; + } + my $safe_title = shell_escape($safe_title); + # Codec name changes between ffmpeg versions + my $codec = 'libx264'; + + # Build the common ffmpeg string + my $ffmpeg_xtra = + ' -vcodec '.$codec + .$self->param('bit_rate', $self->{'v_bitrate'}) + ; + # Options required for the codecs separately + $ffmpeg_xtra .= ' -level 30' + .' -flags loop+slice' + .' -g 250 -keyint_min 25' + .' -sc_threshold 40' + .' -rc_eq \'blurCplx^(1-qComp)\'' + .$self->param('bit_rate_tolerance', $self->{'v_bitrate'}) + .$self->param('rc_max_rate', 1500 - $self->{'a_bitrate'}) + .$self->param('rc_buffer_size', 2000) + .$self->param('i_quant_factor', 0.71428572) + .$self->param('b_quant_factor', 0.76923078) + .$self->param('max_b_frames', 0) + .' -me_method umh' + ; + # Some shared options + if ($self->{'multipass'} || $self->{'vbr'}) { + $ffmpeg_xtra .= $self->param('qcompress', 0.6) + .$self->param('qmax', 51) + .$self->param('max_qdiff', 4) + ; + } + # Dual pass? + if ($self->{'multipass'}) { + # Apparently, the -passlogfile option doesn't work for h264, so we need + # to be aware of other processes that might be working in this directory + if (-e 'x264_2pass.log.temp' || -e 'x264_2pass.log') { + die "ffmpeg does not allow us to specify the name of the multi-pass log\n" + ."file, and x264_2pass.log exists in this directory already. Please\n" + ."wait for the other process to finish, or remove the stale file.\n"; + } + # Add all possible temporary files to the list + push @tmpfiles, 'x264_2pass.log', + 'x264_2pass.log.temp', + 'x264_2pass.log.mbtree'; + # Build the ffmpeg string + print "First pass...\n"; + $self->{'ffmpeg_xtra'} = ' -pass 1' + .$ffmpeg_xtra + .' -f mp4' + .' -refs 1 -subq 1' + .' -trellis 0' + ; + $self->SUPER::export($episode, '', 1); + # Second Pass + print "Final pass...\n"; + $ffmpeg_xtra = ' -pass 2 ' + .$ffmpeg_xtra; + } + # Single Pass + else { + if ($self->{'vbr'}) { + $ffmpeg_xtra .= ' -qmin '.$self->{'quantisation'}; + } + } + # Single/final pass options + $ffmpeg_xtra .= ' -refs '.($self->val('ipod') ? 2 : 7) + .' -subq 7' + .' -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8' + .' -flags2 +bpyramid+wpred+mixed_refs+dct8x8' + .' -me_range 21' + .' -trellis 2' + .' -cmp 1' + # These should match the defaults: + .' -deblockalpha 0 -deblockbeta 0' + ; + # Audio codec name changes between ffmpeg versions + my $acodec = $self->can_encode('libfaac') ? 'libfaac' : 'aac'; + # Don't forget the audio, etc. + $self->{'ffmpeg_xtra'} = $ffmpeg_xtra + ." -acodec $acodec -ar 48000 -async 1" + ." -strict experimental" if ($acodec eq "aac") + .$self->param('ab', $self->{'a_bitrate'}); + # Execute the (final pass) encode + $self->SUPER::export($episode, '.mp4'); + } + +1; #return true + diff --git a/export/ffmpeg/MP3.pm b/export/ffmpeg/MP3.pm index 716ff13..d1db14a 100644 --- a/export/ffmpeg/MP3.pm +++ b/export/ffmpeg/MP3.pm @@ -79,6 +79,7 @@ package export::ffmpeg::MP3; my $episode = shift; # Build the ffmpeg string $self->{'ffmpeg_xtra'} = $self->param('ab', $self->val('bitrate')) + .$self->param('ac', 2) .' -acodec ' .($self->can_encode('libmp3lame') ? 'libmp3lame' : 'mp3') .' -f mp3'; diff --git a/export/ffmpeg/MP4.pm b/export/ffmpeg/MP4.pm index 5ea79e4..7cd36b5 100644 --- a/export/ffmpeg/MP4.pm +++ b/export/ffmpeg/MP4.pm @@ -6,12 +6,6 @@ # http://rob.opendot.cl/index.php?active=3&subactive=1 # http://videotranscoding.wikispaces.com/EncodeForIPodorPSP # -# @url $URL$ -# @date $Date$ -# @version $Revision$ -# @author $Author$ -# @copyright Silicon Mechanics -# package export::ffmpeg::MP4; use base 'export::ffmpeg'; @@ -27,7 +21,6 @@ package export::ffmpeg::MP4; add_arg('a_bitrate|a=i', 'Audio bitrate'); add_arg('v_bitrate|v=i', 'Video bitrate'); add_arg('multipass!', 'Enable two-pass encoding.'); - add_arg('mp4_codec=s', 'Video codec to use for MP4/iPod video (mpeg4 or h264).'); add_arg('mp4_fps=s', 'Framerate to use: auto, 25, 23.97, 29.97.'); add_arg('ipod!', 'Produce ipod-compatible output.'); @@ -70,8 +63,8 @@ package export::ffmpeg::MP4; if (!$self->can_encode('aac') && !$self->can_encode('libfaac')) { push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to aac audio."; } - if (!$self->can_encode('mpeg4') && !$self->can_encode('h264') && !$self->can_encode('libx264')) { - push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to either mpeg4 or h264 video."; + if (!$self->can_encode('mpeg4')) { + push @{$self->{'errors'}}, "Your ffmpeg installation doesn't support encoding to mpeg4 video."; } # Any errors? disable this function $self->{'enabled'} = 0 if ($self->{'errors'} && @{$self->{'errors'}} > 0); @@ -88,13 +81,7 @@ package export::ffmpeg::MP4; $self->{'defaults'}{'v_bitrate'} = 384; $self->{'defaults'}{'a_bitrate'} = 64; $self->{'defaults'}{'width'} = 320; - $self->{'defaults'}{'mp4_codec'} = 'mpeg4'; # Verify commandline options - if ($self->val('mp4_codec') !~ /^(?:mpeg4|h264)$/i) { - die "mp4_codec must be either mpeg4 or h264.\n"; - } - $self->{'mp4_codec'} =~ tr/A-Z/a-z/; - } # Gather settings from the user @@ -112,21 +99,6 @@ package export::ffmpeg::MP4; $self->{'ipod'} = query_text('Enable iPod compatibility?', 'yesno', $self->val('ipod')); - # Video codec - while (1) { - my $codec = query_text('Video codec (mpeg4 or h264)?', - 'string', - $self->{'mp4_codec'}); - if ($codec =~ /^m/) { - $self->{'mp4_codec'} = 'mpeg4'; - last; - } - elsif ($codec =~ /^h/) { - $self->{'mp4_codec'} = 'h264'; - last; - } - print "Please choose either mpeg4 or h264\n"; - } # Video bitrate options $self->{'vbr'} = query_text('Variable bitrate video?', 'yesno', @@ -183,6 +155,11 @@ package export::ffmpeg::MP4; $self->{'aspect_stretched'} = 1; # Framerate my $standard = ($episode->{'finfo'}{'fps'} =~ /^2(?:5|4\.9)/) ? 'PAL' : 'NTSC'; + + if (!defined $self->val('mp4_fps')) { + $self->{'mp4_fps'} = "auto"; + } + if ($standard eq 'PAL') { $self->{'out_fps'} = 25; } @@ -201,39 +178,19 @@ package export::ffmpeg::MP4; $safe_title .= ' - '.$episode->{'subtitle'}; } my $safe_title = shell_escape($safe_title); - # Codec name changes between ffmpeg versions - my $codec = $self->{'mp4_codec'}; - if ($codec eq 'h264' && $self->can_encode('libx264')) { - $codec = 'libx264'; - } + my $codec = 'mpeg4'; + # Build the common ffmpeg string my $ffmpeg_xtra = ' -vcodec '.$codec .$self->param('bit_rate', $self->{'v_bitrate'}) ; # Options required for the codecs separately - if ($self->{'mp4_codec'} eq 'h264') { - $ffmpeg_xtra .= ' -level 30' - .' -flags loop+slice' - .' -g 250 -keyint_min 25' - .' -sc_threshold 40' - .' -rc_eq \'blurCplx^(1-qComp)\'' - .$self->param('bit_rate_tolerance', $self->{'v_bitrate'}) - .$self->param('rc_max_rate', 1500 - $self->{'a_bitrate'}) - .$self->param('rc_buffer_size', 2000) - .$self->param('i_quant_factor', 0.71428572) - .$self->param('b_quant_factor', 0.76923078) - .$self->param('max_b_frames', 0) - .' -me_method umh' - ; - } - else { - $ffmpeg_xtra .= ' -flags +mv4+loop+aic' - .' -trellis 1' - .' -mbd 1' - .' -cmp 2 -subcmp 2' - ; - } + $ffmpeg_xtra .= ' -flags +mv4+loop+aic' + .' -trellis 1' + .' -mbd 1' + .' -cmp 2 -subcmp 2' + ; # Some shared options if ($self->{'multipass'} || $self->{'vbr'}) { $ffmpeg_xtra .= $self->param('qcompress', 0.6) @@ -243,28 +200,13 @@ package export::ffmpeg::MP4; } # Dual pass? if ($self->{'multipass'}) { - # Apparently, the -passlogfile option doesn't work for h264, so we need - # to be aware of other processes that might be working in this directory - if ($self->{'mp4_codec'} eq 'h264' && (-e 'x264_2pass.log.temp' || -e 'x264_2pass.log')) { - die "ffmpeg does not allow us to specify the name of the multi-pass log\n" - ."file, and x264_2pass.log exists in this directory already. Please\n" - ."wait for the other process to finish, or remove the stale file.\n"; - } # Add all possible temporary files to the list - push @tmpfiles, 'x264_2pass.log', - 'x264_2pass.log.temp', - 'x264_2pass.log.mbtree', - 'ffmpeg2pass-0.log'; + push @tmpfiles, 'ffmpeg2pass-0.log'; # Build the ffmpeg string print "First pass...\n"; $self->{'ffmpeg_xtra'} = ' -pass 1' .$ffmpeg_xtra .' -f mp4'; - if ($self->{'mp4_codec'} eq 'h264') { - $self->{'ffmpeg_xtra'} .= ' -refs 1 -subq 1' - .' -trellis 0' - ; - } $self->SUPER::export($episode, '', 1); # Second Pass print "Final pass...\n"; @@ -277,24 +219,12 @@ package export::ffmpeg::MP4; $ffmpeg_xtra .= ' -qmin '.$self->{'quantisation'}; } } - # Single/final pass options - if ($self->{'mp4_codec'} eq 'h264') { - $ffmpeg_xtra .= ' -refs '.($self->val('ipod') ? 2 : 7) - .' -subq 7' - .' -partitions parti4x4+parti8x8+partp4x4+partp8x8+partb8x8' - .' -flags2 +bpyramid+wpred+mixed_refs+8x8dct' - .' -me_range 21' - .' -trellis 2' - .' -cmp 1' - # These should match the defaults: - .' -deblockalpha 0 -deblockbeta 0' - ; - } # Audio codec name changes between ffmpeg versions my $acodec = $self->can_encode('libfaac') ? 'libfaac' : 'aac'; # Don't forget the audio, etc. $self->{'ffmpeg_xtra'} = $ffmpeg_xtra ." -acodec $acodec -ar 48000 -async 1" + ." -strict experimental" if ($acodec eq "aac") .$self->param('ab', $self->{'a_bitrate'}); # Execute the (final pass) encode $self->SUPER::export($episode, '.mp4'); diff --git a/export/ffmpeg/PSP.pm b/export/ffmpeg/PSP.pm index d7c146b..4a172ad 100755 --- a/export/ffmpeg/PSP.pm +++ b/export/ffmpeg/PSP.pm @@ -172,19 +172,22 @@ package export::ffmpeg::PSP; .' -bufsize 65535' .$self->param('ab', 32) .' -acodec '.$acodec + .' -strict experimental' if ($acodec eq "aac") ." -f psp"; # Execute the parent method $self->SUPER::export($episode, '.MP4'); # Make the thumbnail if needed if ($self->{'psp_thumbnail'}) { - my $ffmpeg = find_program('ffmpeg') - or die("where is ffmpeg, we had it when we did an ffmpeg_init?"); - - $ffmpeg .= ' -y -i ' .shell_escape($self->get_outfile($episode, '.MP4',1)) - .' -s 160x90 -padtop 16 -padbottom 14 -r 1 -t 1' - .' -ss 7:00.00 -an -f mjpeg ' - .shell_escape($self->get_outfile($episode, '.THM')); + my $ffmpeg = find_program('mythffmpeg') + or die("where is mythffmpeg, we had it when we did an ffmpeg_init?"); + + $ffmpeg .= ' -y -i ' + .shell_escape($self->get_outfile($episode, '.MP4',1)) + .' -ss 420 -vframes 1' + .' -vf scale=160:90,pad=160:120:0:16:black' + .' -an -f mjpeg ' + .shell_escape($self->get_outfile($episode, '.THM')); `$ffmpeg` unless ($DEBUG); if ($DEBUG) { diff --git a/nuvexport b/nuvexport index 4bb364d..c473f61 100755 --- a/nuvexport +++ b/nuvexport @@ -53,8 +53,8 @@ # Load the exporters based on which suite was selected above if ($export_prog eq 'ffmpeg') { - find_program('ffmpeg') - or die "You need ffmpeg in order to use nuvexport in --ffmpeg mode\n"; + find_program('mythffmpeg') + or die "You need mythffmpeg in order to use nuvexport in --ffmpeg mode\n"; # ffmpeg - seems to work better and is the default require export::ffmpeg::XviD; require export::ffmpeg::DVD; @@ -63,6 +63,7 @@ require export::ffmpeg::MP3; require export::ffmpeg::PSP; require export::ffmpeg::MP4; + require export::ffmpeg::H264; } elsif ($export_prog eq 'mencoder') { find_program('mencoder') @@ -89,6 +90,7 @@ push @Exporters, export::ffmpeg::MP3->new; push @Exporters, export::ffmpeg::PSP->new; push @Exporters, export::ffmpeg::MP4->new; + push @Exporters, export::ffmpeg::H264->new; } elsif ($export_prog eq 'mencoder') { push @Exporters, export::mencoder::XviD->new; diff --git a/nuvexportrc b/nuvexportrc index 2c709fc..655b967 100644 --- a/nuvexportrc +++ b/nuvexportrc @@ -336,18 +336,6 @@ # -# -# Codec to use (mpeg4 or h264). Please note that h264 support requires the -# SVN version of ffmpeg (not CVS!). In fact, even the mpeg4 codec works -# better with the SVN version. -# -# Note: The h.264 files exported by nuvexport seem to play fine on -# ipods, but lack the atom necessary to be recognized by iTunes, so you -# will have to find other means to get the exports onto your ipod (gtkpod -# works great). -# - mp4_codec = h264 - # # Framerate to use: auto, 25, 23.97, 29.97. PAL will always be 25 fps, # and auto will set 29.97 for everything over 320x288 and 23.97 for the