Bug 862088 - Add raw mp3 sniffing. r=padenot
authorRalph Giles <giles@mozilla.com>
Tue, 04 Jun 2013 12:12:00 -0700
changeset 133978 e693549c022b95ce6b65333fda23576d7ac5845d
parent 133977 9c001ba8508afe35dd64c558b4c7d1b1de77af05
child 133979 9f829109dd77472e8ffa1c394ba52d6a9549137d
push id28994
push userrgiles@mozilla.com
push dateTue, 04 Jun 2013 21:39:53 +0000
treeherdermozilla-inbound@e693549c022b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs862088, 875769
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 862088 - Add raw mp3 sniffing. r=padenot Attempt to sniff raw mp3 with no id3 tag by looking for a header followed by a second header at the expected offset from the packet length. Adds mp3 sniffing with and without id3 tags to the mediasniffer xpcshell tests. These files are truncated to 512 bytes. Our method can require up to 1.5k of data, but these have 208/209 byte packets so sniffing still works. notags-bad.mp3 has a corrupt header, and verifies sniffing fails. notags-scan.mp3 has garbage before the header, and verifies sniffing fails. he_free.mp is a vbr test vector which isn't sniffable with this method. fl10.mp2 is an mp2 file, and verifies sniffing fails. ff-inst.exe verifies bug 875769 doesn't regress.
toolkit/components/mediasniffer/moz.build
toolkit/components/mediasniffer/mp3sniff.c
toolkit/components/mediasniffer/mp3sniff.h
toolkit/components/mediasniffer/nsMediaSniffer.cpp
toolkit/components/mediasniffer/test/unit/data/ff-inst.exe
toolkit/components/mediasniffer/test/unit/data/fl10.mp2
toolkit/components/mediasniffer/test/unit/data/he_free.mp3
toolkit/components/mediasniffer/test/unit/data/id3tags.mp3
toolkit/components/mediasniffer/test/unit/data/notags-bad.mp3
toolkit/components/mediasniffer/test/unit/data/notags-scan.mp3
toolkit/components/mediasniffer/test/unit/data/notags.mp3
toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
toolkit/components/mediasniffer/test/unit/test_mediasniffer_webm.js
toolkit/components/mediasniffer/test/unit/xpcshell.ini
--- a/toolkit/components/mediasniffer/moz.build
+++ b/toolkit/components/mediasniffer/moz.build
@@ -12,8 +12,11 @@ EXPORTS += [
     'nsMediaSniffer.h',
 ]
 
 CPP_SOURCES += [
     'nsMediaSniffer.cpp',
     'nsMediaSnifferModule.cpp',
 ]
 
+CSRCS += [
+    'mp3sniff.c',
+]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/mp3sniff.c
@@ -0,0 +1,154 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* MPEG format parsing */
+
+#include "mp3sniff.h"
+
+/* Maximum packet size is 320 kbits/s * 144 / 32 kHz + 1 padding byte */
+#define MP3_MAX_SIZE 1441
+
+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;
+} 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] =
+	{0,  32000,  40000,  48000,  56000,  64000,  80000,  96000,
+         112000, 128000, 160000, 192000, 224000, 256000, 320000, 0};
+  const int samplerates[4] = {44100, 48000, 32000, 0};
+
+  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);
+}
+
+/* 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;
+  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) {
+    /* 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;
+  }
+  return 0;
+}
+
+/* Identify an ID3 tag based on its header. */
+/* http://id3.org/id3v2.4.0-structure */
+static int is_id3(const uint8_t *p, long length) {
+  /* Do we have enough room to see the header? */
+  if (length < 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;
+}
+
+/* Calculate the size of an id3 tag structure from its header. */
+static int id3_framesize(const uint8_t *p, long length)
+{
+  int size;
+
+  /* Header is 10 bytes. */
+  if (length < 10) {
+    return 0;
+  }
+  /* Frame is header plus declared size. */
+  size = 10 + (p[9] | (p[8] << 7) | (p[7] << 14) | (p[6] << 21));
+
+  return size;
+}
+
+int mp3_sniff(const uint8_t *buf, long length)
+{
+  mp3_header header;
+  const uint8_t *p, *q;
+  long skip;
+  long avail;
+
+  p = buf;
+  q = p;
+  avail = length;
+  while (avail >= 4) {
+    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) {
+        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;
+      } else {
+        /* No second header. Not mp3. */
+        return 0;
+      }
+    } else {
+      /* No id3 tag or mp3 header. Not mp3. */
+      return 0;
+    }
+  }
+
+  return 0;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/mp3sniff.h
@@ -0,0 +1,15 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <mozilla/StandardInteger.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int mp3_sniff(const uint8_t *buf, long length);
+
+#ifdef __cplusplus
+}
+#endif
--- a/toolkit/components/mediasniffer/nsMediaSniffer.cpp
+++ b/toolkit/components/mediasniffer/nsMediaSniffer.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsMediaSniffer.h"
 #include "nsMemory.h"
 #include "nsIHttpChannel.h"
 #include "nsString.h"
 #include "nsMimeTypes.h"
 #include "mozilla/ModuleUtils.h"
