Remove AndroidMediaDecoder and friends

They are no longer supported and don't work with newer Android versions anyway.
This commit is contained in:
trav90 2018-12-16 08:02:30 -06:00 committed by Roy Tam
commit 129388e5e0
15 changed files with 0 additions and 1791 deletions

View file

@ -18,11 +18,6 @@
#include "WebMDecoder.h"
#include "WebMDemuxer.h"
#ifdef MOZ_ANDROID_OMX
#include "AndroidMediaDecoder.h"
#include "AndroidMediaReader.h"
#include "AndroidMediaPluginHost.h"
#endif
#ifdef MOZ_FMP4
#include "MP4Decoder.h"
#include "MP4Demuxer.h"
@ -106,21 +101,6 @@ IsHttpLiveStreamingType(const nsACString& aType)
return CodecListContains(gHttpLiveStreamingTypes, aType);
}
#ifdef MOZ_ANDROID_OMX
static bool
IsAndroidMediaType(const nsACString& aType)
{
if (!MediaDecoder::IsAndroidMediaPluginEnabled()) {
return false;
}
static const char* supportedTypes[] = {
"audio/mpeg", "audio/mp4", "video/mp4", "video/x-m4v", nullptr
};
return CodecListContains(supportedTypes, aType);
}
#endif
#ifdef MOZ_FMP4
static bool
IsMP4SupportedType(const MediaContentType& aParsedType,
@ -235,11 +215,6 @@ CanHandleCodecsType(const MediaContentType& aType,
if (IsFlacSupportedType(aType.GetMIMEType(), aType.GetCodecs())) {
return CANPLAY_YES;
}
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled()) {
EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), &codecList);
}
#endif
if (!codecList) {
return CANPLAY_MAYBE;
}
@ -305,12 +280,6 @@ CanHandleMediaType(const MediaContentType& aType,
if (IsFlacSupportedType(aType.GetMIMEType())) {
return CANPLAY_MAYBE;
}
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType.GetMIMEType(), nullptr)) {
return CANPLAY_MAYBE;
}
#endif
return CANPLAY_NO;
}
@ -391,13 +360,6 @@ InstantiateDecoder(const nsACString& aType,
decoder = new FlacDecoder(aOwner);
return decoder.forget();
}
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
decoder = new AndroidMediaDecoder(aOwner, aType);
return decoder.forget();
}
#endif
if (IsWebMSupportedType(aType)) {
decoder = new WebMDecoder(aOwner);
@ -446,12 +408,6 @@ MediaDecoderReader* DecoderTraits::CreateReader(const nsACString& aType, Abstrac
if (IsOggSupportedType(aType)) {
decoderReader = new MediaFormatReader(aDecoder, new OggDemuxer(aDecoder->GetResource()));
} else
#ifdef MOZ_ANDROID_OMX
if (MediaDecoder::IsAndroidMediaPluginEnabled() &&
EnsureAndroidMediaPluginHost()->FindDecoder(aType, nullptr)) {
decoderReader = new AndroidMediaReader(aDecoder, aType);
} else
#endif
if (IsWebMSupportedType(aType)) {
decoderReader =
new MediaFormatReader(aDecoder, new WebMDemuxer(aDecoder->GetResource()));
@ -474,9 +430,6 @@ bool DecoderTraits::IsSupportedInVideoDocument(const nsACString& aType)
return
IsOggSupportedType(aType) ||
IsWebMSupportedType(aType) ||
#ifdef MOZ_ANDROID_OMX
(MediaDecoder::IsAndroidMediaPluginEnabled() && IsAndroidMediaType(aType)) ||
#endif
#ifdef MOZ_FMP4
IsMP4SupportedType(aType, /* DecoderDoctorDiagnostics* */ nullptr) ||
#endif

View file

@ -35,10 +35,6 @@
#include "Layers.h"
#include "mozilla/layers/ShadowLayers.h"
#ifdef MOZ_ANDROID_OMX
#include "AndroidBridge.h"
#endif
using namespace mozilla::dom;
using namespace mozilla::layers;
using namespace mozilla::media;
@ -1617,16 +1613,6 @@ MediaDecoder::IsWebMEnabled()
return Preferences::GetBool("media.webm.enabled");
}
#ifdef MOZ_ANDROID_OMX
bool
MediaDecoder::IsAndroidMediaPluginEnabled()
{
return AndroidBridge::Bridge() &&
AndroidBridge::Bridge()->GetAPIVersion() < 16 &&
Preferences::GetBool("media.plugins.enabled");
}
#endif
NS_IMETHODIMP
MediaMemoryTracker::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize)

View file

@ -447,10 +447,6 @@ private:
static bool IsWaveEnabled();
static bool IsWebMEnabled();
#ifdef MOZ_ANDROID_OMX
static bool IsAndroidMediaPluginEnabled();
#endif
#ifdef MOZ_WMF
static bool IsWMFEnabled();
#endif

View file

@ -1,25 +0,0 @@
/* -*- 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 "MediaDecoderStateMachine.h"
#include "AndroidMediaDecoder.h"
#include "AndroidMediaReader.h"
namespace mozilla {
AndroidMediaDecoder::AndroidMediaDecoder(MediaDecoderOwner* aOwner,
const nsACString& aType)
: MediaDecoder(aOwner), mType(aType)
{
}
MediaDecoderStateMachine* AndroidMediaDecoder::CreateStateMachine()
{
return new MediaDecoderStateMachine(this, new AndroidMediaReader(this, mType));
}
} // namespace mozilla

View file

@ -1,28 +0,0 @@
/* -*- 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(AndroidMediaDecoder_h_)
#define AndroidMediaDecoder_h_
#include "MediaDecoder.h"
#include "AndroidMediaDecoder.h"
namespace mozilla {
class AndroidMediaDecoder : public MediaDecoder
{
nsCString mType;
public:
AndroidMediaDecoder(MediaDecoderOwner* aOwner, const nsACString& aType);
MediaDecoder* Clone(MediaDecoderOwner* aOwner) override {
return new AndroidMediaDecoder(aOwner, mType);
}
MediaDecoderStateMachine* CreateStateMachine() override;
};
} // namespace mozilla
#endif

View file

@ -1,305 +0,0 @@
/* -*- 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/Preferences.h"
#include "MediaResource.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/Services.h"
#include "AndroidMediaPluginHost.h"
#include "nsAutoPtr.h"
#include "nsXPCOMStrings.h"
#include "nsISeekableStream.h"
#include "nsIGfxInfo.h"
#include "prmem.h"
#include "prlink.h"
#include "AndroidMediaResourceServer.h"
#include "nsServiceManagerUtils.h"
#include "MPAPI.h"
#include "nsIPropertyBag2.h"
#if defined(ANDROID)
#include "android/log.h"
#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaPluginHost" , ## args)
#else
#define ALOG(args...) /* do nothing */
#endif
using namespace MPAPI;
Decoder::Decoder() :
mResource(nullptr), mPrivate(nullptr)
{
}
namespace mozilla {
static char* GetResource(Decoder *aDecoder)
{
return static_cast<char*>(aDecoder->mResource);
}
class GetIntPrefEvent : public Runnable {
public:
GetIntPrefEvent(const char* aPref, int32_t* aResult)
: mPref(aPref), mResult(aResult) {}
NS_IMETHOD Run() override {
return Preferences::GetInt(mPref, mResult);
}
private:
const char* mPref;
int32_t* mResult;
};
static bool GetIntPref(const char* aPref, int32_t* aResult)
{
// GetIntPref() is called on the decoder thread, but the Preferences API
// can only be called on the main thread. Post a runnable and wait.
NS_ENSURE_TRUE(aPref, false);
NS_ENSURE_TRUE(aResult, false);
nsCOMPtr<nsIRunnable> event = new GetIntPrefEvent(aPref, aResult);
return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC));
}
static bool
GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength)
{
NS_ENSURE_TRUE(aKey, false);
NS_ENSURE_TRUE(aResult, false);
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
NS_ASSERTION(infoService, "Could not find a system info service");
nsAutoCString key(aKey);
nsAutoCString info;
nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key),
info);
NS_ENSURE_SUCCESS(rv, false);
strncpy(aResult, info.get(), aResultLength);
return true;
}
static PluginHost sPluginHost = {
nullptr,
nullptr,
nullptr,
nullptr,
GetIntPref,
GetSystemInfoString
};
// 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 =
Preferences::GetBool("stagefright.force-enabled", false);
bool disabled =
Preferences::GetBool("stagefright.disabled", false);
if (disabled) {
NS_WARNING("XXX stagefright disabled\n");
return false;
}
if (!forceEnabled) {
nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
if (gfxInfo) {
int32_t status;
nsCString discardFailure;
if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, discardFailure, &status))) {
if (status != nsIGfxInfo::FEATURE_STATUS_OK) {
NS_WARNING("XXX stagefright blacklisted\n");
return false;
}
}
}
}
return true;
}
// Return the name of the shared library that implements Omx based decoding. This varies
// depending on libstagefright version installed on the device and whether it is B2G vs Android.
// nullptr is returned if Omx decoding is not supported on the device,
static const char* GetOmxLibraryName()
{
#if defined(ANDROID)
nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1");
NS_ASSERTION(infoService, "Could not find a system info service");
int32_t version;
nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version);
if (NS_SUCCEEDED(rv)) {
ALOG("Android Version is: %d", version);
}
nsAutoString release_version;
rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version);
if (NS_SUCCEEDED(rv)) {
ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get());
}
nsAutoString device;
rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device);
if (NS_SUCCEEDED(rv)) {
ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get());
}
nsAutoString manufacturer;
rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer);
if (NS_SUCCEEDED(rv)) {
ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get());
}
nsAutoString hardware;
rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware);
if (NS_SUCCEEDED(rv)) {
ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get());
}
#endif
if (!IsOmxSupported())
return nullptr;
#if defined(ANDROID)
if (version >= 17) {
return "libomxpluginkk.so";
}
// Ice Cream Sandwich and Jellybean
return "libomxplugin.so";
#else
return nullptr;
#endif
}
AndroidMediaPluginHost::AndroidMediaPluginHost() {
MOZ_COUNT_CTOR(AndroidMediaPluginHost);
MOZ_ASSERT(NS_IsMainThread());
mResourceServer = AndroidMediaResourceServer::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 = nullptr;
if (path) {
nsAutoCString libpath(path);
PR_Free(path);
int32_t slash = libpath.RFindChar('/');
if (slash != kNotFound) {
libpath.Truncate(slash + 1);
libpath.Append(name);
lib = PR_LoadLibrary(libpath.get());
}
}
if (!lib)
lib = PR_LoadLibrary(name);
if (lib) {
Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST"));
if (manifest) {
mPlugins.AppendElement(manifest);
ALOG("OMX plugin successfully loaded");
}
}
}
}
AndroidMediaPluginHost::~AndroidMediaPluginHost() {
mResourceServer->Stop();
MOZ_COUNT_DTOR(AndroidMediaPluginHost);
}
bool AndroidMediaPluginHost::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) {
Manifest *plugin = mPlugins[n];
const char* const *codecs;
if (plugin->CanDecode(chars, len, &codecs)) {
if (aCodecs)
*aCodecs = codecs;
return true;
}
}
return false;
}
MPAPI::Decoder *AndroidMediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType)
{
NS_ENSURE_TRUE(aResource, nullptr);
nsAutoPtr<Decoder> decoder(new Decoder());
if (!decoder) {
return nullptr;
}
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)) {
return decoder.forget();
}
}
return nullptr;
}
void AndroidMediaPluginHost::DestroyDecoder(Decoder *aDecoder)
{
aDecoder->DestroyDecoder(aDecoder);
char* resource = GetResource(aDecoder);
if (resource) {
// resource *shouldn't* be null, but check anyway just in case the plugin
// decoder does something stupid.
mResourceServer->RemoveResource(nsCString(resource));
free(resource);
}
delete aDecoder;
}
AndroidMediaPluginHost *sAndroidMediaPluginHost = nullptr;
AndroidMediaPluginHost *EnsureAndroidMediaPluginHost()
{
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
if (!sAndroidMediaPluginHost) {
sAndroidMediaPluginHost = new AndroidMediaPluginHost();
}
return sAndroidMediaPluginHost;
}
AndroidMediaPluginHost *GetAndroidMediaPluginHost()
{
MOZ_ASSERT(sAndroidMediaPluginHost);
return sAndroidMediaPluginHost;
}
void AndroidMediaPluginHost::Shutdown()
{
delete sAndroidMediaPluginHost;
sAndroidMediaPluginHost = nullptr;
}
} // namespace mozilla

