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

Brendan brendan at xiph.org
Tue Mar 11 14:28:54 PST 2003



brendan     03/03/11 17:28:53

  Modified:    src      mp3.c
  Log:
  VBR detection heuristic added.
  One last sanity check on MP3 frame headers.

Revision  Changes    Path
1.21      +84 -11    ices/src/mp3.c

Index: mp3.c
===================================================================
RCS file: /cvs/ice/ices/src/mp3.c,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- mp3.c	10 Mar 2003 21:40:40 -0000	1.20
+++ mp3.c	11 Mar 2003 22:28:53 -0000	1.21
@@ -17,10 +17,13 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  *
+ * $Id: mp3.c,v 1.21 2003/03/11 22:28:53 brendan Exp $
  */
 
 #include "definitions.h"
 
+/* reference: http://mpgedit.org/mpgedit/mpeg_format/mpeghdr.htm */
+
 #define MPG_MD_MONO 3
 
 #define MP3_BUFFER_SIZE 4096
@@ -70,7 +73,7 @@
   {11025, 8000, 8000, 0}
 };
 
-static char *mode_names[5] = {"stereo", "j-stereo", "dual-ch", "single-ch", "multi-ch"};
+static char *mode_names[5] = {"stereo", "j-stereo", "dual-ch", "mono", "multi-ch"};
 static char *layer_names[3] = {"I", "II", "III"};
 static char *version_names[3] = {"MPEG-1", "MPEG-2 LSF", "MPEG-2.5"};
 
@@ -83,6 +86,8 @@
 #endif
 static int ices_mp3_close (input_stream_t* self);
 static int mp3_parse_frame(const unsigned char* buf, mp3_header_t* header);
+static int mp3_check_vbr(input_stream_t* source, mp3_header_t* header);
+static size_t mp3_frame_length(mp3_header_t* header);
 
 /* Global function definitions */
 
@@ -155,14 +160,16 @@
 
   source->samplerate = mh.samplerate;
   source->bitrate = mh.bitrate;
-  if (! source->bitrate) {
-    ices_log_error ("Bitrate is 0");
-    return rc;
-  }
 
-  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);
+  if (mp3_check_vbr(source, &mh) > 0)
+    source->bitrate = 0;
+
+  if (source->bitrate)
+    ices_log_debug ("%s layer %s, %d kbps, %d Hz, %s", version_names[mh.version],
+                    layer_names[mh.layer - 1], mh.bitrate, mh.samplerate, mode_names[mh.mode]);
+  else
+    ices_log_debug ("%s layer %s, VBR, %d Hz, %s", version_names[mh.version],
+                    layer_names[mh.layer - 1], mh.samplerate, mode_names[mh.mode]);
   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\tPadding: %d", mh.error_protection,
@@ -302,8 +309,10 @@
   samplerate_idx = (buf[2] >> 2) & 0x3;
   header->mode = (buf[3] >> 6) & 0x3;
   header->layer = 4 - ((buf[1] >> 1) & 0x3);
+  header->emphasis = (buf[3]) & 0x3;
 
-  if ((bitrate_idx == 0xF) || (samplerate_idx == 0x3) || (header->layer == 4))
+  if ((bitrate_idx == 0xF) || (samplerate_idx == 0x3)
+      || (header->layer == 4) || (header->emphasis == 2))
     return 0;
 
   header->error_protection = !(buf[1] & 0x1);
@@ -314,12 +323,76 @@
   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;
+}
+
+/* TODO: check without seeking */
+static int mp3_check_vbr(input_stream_t* source, mp3_header_t* header) {
+  ices_mp3_in_t* mp3_data = (ices_mp3_in_t*) source->data;
+  char buf[4];
+  mp3_header_t next_header;
+  ssize_t framelen;
+  off_t cur;
+
+  if (!source->canseek)
+    return -1;
+
+  cur = lseek(source->fd, 0, SEEK_CUR);
+
+  /* check for Xing VBR tag */
+  lseek(source->fd, 36 - (ssize_t)mp3_data->len, SEEK_CUR);
+  if (read(source->fd, buf, 4) == 4) {
+    if (!strncmp("Xing", buf, 4)) {
+      ices_log_debug("VBR tag found");
+      lseek(source->fd, cur, SEEK_SET);
+      return 1;
+    }
+  } else {
+    ices_log_debug("Error trying to read VBR tag");
+    lseek(source->fd, cur, SEEK_SET);
+    return -1;
+  }
+
+  /* otherwise check next frame if possible */
+  lseek(source->fd, cur, SEEK_SET);
+  if ((framelen = mp3_frame_length(header))) {
+    ices_log_debug("Frame length expected: %d bytes", framelen);
+    lseek(source->fd, framelen - (ssize_t)mp3_data->len, SEEK_CUR);
+    if (read(source->fd, buf, 4) == 4) {
+      if (!mp3_parse_frame(buf, &next_header))
+        ices_log_debug("Couldn't find second frame header");
+      else {
+        ices_log_debug("Second frame: %s layer %s, %d kbps, %d Hz", 
+                       version_names[next_header.version],
+                       layer_names[next_header.layer - 1], next_header.bitrate,
+	               next_header.samplerate);
+        /* if bit rates don't match assume VBR */
+        if (header->bitrate != next_header.bitrate)
+	  return 1;
+      }
+    } else
+      ices_log_debug("Error reading next frame");
+    lseek(source->fd, cur, SEEK_SET);
+  }
+  
+  return 0;
+}
+
+/* Calculate the expected length of the next frame, or return 0 if we don't know how */
+static size_t mp3_frame_length(mp3_header_t* header) {
+  if (!header->bitrate)
+    return 0;
+
+  if (header->layer == 1) {
+    return (12000 * header->bitrate / header->samplerate + header->padding) * 4;
+  } else {
+    return 144000 * header->bitrate / header->samplerate + header->padding;
+  }
+
+  return 0;
 }

<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