Bug 865553 - Handle MPEG version 2 packet lengths. r=padenot, a=akeybl,webaudio
authorRalph Giles <giles@mozilla.com>
Fri, 09 Aug 2013 10:27:00 -0700
changeset 149086 194d916fb4609d736021b587d35010a97a5a7220
parent 149085 a68ce4ee08ed4e4cbe44f671cac6b215dc3a602f
child 149087 4167b7fca818f62891e5dbae6fe2227fa319f7cf
push id4144
push userrgiles@mozilla.com
push dateMon, 19 Aug 2013 21:35:44 +0000
treeherdermozilla-aurora@194d916fb460 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot, akeybl, webaudio
bugs865553
milestone25.0a2
Bug 865553 - Handle MPEG version 2 packet lengths. r=padenot, a=akeybl,webaudio Previous code didn't adjust bitrate and scale factors correctly for computing frame sizes on mpeg 2 and 2.5 files. See http://blog.coryhill.net/2009/06/calculating-mp3-frame-length.html for the extra factor of two. Removes a case for handling length calculation for layer-1 files, which we don't support. Adds a tagless mpeg-2 test case for the new sniffer code.
toolkit/components/mediasniffer/mp3sniff.c
toolkit/components/mediasniffer/test/unit/data/detodos.mp3
toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
--- a/toolkit/components/mediasniffer/mp3sniff.c
+++ b/toolkit/components/mediasniffer/mp3sniff.c
@@ -22,59 +22,62 @@ typedef struct {
   int copyright;
   int original;
   int emphasis;
 } mp3_header;
 
 /* Parse the 4-byte header in p and fill in the header struct. */
 static void mp3_parse(const uint8_t *p, mp3_header *header)
 {
-  const int bitrates[16] =
+  const int bitrates[2][16] = {
+        /* MPEG version 1 layer 3 bitrates. */
 	{0,  32000,  40000,  48000,  56000,  64000,  80000,  96000,
-         112000, 128000, 160000, 192000, 224000, 256000, 320000, 0};
+         112000, 128000, 160000, 192000, 224000, 256000, 320000, 0},
+        /* MPEG Version 2 and 2.5 layer 3 bitrates */
+        {0, 8000, 16000, 24000, 32000, 40000, 48000, 56000, 64000,
+         80000, 96000, 112000, 128000, 144000, 160000, 0} };
   const int samplerates[4] = {44100, 48000, 32000, 0};
 
-  header->version = (p[1] & 0x08) >> 3;
+  header->version = (p[1] & 0x18) >> 3;
   header->layer = 4 - ((p[1] & 0x06) >> 1);
   header->errp = (p[1] & 0x01);
 
-  header->bitrate = bitrates[(p[2] & 0xf0) >> 4];
+  header->bitrate = bitrates[(header->version & 1) ? 0 : 1][(p[2] & 0xf0) >> 4];
   header->freq = samplerates[(p[2] & 0x0c) >> 2];
+  if (header->version == 2) header->freq >>= 1;
+  else if (header->version == 0) header->freq >>= 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);
 }
 
 /* calculate the size of an mp3 frame from its header */
 static int mp3_framesize(mp3_header *header)
 {
   int size;
   int scale;
 
-  if (header->layer == 1) scale = 48;
+  if ((header->version & 1) == 0) scale = 72;
   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;
 }
 
 static int is_mp3(const uint8_t *p, long length) {
   /* Do we have enough room to see a 4 byte header? */
   if (length < 4) return 0;
   /* Do we have a sync pattern? */
-  if (p[0] == 0xff && (p[1]&0xe0) == 0xe0) {
+  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 header. */
     if ((4 - ((p[1] & 0x06) >> 1)) != 3) return 0; /* Only want level 3 */
     return 1;
   }
@@ -126,17 +129,17 @@ int mp3_sniff(const uint8_t *buf, long l
     if (is_id3(p, avail)) {
       /* Skip over any id3 tags */
       skip = id3_framesize(p, avail);
       p += skip;
       avail -= skip;
     } else if (is_mp3(p, avail)) {
       mp3_parse(p, &header);
       skip = mp3_framesize(&header);
-      if (skip < 4 || skip + 4 > avail) {
+      if (skip < 4 || skip + 4 >= avail) {
         return 0;
       }
       p += skip;
       avail -= skip;
       /* Check for a second header at the expected offset. */
       if (is_mp3(p, avail)) {
         /* Looks like mp3. */
         return 1;
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..12e3f89c2000a528d73b5c09c905024c8d86764d
GIT binary patch
literal 512
zc$@(M0{{L0^FYJ^3xTQ>JOD=QFp}JWZ~4Ff|KVSVNQhb@aT1%mx$hC({VYe^I2ahY
z3G(mZ{19{y|Nkg(P9Jaa=3g%|KfQAA<sRv-s98354Bo1S9mT>{Q=_Lv6r;v>3~WVc
zutsN6k>8;I^FqWe5((lQ5(tMxmFQ8M#-w@8LrBPm7*L#JY8vHAA~%0^<{sp#26coE
zW}1naO9NX`Pb`5u$UWF({es7w`sP-44X`Ar*dj9+EkvA}?s49b_jI3Dbkx<Ib#1n>
zksjcX!A5k+|MNh^Kocr<6c7l-gd|lUVoov+3^iO%+?`+G2_OILl#|zi<jwDunFX}L
z$mp?mU@*wZcy8<<m5fE8);@R5)FOgIrka_IlKmiriU3QQ6>$uK6s%I%y676jqM{v3
zvP05UIW-|w|MNn`P7-nU925vfoDB>Yryg_2=JNSvxuKTG{!0=%ALS-;md5V9hdU_-
zLfF|=mseFKh8@z>WjI;0ampLzmvv@&HKq&N>~v&?^3=2CjL%kQiZ3wKoYj4LPVj>S
z6%&_R5FEex>HqUU#9$TDP7nwHMhOnWs~jdGC`iB;ag>OQB^du@k_?(9V`pr9dTsIZ
zXXEeB7gI49QXiD?zp=OT>t4S7L_DO;vW{!MT9qI|2&{oUljFhXzRJ^^?TnQ_bScFn
C6Z|Fs
--- a/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
+++ b/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
@@ -22,16 +22,18 @@ const tests = [
   // Real webm and mkv files truncated to 512 bytes.
   { path: "data/file.webm", expected: "video/webm" },
   { path: "data/file.mkv", expected: "application/octet-stream" },
   // MP3 files with and without id3 headers truncated to 512 bytes.
   // NB these have 208/209 byte frames, but mp3 can require up to
   // 1445 bytes to detect with our method.
   { path: "data/id3tags.mp3", expected: "audio/mpeg" },
   { path: "data/notags.mp3", expected: "audio/mpeg" },
+  // MPEG-2 mp3 files.
+  { path: "data/detodos.mp3", expected: "audio/mpeg" },
   // Padding bit flipped in the first header: sniffing should fail.
   { path: "data/notags-bad.mp3", expected: "application/octet-stream" },
   // Garbage before header: sniffing should fail.
   { path: "data/notags-scan.mp3", expected: "application/octet-stream" },
   // VBR from the layer III test patterns. We can't sniff this.
   { path: "data/he_free.mp3", expected: "application/octet-stream" },
   // Make sure we reject mp2, which has a similar header.
   { path: "data/fl10.mp2", expected: "application/octet-stream" },