[xiph-commits] r14368 - experimental/giles
giles at svn.xiph.org
giles at svn.xiph.org
Sat Jan 5 15:01:53 PST 2008
Author: giles
Date: 2008-01-05 15:01:51 -0800 (Sat, 05 Jan 2008)
New Revision: 14368
Added:
experimental/giles/mp3.txt
experimental/giles/mp3dump.c
Log:
Rough documentation and parser for the mp3 header.
Added: experimental/giles/mp3.txt
===================================================================
--- experimental/giles/mp3.txt (rev 0)
+++ experimental/giles/mp3.txt 2008-01-05 23:01:51 UTC (rev 14368)
@@ -0,0 +1,47 @@
+MP3 header is 32 bits
+
+ 0 1 2 3
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | sync word |V| L |E| rate |frq|P|R| mode |C|O|EM |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+sync word = 1111 1111 111x (x is zero for MPEG 2.5 VBR extension)
+V (version) 1 = MPEG
+L (layer) 01 = layer 3
+E (error protection flag) 1 = no crc
+rate (bitrate enum)
+frq (sampling frequency enum)
+P (padded frame flag) 0 = no padding
+R (reserved or unknown bit)
+mode (joint stereo mode)
+C (copyright flag)
+O (original/copy flag) 1 = original
+EM (emphasis) 00 = none
+
+bitrate is a 4 bit enum, with a lookup table that varies by version
+according to the following table
+
+version: MPEG-1 Layer 1 Layer 2 Layer 3 MPEG-2 LSF Layer 1 Layer 2/3
+enum:
+0000=0 0 0 0 0 0
+0001=1 32k 32k 32k 32k 8k
+0010=2 64k 48k 40k 48k 16k
+0011=3 96k 56k 48k 56k 24k
+0100=4 128k 64k 56k 64k 32k
+0101=5 160k 80k 64k 80k 40k
+0110=6 192k 96k 80k 96k 48k
+0111=7 224k 112k 96k 112k 56k
+1000=8 256k 128k 112k 128k 64k
+1001=9 288k 160k 128k 144k 80k
+1010=10 320k 192k 160k 160k 96k
+1011=11 352k 224k 196k 176k 112k
+1100=12 384k 256k 224k 192k 128k
+1101=13 416k 320k 256k 224k 144k
+1110=14 448k 384k 320k 256k 160k
+1111=15 n/a n/a n/a n/a n/a
+
+In summary, for values v in the header from 1-14, the bitrate is:
+MPEG-1 layer 1: 32*v
+MPEG-1 layer 2: various
+MPEG-1 layer 3: various
Added: experimental/giles/mp3dump.c
===================================================================
--- experimental/giles/mp3dump.c (rev 0)
+++ experimental/giles/mp3dump.c 2008-01-05 23:01:51 UTC (rev 14368)
@@ -0,0 +1,246 @@
+/* MPEG format dump utility */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef struct {
+ int version;
+ int layer;
+ int errp;
+ int bitrate;
+ int freq;
+ int pad;
+ int priv;
+ int mode;
+ int modex;
+ int copyright;
+ int original;
+ int emphasis;
+} mp3header;
+
+int parse(const unsigned char *p, mp3header *header)
+{
+ const int bitrates[16] =
+ {0, 32000, 40000, 48000, 56000, 64000, 80000, 96000,
+ 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0};
+ const int samplerates[4] = {44100, 48000, 32000};
+
+ header->version = (p[1] & 0x08) >> 3;
+ header->layer = 4 - ((p[1] & 0x06) >> 1);
+ header->errp = (p[1] & 0x01);
+
+ header->bitrate = bitrates[(p[2] & 0xf0) >> 4];
+ header->freq = samplerates[(p[2] & 0x0c) >> 2];
+ header->pad = (p[2] & 0x02) >> 1;
+ header->priv = (p[2] & 0x01);
+
+ header->mode = (p[3] & 0xc0) >> 6;
+ header->modex = (p[3] & 0x30) >> 4;
+ header->copyright = (p[3] & 0x08) >> 3;
+ header->original = (p[3] & 0x04) >> 2;
+ header->emphasis = (p[3] & 0x03);
+
+ return 0;
+}
+
+/* calculate the size of an mp3 frame from its header */
+int framesize(mp3header *header)
+{
+ int size;
+ int scale;
+
+ if (header->layer == 1) scale = 48;
+ else scale = 144;
+
+ size = header->bitrate * scale / header->freq;
+ /* divide by an extra factor of 2 for MPEG-2? */
+
+ if (header->pad) size += 1;
+
+ return size;
+}
+
+int dump_header(mp3header *header, FILE *out)
+{
+ fprintf(out, " version %d layer %d", header->version, header->layer);
+ if (header->version == 1 && header->layer == 1)
+ fprintf(out, " (MPEG-1 layer 2)");
+ else if (header->version == 1 && header->layer == 2)
+ fprintf(out, " (MPEG-1 layer 2)");
+ else if (header->version == 1 && header->layer == 3)
+ fprintf(out, " (MPEG-1 layer 3)");
+ fprintf(out, " %d kbps %d Hz", header->bitrate/1000, header->freq);
+ fprintf(out, " %d byte frame", framesize(header));
+ fprintf(out, "\n");
+
+ return 0;
+}
+
+int is_mpack(const unsigned char *p, const unsigned char *e) {
+ /* do we have enough room to see a 4 byte header? */
+ if (p > e) return 0;
+ if (e - p < 4) return 0;
+ /* do we have a sync pattern? */
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xba) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int is_mpsys(const unsigned char *p, const unsigned char *e) {
+ /* do we have enough room to see a 4 byte header? */
+ if (p > e) return 0;
+ if (e - p < 4) return 0;
+ /* do we have a sync pattern? */
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xbb) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int is_mpmap(const unsigned char *p, const unsigned char *e) {
+ /* do we have enough room to see a 4 byte header? */
+ if (p > e) return 0;
+ if (e - p < 4) return 0;
+ /* do we have a sync pattern? */
+ if (p[0] == 0x00 && p[1] == 0x00 && p[2] == 0x01 && p[3] == 0xbc) {
+ return 1;
+ }
+
+ return 0;
+}
+
+int is_mp3(const unsigned char *p, const unsigned char *e) {
+ /* do we have enough room to see a 4 byte header? */
+ if (p > e) return 0;
+ if (e - p < 4) return 0;
+ /* do we have a sync pattern? */
+ if (p[0] == 0xff && (p[1]&0xe0) == 0xe0) {
+ /* do we have any illegal field values? */
+ if (((p[1] & 0x06) >> 1) == 0) return 0; /* no layer 4 */
+ if (((p[2] & 0xf0) >> 4) == 15) return 0; /* bitrate can't be 1111 */
+ if (((p[2] & 0x0c) >> 2) == 3) return 0; /* samplerate can't be 11 */
+ /* looks like a sync'd header */
+ return 1;
+ }
+ return 0;
+}
+
+int is_id3(const unsigned char *p, const unsigned char *e) {
+ /* do we have enough room to see a 4 byte header? */
+ if (p > e) return 0;
+ if (e - p < 10) return 0;
+ /* do we have a sync pattern? */
+ if (p[0] == 'I' && p[1] == 'D' && p[2] == '3') {
+ if (p[3] == 0xff || p[4] == 0xff) return 0; /* illegal version */
+ if (p[6] & 0x80 || p[7] & 0x80 ||
+ p[8] & 0x80) return 0; /* bad length encoding */
+ /* looks like an id3 header */
+ return 1;
+ }
+ return 0;
+}
+
+int dump(FILE *in, FILE *out)
+{
+ mp3header header;
+ unsigned char *buf, *p, *e, *q;
+ long length, skip;
+ long hbytes;
+
+ fseek(in, 0, SEEK_END);
+ length = ftell(in);
+ if (length <= 0) {
+ fprintf(stderr, "couldn't find the end of the file\n");
+ return 1;
+ }
+ fprintf(out, "File has %ld bytes\n", length);
+ fseek(in, 0, SEEK_SET);
+
+ buf = malloc(length);
+ if (!buf) {
+ fprintf(stderr, "couldn't allocate buffer for file data"
+ " (%ld bytes)\n", length);
+ return 2;
+ }
+
+ if (fread(buf, 1, length, in) < length) {
+ fprintf(stderr, "couldn't read all of the file\n");
+ return 3;
+ }
+
+ e = buf + length - 2;
+ p = buf;
+ q = p;
+ while (p < e) {
+ if (is_id3(p, e)) {
+ if (p - q) fprintf(out, "LOST SYNC\n");
+ skip = 10 + (p[9] | (p[8] << 7) | (p[7] << 14) | (p[6] << 21));
+ fprintf(out, " id3 header at 0x%08lx (%ld bytes)\n",
+ (long)(p-buf), skip);
+ p += skip;
+ hbytes += skip;
+ q = p;
+ } else if (is_mp3(p, e)) {
+ if (p - q) fprintf(out, "LOST SYNC\n");
+ parse(p, &header);
+ skip = framesize(&header);
+ fprintf(out, " mp3 header at 0x%08lx (%ld bytes)\n",
+ (long)(p-buf), skip);
+ dump_header(&header, out);
+ p += skip;
+ hbytes += 4;
+ q = p;
+ } else {
+ if (is_mpack(p, e)) {
+ fprintf(out, "MPEG pack header\n");
+ } else if (is_mpsys(p, e)) {
+ fprintf(out, "MPEG system header\n");
+ } else if (is_mpmap(p, e)) {
+ fprintf(out, "MPEG program map\n");
+ }
+ //fprintf(out, "%08lx %02x\n", (long)(p-buf), (int)p[0]);
+ p++;
+ }
+ }
+ free(buf);
+
+ fprintf(stdout, "Framing overhead %ld/%ld bytes (%02.3lf%%)\n",
+ hbytes, length, 100.0*hbytes/length);
+
+ return 0;
+}
+
+void usage(const char *name)
+{
+ fprintf(stdout, "usage: %s file.mp3 [file2.mp3 ...]\n", name);
+}
+
+int main(int argc, char *argv[])
+{
+ int i;
+ FILE *in, *out = stdout;
+
+ if (argc < 2) {
+ usage(argv[0]);
+ return 0;
+ }
+
+ for (i = 1; i < argc; i++) {
+ if (argv[i][0] == '-' && argv[i][1] == '\0') {
+ dump(stdin, out);
+ } else {
+ in = fopen(argv[i], "rb");
+ if (!in) {
+ fprintf(stderr, "couldn't open '%s'\n", argv[i]);
+ continue;
+ }
+ dump(in, out);
+ fclose(in);
+ }
+ }
+
+ return 0;
+}
More information about the commits
mailing list