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 <android/IMediaExtractor.h>
31 #include <android/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(
58 interface_cast<IMediaExtractorService>(binder));
59 sp<IMediaExtractor> ex;
60 mediaExService->makeExtractor(
61 CreateIDataSourceFromDataSource(source),
62 mime ? std::make_unique<std::string>(mime) : nullptr,
63 &ex);
64 return ex;
65 } else {
66 ALOGE("extractor service not running");
67 return NULL;
68 }
69 }
70 return NULL;
71 }
72
CreateFromService(const sp<DataSource> & source,const char * mime)73 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
74 const sp<DataSource> &source, const char *mime) {
75
76 ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
77
78 void *meta = nullptr;
79 void *creator = NULL;
80 FreeMetaFunc freeMeta = nullptr;
81 float confidence;
82 sp<ExtractorPlugin> plugin;
83 uint32_t creatorVersion = 0;
84 creator = sniff(source, &confidence, &meta, &freeMeta, plugin, &creatorVersion);
85 if (!creator) {
86 ALOGV("FAILED to autodetect media content.");
87 return NULL;
88 }
89
90 MediaExtractor *ex = nullptr;
91 if (creatorVersion == EXTRACTORDEF_VERSION_NDK_V1 ||
92 creatorVersion == EXTRACTORDEF_VERSION_NDK_V2) {
93 CMediaExtractor *ret = ((CreatorFunc)creator)(source->wrap(), meta);
94 if (meta != nullptr && freeMeta != nullptr) {
95 freeMeta(meta);
96 }
97 ex = ret != nullptr ? new MediaExtractorCUnwrapper(ret) : nullptr;
98 }
99
100 ALOGV("Created an extractor '%s' with confidence %.2f",
101 ex != nullptr ? ex->name() : "<null>", confidence);
102
103 return CreateIMediaExtractorFromMediaExtractor(ex, source, plugin);
104 }
105
106 struct ExtractorPlugin : public RefBase {
107 ExtractorDef def;
108 void *libHandle;
109 String8 libPath;
110 String8 uuidString;
111
ExtractorPluginandroid::ExtractorPlugin112 ExtractorPlugin(ExtractorDef definition, void *handle, String8 &path)
113 : def(definition), libHandle(handle), libPath(path) {
114 for (size_t i = 0; i < sizeof ExtractorDef::extractor_uuid; i++) {
115 uuidString.appendFormat("%02x", def.extractor_uuid.b[i]);
116 }
117 }
~ExtractorPluginandroid::ExtractorPlugin118 ~ExtractorPlugin() {
119 if (libHandle != nullptr) {
120 ALOGV("closing handle for %s %d", libPath.c_str(), def.extractor_version);
121 dlclose(libHandle);
122 }
123 }
124 };
125
126 Mutex MediaExtractorFactory::gPluginMutex;
127 std::shared_ptr<std::list<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
128 bool MediaExtractorFactory::gPluginsRegistered = false;
129 bool MediaExtractorFactory::gIgnoreVersion = false;
130
131 // static
sniff(const sp<DataSource> & source,float * confidence,void ** meta,FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin,uint32_t * creatorVersion)132 void *MediaExtractorFactory::sniff(
133 const sp<DataSource> &source, float *confidence, void **meta,
134 FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin, uint32_t *creatorVersion) {
135 *confidence = 0.0f;
136 *meta = nullptr;
137
138 std::shared_ptr<std::list<sp<ExtractorPlugin>>> plugins;
139 {
140 Mutex::Autolock autoLock(gPluginMutex);
141 if (!gPluginsRegistered) {
142 return NULL;
143 }
144 plugins = gPlugins;
145 }
146
147 void *bestCreator = NULL;
148 for (auto it = plugins->begin(); it != plugins->end(); ++it) {
149 ALOGV("sniffing %s", (*it)->def.extractor_name);
150 float newConfidence;
151 void *newMeta = nullptr;
152 FreeMetaFunc newFreeMeta = nullptr;
153
154 void *curCreator = NULL;
155 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V1) {
156 curCreator = (void*) (*it)->def.u.v2.sniff(
157 source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
158 } else if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
159 curCreator = (void*) (*it)->def.u.v3.sniff(
160 source->wrap(), &newConfidence, &newMeta, &newFreeMeta);
161 }
162
163 if (curCreator) {
164 if (newConfidence > *confidence) {
165 *confidence = newConfidence;
166 if (*meta != nullptr && *freeMeta != nullptr) {
167 (*freeMeta)(*meta);
168 }
169 *meta = newMeta;
170 *freeMeta = newFreeMeta;
171 plugin = *it;
172 bestCreator = curCreator;
173 *creatorVersion = (*it)->def.def_version;
174 } else {
175 if (newMeta != nullptr && newFreeMeta != nullptr) {
176 newFreeMeta(newMeta);
177 }
178 }
179 }
180 }
181
182 return bestCreator;
183 }
184
185 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,std::list<sp<ExtractorPlugin>> & pluginList)186 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
187 std::list<sp<ExtractorPlugin>> &pluginList) {
188 // sanity check check struct version, uuid, name
189 if (plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V1 &&
190 plugin->def.def_version != EXTRACTORDEF_VERSION_NDK_V2) {
191 ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
192 return;
193 }
194 if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
195 ALOGE("invalid UUID, ignoring");
196 return;
197 }
198 if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
199 ALOGE("extractors should have a name, ignoring");
200 return;
201 }
202
203 for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
204 if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
205 // there's already an extractor with the same uuid
206 if (gIgnoreVersion || (*it)->def.extractor_version < plugin->def.extractor_version) {
207 // this one is newer, replace the old one
208 ALOGW("replacing extractor '%s' version %u with version %u",
209 plugin->def.extractor_name,
210 (*it)->def.extractor_version,
211 plugin->def.extractor_version);
212 pluginList.erase(it);
213 break;
214 } else {
215 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
216 plugin->def.extractor_name,
217 plugin->def.extractor_version,
218 (*it)->def.extractor_version);
219 return;
220 }
221 }
222 }
223 ALOGV("registering extractor for %s", plugin->def.extractor_name);
224 pluginList.push_back(plugin);
225 }
226
227 //static
RegisterExtractors(const char * libDirPath,const android_dlextinfo * dlextinfo,std::list<sp<ExtractorPlugin>> & pluginList)228 void MediaExtractorFactory::RegisterExtractors(
229 const char *libDirPath, const android_dlextinfo* dlextinfo,
230 std::list<sp<ExtractorPlugin>> &pluginList) {
231 ALOGV("search for plugins at %s", libDirPath);
232
233 DIR *libDir = opendir(libDirPath);
234 if (libDir) {
235 struct dirent* libEntry;
236 while ((libEntry = readdir(libDir))) {
237 if (libEntry->d_name[0] == '.') {
238 continue;
239 }
240 String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
241 if (!libPath.contains("extractor.so")) {
242 continue;
243 }
244 void *libHandle = android_dlopen_ext(
245 libPath.string(),
246 RTLD_NOW | RTLD_LOCAL, dlextinfo);
247 CHECK(libHandle != nullptr)
248 << "couldn't dlopen(" << libPath.string() << ") " << strerror(errno);
249
250 GetExtractorDef getDef =
251 (GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
252 CHECK(getDef != nullptr)
253 << libPath.string() << " does not contain sniffer";
254
255 ALOGV("registering sniffer for %s", libPath.string());
256 RegisterExtractor(
257 new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
258 }
259 closedir(libDir);
260 } else {
261 ALOGE("couldn't opendir(%s)", libDirPath);
262 }
263 }
264
compareFunc(const sp<ExtractorPlugin> & first,const sp<ExtractorPlugin> & second)265 static bool compareFunc(const sp<ExtractorPlugin>& first, const sp<ExtractorPlugin>& second) {
266 return strcmp(first->def.extractor_name, second->def.extractor_name) < 0;
267 }
268
269 static std::vector<std::string> gSupportedExtensions;
270
271 // static
LoadExtractors()272 void MediaExtractorFactory::LoadExtractors() {
273 Mutex::Autolock autoLock(gPluginMutex);
274
275 if (gPluginsRegistered) {
276 return;
277 }
278
279 gIgnoreVersion = property_get_bool("debug.extractor.ignore_version", false);
280
281 std::shared_ptr<std::list<sp<ExtractorPlugin>>> newList(new std::list<sp<ExtractorPlugin>>());
282
283 android_namespace_t *mediaNs = android_get_exported_namespace("com_android_media");
284 if (mediaNs != NULL) {
285 const android_dlextinfo dlextinfo = {
286 .flags = ANDROID_DLEXT_USE_NAMESPACE,
287 .library_namespace = mediaNs,
288 };
289 RegisterExtractors("/apex/com.android.media/lib"
290 #ifdef __LP64__
291 "64"
292 #endif
293 "/extractors", &dlextinfo, *newList);
294
295 } else {
296 ALOGE("couldn't find media namespace.");
297 }
298
299 RegisterExtractors("/system/lib"
300 #ifdef __LP64__
301 "64"
302 #endif
303 "/extractors", NULL, *newList);
304
305 RegisterExtractors("/system_ext/lib"
306 #ifdef __LP64__
307 "64"
308 #endif
309 "/extractors", NULL, *newList);
310
311 newList->sort(compareFunc);
312 gPlugins = newList;
313
314 for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
315 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
316 for (size_t i = 0;; i++) {
317 const char* ext = (*it)->def.u.v3.supported_types[i];
318 if (ext == nullptr) {
319 break;
320 }
321 gSupportedExtensions.push_back(std::string(ext));
322 }
323 }
324 }
325
326 gPluginsRegistered = true;
327 }
328
329 // static
getSupportedTypes()330 std::vector<std::string> MediaExtractorFactory::getSupportedTypes() {
331 if (getuid() == AID_MEDIA_EX) {
332 return gSupportedExtensions;
333 }
334 ALOGV("get service manager");
335 sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
336
337 if (binder != 0) {
338 sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
339 std::vector<std::string> supportedTypes;
340 mediaExService->getSupportedTypes(&supportedTypes);
341 return supportedTypes;
342 }
343 return std::vector<std::string>();
344 }
345
dump(int fd,const Vector<String16> &)346 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
347 Mutex::Autolock autoLock(gPluginMutex);
348 String8 out;
349
350 const IPCThreadState* ipc = IPCThreadState::self();
351 const int pid = ipc->getCallingPid();
352 const int uid = ipc->getCallingUid();
353 if (!PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) {
354 // dumpExtractors() will append the following string.
355 // out.appendFormat("Permission Denial: "
356 // "can't dump MediaExtractor from pid=%d, uid=%d\n", pid, uid);
357 ALOGE("Permission Denial: can't dump MediaExtractor from pid=%d, uid=%d", pid, uid);
358 } else {
359 out.append("Available extractors:\n");
360 if (gPluginsRegistered) {
361 for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
362 out.appendFormat(" %25s: plugin_version(%d), uuid(%s), version(%u), path(%s)",
363 (*it)->def.extractor_name,
364 (*it)->def.def_version,
365 (*it)->uuidString.c_str(),
366 (*it)->def.extractor_version,
367 (*it)->libPath.c_str());
368 if ((*it)->def.def_version == EXTRACTORDEF_VERSION_NDK_V2) {
369 out.append(", supports: ");
370 for (size_t i = 0;; i++) {
371 const char* mime = (*it)->def.u.v3.supported_types[i];
372 if (mime == nullptr) {
373 break;
374 }
375 out.appendFormat("%s ", mime);
376 }
377 }
378 out.append("\n");
379 }
380 out.append("\n");
381 } else {
382 out.append(" (no plugins registered)\n");
383 }
384 }
385 write(fd, out.string(), out.size());
386 return OK;
387 }
388
389
390 } // namespace android
391