File Coverage

blib/lib/Video/Manip.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package Video::Manip;
2              
3             #XXX DataDumper has problems with strict
4             #use strict;
5              
6 2     2   65111 use vars qw($VERSION @EXPORT @EXPORT_OK %EXPORT_TAGS);
  2         5  
  2         218  
7             $VERSION = 0.01;
8 2     2   11 use base qw(Exporter);
  2         4  
  2         314  
9             @EXPORT = qw(new use extract);
10             @EXPORT_OK = qw(check getbdys buildcool match redefineenvl reconsevents selectframes);
11             %EXPORT_TAGS = ( all => [@EXPORT_OK] );
12              
13 2     2   1192 use Video::Event::Manual;
  2         5  
  2         53  
14 2     2   1079 use Video::Function;
  2         7  
  2         87  
15 2     2   1510 use Video::FindEvent::Manual;
  0            
  0            
16             use Data::Dumper;
17             use XML::Simple; #do this in findevent::manual or that here to avoid redundancy
18              
19             sub new {
20             my ($class, %args) = @_;
21              
22             my %options = (
23             file => '',
24             rawvideo => '',
25             rawaudio => '',
26             dovideo => '1',
27             doaudio => '1',
28             afps => '44100',
29             vfps => '25',
30             progid => '',
31             writefile => '', #write to file named
32             writedb => '', #write to db named
33             progid => '', #program id
34             algoid => '', #algorithm id
35             genshell => '', #generate shell script, don't actually copy frames
36             actuallydo => '', #copy appropriate frames; must specify sourcedir and destdir also
37             sourcedir => '', #copy video frames from
38             destdir => '', #copy video frames to
39              
40             resolution => '4', #number of parts in a second
41             desiredlength => '', #0 gives longest possible
42             verbose => '0', #integer 0 (none) - 9 (all messages)
43             );
44              
45             foreach my $option (keys %args) {
46             warn __PACKAGE__ . ": unexpected: $option"
47             if (not defined $options{$option});
48             die __PACKAGE__ . ": must specify value as $option => value"
49             if (not $args{$option});
50             $options{$option} = $args{$option};
51             }
52              
53             my $self = bless \%options, ref($class) || $class;
54             foreach my $key (keys %options) {
55             $self->{$key} = $options{$key};
56             }
57             #erm.
58             $self->{'options'} = \%options;
59             return $self;
60             }
61              
62             sub check {
63             # verify Video::FindEvent::* modules load without errors
64             my ($self, $algorithms) = @_;
65             ref($algorithms) eq 'HASH'
66             or die __PACKAGE__ . ": error in algorithms hash";
67             foreach my $algo (keys %$algorithms) {
68             my $module = "Video::FindEvent::" . $algo;
69             check_h($module);
70             }
71             return 1;
72             }
73            
74             sub check_h {
75             my ($module) = @_;
76             eval { "require $module"; }
77             #require $module
78             or die __PACKAGE__ . ": problem with module $module";
79             return 1;
80             }
81              
82            
83             sub use {
84             my ($self, $algorithms) = @_;
85             ref($algorithms) eq 'HASH'
86             or die __PACKAGE__ . ": error in algorithms hash";
87              
88             foreach my $algo (keys %$algorithms) {
89             foreach my $option (keys %{$self->{'options'}}) {
90             $$algorithms{$algo}{$option} = $self->{'options'}{$option}
91             if ($self->{'options'}{$option});
92             }
93            
94             #make sure all is good with module, then require it
95             my $module = "Video::FindEvent::" . $algo;
96             check_h($module);
97             eval { eval "require $module" } or die __PACKAGE__ . ": poof";
98              
99             #build new module with options present in algorithms hash
100             $self->{'algo'}{$algo} = $module->new($$algorithms{$algo});
101             my $refcl = ref($self->{'algo'}{$algo});
102             ref($self->{'algo'}{$algo})
103             or die __PACKAGE__ . ": problem with module $module constructor";
104             }
105             return 1;
106             }
107              
108             sub findevents {
109             my ($self, %args) = @_;
110              
111             #we only want to fork to run the event finding algorithms if we are
112             #running more than one algorithm
113             my $numberalgo = scalar values %{$self->{'algo'}};
114              
115             if ($numberalgo == 1) {
116             foreach my $algo (values %{$self->{'algo'}}) {
117             $algo->findevents(%args);
118             }
119             }
120             else {
121             foreach my $algo (values %{$self->{'algo'}}) {
122             my $pid = fork;
123             if (!$pid) {
124             $algo->findevents(%args);
125             exit 0;
126             }
127             }
128             }
129             return 1;
130             }
131              
132             sub getbdys {
133             my ($self) = @_;
134             #X should not have to rebuild @events here
135             my @events = $self->{'events'} ? @{$self->{'events'}}
136             : @{$self->reconsevents()};
137             my @bdys;
138             foreach my $event (sort { $a->{'time'} <=> $b->{'time'} } @events) {
139             push @bdys, $event->{'time'};
140             }
141             my @sorted = sort { $a <=> $b } @bdys;
142             return \@sorted;
143             }
144              
145             sub buildcool {
146             my ($self, $length, $searchterm, @tags) = @_;
147             my @events = $self->{'events'} ? @{$self->{'events'}}
148             : @{$self->reconsevents()};
149             my $last = $events[-1];
150             unless ($length) {
151             $length = $last->{'time'} if $last->{'time'};
152             $length = $last->{'endtime'} if defined $last->{'endtime'};
153             }
154            
155             my $resolution = $self->{'resolution'};
156             my $desiredlength = $self->{'desiredlength'};
157              
158             my $cool = new Video::Function($resolution, $length);
159             foreach my $event (@events) {
160             if ($searchterm eq '-all') {
161             $cool = $event->buildcool($cool, $length);
162             }
163             else {
164             if ($event->matches($searchterm, @tags)) {
165             $cool = $event->buildcool($cool, $length);
166             }
167             }
168             }
169             my $sum = $cool->sum();
170             if ($self->{'verbose'} > 5) {
171             print "sum: $sum\n";
172             print "length: $length\n";
173             }
174             $desiredlength = $length unless $desiredlength;
175             $cool->zero();
176             $cool->compress($desiredlength, "simple");
177             $cool->truncate();
178             if ($self->{'verbose'} > 5) {
179             print $cool->show();
180             }
181             return $cool;
182             }
183              
184              
185             sub extract {
186             my ($self, $searchterm, @tag) = @_;
187             my $length = 0; # means as long as necessary
188             my $cool = $self->buildcool($length, $searchterm, @tag);
189              
190             #XXX these should be options
191             my $dovideo = 1;
192             my $doaudio = 0;
193              
194             $self->selectframes($cool, $dovideo, $doaudio, $self->{'vfps'}, $self->{'afps'});
195             return 1;
196             }
197              
198             sub match {
199             my ($self, $event, $searchterm, @tags) = @_;
200             return 1 unless $searchterm;
201             return 1 unless @tags;
202             my %hash = %$event;
203             foreach my $key (keys %hash) {
204             foreach my $tag (@tags) {
205             if ($key eq $tag) {
206             if ($searchterm eq $hash{$key}) {
207             return 1;
208             }
209             else {
210             return 0;
211             }
212             }
213             }
214             }
215             return 0;
216             }
217              
218             sub redefineenvl {
219             #behaves like reconsevents, but reads in new config file
220             my ($self, $newconfig) = @_;
221              
222             my @events = $self->{'events'} ? @{$self->{'events'}}
223             : @{$self->reconsevents()};
224             my $config = XMLin($newconfig,
225             keyattr => 'key',
226             forcearray => 0,
227             contentkey => '-command',
228             keeproot => 0,
229             );
230             $config = Video::FindEvent::Manual::abusexml($config);
231            
232              
233             foreach my $event (@events) {
234             #match event against $config and reset envelope
235             foreach my $key (%$config) {
236             if ($event->{'name'} eq $$config{$key}{'name'}) {
237             $event->{'envelope'} = $$config{$key}{'envl'};
238             #do we want to change other properties too?
239             }
240             }
241             }
242             return \@events;
243             }
244              
245              
246             sub reconsevents {
247             #this should talk to the database too.
248             my ($self) = @_;
249              
250             if ($self->{'writefile'} ne '') {
251             my $data = "";
252             my $eventarray = $self->{'writefile'} . ".obj";
253             #? do we always want to check config file for new envelopes?
254             open FH, "+<$eventarray" or die "can't open $eventarray: $!";
255             while () {
256             $data .= $_;
257             }
258             $Data::Dump::Purity = 1;
259             $Data::Dumper::Deepcopy = 1;
260             my $ref = eval($data);
261             $self->{'events'} = $ref if $ref;
262             return $ref if $ref;
263             die __PACKAGE__ . ": can't recons events";
264             }
265             if ($self->{'writedb'} ne '') {
266             die __PACKAGE__ . ": sorry, not implemented. Can't reconstruct events from database. Yet.";
267             }
268              
269             }
270              
271             sub selectframes {
272             #(this was compress.pl)
273             #determine which frames to include in summary based on coolness function
274             my ($self, $cool, $dovideo, $doaudio, $vfps, $afps) = @_;
275             my $resolution = $cool->{'resolution'};
276             my $length = $cool->{'length'};
277             my $destdir = $self->{'destdir'};
278             my $sourcedir = $self->{'sourcedir'};
279              
280              
281             #add trailing / if necessary
282             $sourcedir =~ s/(.*)/$1\// unless ($sourcedir =~ /^.*\/$/);
283             $destdir =~ s/(.*)/$1\// unless ($destdir =~ /^.*\/$/);
284              
285              
286             #number of video frames played in one second
287             #used to calculate how many audio frames to play
288             my $framecounter = 0;
289              
290             #counts total number of frames copied
291             my $copiedframe = 0;
292              
293             #used to adjust volume over one second
294             my $avecool = 0; #over one second
295              
296             #XXX these should be options
297             my $fileprefix = "frame";
298             my $filesuffix = ".jpg";
299              
300             my $actuallydo = 0;
301             $actuallydo = $self->{'actuallydo'} if $self->{'actuallydo'};
302             my $genshell = 0;
303             $genshell = $self->{'genshell'} if $self->{'genshell'};
304              
305             for (my $second=0; $second<$length; $second++) {
306             $framecounter = 0;
307             $avecool = 0;
308             for (my $fraction=0; $fraction<1; $fraction+=(1/$resolution)) {
309             my $vpnf = 0;
310             $avecool = ${$cool->{'function'}}{$second+$fraction};
311             for (my $vf=1; $vf<=($vfps/$resolution); $vf++) {
312             #decide if we should play the next frame
313             next if not defined ${$cool->{'function'}}{$second+$fraction};
314             $vpnf += ${$cool->{'function'}}{$second+$fraction};
315             if ($vpnf >= 1) {
316             my $framenumber = $second*$vfps +
317             $fraction*$vfps +
318             $vf;
319             $framenumber = sprintf("%09d", $framenumber);
320             $copiedframe = sprintf("%09d", $copiedframe);
321             my $infile = $fileprefix . $framenumber . $filesuffix;
322             my $outfile = $fileprefix . $copiedframe . $filesuffix;
323             my $command = "cp " . $sourcedir . $infile . " " . $destdir . $outfile;
324             system($command) if $actuallydo;
325             print "$command\n" if $genshell;
326             $vpnf--;
327             $framecounter++;
328             $copiedframe++;
329             }
330             }
331             }
332             }
333             }
334              
335             1;