File Coverage

blib/lib/FLV/Info.pm
Criterion Covered Total %
statement 75 75 100.0
branch 20 20 100.0
condition n/a
subroutine 12 12 100.0
pod 5 5 100.0
total 112 112 100.0


line stmt bran cond sub pod time code
1             package FLV::Info;
2              
3 3     3   163245 use warnings;
  3         6  
  3         94  
4 3     3   15 use strict;
  3         4  
  3         100  
5 3     3   68 use 5.008;
  3         9  
  3         182  
6 3     3   15 use List::Util qw(max);
  3         5  
  3         375  
7 3     3   3367 use Data::Dumper;
  3         24275  
  3         239  
8              
9 3     3   1811 use FLV::File;
  3         11  
  3         2738  
10              
11             our $VERSION = '0.24';
12              
13             =for stopwords FLVTool2 interframes keyframes FFmpeg SWFs FLVs SWF FLV codec MediaLandscape
14              
15             =head1 NAME
16              
17             FLV::Info - Extract metadata from Adobe Flash Video files
18              
19             =head1 SYNOPSIS
20              
21             use FLV::Info;
22             my $reader = FLV::Info->new();
23             $reader->parse('video.flv');
24             my %info = $reader->get_info();
25             print "$info{video_count} video frames\n";
26             print $reader->report();
27              
28             =head1 DESCRIPTION
29              
30             This module reads Adobe Flash Video (FLV) files and reports metadata about
31             those files.
32              
33             =head1 LEGAL
34              
35             This work is based primarily on the file specification provided by
36             Adobe. Use of that specification is governed by terms indicated at
37             the licensing URL specified below.
38              
39             L
40              
41             =head1 LICENSE
42              
43             Copyright 2006 Clotho Advanced Media, Inc.,
44              
45             Copyright 2007-2009 Chris Dolan,
46              
47             This library is free software; you can redistribute it and/or modify it
48             under the same terms as Perl itself.
49              
50             =head1 METHODS
51              
52             =over
53              
54             =item $pkg->new()
55              
56             Creates a new instance.
57              
58             =cut
59              
60             sub new
61             {
62 11     11 1 144891 my $pkg = shift;
63              
64 11         72 my $self = bless {
65             file => undef,
66             info => undef,
67             }, $pkg;
68              
69 11         40 return $self;
70             }
71              
72             =item $self->parse($filename)
73              
74             =item $self->parse($filehandle)
75              
76             Reads the specified file. If the file does not exist or is an invalid
77             FLV stream, an exception will be thrown via croak().
78              
79             There is no return value.
80              
81             =cut
82              
83             sub parse
84             {
85 21     21 1 271352 my $self = shift;
86 21         48 my $filename = shift;
87              
88 21         62 $self->{info} = undef;
89 21         160 $self->{file} = FLV::File->new();
90 21         249 $self->{file}->parse($filename); # might throw exception
91 12         57 return;
92             }
93              
94             =item $self->get_info()
95              
96             Returns a hash with all FLV metadata. Any fields that are multivalued
97             are concatenated with a slash (C) with the most common values
98             specified first. For example, a common case is the C
99             which is often C since interframes are more
100             common than keyframes. A less common case could be C of
101             C if the FLV was mostly one-channel but had some packets
102             of two-channel audio.
103              
104             =cut
105              
106             sub get_info
107             {
108 10     10 1 30 my $self = shift;
109              
110 10 100       47 if (!$self->{info})
111             {
112 6         16 my %info;
113 6 100       29 if ($self->{file})
114             {
115 4         25 %info = $self->{file}->get_info();
116             }
117 6         32 $self->{info} = \%info;
118             }
119 10         14 return %{ $self->{info} };
  10         150  
120             }
121              
122             =item $self->report()
123              
124             Returns a summary of all FLV metadata as a string. This is a
125             human-readable version of the data returned by get_info().
126              
127             =cut
128              
129             sub report
130             {
131 4     4 1 19 my $self = shift;
132              
133 4         19 my %info = $self->get_info();
134              
135             # l = label
136             # k = key
137             # u = unit (should make sense to pluralize by appending an 's')
138             # r = key match regex
139             # p = prefix
140             # f = filter subroutine
141             my @outputs = (
142             { l => 'File name', k => 'filename', },
143             { l => 'File size', k => 'filesize', u => 'byte', },
144             {
145             l => 'Duration',
146             k => 'duration',
147             u => 'second',
148 4     4   38 f => sub { return 'about ' . ($_[0] / 1000); },
149             }, # convert millisec to sec
150 4         161 { l => 'Video', k => 'video_count', u => 'frame', },
151             { r => qr/\A video_/xms, p => q{ }, },
152             { l => 'Audio', k => 'audio_count', u => 'packet', },
153             { r => qr/\A audio_/xms, p => q{ }, },
154             { l => 'Meta', k => 'meta_count', u => 'event', },
155             { r => qr/\A meta_/xms, p => q{ }, },
156             );
157              
158             # Flag keys to ignore in regex matches
159 4 100       15 my %seen = map { $_->{k} ? ($_->{k} => 1) : () } @outputs;
  36         106  
160              
161             # Apply regex matches
162 4         23 for my $i (reverse 0 .. $#outputs)
163             {
164 36         47 my $output = $outputs[$i];
165 36 100       84 if ($output->{r})
166             {
167 12         17 my @r;
168 12         172 for my $key (grep { $_ =~ $output->{r} } sort keys %info)
  348         979  
169             {
170 104 100       204 next if ($seen{$key});
171 92         365 (my $label = $key) =~ s/$output->{r}//xms;
172 92         332 push @r, { l => $output->{p} . $label, k => $key };
173             }
174 12         85 splice @outputs, $i, 1, @r;
175             }
176             }
177              
178             # Get the length of the longest label so we can pad the rest
179 4         13 my $max_label_length = max map { length $_->{l} } @outputs;
  116         192  
180              
181             # Accumulate output string here
182 4         19 my $out = q{};
183 4         9 for my $output (@outputs)
184             {
185 116         175 my $value = $info{ $output->{k} };
186 116 100       219 next if (!$value);
187              
188             # Apply filter if any
189 108 100       201 if ($output->{f})
190             {
191 4         14 $value = $output->{f}->($value);
192             }
193              
194             # Append unit(s) if any
195 108 100       263 if ($output->{u})
    100          
196             {
197 20 100       56 $value .= q{ } . $output->{u} . ('1' eq $value ? q{} : 's');
198             }
199             elsif (ref $value)
200             {
201              
202             # Make multiline output for a complex data structure
203 2         12 my $d = Data::Dumper->new([$value], ['VAR']);
204 2         56 (my $label = $output->{l}) =~ s/\S+/ >>>/xms;
205 2         5 my $varprefix = '$VAR = '; ##no critic(InterpolationOfMetachars)
206              
207             # "+2" is for 2 spaces in normal output
208 2         6 my $padding
209             = q{ } x ($max_label_length + 2 - length $label . $varprefix);
210              
211 2         15 $d->Pad($label . $padding);
212 2         30 $value = $d->Dump();
213 2         267 $value =~ s/\A\s*>>>\s*\Q$varprefix\E//xms;
214 2         52 $value =~ s/;\s+\z//xms;
215             }
216              
217 108         146 my $label = $output->{l};
218 108         166 my $padding = q{ } x ($max_label_length - length $label);
219              
220 108         384 $out .= "$label $padding $value\n";
221             }
222              
223 4         112 return $out;
224             }
225              
226             =item $self->get_file()
227              
228             Returns the FLV::File instance. This will be C until you call parse().
229              
230             =cut
231              
232             sub get_file
233             {
234 10     10 1 1273 my $self = shift;
235 10         41 return $self->{file};
236             }
237              
238             1;
239              
240             __END__