[Slim-Checkins] r12817 - in /trunk/server: Slim/Player/Source.pm lib/MPEG/Audio/Frame.pm
andy at svn.slimdevices.com
andy at svn.slimdevices.com
Fri Aug 31 08:08:52 PDT 2007
Author: andy
Date: Fri Aug 31 08:08:51 2007
New Revision: 12817
URL: http://svn.slimdevices.com?rev=12817&view=rev
Log:
Put back old MPEG::Audio::Frame read() method, change Alan's optimized version to read_ref
Modified:
trunk/server/Slim/Player/Source.pm
trunk/server/lib/MPEG/Audio/Frame.pm
Modified: trunk/server/Slim/Player/Source.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Player/Source.pm?rev=12817&r1=12816&r2=12817&view=diff
==============================================================================
--- trunk/server/Slim/Player/Source.pm (original)
+++ trunk/server/Slim/Player/Source.pm Fri Aug 31 08:08:51 2007
@@ -1975,6 +1975,9 @@
$channels ||= $info->{STEREO} ? 2 : 1;
}
}
+ else {
+ $log->warn('Unable to find MP3 frame in file!');
+ }
}
# pipe is a socket
@@ -2537,7 +2540,7 @@
my $pos = 0;
- while ( my ($length, $nextPos, $seconds) = MPEG::Audio::Frame->read($buffer, $pos) ) {
+ while ( my ($length, $nextPos, $seconds) = MPEG::Audio::Frame->read_ref($buffer, $pos) ) {
last unless ($length);
# Note: $length may not equal ($nextPos - $pos) if tag data has been skipped
if ( !defined($frames) ) {
Modified: trunk/server/lib/MPEG/Audio/Frame.pm
URL: http://svn.slimdevices.com/trunk/server/lib/MPEG/Audio/Frame.pm?rev=12817&r1=12816&r2=12817&view=diff
==============================================================================
--- trunk/server/lib/MPEG/Audio/Frame.pm (original)
+++ trunk/server/lib/MPEG/Audio/Frame.pm Fri Aug 31 08:08:51 2007
@@ -33,7 +33,13 @@
# constants and tables
-
+BEGIN {
+ if ($] <= 5.006){
+ require Fcntl; Fcntl->import(qw/SEEK_CUR/);
+ } else {
+ require POSIX; POSIX->import(qw/SEEK_CUR/);
+ }
+}
my @version = (
1, # 0b00 MPEG 2.5
@@ -181,14 +187,14 @@
}
}
-
-# constructor and work horse
-sub read {
+# A faster version of read that reads from a scalar ref containing
+# frame data and only returns the length, pos, and seconds
+sub read_ref {
my $pkg = shift || return undef;
my $bufref = shift || return undef;
my $start = shift;
- my $pos = $start;
+ my $pos = $start || 0;
my $len = length($$bufref);
my $header; # the binary header data... what a fabulous pun.
@@ -250,6 +256,357 @@
return ($length, $pos, $seconds);
}
+# original read() method that takes a filehandle
+sub read {
+ my $pkg = shift || return undef;
+ my $fh = shift || return undef;
+
+ local $/ = "\xff"; # get readline to find 8 bits of sync.
+
+ my $offset; # where in the handle
+ my $header; # the binary header data... what a fabulous pun.
+ my @hr; # an array of integer
+
+ OUTER: {
+ while (defined(<$fh>)){ # readline, readline, find me a header, make me a header, catch me a header. somewhate wasteful, perhaps. But I don't want to seek.
+ $header = "\xff";
+ (read $fh, $header, 3, 1 or return undef) == 3 or return undef; # read the rest of the header
+
+ my @hb = unpack("CCCC",$header); # an array of 4 integers for convenient access, each representing a byte of the header
+ # I wish vec could take non powers of 2 for the bit width param... *sigh*
+ # make sure there are no illegal values in the header
+ ($hr[SYNC] = ($hb[B_SYNC] & M_SYNC) >> R_SYNC) != 0x07 and next; # see if the sync remains
+ ($hr[VERSION] = ($hb[B_VERSION] & M_VERSION) >> R_VERSION) == 0x00 and ($mpeg25 or next);
+ ($hr[VERSION]) == 0x01 and next;
+ ($hr[LAYER] = ($hb[B_LAYER] & M_LAYER) >> R_LAYER) == 0x00 and next;
+ ($hr[BITRATE] = ($hb[B_BITRATE] & M_BITRATE) >> R_BITRATE) == 0x0f and next;
+ ($hr[SAMPLE] = ($hb[B_SAMPLE] & M_SAMPLE) >> R_SAMPLE) == 0x03 and next;
+ ($hr[EMPH] = ($hb[B_EMPH] & M_EMPH) >> R_EMPH) == 0x02 and ($lax or next);
+ # and drink up all that we don't bother verifying
+ $hr[CRC] = ($hb[B_CRC] & M_CRC) >> R_CRC;
+ $hr[PAD] = ($hb[B_PAD] & M_PAD) >> R_PAD;
+ $hr[PRIVATE] = ($hb[B_PRIVATE] & M_PRIVATE) >> R_PRIVATE;
+ $hr[CHANMODE] = ($hb[B_CHANMODE] & M_CHANMODE) >> R_CHANMODE;
+ $hr[MODEXT] = ($hb[B_MODEXT] & M_MODEXT) >> R_MODEXT;
+ $hr[COPY] = ($hb[B_COPY] & M_COPY) >> R_COPY;
+ $hr[HOME] = ($hb[B_HOME] & M_HOME) >> R_HOME;
+
+ # record the offset
+ $offset = tell($fh) - 4;
+
+ last OUTER; # were done reading for the header
+ }
+ seek $fh, -3, SEEK_CUR;
+ return undef;
+ }
+
+
+ my $sum = '';
+ if (!$hr[CRC]){
+ (read $fh, $sum, 2 or return undef) == 2 or return undef;
+ }
+
+ my $bitrate = $bitrates[$version[$hr[VERSION]]][$layer[$hr[LAYER]]][$hr[BITRATE]] || $free_bitrate or return undef;
+ my $sample = $samples[$hr[VERSION]][$hr[SAMPLE]];
+
+ my $use_smaller = $hr[VERSION] == 2 || $hr[VERSION] == 0; # FIXME VERSION == 2 means no support for MPEG2 multichannel
+ my $length = $layer[$hr[LAYER]]
+ ? (($use_smaller ? 72 : 144) * ($bitrate * 1000) / $sample + $hr[PAD]) # layers 2 & 3
+ : ((($use_smaller ? 6 : 12 ) * ($bitrate * 1000) / $sample + $hr[PAD]) * 4); # layer 1
+
+ my $clength = $length - 4 - ($hr[CRC] ? 0 : 2);
+ (read $fh, my($content), $clength or return undef) == $clength or return undef; # appearantly header length is included... learned this the hard way.
+
+ my $self = bless {}, $pkg;
+
+ %$self = (
+ binhead => $header, # binary header
+ header => \@hr, # array of integer header records
+ content => $content, # the actuaol content of the frame, excluding the header and crc
+ length => $length, # the length of the header + content == length($frame->content()) + 4 + ($frame->crc() ? 2 : 0);
+ bitrate => $bitrate, # the bitrate, in kilobits
+ sample => $sample, # the sample rate, in Hz
+ offset => $offset, # the offset where the header was found in the handle, based on tell
+ crc_sum => $sum, # the bytes of the network order short that is the crc sum
+ );
+
+ $self;
+}
+
+# methods
+
+sub asbin { # binary representation of the frame
+ my $self = shift;
+ $self->{binhead} . $self->{crc_sum} . $self->{content}
+}
+
+sub content { # byte content of frame, no header, no CRC sum
+ my $self = shift;
+ $self->{content}
+}
+
+sub header { # array of records in list context, binary header in scalar context
+ my $self = shift;
+ wantarray
+ ? @{ $self->{header} }
+ : $self->{binhead}
+}
+
+sub crc { # the actual sum bytes
+ my $self = shift;
+ $self->{crc_sum}
+}
+
+sub has_crc { # does a crc exist?
+ my $self = shift;
+ not $self->{header}[CRC];
+}
+
+sub length { # length of frame in bytes, including header and header CRC
+ my $self = shift;
+ $self->{length}
+}
+
+sub bitrate { # symbolic bit rate
+ my $self = shift;
+ $self->{bitrate}
+}
+
+sub free_bitrate {
+ my $self = shift;
+ $self->{header}[BITRATE] == 0;
+}
+
+sub sample { # symbolic sample rate
+ my $self = shift;
+ $self->{sample}
+}
+
+sub channels { # the data we want is the data in the header in this case
+ my $self = shift;
+ $self->{header}[CHANMODE]
+}
+
+sub stereo {
+ my $self = shift;
+ $self->channels == 0;
+}
+
+sub joint_stereo {
+ my $self = shift;
+ $self->channels == 1;
+}
+
+sub dual_channel {
+ my $self = shift;
+ $self->channels == 2;
+}
+
+sub mono {
+ my $self = shift;
+ $self->channels == 3;
+}
+
+sub modext {
+ my $self = shift;
+ $self->{header}[MODEXT];
+}
+
+sub _jmodes {
+ my $self = shift;
+ $self->layer3 || die "Joint stereo modes only make sense with layer III"
+}
+
+sub normal_joint_stereo {
+ my $self = shift;
+ $self->_jmodes && $self->joint_stereo && !$self->intensity_stereo && !$self->ms_stereo;
+}
+
+sub intensity_stereo {
+ my $self = shift;
+ $self->_jmodes and $self->joint_stereo and $self->modext % 2 == 1;
+}
+
+sub intensity_stereo_only {
+ my $self = shift;
+ $self->_jmodes && $self->intensity_stereo && !$self->ms_stereo;
+}
+
+sub ms_stereo {
+ my $self = shift;
+ $self->_jmodes and $self->joint_stereo and $self->modext > 1;
+}
+
+sub ms_stereo_only {
+ my $self = shift;
+ $self->_jmodes and $self->ms_stereo && !$self->intensity_stereo;
+}
+
+sub ms_and_intensity_stereo {
+ my $self = shift;
+ $self->_jmodes and $self->ms_stereo && $self->intensity_stereo;
+}
+*intensity_and_ms_stereo = \&ms_and_intensity_stereo;
+
+sub _bands {
+ my $self = shift;
+ !$self->layer3 || die "Intensity stereo bands only make sense with layers I I";
+}
+
+sub band_4 {
+ my $self = shift;
+ $self->_bands and $self->modext == 0;
+}
+
+sub band_8 {
+ my $self = shift;
+ $self->_bands and $self->modext == 1;
+}
+
+sub band_12 {
+ my $self = shift;
+ $self->_bands and $self->modext == 2;
+}
+
+sub band_16 {
+ my $self = shift;
+ $self->_bands and $self->modext == 3;
+}
+
+sub any_stereo {
+ my $self = shift;
+ $self->stereo or $self->joint_stereo;
+}
+
+sub seconds { # duration in floating point seconds
+ my $self = shift;
+
+ no integer;
+ $layer[$self->{header}[LAYER]]
+ ? (($version[$self->{header}[VERSION]] == 0 ? 1152 : 576) / $self->sample())
+ : (($version[$self->{header}[VERSION]] == 0 ? 384 : 192) / $self->sample())
+}
+
+sub framerate {
+ no integer;
+ 1 / $_[0]->seconds();
+}
+
+sub pad {
+ my $self = shift;
+ $self->{header}[PAD];
+}
+
+sub home {
+ my $self = shift;
+ $self->{header}[HOME];
+}
+
+sub copyright {
+ my $self = shift;
+ $self->{header}[COPY];
+}
+
+sub private {
+ my $self = shift;
+ $self->{header}[PRIVATE];
+}
+
+sub version {
+ my $self = shift;
+ $self->{header}[VERSION];
+}
+
+sub mpeg1 {
+ my $self = shift;
+ $self->version == 3;
+}
+
+sub mpeg2 {
+ my $self = shift;
+ $self->version == 2;
+}
+
+sub mpeg25 {
+ my $self = shift;
+ $self->version == 0;
+}
+
+sub layer {
+ my $self = shift;
+ $self->{header}[LAYER];
+}
+
+sub layer1 {
+ my $self = shift;
+ $self->layer == 3;
+}
+
+sub layer2 {
+ my $self = shift;
+ $self->layer == 2;
+}
+
+sub layer3 {
+ my $self = shift;
+ $self->layer == 1;
+}
+
+sub emph {
+ my $self = shift;
+ $self->{header}[EMPH];
+}
+*emphasize = \&emph;
+*emphasise = \&emph;
+*emphasis = \&emph;
+
+sub offset { # the position in the handle where the frame was found
+ my $self = shift;
+ $self->{offset}
+}
+
+sub crc_ok {
+ not shift->broken;
+}
+
+sub broken { # was the crc broken?
+ my $self = shift;
+ if (not defined $self->{broken}){
+ return $self->{broken} = 0 unless $self->has_crc; # we assume it's OK if we have no CRC at all
+ return $self->{broken} = 0 unless (($self->{header}[LAYER] & 0x02) == 0x00); # can't sum
+
+ my $bits = $protbits[$layer[$self->{header}[LAYER]]][$self->{header}[CHANMODE] == 0x03 ? 0 : 1 ];
+ my $i;
+
+ my $c = 0xffff;
+
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ord((substr($self->{binhead},2,1)))) & 0xff];
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ord((substr($self->{binhead},3,1)))) & 0xff];
+
+ for ($i = 0; $bits >= 32; do { $bits-=32; $i+=4 }){
+ my $data = unpack("N",substr($self->{content},$i,4));
+
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ($data >> 24)) & 0xff];
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ($data >> 16)) & 0xff];
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ($data >> 8)) & 0xff];
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ ($data >> 0)) & 0xff];
+
+ }
+ while ($bits >= 8){
+ $c = ($c << 8) ^ $crc_table[(($c >> 8) ^ (ord(substr($self->{content},$i++,1)))) & 0xff];
+ } continue { $bits -= 8 }
+ $self->{broken} = (( $c & 0xffff ) != unpack("n",$self->{crc_sum})) ? 1 : 0;
+ }
+
+ return $self->{broken};
+}
+
+
+# tie hack
+
+sub TIEHANDLE { bless \$_[1],$_[0] } # encapsulate the handle to save on unblessing and stuff
+sub READLINE { (ref $_[0])->read(${$_[0]}) } # read from the encapsulated handle
+
1; # keep your mother happy
__END__
More information about the checkins
mailing list