[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