View file

@ -1,41 +0,0 @@
/* -*- 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(AndroidMediaPluginHost_h_)
#define AndroidMediaPluginHost_h_
#include "nsTArray.h"
#include "MediaResource.h"
#include "MPAPI.h"
#include "AndroidMediaResourceServer.h"
namespace mozilla {
class AndroidMediaPluginHost {
RefPtr<AndroidMediaResourceServer> mResourceServer;
nsTArray<MPAPI::Manifest *> mPlugins;
MPAPI::Manifest *FindPlugin(const nsACString& aMimeType);
public:
AndroidMediaPluginHost();
~AndroidMediaPluginHost();
static void Shutdown();
bool FindDecoder(const nsACString& aMimeType, const char* const** aCodecs);
MPAPI::Decoder *CreateDecoder(mozilla::MediaResource *aResource, const nsACString& aMimeType);
void DestroyDecoder(MPAPI::Decoder *aDecoder);
};
// Must be called on the main thread. Creates the plugin host if it doesn't
// already exist.
AndroidMediaPluginHost *EnsureAndroidMediaPluginHost();
// May be called on any thread after EnsureAndroidMediaPluginHost has been called.
AndroidMediaPluginHost *GetAndroidMediaPluginHost();
} // namespace mozilla
#endif

View file

@ -1,449 +0,0 @@
/* -*- 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 "AndroidMediaReader.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/gfx/Point.h"
#include "MediaResource.h"
#include "VideoUtils.h"
#include "AndroidMediaDecoder.h"
#include "AndroidMediaPluginHost.h"
#include "MediaDecoderStateMachine.h"
#include "ImageContainer.h"
#include "AbstractMediaDecoder.h"
#include "gfx2DGlue.h"
#include "VideoFrameContainer.h"
#include "mozilla/CheckedInt.h"
namespace mozilla {
using namespace mozilla::gfx;
using namespace mozilla::media;
typedef mozilla::layers::Image Image;
typedef mozilla::layers::PlanarYCbCrImage PlanarYCbCrImage;
AndroidMediaReader::AndroidMediaReader(AbstractMediaDecoder *aDecoder,
const nsACString& aContentType) :
MediaDecoderReader(aDecoder),
mType(aContentType),
mPlugin(nullptr),
mHasAudio(false),
mHasVideo(false),
mVideoSeekTimeUs(-1),
mAudioSeekTimeUs(-1)
{
}
nsresult AndroidMediaReader::ReadMetadata(MediaInfo* aInfo,
MetadataTags** aTags)
{
MOZ_ASSERT(OnTaskQueue());
if (!mPlugin) {
mPlugin = GetAndroidMediaPluginHost()->CreateDecoder(mDecoder->GetResource(), mType);
if (!mPlugin) {
return NS_ERROR_FAILURE;
}
}
// Set the total duration (the max of the audio and video track).
int64_t durationUs;
mPlugin->GetDuration(mPlugin, &durationUs);
if (durationUs) {
mInfo.mMetadataDuration.emplace(TimeUnit::FromMicroseconds(durationUs));
}
if (mPlugin->HasVideo(mPlugin)) {
int32_t width, height;
mPlugin->GetVideoParameters(mPlugin, &width, &height);
nsIntRect pictureRect(0, 0, width, height);
// Validate the container-reported frame and pictureRect sizes. This ensures
// that our video frame creation code doesn't overflow.
nsIntSize displaySize(width, height);
nsIntSize frameSize(width, height);
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
return NS_ERROR_FAILURE;
}
// Video track's frame sizes will not overflow. Activate the video track.
mHasVideo = true;
mInfo.mVideo.mDisplay = displaySize;
mPicture = pictureRect;
mInitialFrame = frameSize;
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
if (container) {
container->ClearCurrentFrame(IntSize(displaySize.width, displaySize.height));
}
}
if (mPlugin->HasAudio(mPlugin)) {
int32_t numChannels, sampleRate;
mPlugin->GetAudioParameters(mPlugin, &numChannels, &sampleRate);
mHasAudio = true;
mInfo.mAudio.mChannels = numChannels;
mInfo.mAudio.mRate = sampleRate;
}
*aInfo = mInfo;
*aTags = nullptr;
return NS_OK;
}
RefPtr<ShutdownPromise>
AndroidMediaReader::Shutdown()
{
ResetDecode();
if (mPlugin) {
GetAndroidMediaPluginHost()->DestroyDecoder(mPlugin);
mPlugin = nullptr;
}
return MediaDecoderReader::Shutdown();
}
// Resets all state related to decoding, emptying all buffers etc.
nsresult AndroidMediaReader::ResetDecode(TrackSet aTracks)
{
if (mLastVideoFrame) {
mLastVideoFrame = nullptr;
}
mSeekRequest.DisconnectIfExists();
mSeekPromise.RejectIfExists(NS_OK, __func__);
return MediaDecoderReader::ResetDecode(aTracks);
}
bool AndroidMediaReader::DecodeVideoFrame(bool &aKeyframeSkip,
int64_t aTimeThreshold)
{
// Record number of frames decoded and parsed. Automatically update the
// stats counters using the AutoNotifyDecoded stack-based class.
AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
// Throw away the currently buffered frame if we are seeking.
if (mLastVideoFrame && mVideoSeekTimeUs != -1) {
mLastVideoFrame = nullptr;
}
ImageBufferCallback bufferCallback(mDecoder->GetImageContainer());
RefPtr<Image> currentImage;
// Read next frame
while (true) {
MPAPI::VideoFrame frame;
if (!mPlugin->ReadVideo(mPlugin, &frame, mVideoSeekTimeUs, &bufferCallback)) {
// We reached the end of the video stream. If we have a buffered
// video frame, push it the video queue using the total duration
// of the video as the end time.
if (mLastVideoFrame) {
int64_t durationUs;
mPlugin->GetDuration(mPlugin, &durationUs);
durationUs = std::max<int64_t>(durationUs - mLastVideoFrame->mTime, 0);
RefPtr<VideoData> data = VideoData::ShallowCopyUpdateDuration(mLastVideoFrame,
durationUs);
mVideoQueue.Push(data);
mLastVideoFrame = nullptr;
}
return false;
}
mVideoSeekTimeUs = -1;
if (aKeyframeSkip) {
// Disable keyframe skipping for now as
// stagefright doesn't seem to be telling us
// when a frame is a keyframe.
#if 0
if (!frame.mKeyFrame) {
++a.mStats.mParsedFrames;
++a.mStats.mDroppedFrames;
continue;
}
#endif
aKeyframeSkip = false;
}
if (frame.mSize == 0)
return true;
currentImage = bufferCallback.GetImage();
int64_t pos = mDecoder->GetResource()->Tell();
IntRect picture = mPicture;
RefPtr<VideoData> v;
if (currentImage) {
gfx::IntSize frameSize = currentImage->GetSize();
if (frameSize.width != mInitialFrame.width ||
frameSize.height != mInitialFrame.height) {
// Frame size is different from what the container reports. This is legal,
// and we will preserve the ratio of the crop rectangle as it
// was reported relative to the picture size reported by the container.
picture.x = (mPicture.x * frameSize.width) / mInitialFrame.width;
picture.y = (mPicture.y * frameSize.height) / mInitialFrame.height;
picture.width = (frameSize.width * mPicture.width) / mInitialFrame.width;
picture.height = (frameSize.height * mPicture.height) / mInitialFrame.height;
}
v = VideoData::CreateFromImage(mInfo.mVideo,
pos,
frame.mTimeUs,
1, // We don't know the duration yet.
currentImage,
frame.mKeyFrame,
-1,
picture);
} else {
// Assume YUV
VideoData::YCbCrBuffer b;
b.mPlanes[0].mData = static_cast<uint8_t *>(frame.Y.mData);
b.mPlanes[0].mStride = frame.Y.mStride;
b.mPlanes[0].mHeight = frame.Y.mHeight;
b.mPlanes[0].mWidth = frame.Y.mWidth;
b.mPlanes[0].mOffset = frame.Y.mOffset;
b.mPlanes[0].mSkip = frame.Y.mSkip;
b.mPlanes[1].mData = static_cast<uint8_t *>(frame.Cb.mData);
b.mPlanes[1].mStride = frame.Cb.mStride;
b.mPlanes[1].mHeight = frame.Cb.mHeight;
b.mPlanes[1].mWidth = frame.Cb.mWidth;
b.mPlanes[1].mOffset = frame.Cb.mOffset;
b.mPlanes[1].mSkip = frame.Cb.mSkip;
b.mPlanes[2].mData = static_cast<uint8_t *>(frame.Cr.mData);
b.mPlanes[2].mStride = frame.Cr.mStride;
b.mPlanes[2].mHeight = frame.Cr.mHeight;
b.mPlanes[2].mWidth = frame.Cr.mWidth;
b.mPlanes[2].mOffset = frame.Cr.mOffset;
b.mPlanes[2].mSkip = frame.Cr.mSkip;
if (frame.Y.mWidth != mInitialFrame.width ||
frame.Y.mHeight != mInitialFrame.height) {
// Frame size is different from what the container reports. This is legal,
// and we will preserve the ratio of the crop rectangle as it
// was reported relative to the picture size reported by the container.
picture.x = (mPicture.x * frame.Y.mWidth) / mInitialFrame.width;
picture.y = (mPicture.y * frame.Y.mHeight) / mInitialFrame.height;
picture.width = (frame.Y.mWidth * mPicture.width) / mInitialFrame.width;
picture.height = (frame.Y.mHeight * mPicture.height) / mInitialFrame.height;
}
// This is the approximate byte position in the stream.
v = VideoData::CreateAndCopyData(mInfo.mVideo,
mDecoder->GetImageContainer(),
pos,
frame.mTimeUs,
1, // We don't know the duration yet.
b,
frame.mKeyFrame,
-1,
picture);
}
if (!v) {
return false;
}
a.mStats.mParsedFrames++;
a.mStats.mDecodedFrames++;
NS_ASSERTION(a.mStats.mDecodedFrames <= a.mStats.mParsedFrames, "Expect to decode fewer frames than parsed in AndroidMedia...");
// Since MPAPI doesn't give us the end time of frames, we keep one frame
// buffered in AndroidMediaReader and push it into the queue as soon
// we read the following frame so we can use that frame's start time as
// the end time of the buffered frame.
if (!mLastVideoFrame) {
mLastVideoFrame = v;
continue;
}
// Calculate the duration as the timestamp of the current frame minus the
// timestamp of the previous frame. We can then return the previously
// decoded frame, and it will have a valid timestamp.
int64_t duration = v->mTime - mLastVideoFrame->mTime;
mLastVideoFrame = VideoData::ShallowCopyUpdateDuration(mLastVideoFrame, duration);
// We have the start time of the next frame, so we can push the previous
// frame into the queue, except if the end time is below the threshold,
// in which case it wouldn't be displayed anyway.
if (mLastVideoFrame->GetEndTime() < aTimeThreshold) {
mLastVideoFrame = nullptr;
continue;
}
// Buffer the current frame we just decoded.
mVideoQueue.Push(mLastVideoFrame);
mLastVideoFrame = v;
break;
}
return true;
}
bool AndroidMediaReader::DecodeAudioData()
{
MOZ_ASSERT(OnTaskQueue());
// This is the approximate byte position in the stream.
int64_t pos = mDecoder->GetResource()->Tell();
// Read next frame
MPAPI::AudioFrame source;
if (!mPlugin->ReadAudio(mPlugin, &source, mAudioSeekTimeUs)) {
return false;
}
mAudioSeekTimeUs = -1;
// Ignore empty buffers which stagefright media read will sporadically return
if (source.mSize == 0)
return true;
uint32_t frames = source.mSize / (source.mAudioChannels *
sizeof(AudioDataValue));
typedef AudioCompactor::NativeCopy MPCopy;
return mAudioCompactor.Push(pos,
source.mTimeUs,
source.mAudioSampleRate,
frames,
source.mAudioChannels,
MPCopy(static_cast<uint8_t *>(source.mData),
source.mSize,
source.mAudioChannels));
}
RefPtr<MediaDecoderReader::SeekPromise>
AndroidMediaReader::Seek(SeekTarget aTarget, int64_t aEndTime)
{
MOZ_ASSERT(OnTaskQueue());
RefPtr<SeekPromise> p = mSeekPromise.Ensure(__func__);
if (mHasAudio && mHasVideo) {
// The decoder seeks/demuxes audio and video streams separately. So if
// we seek both audio and video to aTarget, the audio stream can typically
// seek closer to the seek target, since typically every audio block is
// a sync point, whereas for video there are only keyframes once every few
// seconds. So if we have both audio and video, we must seek the video
// stream to the preceeding keyframe first, get the stream time, and then
// seek the audio stream to match the video stream's time. Otherwise, the
// audio and video streams won't be in sync after the seek.
mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds();
RefPtr<AndroidMediaReader> self = this;
mSeekRequest.Begin(DecodeToFirstVideoData()->Then(OwnerThread(), __func__, [self] (MediaData* v) {
self->mSeekRequest.Complete();
self->mAudioSeekTimeUs = v->mTime;
self->mSeekPromise.Resolve(media::TimeUnit::FromMicroseconds(self->mAudioSeekTimeUs), __func__);
}, [self, aTarget] () {
self->mSeekRequest.Complete();
self->mAudioSeekTimeUs = aTarget.GetTime().ToMicroseconds();
self->mSeekPromise.Resolve(aTarget.GetTime(), __func__);
}));
} else {
mAudioSeekTimeUs = mVideoSeekTimeUs = aTarget.GetTime().ToMicroseconds();
mSeekPromise.Resolve(aTarget.GetTime(), __func__);
}
return p;
}
AndroidMediaReader::ImageBufferCallback::ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer) :
mImageContainer(aImageContainer)
{
}
void *
AndroidMediaReader::ImageBufferCallback::operator()(size_t aWidth, size_t aHeight,
MPAPI::ColorFormat aColorFormat)
{
if (!mImageContainer) {
NS_WARNING("No image container to construct an image");
return nullptr;
}
RefPtr<Image> image;
switch(aColorFormat) {
case MPAPI::RGB565:
image = mozilla::layers::CreateSharedRGBImage(mImageContainer,
nsIntSize(aWidth, aHeight),
SurfaceFormat::R5G6B5_UINT16);
if (!image) {
NS_WARNING("Could not create rgb image");
return nullptr;
}
mImage = image;
return image->GetBuffer();
case MPAPI::I420:
return CreateI420Image(aWidth, aHeight);
default:
NS_NOTREACHED("Color format not supported");
return nullptr;
}
}
uint8_t *
AndroidMediaReader::ImageBufferCallback::CreateI420Image(size_t aWidth,
size_t aHeight)
{
RefPtr<PlanarYCbCrImage> yuvImage = mImageContainer->CreatePlanarYCbCrImage();
mImage = yuvImage;
if (!yuvImage) {
NS_WARNING("Could not create I420 image");
return nullptr;
}
// Use uint32_t throughout to match AllocateAndGetNewBuffer's param
const auto checkedFrameSize =
CheckedInt<uint32_t>(aWidth) * aHeight;
// Allocate enough for one full resolution Y plane
// and two quarter resolution Cb/Cr planes.
const auto checkedBufferSize =
checkedFrameSize + checkedFrameSize / 2;
if (!checkedBufferSize.isValid()) { // checks checkedFrameSize too
NS_WARNING("Could not create I420 image");
return nullptr;
}
const auto frameSize = checkedFrameSize.value();
uint8_t *buffer =
yuvImage->AllocateAndGetNewBuffer(checkedBufferSize.value());
mozilla::layers::PlanarYCbCrData frameDesc;
frameDesc.mYChannel = buffer;
frameDesc.mCbChannel = buffer + frameSize;
frameDesc.mCrChannel = frameDesc.mCbChannel + frameSize / 4;
frameDesc.mYSize = IntSize(aWidth, aHeight);
frameDesc.mCbCrSize = IntSize(aWidth / 2, aHeight / 2);
frameDesc.mYStride = aWidth;
frameDesc.mCbCrStride = aWidth / 2;
frameDesc.mYSkip = 0;
frameDesc.mCbSkip = 0;
frameDesc.mCrSkip = 0;
frameDesc.mPicX = 0;
frameDesc.mPicY = 0;
frameDesc.mPicSize = IntSize(aWidth, aHeight);
yuvImage->AdoptData(frameDesc);
return buffer;
}
already_AddRefed<Image>
AndroidMediaReader::ImageBufferCallback::GetImage()
{
return mImage.forget();
}
} // namespace mozilla

View file

@ -1,75 +0,0 @@
/* -*- 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(AndroidMediaReader_h_)
#define AndroidMediaReader_h_
#include "mozilla/Attributes.h"
#include "MediaResource.h"
#include "MediaDecoderReader.h"
#include "ImageContainer.h"
#include "mozilla/layers/SharedRGBImage.h"
#include "MPAPI.h"
class nsACString;
namespace mozilla {
class AbstractMediaDecoder;
namespace layers {
class ImageContainer;
}
class AndroidMediaReader : public MediaDecoderReader
{
nsCString mType;
MPAPI::Decoder *mPlugin;
bool mHasAudio;
bool mHasVideo;
nsIntRect mPicture;
nsIntSize mInitialFrame;
int64_t mVideoSeekTimeUs;
int64_t mAudioSeekTimeUs;
RefPtr<VideoData> mLastVideoFrame;
MozPromiseHolder<MediaDecoderReader::SeekPromise> mSeekPromise;
MozPromiseRequestHolder<MediaDecoderReader::MediaDataPromise> mSeekRequest;
public:
AndroidMediaReader(AbstractMediaDecoder* aDecoder,
const nsACString& aContentType);
nsresult ResetDecode(TrackSet aTracks = TrackSet(TrackInfo::kAudioTrack,
TrackInfo::kVideoTrack)) override;
bool DecodeAudioData() override;
bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold) override;
nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) override;
RefPtr<SeekPromise> Seek(SeekTarget aTarget, int64_t aEndTime) override;
RefPtr<ShutdownPromise> Shutdown() override;
class ImageBufferCallback : public MPAPI::BufferCallback {
typedef mozilla::layers::Image Image;
public:
ImageBufferCallback(mozilla::layers::ImageContainer *aImageContainer);
void *operator()(size_t aWidth, size_t aHeight,
MPAPI::ColorFormat aColorFormat) override;
already_AddRefed<Image> GetImage();
private:
uint8_t *CreateI420Image(size_t aWidth, size_t aHeight);
mozilla::layers::ImageContainer *mImageContainer;
RefPtr<Image> mImage;
};
};
} // namespace mozilla
#endif

View file

@ -1,503 +0,0 @@
/* -*- 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 "mozilla/IntegerPrintfMacros.h"
#include "mozilla/UniquePtr.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 "AndroidMediaResourceServer.h"
#if defined(_MSC_VER)
#define strtoll _strtoi64
#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 Runnable {
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 AndroidMediaResourceServer that owns the MediaResource instances
// served. This is used to lookup the MediaResource from the URL.
RefPtr<AndroidMediaResourceServer> 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,
AndroidMediaResourceServer* 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);
RefPtr<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.
RefPtr<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(), nullptr, 10);
// Clamp 'start' to be between 0 and the resource length.
start = std::max(int64_t(0), 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;
auto b = MakeUnique<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.get(), buffer_size, "Content-Length: %" PRId64 "\r\n", contentlength);
rv = WriteAll(b.get(), strlen(b.get()));
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.get(), buffer_size, "Content-Range: "
"bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n",
start, resource->GetLength() - 1, resource->GetLength());
rv = WriteAll(b.get(), strlen(b.get()));
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.get(), 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.get(), bytesRead);
if (NS_FAILED (rv)) break;
rv = resource->ReadAt(start, b.get(), 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);
}
/*
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 AndroidMediaResourceServer that created this listener, using the
URL the client requested.
*/
class ResourceSocketListener : public nsIServerSocketListener
{
public:
// The AndroidMediaResourceServer used to look up the MediaResource
// on requests.
RefPtr<AndroidMediaResourceServer> mServer;
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSISERVERSOCKETLISTENER
ResourceSocketListener(AndroidMediaResourceServer* aServer) :
mServer(aServer)
{
}
private:
virtual ~ResourceSocketListener() { }
};
NS_IMPL_ISUPPORTS(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;
}
AndroidMediaResourceServer::AndroidMediaResourceServer() :
mMutex("AndroidMediaResourceServer")
{
}
NS_IMETHODIMP
AndroidMediaResourceServer::Run()
{
MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
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<AndroidMediaResourceServer>
AndroidMediaResourceServer::Start()
{
MOZ_ASSERT(NS_IsMainThread());
RefPtr<AndroidMediaResourceServer> server = new AndroidMediaResourceServer();
server->Run();
return server.forget();
}
void
AndroidMediaResourceServer::Stop()
{
MutexAutoLock lock(mMutex);
mSocket->Close();
mSocket = nullptr;
}
nsresult
AndroidMediaResourceServer::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;
nsAutoCString salt;
rv = GenerateRandomPathName(salt, 16);
if (NS_FAILED(rv)) return rv;
aUrl += "/";
aUrl += salt;
return NS_OK;
}
nsresult
AndroidMediaResourceServer::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(url) != mResources.end()) return NS_ERROR_FAILURE;
mResources[url] = aResource;
}
aUrl = url;
return NS_OK;
}
void
AndroidMediaResourceServer::RemoveResource(nsCString const& aUrl)
{
MutexAutoLock lock(mMutex);
mResources.erase(aUrl);
}
nsCString
AndroidMediaResourceServer::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>
AndroidMediaResourceServer::GetResource(nsCString const& aUrl)
{
MutexAutoLock lock(mMutex);
ResourceMap::const_iterator it = mResources.find(aUrl);
if (it == mResources.end()) return nullptr;
RefPtr<MediaResource> resource = it->second;
return resource.forget();
}

