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