File Coverage

blib/lib/POE/Component/Enc/Ogg.pm
Criterion Covered Total %
statement 28 93 30.1
branch 3 50 6.0
condition 14 25 56.0
subroutine 6 15 40.0
pod 2 3 66.6
total 53 186 28.4


line stmt bran cond sub pod time code
1             # Ogg Vorbis encoding component for POE
2             # Copyright (c) 2004 Steve James. All rights reserved.
3             #
4             # This library is free software; you can redistribute it and/or modify
5             # it under the same terms as Perl itself.
6             #
7              
8             package POE::Component::Enc::Ogg;
9              
10 1     1   41141 use 5.008;
  1         3  
  1         33  
11 1     1   5 use strict;
  1         2  
  1         39  
12 1     1   10 use warnings;
  1         6  
  1         194  
13 1     1   5 use Carp;
  1         1  
  1         88  
14 1     1   846 use POE qw(Wheel::Run Filter::Line Driver::SysRW);
  1         65560  
  1         7  
15              
16             our $VERSION = sprintf("%d.%02d", q$Revision: 1.5 $ =~ /(\d+)\.(\d+)/);
17              
18             # Create a new encoder object
19             sub new {
20 6     6 1 34745 my $class = shift;
21 6         15 my $opts = shift;
22              
23 6         119 my $self = bless({}, $class);
24              
25 6 50       67 my %opts = !defined($opts) ? () : ref($opts) ? %$opts : ($opts, @_);
    100          
26 6         68 %$self = (%$self, %opts);
27              
28 6   100     142 $self->{quality} ||= 3; # Default quality level of 3
29 6   100     21 $self->{priority} ||= 0; # No priority delta by default
30              
31 6   100     27 $self->{parent} ||= 'main'; # Default parent
32 6   100     21 $self->{status} ||= 'status'; # Default events
33 6   100     19 $self->{error} ||= 'error';
34 6   100     28 $self->{done} ||= 'done';
35 6   100     20 $self->{warning} ||= 'warning';
36              
37 6         25 return $self;
38             }
39              
40              
41             # Start an encoder.
42             sub enc {
43 0     0 1   my $self = shift;
44 0           my $opts = shift;
45              
46 0 0         my %opts = !defined($opts) ? () : ref($opts) ? %$opts : ($opts, @_);
    0          
47 0           %$self = (%$self, %opts);
48              
49 0 0         croak "No input file specified" unless $self->{input};
50              
51 0 0         croak "Input file does not exist: '$self->{input}'"
52             unless (-f $self->{input});
53              
54 0           $self->{input} =~ /(.*)\.(.*)$/;
55 0           my ($path, $ext) = ($1, $2);
56              
57 0 0 0       croak "Input file extension must be 'wav' or 'flac': I have '$ext'"
58             unless ($ext eq 'wav' || $ext eq 'flac');
59              
60             # Output filename is derived from input, unless specified
61 0 0         unless ($self->{output}) {
62 0           $self->{output} = "$path.ogg";
63             }
64              
65             # For posting events to the parent session. Always passes $self as
66             # the first event argument.
67             sub post_parent {
68 0     0 0   my $kernel = shift;
69 0           my $self = shift;
70 0           my $event = shift;
71              
72 0 0         $kernel->post($self->{parent}, $event, $self, @_)
73             or carp "Failed to post to '$self->{parent}': $!";
74             }
75              
76             POE::Session->create(
77             inline_states => {
78             _start => sub {
79 0     0     my ($heap, $kernel, $self) = @_[HEAP, KERNEL, ARG0];
80              
81 0           $kernel->sig(CHLD => "child"); # We must handle SIGCHLD
82              
83 0           $heap->{self} = $self;
84              
85 0           my @args; # List of arguments for encoder
86              
87 0 0         push @args, '--album="' . $self->{album} .'"'
88             if $self->{album};
89              
90 0 0         push @args, '--genre="' . $self->{genre} .'"'
91             if $self->{genre};
92              
93 0 0         push @args, '--title="' . $self->{title} .'"'
94             if $self->{title};
95              
96 0 0         push @args, '--date="' . $self->{date} .'"'
97             if $self->{date};
98              
99 0 0         push @args, '--artist="' . $self->{artist} .'"'
100             if $self->{artist};
101              
102 0           push @args, '--output="' . $self->{output} .'"';
103              
104 0 0         push @args, '--quality="' . $self->{quality} .'"'
105             if $self->{quality};
106              
107 0 0         push @args, '--tracknum="'. $self->{tracknumber}.'"'
108             if $self->{tracknumber};
109              
110             # The comment parameter is a list of tag-value pairs.
111             # Each list element must be passed to the encoder as a
112             # separate --comment argument.
113 0 0         if ($self->{comment}) {
114 0           foreach (@{$self->{comment}}) {
  0            
115 0           push @args, '--comment="' . $_ .'"'
116             }
117             }
118              
119             # Name of the encoder program we will use
120 0           my $encoder = 'oggenc';
121              
122             # We might need to use a decoder front-end pipe
123 0           my $decoder = '';
124              
125             # If the input is in flac format, use a flac decoder
126             # front-end, and pipe it to the encoder.
127             # Otherwise pass the input file name direct to the encoder
128 0 0         if ($ext eq 'flac') {
129 0           $decoder = "flac --decode --silent --stdout $self->{input} |";
130 0           push @args, '-';
131             } else {
132 0           push @args, $self->{input};
133             }
134              
135 0           $heap->{wheel} = POE::Wheel::Run->new(
136             Program => $decoder.$encoder,
137             ProgramArgs => \@args,
138             Priority => $self->{priority},
139             StdioFilter => POE::Filter::Line->new(),
140             Conduit => 'pty',
141             StdoutEvent => 'wheel_stdout',
142             CloseEvent => 'wheel_done',
143             ErrorEvent => 'wheel_error',
144             );
145             },
146              
147 0     0     _stop => sub {
148             },
149              
150             close => sub {
151 0     0     delete $_[HEAP]->{wheel};
152             },
153              
154             # Handle CHLD signal. Stop the wheel if the exited child is ours.
155             child => sub {
156 0     0     my ($kernel, $heap, $signame, $child_pid, $exit_code)
157             = @_[KERNEL, HEAP, ARG0, ARG1, ARG2];
158              
159 0 0 0       if ($heap->{wheel} && $heap->{wheel}->PID() == $child_pid) {
160 0           delete $heap->{wheel};
161              
162             # If we got en exit code, the child died unexpectedly,
163             # so create a wheel-error event. otherwise the child exited
164             # normally, so create a wheel-done event.
165 0 0         if ($exit_code) {
166 0           $kernel->yield('wheel_error', $exit_code);
167             } else {
168 0           $kernel->yield('wheel_done');
169             }
170             }
171             },
172              
173             wheel_stdout => sub {
174 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
175 0           my $self = $heap->{self};
176 0           $_ = $_[ARG0];
177              
178 0 0         if (m{^ERROR: (.*)}i) {
    0          
    0          
179             # An error message has been emitted by the encoder.
180             # Remember the message for later
181 0           $self->{message} = $1;
182             } elsif (m{^WARNING: (.*)}i) {
183             # A warning message has been emitted by the encoder.
184             # Post the warning message to the parent
185 0           post_parent($kernel, $self, $self->{warning},
186             $self->{input},
187             $self->{output},
188             $1
189             );
190 0           return;
191             } elsif (m{^
192             \s+ \[ \s+ ([0-9.]+) % \s* \]
193             \s+ \[ \s+ (\d+) m (\d+) s \s+ remaining \s* \]
194             }x) {
195             # We have a progress message from the encoder
196             # Post the percentage and number of remaining seconds
197             # to the parent.
198 0           my ($percent, $seconds) = ($1, $2 * 60 + $3);
199              
200 0           post_parent($kernel, $self, $self->{status},
201             $self->{input},
202             $self->{output},
203             $percent, $seconds
204             );
205             }
206             },
207              
208             wheel_error => sub {
209 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
210 0           my $self = $heap->{self};
211              
212 0   0       post_parent($kernel, $self, $self->{error},
213             $self->{input},
214             $self->{output},
215             $_[ARG0],
216             $self->{message} || ''
217             );
218              
219             # Remove output file: might be incomplete
220 0 0 0       $_ = $self->{output}; unlink if ($_ && -f);
  0            
221             },
222              
223             wheel_done => sub {
224 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
225 0           my $self = $heap->{self};
226              
227             # Delete the input file if instructed
228 0 0         unlink $self->{input} if $self->{delete};
229              
230 0           post_parent($kernel, $self, $self->{done},
231             $self->{input},
232             $self->{output}
233             );
234             },
235             },
236 0           args => [$self]
237             );
238             }
239              
240             1;
241             __END__