Skip to content

Commit 27d462c

Browse files
committed
first mencoder export module (thanks Icemaan) -- I can't get it to work,
but at least it's a start. I've also done some other poking around to improve the nuvexportrc stuff.
1 parent 73e1ebb commit 27d462c

File tree

8 files changed

+501
-44
lines changed

8 files changed

+501
-44
lines changed

trunk/Makefile

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ MANS = man/nuvexport.1 \
1111
EXPORT_MODULES = export/generic.pm \
1212
export/ffmpeg.pm \
1313
export/transcode.pm \
14+
export/mencoder.pm \
1415
export/MPEG2_cut.pm \
1516
export/NUV_SQL.pm \
1617
export/transcode/DVCD.pm \
@@ -25,9 +26,11 @@ EXPORT_MODULES = export/generic.pm \
2526
export/ffmpeg/SVCD.pm \
2627
export/ffmpeg/DVCD.pm \
2728
export/ffmpeg/DVD.pm \
28-
export/ffmpeg/VCD.pm
29+
export/ffmpeg/VCD.pm \
30+
export/mencoder/XviD.pm
2931
MODULE_SUBDIRS = transcode \
30-
ffmpeg
32+
ffmpeg \
33+
mencoder
3134
MYTHTV_MODULES = mythtv/nuvinfo.pm \
3235
mythtv/db.pm \
3336
mythtv/recordings.pm

trunk/export/generic.pm

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/perl -w
2-
#Last Updated: 2005.02.25 (xris)
2+
#Last Updated: 2005.02.28 (xris)
33
#
44
# generic.pm
55
#
@@ -213,7 +213,7 @@ package export::generic;
213213
if ($data && length $data > 0) {
214214
print $write $data;
215215
}
216-
# Sleep for 1/100 second so we don't go too fast and annoy the cpu,
216+
# Sleep for 1/20 second so we don't go too fast and annoy the cpu,
217217
# but still read fast enough that transcode won't slow down, either.
218218
usleep(5000);
219219
}

