[xiph-cvs] cvs commit: ices/src mp3.c

Brendan brendan at xiph.org
Mon Mar 10 12:43:45 PST 2003



brendan     03/03/10 15:43:45

  Modified:    src      mp3.c
  Log:
  More robust MP3 parsing:
  * Read all the way to the end of the file for a valid frame if necessary.
  * Disallow MPEG 2.5. It confuses the decoder, and I've found files with
    junk in front that looks like an MPEG 2.5 header, causing bitrate/samplerate
    etc to be wrong.

Revision  Changes    Path
1.18      +96 -63    ices/src/mp3.c

Index: mp3.c
===================================================================
RCS file: /cvs/ice/ices/src/mp3.c,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- mp3.c	9 Mar 2003 04:11:33 -0000	1.17
+++ mp3.c	10 Mar 2003 20:43:45 -0000	1.18
@@ -23,13 +23,14 @@
 
 #define MPG_MD_MONO 3
 
-typedef struct mp3_headerSt
-{
-  int lay;
+#define MP3_BUFFER_SIZE 4096
+
+typedef struct {
   int version;
+  int layer;
   int error_protection;
-  int bitrate_index;
-  int sampling_frequency;
+  int bitrate;
+  int samplerate;
   int padding;
   int extension;
   int mode;
@@ -37,7 +38,7 @@
   int copyright;
   int original;
   int emphasis;
-  int stereo;
+  int channels;
 } mp3_header_t;
 
 typedef struct {
@@ -84,20 +85,20 @@
                                  int16_t* left, int16_t* right);
 #endif
 static int ices_mp3_close (input_stream_t* self);
+static int mp3_parse_frame(const unsigned char* buf, mp3_header_t* header);
 
 /* Global function definitions */
 
 /* Parse mp3 file for header information; bitrate, channels, mode, sample_rate.
  */
-int
-ices_mp3_parse (input_stream_t* source)
+static int ices_mp3_parse (input_stream_t* source)
 {
   ices_mp3_in_t* mp3_data = (ices_mp3_in_t*) source->data;
   unsigned char *buffer;
-  unsigned short temp;
   mp3_header_t mh;
-  size_t len;
+  size_t len, rlen;
   int rc = 1;
+  int off = 0;
 
   if (! mp3_data->len)
     return 1;
@@ -114,73 +115,61 @@
     rc = -1;
   }
 
-  len = mp3_data->len;
-  buffer = mp3_data->buf + mp3_data->pos;
+  /* seek past garbage if necessary */
+  do {
+    len = mp3_data->len;
+    buffer = mp3_data->buf;
 
-  /* ID3v2 may have consumed the buffer */
-  if (! len) {
-    buffer = (unsigned char*) malloc (1024);
-    len = source->read (source, buffer, 1024);
-    mp3_data->buf = buffer;
-    mp3_data->len = len;
-    mp3_data->pos = 0;
-  }
+    if (!len) {
+      buffer = (unsigned char*) malloc (MP3_BUFFER_SIZE);
+      len = source->read (source, buffer, MP3_BUFFER_SIZE);
+      mp3_data->buf = buffer;
+      mp3_data->len = len;
+      mp3_data->pos = 0;
+    }
+    /* we must be able to read at least 4 bytes of header */
 
-  do {
-    temp = ((buffer[0] << 4) & 0xFF0) | ((buffer[1] >> 4) & 0xE);
-    buffer++;
-    len--;
-  } while ((temp != 0xFFE) && (len-4 > 0));
+    while (!(rc = mp3_parse_frame(buffer + mp3_data->pos, &mh)) && (mp3_data->len >= 4)) {
+      mp3_data->pos++;
+      mp3_data->len--;
+      off++;
+    }
 
-  if (temp != 0xFFE) {
-    ices_log_error ("Couldn't find synch");
-    return rc;
-  }
+    len = mp3_data->len;
+    /* copy remaining bits to front, refill buffer without malloc/free */
+    if (len && len < 4) {
+      memcpy (buffer, buffer + mp3_data->pos, len);
+      /* make read fetch from source instead of buffer */
+      mp3_data->len = 0;
+      rlen = source->read(source, buffer + len, MP3_BUFFER_SIZE - len);
+      mp3_data->len = len + rlen;
+      mp3_data->pos = 0;
+      len = rlen;
+    }
+  } while (!rc && len);
 
-  buffer--;
-  switch ((buffer[1] >> 3 & 0x3)) {
-    case 3:
-      mh.version = 0;
-      break;
-    case 2:
-      mh.version = 1;
-      break;
-    case 0:
-      mh.version = 2;
-      break;
-    default:
-      return rc;
-      break;
+  if (!rc) {
+    ices_log_error ("Couldn't find synch");
+    return -1;
   }
 
-  mh.lay = 4 - ((buffer[1] >> 1) & 0x3);
-  mh.error_protection = !(buffer[1] & 0x1);
-  mh.bitrate_index = (buffer[2] >> 4) & 0x0F;
-  mh.sampling_frequency = (buffer[2] >> 2) & 0x3;
-  mh.padding = (buffer[2] >> 1) & 0x01;
-  mh.extension = buffer[2] & 0x01;
-  mh.mode = (buffer[3] >> 6) & 0x3;
-  mh.mode_ext = (buffer[3] >> 4) & 0x03;
-  mh.copyright = (buffer[3] >> 3) & 0x01;
-  mh.original = (buffer[3] >> 2) & 0x1;
-  mh.emphasis = (buffer[3]) & 0x3;
-  mh.stereo = (mh.mode == MPG_MD_MONO) ? 1 : 2;
+  if (off)
+    ices_log_debug("Skipped %d bytes of garbage before MP3", off);
 
-  source->bitrate = bitrates[mh.version][mh.lay -1][mh.bitrate_index];
+  source->samplerate = mh.samplerate;
+  source->bitrate = mh.bitrate;
   if (! source->bitrate) {
     ices_log_error ("Bitrate is 0");
     return rc;
   }
-  source->samplerate = s_freq[mh.version][mh.sampling_frequency];
 
-  ices_log_debug ("Layer: %s\t\tVersion: %s\tFrequency: %d", layer_names[mh.lay - 1],
-                  version_names[mh.version], source->samplerate);
-  ices_log_debug ("Bitrate: %d kbit/s\tPadding: %d\tMode: %s", source->bitrate, mh.padding,
-                  mode_names[mh.mode]);
+  ices_log_debug ("%s layer %s, %d kbps, %d Hz", version_names[mh.version],
+                  layer_names[mh.layer - 1], mh.bitrate, mh.samplerate);
+  ices_log_debug ("Mode: %s, %d channels", mode_names[mh.mode], mh.channels);
   ices_log_debug ("Ext: %d\tMode_Ext: %d\tCopyright: %d\tOriginal: %d", mh.extension,
                   mh.mode_ext, mh.copyright, mh.original);
-  ices_log_debug ("Error Protection: %d\tEmphasis: %d\tStereo: %d", mh.error_protection,
-                  mh.emphasis, mh.stereo);
+  ices_log_debug ("Error Protection: %d\tEmphasis: %d\tPadding: %d", mh.error_protection,
+                  mh.emphasis, mh.padding);
 
   return 0;
 }
@@ -290,4 +279,48 @@
   free (self->data);
 
   return close (self->fd);
+}
+
+static int mp3_parse_frame(const unsigned char* buf, mp3_header_t* header) {
+  int bitrate_idx, samplerate_idx;
+
+  if (((buf[0] << 4) | ((buf[1] >> 4) & 0xE)) != 0xFFE)
+    return 0;
+
+  switch ((buf[1] >> 3 & 0x3)) {
+    case 3:
+      header->version = 0;
+      break;
+    case 2:
+      header->version = 1;
+      break;
+    case 0:
+      header->version = 2;
+      return 0;
+    default:
+      return 0;
+      break;
+  }
+
+  bitrate_idx = (buf[2] >> 4) & 0xF;
+  samplerate_idx = (buf[2] >> 2) & 0x3;
+  header->mode = (buf[3] >> 6) & 0x3;
+  header->layer = 4 - ((buf[1] >> 1) & 0x3);
+
+  if ((bitrate_idx == 0xF) || (samplerate_idx == 0x3) || (header->layer == 0))
+    return 0;
+
+  header->error_protection = !(buf[1] & 0x1);
+  header->bitrate = bitrates[header->version][header->layer-1][bitrate_idx];
+  header->samplerate = s_freq[header->version][samplerate_idx];
+  header->padding = (buf[2] >> 1) & 0x01;
+  header->extension = buf[2] & 0x01;
+  header->mode = (buf[3] >> 6) & 0x3;
+  header->mode_ext = (buf[3] >> 4) & 0x03;
+  header->copyright = (buf[3] >> 3) & 0x01;
+  header->original = (buf[3] >> 2) & 0x1;
+  header->emphasis = (buf[3]) & 0x3;
+  header->channels = (header->mode == MPG_MD_MONO) ? 1 : 2;
+
+  return 1;
 }

<p><p>--- >8 ----
List archives:  http://www.xiph.org/archives/
Ogg project homepage: http://www.xiph.org/ogg/
To unsubscribe from this list, send a message to 'cvs-request at xiph.org'
containing only the word 'unsubscribe' in the body.  No subject is needed.
Unsubscribe messages sent to the list will be ignored/filtered.



More information about the commits mailing list