View file

@ -1,96 +0,0 @@
/* -*- 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(AndroidMediaResourceServer_h_)
#define AndroidMediaResourceServer_h_
#include <map>
#include "nsIServerSocket.h"
#include "MediaResource.h"
namespace mozilla {
class MediaResource;
/*
AndroidMediaResourceServer 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 AndroidMediaResourceServer 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 AndroidMediaResourceServer : public Runnable
{
private:
// Mutex protecting private members of AndroidMediaResourceServer.
// 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,
RefPtr<mozilla::MediaResource> > ResourceMap;
ResourceMap mResources;
// Create a AndroidMediaResourceServer 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.
AndroidMediaResourceServer();
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 AndroidMediaResourceServer and start it listening. This call will
// perform a synchronous request on the main thread.
static already_AddRefed<AndroidMediaResourceServer> 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

@ -1,165 +0,0 @@
/* -*- 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(MPAPI_h_)
#define MPAPI_h_
#include <stdint.h>
namespace MPAPI {
enum ColorFormat {
I420,
RGB565
};
/*
* A callback for the plugin to use to request a buffer owned by gecko. This can
* save us a copy or two down the line.
*/
class BufferCallback {
public:
virtual void *operator()(size_t aWidth, size_t aHeight,
ColorFormat aColorFormat) = 0;
};
struct VideoPlane {
VideoPlane() :
mData(0),
mStride(0),
mWidth(0),
mHeight(0),
mOffset(0),
mSkip(0)
{}
void *mData;
int32_t mStride;
int32_t mWidth;
int32_t mHeight;
int32_t mOffset;
int32_t mSkip;
};
struct VideoFrame {
int64_t mTimeUs;
bool mKeyFrame;
void *mData;
size_t mSize;
int32_t mStride;
int32_t mSliceHeight;
int32_t mRotation;
VideoPlane Y;
VideoPlane Cb;
VideoPlane Cr;
VideoFrame() :
mTimeUs(0),
mKeyFrame(false),
mData(0),
mSize(0),
mStride(0),
mSliceHeight(0),
mRotation(0)
{}
void Set(int64_t aTimeUs, bool aKeyFrame,
void *aData, size_t aSize, int32_t aStride, int32_t aSliceHeight, int32_t aRotation,
void *aYData, int32_t aYStride, int32_t aYWidth, int32_t aYHeight, int32_t aYOffset, int32_t aYSkip,
void *aCbData, int32_t aCbStride, int32_t aCbWidth, int32_t aCbHeight, int32_t aCbOffset, int32_t aCbSkip,
void *aCrData, int32_t aCrStride, int32_t aCrWidth, int32_t aCrHeight, int32_t aCrOffset, int32_t aCrSkip)
{
mTimeUs = aTimeUs;
mKeyFrame = aKeyFrame;
mData = aData;
mSize = aSize;
mStride = aStride;
mSliceHeight = aSliceHeight;
mRotation = aRotation;
Y.mData = aYData;
Y.mStride = aYStride;
Y.mWidth = aYWidth;
Y.mHeight = aYHeight;
Y.mOffset = aYOffset;
Y.mSkip = aYSkip;
Cb.mData = aCbData;
Cb.mStride = aCbStride;
Cb.mWidth = aCbWidth;
Cb.mHeight = aCbHeight;
Cb.mOffset = aCbOffset;
Cb.mSkip = aCbSkip;
Cr.mData = aCrData;
Cr.mStride = aCrStride;
Cr.mWidth = aCrWidth;
Cr.mHeight = aCrHeight;
Cr.mOffset = aCrOffset;
Cr.mSkip = aCrSkip;
}
};
struct AudioFrame {
int64_t mTimeUs;
void *mData; // 16PCM interleaved
size_t mSize; // Size of mData in bytes
int32_t mAudioChannels;
int32_t mAudioSampleRate;
AudioFrame() :
mTimeUs(0),
mData(0),
mSize(0),
mAudioChannels(0),
mAudioSampleRate(0)
{
}
void Set(int64_t aTimeUs,
void *aData, size_t aSize,
int32_t aAudioChannels, int32_t aAudioSampleRate)
{
mTimeUs = aTimeUs;
mData = aData;
mSize = aSize;
mAudioChannels = aAudioChannels;
mAudioSampleRate = aAudioSampleRate;
}
};
struct Decoder;
struct PluginHost {
bool (*Read)(Decoder *aDecoder, char *aBuffer, int64_t aOffset, uint32_t aCount, uint32_t* aBytes);
uint64_t (*GetLength)(Decoder *aDecoder);
void (*SetMetaDataReadMode)(Decoder *aDecoder);
void (*SetPlaybackReadMode)(Decoder *aDecoder);
bool (*GetIntPref)(const char *aPref, int32_t *aResult);
bool (*GetSystemInfoString)(const char *aKey, char *aResult, size_t aResultLen);
};
struct Decoder {
void *mResource;
void *mPrivate;
Decoder();
void (*GetDuration)(Decoder *aDecoder, int64_t *durationUs);
void (*GetVideoParameters)(Decoder *aDecoder, int32_t *aWidth, int32_t *aHeight);
void (*GetAudioParameters)(Decoder *aDecoder, int32_t *aNumChannels, int32_t *aSampleRate);
bool (*HasVideo)(Decoder *aDecoder);
bool (*HasAudio)(Decoder *aDecoder);
bool (*ReadVideo)(Decoder *aDecoder, VideoFrame *aFrame, int64_t aSeekTimeUs, BufferCallback *aBufferCallback);
bool (*ReadAudio)(Decoder *aDecoder, AudioFrame *aFrame, int64_t aSeekTimeUs);
void (*DestroyDecoder)(Decoder *);
};
struct Manifest {
bool (*CanDecode)(const char *aMimeChars, size_t aMimeLen, const char* const**aCodecs);
bool (*CreateDecoder)(PluginHost *aPluginHost, Decoder *aDecoder,
const char *aMimeChars, size_t aMimeLen);
};
}
#endif

