|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 #include "mozilla/Preferences.h" |
|
7 #include "mozilla/dom/TimeRanges.h" |
|
8 #include "MediaResource.h" |
|
9 #include "mozilla/dom/HTMLMediaElement.h" |
|
10 #include "MediaPluginHost.h" |
|
11 #include "nsXPCOMStrings.h" |
|
12 #include "nsISeekableStream.h" |
|
13 #include "MediaPluginReader.h" |
|
14 #include "nsIGfxInfo.h" |
|
15 #include "gfxCrashReporterUtils.h" |
|
16 #include "prmem.h" |
|
17 #include "prlink.h" |
|
18 #include "MediaResourceServer.h" |
|
19 #include "nsServiceManagerUtils.h" |
|
20 |
|
21 #include "MPAPI.h" |
|
22 |
|
23 #include "nsIPropertyBag2.h" |
|
24 |
|
25 #if defined(ANDROID) || defined(MOZ_WIDGET_GONK) |
|
26 #include "android/log.h" |
|
27 #define ALOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaPluginHost" , ## args) |
|
28 #else |
|
29 #define ALOG(args...) /* do nothing */ |
|
30 #endif |
|
31 |
|
32 using namespace MPAPI; |
|
33 |
|
34 Decoder::Decoder() : |
|
35 mResource(nullptr), mPrivate(nullptr) |
|
36 { |
|
37 } |
|
38 |
|
39 namespace mozilla { |
|
40 |
|
41 static char* GetResource(Decoder *aDecoder) |
|
42 { |
|
43 return static_cast<char*>(aDecoder->mResource); |
|
44 } |
|
45 |
|
46 class GetIntPrefEvent : public nsRunnable { |
|
47 public: |
|
48 GetIntPrefEvent(const char* aPref, int32_t* aResult) |
|
49 : mPref(aPref), mResult(aResult) {} |
|
50 NS_IMETHOD Run() { |
|
51 return Preferences::GetInt(mPref, mResult); |
|
52 } |
|
53 private: |
|
54 const char* mPref; |
|
55 int32_t* mResult; |
|
56 }; |
|
57 |
|
58 static bool GetIntPref(const char* aPref, int32_t* aResult) |
|
59 { |
|
60 // GetIntPref() is called on the decoder thread, but the Preferences API |
|
61 // can only be called on the main thread. Post a runnable and wait. |
|
62 NS_ENSURE_TRUE(aPref, false); |
|
63 NS_ENSURE_TRUE(aResult, false); |
|
64 nsCOMPtr<nsIRunnable> event = new GetIntPrefEvent(aPref, aResult); |
|
65 return NS_SUCCEEDED(NS_DispatchToMainThread(event, NS_DISPATCH_SYNC)); |
|
66 } |
|
67 |
|
68 static bool |
|
69 GetSystemInfoString(const char *aKey, char *aResult, size_t aResultLength) |
|
70 { |
|
71 NS_ENSURE_TRUE(aKey, false); |
|
72 NS_ENSURE_TRUE(aResult, false); |
|
73 |
|
74 nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); |
|
75 NS_ASSERTION(infoService, "Could not find a system info service"); |
|
76 |
|
77 nsAutoCString key(aKey); |
|
78 nsAutoCString info; |
|
79 nsresult rv = infoService->GetPropertyAsACString(NS_ConvertUTF8toUTF16(key), |
|
80 info); |
|
81 |
|
82 NS_ENSURE_SUCCESS(rv, false); |
|
83 |
|
84 strncpy(aResult, info.get(), aResultLength); |
|
85 |
|
86 return true; |
|
87 } |
|
88 |
|
89 static PluginHost sPluginHost = { |
|
90 nullptr, |
|
91 nullptr, |
|
92 nullptr, |
|
93 nullptr, |
|
94 GetIntPref, |
|
95 GetSystemInfoString |
|
96 }; |
|
97 |
|
98 // Return true if Omx decoding is supported on the device. This checks the |
|
99 // built in whitelist/blacklist and preferences to see if that is overridden. |
|
100 static bool IsOmxSupported() |
|
101 { |
|
102 bool forceEnabled = |
|
103 Preferences::GetBool("stagefright.force-enabled", false); |
|
104 bool disabled = |
|
105 Preferences::GetBool("stagefright.disabled", false); |
|
106 |
|
107 if (disabled) { |
|
108 NS_WARNING("XXX stagefright disabled\n"); |
|
109 return false; |
|
110 } |
|
111 |
|
112 ScopedGfxFeatureReporter reporter("Stagefright", forceEnabled); |
|
113 |
|
114 if (!forceEnabled) { |
|
115 nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1"); |
|
116 if (gfxInfo) { |
|
117 int32_t status; |
|
118 if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_STAGEFRIGHT, &status))) { |
|
119 if (status != nsIGfxInfo::FEATURE_NO_INFO) { |
|
120 NS_WARNING("XXX stagefright blacklisted\n"); |
|
121 return false; |
|
122 } |
|
123 } |
|
124 } |
|
125 } |
|
126 |
|
127 reporter.SetSuccessful(); |
|
128 return true; |
|
129 } |
|
130 |
|
131 // Return the name of the shared library that implements Omx based decoding. This varies |
|
132 // depending on libstagefright version installed on the device and whether it is B2G vs Android. |
|
133 // nullptr is returned if Omx decoding is not supported on the device, |
|
134 static const char* GetOmxLibraryName() |
|
135 { |
|
136 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) |
|
137 nsCOMPtr<nsIPropertyBag2> infoService = do_GetService("@mozilla.org/system-info;1"); |
|
138 NS_ASSERTION(infoService, "Could not find a system info service"); |
|
139 |
|
140 int32_t version; |
|
141 nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("version"), &version); |
|
142 if (NS_SUCCEEDED(rv)) { |
|
143 ALOG("Android Version is: %d", version); |
|
144 } |
|
145 |
|
146 nsAutoString release_version; |
|
147 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("release_version"), release_version); |
|
148 if (NS_SUCCEEDED(rv)) { |
|
149 ALOG("Android Release Version is: %s", NS_LossyConvertUTF16toASCII(release_version).get()); |
|
150 } |
|
151 |
|
152 nsAutoString device; |
|
153 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("device"), device); |
|
154 if (NS_SUCCEEDED(rv)) { |
|
155 ALOG("Android Device is: %s", NS_LossyConvertUTF16toASCII(device).get()); |
|
156 } |
|
157 |
|
158 nsAutoString manufacturer; |
|
159 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), manufacturer); |
|
160 if (NS_SUCCEEDED(rv)) { |
|
161 ALOG("Android Manufacturer is: %s", NS_LossyConvertUTF16toASCII(manufacturer).get()); |
|
162 } |
|
163 |
|
164 nsAutoString hardware; |
|
165 rv = infoService->GetPropertyAsAString(NS_LITERAL_STRING("hardware"), hardware); |
|
166 if (NS_SUCCEEDED(rv)) { |
|
167 ALOG("Android Hardware is: %s", NS_LossyConvertUTF16toASCII(hardware).get()); |
|
168 } |
|
169 #endif |
|
170 |
|
171 if (!IsOmxSupported()) |
|
172 return nullptr; |
|
173 |
|
174 #if defined(ANDROID) && !defined(MOZ_WIDGET_GONK) |
|
175 if (version >= 17) { |
|
176 return "libomxpluginkk.so"; |
|
177 } |
|
178 else if (version == 13 || version == 12 || version == 11) { |
|
179 return "libomxpluginhc.so"; |
|
180 } |
|
181 else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.6")) { |
|
182 // Gingerbread versions from 2.3.6 and above have a different DataSource |
|
183 // layout to those on 2.3.5 and below. |
|
184 return "libomxplugingb.so"; |
|
185 } |
|
186 else if (version == 10 && release_version >= NS_LITERAL_STRING("2.3.4") && |
|
187 device.Find("HTC") == 0) { |
|
188 // HTC devices running Gingerbread 2.3.4+ (HTC Desire HD, HTC Evo Design, etc) seem to |
|
189 // use a newer version of Gingerbread libstagefright than other 2.3.4 devices. |
|
190 return "libomxplugingb.so"; |
|
191 } |
|
192 else if (version == 9 || (version == 10 && release_version <= NS_LITERAL_STRING("2.3.5"))) { |
|
193 // Gingerbread versions from 2.3.5 and below have a different DataSource |
|
194 // than 2.3.6 and above. |
|
195 return "libomxplugingb235.so"; |
|
196 } |
|
197 else if (version == 8) { |
|
198 // Froyo |
|
199 return "libomxpluginfroyo.so"; |
|
200 } |
|
201 else if (version < 8) { |
|
202 // Below Froyo not supported |
|
203 return nullptr; |
|
204 } |
|
205 |
|
206 // Ice Cream Sandwich and Jellybean |
|
207 return "libomxplugin.so"; |
|
208 |
|
209 #elif defined(ANDROID) && defined(MOZ_WIDGET_GONK) |
|
210 return "libomxplugin.so"; |
|
211 #else |
|
212 return nullptr; |
|
213 #endif |
|
214 } |
|
215 |
|
216 MediaPluginHost::MediaPluginHost() { |
|
217 MOZ_COUNT_CTOR(MediaPluginHost); |
|
218 |
|
219 mResourceServer = MediaResourceServer::Start(); |
|
220 |
|
221 const char* name = GetOmxLibraryName(); |
|
222 ALOG("Loading OMX Plugin: %s", name ? name : "nullptr"); |
|
223 if (name) { |
|
224 char *path = PR_GetLibraryFilePathname("libxul.so", (PRFuncPtr) GetOmxLibraryName); |
|
225 PRLibrary *lib = nullptr; |
|
226 if (path) { |
|
227 nsAutoCString libpath(path); |
|
228 PR_Free(path); |
|
229 int32_t slash = libpath.RFindChar('/'); |
|
230 if (slash != kNotFound) { |
|
231 libpath.Truncate(slash + 1); |
|
232 libpath.Append(name); |
|
233 lib = PR_LoadLibrary(libpath.get()); |
|
234 } |
|
235 } |
|
236 if (!lib) |
|
237 lib = PR_LoadLibrary(name); |
|
238 |
|
239 if (lib) { |
|
240 Manifest *manifest = static_cast<Manifest *>(PR_FindSymbol(lib, "MPAPI_MANIFEST")); |
|
241 if (manifest) { |
|
242 mPlugins.AppendElement(manifest); |
|
243 ALOG("OMX plugin successfully loaded"); |
|
244 } |
|
245 } |
|
246 } |
|
247 } |
|
248 |
|
249 MediaPluginHost::~MediaPluginHost() { |
|
250 mResourceServer->Stop(); |
|
251 MOZ_COUNT_DTOR(MediaPluginHost); |
|
252 } |
|
253 |
|
254 bool MediaPluginHost::FindDecoder(const nsACString& aMimeType, const char* const** aCodecs) |
|
255 { |
|
256 const char *chars; |
|
257 size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); |
|
258 for (size_t n = 0; n < mPlugins.Length(); ++n) { |
|
259 Manifest *plugin = mPlugins[n]; |
|
260 const char* const *codecs; |
|
261 if (plugin->CanDecode(chars, len, &codecs)) { |
|
262 if (aCodecs) |
|
263 *aCodecs = codecs; |
|
264 return true; |
|
265 } |
|
266 } |
|
267 return false; |
|
268 } |
|
269 |
|
270 MPAPI::Decoder *MediaPluginHost::CreateDecoder(MediaResource *aResource, const nsACString& aMimeType) |
|
271 { |
|
272 NS_ENSURE_TRUE(aResource, nullptr); |
|
273 |
|
274 nsAutoPtr<Decoder> decoder(new Decoder()); |
|
275 if (!decoder) { |
|
276 return nullptr; |
|
277 } |
|
278 |
|
279 const char *chars; |
|
280 size_t len = NS_CStringGetData(aMimeType, &chars, nullptr); |
|
281 for (size_t n = 0; n < mPlugins.Length(); ++n) { |
|
282 Manifest *plugin = mPlugins[n]; |
|
283 const char* const *codecs; |
|
284 if (!plugin->CanDecode(chars, len, &codecs)) { |
|
285 continue; |
|
286 } |
|
287 |
|
288 nsCString url; |
|
289 nsresult rv = mResourceServer->AddResource(aResource, url); |
|
290 if (NS_FAILED (rv)) continue; |
|
291 |
|
292 decoder->mResource = strdup(url.get()); |
|
293 if (plugin->CreateDecoder(&sPluginHost, decoder, chars, len)) { |
|
294 aResource->AddRef(); |
|
295 return decoder.forget(); |
|
296 } |
|
297 } |
|
298 |
|
299 return nullptr; |
|
300 } |
|
301 |
|
302 void MediaPluginHost::DestroyDecoder(Decoder *aDecoder) |
|
303 { |
|
304 aDecoder->DestroyDecoder(aDecoder); |
|
305 char* resource = GetResource(aDecoder); |
|
306 if (resource) { |
|
307 // resource *shouldn't* be null, but check anyway just in case the plugin |
|
308 // decoder does something stupid. |
|
309 mResourceServer->RemoveResource(nsCString(resource)); |
|
310 free(resource); |
|
311 } |
|
312 delete aDecoder; |
|
313 } |
|
314 |
|
315 MediaPluginHost *sMediaPluginHost = nullptr; |
|
316 MediaPluginHost *GetMediaPluginHost() |
|
317 { |
|
318 if (!sMediaPluginHost) { |
|
319 sMediaPluginHost = new MediaPluginHost(); |
|
320 } |
|
321 return sMediaPluginHost; |
|
322 } |
|
323 |
|
324 void MediaPluginHost::Shutdown() |
|
325 { |
|
326 delete sMediaPluginHost; |
|
327 sMediaPluginHost = nullptr; |
|
328 } |
|
329 |
|
330 } // namespace mozilla |