Audio-Mad
view release on metacpan or search on metacpan
lib/Audio/Mad/Util.pm view on Meta::CPAN
sub mad_stream_info {
my ($fh, $do_toc, $do_ex) = @_;
my ($h, $buf) = ({}, '');
$do_toc = 0 unless (defined($do_toc) && $do_toc ne '');
$do_ex = 1 unless (defined($do_ex) && $do_ex ne '');
## fail if file is zero bytes in length
return undef if (($h->{f_size} = (stat($fh))[7]) == 0);
## get a new stream object and turn off crc
## checking, as many encoders are broken
## and it isn't too terribly useful in decoding but
## works nicely for checking stream validity.
## creates a mad_stream structure
my $stream = new Audio::Mad::Stream(MAD_OPTION_IGNORECRC);
## creates a mad_frame structure
my $frame = new Audio::Mad::Frame;
## setup some initial counter type things.
$h->{s_frames} = 0;
$h->{s_vbr} = 0;
## s_size is the size of the stream, and we attempt to
## correct this key for ID3 tags and other extraneous
## data -- in the case of a xing header, we use it's
## size variable if possible..
$h->{s_size} = $h->{f_size};
## create a mad_timer structure
$h->{s_duration} = new Audio::Mad::Timer;
## we use these variables to calaculate a toc strucutre
## when requested by the do_toc argument to this sub,
## it provides an easy index for searching through the
## stream.
my $scale = $h->{s_size} / 100;
my @toc = ();
## to start, we cycle on a frame-by-frame basis..
FRAME: while(1) {
## calls mad_frame_decode, and returns the
## same value (-1 for error);
if ($frame->decode_header($stream) == -1) {
## corresponds to MAD_RECOVERABLE(stream->error),
## if it is recoverable, just skip to the next
## frame.
next FRAME if ($stream->err_ok());
## otherwise, if we get an error due to and
## end of buffer condition (BUFLEN), or because
## the buffer was never set (BUFPTR) take action
## to fill the buffer.
if (
$stream->error == MAD_ERROR_BUFLEN ||
$stream->error == MAD_ERROR_BUFPTR
) {
## this is to capture the frame fragment at
## the end of the buffer, if we don't do this
## we lose the frame.
$buf = substr($buf, $stream->next_frame);
## attempt to read more data onto the end of
## the buffer, we drop out of our loop at
## the end of the file.
last FRAME if (sysread($fh, $buf, 128000, length($buf)) == 0);
## call mad_stream_buffer on the newly created buffer.
$stream->buffer($buf);
## from here, we restart our loop on the frame
## we just failed to decode.
redo FRAME;
}
## otherwise, it's a fatal error that we're not
## prepared to deal with..
return undef;
}
## keep a tally of the frames processed during
## this loop, and evaluate this block only
## during the first frame processed..
unless ($h->{s_frames}++) {
## this is some fairly obvious setup..
$h->{s_bitrate} =
$h->{s_avgrate} = $frame->bitrate();
$h->{s_samplerate} = $frame->samplerate();
$h->{s_mode} = $frame->mode();
$h->{s_layer} = $frame->layer();
$h->{s_flags} = $frame->flags();
## try to parse a xing header out of the first frame.
mad_parse_xing($h, substr($buf, $stream->this_frame, $stream->next_frame - $stream->this_frame));
## if we got a xing header, xing_flags will always
## be defined. next we do some vbr specific setup..
if (defined($h->{xing_flags})) {
$h->{s_vbr} = 1;
## we try to believe the xing header is accurate
## when possible, but you can always use f_size
## to get the true file size (possibly for seeking)
if ($h->{xing_flags} & 0x0002) {
$h->{s_size} = $h->{xing_bytes};
}
if ($h->{xing_flags} & 0x0001) {
if ($do_toc) {
## track that it's a vbr file that had it's own
## xing header with a frame count, but we were
## requested to generate our own anyways..
$h->{s_vbr} = 3;
} else {
## otherwise we just believe what the xing header
( run in 1.080 second using v1.01-cache-2.11-cpan-364913b4093 )