| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package File::Headerinfo; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
30794
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
40
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use Carp; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
109
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
5
|
use vars qw( $VERSION $AUTOLOAD ); |
|
|
1
|
|
|
|
|
12
|
|
|
|
1
|
|
|
|
|
777
|
|
|
7
|
|
|
|
|
|
|
$VERSION = '0.03'; |
|
8
|
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
|
10
|
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
File::Headerinfo - a general purpose extractor of header information from media files. Can handle most image, video and audio file types. |
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use File::Headerinfo; |
|
16
|
|
|
|
|
|
|
my $headerdata = File::Headerinfo->read('/path/to/file.ext'); |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
or |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $reader = File::Headerinfo->new; |
|
21
|
|
|
|
|
|
|
my %filedata = map { $_ => $reader->read($_) } @files; |
|
22
|
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
I is little more than a collection of wrappers around existing modules like MP3::Info and Image::Size. It gathers them all behind a simple, friendly interface and offers an easy way to get header information from almost any kind of media file. |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
The main Headerinfo modules is a very simple factory class: the real work is done by a set of dedicated subclasses each able to read a different sort of file. A dispatch table in the factory class maps each file suffix onto the subclass that can read that kind of file. |
|
28
|
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
In normal use that minor complexity is hidden from view: all you have to do is pass a file to the read() method and it will do the right thing. |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 METHODS |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 read( $path, $type) |
|
34
|
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
Examines the file we have been supplied with, creates an object of the appropriate class and tells it to examine the file. |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
Loading of the subclasses is deferred until it's necessary, as some of them use quite chunky modules to do the file-reading work for them. |
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
You can force a file to be treated as a particular type by supplying the appropriate three or four letter file suffix as a second parameter: |
|
40
|
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
my $fileinfo = File::Headerinfo->read('/path/to/file', 'mpeg'); |
|
42
|
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub read { |
|
46
|
5
|
|
|
5
|
1
|
16457
|
my $base = shift; |
|
47
|
5
|
|
|
|
|
32
|
my $class = $base->subclass(@_); |
|
48
|
5
|
50
|
|
|
|
19
|
return unless $class; |
|
49
|
5
|
|
|
|
|
550
|
eval "require $class;"; |
|
50
|
5
|
100
|
|
|
|
513
|
Carp::croak($@) if $@; |
|
51
|
4
|
|
|
|
|
53
|
my $self = $class->new(@_); |
|
52
|
4
|
|
|
|
|
18
|
$self->parse_file; |
|
53
|
4
|
|
|
|
|
35
|
return $self->report; |
|
54
|
|
|
|
|
|
|
} |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head2 new() |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
A very simple constructor that is rarely called directly. It is inherited by all the specific-format subclasses, so you could do this: |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
my $reader = File::Headerinfo::SWF->new('/path/to/file.swf'); |
|
61
|
|
|
|
|
|
|
$reader->parse_file; |
|
62
|
|
|
|
|
|
|
my $report = $reader->report; |
|
63
|
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
but needn't bother, since the same thing is achieved by writing: |
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $report = File::Headerinfo->read('/path/to/file.swf'); |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=cut |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
sub new { |
|
71
|
5
|
|
|
5
|
1
|
33
|
my $class = shift; |
|
72
|
5
|
|
|
|
|
36
|
return bless { |
|
73
|
|
|
|
|
|
|
_path => $_[0], |
|
74
|
|
|
|
|
|
|
}, $class; |
|
75
|
|
|
|
|
|
|
} |
|
76
|
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
=head2 path() |
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
Gets or sets the full path to the file we're trying to examine. |
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=cut |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
sub path { |
|
84
|
4
|
|
|
4
|
1
|
72
|
my $self = shift; |
|
85
|
4
|
50
|
|
|
|
15
|
return $self->{_path} = $_[0] if @_; |
|
86
|
4
|
|
|
|
|
40
|
return $self->{_path}; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=head2 subclass( $path, $type ) |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
Identifies the subclass (or other class) that is meant to read files of the type supplied. |
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=cut |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
sub subclass { |
|
96
|
5
|
|
|
5
|
1
|
33
|
my ($base, $path, $type) = @_; |
|
97
|
5
|
50
|
|
|
|
55
|
return unless $path; |
|
98
|
5
|
|
|
|
|
28
|
my $media_classes = $base->media_classes; |
|
99
|
5
|
50
|
33
|
|
|
43
|
my $class = $media_classes->{ $type || _suffix($path) } or $base->default_media_class; |
|
100
|
5
|
|
|
|
|
43
|
return $class; |
|
101
|
|
|
|
|
|
|
} |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=head2 _suffix( $path ) |
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Not a method: just a useful helper. Returns the file suffix, with no dot. Useful if we have no other way of identifying the file type. |
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
=cut |
|
108
|
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
sub _suffix { |
|
110
|
5
|
|
|
5
|
|
9
|
my $path = shift; |
|
111
|
5
|
|
|
|
|
33
|
$path =~ /\.(\w+)$/; |
|
112
|
5
|
|
|
|
|
61
|
return $1; |
|
113
|
|
|
|
|
|
|
} |
|
114
|
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
=head2 parse_file() |
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
Each format-specific subclass has its own way of parsing the media file. This is just a placeholder. |
|
118
|
|
|
|
|
|
|
|
|
119
|
|
|
|
|
|
|
=cut |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub parse_file { |
|
122
|
0
|
|
|
0
|
1
|
0
|
Carp::croak("File::Headerinfo::parse_file should not be called directly: use the right subclass for your file."); |
|
123
|
|
|
|
|
|
|
} |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=head2 media_classes() |
|
126
|
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
Returns a hashref that maps media types onto class names. The types can come from stored information or from the suffix of the file. |
|
128
|
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
=cut |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
sub media_classes { |
|
132
|
|
|
|
|
|
|
return { |
|
133
|
5
|
|
|
5
|
1
|
175
|
gif => 'File::Headerinfo::Image', |
|
134
|
|
|
|
|
|
|
jpg => 'File::Headerinfo::Image', |
|
135
|
|
|
|
|
|
|
jpeg => 'File::Headerinfo::Image', |
|
136
|
|
|
|
|
|
|
png => 'File::Headerinfo::Image', |
|
137
|
|
|
|
|
|
|
mng => 'File::Headerinfo::Image', |
|
138
|
|
|
|
|
|
|
xbm => 'File::Headerinfo::Image', |
|
139
|
|
|
|
|
|
|
xpm => 'File::Headerinfo::Image', |
|
140
|
|
|
|
|
|
|
tif => 'File::Headerinfo::Image', |
|
141
|
|
|
|
|
|
|
tiff => 'File::Headerinfo::Image', |
|
142
|
|
|
|
|
|
|
psd => 'File::Headerinfo::Image', |
|
143
|
|
|
|
|
|
|
ppm => 'File::Headerinfo::Image', |
|
144
|
|
|
|
|
|
|
mp3 => 'File::Headerinfo::MP3', |
|
145
|
|
|
|
|
|
|
wav => 'File::Headerinfo::WAV', |
|
146
|
|
|
|
|
|
|
swf => 'File::Headerinfo::SWF', |
|
147
|
|
|
|
|
|
|
mov => 'File::Headerinfo::Video', |
|
148
|
|
|
|
|
|
|
moov => 'File::Headerinfo::Video', |
|
149
|
|
|
|
|
|
|
aiff => 'File::Headerinfo::Video', |
|
150
|
|
|
|
|
|
|
mpeg => 'File::Headerinfo::Video', |
|
151
|
|
|
|
|
|
|
mpg => 'File::Headerinfo::Video', |
|
152
|
|
|
|
|
|
|
asf => 'File::Headerinfo::Video', |
|
153
|
|
|
|
|
|
|
avi => 'File::Headerinfo::Video', |
|
154
|
|
|
|
|
|
|
divx => 'File::Headerinfo::Video', |
|
155
|
|
|
|
|
|
|
dvx => 'File::Headerinfo::Video', |
|
156
|
|
|
|
|
|
|
}; |
|
157
|
|
|
|
|
|
|
} |
|
158
|
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head2 default_media_class() |
|
160
|
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
returns the class name we'll try to use if we can't think of anything else. |
|
162
|
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
=cut |
|
164
|
|
|
|
|
|
|
|
|
165
|
0
|
|
|
0
|
1
|
0
|
sub default_media_class { 'File::Headerinfo::Video' } |
|
166
|
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=head2 fields() |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Defines the list of parameters that will be provided by each subclass, which is currently: height, width, duration, filetype, fps, filesize, freq, datarate, vcodec, metadata. |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
This list is used by AUTOLOAD to create get and set methods, and by report to build its hash of discovered values. It can be overridden by the format-specific subclass if it needs to be extended, but no harm will come from having unused fields here. |
|
172
|
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=cut |
|
174
|
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
sub fields { |
|
176
|
67
|
|
|
67
|
1
|
206
|
return qw(height width duration filetype freq fps filesize datarate vcodec metadata version); |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
=head2 allowed_field( $fieldname ) |
|
180
|
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
Returns true if the supplied value is in the list of allowed fields. |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
=cut |
|
184
|
|
|
|
|
|
|
|
|
185
|
|
|
|
|
|
|
sub allowed_field { |
|
186
|
63
|
|
|
63
|
1
|
93
|
my ($self, $f) = @_; |
|
187
|
63
|
|
|
|
|
3548
|
my %fields = map {$_ => 1 } $self->fields; |
|
|
693
|
|
|
|
|
1152
|
|
|
188
|
63
|
|
|
|
|
338
|
return $fields{$f}; |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 report() |
|
192
|
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Returns a hashref containing all the available file information. This method is usually called by read() to return everything at once. |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=cut |
|
196
|
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
sub report { |
|
198
|
4
|
|
|
4
|
1
|
8
|
my $self = shift; |
|
199
|
4
|
|
|
|
|
10
|
my %report = map { $_ => $self->$_() } $self->fields; |
|
|
44
|
|
|
|
|
235
|
|
|
200
|
4
|
|
|
|
|
91
|
return \%report; |
|
201
|
|
|
|
|
|
|
} |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub AUTOLOAD { |
|
204
|
63
|
|
|
63
|
|
132
|
my $self = shift; |
|
205
|
63
|
|
|
|
|
91
|
my $field = $AUTOLOAD; |
|
206
|
63
|
|
|
|
|
250
|
$field =~ s/.*://; |
|
207
|
63
|
50
|
|
|
|
230
|
return unless $self->allowed_field($field); |
|
208
|
63
|
100
|
|
|
|
189
|
return $self->{$field} = $_[0] if @_; |
|
209
|
45
|
|
|
|
|
370
|
return $self->{$field}; |
|
210
|
|
|
|
|
|
|
} |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head1 COPYRIGHT |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
Copyright 2004 William Ross (wross@cpan.org) |
|
215
|
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
This library is free software; you can redistribute it |
|
217
|
|
|
|
|
|
|
and/or modify it under the same terms as Perl itself. |
|
218
|
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=cut |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
1; |