Bug 860599 - Use DataSource::CreateFromURI instead of MediaStreamSource. r=cpearce, a=akeybl
authorChris Double <chris.double@double.co.nz>
Fri, 30 Aug 2013 16:51:38 +1200
changeset 153947 e484bfe5427eaae0dd67453603766a51f30cf06c
parent 153946 6057c8014078419bbfb3dde273fcf58bdfa2b353
child 153948 2a70b9450fc1a81a44f93a29e18cebcca512ed0a
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, akeybl
bugs860599
milestone25.0a2
Bug 860599 - Use DataSource::CreateFromURI instead of MediaStreamSource. r=cpearce, a=akeybl
content/media/MediaResource.h
content/media/plugins/MediaPluginHost.cpp
content/media/plugins/MediaPluginHost.h
content/media/plugins/MediaResourceServer.cpp
content/media/plugins/MediaResourceServer.h
content/media/plugins/moz.build
media/omx-plugin/OmxPlugin.cpp
media/omx-plugin/lib/gb/libstagefright/libstagefright.cpp
media/omx-plugin/lib/ics/libstagefright/libstagefright.cpp
--- a/content/media/MediaResource.h
+++ b/content/media/MediaResource.h
@@ -187,17 +187,18 @@ inline MediaByteRange::MediaByteRange(Ti
  * support efficient random access, such as HTTP.
  *
  * Instances of this class must be created on the main thread.
  * Most methods must be called on the main thread only. Read, Seek and
  * Tell must only be called on non-main threads. In the case of the Ogg
  * Decoder they are called on the Decode thread for example. You must
  * ensure that no threads are calling these methods once Close is called.
  *
- * Instances of this class are explicitly managed. 'delete' it when done.
+ * Instances of this class are reference counted. Use nsRefPtr for
+ * managing the lifetime of instances of this class.
  *
  * The generic implementation of this class is ChannelMediaResource, which can
  * handle any URI for which Necko supports AsyncOpen.
  * The 'file:' protocol can be implemented efficiently with direct random
  * access, so the FileMediaResource implementation class bypasses the cache.
  * MediaResource::Create automatically chooses the best implementation class.
  */
 class MediaResource
--- a/content/media/plugins/MediaPluginHost.cpp
+++ b/content/media/plugins/MediaPluginHost.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "MediaPluginHost.h"
 #include "nsXPCOMStrings.h"
 #include "nsISeekableStream.h"
 #include "MediaPluginReader.h"
 #include "nsIGfxInfo.h"
 #include "gfxCrashReporterUtils.h"
 #include "prmem.h"
+#include "MediaResourceServer.h"
 
 #include "MPAPI.h"
 
 #if defined(ANDROID) || defined(MOZ_WIDGET_GONK)
 #include "android/log.h"
 #define ALOG(args...)  __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args)
 #else
 #define ALOG(args...) /* do nothing */
@@ -29,50 +30,19 @@ using namespace MPAPI;
 
 Decoder::Decoder() :
   mResource(NULL), mPrivate(NULL)
 {
 }
 
 namespace mozilla {
 
-static MediaResource *GetResource(Decoder *aDecoder)
-{
-  return reinterpret_cast<MediaResource *>(aDecoder->mResource);
-}
-
-static bool Read(Decoder *aDecoder, char *aBuffer, int64_t aOffset, uint32_t aCount, uint32_t* aBytes)
+static char* GetResource(Decoder *aDecoder)
 {
-  MediaResource *resource = GetResource(aDecoder);
-  if (aOffset != resource->Tell()) {
-    nsresult rv = resource->Seek(nsISeekableStream::NS_SEEK_SET, aOffset);
-    if (NS_FAILED(rv)) {
-      return false;
-    }
-  }
-  nsresult rv = resource->Read(aBuffer, aCount, aBytes);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  return true;
-}
-
-static uint64_t GetLength(Decoder *aDecoder)
-{
-  return GetResource(aDecoder)->GetLength();
-}
-
-static void SetMetaDataReadMode(Decoder *aDecoder)
-{
-  GetResource(aDecoder)->SetReadMode(MediaCacheStream::MODE_METADATA);
-}
-
-static void SetPlaybackReadMode(Decoder *aDecoder)
-{
-  GetResource(aDecoder)->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
+  return static_cast<char*>(aDecoder->mResource);
 }
 
 class GetIntPrefEvent : public nsRunnable {
 public:
   GetIntPrefEvent(const char* aPref, int32_t* aResult)
     : mPref(aPref), mResult(aResult) {}
   NS_IMETHOD Run() {
     return Preferences::GetInt(mPref, mResult);
@@ -88,20 +58,20 @@ static bool GetIntPref(const char* aPref
   // can only be called on the main thread. Post a runnable and wait.
   NS_ENSURE_TRUE(aPref, false);
   NS_ENSURE_TRUE(aResult, false);
   nsCOMPtr<GetIntPrefEvent> event = new GetIntPrefEvent(aPref, aResult);
   return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC));
 }
 
 static PluginHost sPluginHost = {
-  Read,
-  GetLength,
-  SetMetaDataReadMode,
-  SetPlaybackReadMode,
+  nullptr,
+  nullptr,
+  nullptr,
+  nullptr,
   GetIntPref
 };
 
 // Return true if Omx decoding is supported on the device. This checks the
 // built in whitelist/blacklist and preferences to see if that is overridden.
 static bool IsOmxSupported()
 {
   bool forceEnabled =
@@ -217,16 +187,18 @@ static const char* GetOmxLibraryName()
 #else
   return nullptr;
 #endif
 }
 
 MediaPluginHost::MediaPluginHost() {
   MOZ_COUNT_CTOR(MediaPluginHost);
 
+  mResourceServer = MediaResourceServer::Start();
+
   const char* name = GetOmxLibraryName();
   ALOG("Loading OMX Plugin: %s", name ? name : "nullptr");
   if (name) {
     char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName);
     PRLibrary *lib = NULL;
     if (path) {
       nsAutoCString libpath(path);
       PR_Free(path);
@@ -246,16 +218,17 @@ MediaPluginHost::MediaPluginHost() {
         mPlugins.AppendElement(manifest);
         ALOG("OMX plugin successfully loaded");
      }
     }
   }
 }
 
 MediaPluginHost::~MediaPluginHost() {
+  mResourceServer->Stop();
   MOZ_COUNT_DTOR(MediaPluginHost);
 }
 
 bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs)
 {
   const char *chars;
   size_t len = NS_CStringGetData(aMimeType, &chars, nullptr);
   for (size_t n = 0; n < mPlugins.Length(); ++n) {
@@ -273,43 +246,49 @@ bool MediaPluginHost::FindDecoder(const 
 MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType)
 {
   NS_ENSURE_TRUE(aResource, nullptr);
 
   nsAutoPtr<Decoder> decoder(new Decoder());
   if (!decoder) {
     return nullptr;
   }
-  decoder->mResource = aResource;
 
   const char *chars;
   size_t len = NS_CStringGetData(aMimeType, &chars, nullptr);
   for (size_t n = 0; n < mPlugins.Length(); ++n) {
     Manifest *plugin = mPlugins[n];
     const char* const *codecs;
     if (!plugin->CanDecode(chars, len, &codecs)) {
       continue;
     }
+
+    nsCString url;
+    nsresult rv = mResourceServer->AddResource(aResource, url);
+    if (NS_FAILED (rv)) continue;
+
+    decoder->mResource = strdup(url.get());
     if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) {
       aResource->AddRef();
       return decoder.forget();
     }
   }
 
   return nullptr;
 }
 
 void MediaPluginHost::DestroyDecoder(Decoder *aDecoder)
 {
   aDecoder->DestroyDecoder(aDecoder);
-  MediaResource* resource = GetResource(aDecoder);
+  char* resource = GetResource(aDecoder);
   if (resource) {
     // resource *shouldn't* be null, but check anyway just in case the plugin
     // decoder does something stupid.
-    resource->Release();
+    mResourceServer->RemoveResource(nsCString(resource));
+    free(resource);
   }
   delete aDecoder;
 }
 
 MediaPluginHost *sMediaPluginHost = nullptr;
 MediaPluginHost *GetMediaPluginHost()
 {
   if (!sMediaPluginHost) {
--- a/content/media/plugins/MediaPluginHost.h
+++ b/content/media/plugins/MediaPluginHost.h
@@ -4,23 +4,26 @@
  * 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/. */
 #if !defined(MediaPluginHost_h_)
 #define MediaPluginHost_h_
 
 #include "nsTArray.h"
 #include "MediaResource.h"
 #include "MPAPI.h"
+#include "MediaResourceServer.h"
 
 namespace mozilla {
 
 class MediaPluginReader;
 
 class MediaPluginHost {
+  nsCOMPtr<MediaResourceServer> mResourceServer;
   nsTArray<MPAPI::Manifest *> mPlugins;
+
   MPAPI::Manifest *FindPlugin(const nsACString& aMimeType);
 public:
   MediaPluginHost();
   ~MediaPluginHost();
 
   static void Shutdown();
 
   bool FindDecoder(const nsACString& aMimeType, const char* const** aCodecs);
new file mode 100644
--- /dev/null
+++ b/content/media/plugins/MediaResourceServer.cpp
@@ -0,0 +1,526 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/Assertions.h"
+#include "mozilla/Base64.h"
+#include "nsThreadUtils.h"
+#include "nsIServiceManager.h"
+#include "nsISocketTransport.h"
+#include "nsIOutputStream.h"
+#include "nsIInputStream.h"
+#include "nsIRandomGenerator.h"
+#include "nsReadLine.h"
+#include "nsNetCID.h"
+#include "VideoUtils.h"
+#include "MediaResource.h"
+#include "MediaResourceServer.h"
+
+#if defined(_MSC_VER)
+#define strtoll _strtoi64
+#define snprintf _snprintf_s
+#endif
+
+using namespace mozilla;
+
+/*
+  ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals
+  with the carriage return/line feed requirements of HTTP requests.
+*/
+template<typename CharT, class StreamType, class StringType>
+nsresult
+ReadCRLF (StreamType* aStream, nsLineBuffer<CharT> * aBuffer,
+          StringType & aLine, bool *aMore)
+{
+  // eollast is true if the last character in the buffer is a '\r',
+  // signaling a potential '\r\n' sequence split between reads.
+  bool eollast = false;
+
+  aLine.Truncate();
+
+  while (1) { // will be returning out of this loop on eol or eof
+    if (aBuffer->start == aBuffer->end) { // buffer is empty.  Read into it.
+      uint32_t bytesRead;
+      nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead);
+      if (NS_FAILED(rv) || bytesRead == 0) {
+        *aMore = false;
+        return rv;
+      }
+      aBuffer->start = aBuffer->buf;
+      aBuffer->end = aBuffer->buf + bytesRead;
+      *(aBuffer->end) = '\0';
+    }
+
+    /*
+     * Walk the buffer looking for an end-of-line.
+     * There are 4 cases to consider:
+     *  1. the CR char is the last char in the buffer
+     *  2. the CRLF sequence are the last characters in the buffer
+     *  3. the CRLF sequence + one or more chars at the end of the buffer
+     *      we need at least one char after the first CRLF sequence to
+     *      set |aMore| correctly.
+     *  4. The LF character is the first char in the buffer when eollast is
+     *      true.
+     */
+    CharT* current = aBuffer->start;
+    if (eollast) { // Case 4
+      if (*current == '\n') {
+        aBuffer->start = ++current;
+        *aMore = true;
+        return NS_OK;
+      }
+      else {
+        eollast = false;
+        aLine.Append('\r');
+      }
+    }
+    // Cases 2 and 3
+    for ( ; current < aBuffer->end-1; ++current) {
+      if (*current == '\r' && *(current+1) == '\n') {
+        *current++ = '\0';
+        *current++ = '\0';
+        aLine.Append(aBuffer->start);
+        aBuffer->start = current;
+        *aMore = true;
+        return NS_OK;
+      }
+    }
+    // Case 1
+    if (*current == '\r') {
+      eollast = true;
+      *current++ = '\0';
+    }
+
+    aLine.Append(aBuffer->start);
+    aBuffer->start = aBuffer->end; // mark the buffer empty
+  }
+}
+
+// Each client HTTP request results in a thread being spawned to process it.
+// That thread has a single event dispatched to it which handles the HTTP
+// protocol. It parses the headers and forwards data from the MediaResource
+// associated with the URL back to client. When the request is complete it will
+// shutdown the thread.
+class ServeResourceEvent : public nsRunnable {
+private:
+  // Reading from this reads the data sent from the client.
+  nsCOMPtr<nsIInputStream> mInput;
+
+  // Writing to this sends data to the client.
+  nsCOMPtr<nsIOutputStream> mOutput;
+
+  // The MediaResourceServer that owns the MediaResource instances
+  // served. This is used to lookup the MediaResource from the URL.
+  nsCOMPtr<MediaResourceServer> mServer;
+
+  // Write 'aBufferLength' bytes from 'aBuffer' to 'mOutput'. This
+  // method ensures all the data is written by checking the number
+  // of bytes returned from the output streams 'Write' method and
+  // looping until done.
+  nsresult WriteAll(char const* aBuffer, int32_t aBufferLength);
+
+public:
+  ServeResourceEvent(nsIInputStream* aInput, nsIOutputStream* aOutput,
+                     MediaResourceServer* aServer)
+    : mInput(aInput), mOutput(aOutput), mServer(aServer) {}
+
+  // This method runs on the thread and exits when it has completed the
+  // HTTP request.
+  NS_IMETHOD Run();
+
+  // Given the first line of an HTTP request, parse the URL requested and
+  // return the MediaResource for that URL.
+  already_AddRefed<MediaResource> GetMediaResource(nsCString const& aHTTPRequest);
+
+  // Gracefully shutdown the thread and cleanup resources
+  void Shutdown();
+};
+
+nsresult
+ServeResourceEvent::WriteAll(char const* aBuffer, int32_t aBufferLength)
+{
+  while (aBufferLength > 0) {
+    uint32_t written = 0;
+    nsresult rv = mOutput->Write(aBuffer, aBufferLength, &written);
+    if (NS_FAILED (rv)) return rv;
+
+    aBufferLength -= written;
+    aBuffer += written;
+  }
+
+  return NS_OK;
+}
+
+already_AddRefed<MediaResource>
+ServeResourceEvent::GetMediaResource(nsCString const& aHTTPRequest)
+{
+  // Check that the HTTP method is GET
+  const char* HTTP_METHOD = "GET ";
+  if (strncmp(aHTTPRequest.get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
+    return nullptr;
+  }
+
+  const char* url_start = strchr(aHTTPRequest.get(), ' ');
+  if (!url_start) {
+    return nullptr;
+  }
+
+  const char* url_end = strrchr(++url_start, ' ');
+  if (!url_end) {
+    return nullptr;
+  }
+
+  // The path extracted from the HTTP request is used as a key in hash
+  // table. It is not related to retrieving data from the filesystem so
+  // we don't need to do any sanity checking on ".." paths and similar
+  // exploits.
+  nsCString relative(url_start, url_end - url_start);
+  nsRefPtr<MediaResource> resource =
+    mServer->GetResource(mServer->GetURLPrefix() + relative);
+  return resource.forget();
+}
+
+NS_IMETHODIMP
+ServeResourceEvent::Run() {
+  bool more = false; // Are there HTTP headers to read after the first line
+  nsCString line;    // Contains the current line read from input stream
+  nsLineBuffer<char>* buffer = new nsLineBuffer<char>();
+  nsresult rv = ReadCRLF(mInput.get(), buffer, line, &more);
+  if (NS_FAILED(rv)) { Shutdown(); return rv; }
+
+  // First line contains the HTTP GET request. Extract the URL and obtain
+  // the MediaResource for it.
+  nsRefPtr<MediaResource> resource = GetMediaResource(line);
+  if (!resource) {
+    const char* response_404 = "HTTP/1.1 404 Not Found\r\n"
+                               "Content-Length: 0\r\n\r\n";
+    rv = WriteAll(response_404, strlen(response_404));
+    Shutdown();
+    return rv;
+  }
+
+  // Offset in bytes to start reading from resource.
+  // This is zero by default but can be set to another starting value if
+  // this HTTP request includes a byte range request header.
+  int64_t start = 0;
+
+  // Keep reading lines until we get a zero length line, which is the HTTP
+  // protocol's way of signifying the end of headers and start of body, or
+  // until we have no more data to read.
+  while (more && line.Length() > 0) {
+    rv = ReadCRLF(mInput.get(), buffer, line, &more);
+    if (NS_FAILED(rv)) { Shutdown(); return rv; }
+
+    // Look for a byte range request header. If there is one, set the
+    // media resource offset to start from to that requested. Here we
+    // only check for the range request format used by Android rather
+    // than implementing all possibilities in the HTTP specification.
+    // That is, the range request is of the form:
+    //   Range: bytes=nnnn-
+    // Were 'nnnn' is an integer number.
+    // The end of the range is not checked, instead we return up to
+    // the end of the resource and the client is informed of this via
+    // the content-range header.
+    NS_NAMED_LITERAL_CSTRING(byteRange, "Range: bytes=");
+    const char* s = strstr(line.get(), byteRange.get());
+    if (s) {
+      start = strtoll(s+byteRange.Length(), NULL, 10);
+
+      // Clamp 'start' to be between 0 and the resource length.
+      start = std::max(0ll, std::min(resource->GetLength(), start));
+    }
+  }
+
+  // HTTP response to use if this is a non byte range request
+  const char* response_normal = "HTTP/1.1 200 OK\r\n";
+
+  // HTTP response to use if this is a byte range request
+  const char* response_range = "HTTP/1.1 206 Partial Content\r\n";
+
+  // End of HTTP reponse headers is indicated by an empty line.
+  const char* response_end = "\r\n";
+
+  // If the request was a byte range request, we need to read from the
+  // requested offset. If the resource is non-seekable, or the seek
+  // fails, then the start offset is set back to zero. This results in all
+  // HTTP response data being as if the byte range request was not made.
+  if (start > 0 && !resource->IsTransportSeekable()) {
+    start = 0;
+  }
+
+  const char* response_line = start > 0 ?
+                                response_range :
+                                response_normal;
+  rv = WriteAll(response_line, strlen(response_line));
+  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
+
+  // Buffer used for reading from the input stream and writing to
+  // the output stream. The buffer size should be big enough for the
+  // HTTP response headers sent below. A static_assert ensures
+  // this where the buffer is used.
+  const int buffer_size = 32768;
+  nsAutoArrayPtr<char> b(new char[buffer_size]);
+
+  // If we know the length of the resource, send a Content-Length header.
+  int64_t contentlength = resource->GetLength() - start;
+  if (contentlength > 0) {
+    static_assert (buffer_size > 1024,
+                   "buffer_size must be large enough "
+                   "to hold response headers");
+    snprintf(b, buffer_size, "Content-Length: %lld\r\n", contentlength);
+    rv = WriteAll(b, strlen(b));
+    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
+  }
+
+  // If the request was a byte range request, respond with a Content-Range
+  // header which details the extent of the data returned.
+  if (start > 0) {
+    static_assert (buffer_size > 1024,
+                   "buffer_size must be large enough "
+                   "to hold response headers");
+    snprintf(b, buffer_size, "Content-Range: bytes %lld-%lld/%lld\r\n",
+             start, resource->GetLength() - 1, resource->GetLength());
+    rv = WriteAll(b, strlen(b));
+    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
+  }
+
+  rv = WriteAll(response_end, strlen(response_end));
+  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
+
+  rv = mOutput->Flush();
+  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
+
+  // Read data from media resource
+  uint32_t bytesRead = 0; // Number of bytes read/written to streams
+  rv = resource->ReadAt(start, b, buffer_size, &bytesRead);
+  while (NS_SUCCEEDED(rv) && bytesRead != 0) {
+    // Keep track of what we think the starting position for the next read
+    // is. This is used in subsequent ReadAt calls to ensure we are reading
+    // from the correct offset in the case where another thread is reading
+    // from th same MediaResource.
+    start += bytesRead;
+
+    // Write data obtained from media resource to output stream
+    rv = WriteAll(b, bytesRead);
+    if (NS_FAILED (rv)) break;
+
+    rv = resource->ReadAt(start, b, 32768, &bytesRead);
+  }
+
+  Shutdown();
+  return NS_OK;
+}
+
+void
+ServeResourceEvent::Shutdown()
+{
+  // Cleanup resources and exit.
+  mInput->Close();
+  mOutput->Close();
+
+  // To shutdown the current thread we need to first exit this event.
+  // The Shutdown event below is posted to the main thread to do this.
+  nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(NS_GetCurrentThread());
+  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
+}
+
+/*
+  This is the listener attached to the server socket. When an HTTP
+  request is made by the client the OnSocketAccepted method is
+  called. This method will spawn a thread to process the request.
+  The thread receives a single event which does the parsing of
+  the HTTP request and forwarding the data from the MediaResource
+  to the output stream of the request.
+
+  The MediaResource used for providing the request data is obtained
+  from the MediaResourceServer that created this listener, using the
+  URL the client requested.
+*/
+class ResourceSocketListener : public nsIServerSocketListener
+{
+public:
+  // The MediaResourceServer used to look up the MediaResource
+  // on requests.
+  nsCOMPtr<MediaResourceServer> mServer;
+
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISERVERSOCKETLISTENER
+
+  ResourceSocketListener(MediaResourceServer* aServer) :
+    mServer(aServer)
+  {
+  }
+
+  virtual ~ResourceSocketListener() { }
+};
+
+NS_IMPL_ISUPPORTS1(ResourceSocketListener, nsIServerSocketListener)
+
+NS_IMETHODIMP
+ResourceSocketListener::OnSocketAccepted(nsIServerSocket* aServ,
+                                         nsISocketTransport* aTrans)
+{
+  nsCOMPtr<nsIInputStream> input;
+  nsCOMPtr<nsIOutputStream> output;
+  nsresult rv;
+
+  rv = aTrans->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(input));
+  if (NS_FAILED(rv)) return rv;
+
+  rv = aTrans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(output));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<nsIThread> thread;
+  rv = NS_NewThread(getter_AddRefs(thread));
+  if (NS_FAILED(rv)) return rv;
+
+  nsCOMPtr<nsIRunnable> event = new ServeResourceEvent(input.get(), output.get(), mServer);
+  return thread->Dispatch(event, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+ResourceSocketListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus)
+{
+  return NS_OK;
+}
+
+MediaResourceServer::MediaResourceServer() :
+  mMutex("MediaResourceServer")
+{
+}
+
+NS_IMETHODIMP
+MediaResourceServer::Run()
+{
+  MutexAutoLock lock(mMutex);
+
+  nsresult rv;
+  mSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID, &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = mSocket->InitSpecialConnection(-1,
+                                      nsIServerSocket::LoopbackOnly
+                                      | nsIServerSocket::KeepWhenOffline,
+                                      -1);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = mSocket->AsyncListen(new ResourceSocketListener(this));
+  if (NS_FAILED(rv)) return rv;
+
+  return NS_OK;
+}
+
+/* static */
+already_AddRefed<MediaResourceServer>
+MediaResourceServer::Start()
+{
+  nsCOMPtr<MediaResourceServer> server = new MediaResourceServer();
+  NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
+  return server.forget();
+}
+
+void
+MediaResourceServer::Stop()
+{
+  MutexAutoLock lock(mMutex);
+  mSocket->Close();
+  mSocket = nullptr;
+}
+
+nsresult
+MediaResourceServer::AppendRandomPath(nsCString& aUrl)
+{
+  // Use a cryptographic quality PRNG to generate raw random bytes
+  // and convert that to a base64 string for use as an URL path. This
+  // is based on code from nsExternalAppHandler::SetUpTempFile.
+  nsresult rv;
+  nsCOMPtr<nsIRandomGenerator> rg =
+    do_GetService("@mozilla.org/security/random-generator;1", &rv);
+  if (NS_FAILED(rv)) return rv;
+
+  // For each three bytes of random data we will get four bytes of
+  // ASCII. Request a bit more to be safe and truncate to the length
+  // we want at the end.
+  const uint32_t wantedFileNameLength = 16;
+  const uint32_t requiredBytesLength =
+    static_cast<uint32_t>((wantedFileNameLength + 1) / 4 * 3);
+
+  uint8_t* buffer;
+  rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
+  if (NS_FAILED(rv)) return rv;
+
+  nsAutoCString tempLeafName;
+  nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer),
+                                   requiredBytesLength);
+  rv = Base64Encode(randomData, tempLeafName);
+  NS_Free(buffer);
+  buffer = nullptr;
+  if (NS_FAILED (rv)) return rv;
+
+  tempLeafName.Truncate(wantedFileNameLength);
+
+  // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
+  // to replace illegal characters -- notably '/'
+  tempLeafName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
+
+  aUrl += "/";
+  aUrl += tempLeafName;
+
+  return NS_OK;
+}
+
+nsresult
+MediaResourceServer::AddResource(mozilla::MediaResource* aResource, nsCString& aUrl)
+{
+  nsCString url = GetURLPrefix();
+  nsresult rv = AppendRandomPath(url);
+  if (NS_FAILED (rv)) return rv;
+
+  {
+    MutexAutoLock lock(mMutex);
+
+    // Adding a resource URL that already exists is considered an error.
+    if (mResources.find(aUrl) != mResources.end()) return NS_ERROR_FAILURE;
+    mResources[url] = aResource;
+  }
+
+  aUrl = url;
+
+  return NS_OK;
+}
+
+void
+MediaResourceServer::RemoveResource(nsCString const& aUrl)
+{
+  MutexAutoLock lock(mMutex);
+  mResources.erase(aUrl);
+}
+
+nsCString
+MediaResourceServer::GetURLPrefix()
+{
+  MutexAutoLock lock(mMutex);
+
+  int32_t port = 0;
+  nsresult rv = mSocket->GetPort(&port);
+  if (NS_FAILED (rv) || port < 0) {
+    return nsCString("");
+  }
+
+  char buffer[256];
+  snprintf(buffer, sizeof(buffer), "http://127.0.0.1:%d", port >= 0 ? port : 0);
+  return nsCString(buffer);
+}
+
+already_AddRefed<MediaResource>
+MediaResourceServer::GetResource(nsCString const& aUrl)
+{
+  MutexAutoLock lock(mMutex);
+  ResourceMap::const_iterator it = mResources.find(aUrl);
+  if (it == mResources.end()) return nullptr;
+
+  nsRefPtr<MediaResource> resource = it->second;
+  return resource.forget();
+}
new file mode 100644
--- /dev/null
+++ b/content/media/plugins/MediaResourceServer.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+#if !defined(MediaResourceServer_h_)
+#define MediaResourceServer_h_
+
+#include <map>
+#include "nsIServerSocket.h"
+#include "MediaResource.h"
+
+namespace mozilla {
+
+class MediaResource;
+
+/*
+  MediaResourceServer instantiates a socket server that understands
+  HTTP requests for MediaResource instances. The server runs on an
+  automatically selected port and MediaResource instances are registered.
+  The registration returns a string URL than can be used to fetch the
+  resource. That URL contains a randomly generated path to make it
+  difficult for other local applications on the device to guess it.
+
+  The HTTP protocol is limited in that it supports only what the
+  Android DataSource implementation uses to fetch media. It
+  understands HTTP GET and byte range requests.
+
+  The intent of this class is to be used in Media backends that
+  have a system component that does its own network requests. These
+  requests are made against this server which then uses standard
+  Gecko network requests and media cache usage.
+
+  The MediaResourceServer can be instantiated on any thread and
+  its methods are threadsafe - they can be called on any thread.
+  The server socket itself is always run on the main thread and
+  this is done by the Start() static method by synchronously
+  dispatching to the main thread.
+*/
+class MediaResourceServer : public nsRunnable
+{
+private:
+  // Mutex protecting private members of MediaResourceServer.
+  // All member variables below this point in the class definition
+  // must acquire the mutex before access.
+  mozilla::Mutex mMutex;
+
+  // Server socket used to listen for incoming connections
+  nsCOMPtr<nsIServerSocket> mSocket;
+
+  // Mapping between MediaResource URL's to the MediaResource
+  // object served at that URL.
+  typedef std::map<nsCString,
+                  nsRefPtr<mozilla::MediaResource> > ResourceMap;
+  ResourceMap mResources;
+
+  // Create a MediaResourceServer that will listen on an automatically
+  // selected port when started. This is private as it should only be
+  // called internally from the public 'Start' method.
+  MediaResourceServer();
+  NS_IMETHOD Run();
+
+  // Append a random URL path to a string. This is used for creating a
+  // unique URl for a resource which helps prevent malicious software
+  // running on the same machine as the server from guessing the URL
+  // and accessing video data.
+  nsresult AppendRandomPath(nsCString& aURL);
+
+public:
+  // Create a MediaResourceServer and start it listening. This call will
+  // perform a synchronous request on the main thread.
+  static already_AddRefed<MediaResourceServer> Start();
+
+  // Stops the server from listening and accepting further connections.
+  void Stop();
+
+  // Add a MediaResource to be served by this server. Stores the
+  // absolute URL that can be used to access the resource in 'aUrl'.
+  nsresult AddResource(mozilla::MediaResource* aResource, nsCString& aUrl);
+
+  // Remove a MediaResource so it is no longer served by this server.
+  // The URL provided must match exactly that provided by a previous
+  // call to "AddResource".
+  void RemoveResource(nsCString const& aUrl);
+
+  // Returns the prefix for HTTP requests to the server. This plus
+  // the result of AddResource results in an Absolute URL.
+  nsCString GetURLPrefix();
+
+  // Returns the resource asociated with a given URL
+  already_AddRefed<mozilla::MediaResource> GetResource(nsCString const& aUrl);
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/media/plugins/moz.build
+++ b/content/media/plugins/moz.build
@@ -6,18 +6,20 @@
 
 MODULE = 'content'
 
 EXPORTS += [
     'MPAPI.h',
     'MediaPluginDecoder.h',
     'MediaPluginHost.h',
     'MediaPluginReader.h',
+    'MediaResourceServer.h',
 ]
 
 CPP_SOURCES += [
     'MediaPluginDecoder.cpp',
     'MediaPluginHost.cpp',
     'MediaPluginReader.cpp',
+    'MediaResourceServer.cpp',
 ]
 
 LIBRARY_NAME = 'gkconmediaplugins_s'
 
--- a/media/omx-plugin/OmxPlugin.cpp
+++ b/media/omx-plugin/OmxPlugin.cpp
@@ -33,101 +33,20 @@
 
 #if defined(MOZ_ANDROID_FROYO) || defined(MOZ_ANDROID_GB)
 // Android versions 2.x.x have common API differences
 #define MOZ_ANDROID_V2_X_X
 #endif
 
 using namespace MPAPI;
 
-namespace android {
-
 #if !defined(MOZ_STAGEFRIGHT_OFF_T)
 #define MOZ_STAGEFRIGHT_OFF_T off64_t
 #endif
 
-// MediaStreamSource is a DataSource that reads from a MPAPI media stream.
-class MediaStreamSource : public DataSource {
-  PluginHost *mPluginHost;
-public:
-  MediaStreamSource(PluginHost *aPluginHost, Decoder *aDecoder);
-
-  virtual status_t initCheck() const;
-  virtual ssize_t readAt(MOZ_STAGEFRIGHT_OFF_T offset, void *data, size_t size);
-  virtual status_t getSize(MOZ_STAGEFRIGHT_OFF_T *size);
-  virtual uint32_t flags() {
-    return kWantsPrefetching;
-  }
-
-  virtual ~MediaStreamSource();
-
-private:
-  Decoder *mDecoder;
-
-  MediaStreamSource(const MediaStreamSource &);
-  MediaStreamSource &operator=(const MediaStreamSource &);
-
-#ifdef MOZ_ANDROID_HTC_WORKAROUND
-  // libstagefright on some Jellybean HTC devices (at least the Tegra 3 One X)
-  // calls this function and expects this magic number to be returned when
-  // sniffing audio stream formats.
-  // It is unclear what this is for or what it does.
-  virtual uint32_t MagicalHTCIncantation() { return 0x3f0; }
-#endif
-};
-
-MediaStreamSource::MediaStreamSource(PluginHost *aPluginHost, Decoder *aDecoder) :
-  mPluginHost(aPluginHost)
-{
-  mDecoder = aDecoder;
-}
-
-MediaStreamSource::~MediaStreamSource()
-{
-}
-
-status_t MediaStreamSource::initCheck() const
-{
-  return OK;
-}
-
-ssize_t MediaStreamSource::readAt(MOZ_STAGEFRIGHT_OFF_T offset, void *data, size_t size)
-{
-  char *ptr = reinterpret_cast<char *>(data);
-  size_t todo = size;
-  while (todo > 0) {
-    uint32_t bytesRead;
-    if (!mPluginHost->Read(mDecoder, ptr, offset, todo, &bytesRead)) {
-      return ERROR_IO;
-    }
-
-    if (bytesRead == 0) {
-      return size - todo;
-    }
-
-    offset += bytesRead;
-    todo -= bytesRead;
-    ptr += bytesRead;
-  }
-  return size;
-}
-
-status_t MediaStreamSource::getSize(MOZ_STAGEFRIGHT_OFF_T *size)
-{
-  uint64_t length = mPluginHost->GetLength(mDecoder);
-  if (length == static_cast<uint64_t>(-1))
-    return ERROR_UNSUPPORTED;
-
-  *size = length;
-
-  return OK;
-}
-
-}  // namespace android
-
 using namespace android;
 
 namespace OmxPlugin {
 
 const int OMX_QCOM_COLOR_FormatYVU420PackedSemiPlanar32m4ka = 0x7FA30C01;
 const int OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00;
 const int OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100;
 
@@ -364,23 +283,22 @@ static sp<MediaSource> CreateVideoSource
   return OMXCodec::Create(aOmx, aVideoTrack->getFormat(), false, aVideoTrack,
                           NULL, flags);
 }
 
 bool OmxDecoder::Init() {
   //register sniffers, if they are not registered in this process.
   DataSource::RegisterDefaultSniffers();
 
-  sp<DataSource> dataSource = new MediaStreamSource(mPluginHost, mDecoder);
-  if (dataSource->initCheck()) {
+  sp<DataSource> dataSource =
+    DataSource::CreateFromURI(static_cast<char*>(mDecoder->mResource));
+  if (!dataSource.get() || dataSource->initCheck()) {
     return false;
   }
 
-  mPluginHost->SetMetaDataReadMode(mDecoder);
-
   sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
   if (extractor == NULL) {
     return false;
   }
 
   ssize_t audioTrackIndex = -1;
   ssize_t videoTrackIndex = -1;
   const char *audioMime = NULL;
@@ -402,18 +320,16 @@ bool OmxDecoder::Init() {
       audioMime = mime;
     }
   }
 
   if (videoTrackIndex == -1 && audioTrackIndex == -1) {
     return false;
   }
 
-  mPluginHost->SetPlaybackReadMode(mDecoder);
-
   int64_t totalDurationUs = 0;
 
 #ifdef MOZ_WIDGET_GONK
   sp<IOMX> omx = GetOMX();
 #else
   // OMXClient::connect() always returns OK and abort's fatally if
   // it can't connect. We may need to implement the connect functionality
   // ourselves if this proves to be an issue.
--- a/media/omx-plugin/lib/gb/libstagefright/libstagefright.cpp
+++ b/media/omx-plugin/lib/gb/libstagefright/libstagefright.cpp
@@ -138,23 +138,9 @@ MOZ_EXPORT status_t OMXClient::connect()
 {
   return OK;
 }
 
 MOZ_EXPORT void OMXClient::disconnect()
 {
 }
 
-class __attribute__ ((visibility ("default"))) UnknownDataSource : public DataSource {
-public:
-UnknownDataSource();
-
-virtual status_t initCheck() const { return 0; }
-virtual ssize_t readAt(MOZ_STAGEFRIGHT_OFF_T offset, void *data, size_t size) { return 0; }
-virtual status_t getSize(MOZ_STAGEFRIGHT_OFF_T *size) { return 0; }
-
-virtual ~UnknownDataSource() { }
-};
-
-UnknownDataSource foo;
-
-MOZ_EXPORT UnknownDataSource::UnknownDataSource() { }
 }
--- a/media/omx-plugin/lib/ics/libstagefright/libstagefright.cpp
+++ b/media/omx-plugin/lib/ics/libstagefright/libstagefright.cpp
@@ -90,16 +90,22 @@ MOZ_EXPORT MediaSource::ReadOptions::Rea
 {
 }
 
 MOZ_EXPORT void
 MediaSource::ReadOptions::setSeekTo(int64_t time_us, SeekMode mode)
 {
 }
 
+MOZ_EXPORT sp<DataSource> DataSource::CreateFromURI(
+          const char *uri,
+          const KeyedVector<String8, String8> *headers) {
+  return 0;
+}
+
 MOZ_EXPORT bool
 DataSource::getUInt16(off64_t offset, uint16_t *x)
 {
   return false;
 }
 
 MOZ_EXPORT status_t
 DataSource::getSize(off64_t *size)
@@ -144,31 +150,16 @@ MOZ_EXPORT status_t OMXClient::connect()
 {
   return OK;
 }
 
 MOZ_EXPORT void OMXClient::disconnect()
 {
 }
 
-class __attribute__ ((visibility ("default"))) UnknownDataSource : public DataSource {
-public:
-UnknownDataSource();
-
-virtual status_t initCheck() const { return 0; }
-virtual ssize_t readAt(off64_t offset, void *data, size_t size) { return 0; }
-virtual status_t getSize(off64_t *size) { return 0; }
-
-virtual ~UnknownDataSource() { }
-};
-
-UnknownDataSource foo;
-
-MOZ_EXPORT UnknownDataSource::UnknownDataSource() { }
-
 MOZ_EXPORT
 ColorConverter::ColorConverter(OMX_COLOR_FORMATTYPE, OMX_COLOR_FORMATTYPE) { }
 
 MOZ_EXPORT
 ColorConverter::~ColorConverter() { }
 
 MOZ_EXPORT bool
 ColorConverter::isValid() const { return false; }