mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-28 15:23:51 +00:00
Bug 860599 - Use DataSource::CreateFromURI instead of MediaStreamSource - r=cpearce
--HG-- extra : rebase_source : da5a909d64ad903f86ad7fb76cb306252d8ccaad
This commit is contained in:
parent
7cc0542786
commit
3dc98ebb4b
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
526
content/media/plugins/MediaResourceServer.cpp
Normal file
526
content/media/plugins/MediaResourceServer.cpp
Normal 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();
|
||||
}
|
96
content/media/plugins/MediaResourceServer.h
Normal file
96
content/media/plugins/MediaResourceServer.h
Normal 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
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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() { }
|
||||
}
|
||||
|
@ -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) { }
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user