Bug 860599 - Use DataSource::CreateFromURI instead of MediaStreamSource - r=cpearce

--HG--
extra : rebase_source : da5a909d64ad903f86ad7fb76cb306252d8ccaad
This commit is contained in:
Chris Double 2013-08-30 16:51:38 +12:00
parent 7cc0542786
commit 3dc98ebb4b
9 changed files with 657 additions and 157 deletions

View File

@ -193,7 +193,8 @@ inline MediaByteRange::MediaByteRange(TimestampedMediaByteRange& aByteRange)
* 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.

View File

@ -14,6 +14,7 @@
#include "nsIGfxInfo.h"
#include "gfxCrashReporterUtils.h"
#include "prmem.h"
#include "MediaResourceServer.h"
#include "MPAPI.h"
@ -33,40 +34,9 @@ Decoder::Decoder() :
namespace mozilla {
static MediaResource *GetResource(Decoder *aDecoder)
static char* 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)
{
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 {
@ -92,10 +62,10 @@ static bool GetIntPref(const char* aPref, int32_t* aResult)
}
static PluginHost sPluginHost = {
Read,
GetLength,
SetMetaDataReadMode,
SetPlaybackReadMode,
nullptr,
nullptr,
nullptr,
nullptr,
GetIntPref
};
@ -221,6 +191,8 @@ static const char* GetOmxLibraryName()
MediaPluginHost::MediaPluginHost() {
MOZ_COUNT_CTOR(MediaPluginHost);
mResourceServer = MediaResourceServer::Start();
const char* name = GetOmxLibraryName();
ALOG("Loading OMX Plugin: %s", name ? name : "nullptr");
if (name) {
@ -250,6 +222,7 @@ MediaPluginHost::MediaPluginHost() {
}
MediaPluginHost::~MediaPluginHost() {
mResourceServer->Stop();
MOZ_COUNT_DTOR(MediaPluginHost);
}
@ -277,7 +250,6 @@ MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const n
if (!decoder) {
return nullptr;
}
decoder->mResource = aResource;
const char *chars;
size_t len = NS_CStringGetData(aMimeType, &chars, nullptr);
@ -287,6 +259,12 @@ MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const n
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();
@ -299,11 +277,12 @@ MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const n
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;
}

View File

@ -9,13 +9,16 @@
#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();

View File

@ -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();
}

View File

@ -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

View File

@ -11,12 +11,14 @@ EXPORTS += [
'MediaPluginDecoder.h',
'MediaPluginHost.h',
'MediaPluginReader.h',
'MediaResourceServer.h',
]
CPP_SOURCES += [
'MediaPluginDecoder.cpp',
'MediaPluginHost.cpp',
'MediaPluginReader.cpp',
'MediaResourceServer.cpp',
]
LIBRARY_NAME = 'gkconmediaplugins_s'

View File

@ -38,91 +38,10 @@
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 {
@ -369,13 +288,12 @@ 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;
@ -407,8 +325,6 @@ bool OmxDecoder::Init() {
return false;
}
mPluginHost->SetPlaybackReadMode(mDecoder);
int64_t totalDurationUs = 0;
#ifdef MOZ_WIDGET_GONK

View File

@ -143,18 +143,4 @@ 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() { }
}

View File

@ -95,6 +95,12 @@ 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)
{
@ -149,21 +155,6 @@ 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) { }