source: gs2-extensions/parallel-building/trunk/src/perllib/cpan/Image/ExifTool/Vorbis.pm@ 24626

Last change on this file since 24626 was 24626, checked in by jmt12, 13 years ago

An (almost) complete copy of the perllib directory from a (circa SEP2011) head checkout from Greenstone 2 trunk - in order to try and make merging in this extension a little easier later on (as there have been some major changes to buildcol.pl commited in the main trunk but not in the x64 branch)

File size: 12.6 KB
Line 
1#------------------------------------------------------------------------------
2# File: Vorbis.pm
3#
4# Description: Read Ogg Vorbis meta information
5#
6# Revisions: 11/10/2006 - P. Harvey Created
7#
8# References: 1) http://www.xiph.org/vorbis/doc/
9# 2) http://flac.sourceforge.net/ogg_mapping.html
10#------------------------------------------------------------------------------
11
12package Image::ExifTool::Vorbis;
13
14use strict;
15use vars qw($VERSION);
16use Image::ExifTool qw(:DataAccess :Utils);
17
18$VERSION = '1.03';
19
20my $MAX_PACKETS = 2; # maximum packets to scan from each stream at start of file
21
22sub ProcessComments($$$);
23sub DecodeCoverArt($);
24
25# Vorbis comment tags
26%Image::ExifTool::Vorbis::Main = (
27 NOTES => q{
28 ExifTool extracts the following Vorbis information from Ogg files. As well
29 as this, ExifTool also extracts FLAC and ID3 information from Ogg files.
30 },
31 1 => {
32 Name => 'Identification',
33 SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Identification' },
34 },
35 3 => {
36 Name => 'Comments',
37 SubDirectory => { TagTable => 'Image::ExifTool::Vorbis::Comments' },
38 },
39);
40
41%Image::ExifTool::Vorbis::Identification = (
42 PROCESS_PROC => \&Image::ExifTool::ProcessBinaryData,
43 GROUPS => { 2 => 'Audio' },
44 0 => {
45 Name => 'VorbisVersion',
46 Format => 'int32u',
47 },
48 4 => 'AudioChannels',
49 5 => {
50 Name => 'SampleRate',
51 Format => 'int32u',
52 },
53 9 => {
54 Name => 'MaximumBitrate',
55 Format => 'int32u',
56 RawConv => '$val || undef',
57 PrintConv => 'ConvertBitrate($val)',
58 },
59 13 => {
60 Name => 'NominalBitrate',
61 Format => 'int32u',
62 RawConv => '$val || undef',
63 PrintConv => 'ConvertBitrate($val)',
64 },
65 17 => {
66 Name => 'MinimumBitrate',
67 Format => 'int32u',
68 RawConv => '$val || undef',
69 PrintConv => 'ConvertBitrate($val)',
70 },
71);
72
73%Image::ExifTool::Vorbis::Comments = (
74 PROCESS_PROC => \&ProcessComments,
75 GROUPS => { 2 => 'Audio' },
76 NOTES => q{
77 The tags below are only some common tags found in the Vorbis comments of Ogg
78 Vorbis and Ogg FLAC audio files, however ExifTool will extract values from
79 any tag found, even if not listed here.
80 },
81 vendor => { Notes => 'from comment header' },
82 TITLE => { Name => 'Title' },
83 VERSION => { Name => 'Version' },
84 ALBUM => { Name => 'Album' },
85 TRACKNUMBER=>{ Name => 'TrackNumber' },
86 ARTIST => { Name => 'Artist', Groups => { 2 => 'Author' }, List => 1 },
87 PERFORMER => { Name => 'Performer', Groups => { 2 => 'Author' }, List => 1 },
88 COPYRIGHT => { Name => 'Copyright', Groups => { 2 => 'Author' } },
89 LICENSE => { Name => 'License', Groups => { 2 => 'Author' } },
90 ORGANIZATION=>{Name => 'Organization', Groups => { 2 => 'Author' } },
91 DESCRIPTION=>{ Name => 'Description' },
92 GENRE => { Name => 'Genre' },
93 DATE => { Name => 'Date', Groups => { 2 => 'Time' } },
94 LOCATION => { Name => 'Location', Groups => { 2 => 'Location' } },
95 CONTACT => { Name => 'Contact', Groups => { 2 => 'Author' }, List => 1 },
96 ISRC => { Name => 'ISRCNumber' },
97 COVERARTMIME => { Name => 'CoverArtMIMEType' },
98 COVERART => {
99 Name => 'CoverArt',
100 Notes => 'base64-encoded image',
101 ValueConv => q{
102 require Image::ExifTool::XMP;
103 Image::ExifTool::XMP::DecodeBase64($val);
104 },
105 },
106 REPLAYGAIN_TRACK_PEAK => { Name => 'ReplayGainTrackPeak' },
107 REPLAYGAIN_TRACK_GAIN => { Name => 'ReplayGainTrackGain' },
108 REPLAYGAIN_ALBUM_PEAK => { Name => 'ReplayGainAlbumPeak' },
109 REPLAYGAIN_ALBUM_GAIN => { Name => 'ReplayGainAlbumGain' },
110 # observed in "Xiph.Org libVorbis I 20020717" ogg:
111 ENCODED_USING => { Name => 'EncodedUsing' },
112 ENCODED_BY => { Name => 'EncodedBy' },
113 COMMENT => { Name => 'Comment' },
114);
115
116#------------------------------------------------------------------------------
117# Process Vorbis Comments
118# Inputs: 0) ExifTool object ref, 1) dirInfo ref, 2) tag table ref
119# Returns: 1 on success, otherwise returns 0 and sets a Warning
120sub ProcessComments($$$)
121{
122 my ($exifTool, $dirInfo, $tagTablePtr) = @_;
123 my $dataPt = $$dirInfo{DataPt};
124 my $dataPos = $$dirInfo{DataPos};
125 my $pos = $$dirInfo{DirStart} || 0;
126 my $end = $$dirInfo{DirLen} ? $pos + $$dirInfo{DirLen} : length $$dataPt;
127 my ($num, $index);
128
129 SetByteOrder('II');
130 for (;;) {
131 last if $pos + 4 > $end;
132 my $len = Get32u($dataPt, $pos);
133 last if $pos + 4 + $len > $end;
134 my $start = $pos + 4;
135 my $buff = substr($$dataPt, $start, $len);
136 $pos = $start + $len;
137 my ($tag, $val);
138 if (defined $num) {
139 $buff =~ /(.*?)=(.*)/s or last;
140 ($tag, $val) = ($1, $2);
141 } else {
142 $tag = 'vendor';
143 $val = $buff;
144 $num = ($pos + 4 < $end) ? Get32u($dataPt, $pos) : 0;
145 $exifTool->VPrint(0, " + [Vorbis comments with $num entries]\n");
146 $pos += 4;
147 }
148 # add tag to table unless it exists already
149 unless ($$tagTablePtr{$tag}) {
150 my $name = ucfirst(lc($tag));
151 # remove invalid characters in tag name and capitalize following letters
152 $name =~ s/[^\w-]+(.?)/\U$1/sg;
153 $name =~ s/([a-z0-9])_([a-z])/$1\U$2/g;
154 Image::ExifTool::AddTagToTable($tagTablePtr, $tag, { Name => $name });
155 }
156 $exifTool->HandleTag($tagTablePtr, $tag, $exifTool->Decode($val, 'UTF8'),
157 Index => $index,
158 DataPt => $dataPt,
159 DataPos => $dataPos,
160 Start => $start,
161 Size => $len,
162 );
163 # all done if this was our last tag
164 $num-- or return 1;
165 $index = (defined $index) ? $index + 1 : 0;
166 }
167 $exifTool->Warn('Format error in Vorbis comments');
168 return 0;
169}
170
171#------------------------------------------------------------------------------
172# Process Ogg packet
173# Inputs: 0) ExifTool object ref, 1) data ref, 2) tag table ref
174# Returns: 1 on success
175sub ProcessPacket($$$)
176{
177 my ($exifTool, $dataPt, $tagTablePtr) = @_;
178 if ($$dataPt =~ /^(.)vorbis/s) {
179 my $tag = ord($1);
180 my $tagInfo = $exifTool->GetTagInfo($tagTablePtr, $tag);
181 return 0 unless $tagInfo and $$tagInfo{SubDirectory};
182 my %dirInfo = (
183 DataPt => $dataPt,
184 DirName => $$tagInfo{Name},
185 DirStart => 7,
186 );
187 my $table = GetTagTable($tagInfo->{SubDirectory}->{TagTable});
188 return $exifTool->ProcessDirectory(\%dirInfo, $table);
189 }
190 return 0;
191}
192
193#------------------------------------------------------------------------------
194# Extract information from an Ogg Vorbis or Ogg FLAC file
195# Inputs: 0) ExifTool object reference, 1) dirInfo reference
196# Returns: 1 on success, 0 if this wasn't a valid Ogg Vorbis file
197sub ProcessOGG($$)
198{
199 my ($exifTool, $dirInfo) = @_;
200
201 # must first check for leading/trailing ID3 information
202 unless ($exifTool->{DoneID3}) {
203 require Image::ExifTool::ID3;
204 Image::ExifTool::ID3::ProcessID3($exifTool, $dirInfo) and return 1;
205 }
206 my $raf = $$dirInfo{RAF};
207 my $verbose = $exifTool->Options('Verbose');
208 my $out = $exifTool->Options('TextOut');
209 my ($success, $page, $packets, $streams) = (0,0,0,0);
210 my ($buff, $tagTablePtr, $flag, $stream, %val, $numFlac);
211
212 for (;;) {
213 # must read ahead to next page to see if it is a continuation
214 # (this code would be a lot simpler if the continuation flag
215 # was on the leading instead of the trailing page!)
216 if ($raf and $raf->Read($buff, 28) == 28) {
217 # validate magic number
218 unless ($buff =~ /^OggS/) {
219 $success and $exifTool->Warn('Lost synchronization');
220 last;
221 }
222 unless ($success) {
223 # set file type and initialize on first page
224 $success = 1;
225 $exifTool->SetFileType();
226 SetByteOrder('II');
227 $tagTablePtr = GetTagTable('Image::ExifTool::Vorbis::Main');
228 }
229 $flag = Get8u(\$buff, 5); # page flag
230 $stream = Get32u(\$buff, 14); # stream serial number
231 ++$streams if $flag & 0x02; # count start-of-stream pages
232 ++$packets unless $flag & 0x01; # keep track of packet count
233 } else {
234 # all done unless we have to process our last packet
235 last unless %val;
236 ($stream) = sort keys %val; # take a stream
237 $flag = 0; # no continuation
238 undef $raf; # flag for done reading
239 }
240
241 if (defined $numFlac) {
242 # stop to process FLAC headers if we hit the end of file
243 last unless $raf;
244 --$numFlac; # one less header packet to read
245 } else {
246 # can finally process previous packet from this stream
247 # unless this is a continuation page
248 if (defined $val{$stream} and not $flag & 0x01) {
249 ProcessPacket($exifTool, \$val{$stream}, $tagTablePtr);
250 delete $val{$stream};
251 # only read the first $MAX_PACKETS packets from each stream
252 if ($packets > $MAX_PACKETS * $streams or not defined $raf) {
253 last unless %val; # all done (success!)
254 next; # process remaining stream(s)
255 }
256 }
257 # stop processing Ogg Vorbis if we have scanned enough packets
258 last if $packets > $MAX_PACKETS * $streams and not %val;
259 }
260
261 # continue processing the current page
262 my $pageNum = Get32u(\$buff, 18); # page sequence number
263 my $nseg = Get8u(\$buff, 26); # number of segments
264 # calculate total data length
265 my $dataLen = Get8u(\$buff, 27);
266 if ($nseg) {
267 $raf->Read($buff, $nseg-1) == $nseg-1 or last;
268 my @segs = unpack('C*', $buff);
269 # could check that all these (but the last) are 255...
270 foreach (@segs) { $dataLen += $_ }
271 }
272 if (defined $page) {
273 if ($page == $pageNum) {
274 ++$page;
275 } else {
276 $exifTool->Warn('Missing page(s) in Ogg file');
277 undef $page;
278 }
279 }
280 # read page data
281 $raf->Read($buff, $dataLen) == $dataLen or last;
282 if ($verbose > 1) {
283 printf $out "Page %d, stream 0x%x, flag 0x%x (%d bytes)\n",
284 $pageNum, $stream, $flag, $dataLen;
285 $exifTool->VerboseDump(\$buff, DataPos => $raf->Tell() - $dataLen);
286 }
287 if (defined $val{$stream}) {
288 $val{$stream} .= $buff; # add this continuation page
289 } elsif (not $flag & 0x01) { # ignore remaining pages of a continued packet
290 # ignore the first page of any packet we aren't parsing
291 if ($buff =~ /^(.)vorbis/s and $$tagTablePtr{ord($1)}) {
292 $val{$stream} = $buff; # save this page
293 } elsif ($buff =~ /^\x7fFLAC..(..)/s) {
294 $numFlac = unpack('n',$1);
295 $val{$stream} = substr($buff, 9);
296 }
297 }
298 if (defined $numFlac) {
299 # stop to process FLAC headers if we have them all
300 last if $numFlac <= 0;
301 } elsif (defined $val{$stream} and $flag & 0x04) {
302 # process Ogg Vorbis packet now if end-of-stream bit is set
303 ProcessPacket($exifTool, \$val{$stream}, $tagTablePtr);
304 delete $val{$stream};
305 }
306 }
307 if (defined $numFlac and defined $val{$stream}) {
308 # process FLAC headers as if it was a complete FLAC file
309 require Image::ExifTool::FLAC;
310 my %dirInfo = ( RAF => new File::RandomAccess(\$val{$stream}) );
311 Image::ExifTool::FLAC::ProcessFLAC($exifTool, \%dirInfo);
312 }
313 return $success;
314}
315
3161; # end
317
318__END__
319
320=head1 NAME
321
322Image::ExifTool::Vorbis - Read Ogg Vorbis meta information
323
324=head1 SYNOPSIS
325
326This module is used by Image::ExifTool
327
328=head1 DESCRIPTION
329
330This module contains definitions required by Image::ExifTool to extract meta
331information from Ogg Vorbis and Ogg FLAC audio files.
332
333=head1 AUTHOR
334
335Copyright 2003-2011, Phil Harvey (phil at owl.phy.queensu.ca)
336
337This library is free software; you can redistribute it and/or modify it
338under the same terms as Perl itself.
339
340=head1 REFERENCES
341
342=over 4
343
344=item L<http://www.xiph.org/vorbis/doc/>
345
346=item L<http://flac.sourceforge.net/ogg_mapping.html>
347
348=back
349
350=head1 SEE ALSO
351
352L<Image::ExifTool::TagNames/Vorbis Tags>,
353L<Image::ExifTool(3pm)|Image::ExifTool>
354
355=cut
356
Note: See TracBrowser for help on using the repository browser.