1 #############################################################################
3 # http://search.cpan.org/~amolloy/Ogg-Vorbis-Header-PurePerl-0.07/PurePerl.pm
4 # written by Andrew Molloy
5 # Code under GNU GENERAL PUBLIC LICENCE v2
7 #############################################################################
15 use Fcntl qw
/SEEK_END/;
17 our $VERSION = '0.07';
24 return load
($class, $file);
35 # there must be a better way...
36 if ($class eq 'vorbiscomm')
38 $self = bless \
%data, $class;
45 if ($self->{'FILE_LOADED'})
50 $self->{'FILE_LOADED'} = 1;
52 # check that the file exists and is readable
53 unless ( -e
$file && -r _
)
55 warn "File does not exist or cannot be read.";
56 # file does not exist, can't do anything
61 # make sure dos-type systems can handle it...
64 $data{'filename'} = $file;
65 $data{'fileHandle'} = \
*FILE
;
69 _loadComments
(\
%data);
70 _calculateTrackLength
(\
%data);
83 # if the user did not supply a key, return the entire hash
86 return $self->{'INFO'};
89 # otherwise, return the value for the given key
90 return $self->{'INFO'}{lc $key};
97 if ( $self && $self->{'COMMENT_KEYS'} ) {
98 return @
{$self->{'COMMENT_KEYS'}};
109 # if the user supplied key does not exist, return undef
110 unless($self->{'COMMENTS'}{lc $key})
115 return @
{$self->{'COMMENTS'}{lc $key}};
120 warn "Ogg::Vorbis::Header::PurePerl add_comments() unimplemented.";
125 warn "Ogg::Vorbis::Header::PurePerl edit_comment() unimplemented.";
130 warn "Ogg::Vorbis::Header::PurePerl delete_comment() unimplemented.";
135 warn "Ogg::Vorbis::Header::PurePerl clear_comments() unimplemented.";
142 return $self->{'fileName'};
147 warn "Ogg::Vorbis::Header::PurePerl write_vorbis unimplemented.";
155 my $fh = $data->{'fileHandle'};
158 # check the header to make sure this is actually an Ogg-Vorbis file
159 $byteCount = _checkHeader
($data);
163 # if it's not, we can't do anything
167 $data->{'startInfoHeader'} = $byteCount;
174 my $fh = $data->{'fileHandle'};
177 my $byteCount = 0; # stores how far into the file we've read,
178 # so later reads into the file can skip right
179 # past all of the header stuff
181 # check that the first four bytes are 'OggS'
182 read($fh, $buffer, 4);
183 if ($buffer ne 'OggS')
185 warn "This is not an Ogg bitstream (no OggS header).";
190 # check the stream structure version (1 byte, should be 0x00)
191 read($fh, $buffer, 1);
192 if (ord($buffer) != 0x00)
194 warn "This is not an Ogg bitstream (invalid structure version).";
199 # check the header type flag
200 # This is a bitfield, so technically we should check all of the bits
201 # that could potentially be set. However, the only value this should
202 # possibly have at the beginning of a proper Ogg-Vorbis file is 0x02,
203 # so we just check for that. If it's not that, we go on anyway, but
204 # give a warning (this behavior may (should?) be modified in the future.
205 read($fh, $buffer, 1);
206 if (ord($buffer) != 0x02)
208 warn "Invalid header type flag (trying to go ahead anyway).";
212 # skip to the page_segments count
213 read($fh, $buffer, 20);
215 # we do nothing with this data
217 # read the number of page segments
218 read($fh, $buffer, 1);
219 $pageSegCount = ord($buffer);
222 # read $pageSegCount bytes, then throw 'em out
223 read($fh, $buffer, $pageSegCount);
224 $byteCount += $pageSegCount;
226 # check packet type. Should be 0x01 (for indentification header)
227 read($fh, $buffer, 1);
228 if (ord($buffer) != 0x01)
230 warn "Wrong vorbis header type, giving up.";
235 # check that the packet identifies itself as 'vorbis'
236 read($fh, $buffer, 6);
237 if ($buffer ne 'vorbis')
239 warn "This does not appear to be a vorbis stream, giving up.";
244 # at this point, we assume the bitstream is valid
251 my $start = $data->{'startInfoHeader'};
252 my $fh = $data->{'fileHandle'};
254 my $byteCount = $start;
259 # read the vorbis version
260 read($fh, $buffer, 4);
261 $info{'version'} = _decodeInt
($buffer);
264 # read the number of audio channels
265 read($fh, $buffer, 1);
266 $info{'channels'} = ord($buffer);
269 # read the sample rate
270 read($fh, $buffer, 4);
271 $info{'rate'} = _decodeInt
($buffer);
274 # read the bitrate maximum
275 read($fh, $buffer, 4);
276 $info{'bitrate_upper'} = _decodeInt
($buffer);
279 # read the bitrate nominal
280 read($fh, $buffer, 4);
281 $info{'bitrate_nominal'} = _decodeInt
($buffer);
284 # read the bitrate minimal
285 read($fh, $buffer, 4);
286 $info{'bitrate_lower'} = _decodeInt
($buffer);
289 # read the blocksize_0 and blocksize_1
290 read($fh, $buffer, 1);
291 # these are each 4 bit fields, whose actual value is 2 to the power
292 # of the value of the field
293 $info{'blocksize_0'} = 2 << ((ord($buffer) & 0xF0) >> 4);
294 $info{'blocksize_1'} = 2 << (ord($buffer) & 0x0F);
297 # read the framing_flag
298 read($fh, $buffer, 1);
299 $info{'framing_flag'} = ord($buffer);
302 # bitrate_window is -1 in the current version of vorbisfile
303 $info{'bitrate_window'} = -1;
305 $data->{'startCommentHeader'} = $byteCount;
307 $data->{'INFO'} = \
%info;
313 my $fh = $data->{'fileHandle'};
314 my $start = $data->{'startCommentHeader'};
318 my $user_comment_count;
319 my $byteCount = $start;
324 # check that the first four bytes are 'OggS'
325 read($fh, $buffer, 4);
326 if ($buffer ne 'OggS')
328 warn "No comment header?";
333 # skip over next ten bytes
334 read($fh, $buffer, 10);
337 # read the stream serial number
338 read($fh, $buffer, 4);
339 push @
{$data->{'commentSerialNumber'}}, _decodeInt
($buffer);
342 # read the page sequence number (should be 0x01)
343 read($fh, $buffer, 4);
344 if (_decodeInt
($buffer) != 0x01)
346 warn "Comment header page sequence number is not 0x01: " +
348 warn "Going to keep going anyway.";
352 # and ignore the page checksum for now
353 read($fh, $buffer, 4);
356 # get the number of entries in the segment_table...
357 read($fh, $buffer, 1);
358 $page_segments = _decodeInt
($buffer);
360 # then skip on past it
361 read($fh, $buffer, $page_segments);
362 $byteCount += $page_segments;
364 # check the header type (should be 0x03)
365 read($fh, $buffer, 1);
366 if (ord($buffer) != 0x03)
368 warn "Wrong header type: " . ord($buffer);
372 # now we should see 'vorbis'
373 read($fh, $buffer, 6);
374 if ($buffer ne 'vorbis')
376 warn "Missing comment header. Should have found 'vorbis', found " .
381 # get the vendor length
382 read($fh, $buffer, 4);
383 $vendor_length = _decodeInt
($buffer);
387 read($fh, $buffer, $vendor_length);
388 $comments{'vendor'} = $buffer;
389 $byteCount += $vendor_length;
391 # read in the number of user comments
392 read($fh, $buffer, 4);
393 $user_comment_count = _decodeInt
($buffer);
396 $data->{'COMMENT_KEYS'} = [];
398 # finally, read the comments
399 for (my $i = 0; $i < $user_comment_count; $i++)
401 # first read the length
402 read($fh, $buffer, 4);
403 my $comment_length = _decodeInt
($buffer);
406 # then the comment itself
407 read($fh, $buffer, $comment_length);
408 $byteCount += $comment_length;
410 my ($key) = $buffer =~ /^([^=]+)/;
411 my ($value) = $buffer =~ /=(.*)$/;
413 push @
{$comments{lc $key}}, $value;
414 push @
{$data->{'COMMENT_KEYS'}}, lc $key;
417 # read past the framing_bit
418 read($fh, $buffer, 1);
421 $data->{'INFO'}{'offset'} = $byteCount;
423 $data->{'COMMENTS'} = \
%comments;
425 # Now find the offset of the first page
427 while(_findPage
($fh))
429 $byteCount = tell($fh) - 4;
432 read($fh, $buffer, 1);
433 if (ord($buffer) != 0x00)
435 warn "Invalid stream structure version: " .
436 sprintf("%x", ord($buffer));
441 read($fh, $buffer, 1);
442 # Audio data starts as a fresh packet on a new page, so
443 # if header_type is odd it's not a fresh packet
444 next if ( ord($buffer) % 2 );
446 # skip past granule position, stream_serial_number,
447 # page_sequence_number, and crc
448 read($fh, $buffer, 20);
451 read($fh, $buffer, 1);
452 my $page_segments = ord($buffer);
454 # skip past the segment table
455 read($fh, $buffer, $page_segments);
457 # read packet_type byte
458 read($fh, $buffer, 1);
460 # Not an audio packet. All audio packet numbers are even
461 next if ( ord($buffer) % 2 );
463 # Found the first audio packet
467 $data->{'INFO'}{'audio_offset'} = $byteCount;
470 sub _calculateTrackLength
473 my $fh = $data->{'fileHandle'};
476 my $granule_position;
478 seek($fh,-8500,SEEK_END
); # that magic number is from vorbisfile.c
479 # in the constant CHUNKSIZE, which comes
480 # with the comment /* a shade over 8k;
481 # anyone using pages well over 8k gets
482 # what they deserve */
484 # we just keep looking through the headers until we get to the last one
485 # (there might be a couple of blocks here)
486 while(_findPage
($fh))
488 # stream structure version - must be 0x00
489 read($fh, $buffer, 1);
490 if (ord($buffer) != 0x00)
492 warn "Invalid stream structure version: " .
493 sprintf("%x", ord($buffer));
498 read($fh, $buffer, 1);
499 # we should check this, but for now we'll just ignore it
501 # absolute granule position - this is what we need!
502 read($fh, $buffer, 8);
503 $granule_position = _decodeInt
($buffer);
505 # skip past stream_serial_number, page_sequence_number, and crc
506 read($fh, $buffer, 12);
509 read($fh, $buffer, 1);
510 my $page_segments = ord($buffer);
515 # calculate approx. page size
516 for (my $i = 0; $i < $page_segments; $i++)
518 read($fh, $buffer, 1);
519 $pageSize += ord($buffer);
522 seek $fh, $pageSize, 1;
525 $data->{'INFO'}{'length'} =
526 int($granule_position / $data->{'INFO'}{'rate'});
531 # search forward in the file for the 'OggS' page header
536 while (read($fh, $char, 1))
538 $curStr = $char . $curStr;
539 $curStr = substr($curStr, 0, 4);
541 # we are actually looking for the string 'SggO' because we
542 # tack character on to our test string backwards, to make
543 # trimming it to 4 characters easier.
544 if ($curStr eq 'SggO')
557 my @byteList = split //, $bytes;
558 my $numBytes = @byteList;
561 for (my $i = 0; $i < $numBytes; $i ++)
563 $num += ord($byteList[$i]) * $mult;
572 my $byte = ord(shift);
574 $byte = $byte & 0xF8; # clear out the bottm 3 bits
575 $byte = $byte >> 3; # and shifted down to where it belongs
582 my $byte = ord(shift);
584 $byte = $byte & 0xFC; # clear out the bottm 4 bits
585 $byte = $byte >> 4; # and shifted down to where it belongs
614 Ogg::Vorbis::Header::PurePerl - An object-oriented interface to Ogg Vorbis
615 information and comment fields, implemented entirely in Perl. Intended to be
616 a drop in replacement for Ogg::Vobis::Header.
618 Unlike Ogg::Vorbis::Header, this module will go ahead and fill in all of the
619 information fields as soon as you construct the object. In other words,
620 the C<new> and C<load> constructors have identical behavior.
624 use Ogg::Vorbis::Header::PurePerl;
625 my $ogg = Ogg::Vorbis::Header::PurePerl->new("song.ogg");
626 while (my ($k, $v) = each %{$ogg->info}) {
629 foreach my $com ($ogg->comment_tags) {
630 print "$com: $_\n" foreach $ogg->comment($com);
635 This module is intended to be a drop in replacement for Ogg::Vorbis::Header,
636 implemented entirely in Perl. It provides an object-oriented interface to
637 Ogg Vorbis information and comment fields. (NOTE: This module currently
638 supports only read operations).
642 =head2 C<new ($filename)>
644 Opens an Ogg Vorbis file, ensuring that it exists and is actually an
645 Ogg Vorbis stream. This method does not actually read any of the
646 information or comment fields, and closes the file immediately.
648 =head2 C<load ([$filename])>
650 Opens an Ogg Vorbis file, ensuring that it exists and is actually an
651 Ogg Vorbis stream, then loads the information and comment fields. This
652 method can also be used without a filename to load the information
653 and fields of an already constructed instance.
655 =head1 INSTANCE METHODS
657 =head2 C<info ([$key])>
659 Returns a hashref containing information about the Ogg Vorbis file from
660 the file's information header. Hash fields are: version, channels, rate,
661 bitrate_upper, bitrate_nominal, bitrate_lower, bitrate_window, and length.
662 The bitrate_window value is not currently used by the vorbis codec, and
665 The optional parameter, key, allows you to retrieve a single value from
666 the object's hash. Returns C<undef> if the key is not found.
668 =head2 C<comment_tags ()>
670 Returns an array containing the key values for the comment fields.
671 These values can then be passed to C<comment> to retrieve their values.
673 =head2 C<comment ($key)>
675 Returns an array of comment values associated with the given key.
677 =head2 C<add_comments ($key, $value, [$key, $value, ...])>
681 =head2 C<edit_comment ($key, $value, [$num])>
685 =head2 C<delete_comment ($key, [$num])>
689 =head2 C<clear_comments ([@keys])>
693 =head2 C<write_vorbis ()>
699 Returns the path/filename of the file the object represents.
703 This is ALPHA SOFTWARE. It may very well be very broken. Do not use it in
704 a production environment. You have been warned.
706 =head1 ACKNOWLEDGEMENTS
708 Dave Brown <cpan@dagbrown.com> made this module significantly faster
709 at calculating the length of ogg files.
711 Robert Moser II <rlmoser@earthlink.net> fixed a problem with files that
716 Andrew Molloy E<lt>amolloy@kaizolabs.comE<gt>
720 Copyright (c) 2003, Andrew Molloy. All Rights Reserved.
722 This program is free software; you can redistribute it and/or modify it
723 under the terms of the GNU General Public License as published by the
724 Free Software Foundation; either version 2 of the License, or (at
725 your option) any later version. A copy of this license is included
726 with this module (LICENSE.GPL).
730 L<Ogg::Vorbis::Header>, L<Ogg::Vorbis::Decoder>