View file

@ -1,27 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
EXPORTS += [
'AndroidMediaDecoder.h',
'AndroidMediaPluginHost.h',
'AndroidMediaReader.h',
'AndroidMediaResourceServer.h',
'MPAPI.h',
]
UNIFIED_SOURCES += [
'AndroidMediaDecoder.cpp',
'AndroidMediaPluginHost.cpp',
'AndroidMediaReader.cpp',
'AndroidMediaResourceServer.cpp',
]
LOCAL_INCLUDES += [
'/dom/base',
'/dom/html',
]
FINAL_LIBRARY = 'xul'

View file

@ -43,9 +43,6 @@ DIRS += [
'standalone',
]
if CONFIG['MOZ_ANDROID_OMX']:
DIRS += ['android']
if CONFIG['MOZ_FMP4']:
DIRS += ['fmp4']

View file

@ -92,10 +92,6 @@
#include "nsSynthVoiceRegistry.h"
#endif
#ifdef MOZ_ANDROID_OMX
#include "AndroidMediaPluginHost.h"
#endif
#include "CubebUtils.h"
#include "Latency.h"
#include "WebAudioUtils.h"
@ -381,11 +377,6 @@ nsLayoutStatics::Shutdown()
nsAutoCopyListener::Shutdown();
FrameLayerBuilder::Shutdown();
#ifdef MOZ_ANDROID_OMX
AndroidMediaPluginHost::Shutdown();
#endif
CubebUtils::ShutdownLibrary();
AsyncLatencyLogger::ShutdownLogger();
WebAudioUtils::Shutdown();