Bugzilla – Bug 8563
Scanner reads incorrect "sample rate" on 88.2kHz MP4 files
Last modified: 2009-10-05 14:29:18 UTC
The scanner returns an incorrect sample rate value from MP4 (M4A) music files that have been sampled at rates >64kHz The scanner reads the sample rate from the MP4 "STSD" box; this is a 16 bit integer value so it is limited to sample rates <64kHz. It seems that encoders using sample rates of 88.2kHz ($15888) or 96kHz ($17700) store the low bytes ($5888 resp. $7700) of the actual sample rate value in STSD In other words, for a 88.2kHz ($15888) file, the scanner reports it as having a sample rate of 22.7kHz ($5888) You need to find another way of detecting files sampled >64kHz and restoring the "missing" high order bytes. One idea is to use the "time scale" attribute from the MDHD box, but it seems that "time scale" is not always equal to "sample rate"...
Andrew: can you please attach a sample file, as well as the scaner log with the error in it?
The sample file in 88 Mega Byte so I suggest you download it from Thomas Cunningham's server: http://thomasc.no-ip.org/andrewg/ There is no point trying to post a "scaner log with the error" since the scanner does not report any error! The scanner extracts an incorrect value of the Sample Rate, but it does not report an error. It wrongly extracts a sample rate of 22.7kHz ($05888) whereas it should properly extract 88.2kHz ($15888)...
Bug 8562 has an iTunes Plus 48 kHz AAC file that is read by the scanner as 44.1 kHz. Maybe this bug is not limited to sample rates greater then 64 kHz?
MP4 defines a Sample Rate attribute in the "STSD" box, and it also defines a "Time Scale" attribute in the "MDHD" box. I have encountered MP4 files where the two values are the same, and encountered others where the values are different. Anyway, from looking at the source code, I think Scanner takes its Sample rate display value from "STSD"...
Created attachment 3515 [details] AAC file with 88.2 kHz sample rate
I can't find any good documentation on how the sample rate is stored, so I'm going to have to punt this to 7.3. The high bits of the sample rate are stored somewhere, I'm just not sure where. If you can point me at some documentation that would help speed up a fix.
(In reply to comment #6) > I can't find any good documentation on how the sample rate is stored, so I'm > going to have to punt this to 7.3. The high bits of the sample rate are stored > somewhere, I'm just not sure where. If you can point me at some documentation > that would help speed up a fix. I found two possible sources: 1) In ISO/IEC 14496-12 Chapter 8.3 it describes the MVHD "Movie Header" box. This box contains a "timescale" field that is a 32 bit integer "this is the number of time units that pass in one second". 2) In ISO/IEC 14496-12 Chapter 8.16 it describes the STSD "Sample Description" box. This box can contain an AudioSampleEntry that contains a "samplerate" field that is a 32 bit integer "contains the sampling rate expressed as a 16.16 fixed point number (hi.lo)" (whatever that means...) Anyway, the code strategy that I use is as follows: Parser.SampleRate := MVHD.timescale; if (STSD.samplerate > 0) and (STSD.samplerate <> MVHD.timescale) then begin // kludge #1: // merge MVHD.timescale hi order bytes and STSD.samplerate lo order bytes Parser.SampleRate := STSD.samplerate + (MVHD.timescale and $FFFF0000); // kludge #2: // convert $5888 to $15888 (88200), or $7700 to $17700 (96000) if (STSD.samplerate = $5888) or (STSD.samplerate = $7700) then begin Parser.SampleRate := STSD.samplerate + $10000; end; end; Enjoy!!
Sorry, not going to make this release. Since you seem to know how to do this, a patch for MP4::Info would be very appreciated. (lib/MP4/Info.pm)
(In reply to comment #8) > Sorry, not going to make this release. Since you seem to know how to do this, > a patch for MP4::Info would be very appreciated. (lib/MP4/Info.pm) I am no expert in Perl, but the following is a first attempt. No testing or debugging attempted... :-) (lib/MP4/Info.pm) sub parse_mvhd { my ($fh, $level, $size, $tags) = @_; my ($data, $version, $scale, $duration, $secs); if ($size < 32) { $@ = 'Parse error'; return -1; } if (read ($fh, $data, $size) != $size) { $@ = 'Premature eof'; return -1; } $version = unpack('C', $data) & 255; if ($version==0) { ($scale,$duration) = unpack 'NN', substr ($data, 12, 8); } elsif ($version==1) { my ($hi,$lo); print "Long version\n" if $debug; ($scale,$hi,$lo) = unpack 'NNN', substr ($data, 20, 12); $duration=$hi*(2**32) + $lo; } else { return 0; } printf " %sDur/Scl=$duration/$scale\n", ' 'x(2*$level) if $debug; $secs=$duration/$scale; $tags->{SECS} = int (0.5+$secs); $tags->{MM} = int ($secs/60); $tags->{SS} = int ($secs - $tags->{MM}*60); $tags->{MS} = int (0.5 + 1000*($secs - int ($secs))); $tags->{TIME} = sprintf "%02d:%02d", $tags->{MM}, $tags->{SECS} - $tags->{MM}*60; # Begin: Code modified by Andrew Fiddian-Green $tags->{FREQUENCY} = $scale; # End: Code modified by Andrew Fiddian-Green return 0; } sub parse_stsd { my ($fh, $level, $size, $tags) = @_; # Begin: Code modified by Andrew Fiddian-Green my ($data, $data_format, $stsd_samplerate, $mvhd_samplerate); # End: Code modified by Andrew Fiddian-Green if ($size < 44) { $@ = 'Parse error'; return -1; } if (read ($fh, $data, $size) != $size) { $@ = 'Premature eof'; return -1; } # Assumes first entry in table contains the data printf " %sSample=%s\n", ' 'x(2*$level), substr ($data, 12, 4) if $debug; $data_format = lc substr ($data, 12, 4); # Is this an audio track? (Ought to look for presence of an SMHD uncle # atom instead to allow for other audio data formats). if (($data_format eq 'mp4a') || # AAC, aacPlus ($data_format eq 'alac') || # Apple lossless ($data_format eq 'drms') || # Apple encrypted AAC ($data_format eq 'samr') || # Narrow-band AMR ($data_format eq 'sawb') || # AMR wide-band ($data_format eq 'sawp') || # AMR wide-band + ($data_format eq 'enca')) # Generic encrypted audio { $tags->{ENCODING} = $data_format; # $version = unpack "n", substr ($data, 24, 2); # s8.16 is inconsistent. In practice, channels always appears == 2. # $tags->{STEREO} = (unpack ("n", substr ($data, 32, 2)) > 1) ? 1 : 0; # Old Quicktime field. No longer used. # $tags->{VBR} = (unpack ("n", substr ($data, 36, 2)) == -2) ? 1 : 0; # Begin: Code modified by Andrew Fiddian-Green $mvhd_samplerate = $tags->{FREQUENCY} $stsd_samplerate = unpack ('N', substr ($data, 40, 4)) / 65536000; # we may have already got a SampleRate from MVHD box if ($stsd_samplerate > 0) && ($stsd_samplerate != $mvhd_samplerate) { # kludge 1: add hi order bytes from from MVHD sample rate $stsd_samplerate = $stsd_samplerate + ($mvhd_samplerate && $FFFF0000); # kludge 2: convert $5888 to $15888 (88200) # or $7700 to $17700 (96000) if ($stsd_samplerate == $5888) || ($stsd_samplerate == $7700) { $stsd_samplerate = $stsd_samplerate + $10000; } $tags->{FREQUENCY} = $stsd_samplerate; } # End: Code modified by Andrew Fiddian-Green printf " %sFreq=%s\n", ' 'x(2*$level), $tags->{FREQUENCY} if $debug; } $tags->{ENCRYPTED}=1 if (($data_format eq 'drms') || (substr($data_format, 0, 3) eq 'enc')); return 0; }
Thanks, can you attach it as a patch? (diff -u)
(In reply to comment #10) > Thanks, can you attach it as a patch? (diff -u) Apologies but I don't know what you mean by "diff -u" ...
We are now planning to make a 7.3.3 release. Please review your bugs (all marked open against 7.3.3) to see if they can be fixed in the next few weeks, or if they should be retargeted for 7.4 or future. Thanks!
Since there's now a planned 7.3.3 release, bugs which won't make the cut-off are being moved to the next target out. If you feel that this bug needs to be addressed more (or less) urgently than the 7.4 release, please cc chris@slimdevices.com and leave a comment in the bug to that effect so we can review it. Thanks.
For some reason Bugzilla did not change the target when I did this yesterday. Or maybe it was me. In either case, I'm trying it again.
Moving all SQLite-related bugs to 8.0.
== Auto-comment from SVN commit #412 to the opensource repo by andy == == https://svn.slimdevices.com/opensource?view=revision&revision=412 == Fixed bug 8563, properly read sample rate from mdhd box instead of mvhd box
This bug has been marked as fixed in the 7.4.0 release version of SqueezeBox Server! * SqueezeCenter: 28672 * Squeezebox 2 and 3: 130 * Transporter: 80 * Receiver: 65 * Boom: 50 * Controller: 7790 * Radio: 7790 Please see the Release Notes for all the details: http://wiki.slimdevices.com/index.php/Release_Notes If you haven't already, please download and install the new version from http://www.logitechsqueezebox.com/support/download-squeezebox-server.html If you are still experiencing this problem, feel free to reopen the bug with your new comments and we'll have another look.