trunk/export/mencoder.pm

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
#!/usr/bin/perl -w
2+
#Last Updated: 2005.02.27 (icemaann)
3+
#
4+
# mencoder.pm
5+
#
6+
# routines for setting up mencoder
7+
#
8+
9+
package export::mencoder;
10+
use base 'export::generic';
11+
12+
use export::generic;
13+
14+
use Time::HiRes qw(usleep);
15+
use POSIX;
16+
17+
use nuv_export::shared_utils;
18+
use nuv_export::cli;
19+
use nuv_export::ui;
20+
use mythtv::recordings;
21+
22+
# Load the following extra parameters from the commandline
23+
add_arg('zoom_filter:s', 'Which zoom filter to use.');
24+
25+
# This superclass defines several object variables:
26+
#
27+
# path (defined by generic)
28+
# use_cutlist (defined by generic)
29+
# noise_reduction
30+
# deinterlace
31+
# crop
32+
# zoom_filter
33+
#
34+
35+
# Check for mencoder
36+
sub init_mencoder {
37+
my $self = shift;
38+
# Make sure we have mencoder
39+
find_program('mencoder')
40+
or push @{$self->{'errors'}}, 'You need mencoder to use this exporter.';
41+
}
42+
43+
# Gather data for mencoder
44+
sub gather_settings {
45+
my $self = shift;
46+
my $skip = shift;
47+
# Gather generic settings
48+
$self->SUPER::gather_settings($skip ? $skip - 1 : 0);
49+
return if ($skip);
50+
# Zoom Filter
51+
if (defined arg('zoom_filter')) {
52+
if (!arg('zoom_filter')) {
53+
$self->{'zoom_filter'} = 'B_spline';
54+
}
55+
elsif (arg('zoom_filter') =~ /^(?:Lanczos3|Bell|Box|Mitchell|Hermite|B_spline|Triangle)$/) {
56+
$self->{'zoom_filter'} = arg('zoom_filter');
57+
}
58+
else {
59+
die "Unknown zoom_filter: ".arg('zoom_filter')."\n";
60+
}
61+
}
62+
}
63+
64+
#Fix/build mencoder filter chain
65+
sub build_vop_line{
66+
my $cmdline = shift;
67+
my $vop = '';
68+
while($cmdline =~ m/.*?-vop\s+([^\s]+)\s/gs){
69+
$vop .= ",$1";
70+
}
71+
$vop =~ s/^,+//;
72+
$vop =~ s/,+$//;
73+
$cmdline =~ s/-vop\s+[^\s]+\s/ /g;
74+
$cmdline .= " -vop $vop ";
75+
return $cmdline;
76+
}
77+
78+
79+
sub export {
80+
my $self = shift;
81+
my $episode = shift;
82+
my $suffix = (shift or '');
83+
my $skip_audio = shift;
84+
# Init the commands
85+
my $mencoder = '';
86+
my $mythtranscode = '';
87+
# Load nuv info
88+
load_finfo($episode);
89+
# Start the mencoder command
90+
$mencoder = "$NICE mencoder";
91+
# Import aspect ratio
92+
if ($episode->{'finfo'}{'aspect'}) {
93+
$mencoder .= ' -aspect ';
94+
if ($episode->{'finfo'}{'aspect'} == 1 || $episode->{'finfo'}{'aspect'} eq '1:1') {
95+
$mencoder .= '1:1';
96+
}
97+
elsif ($episode->{'finfo'}{'aspect'} =~ m/^1.3/ || $episode->{'finfo'}{'aspect'} eq '4:3') {
98+
$mencoder .= '4:3';
99+
}
100+
elsif ($episode->{'finfo'}{'aspect'} =~ m/^1.7/ || $episode->{'finfo'}{'aspect'} eq '16:9') {
101+
$mencoder .= '16:9';
102+
}
103+
elsif ($episode->{'finfo'}{'aspect'} == 2.21 || $episode->{'finfo'}{'aspect'} eq '2.21:1') {
104+
$mencoder .= '2.21:1';
105+
}
106+
}
107+
# Not an mpeg mencoder can not do cutlists (from what I can tell..)
108+
unless ($episode->{'finfo'}{'is_mpeg'} && !$self->{'use_cutlist'}) {
109+
# swap red/blue -- used with svcd, need to see if it's needed everywhere
110+
# $mencoder .= ' -vop rgb2bgr '; #this is broken in mencoder 1.0preX
111+
# Set up the fifo dirs?
112+
if (-e "/tmp/fifodir_$$/vidout" || -e "/tmp/fifodir_$$/audout") {
113+
die "Possibly stale mythtranscode fifo's in /tmp/fifodir_$$/.\nPlease remove them before running nuvexport.\n\n";
114+
}
115+
# Here, we have to fork off a copy of mythtranscode (need to use --fifosync with mencoder? needs testing)
116+
$mythtranscode = "$NICE mythtranscode --showprogress -p autodetect -c $episode->{'channel'} -s $episode->{'start_time_sep'} -f \"/tmp/fifodir_$$/\"";
117+
# On no-audio encodes, we need to do something to keep mythtranscode's audio buffers from filling up available RAM
118+
# $mythtranscode .= ' --fifosync' if ($skip_audio);
119+
# let mythtranscode handle the cutlist
120+
$mythtranscode .= ' --honorcutlist' if ($self->{'use_cutlist'});
121+
}
122+
# Figure out the input files
123+
if ($episode->{'finfo'}{'is_mpeg'} && !$self->{'use_cutlist'}) {
124+
$mencoder .= " -idx $episode->{'filename'} ";
125+
}
126+
else {
127+
$mencoder .= " -noskip -idx /tmp/fifodir_$$/vidout -audiofile /tmp/fifodir_$$/audout "
128+
.' -rawvideo'
129+
.' on:w='.$episode->{'finfo'}{'width'}.':h='.$episode->{'finfo'}{'height'}
130+
.':fps='.$episode->{'finfo'}{'fps'}
131+
.' -rawaudio on:rate='.$episode->{'finfo'}{'audio_sample_rate'}.':channels='.$episode->{'finfo'}{'audio_channels'}
132+
;
133+
}
134+
# NOTE: this comes before the standard filters below, because
135+
# mencoder applies filters in reverse
136+
# Add any additional settings from the child module
137+
$mencoder .= ' '.$self->{'mencoder_xtra'};
138+
# Crop?
139+
if ($self->{'crop'}) {
140+
my $w = sprintf('%.0f', .98 * $episode->{'finfo'}{'width'});
141+
my $h = sprintf('%.0f', .98 * $episode->{'finfo'}{'height'});
142+
$w-- if ($w > 0 && $w % 2); # mencoder freaks out if these are odd numbers (does it?)
143+
$h-- if ($h > 0 && $h % 2);
144+
$mencoder .= " -vop crop=$w:$h " if ($h || $w);
145+
}
146+
# Use the cutlist? (only for mpeg files -- nuv files are handled by mythtranscode)
147+
# Can we cut with mencoder?
148+
# Filters (remember, mencoder reads these in reverse order (so deint should be last if used)
149+
# Normally you would do -vop filter1=<val>,filter2=<val>,lavcdeint...
150+
if ($self->{'noise_reduction'}) {
151+
$mencoder .= " -vop denoise3d";
152+
}
153+
if ($self->{'deinterlace'}) {
154+
$mencoder .= " -vop lavcdeint";
155+
#smartyuv|smartdeinter|dilyuvmmx
156+
}
157+
# Output directory set to null means the first pass of a multipass
158+
if (!$self->{'path'} || $self->{'path'} =~ /^\/dev\/null\b/) {
159+
$mencoder .= ' -o /dev/null';
160+
}
161+
# Add the output filename
162+
else {
163+
$mencoder .= ' -o '.shell_escape($self->get_outfile($episode, $suffix));
164+
}
165+
# mencoder pids
166+
my ($mythtrans_pid, $mencoder_pid, $mythtrans_h, $mencoder_h);
167+
# Set up and run mythtranscode?
168+
if ($mythtranscode) {
169+
# Create a directory for mythtranscode's fifo's
170+
mkdir("/tmp/fifodir_$$/", 0755) or die "Can't create /tmp/fifodir_$$/: $!\n\n";
171+
($mythtrans_pid, $mythtrans_h) = fork_command("$mythtranscode 2>&1");
172+
$children{$mythtrans_pid} = 'mythtranscode' if ($mythtrans_pid);
173+
fifos_wait("/tmp/fifodir_$$/");
174+
push @tmpfiles, "/tmp/fifodir_$$", "/tmp/fifodir_$$/audout", "/tmp/fifodir_$$/vidout";
175+
}
176+
#Fix -vop options before we execute mencoder
177+
$mencoder = build_vop_line($mencoder);
178+
# Execute mencoder
179+
print "Starting mencoder.\n" unless ($DEBUG);
180+
($mencoder_pid, $mencoder_h) = fork_command("$mencoder 2>&1");
181+
$children{$mencoder_pid} = 'mencoder' if ($mencoder_pid);
182+
# Get ready to count the frames that have been processed
183+
my ($frames, $fps);
184+
$frames = 0;
185+
$fps = 0.0;
186+
my $total_frames = $episode->{'lastgop'} * (($episode->{'finfo'}{'fps'} =~ /^2(?:5|4\.9)/) ? 12 : 15);
187+
# Keep track of any warnings
188+
my $warnings = '';
189+
my $death_timer = 0;
190+
my $last_death = '';
191+
# Wait for child processes to finish
192+
while ((keys %children) > 0) {
193+
my $l;
194+
my $pct;
195+
# Show progress
196+
if ($frames && $total_frames) {
197+
$pct = sprintf('%.2f', 100 * $frames / $total_frames);
198+
}
199+
else {
200+
$pct = "0.00";
201+
}
202+
print "\rprocessed: $frames of $total_frames frames ($pct\%), $fps fps ";
203+
# Read from the mencoder handle
204+
while (has_data($mencoder_h) and $l = <$mencoder_h>) {
205+
if ($l =~ /^Pos:.*?(\d+)f.*?\(.*?(\d+)fps/) {
206+
$frames = int($1);
207+
$fps = $2;
208+
}
209+
# Look for error messages
210+
elsif ($l =~ m/\[mencoder\] warning/) {
211+
$warnings .= $l;
212+
}
213+
elsif ($l =~ m/\[mencoder\] critical/) {
214+
$warnings .= $l;
215+
die "\nmencoder had critical errors:\n\n$warnings";
216+
}
217+
}
218+
# Read from the mythtranscode handle?
219+
if ($mythtranscode && $mythtrans_pid) {
220+
while (has_data($mythtrans_h) and $l = <$mythtrans_h>) {
221+
if ($l =~ /Processed:\s*(\d+)\s*of\s*(\d+)\s*frames\s*\((\d+)\s*seconds\)/) {
222+
#$frames = int($1);
223+
$total_frames = $2;
224+
}
225+
}
226+
}
227+
# Has the deathtimer been started? Stick around for awhile, but not too long
228+
if ($death_timer > 0 && time() - $death_timer > 30) {
229+
$str = "\n\n$last_death died early.";
230+
if ($warnings) {
231+
$str .= "See mencoder warnings:\n\n$warnings";
232+
}
233+
else {
234+
$str .= "Please use the --debug option to figure out what went wrong.\n\n";
235+
}
236+
die $str;
237+
}
238+
# The pid?
239+
$pid = waitpid(-1, &WNOHANG);
240+
if ($children{$pid}) {
241+
print "\n$children{$pid} finished.\n" unless ($DEBUG);
242+
$last_death = $children{$pid};
243+
$death_timer = time();
244+
delete $children{$pid};
245+
}
246+
# Sleep for 1/100 second so we don't go too fast and annoy the cpu
247+
usleep(100000);
248+
}
249+
# Remove the fifodir? (in case we're doing multipass, so we don't generate errors on the next time through)
250+
if ($mythtranscode) {
251+
unlink "/tmp/fifodir_$$/audout", "/tmp/fifodir_$$/vidout";
252+
rmdir "/tmp/fifodir_$$";
253+
}
254+
}
255+
256+
257+
# Return true
258+
1;
259+
260+
# vim:ts=4:sw=4:ai:et:si:sts=4

0 commit comments

Comments
 (0)