1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/plugins/MediaPluginHost.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,330 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 +#include "mozilla/Preferences.h" 1.10 +#include "mozilla/dom/TimeRanges.h" 1.11 +#include "MediaResource.h" 1.12 +#include "mozilla/dom/HTMLMediaElement.h" 1.13 +#include "MediaPluginHost.h" 1.14 +#include "nsXPCOMStrings.h" 1.15 +#include "nsISeekableStream.h" 1.16 +#include "MediaPluginReader.h" 1.17 +#include "nsIGfxInfo.h" 1.18 +#include "gfxCrashReporterUtils.h" 1.19 +#include "prmem.h" 1.20 +#include "prlink.h" 1.21 +#include "MediaResourceServer.h" 1.22 +#include "nsServiceManagerUtils.h" 1.23 + 1.24 +#include "MPAPI.h" 1.25 + 1.26 +#include "nsIPropertyBag2.h" 1.27 + 1.28 +#if defined(ANDROID) || defined(MOZ_WIDGET_GONK) 1.29 +#include "android/log.h" 1.30 +#define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args) 1.31 +#else 1.32 +#define ALOG(args...) /* do nothing */ 1.33 +#endif 1.34 + 1.35 +using namespace MPAPI; 1.36 + 1.37 +Decoder::Decoder() : 1.38 + mResource(nullptr), mPrivate(nullptr) 1.39 +{ 1.40 +} 1.41 + 1.42 +namespace mozilla { 1.43 + 1.44 +static char* GetResource(Decoder *aDecoder) 1.45 +{ 1.46 + return static_cast<char*>(aDecoder->mResource); 1.47 +} 1.48 + 1.49 +class GetIntPrefEvent : public nsRunnable { 1.50 +public: 1.51 + GetIntPrefEvent(const char* aPref, int32_t* aResult) 1.52 + : mPref(aPref), mResult(aResult) {} 1.53 + NS_IMETHOD Run() { 1.54 + return Preferences::GetInt(mPref, mResult); 1.55 + } 1.56 +private: 1.57 + const char* mPref; 1.58 + int32_t* mResult; 1.59 +}; 1.60 + 1.61 +static bool GetIntPref(const char* aPref, int32_t* aResult) 1.62 +{ 1.63 + // GetIntPref() is called on the decoder thread, but the Preferences API 1.64 + // can only be called on the main thread. Post a runnable and wait. 1.65 + NS_ENSURE_TRUE(aPref, false); 1.66 + NS_ENSURE_TRUE(aResult, false); 1.67 + nsCOMPtr<nsIRunnable> event = new GetIntPrefEvent(aPref, aResult); 1.68 + return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC)); 1.69 +} 1.70 + 1.71 +static bool 1.72 +GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength) 1.73 +{ 1.74 + NS_ENSURE_TRUE(aKey, false); 1.75 + NS_ENSURE_TRUE(aResult, false); 1.76 + 1.77 + nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); 1.78 + NS_ASSERTION(infoService, "Could not find a system info service"); 1.79 + 1.80 + nsAutoCString key(aKey); 1.81 + nsAutoCString info; 1.82 + nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key), 1.83 + info); 1.84 + 1.85 + NS_ENSURE_SUCCESS(rv, false); 1.86 + 1.87 + strncpy(aResult, info.get(), aResultLength); 1.88 + 1.89 + return true; 1.90 +} 1.91 + 1.92 +static PluginHost sPluginHost = { 1.93 + nullptr, 1.94 + nullptr, 1.95 + nullptr, 1.96 + nullptr, 1.97 + GetIntPref, 1.98 + GetSystemInfoString 1.99 +}; 1.100 + 1.101 +// Return true if Omx decoding is supported on the device. This checks the 1.102 +// built in whitelist/blacklist and preferences to see if that is overridden. 1.103 +static bool IsOmxSupported() 1.104 +{ 1.105 + bool forceEnabled = 1.106 + Preferences::GetBool("stagefright.force-enabled", false); 1.107 + bool disabled = 1.108 + Preferences::GetBool("stagefright.disabled", false); 1.109 + 1.110 + if (disabled) { 1.111 + NS_WARNING("XXX stagefright disabled\n"); 1.112 + return false; 1.113 + } 1.114 + 1.115 + ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled); 1.116 + 1.117 + if (!forceEnabled) { 1.118 + nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); 1.119 + if (gfxInfo) { 1.120 + int32_t status; 1.121 + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) { 1.122 + if (status != nsIGfxInfo::FEATURE_NO_INFO) { 1.123 + NS_WARNING("XXX stagefright blacklisted\n"); 1.124 + return false; 1.125 + } 1.126 + } 1.127 + } 1.128 + } 1.129 + 1.130 + reporter.SetSuccessful(); 1.131 + return true; 1.132 +} 1.133 + 1.134 +// Return the name of the shared library that implements Omx based decoding. This varies 1.135 +// depending on libstagefright version installed on the device and whether it is B2G vs Android. 1.136 +// nullptr is returned if Omx decoding is not supported on the device, 1.137 +static const char* GetOmxLibraryName() 1.138 +{ 1.139 +#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) 1.140 + nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); 1.141 + NS_ASSERTION(infoService, "Could not find a system info service"); 1.142 + 1.143 + int32_t version; 1.144 + nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version); 1.145 + if (NS_SUCCEEDED(rv)) { 1.146 + ALOG("Android Version is: %d", version); 1.147 + } 1.148 + 1.149 + nsAutoString release_version; 1.150 + rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version); 1.151 + if (NS_SUCCEEDED(rv)) { 1.152 + ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get()); 1.153 + } 1.154 + 1.155 + nsAutoString device; 1.156 + rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device); 1.157 + if (NS_SUCCEEDED(rv)) { 1.158 + ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get()); 1.159 + } 1.160 + 1.161 + nsAutoString manufacturer; 1.162 + rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer); 1.163 + if (NS_SUCCEEDED(rv)) { 1.164 + ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get()); 1.165 + } 1.166 + 1.167 + nsAutoString hardware; 1.168 + rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware); 1.169 + if (NS_SUCCEEDED(rv)) { 1.170 + ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get()); 1.171 + } 1.172 +#endif 1.173 + 1.174 + if (!IsOmxSupported()) 1.175 + return nullptr; 1.176 + 1.177 +#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) 1.178 + if (version >= 17) { 1.179 + return "libomxpluginkk.so"; 1.180 + } 1.181 + else if (version == 13 || version == 12 || version == 11) { 1.182 + return "libomxpluginhc.so"; 1.183 + } 1.184 + else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) { 1.185 + // Gingerbread versions from 2.3.6 and above have a different DataSource 1.186 + // layout to those on 2.3.5 and below. 1.187 + return "libomxplugingb.so"; 1.188 + } 1.189 + else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.4") && 1.190 + device.Find("HTC") == 0) { 1.191 + // HTC devices running Gingerbread 2.3.4+ (HTC Desire HD, HTC Evo Design, etc) seem to 1.192 + // use a newer version of Gingerbread libstagefright than other 2.3.4 devices. 1.193 + return "libomxplugingb.so"; 1.194 + } 1.195 + else if (version == 9 || (version == 10 && release_version <= NS_LITERAL_STRING("2.3.5"))) { 1.196 + // Gingerbread versions from 2.3.5 and below have a different DataSource 1.197 + // than 2.3.6 and above. 1.198 + return "libomxplugingb235.so"; 1.199 + } 1.200 + else if (version == 8) { 1.201 + // Froyo 1.202 + return "libomxpluginfroyo.so"; 1.203 + } 1.204 + else if (version < 8) { 1.205 + // Below Froyo not supported 1.206 + return nullptr; 1.207 + } 1.208 + 1.209 + // Ice Cream Sandwich and Jellybean 1.210 + return "libomxplugin.so"; 1.211 + 1.212 +#elif defined(ANDROID) && defined(MOZ_WIDGET_GONK) 1.213 + return "libomxplugin.so"; 1.214 +#else 1.215 + return nullptr; 1.216 +#endif 1.217 +} 1.218 + 1.219 +MediaPluginHost::MediaPluginHost() { 1.220 + MOZ_COUNT_CTOR(MediaPluginHost); 1.221 + 1.222 + mResourceServer = MediaResourceServer::Start(); 1.223 + 1.224 + const char* name = GetOmxLibraryName(); 1.225 + ALOG("Loading OMX Plugin: %s", name ? name : "nullptr"); 1.226 + if (name) { 1.227 + char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName); 1.228 + PRLibrary *lib = nullptr; 1.229 + if (path) { 1.230 + nsAutoCString libpath(path); 1.231 + PR_Free(path); 1.232 + int32_t slash = libpath.RFindChar('/'); 1.233 + if (slash != kNotFound) { 1.234 + libpath.Truncate(slash + 1); 1.235 + libpath.Append(name); 1.236 + lib = PR_LoadLibrary(libpath.get()); 1.237 + } 1.238 + } 1.239 + if (!lib) 1.240 + lib = PR_LoadLibrary(name); 1.241 + 1.242 + if (lib) { 1.243 + Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST")); 1.244 + if (manifest) { 1.245 + mPlugins.AppendElement(manifest); 1.246 + ALOG("OMX plugin successfully loaded"); 1.247 + } 1.248 + } 1.249 + } 1.250 +} 1.251 + 1.252 +MediaPluginHost::~MediaPluginHost() { 1.253 + mResourceServer->Stop(); 1.254 + MOZ_COUNT_DTOR(MediaPluginHost); 1.255 +} 1.256 + 1.257 +bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs) 1.258 +{ 1.259 + const char *chars; 1.260 + size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); 1.261 + for (size_t n = 0; n < mPlugins.Length(); ++n) { 1.262 + Manifest *plugin = mPlugins[n]; 1.263 + const char* const *codecs; 1.264 + if (plugin->CanDecode(chars, len, &codecs)) { 1.265 + if (aCodecs) 1.266 + *aCodecs = codecs; 1.267 + return true; 1.268 + } 1.269 + } 1.270 + return false; 1.271 +} 1.272 + 1.273 +MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType) 1.274 +{ 1.275 + NS_ENSURE_TRUE(aResource, nullptr); 1.276 + 1.277 + nsAutoPtr<Decoder> decoder(new Decoder()); 1.278 + if (!decoder) { 1.279 + return nullptr; 1.280 + } 1.281 + 1.282 + const char *chars; 1.283 + size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); 1.284 + for (size_t n = 0; n < mPlugins.Length(); ++n) { 1.285 + Manifest *plugin = mPlugins[n]; 1.286 + const char* const *codecs; 1.287 + if (!plugin->CanDecode(chars, len, &codecs)) { 1.288 + continue; 1.289 + } 1.290 + 1.291 + nsCString url; 1.292 + nsresult rv = mResourceServer->AddResource(aResource, url); 1.293 + if (NS_FAILED (rv)) continue; 1.294 + 1.295 + decoder->mResource = strdup(url.get()); 1.296 + if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) { 1.297 + aResource->AddRef(); 1.298 + return decoder.forget(); 1.299 + } 1.300 + } 1.301 + 1.302 + return nullptr; 1.303 +} 1.304 + 1.305 +void MediaPluginHost::DestroyDecoder(Decoder *aDecoder) 1.306 +{ 1.307 + aDecoder->DestroyDecoder(aDecoder); 1.308 + char* resource = GetResource(aDecoder); 1.309 + if (resource) { 1.310 + // resource *shouldn't* be null, but check anyway just in case the plugin 1.311 + // decoder does something stupid. 1.312 + mResourceServer->RemoveResource(nsCString(resource)); 1.313 + free(resource); 1.314 + } 1.315 + delete aDecoder; 1.316 +} 1.317 + 1.318 +MediaPluginHost *sMediaPluginHost = nullptr; 1.319 +MediaPluginHost *GetMediaPluginHost() 1.320 +{ 1.321 + if (!sMediaPluginHost) { 1.322 + sMediaPluginHost = new MediaPluginHost(); 1.323 + } 1.324 + return sMediaPluginHost; 1.325 +} 1.326 + 1.327 +void MediaPluginHost::Shutdown() 1.328 +{ 1.329 + delete sMediaPluginHost; 1.330 + sMediaPluginHost = nullptr; 1.331 +} 1.332 + 1.333 +} // namespace mozilla