michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim:set ts=2 sw=2 sts=2 et cindent: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/TimeRanges.h" michael@0: #include "MediaResource.h" michael@0: #include "mozilla/dom/HTMLMediaElement.h" michael@0: #include "MediaPluginHost.h" michael@0: #include "nsXPCOMStrings.h" michael@0: #include "nsISeekableStream.h" michael@0: #include "MediaPluginReader.h" michael@0: #include "nsIGfxInfo.h" michael@0: #include "gfxCrashReporterUtils.h" michael@0: #include "prmem.h" michael@0: #include "prlink.h" michael@0: #include "MediaResourceServer.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: #include "MPAPI.h" michael@0: michael@0: #include "nsIPropertyBag2.h" michael@0: michael@0: #if defined(ANDROID) || defined(MOZ_WIDGET_GONK) michael@0: #include "android/log.h" michael@0: #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args) michael@0: #else michael@0: #define ALOG(args...) /* do nothing */ michael@0: #endif michael@0: michael@0: using namespace MPAPI; michael@0: michael@0: Decoder::Decoder() : michael@0: mResource(nullptr), mPrivate(nullptr) michael@0: { michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: static char* GetResource(Decoder *aDecoder) michael@0: { michael@0: return static_cast(aDecoder->mResource); michael@0: } michael@0: michael@0: class GetIntPrefEvent : public nsRunnable { michael@0: public: michael@0: GetIntPrefEvent(const char* aPref, int32_t* aResult) michael@0: : mPref(aPref), mResult(aResult) {} michael@0: NS_IMETHOD Run() { michael@0: return Preferences::GetInt(mPref, mResult); michael@0: } michael@0: private: michael@0: const char* mPref; michael@0: int32_t* mResult; michael@0: }; michael@0: michael@0: static bool GetIntPref(const char* aPref, int32_t* aResult) michael@0: { michael@0: // GetIntPref() is called on the decoder thread, but the Preferences API michael@0: // can only be called on the main thread. Post a runnable and wait. michael@0: NS_ENSURE_TRUE(aPref, false); michael@0: NS_ENSURE_TRUE(aResult, false); michael@0: nsCOMPtr event = new GetIntPrefEvent(aPref, aResult); michael@0: return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC)); michael@0: } michael@0: michael@0: static bool michael@0: GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength) michael@0: { michael@0: NS_ENSURE_TRUE(aKey, false); michael@0: NS_ENSURE_TRUE(aResult, false); michael@0: michael@0: nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); michael@0: NS_ASSERTION(infoService, "Could not find a system info service"); michael@0: michael@0: nsAutoCString key(aKey); michael@0: nsAutoCString info; michael@0: nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key), michael@0: info); michael@0: michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: strncpy(aResult, info.get(), aResultLength); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static PluginHost sPluginHost = { michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: nullptr, michael@0: GetIntPref, michael@0: GetSystemInfoString michael@0: }; michael@0: michael@0: // Return true if Omx decoding is supported on the device. This checks the michael@0: // built in whitelist/blacklist and preferences to see if that is overridden. michael@0: static bool IsOmxSupported() michael@0: { michael@0: bool forceEnabled = michael@0: Preferences::GetBool("stagefright.force-enabled", false); michael@0: bool disabled = michael@0: Preferences::GetBool("stagefright.disabled", false); michael@0: michael@0: if (disabled) { michael@0: NS_WARNING("XXX stagefright disabled\n"); michael@0: return false; michael@0: } michael@0: michael@0: ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled); michael@0: michael@0: if (!forceEnabled) { michael@0: nsCOMPtr gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); michael@0: if (gfxInfo) { michael@0: int32_t status; michael@0: if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) { michael@0: if (status != nsIGfxInfo::FEATURE_NO_INFO) { michael@0: NS_WARNING("XXX stagefright blacklisted\n"); michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: reporter.SetSuccessful(); michael@0: return true; michael@0: } michael@0: michael@0: // Return the name of the shared library that implements Omx based decoding. This varies michael@0: // depending on libstagefright version installed on the device and whether it is B2G vs Android. michael@0: // nullptr is returned if Omx decoding is not supported on the device, michael@0: static const char* GetOmxLibraryName() michael@0: { michael@0: #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) michael@0: nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); michael@0: NS_ASSERTION(infoService, "Could not find a system info service"); michael@0: michael@0: int32_t version; michael@0: nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ALOG("Android Version is: %d", version); michael@0: } michael@0: michael@0: nsAutoString release_version; michael@0: rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get()); michael@0: } michael@0: michael@0: nsAutoString device; michael@0: rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get()); michael@0: } michael@0: michael@0: nsAutoString manufacturer; michael@0: rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get()); michael@0: } michael@0: michael@0: nsAutoString hardware; michael@0: rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get()); michael@0: } michael@0: #endif michael@0: michael@0: if (!IsOmxSupported()) michael@0: return nullptr; michael@0: michael@0: #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) michael@0: if (version >= 17) { michael@0: return "libomxpluginkk.so"; michael@0: } michael@0: else if (version == 13 || version == 12 || version == 11) { michael@0: return "libomxpluginhc.so"; michael@0: } michael@0: else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) { michael@0: // Gingerbread versions from 2.3.6 and above have a different DataSource michael@0: // layout to those on 2.3.5 and below. michael@0: return "libomxplugingb.so"; michael@0: } michael@0: else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.4") && michael@0: device.Find("HTC") == 0) { michael@0: // HTC devices running Gingerbread 2.3.4+ (HTC Desire HD, HTC Evo Design, etc) seem to michael@0: // use a newer version of Gingerbread libstagefright than other 2.3.4 devices. michael@0: return "libomxplugingb.so"; michael@0: } michael@0: else if (version == 9 || (version == 10 && release_version <= NS_LITERAL_STRING("2.3.5"))) { michael@0: // Gingerbread versions from 2.3.5 and below have a different DataSource michael@0: // than 2.3.6 and above. michael@0: return "libomxplugingb235.so"; michael@0: } michael@0: else if (version == 8) { michael@0: // Froyo michael@0: return "libomxpluginfroyo.so"; michael@0: } michael@0: else if (version < 8) { michael@0: // Below Froyo not supported michael@0: return nullptr; michael@0: } michael@0: michael@0: // Ice Cream Sandwich and Jellybean michael@0: return "libomxplugin.so"; michael@0: michael@0: #elif defined(ANDROID) && defined(MOZ_WIDGET_GONK) michael@0: return "libomxplugin.so"; michael@0: #else michael@0: return nullptr; michael@0: #endif michael@0: } michael@0: michael@0: MediaPluginHost::MediaPluginHost() { michael@0: MOZ_COUNT_CTOR(MediaPluginHost); michael@0: michael@0: mResourceServer = MediaResourceServer::Start(); michael@0: michael@0: const char* name = GetOmxLibraryName(); michael@0: ALOG("Loading OMX Plugin: %s", name ? name : "nullptr"); michael@0: if (name) { michael@0: char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName); michael@0: PRLibrary *lib = nullptr; michael@0: if (path) { michael@0: nsAutoCString libpath(path); michael@0: PR_Free(path); michael@0: int32_t slash = libpath.RFindChar('/'); michael@0: if (slash != kNotFound) { michael@0: libpath.Truncate(slash + 1); michael@0: libpath.Append(name); michael@0: lib = PR_LoadLibrary(libpath.get()); michael@0: } michael@0: } michael@0: if (!lib) michael@0: lib = PR_LoadLibrary(name); michael@0: michael@0: if (lib) { michael@0: Manifest *manifest = static_cast(PR_FindSymbol(lib, "MPAPI_MANIFEST")); michael@0: if (manifest) { michael@0: mPlugins.AppendElement(manifest); michael@0: ALOG("OMX plugin successfully loaded"); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: MediaPluginHost::~MediaPluginHost() { michael@0: mResourceServer->Stop(); michael@0: MOZ_COUNT_DTOR(MediaPluginHost); michael@0: } michael@0: michael@0: bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs) michael@0: { michael@0: const char *chars; michael@0: size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); michael@0: for (size_t n = 0; n < mPlugins.Length(); ++n) { michael@0: Manifest *plugin = mPlugins[n]; michael@0: const char* const *codecs; michael@0: if (plugin->CanDecode(chars, len, &codecs)) { michael@0: if (aCodecs) michael@0: *aCodecs = codecs; michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType) michael@0: { michael@0: NS_ENSURE_TRUE(aResource, nullptr); michael@0: michael@0: nsAutoPtr decoder(new Decoder()); michael@0: if (!decoder) { michael@0: return nullptr; michael@0: } michael@0: michael@0: const char *chars; michael@0: size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); michael@0: for (size_t n = 0; n < mPlugins.Length(); ++n) { michael@0: Manifest *plugin = mPlugins[n]; michael@0: const char* const *codecs; michael@0: if (!plugin->CanDecode(chars, len, &codecs)) { michael@0: continue; michael@0: } michael@0: michael@0: nsCString url; michael@0: nsresult rv = mResourceServer->AddResource(aResource, url); michael@0: if (NS_FAILED (rv)) continue; michael@0: michael@0: decoder->mResource = strdup(url.get()); michael@0: if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) { michael@0: aResource->AddRef(); michael@0: return decoder.forget(); michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: void MediaPluginHost::DestroyDecoder(Decoder *aDecoder) michael@0: { michael@0: aDecoder->DestroyDecoder(aDecoder); michael@0: char* resource = GetResource(aDecoder); michael@0: if (resource) { michael@0: // resource *shouldn't* be null, but check anyway just in case the plugin michael@0: // decoder does something stupid. michael@0: mResourceServer->RemoveResource(nsCString(resource)); michael@0: free(resource); michael@0: } michael@0: delete aDecoder; michael@0: } michael@0: michael@0: MediaPluginHost *sMediaPluginHost = nullptr; michael@0: MediaPluginHost *GetMediaPluginHost() michael@0: { michael@0: if (!sMediaPluginHost) { michael@0: sMediaPluginHost = new MediaPluginHost(); michael@0: } michael@0: return sMediaPluginHost; michael@0: } michael@0: michael@0: void MediaPluginHost::Shutdown() michael@0: { michael@0: delete sMediaPluginHost; michael@0: sMediaPluginHost = nullptr; michael@0: } michael@0: michael@0: } // namespace mozilla