Reading the MP3 file/frame format

157 views
Skip to first unread message

Matthias Claes

unread,
Dec 15, 2003, 6:28:58 PM12/15/03
to
I've informed myself about the MP3 format by means of several websites
describing the internal structure of both CBR and VBR files. As far as I
understand it, it comes down to seeking the 12 sync-bits, validating wether
the frame header is valid or not, calculating the frame size, and then
moving on to the next sync-bits.
Two things are not clear to me:

Frame size is equal to (144 * bitrate / samplerate) + 1 (if padding is set)
However, if the protection bit is NOT set, a 16-bit CRC-checksum is added
between the frame header and the audio data. Is this checksum taken from the
audio-data bits? Or should the frame size be equal to (144 * bitrate /
samplerate) + 1 (padding byte) + 2 (checksum bytes)?

In an untagged MP3 file (or even a ID3v1 tagged one), I'd expected the first
frame to begin at byte 0. WinAmp indeed reports: 'Header found at: 0 bytes'.
When I read the file (I tried with both File Mapping and reading by
Random-Access), the file never starts with sync-bits. Instead, I find the
first frame at offset 622. I cleaned the file with both mp3Trim and VBRFix,
removing all possible tags. Is there any explanation for this? In VBR files,
the first frame my app finds is usually 4 bytes off compared to what WinAmp
reports.

With kind regards,
Matthias Claes


Chris P.

unread,
Dec 16, 2003, 10:04:44 AM12/16/03
to
"Matthias Claes" wrote

Hi Matthias,

I don't know how many MP3 format experts we have in the group if any. Most
of us that use MP3 format do so with the use of 3rd party filters that do
all the grunt work for us. You probably want to find a good MP3 format
discussion group or book that discusses the format in detail.


Thiam

unread,
Dec 16, 2003, 1:46:50 PM12/16/03
to
> I don't know how many MP3 format experts we have in the group if any.
Most
> of us that use MP3 format do so with the use of 3rd party filters that do
> all the grunt work for us. You probably want to find a good MP3 format
> discussion group or book that discusses the format in detail.

I went through the groupslist my ISP's newsserver offers, but nothing comes
close. Guess I'll have to keep plowing through the web.

Matthias Claes


Alessandro Angeli [MVP::DigitalMedia]

unread,
Dec 16, 2003, 3:54:35 PM12/16/03
to
I've checked a few of my CBR MP3 files and the first frame
header always starts at byte offset 0. I have no VBR file
handy to check. Is there maybe something wrong in the way
you hunt for the frame sync sequence so that you skip the
first occurrence? I'll try to post a code snipped as soon as
I can. In the meantime, you can open the files in a hex
editor and have a look at whether the header actually starts
at 0 (it usually is something like FF FB... very
noticeable).

Matthias Claes wrote:

--

/**
* Alessandro Angeli
*
* MVP::DigitalMedia
*
* a dot angeli at biosys dot net
*/

Alessandro Angeli [MVP::DigitalMedia]

unread,
Dec 17, 2003, 8:28:55 AM12/17/03
to
Here is the sample code (beware of wrapping). The bitrate table is not
complete (too much work;) and the others are filled out at the best of my
knowledge. Since I have no MP3 with CRC at hand, I haven't checked whether
the CRC bytes are counted in the frame payload size (I never thought of it
before).


-----------------------------------------------------------------
#include <stdlib.h>
#include <stdio.h>

#define MPA_SYNC_CODE 0xFFE00000UL

struct {
int major;
int minor;
} versions[/* version */] = { {2,5}, {0,0}, {2,0}, {1,0}, };

int layers[/* layer */] = { 0, 3, 2, 1, };

char *booleans[/* boolean */] = { "no", "yes", };