+#include "mp3sniff.h"
 #ifdef MOZ_WEBM
 #include "nestegg/nestegg.h"
 #endif
 
 #include "nsIClassInfoImpl.h"
 #include <algorithm>
 
 // The minimum number of bytes that are needed to attempt to sniff an mp4 file.
@@ -79,16 +80,23 @@ static bool MatchesWebM(const uint8_t* a
 {
 #ifdef MOZ_WEBM
   return nestegg_sniff((uint8_t*)aData, aLength) ? true : false;
 #else
   return false;
 #endif
 }
 
+// This function implements mp3 sniffing based on parsing
+// packet headers and looking for expected boundaries.
+static bool MatchesMP3(const uint8_t* aData, const uint32_t aLength)
+{
+  return mp3_sniff(aData, (long)aLength);
+}
+
 NS_IMETHODIMP
 nsMediaSniffer::GetMIMETypeFromContent(nsIRequest* aRequest,
                                        const uint8_t* aData,
                                        const uint32_t aLength,
                                        nsACString& aSniffedType)
 {
   // For media, we want to sniff only if the Content-Type is unknown, or if it
   // is application/octet-stream.
@@ -129,13 +137,18 @@ nsMediaSniffer::GetMIMETypeFromContent(n
     return NS_OK;
   }
 
   if (MatchesWebM(aData, clampedLength)) {
     aSniffedType.AssignLiteral(VIDEO_WEBM);
     return NS_OK;
   }
 
+  if (MatchesMP3(aData, clampedLength)) {
+    aSniffedType.AssignLiteral(AUDIO_MP3);
+    return NS_OK;
+  }
+
   // Could not sniff the media type, we are required to set it to
   // application/octet-stream.
   aSniffedType.AssignLiteral(APPLICATION_OCTET_STREAM);
   return NS_ERROR_NOT_AVAILABLE;
 }
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0f02f36e1a90ac3e7542e5862f680b29e0115c69
GIT binary patch
literal 512
zc%1W%n!v!!z`(!)#Q*;@FzjG}00#!b-~|x#$?xK0*ur^MaR;N%S;dfy%wmOtqWtut
z#9W2s#Js%x5{0Bxg`(0th0Ht!7yn>|-29YOJzice6{s2e$~l{ViKSnF;i;TC7k&ci
zO0LWcpTPXw3m?IB@`WE@HE9=efV8Eg6<B<oY1oA$K-yLy@xpH)9h8}z0Wt^OtpTnK
z3_grNpSmvzfQda|;AiA!WMg1h0OS;aFeAeSStbUC21W)31tv%+2mmQ2Ncb=?AZUgO
zOb|JUGy|9;fK0<FFo@}40vQiBHzo&3LLiB8g)9+)O(qBph%f*%K$gQC#Db6q0Bv|s
AZvX%Q
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bf84d736750e05bc0b53141721f3c73df830af5d
GIT binary patch
literal 512
zc%1wHX9a_V^4;>#^76E_G#3}+T$M?lN}ehc7=VEP|NsB|zg7Xk`hcT6vpaXJU-fJE
z?45__Se~A<^Zc4$m-|f~pWSi0cjxW7-|yE|KA&d+MwPGETYPT+fB%02=l}2jA28L|
z*8x%O_xng_@B8X|P$DN<?C8dAw{y4Mx2yU2`T6<zcG*$awytg9W1Rly0dqs@e+zTT
z9Y7@4#=fvh?Ox~h1*I=!s~3HDeb;=K{owlMu%jDuqNQ_zCV({q&3LwTZFbc47jtb}
zN`I;QC(3GkZUdtDGt)ooIE4#LUE;h_;BisgQ_;8Dw>AVPED0CA`@J?}OQXZAtp}Ug
znA-&wwir*<Jhv=guiy0L?9+?SR&3wlv3-Z9Qj^Dm!sl};pWUt89@l)IeeZ|vhvEm{
zH(z(XSQb0E^Vsb?5tWtk`^9vXT(z!AysvD3zIiEE;?FlPoy7On0?~{=Y4)P=tl<YI
zmZii_JEJ=z=J?M3Y0NsTi!*Lt`+DV7!H(S9esf=6^_sumEAC_O+SN)&vU6XZ*eG?o
zLhYM{+Bfqw-<y+tt_hpAKWhB3z+^$D!@0^iKhIvC-Bvh{z47wOE4OmF#Cl~cx<y}K
t)$CD}x_#_J!Le%}_sAG-Z741VqAA}}ZKrf*Dn)g2EOJtJ<Llus2LOa_7iIte
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..e3da8e6a724db47ddaac2d034f5c0c2cb6c7c821
GIT binary patch
literal 512
zc%1wHn*jtiN}gukV&x$2Jaa=?uLskSbRE76FBxL_(hlA-Rrs${8UHkU{{Mf1y?<Q)
z|NsAw=l}QrU+RA*pZfo4>-ql&C+`0z2o(PD^8c6rf90R{YyVkW{Qv(U!~Grmfx-vp
z|NH;1A7sYAu$}*Z7@7Y+WDgXUwf{H&{{k8s&42;uY8h$Z|8FLS76Q>gsU27W0EC3&
AC;$Ke
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..23091e666729c3e881428b75fc5d1f27d1275938
GIT binary patch
literal 512
zc%1WeF=l1}0`{^H&k!RZgB6IqN^=WB0$dG&OkN-kPRuRHNp&nL$t*4jarAKl3h^L>
zbCODPLn2*+pn8oA42?pZ{rylBxu@n8r8@ij`T|9yfjA-}!Z|-TH#M&Wsz4zb!qp22
sa`px)0J#ZhPJn+flw)KV;uHYV&H|D#GXXje<XRy#=Yd_rFbaqU0QW5}`~Uy|
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5ad89786fa59971971107ff398e0022bfc643374
GIT binary patch
literal 512
zc%1wHJLm`lI`GU(%Lh{YK+Mg+z<G*690v4Yz!e5!VW0#Cx?x}u4D5n|i!ks42L8hU
z&@M|KM_*TCJxhxs#-$)<=*coLh&ZUQcsemKIDp*ou;T`%V^P8X-vK}+Y$?hyB@-DK
ze<^+_w+v!tU^qQ3<HXUVjgdAT3=(S6>TbPKh7bP>H$V7c!6EZUhJ~+w3XA=!1NHwt
zT7G)}|G&mNjsy!CK6d8D0}l!`1Q-~&G(=g8e?GUbm3s2l_+-x+lat0;!SAyZr#Bp8
z%xAbDz{c!w^_Z@0ixQ*ag2)RdW<OY%4}WJlnvmoeF~|M`t60N<fQCke2P+#ilK&qR
z+){L)Bf@OL(SwOezyE(s;|ZF`F|i_luK4`_Kh-upIxjzYy;lRPw$w?b++95Xf$_uA
zz}?8%y=N<fVWq}8n@fF9=l*{fZSy>@aot}&@%a}XMwm&}i8vp&m6^EbA&Xo5m4mzg
wihtE}xL36OYQOQEf>i+(zhs`(Wl79DE*J6P&*svG4-pXpKNa0w78uk30BIb~(EtDd
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..949b7c4687077ca439d37bd6183dc561c32e2365
GIT binary patch
literal 512
zc$`a5EJ{jDPv!dmJKzWdI`GU(%Lh{YK+Mg+z<G*690v4Yz!e5!VW0#Cx?x}u4D5n|
zi!ks42L8hU&@M|KM_*TCJxhxs#-$)<=*coLh&ZUQcsemKIDp*ou;T`%V^IObKWr(=
zF(nfj7=I~#D7Oq^W?(oyE#t(|q>Yg_9Sjm`(&}!#Qic!z3pYRbVZkBuM}~#3ehQ2I
zssr`^K3aZy|Np<nJB|bk89sLA#sd!uGz1tJxHLpri+?`1ua$c8*7#)48IzO7TEXwL
z6Q?&EV$5f_Ai&1#aP^q3ZHp44;)2KvCT2fam=AwvIhv5<7%|8G1FKlWfq;fag$FAe
zG?M=x6x>pDpd-R;!qJ0?Nx%PpOydcf$T6`Zf3Eoa|3B3>JvuKxdA(NytG3ihrQBUS
z|9=M^VQJuQ<m}$FmBFx5W1Y>VzNd5lKa93{p4YhUFQ54Q3lAgAr0PVR58KL2-1Csd
tE&j^E-G9Zu>N(si+J3d)cuv8pfQnx-&+4)yW*(P|`0!_QX~Tzz2mqGP&Y=JR
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c7db9436179fbd3f1e8aa05c011c34379b6f9d72
GIT binary patch
literal 512
zc%1wHJKzWdI`GU(%Lh{YK+Mg+z<G*690v4Yz!e5!VW0#Cx?x}u4D5n|i!ks42L8hU
z&@M|KM_*TCJxhxs#-$)<=*coLh&ZUQcsemKIDp*ou;T`%V^P6>uz%Q6lw(RJGBEy9
z{7`Ng#LU2OdRoSbqe&YhZ8{hv)TGtjdZi2>{uge3@WX;b=8p^uU;Pvo`&9?(|9!Ol
z^#1>UjdvUg7BYP7%#8;g6le%AFmP#zvKIe*ZeJ_)<gM|^o--yVjkSW`XD3c?IK-IG
za6y2L+2QIjUE3BVM#Tk@7fj54urMF~&T=#%$uVM%{RdXDh64c&jS3G|HfSXOKPb4R
z=s-t=*@UA96O(@b|Cq)TG?8OsMgCmz`Tu{aZF+QGe)4*+23BpUlS;X}c>ezmI>OSx
z-N@O!XDfqYrN%m&OMOr0{(l&4^E|I{-CsWO`4=8Wm`T-%I3Ko^nYia6i(CAagS-EV
vf7NriSG4_Vzww-cRRI;hWS-S!Nz6Ph7xCfG=F)}_5fK7E72RDH7}Nj&WgyPa
new file mode 100644
--- /dev/null
+++ b/toolkit/components/mediasniffer/test/unit/test_mediasniffer_ext.js
@@ -0,0 +1,116 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cc = Components.classes;
+const CC = Components.Constructor;
+
+var BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
+                            "nsIBinaryOutputStream",
+                            "setOutputStream");
+
+Cu.import("resource://testing-common/httpd.js");
+
+var httpserver = new HttpServer();
+
+var testRan = 0;
+
+// The tests files we want to test, and the type we should have after sniffing.
+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" },
+  // 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" },
+  // Truncated ff installer regression test for bug 875769.
+  { path: "data/ff-inst.exe", expected: "application/octet-stream" },
+];
+
+// A basic listener that reads checks the if we sniffed properly.
+var listener = {
+  onStartRequest: function(request, context) {
+    do_print("Sniffing " + tests[testRan].path);
+    do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType, tests[testRan].expected);
+  },
+
+  onDataAvailable: function(request, context, stream, offset, count) {
+    try {
+      var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
+                          .createInstance(Components.interfaces.nsIBinaryInputStream);
+      bis.setInputStream(stream);
+      var array = bis.readByteArray(bis.available());
+    } catch (ex) {
+      do_throw("Error in onDataAvailable: " + ex);
+    }
+  },
+
+  onStopRequest: function(request, context, status) {
+    testRan++;
+    runNext();
+  }
+};
+
+function setupChannel(url) {
+  var ios = Components.classes["@mozilla.org/network/io-service;1"].
+                       getService(Ci.nsIIOService);
+  var chan = ios.newChannel("http://localhost:4444" + url, "", null);
+  var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
+  return httpChan;
+}
+
+function runNext() {
+  if (testRan == tests.length) {
+    do_test_finished();
+    return;
+  }
+  var channel = setupChannel("/");
+  channel.asyncOpen(listener, channel, null);
+}
+
+function getFileContents(aFile) {
+  const PR_RDONLY = 0x01;
+  var fileStream = Cc["@mozilla.org/network/file-input-stream;1"]
+                      .createInstance(Ci.nsIFileInputStream);
+  fileStream.init(aFile, 1, -1, null);
+  var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
+                      .createInstance(Components.interfaces.nsIBinaryInputStream);
+  bis.setInputStream(fileStream);
+
+  var data = bis.readByteArray(bis.available());
+
+  return data;
+}
+
+function handler(metadata, response) {
+  response.setStatusLine(metadata.httpVersion, 200, "OK");
+  // Send an empty Content-Type, so we are guaranteed to sniff.
+  response.setHeader("Content-Type", "", false);
+  var body = getFileContents(do_get_file(tests[testRan].path));
+  var bos = new BinaryOutputStream(response.bodyOutputStream);
+  bos.writeByteArray(body, body.length);
+}
+
+function run_test() {
+  // We use a custom handler so we can change the header to force sniffing.
+  httpserver.registerPathHandler("/", handler);
+  httpserver.start(4444);
+  do_test_pending();
+  try {
+    runNext();
+  } catch (e) {
+    print("ERROR - " + e + "\n");
+  }
+}
deleted file mode 100644
--- a/toolkit/components/mediasniffer/test/unit/test_mediasniffer_webm.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-const Cc = Components.classes;
-const CC = Components.Constructor;
-
-var BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
-                            "nsIBinaryOutputStream",
-                            "setOutputStream");
-
-Cu.import("resource://testing-common/httpd.js");
-
-var httpserver = new HttpServer();
-
-var testRan = 0;
-
-// The tests files we want to test, and the type we should have after sniffing.
-// Those file are real webm and mkv files truncated to 512 bytes.
-const tests = [
-  { path: "data/file.webm", expected: "video/webm" },
-  { path: "data/file.mkv", expected: "application/octet-stream" },
-];
-
-// A basic listener that reads checks the if we sniffed properly.
-var listener = {
-  onStartRequest: function(request, context) {
-    do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType, tests[testRan].expected);
-  },
-
-  onDataAvailable: function(request, context, stream, offset, count) {
-    try {
-      var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
-                          .createInstance(Components.interfaces.nsIBinaryInputStream);
-      bis.setInputStream(stream);
-      var array = bis.readByteArray(bis.available());
-    } catch (ex) {
-      do_throw("Error in onDataAvailable: " + ex);
-    }
-  },
-
-  onStopRequest: function(request, context, status) {
-    testRan++;
-    runNext();
-  }
-};
-
-function setupChannel(url) {
-  var ios = Components.classes["@mozilla.org/network/io-service;1"].
-                       getService(Ci.nsIIOService);
-  var chan = ios.newChannel("http://localhost:4444" + url, "", null);
-  var httpChan = chan.QueryInterface(Components.interfaces.nsIHttpChannel);
-  return httpChan;
-}
-
-function runNext() {
-  if (testRan == tests.length) {
-    do_test_finished();
-    return;
-  }
-  var channel = setupChannel("/");
-  channel.asyncOpen(listener, channel, null);
-}
-
-function getFileContents(aFile) {
-  const PR_RDONLY = 0x01;
-  var fileStream = Cc["@mozilla.org/network/file-input-stream;1"]
-                      .createInstance(Ci.nsIFileInputStream);
-  fileStream.init(aFile, 1, -1, null);
-  var bis = Components.classes["@mozilla.org/binaryinputstream;1"]
-                      .createInstance(Components.interfaces.nsIBinaryInputStream);
-  bis.setInputStream(fileStream);
-
-  var data = bis.readByteArray(bis.available());
-
-  return data;
-}
-
-function handler(metadata, response) {
-  response.setStatusLine(metadata.httpVersion, 200, "OK");
-  // Send an empty Content-Type, so we are guaranteed to sniff.
-  response.setHeader("Content-Type", "", false);
-  var body = getFileContents(do_get_file(tests[testRan].path));
-  var bos = new BinaryOutputStream(response.bodyOutputStream);
-  bos.writeByteArray(body, body.length);
-}
-
-function run_test() {
-  // We use a custom handler so we can change the header to force sniffing.
-  httpserver.registerPathHandler("/", handler);
-  httpserver.start(4444);
-  do_test_pending();
-  try {
-    runNext();
-  } catch (e) {
-    print("ERROR - " + e + "\n");
-  }
-}
--- a/toolkit/components/mediasniffer/test/unit/xpcshell.ini
+++ b/toolkit/components/mediasniffer/test/unit/xpcshell.ini
@@ -1,6 +1,6 @@
 [DEFAULT]
 head =
 tail =
 
 [test_mediasniffer.js]
-[test_mediasniffer_webm.js]
+[test_mediasniffer_ext.js]