diff -r 000000000000 -r 6474c204b198 content/media/plugins/MediaPluginHost.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/content/media/plugins/MediaPluginHost.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,330 @@ +/* -*- 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 "mozilla/dom/TimeRanges.h" +#include "MediaResource.h" +#include "mozilla/dom/HTMLMediaElement.h" +#include "MediaPluginHost.h" +#include "nsXPCOMStrings.h" +#include "nsISeekableStream.h" +#include "MediaPluginReader.h" +#include "nsIGfxInfo.h" +#include "gfxCrashReporterUtils.h" +#include "prmem.h" +#include "prlink.h" +#include "MediaResourceServer.h" +#include "nsServiceManagerUtils.h" + +#include "MPAPI.h" + +#include "nsIPropertyBag2.h" + +#if defined(ANDROID) || defined(MOZ_WIDGET_GONK) +#include "android/log.h" +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args) +#else +#define ALOG(args...) /* do nothing */ +#endif + +using namespace MPAPI; + +Decoder::Decoder() : + mResource(nullptr), mPrivate(nullptr) +{ +} + +namespace mozilla { + +static char* GetResource(Decoder *aDecoder) +{ + return static_cast(aDecoder->mResource); +} + +class GetIntPrefEvent : public nsRunnable { +public: + GetIntPrefEvent(const char* aPref, int32_t* aResult) + : mPref(aPref), mResult(aResult) {} + NS_IMETHOD Run() { + return Preferences::GetInt(mPref, mResult); + } +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 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 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; + } + + ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled); + + if (!forceEnabled) { + nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); + if (gfxInfo) { + int32_t status; + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) { + if (status != nsIGfxInfo::FEATURE_NO_INFO) { + NS_WARNING("XXX stagefright blacklisted\n"); + return false; + } + } + } + } + + reporter.SetSuccessful(); + 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) && !defined(MOZ_WIDGET_GONK) + nsCOMPtr 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) && !defined(MOZ_WIDGET_GONK) + if (version >= 17) { + return "libomxpluginkk.so"; + } + else if (version == 13 || version == 12 || version == 11) { + return "libomxpluginhc.so"; + } + else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) { + // Gingerbread versions from 2.3.6 and above have a different DataSource + // layout to those on 2.3.5 and below. + return "libomxplugingb.so"; + } + else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.4") && + device.Find("HTC") == 0) { + // HTC devices running Gingerbread 2.3.4+ (HTC Desire HD, HTC Evo Design, etc) seem to + // use a newer version of Gingerbread libstagefright than other 2.3.4 devices. + return "libomxplugingb.so"; + } + else if (version == 9 || (version == 10 && release_version <= NS_LITERAL_STRING("2.3.5"))) { + // Gingerbread versions from 2.3.5 and below have a different DataSource + // than 2.3.6 and above. + return "libomxplugingb235.so"; + } + else if (version == 8) { + // Froyo + return "libomxpluginfroyo.so"; + } + else if (version < 8) { + // Below Froyo not supported + return nullptr; + } + + // Ice Cream Sandwich and Jellybean + return "libomxplugin.so"; + +#elif defined(ANDROID) && defined(MOZ_WIDGET_GONK) + return "libomxplugin.so"; +#else + return nullptr; +#endif +} + +MediaPluginHost::MediaPluginHost() { + MOZ_COUNT_CTOR(MediaPluginHost); + + mResourceServer = MediaResourceServer::Start(); + + const char* name = GetOmxLibraryName(); + ALOG("Loading OMX Plugin: %s", name ? name : "nullptr"); + if (name) { + char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName); + PRLibrary *lib = 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(PR_FindSymbol(lib, "MPAPI_MANIFEST")); + if (manifest) { + mPlugins.AppendElement(manifest); + ALOG("OMX plugin successfully loaded"); + } + } + } +} + +MediaPluginHost::~MediaPluginHost() { + mResourceServer->Stop(); + MOZ_COUNT_DTOR(MediaPluginHost); +} + +bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs) +{ + const char *chars; + size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); + for (size_t n = 0; n < mPlugins.Length(); ++n) { + Manifest *plugin = mPlugins[n]; + const char* const *codecs; + if (plugin->CanDecode(chars, len, &codecs)) { + if (aCodecs) + *aCodecs = codecs; + return true; + } + } + return false; +} + +MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType) +{ + NS_ENSURE_TRUE(aResource, nullptr); + + nsAutoPtr 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)) { + aResource->AddRef(); + return decoder.forget(); + } + } + + return nullptr; +} + +void MediaPluginHost::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; +} + +MediaPluginHost *sMediaPluginHost = nullptr; +MediaPluginHost *GetMediaPluginHost() +{ + if (!sMediaPluginHost) { + sMediaPluginHost = new MediaPluginHost(); + } + return sMediaPluginHost; +} + +void MediaPluginHost::Shutdown() +{ + delete sMediaPluginHost; + sMediaPluginHost = nullptr; +} + +} // namespace mozilla