int bitrates[4 /* version */][4 /* layer */][16 /* bitrate */] = {
{ /* V2.5 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V0.0 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V2.0 */
/* L0 */ { 0, },
/* L3 */ { 0, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
{ /* V1.0 */
/* L0 */ { 0, },
/* L3 */ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256,
320, -1, },
/* L2 */ { 0, },
/* L1 */ { 0, },
},
};

int samplerates[4 /* version */][4 /* samplerate */] = {
/* V2.5 */ { 11025, 12000, 8000, -1, },
/* V0.0 */ { -1, -1, -1, -1, },
/* V2.0 */ { 22050, 24000, 16000, -1, },
/* V1.0 */ { 44100, 48000, 32000, -1, },
};

char *chanmodes[] = { "stereo", "joint stereo", "dual channel", "mono", };

struct {
int intensity;
int ms;
} extensions[] = { {0,0}, {1,0}, {0,1}, {1,1}, };

char *emphasises[] = { "none", "50/12 ms", "(reserved)", "CCITT J.17", };

int coeffs[4 /* version */][4 /* layer */] = {
/* V2.5 */ { 0, 0, 0, 0, },
/* V0.0 */ { 0, 0, 0, 0, },
/* V2.0 */ { 0, 72000, 72000, 24000, },
/* V1.0 */ { 0, 144000, 144000, 48000, },
};

int main(int argc, char *argv[])
{
int rc = 0;
FILE *hf = NULL;
unsigned long hdr = 0x00000000UL;
unsigned char b;
long offset;
int version;
int layer;
int protection;
int bitrate;
int samplerate;
int padding;
int private;
int chanmode;
int extension;
int copyright;
int original;
int emphasis;
int size;

printf("*** fopen(\"%s\")...\n",argv[1]);
if(NULL == (hf = fopen(argv[1],"rb"))) { rc = errno; goto exit; }

while(1) {
if(fread(&b,1,1,hf) < 1) { rc = errno; goto exit; }
hdr = (hdr << 8) | b;
if((hdr & MPA_SYNC_CODE) == MPA_SYNC_CODE) {
if((offset = ftell(hf)) < 0) { rc = errno; goto exit; }
offset -= 4;
printf("*** frame header found @ 0x%08lX (%ld):\n",offset,offset);
/// hdr = hdr & ~MPA_SYNC_CODE;
version = (hdr >> 19) & 0x03;
layer = (hdr >> 17) & 0x03;
protection = (hdr >> 16) & 0x01; protection = 1 - protection;
bitrate = (hdr >> 12) & 0x0F;
samplerate = (hdr >> 10) & 0x03;
padding = (hdr >> 9) & 0x01;
private = (hdr >> 8) & 0x01;
chanmode = (hdr >> 6) & 0x03;
extension = (hdr >> 4) & 0x03;
copyright = (hdr >> 3) & 0x01;
original = (hdr >> 2) & 0x01;
emphasis = (hdr >> 0) & 0x03;
printf(">>>\t version .. = (%d) MPEG
%d.%d\n",version,versions[version].major,versions[version].minor);
printf(">>>\t layer .... = (%d) Layer %d\n",layer,layers[layer]);
printf(">>>\t protection = (%d) %s\n",protection,booleans[protection]);
printf(">>>\t bitrate .. = (%d) %d
Kbps\n",bitrate,bitrates[version][layer][bitrate]);
printf(">>>\t samplerate = (%d) %d
Hz\n",samplerate,samplerates[version][samplerate]);
printf(">>>\t padding .. = (%d) %s\n",padding,booleans[padding]);
printf(">>>\t private .. = (%d) %s\n",private,booleans[private]);
printf(">>>\t chanmode . = (%d) %s\n",chanmode,chanmodes[chanmode]);
printf(">>>\t extension = (%d) Intensity stereo = %s, MS stereo =
%s\n",extension,booleans[extensions[extension].intensity],booleans[extension
s[extension].ms]);
printf(">>>\t copyright = (%d) %s\n",copyright,booleans[copyright]);
printf(">>>\t original . = (%d) %s\n",original,booleans[original]);
printf(">>>\t emphasis . = (%d) %s\n",emphasis,emphasises[emphasis]);
size = coeffs[version][layer]
* bitrates[version][layer][bitrate]
/ samplerates[version][samplerate]
+ padding - 4;
printf(">>>\t payload .. = %d bytes\n",size);
if(fseek(hf,size,SEEK_CUR)) { rc = errno; goto exit; }
}
}

exit:
printf(">>> rc = %d\n",rc);
if(hf) (void)fclose(hf);
return rc;
}
-----------------------------------------------------------------

--

Alessandro Angeli

MVP::DigitalMedia

Reply all
Reply to author
Forward
0 new messages