| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,269 @@ | ||
| package export_SVCD; | ||
|
|
||
| # Load the nuv utilities | ||
| use nuv_utils; | ||
|
|
||
| # Make sure we have pointers to the main:: namespace for certain variables | ||
| *Prog = *main::Prog; | ||
| *gui = *main::gui; | ||
|
|
||
| sub new { | ||
| my $class = shift; | ||
| my $self = { | ||
| 'name' => 'Export to SVCD', | ||
| 'enabled' => 1, | ||
| 'started' => 0, | ||
| 'fifodir' => "fifodir.$$", | ||
| 'children' => [], | ||
| 'errors' => undef, | ||
| 'episode' => undef, | ||
| 'savepath' => '.', | ||
| 'outfile' => 'out.mpg', | ||
| 'tmp_a' => 'out.mp2', | ||
| 'tmp_v' => 'out.m2v', | ||
| 'use_cutlist' => 0, | ||
| 'a_bitrate' => 192, | ||
| 'v_bitrate' => 2500, | ||
| 'quantisation' => 5, # 4 through 6 is probably right... | ||
| 'noise_reduction' => 1, | ||
| @_ #allows user-specified attributes to override the defaults | ||
| }; | ||
| bless($self, $class); | ||
| # Make sure that we have an mp2 encoder | ||
| $Prog{mp2_encoder} = find_program('toolame', 'mp2enc'); | ||
| push @{$self->{errors}}, 'You need toolame or mp2enc to export an svcd.' unless ($Prog{mp2_encoder}); | ||
| # Make sure that we have an mplexer | ||
| $Prog{mplexer} = find_program('tcmplex', 'mplex'); | ||
| push @{$self->{errors}}, 'You need tcmplex or mplex to export an svcd.' unless ($Prog{mplexer}); | ||
| # Make sure that we have the other necessary programs | ||
| find_program('mpeg2enc') | ||
| or push @{$self->{errors}}, 'You need mpeg2enc to export an svcd.'; | ||
| find_program('yuvscaler') | ||
| or push @{$self->{errors}}, 'You need yuvscaler to export an svcd.'; | ||
| # Do we have yuvdenoise? | ||
| $Prog{yuvdenoise} = find_program('yuvdenoise'); | ||
| # Any errors? disable this function | ||
| $self->{enabled} = 0 if ($self->{errors} && @{$self->{errors}} > 0); | ||
| # Return | ||
| return $self; | ||
| } | ||
|
|
||
| sub gather_data { | ||
| my $self = shift; | ||
| my $default_filename = ''; | ||
| # Get the save path | ||
| $self->{savepath} = $gui->query_savepath(); | ||
| # Ask the user for the filename | ||
| if($self->{episode}->{show_name} ne 'Untitled' and $self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}.' - '.$self->{episode}->{title}; | ||
| } | ||
| elsif($self->{episode}->{show_name} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}; | ||
| } | ||
| elsif($self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{title}; | ||
| } | ||
|
|
||
| $self->{outfile} = $gui->query_filename($default_filename, 'mpg', $self->{savepath}); | ||
| # Ask the user if he/she wants to use the cutlist | ||
| if ($self->{episode}->{cutlist} && $self->{episode}->{cutlist} =~ /\d/) { | ||
| $self->{use_cutlist} = $gui->query_text('Enable Myth cutlist?', | ||
| 'yesno', | ||
| $self->{use_cutlist} ? 'Yes' : 'No'); | ||
| } | ||
| else { | ||
| $gui->notify('No cutlist found. Hopefully this means that you already removed the commercials.'); | ||
| } | ||
| # Ask the user what audio bitrate he/she wants | ||
| my $a_bitrate = $gui->query_text('Audio bitrate?', | ||
| 'int', | ||
| $self->{a_bitrate}); | ||
| while ($a_bitrate < 64 && $a_bitrate > 384) { | ||
| if ($a_bitrate < 64) { | ||
| $gui->notify('Too low; please choose a bitrate >= 64.'); | ||
| } | ||
| elsif ($a_bitrate > 384) { | ||
| $gui->notify('Too high; please choose a bitrate <= 384.'); | ||
| } | ||
| $a_bitrate = $gui->query_text('Audio bitrate?', | ||
| 'int', | ||
| $self->{a_bitrate}); | ||
| } | ||
| $self->{a_bitrate} = $a_bitrate; | ||
| # Ask the user what video bitrate he/she wants, or calculate the max bitrate (2756 max, though we round down a bit since some dvd players can't handle the max) | ||
| # Then again, mpeg2enc seems to have trouble with bitrates > 2500 | ||
| $self->{v_bitrate} = 2742 - $self->{a_bitrate} ? 2500 : 2742 - $self->{a_bitrate}; | ||
| my $v_bitrate = $gui->query_text('Maximum video bitrate for VBR?', | ||
| 'int', | ||
| $self->{v_bitrate}); | ||
| while ($v_bitrate < 1000 && $v_bitrate > $max_v_bitrate) { | ||
| if ($v_bitrate < 1000) { | ||
| $gui->notify('Too low; please choose a bitrate >= 1000.'); | ||
| } | ||
| elsif ($v_bitrate > $max_v_bitrate) { | ||
| $gui->notify("Too high; please choose a bitrate <= $self->{v_bitrate}."); | ||
| } | ||
| $v_bitrate = $gui->query_text('Maximum video bitrate for VBR?', | ||
| 'int', | ||
| $self->{v_bitrate}); | ||
| } | ||
| $self->{v_bitrate} = $v_bitrate; | ||
| # Ask the user what vbr quality (quantisation) he/she wants - 2..31 | ||
| my $quantisation = $gui->query_text('VBR quality/quantisation (2-31)?', 'float', $self->{quantisation}); | ||
| while ($quantisation < 2 && $quantisation > 31) { | ||
| if ($quantisation < 2) { | ||
| print "Too low; please choose a number between 2 and 31.\n"; | ||
| } | ||
| elsif ($quantisation > 31) { | ||
| print "Too high; please choose a number between 2 and 31\n"; | ||
| } | ||
| $quantisation = $gui->query_text('VBR quality/quantisation (2-31)?', | ||
| 'float', | ||
| $self->{quantisation}); | ||
| } | ||
| $self->{quantisation} = $quantisation; | ||
| # Ask the user what vbr quality (quantisation) he/she wants - 2..31 | ||
| if ($Prog{yuvdenoise}) { | ||
| $self->{noise_reduction} = $gui->query_text('Enable noise reduction (slower, but better results)?', | ||
| 'yesno', | ||
| $self->{noise_reduction} ? 'Yes' : 'No'); | ||
| } | ||
| else { | ||
| $gui->notify('Couldn\'t find yuvdenoise. Please install it if you want noise reduction.'); | ||
| } | ||
| # Do we want bin/cue files, or just an mpeg? | ||
| # nothing, at the moment. | ||
| } | ||
|
|
||
| sub execute { | ||
| my $self = shift; | ||
| # make sure that the fifo dir is clean | ||
| if (-e "$self->{fifodir}/vidout" || -e "$self->{fifodir}/audout") { | ||
| die "Possibly stale mythtranscode fifo's in $self->{fifodir}.\nPlease remove them before running nuvexport.\n\n"; | ||
| } | ||
| # Gather any necessary data | ||
| $self->{episode} = shift; | ||
| $self->gather_data; | ||
| # Load nuv info | ||
| my %nuv_info = nuv_info($self->{episode}->{filename}); | ||
| # Set this to true so that the cleanup routine actually runs | ||
| $self->{started} = 1; | ||
| # Create a directory for mythtranscode's fifo's | ||
| unless (-d $self->{fifodir}) { | ||
| mkdir($self->{fifodir}, 0755) or die "Can't create $self->{fifodir}: $!\n\n"; | ||
| } | ||
| # Generate some names for the temporary audio and video files | ||
| ($self->{tmp_a} = $self->{episode}->{filename}) =~ s/\.nuv$/.mp2/; | ||
| ($self->{tmp_v} = $self->{episode}->{filename}) =~ s/\.nuv$/.m2v/; | ||
| # Here, we have to fork off a copy of mythtranscode | ||
| my $command = "nice -n 19 mythtranscode -p autodetect -c $self->{episode}->{channel} -s $self->{episode}->{start_time_sep} -f $self->{fifodir} --fifosync"; | ||
| $command .= ' --honorcutlist' if ($self->{use_cutlist}); | ||
| if ($DEBUG) { | ||
| print "\nmythtranscode command:\n\n$command\n"; | ||
| } | ||
| else { | ||
| push @{$self->{children}}, fork_command($command); | ||
| } | ||
| # Sleep a bit to let mythtranscode start up | ||
| if (!$DEBUG) { | ||
| my $overload = 0; | ||
| while (++$overload < 30 && !(-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout")) { | ||
| sleep 1; | ||
| print "Waiting for mythtranscode to set up the fifos.\n"; | ||
| } | ||
| unless (-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout") { | ||
| die "Waited too long for mythtranscode to create its fifos. Please try again.\n\n"; | ||
| } | ||
| } | ||
| # Now we fork off a process to encode the audio | ||
| if ($Prog{mp2_encoder} =~ /\btoolame$/) { | ||
| $sample = $nuv_info{audio_sample_rate} / 1000; | ||
| $command = "nice -n 19 toolame -s $sample -m j -b $self->{a_bitrate} $self->{fifodir}/audout $self->{tmp_a}"; | ||
| } | ||
| else { | ||
| $command = "nice -n 19 ffmpeg -f s16le -ar $nuv_info{audio_sample_rate} -ac 2 -i $self->{fifodir}/audout -vn -f wav -" | ||
| ." | nice -n 19 mp2enc -b $self->{a_bitrate} -r $nuv_info{audio_sample_rate} -s -o $self->{tmp_a}"; | ||
| } | ||
| if ($DEBUG) { | ||
| print "\ntoolame command:\n\n$command\n"; | ||
| } | ||
| else { | ||
| push @{$self->{children}}, fork_command($command); | ||
| } | ||
| # And lastly, we fork off a process to encode the video | ||
| # Multiple CPU's? Let's multiprocess | ||
| $cpus = num_cpus(); | ||
| # pulldown does NOT work - keeps complaining about unsupport fps even when it's already set to 29.97 | ||
| #my $pulldown = 0; | ||
| # Build the command to rescale the image and encode the video | ||
| my $framerate; | ||
| $command = "nice -n 19 ffmpeg -f rawvideo -s $nuv_info{width}x$nuv_info{height} -r $nuv_info{fps} -i $self->{fifodir}/vidout -f yuv4mpegpipe -"; | ||
| # Certain options for PAL | ||
| if ($nuv_info{fps} =~ /^2(?:5|4\.9)/) { | ||
| $command .= " | nice -n 19 yuvdenoise -r 16" if ($self->{noise_reduction}); | ||
| $command .= " | nice -n 19 yuvscaler -v 0 -n p -M BICUBIC -O SVCD"; | ||
| $framerate = 3; | ||
| } | ||
| # Other options for NTSC | ||
| else { | ||
| # SOMEDAY I'd like to be able to get 3:2 pulldown working properly.... | ||
| #$command .= " | yuvkineco -F 1" if ($pulldown); | ||
| $command .= " | nice -n 19 yuvdenoise -r 16" if ($self->{noise_reduction}); | ||
| $command .= " | nice -n 19 yuvscaler -v 0 -n n -M BICUBIC -O SVCD"; | ||
| $framerate = 4; | ||
| } | ||
| # Finish building $command, and execute it | ||
| $command .= " | nice -n 19 mpeg2enc --format 5 --quantisation $self->{quantisation} --quantisation-reduction 2" | ||
| ." --video-bitrate $self->{v_bitrate} --aspect 2 --frame-rate $framerate" | ||
| #.($pulldown ? ' --frame-rate 1 --3-2-pulldown' : " --frame-rate $framerate") | ||
| #." --interlace-mode 1 --motion-search-radius 24 --video-buffer 230" | ||
| ." --interlace-mode 0 --motion-search-radius 24 --video-buffer 230" | ||
| ." --nonvideo-bitrate $self->{a_bitrate} --sequence-length 795" | ||
| ." --reduction-4x4 1 --reduction-2x2 1 --keep-hf" | ||
| .($cpus > 1 ? " --multi-thread $cpus" : '') | ||
| ." -o $self->{tmp_v}"; | ||
| if ($DEBUG) { | ||
| print "\nmpeg2enc command:\n\n$command\n"; | ||
| } | ||
| else { | ||
| push @{$self->{children}}, fork_command($command); | ||
| } | ||
| # Wait for child processes to finish | ||
| 1 while (wait > 0); | ||
| $self->{children} = undef; | ||
| # Multiplex the streams | ||
| my $safe_outfile = shell_escape($self->{outfile}); | ||
| if ($Prog{mplexer} =~ /\btcmplex$/) { | ||
| $command = "nice -n 19 tcmplex -m s -i $self->{tmp_v} -p $self->{tmp_a} -o $safe_outfile"; | ||
| } | ||
| else { | ||
| $command = "nice -n 19 mplex -f 5 $self->{tmp_v} $self->{tmp_a} -o $safe_outfile"; | ||
| } | ||
| if ($DEBUG) { | ||
| print "\nmultiplex command:\n\n$command\n\n"; | ||
| exit; | ||
| } | ||
| system($command); | ||
| } | ||
|
|
||
| sub cleanup { | ||
| my $self = shift; | ||
| return unless ($self->{started}); | ||
| # Make sure any child processes also go away | ||
| if ($self->{children} && @{$self->{children}}) { | ||
| foreach my $child (@{$self->{children}}) { | ||
| kill('INT', $child); | ||
| } | ||
| 1 while (wait > 0); | ||
| } | ||
| # Remove any temporary files | ||
| foreach my $file ("$self->{fifodir}/audout", "$self->{fifodir}/vidout", $self->{tmp_a}, $self->{tmp_v}) { | ||
| unlink $file if (-e $file); | ||
| } | ||
| rmdir $self->{fifodir} if (-e $self->{fifodir}); | ||
| } | ||
|
|
||
| 1; #return true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,182 @@ | ||
| package export_VCD; | ||
|
|
||
| # Load the nuv utilities | ||
| use nuv_utils; | ||
|
|
||
| # Make sure we have pointers to the main:: namespace for certain variables | ||
| *Prog = *main::Prog; | ||
| *gui = *main::gui; | ||
|
|
||
| sub new { | ||
| my $class = shift; | ||
| my $self = { | ||
| 'name' => 'Export to VCD', | ||
| 'enabled' => 1, | ||
| 'started' => 0, | ||
| 'fifodir' => "fifodir.$$", | ||
| 'children' => [], | ||
| 'errors' => undef, | ||
| 'episode' => undef, | ||
| 'savepath' => '.', | ||
| 'outfile' => 'out.mpg', | ||
| 'tmp_a' => 'out.mp2', | ||
| 'tmp_v' => 'out.m2v', | ||
| 'use_cutlist' => 0, | ||
| 'noise_reduction' => 1, | ||
| @_ #allows user-specified attributes to override the defaults | ||
| }; | ||
| bless($self, $class); | ||
| # Make sure that we have an mp2 encoder | ||
| $Prog{mp2_encoder} = find_program('toolame', 'mp2enc'); | ||
| push @{$self->{errors}}, 'You need toolame or mp2enc to export an svcd.' unless ($Prog{mp2_encoder}); | ||
| # Make sure that we have an mplexer | ||
| $Prog{mplexer} = find_program('tcmplex', 'mplex'); | ||
| push @{$self->{errors}}, 'You need tcmplex or mplex to export an svcd.' unless ($Prog{mplexer}); | ||
| # Any errors? disable this function | ||
| $self->{enabled} = 0 if ($self->{errors} && @{$self->{errors}} > 0); | ||
| # Return | ||
| return $self; | ||
| } | ||
|
|
||
| sub gather_data { | ||
| my $self = shift; | ||
| my $default_filename; | ||
| # Get the save path | ||
| $self->{savepath} = $gui->query_savepath(); | ||
| # Ask the user for the filename | ||
| if($self->{episode}->{show_name} ne 'Untitled' and $self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}.' - '.$self->{episode}->{title}; | ||
| } | ||
| elsif($self->{episode}->{show_name} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}; | ||
| } | ||
| elsif($self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{title}; | ||
| } | ||
|
|
||
| $self->{outfile} = $gui->query_filename($default_filename, 'mpg', $self->{savepath}); | ||
| # Ask the user if he/she wants to use the cutlist | ||
| if ($self->{episode}->{cutlist} && $self->{episode}->{cutlist} =~ /\d/) { | ||
| $self->{use_cutlist} = $gui->query_text('Enable Myth cutlist?', | ||
| 'yesno', | ||
| $self->{use_cutlist} ? 'Yes' : 'No'); | ||
| } | ||
| else { | ||
| $gui->notify('No cutlist found. Hopefully this means that you already removed the commercials.'); | ||
| } | ||
| # Ask the user if he/she wants noise reduction | ||
| $self->{noise_reduction} = $gui->query_text('Enable noise reduction (slower, but better results)?', | ||
| 'yesno', | ||
| $self->{noise_reduction} ? 'Yes' : 'No'); | ||
| # Do we want bin/cue files, or just an mpeg? | ||
| # nothing, at the moment. | ||
| } | ||
|
|
||
| sub execute { | ||
| my $self = shift; | ||
| # make sure that the fifo dir is clean | ||
| if (-e "$self->{fifodir}/vidout" || -e "$self->{fifodir}/audout") { | ||
| die "Possibly stale mythtranscode fifo's in $self->{fifodir}.\nPlease remove them before running nuvexport.\n\n"; | ||
| } | ||
| # Gather any necessary data | ||
| $self->{episode} = shift; | ||
| $self->gather_data; | ||
| # Load nuv info | ||
| my %nuv_info = nuv_info($self->{episode}->{filename}); | ||
| # Set this to true so that the cleanup routine actually runs | ||
| $self->{started} = 1; | ||
| # Create a directory for mythtranscode's fifo's | ||
| unless (-d $self->{fifodir}) { | ||
| mkdir($self->{fifodir}, 0755) or die "Can't create $self->{fifodir}: $!\n\n"; | ||
| } | ||
| # Generate some names for the temporary audio and video files | ||
| ($self->{tmp_a} = $self->{episode}->{filename}) =~ s/\.nuv$/.mp2/; | ||
| ($self->{tmp_v} = $self->{episode}->{filename}) =~ s/\.nuv$/.m1v/; | ||
| # Here, we have to fork off a copy of mythtranscode | ||
| my $command = "nice -n 19 mythtranscode -p autodetect -c $self->{episode}->{channel} -s $self->{episode}->{start_time_sep} -f $self->{fifodir} --fifosync"; | ||
| $command .= ' --honorcutlist' if ($self->{use_cutlist}); | ||
| push @{$self->{children}}, fork_command($command); | ||
| # Sleep a bit to let mythtranscode start up | ||
| my $overload = 0; | ||
| while (++$overload < 30 && !(-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout")) { | ||
| sleep 1; | ||
| print "Waiting for mythtranscode to set up the fifos.\n"; | ||
| } | ||
| unless (-e "$self->{fifodir}/audout" && -e "$self->{fifodir}/vidout") { | ||
| die "Waited too long for mythtranscode to create its fifos. Please try again.\n\n"; | ||
| } | ||
| # Now we fork off a process to encode the audio | ||
| if ($Prog{mp2_encoder} =~ /\btoolame$/) { | ||
| $sample = $nuv_info{audio_sample_rate} / 1000; | ||
| $command = "nice -n 19 toolame -s $sample -m j -b 192 $self->{fifodir}/audout $self->{tmp_a}"; | ||
| } | ||
| else { | ||
| $command = "nice -n 19 ffmpeg -f s16le -ar $nuv_info{audio_sample_rate} -ac 2 -i $self->{fifodir}/audout -vn -f wav -" | ||
| ." | nice -n 19 mp2enc -b 192 -r $nuv_info{audio_sample_rate} -s -o $self->{tmp_a}"; | ||
| } | ||
| push @{$self->{children}}, fork_command($command); | ||
| # And lastly, we fork off a process to encode the video | ||
| # Multiple CPU's? Let's multiprocess | ||
| $cpus = num_cpus(); | ||
| # pulldown does NOT work - keeps complaining about unsupport fps even when it's already set to 29.97 | ||
| #my $pulldown = 0; | ||
| # Build the command to rescale the image and encode the video | ||
| my $framerate; | ||
| $command = "nice -n 19 ffmpeg -f rawvideo -s $nuv_info{width}x$nuv_info{height} -r $nuv_info{fps} -i $self->{fifodir}/vidout -f yuv4mpegpipe -"; | ||
| # Certain options for PAL | ||
| if ($nuv_info{fps} =~ /^2(?:5|4\.9)/) { | ||
| $command .= " | nice -n 19 yuvdenoise -r 16" if ($self->{noise_reduction}); | ||
| $command .= " | nice -n 19 yuvscaler -v 0 -n p -M BICUBIC -O VCD"; | ||
| $framerate = 3; | ||
| } | ||
| # Other options for NTSC | ||
| else { | ||
| # SOMEDAY I'd like to be able to get 3:2 pulldown working properly.... | ||
| #$command .= " | yuvkineco -F 1" if ($pulldown); | ||
| $command .= " | nice -n 19 yuvdenoise -r 16" if ($self->{noise_reduction}); | ||
| $command .= " | nice -n 19 yuvscaler -v 0 -n n -O VCD"; | ||
| $framerate = 4; | ||
| } | ||
| # Finish building $command, and execute it | ||
| $command .= " | nice -n 19 mpeg2enc --format 1 --quantisation-reduction 2" | ||
| ." --frame-rate $framerate -n n" | ||
| #.($pulldown ? ' --frame-rate 1 --3-2-pulldown' : " --frame-rate $framerate") | ||
| ." --sequence-length 600" | ||
| ." --reduction-4x4 1 --reduction-2x2 1 --keep-hf" | ||
| .($cpus > 1 ? " --multi-thread $cpus" : '') | ||
| ." -o $self->{tmp_v}"; | ||
| push @{$self->{children}}, fork_command($command); | ||
| # Wait for child processes to finish | ||
| 1 while (wait > 0); | ||
| $self->{children} = undef; | ||
| # Multiplex the streams | ||
| my $safe_outfile = shell_escape($self->{outfile}); | ||
| if ($Prog{mplexer} =~ /\btcmplex$/) { | ||
| system("nice -n 19 tcmplex -m v -i $self->{tmp_v} -p $self->{tmp_a} -o $safe_outfile"); | ||
| } | ||
| else { | ||
| system("nice -n 19 mplex -f 1 $self->{tmp_v} $self->{tmp_a} -o $safe_outfile"); | ||
| } | ||
| } | ||
|
|
||
| sub cleanup { | ||
| my $self = shift; | ||
| return unless ($self->{started}); | ||
| # Make sure any child processes also go away | ||
| if ($self->{children} && @{$self->{children}}) { | ||
| foreach my $child (@{$self->{children}}) { | ||
| kill('INT', $child); | ||
| } | ||
| 1 while (wait > 0); | ||
| } | ||
| # Remove any temporary files | ||
| foreach my $file ("$self->{fifodir}/audout", "$self->{fifodir}/vidout", $self->{tmp_a}, $self->{tmp_v}) { | ||
| unlink $file if (-e $file); | ||
| } | ||
| rmdir $self->{fifodir} if (-e $self->{fifodir}); | ||
| } | ||
|
|
||
| 1; #return true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| package export_WMV; | ||
|
|
||
| # Load the nuv utilities | ||
| use nuv_utils; | ||
|
|
||
| # Make sure we have pointers to the main:: namespace for certain variables | ||
| *Prog = *main::Prog; | ||
| *gui = *main::gui; | ||
|
|
||
| sub new { | ||
| my $class = shift; | ||
| my $self = { | ||
| 'name' => 'Export WMV', | ||
| 'enabled' => 1, | ||
| 'errors' => undef, | ||
| 'episode' => undef, | ||
| 'savepath' => '.', | ||
| 'outfile' => 'out.wmv', | ||
| 'use_cutlist' => 0, | ||
| 'a_bitrate' => 64, | ||
| 'v_bitrate' => 256, | ||
| 'h_res' => 320, | ||
| 'v_res' => 240, | ||
| 'sql_file' => undef, | ||
| @_ #allows user-specified attributes to override the defaults | ||
| }; | ||
| bless($self, $class); | ||
| # Any errors? disable this function | ||
| $self->{enabled} = 0 if ($self->{errors} && @{$self->{errors}} > 0); | ||
| # Return | ||
| return $self; | ||
| } | ||
|
|
||
| sub gather_data { | ||
| my $self = shift; | ||
| my $default_filename; | ||
| # Get the save path | ||
| $self->{savepath} = $gui->query_savepath(); | ||
| # Ask the user for the filename | ||
| if($self->{episode}->{show_name} ne 'Untitled' and $self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}.' - '.$self->{episode}->{title}; | ||
| } | ||
| elsif($self->{episode}->{show_name} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{show_name}; | ||
| } | ||
| elsif($self->{episode}->{title} ne 'Untitled') | ||
| { | ||
| $default_filename = $self->{episode}->{title}; | ||
| } | ||
|
|
||
| $self->{outfile} = $gui->query_filename($default_filename, 'wmv', $self->{savepath}); | ||
| # Ask the user if he/she wants to use the cutlist | ||
| if ($self->{episode}->{cutlist} && $self->{episode}->{cutlist} =~ /\d/) { | ||
| $self->{use_cutlist} = $gui->query_text('Enable Myth cutlist?', | ||
| 'yesno', | ||
| $self->{use_cutlist} ? 'Yes' : 'No'); | ||
| } | ||
| else { | ||
| $gui->notify('No cutlist found. Hopefully this means that you already removed the commercials.'); | ||
| } | ||
| # Ask the user what audio bitrate he/she wants | ||
| my $a_bitrate = $gui->query_text('Audio bitrate?', | ||
| 'int', | ||
| $self->{a_bitrate}); | ||
| $self->{a_bitrate} = $a_bitrate; | ||
| # Ask the user what video bitrate he/she wants | ||
| my $v_bitrate = $gui->query_text('Video bitrate?', | ||
| 'int', | ||
| $self->{v_bitrate}); | ||
| $self->{v_bitrate} = $v_bitrate; | ||
| # Ask the user what horiz res he/she wants | ||
| my $h_res = $gui->query_text('Horizontal resolution?', 'int', $self->{h_res}); | ||
| $self->{h_res} = $h_res; | ||
| # Ask the user what vert res he/she wants | ||
| my $v_res = $gui->query_text('Vertical resolution?', 'int', $self->{v_res}); | ||
| $self->{v_res} = $v_res; | ||
| } | ||
|
|
||
| sub execute { | ||
| my $self = shift; | ||
| # make sure that the fifo dir is clean | ||
| if (-e 'fifodir/vidout' || -e 'fifodir/audout') { | ||
| die "Possibly stale mythtranscode fifo's in fifodir.\nPlease remove them before running nuvexport.\n\n"; | ||
| } | ||
| # Gather any necessary data | ||
| $self->{episode} = shift; | ||
| $self->gather_data; | ||
| # Load nuv info | ||
| my %nuv_info = nuv_info($self->{episode}->{filename}); | ||
| # Set this to true so that the cleanup routine actually runs | ||
| $self->{started} = 1; | ||
| # Create a directory for mythtranscode's fifo's | ||
| unless (-d 'fifodir') { | ||
| mkdir('fifodir', 0755) or die "Can't create fifodir: $!\n\n"; | ||
| } | ||
| # Here, we have to fork off a copy of mythtranscode | ||
| my $command = "nice -n 19 mythtranscode -p autodetect -c $self->{episode}->{channel} -s $self->{episode}->{start_time_sep} -f fifodir"; | ||
| $command .= ' --honorcutlist' if ($self->{use_cutlist}); | ||
| push @{$self->{children}}, fork_command($command); | ||
| # Sleep a bit to let mythtranscode start up | ||
| my $overload = 0; | ||
| while (++$overload < 30 && !(-e 'fifodir/audout' && -e 'fifodir/vidout')) { | ||
| sleep 1; | ||
| print "Waiting for mythtranscode to set up the fifos.\n"; | ||
| } | ||
| unless (-e 'fifodir/audout' && -e 'fifodir/vidout') { | ||
| die "Waited too long for mythtranscode to create its fifos. Please try again.\n\n"; | ||
| } | ||
| # Now we fork off a process to encode everything | ||
| $safe_outfile = shell_escape($self->{outfile}); | ||
| $command = "nice -n 19 ffmpeg -y -f s16le -ar $nuv_info{audio_sample_rate} -ac 2 -i fifodir/audout -f rawvideo -s $nuv_info{width}x$nuv_info{height} -r $nuv_info{fps} -i fifodir/vidout -b $self->{v_bitrate} -ab $self->{a_bitrate} -s $self->{h_res}x$self->{v_res} $safe_outfile"; | ||
| push @{$self->{children}}, fork_command($command); | ||
| # Wait for child processes to finish | ||
| 1 while (wait > 0); | ||
| $self->{children} = undef; | ||
| } | ||
|
|
||
| sub cleanup { | ||
| my $self = shift; | ||
| return unless ($self->{started}); | ||
| # Make sure any child processes also go away | ||
| if ($self->{children} && @{$self->{children}}) { | ||
| foreach my $child (@{$self->{children}}) { | ||
| kill('INT', $child); | ||
| } | ||
| 1 while (wait > 0); | ||
| } | ||
| # Remove any temporary files | ||
| foreach my $file ('fifodir/audout', 'fifodir/vidout') { | ||
| unlink $file if (-e $file); | ||
| } | ||
| rmdir 'fifodir' if (-e 'fifodir'); | ||
| } | ||
|
|
||
| 1; #return true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,263 @@ | ||
| package gui_text; | ||
|
|
||
| use File::Path; | ||
|
|
||
| # Make sure we have pointers to the main:: namespace for certain variables | ||
| *Shows = *main::Shows; | ||
| *Functions = *main::Functions; | ||
| *num_shows = *main::num_shows; | ||
|
|
||
| sub new { | ||
| my $class = shift; | ||
| my $self = { | ||
| 'query_stage' => 'show', | ||
| 'show_choice' => '', | ||
| 'episode_choice' => undef, | ||
| @_ #allows user-specified attributes to override the defaults | ||
| }; | ||
| return bless($self, $class); | ||
| } | ||
|
|
||
| sub main_loop { | ||
| my $self = shift; | ||
| # Display the show list | ||
| while (1) { | ||
| # Clear the screen | ||
| system('clear'); | ||
| # Stage "quit" means, well, quit... | ||
| last if ($self->stage eq 'quit'); | ||
| # Are we asking the user which show to encode? | ||
| if (!$self->{show_choice} || $self->stage eq 'show') { | ||
| $self->query_shows; | ||
| } | ||
| # Nope. What about the episode choice? | ||
| elsif (!$self->{episode_choice} || $self->stage eq 'episode') { | ||
| $self->query_episodes; | ||
| } | ||
| # Time to decide what we want to do? | ||
| elsif ($self->stage eq 'function') { | ||
| $self->query_functions; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| sub query_shows { | ||
| my $self = shift; | ||
| # Build the query | ||
| my $query = "\nYou have recorded the following shows:\n\n"; | ||
| my ($count, @show_choices); | ||
| foreach $show (sort keys %Shows) { | ||
| $count++; | ||
| # Print out this choice, adjusting space where necessary | ||
| $query .= ' '; | ||
| $query .= ' ' if ($num_shows > 10 && $count < 10); | ||
| $query .= ' ' if ($num_shows > 100 && $count < 100); | ||
| $query .= "$count. "; | ||
| # print out the name of this show, and an episode count | ||
| my $num_episodes = @{$Shows{$show}}; | ||
| $query .= "$show ($num_episodes episode".($num_episodes == 1 ? '' : 's').")\n"; | ||
| $show_choices[$count-1] = $show; | ||
| } | ||
| $query .= "\n q. Quit\n\nChoose a show: "; | ||
| # Query the user | ||
| my $choice = $self->query_text($query, 'string', ''); | ||
| # Quit? | ||
| return $self->stage('quit') if ($choice =~ /^\W*q/i); | ||
| # Move on to the next stage if the user chose a valid show | ||
| $choice =~ s/^\D*/0/s; # suppress warnings | ||
| if ($choice > 0 && $show_choices[$choice-1]) { | ||
| $self->{show_choice} = $show_choices[$choice-1]; | ||
| $self->stage('episode'); | ||
| } | ||
| } | ||
|
|
||
| sub query_episodes { | ||
| my $self = shift; | ||
| my $num_episodes = @{$Shows{$self->{show_choice}}}; | ||
| # Define a newline + whitespace so we can tab out extra lines of episode description | ||
| my $newline = "\n" . ' ' x (4 + length $num_episodes); | ||
| # Build the query | ||
| my $query = "\nYou have recorded the following episodes of $self->{show_choice}:\n\n"; | ||
| my ($count, @episode_choices); | ||
| foreach $episode (@{$Shows{$self->{show_choice}}}) { | ||
| $count++; | ||
| # Print out this choice, adjusting space where necessary | ||
| $query .= ' '; | ||
| $query .= ' ' if ($num_episodes > 10 && $count < 10); | ||
| $query .= ' ' if ($num_episodes > 100 && $count < 100); | ||
| $query .= "$count. "; | ||
| # print out the name of this show, and an episode count | ||
| $query .= join($newline, "$episode->{title} ($episode->{showtime})", | ||
| $episode->{description})."\n"; | ||
| $episode_choices[$count-1] = $episode; | ||
| } | ||
| $query .= "\n r. Return to shows menu\n q. Quit\n\nChoose an episode: "; | ||
| # Query the user | ||
| my $choice = $self->query_text($query, 'string', ''); | ||
| # Quit? | ||
| return $self->stage('quit') if ($choice =~ /^\W*q/i); | ||
| # Backing up a stage? | ||
| return $self->stage('show') if ($choice =~ /^\W*[rb]/i); | ||
| # Move on to the next stage if the user chose a valid episode | ||
| $choice =~ s/^\D*/0/s; # suppress warnings | ||
| if ($choice > 0 && $episode_choices[$choice-1]) { | ||
| $self->{episode_choice} = $episode_choices[$choice-1]; | ||
| $self->stage('function'); | ||
| } | ||
| } | ||
|
|
||
| sub query_functions { | ||
| my $self = shift; | ||
| # Build the query | ||
| my $query = "What would you like to do with your recording?\n\n" | ||
| ." Show: $self->{show_choice}\n" | ||
| ." Episode: $self->{episode_choice}->{title}\n" | ||
| ." Airtime: $self->{episode_choice}->{showtime}\n\n"; | ||
| # What are our function options? | ||
| my ($count); | ||
| foreach my $function (@Functions) { | ||
| $count++; | ||
| $query .= (' ' x (3 - length($count)))."$count. ".$function->{name}; | ||
| $query .= ' (disabled)' unless ($function->{enabled}); | ||
| $query .= "\n"; | ||
| } | ||
| $query .= "\n r. Return to episode menu\n q. Quit\n\nChoose a function: "; | ||
| # Query the user | ||
| my $choice = $self->query_text($query, 'string', ''); | ||
| # Quit? | ||
| return $self->stage('quit') if ($choice =~ /^\W*q/i); | ||
| # Backing up a stage? | ||
| return $self->stage('episode') if ($choice =~ /^\W*[rb]/i); | ||
| # Execute the chosen function, and then quit | ||
| $choice =~ s/^\D*/0/s; # suppress warnings | ||
| # No choice given? | ||
| if ($choice < 1) { | ||
| next; | ||
| } | ||
| # Make sure that this function is enabled | ||
| elsif ($choice < 1 || !$Functions[$choice-1]->{enabled}) { | ||
| if ($Functions[$choice-1]->{errors} && @{$Functions[$choice-1]->{errors}}) { | ||
| $self->notify("\n".join("\n", @{$Functions[$choice-1]->{errors}})."\n"); | ||
| } | ||
| else { | ||
| $self->notify('Function "'.$Functions[$choice-1]->{name}."\" is disabled.\n"); | ||
| } | ||
| $self->notify("Press ENTER to continue.\n"); | ||
| <STDIN>; | ||
| } | ||
| elsif ($Functions[$choice-1]->{enabled}) { | ||
| $Functions[$choice-1]->execute($self->{episode_choice}); | ||
| $self->stage('quit'); | ||
| } | ||
| } | ||
|
|
||
| sub query_filename { | ||
| my $self = shift; | ||
| my $default = shift; | ||
| my $suffix = shift; | ||
| my $savepath = shift; | ||
| my $outfile = undef; | ||
| until ($outfile) { | ||
| $outfile = $self->query_text('Output filename? ', 'string', "$default.$suffix"); | ||
| $outfile =~ s/(?:\.$suffix)?$/.$suffix/si; | ||
| if (-e "$savepath/$outfile") { | ||
| if (-f "$savepath/$outfile") { | ||
| unless ($self->query_text("$savepath/$outfile exists. Overwrite?", 'yesno', 'No')) { | ||
| $outfile = undef; | ||
| } | ||
| } | ||
| else { | ||
| $self->notify("$savepath/$outfile exists and is not a regular file; please choose another."); | ||
| $outfile = undef; | ||
| } | ||
| } | ||
| } | ||
| return $outfile; | ||
| } | ||
|
|
||
| sub query_savepath { | ||
| $self = shift; | ||
| # Where are we saving the files to? | ||
| my $savepath = undef; | ||
| until ($savepath) { | ||
| $savepath = $self->query_text('Where would you like to export the files to?', 'string', '.');; | ||
| $savepath =~ s/\/+$//s; | ||
| unless ($savepath eq '.') { | ||
| # Make sure this is a valid directory | ||
| if (-e $savepath && !-d $savepath) { | ||
| $savepath = undef; | ||
| $self->notify("$savepath exists, but is not a directory."); | ||
| } | ||
| # Doesn't exist - query the user to create it | ||
| elsif (!-e $savepath) { | ||
| my $create = $self->query_text("$savepath doesn't exist. Create it?", 'yesno', 'Yes'); | ||
| if ($create) { | ||
| mkpath($savepath, 1, 0711) or die "Couldn't create $savepath: $!\n\n"; | ||
| } | ||
| else { | ||
| $savepath = undef; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| return $savepath; | ||
| } | ||
|
|
||
| sub query_text { | ||
| my $self = shift; | ||
| my $text = shift; | ||
| my $expect = shift; | ||
| my $default = shift; | ||
| my $default_extra = shift; | ||
| my $return = undef; | ||
| # Loop until we get a valid response | ||
| while (1) { | ||
| # Ask the question, get the answer | ||
| print $text, | ||
| ($default ? " [$default]".($default_extra ? $default_extra : '').' ' | ||
| : ' '); | ||
| chomp($return = <STDIN>); | ||
| # Nothing typed, is there a default value? | ||
| unless ($return =~ /\w/) { | ||
| next unless (defined $default); | ||
| $return = $default; | ||
| } | ||
| # Looking for a boolean/yesno response? | ||
| if ($expect =~ /yes.*no|bool/i) { | ||
| return $return =~ /^\W*[nf0]/i ? 0 : 1; | ||
| } | ||
| # Looking for an integer? | ||
| elsif ($expect =~ /int/i) { | ||
| $return =~ s/^\D*/0/; | ||
| if ($return != int($return)) { | ||
| print "Whole numbers only, please.\n"; | ||
| next; | ||
| } | ||
| return $return; | ||
| } | ||
| # Looking for a float? | ||
| elsif ($expect =~ /float/i) { | ||
| $return =~ s/^\D*/0/; | ||
| return $return + 0; | ||
| } | ||
| # Well, then we must be looking for a string | ||
| else { | ||
| return $return; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| sub notify { | ||
| my $self = shift; | ||
| print shift, "\n"; | ||
| } | ||
|
|
||
| sub stage { | ||
| my $self = shift; | ||
| my $stage = shift; | ||
| $self->{query_stage} = $stage if (defined $stage); | ||
| $self->{query_stage} = 'show' unless ($self->{query_stage}); | ||
| return $self->{query_stage}; | ||
| } | ||
|
|
||
| 1; #return true |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| #!/bin/sh | ||
|
|
||
| # First, install nuvexport itself | ||
| install -Dv -o root -g root -m 0755 nuvexport /usr/local/bin/nuvexport | ||
| install -Dv -o root -g root -m 0755 nuvinfo /usr/local/bin/nuvinfo | ||
|
|
||
| # Next, create the nuvexport shared directory | ||
| mkdir -pvm 0755 /usr/local/share/nuvexport | ||
|
|
||
| # Finally, install the other files | ||
| install -v -o root -g root -m 0644 *pm /usr/local/share/nuvexport |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,228 @@ | ||
| package nuv_utils; | ||
|
|
||
| use Exporter; | ||
| our @ISA = qw/ Exporter /; | ||
| our @EXPORT = qw/ generate_showtime find_program nuv_info num_cpus fork_command shell_escape mysql_escape Quit /; | ||
|
|
||
| # Returns a nicely-formatted timestamp from a specified time | ||
| sub generate_showtime { | ||
| $showtime = ''; | ||
| # Get the | ||
| my ($year, $month, $day, $hour, $minute, $second) = @_; | ||
| $month = int($month); | ||
| $day = int($day); | ||
| # Get the current time, so we know whether or not to display certain fields (eg. year) | ||
| my ($this_second, $this_minute, $this_hour, $ignore, $this_month, $this_year) = localtime; | ||
| $this_year += 1900; | ||
| $this_month++; | ||
| # Default the meridian to AM | ||
| my $meridian = 'AM'; | ||
| # Generate the showtime string | ||
| $showtime .= "$month/$day"; | ||
| $showtime .= "/$year" unless ($year == $this_year); | ||
| if ($hour == 0) { | ||
| $hour = 12; | ||
| } | ||
| elsif ($hour > 12) { | ||
| $hour -= 12; | ||
| $meridian = 'PM'; | ||
| } | ||
| $showtime .= ", $hour:$minute $meridian"; | ||
| # Return | ||
| return $showtime; | ||
| } | ||
|
|
||
| # This searches the path for the specified programs, and returns the lowest-index-value program found | ||
| sub find_program { | ||
| # Load the programs, and get a count of the priorities | ||
| my(%programs, $num_programs); | ||
| foreach my $program (@_) { | ||
| $programs{$program} = ++$num_programs; | ||
| } | ||
| # No programs requested? | ||
| return undef unless ($num_programs > 0); | ||
| # Search for the program(s) | ||
| my %found; | ||
| foreach my $path (split(/:/, $ENV{PATH}), '.') { | ||
| foreach my $program (keys %programs) { | ||
| if (-e "$path/$program" && (!$found{name} || $programs{$program} < $programs{$found{name}})) { | ||
| $found{name} = $program; | ||
| $found{path} = $path; | ||
| } | ||
| # Leave early if we found the highest priority program | ||
| last if ($found{name} && $programs{$found{name}} == 1); | ||
| } | ||
| } | ||
| # Return | ||
| return undef unless ($found{path} && $found{name}); | ||
| return $found{path}.'/'.$found{name}; | ||
| } | ||
|
|
||
| # Opens a .nuv file and returns information about it | ||
| sub nuv_info { | ||
| my $file = shift; | ||
| my(%info, $buffer); | ||
| # open the file | ||
| open(DATA, "$main::video_dir/$file") or die "Can't open $file: $!\n\n"; | ||
| # Read the file info header | ||
| read(DATA, $buffer, 72); | ||
| # Unpack the data structure | ||
| ($info{finfo}, # "NuppelVideo" + \0 | ||
| $info{version}, # "0.05" + \0 | ||
| $info{width}, | ||
| $info{height}, | ||
| $info{desiredheight}, # 0 .. as it is | ||
| $info{desiredwidth}, # 0 .. as it is | ||
| $info{pimode}, # P .. progressive, I .. interlaced (2 half pics) [NI] | ||
| $info{aspect}, # 1.0 .. square pixel (1.5 .. e.g. width=480: width*1.5=720 for capturing for svcd material | ||
| $info{fps}, | ||
| $info{videoblocks}, # count of video-blocks -1 .. unknown 0 .. no video | ||
| $info{audioblocks}, # count of audio-blocks -1 .. unknown 0 .. no audio | ||
| $info{textsblocks}, # count of text-blocks -1 .. unknown 0 .. no text | ||
| $info{keyframedist} | ||
| ) = unpack('Z12 Z5 xxx i i i i a xxx d d i i i i', $buffer); | ||
| # Is this even a NUV file? | ||
| return mpeg_info($file) unless ($info{finfo} =~ /\w/); | ||
| # Perl occasionally over-reads on the previous read() | ||
| seek(DATA, 72, 0); | ||
| # Read and parse the first frame header | ||
| read(DATA, $buffer, 12); | ||
| my ($frametype, | ||
| $comptype, | ||
| $keyframe, | ||
| $filters, | ||
| $timecode, | ||
| $packetlength) = unpack('a a a a i i', $buffer); | ||
| # Parse the frame | ||
| die "Illegal nuv file format: $file\n\n" unless ($frametype eq 'D'); | ||
| # Read some more stuff if we have to | ||
| read(DATA, $buffer, $packetlength) if ($packetlength); | ||
| # Read the remaining frame headers | ||
| while (12 == read(DATA, $buffer, 12)) { | ||
| # Parse the frame header | ||
| ($frametype, | ||
| $comptype, | ||
| $keyframe, | ||
| $filters, | ||
| $timecode, | ||
| $packetlength) = unpack('a a a a i i', $buffer); | ||
| # Read some more stuff if we have to | ||
| read(DATA, $buffer, $packetlength) if ($packetlength); | ||
| # Look for the audio frame | ||
| if ($frametype eq 'X') { | ||
| my $frame_version; | ||
| ($frame_version, | ||
| $info{video_fourcc}, | ||
| $info{audio_fourcc}, | ||
| $info{audio_sample_rate}, | ||
| $info{audio_bits_per_sample}, | ||
| $info{audio_channels}, | ||
| $info{audio_compression_ratio}, | ||
| $info{audio_quality}, | ||
| $info{rtjpeg_quality}, | ||
| $info{rtjpeg_luma_filter}, | ||
| $info{rtjpeg_chroma_filter}, | ||
| $info{lavc_bitrate}, | ||
| $info{lavc_qmin}, | ||
| $info{lavc_qmax}, | ||
| $info{lavc_maxqdiff}, | ||
| $info{seektable_offset}, | ||
| $info{keyframeadjust_offset} | ||
| ) = unpack('iiiiiiiiiiiiiiill', $buffer); | ||
| # Found the audio data we want - time to leave | ||
| last; | ||
| } | ||
| # Done reading frames - let's leave | ||
| else { | ||
| last; | ||
| } | ||
| } | ||
| # Close the file | ||
| close DATA; | ||
| # Make sure some things are actually numbers | ||
| $info{width} += 0; | ||
| $info{height} += 0; | ||
| # Return | ||
| return %info; | ||
| } | ||
|
|
||
| # Uses one of two mpeg info programs to load data about mpeg-based nuv files | ||
| sub mpeg_info { | ||
| my $file = "$main::video_dir/".shift; | ||
| $file =~ s/'/\\'/sg; | ||
| my %info; | ||
| # First, we check for the existence of an mpeg info program | ||
| my $program = find_program('tcprobe', 'mpgtx'); | ||
| # Nothing found? Die | ||
| die "You need tcprobe (transcode) or mpgtx to use this script on mpeg-based nuv files.\n\n" unless ($program); | ||
| # Grab tcprobe info | ||
| if ($program =~ /tcprobe$/) { | ||
| my $data = `$program -i '$file'`; | ||
| ($info{width}, $info{height}) = $data =~ /frame\s+size:\s+-g\s+(\d+)x(\d+)\b/m; | ||
| ($info{fps}) = $data =~ /frame\s+rate:\s+-f\s+(\d+(?:\.\s+)?)\b/m; | ||
| ($info{audio_sample_rate}) = $data =~ /audio\s+track:.+?-e\s+(\d+)\b/m; | ||
| } | ||
| # Grab tcmplex info | ||
| elsif ($program =~ /mpgtx$/) { | ||
| my $data = `$program -i '$file'`; | ||
| ($info{width}, $info{height}, $info{fps}) = $data =~ /\bSize\s+\[(\d+)\s*x\s*(\d+)\]\s+(\d+(?:\.\d+)?)\s*fps/m; | ||
| ($info{audio_sample_rate}) = $data =~ /\b(\d+)\s*Hz/m; | ||
| } | ||
| # Return | ||
| return %info; | ||
| } | ||
|
|
||
| # Queries /proc/cpuinfo to find out how many cpu's are available on this machine | ||
| sub num_cpus { | ||
| my $cpuinfo = `cat /proc/cpuinfo`; | ||
| $num = 0; | ||
| while ($cpuinfo =~ /^processor\s*:\s*\d+/mg) { | ||
| $num++; | ||
| } | ||
| return $num; | ||
| } | ||
|
|
||
| # This subroutine forks and executes one system command - nothing fancy | ||
| sub fork_command { | ||
| my $command = shift; | ||
| # Fork and return the child's pid | ||
| my $pid = undef; | ||
| if ($pid = fork) { | ||
| return $pid | ||
| } | ||
| # $pid defined means that this is now the forked child | ||
| elsif (defined $pid) { | ||
| system($command); | ||
| # Don't forget to exit, or we'll keep going back into places that the child shouldn't play | ||
| exit(0); | ||
| } | ||
| # Couldn't fork, guess we have to quit | ||
| die "Couldn't fork: $!\n\n$command\n\n"; | ||
| } | ||
|
|
||
| sub shell_escape { | ||
| $file = shift; | ||
| $file =~ s/(["\$])/\\$1/sg; | ||
| return "\"$file\""; | ||
| } | ||
|
|
||
| sub mysql_escape { | ||
| $string = shift; | ||
| return 'NULL' unless (defined $string); | ||
| $string =~ s/'/\\'/sg; | ||
| return "'$string'"; | ||
| } | ||
|
|
||
| sub Quit { | ||
| # Allow the functions to clean up after themselves | ||
| if (@main::Functions) { | ||
| foreach $function (@main::Functions) { | ||
| $function->cleanup; | ||
| } | ||
| } | ||
| # Print a nice goodbye message, and leave | ||
| print "\nThanks for using nuvexport!\n\n"; | ||
| exit; | ||
| } | ||
|
|
||
| 1; #return true |