File Coverage

blib/lib/POE/Component/Enc/Flac.pm
Criterion Covered Total %
statement 28 80 35.0
branch 3 34 8.8
condition 14 22 63.6
subroutine 6 15 40.0
pod 2 3 66.6
total 53 154 34.4


line stmt bran cond sub pod time code
1             # FLAC 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::Flac;
9              
10 1     1   30277 use 5.008;
  1         4  
  1         41  
11 1     1   6 use strict;
  1         139  
  1         37  
12 1     1   23 use warnings;
  1         12  
  1         39  
13 1     1   5 use Carp;
  1         2  
  1         148  
14 1     1   2567 use POE qw(Wheel::Run Filter::Line Driver::SysRW);
  1         65068  
  1         6  
15              
16             our $VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
17              
18             # Create a new encoder object
19             sub new {
20 5     5 1 18359 my $class = shift;
21 5         10 my $opts = shift;
22              
23 5         22 my $self = bless({}, $class);
24              
25 5 50       41 my %opts = !defined($opts) ? () : ref($opts) ? %$opts : ($opts, @_);
    100          
26 5         38 %$self = (%$self, %opts);
27              
28 5   100     26 $self->{compression} ||= 5; # Default compression level
29 5   100     17 $self->{priority} ||= 0; # No priority delta by default
30              
31 5   100     20 $self->{parent} ||= 'main'; # Default parent
32 5   100     14 $self->{status} ||= 'status'; # Default events
33 5   100     19 $self->{error} ||= 'error';
34 5   100     14 $self->{done} ||= 'done';
35 5   100     16 $self->{warning} ||= 'warning';
36              
37 5         186 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             # Output filename is derived from input, unless specified
52 0 0         unless ($self->{output}) {
53 0           ($self->{output} = $self->{input}) =~ s/(.*)\.(.*)$/$1.flac/;
54             }
55              
56             # For posting events to the parent session. Always passes $self as
57             # the first event argument.
58             sub post_parent {
59 0     0 0   my $kernel = shift;
60 0           my $self = shift;
61 0           my $event = shift;
62              
63 0 0         $kernel->post($self->{parent}, $event, $self, @_)
64             or carp "Failed to post to '$self->{parent}': $!";
65             }
66              
67             POE::Session->create(
68             inline_states => {
69             _start => sub {
70 0     0     my ($heap, $kernel, $self) = @_[HEAP, KERNEL, ARG0];
71              
72 0           $kernel->sig(CHLD => "child"); # We must handle SIGCHLD
73              
74 0           $heap->{self} = $self;
75              
76 0           my @args; # List of arguments for encoder
77              
78 0           push @args, '--output-name="' . $self->{output} .'"';
79              
80 0 0         push @args, '-' . $self->{compression}
81             if $self->{compression};
82              
83             # The comment parameter is a list of tag-value pairs.
84             # Each list element must be passed to the encoder as a
85             # separate --tag argument.
86 0 0         if ($self->{comment}) {
87 0           foreach (@{$self->{comment}}) {
  0            
88 0           push @args, '--tag="' . $_ .'"'
89             }
90             }
91              
92             # Finally, the input file
93 0           push @args, $self->{input};
94              
95 0           $heap->{wheel} = POE::Wheel::Run->new(
96             Program => 'flac',
97             ProgramArgs => \@args,
98             Priority => $self->{priority},
99             StdioFilter => POE::Filter::Line->new(),
100             Conduit => 'pty',
101             StdoutEvent => 'wheel_stdout',
102             CloseEvent => 'wheel_done',
103             ErrorEvent => 'wheel_error',
104             );
105             },
106              
107 0     0     _stop => sub {
108             },
109              
110             close => sub {
111 0     0     delete $_[HEAP]->{wheel};
112             },
113              
114             # Handle CHLD signal. Stop the wheel if the exited child is ours.
115             child => sub {
116 0     0     my ($kernel, $heap, $signame, $child_pid, $exit_code)
117             = @_[KERNEL, HEAP, ARG0, ARG1, ARG2];
118              
119 0 0 0       if ($heap->{wheel} && $heap->{wheel}->PID() == $child_pid) {
120 0           delete $heap->{wheel};
121              
122             # If we got en exit code, the child died unexpectedly,
123             # so create a wheel-error event. otherwise the child exited
124             # normally, so create a wheel-done event.
125 0 0         if ($exit_code) {
126 0           $kernel->yield('wheel_error', $exit_code);
127             } else {
128 0           $kernel->yield('wheel_done');
129             }
130             }
131             },
132              
133             wheel_stdout => sub {
134 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
135 0           my $self = $heap->{self};
136 0           $_ = $_[ARG0];
137              
138 0 0         if (m{^ERROR: (.*)}i) {
    0          
    0          
    0          
139             # An error message has been emitted by the encoder.
140             # Remember the message for later
141 0           $self->{message} = $1;
142             } elsif (m{^WARNING: (.*)}i) {
143             # A warning message has been emitted by the encoder.
144             # Post the warning message to the parent
145 0           post_parent($kernel, $self, $self->{warning},
146             $self->{input},
147             $self->{output},
148             $1
149             );
150 0           return;
151             } elsif (m{
152             \S+:\s+ # input file name
153             (\d+)%\s+complete, # Percentage completion
154             \s+ratio=([0-9.]+) # Current compression ratio
155             }x) {
156             # We have a progress message from the compressor
157             # Post the percentage and ratio to the parent.
158 0           my ($percent, $ratio) = ($1, $2);
159              
160 0           post_parent($kernel, $self, $self->{status},
161             $self->{input},
162             $self->{output},
163             $percent, $ratio
164             );
165             } elsif (m{
166             \S+:\s+ # input file name
167             wrote\s+(\d+)\s+bytes, # Percentage completion
168             \s+ratio=([0-9.]+) # Compression ratio
169             }x) {
170             # We have a completion message from the compressor
171             # Post the percentage and ratio to the parent.
172 0           my ($size, $ratio) = ($1, $2);
173              
174 0           post_parent($kernel, $self, $self->{status},
175             $self->{input},
176             $self->{output},
177             100, $ratio
178             );
179             }
180             },
181              
182             wheel_error => sub {
183 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
184 0           my $self = $heap->{self};
185              
186 0   0       post_parent($kernel, $self, $self->{error},
187             $self->{input},
188             $self->{output},
189             $_[ARG0],
190             $self->{message} || ''
191             );
192              
193             # Remove output file: might be incomplete
194 0 0 0       $_ = $self->{output}; unlink if ($_ && -f);
  0            
195             },
196              
197             wheel_done => sub {
198 0     0     my ($kernel, $heap) = @_[KERNEL, HEAP];
199 0           my $self = $heap->{self};
200              
201             # Delete the input file if instructed
202 0 0         unlink $self->{input} if $self->{delete};
203              
204 0           post_parent($kernel, $self, $self->{done},
205             $self->{input},
206             $self->{output}
207             );
208             },
209             },
210 0           args => [$self]
211             );
212             }
213              
214             1;
215             __END__