1 /*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "MediaExtractorFactory"
19 #include <utils/Log.h>
20
21 #include <android/dlext.h>
22 #include <android-base/logging.h>
23 #include <binder/IPCThreadState.h>
24 #include <binder/PermissionCache.h>
25 #include <binder/IServiceManager.h>
26 #include <media/DataSource.h>
27 #include <media/stagefright/InterfaceUtils.h>
28 #include <media/stagefright/MediaExtractor.h>
29 #include <media/stagefright/MediaExtractorFactory.h>
30 #include <media/IMediaExtractor.h>
31 #include <media/IMediaExtractorService.h>
32 #include <nativeloader/dlext_namespaces.h>
33 #include <private/android_filesystem_config.h>
34 #include <cutils/properties.h>
35 #include <utils/String8.h>
36
37 #include <dirent.h>
38 #include <dlfcn.h>
39
40 namespace android {
41
42 // static
Create(const sp<DataSource> & source,const char * mime)43 sp<IMediaExtractor> MediaExtractorFactory::Create(
44 const sp<DataSource> &source, const char *mime) {
45 ALOGV("MediaExtractorFactory::Create %s", mime);
46
47 if (!property_get_bool("media.stagefright.extractremote", true)) {
48 // local extractor
49 ALOGW("creating media extractor in calling process");
50 return CreateFromService(source, mime);
51 } else {
52 // remote extractor
53 ALOGV("get service manager");
54 sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
55
56 if (binder != 0) {
57 sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
58 sp<IMediaExtractor> ex = mediaExService->makeExtractor(
59 CreateIDataSourceFromDataSource(source), mime);
60 return ex;
61 } else {
62 ALOGE("extractor service not running");
63 return NULL;
64 }
65 }
66 return NULL;
67 }
68
CreateFromService(const sp<DataSource> & source,const char * mime)69 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
70 const sp<DataSource> &source, const char *mime) {
71
72 ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
73
74 void *meta = nullptr;
75 void *creator = NULL;
76 FreeMetaFunc freeMeta = nullptr;
77 float confidence;
78 sp<ExtractorPlugin> plugin;
79 uint32_t creatorVersion = 0;
80 creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion);
81 if (!creator) {
82 ALOGV("FAILED to autodetect media content.");
83 return NULL;
84 }
85
86 MediaExtractor *ex = nullptr;
87 if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 ||
88 creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) {
89 CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
90 if (meta != nullptr && freeMeta != nullptr) {
91 freeMeta(meta);
92 }
93 ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
94 }
95
96 ALOGV("Created an extractor '%s' with confidence %.2f",
97 ex != nullptr ? ex->name() : "<null>", confidence);
98
99 return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
100 }
101
102 struct ExtractorPlugin : public RefBase {
103 ExtractorDef def;
104 void *libHandle;
105 String8 libPath;
106 String8 uuidString;
107
ExtractorPluginandroid::ExtractorPlugin108 ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path)
109 : def(definition), libHandle(handle), libPath(path) {
110 for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) {
111 uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
112 }
113 }
~ExtractorPluginandroid::ExtractorPlugin114 ~ExtractorPlugin() {
115 if (libHandle != nullptr) {
116 ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
117 dlclose(libHandle);
118 }
119 }
120 };
121
122 Mutex MediaExtractorFactory::gPluginMutex;
123 std::shared_ptr<std::list<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
124 bool MediaExtractorFactory::gPluginsRegistered = false;
125 bool MediaExtractorFactory::gIgnoreVersion = false;
126
127 // static
sniff(const sp<DataSource> & source,float * confidence,void ** meta,FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin,uint32_t * creatorVersion)128 void *MediaExtractorFactory::sniff(
129 const sp<DataSource> &source, float *confidence, void **meta,
130 FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
131 *confidence = 0.0f;
132 *meta = nullptr;
133
134 std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
135 {
136 Mutex::Autolock autoLock(gPluginMutex);
137 if (!gPluginsRegistered) {
138 return NULL;
139 }
140 plugins = gPlugins;
141 }
142
143 void *bestCreator = NULL;
144 for (auto it = plugins->begin(); it != plugins->end(); ++it) {
145 ALOGV("sniffing %s", (*it)->def.extractor_name);
146 float newConfidence;
147 void *newMeta = nullptr;
148 FreeMetaFunc newFreeMeta = nullptr;
149
150 void *curCreator = NULL;
151 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
152 curCreator = (void*) (*it)->def.u.v2.sniff(
153 source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
154 } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
155 curCreator = (void*) (*it)->def.u.v3.sniff(
156 source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
157 }
158
159 if (curCreator) {
160 if (newConfidence > *confidence) {
161 *confidence = newConfidence;
162 if (*meta != nullptr && *freeMeta != nullptr) {
163 (*freeMeta)(*meta);
164 }
165 *meta = newMeta;
166 *freeMeta = newFreeMeta;
167 plugin = *it;
168 bestCreator = curCreator;
169 *creatorVersion = (*it)->def.def_version;
170 } else {
171 if (newMeta != nullptr && newFreeMeta != nullptr) {
172 newFreeMeta(newMeta);
173 }
174 }
175 }
176 }
177
178 return bestCreator;
179 }
180
181 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,std::list<sp<ExtractorPlugin>> & pluginList)182 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
183 std::list<sp<ExtractorPlugin>> &pluginList) {
184 // sanity check check struct version, uuid, name
185 if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
186 plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
187 ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
188 return;
189 }
190 if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
191 ALOGE("invalid UUID, ignoring");
192 return;
193 }
194 if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
195 ALOGE("extractors should have a name, ignoring");
196 return;
197 }
198
199 for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
200 if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
201 // there's already an extractor with the same uuid
202 if (gIgnoreVersion || (*it)->def.extractor_version < plugin->def.extractor_version) {
203 // this one is newer, replace the old one
204 ALOGW("replacing extractor '%s' version %u with version %u",
205 plugin->def.extractor_name,
206 (*it)->def.extractor_version,
207 plugin->def.extractor_version);
208 pluginList.erase(it);
209 break;
210 } else {
211 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
212 plugin->def.extractor_name,
213 plugin->def.extractor_version,
214 (*it)->def.extractor_version);
215 return;
216 }
217 }
218 }
219 ALOGV("registering extractor for %s", plugin->def.extractor_name);
220 pluginList.push_back(plugin);
221 }
222
223 //static
RegisterExtractors(const char * libDirPath,const android_dlextinfo * dlextinfo,std::list<sp<ExtractorPlugin>> & pluginList)224 void MediaExtractorFactory::RegisterExtractors(
225 const char *libDirPath, const android_dlextinfo* dlextinfo,
226 std::list<sp<ExtractorPlugin>> &pluginList) {
227 ALOGV("search for plugins at %s", libDirPath);
228
229 DIR *libDir = opendir(libDirPath);
230 if (libDir) {
231 struct dirent* libEntry;
232 while ((libEntry = readdir(libDir))) {
233 if (libEntry->d_name[0] == '.') {
234 continue;
235 }
236 String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
237 if (!libPath.contains("extractor.so")) {
238 continue;
239 }
240 void *libHandle = android_dlopen_ext(
241 libPath.string(),
242 RTLD_NOW | RTLD_LOCAL, dlextinfo);
243 CHECK(libHandle != nullptr)
244 << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
245
246 GetExtractorDef getDef =
247 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
248 CHECK(getDef != nullptr)
249 << libPath.string() << " does not contain sniffer";
250
251 ALOGV("registering sniffer for %s", libPath.string());
252 RegisterExtractor(
253 new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
254 }
255 closedir(libDir);
256 } else {
257 ALOGE("couldn't opendir(%s)", libDirPath);
258 }
259 }
260
compareFunc(const sp<ExtractorPlugin> & first,const sp<ExtractorPlugin> & second)261 static bool compareFunc(const sp<ExtractorPlugin>& first, const sp<ExtractorPlugin>& second) {
262 return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
263 }
264
265 static std::unordered_set<std::string> gSupportedExtensions;
266
267 // static
LoadExtractors()268 void MediaExtractorFactory::LoadExtractors() {
269 Mutex::Autolock autoLock(gPluginMutex);
270
271 if (gPluginsRegistered) {
272 return;
273 }
274
275 gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
276
277 std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
278
279 android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
280 if (mediaNs != NULL) {
281 const android_dlextinfo dlextinfo = {
282 .flags = ANDROID_DLEXT_USE_NAMESPACE,
283 .library_namespace = mediaNs,
284 };
285 RegisterExtractors("/apex/com.android.media/lib"
286 #ifdef __LP64__
287 "64"
288 #endif
289 "/extractors", &dlextinfo, *newList);
290
291 } else {
292 ALOGE("couldn't find media namespace.");
293 }
294
295 RegisterExtractors("/system/lib"
296 #ifdef __LP64__
297 "64"
298 #endif
299 "/extractors", NULL, *newList);
300
301 RegisterExtractors("/system_ext/lib"
302 #ifdef __LP64__
303 "64"
304 #endif
305 "/extractors", NULL, *newList);
306
307 newList->sort(compareFunc);
308 gPlugins = newList;
309
310 for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
311 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
312 for (size_t i = 0;; i++) {
313 const char* ext = (*it)->def.u.v3.supported_types[i];
314 if (ext == nullptr) {
315 break;
316 }
317 gSupportedExtensions.insert(std::string(ext));
318 }
319 }
320 }
321
322 gPluginsRegistered = true;
323 }
324
325 // static
getSupportedTypes()326 std::unordered_set<std::string> MediaExtractorFactory::getSupportedTypes() {
327 if (getuid() == AID_MEDIA_EX) {
328 return gSupportedExtensions;
329 }
330 ALOGV("get service manager");
331 sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
332
333 if (binder != 0) {
334 sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
335 return mediaExService->getSupportedTypes();
336 }
337 return std::unordered_set<std::string>();
338 }
339
dump(int fd,const Vector<String16> &)340 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
341 Mutex::Autolock autoLock(gPluginMutex);
342 String8 out;
343
344 const IPCThreadState* ipc = IPCThreadState::self();
345 const int pid = ipc->getCallingPid();
346 const int uid = ipc->getCallingUid();
347 if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
348 // dumpExtractors() will append the following string.
349 // out.appendFormat("Permission Denial: "
350 // "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
351 ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid);
352 } else {
353 out.append("Available extractors:\n");
354 if (gPluginsRegistered) {
355 for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
356 out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)",
357 (*it)->def.extractor_name,
358 (*it)->def.def_version,
359 (*it)->uuidString.c_str(),
360 (*it)->def.extractor_version,
361 (*it)->libPath.c_str());
362 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
363 out.append(", supports: ");
364 for (size_t i = 0;; i++) {
365 const char* mime = (*it)->def.u.v3.supported_types[i];
366 if (mime == nullptr) {
367 break;
368 }
369 out.appendFormat("%s ", mime);
370 }
371 }
372 out.append("\n");
373 }
374 out.append("\n");
375 } else {
376 out.append(" (no plugins registered)\n");
377 }
378 }
379 write(fd, out.string(), out.size());
380 return OK;
381 }
382
383
384 } // namespace android
385