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 <binder/IServiceManager.h>
22 #include <media/DataSource.h>
23 #include <media/MediaAnalyticsItem.h>
24 #include <media/MediaExtractor.h>
25 #include <media/stagefright/foundation/AMessage.h>
26 #include <media/stagefright/InterfaceUtils.h>
27 #include <media/stagefright/MediaExtractorFactory.h>
28 #include <media/IMediaExtractor.h>
29 #include <media/IMediaExtractorService.h>
30 #include <cutils/properties.h>
31 #include <utils/String8.h>
32 #include <ziparchive/zip_archive.h>
33
34 #include <dirent.h>
35 #include <dlfcn.h>
36
37 namespace android {
38
39 // static
Create(const sp<DataSource> & source,const char * mime)40 sp<IMediaExtractor> MediaExtractorFactory::Create(
41 const sp<DataSource> &source, const char *mime) {
42 ALOGV("MediaExtractorFactory::Create %s", mime);
43
44 if (!property_get_bool("media.stagefright.extractremote", true)) {
45 // local extractor
46 ALOGW("creating media extractor in calling process");
47 return CreateFromService(source, mime);
48 } else {
49 // remote extractor
50 ALOGV("get service manager");
51 sp<IBinder> binder = defaultServiceManager()->getService(String16("media.extractor"));
52
53 if (binder != 0) {
54 sp<IMediaExtractorService> mediaExService(interface_cast<IMediaExtractorService>(binder));
55 sp<IMediaExtractor> ex = mediaExService->makeExtractor(
56 CreateIDataSourceFromDataSource(source), mime);
57 return ex;
58 } else {
59 ALOGE("extractor service not running");
60 return NULL;
61 }
62 }
63 return NULL;
64 }
65
CreateFromService(const sp<DataSource> & source,const char * mime)66 sp<IMediaExtractor> MediaExtractorFactory::CreateFromService(
67 const sp<DataSource> &source, const char *mime) {
68
69 ALOGV("MediaExtractorFactory::CreateFromService %s", mime);
70
71 UpdateExtractors(nullptr);
72
73 // initialize source decryption if needed
74 source->DrmInitialization(nullptr /* mime */);
75
76 void *meta = nullptr;
77 MediaExtractor::CreatorFunc creator = NULL;
78 MediaExtractor::FreeMetaFunc freeMeta = nullptr;
79 float confidence;
80 sp<ExtractorPlugin> plugin;
81 creator = sniff(source.get(), &confidence, &meta, &freeMeta, plugin);
82 if (!creator) {
83 ALOGV("FAILED to autodetect media content.");
84 return NULL;
85 }
86
87 MediaExtractor *ret = creator(source.get(), meta);
88 if (meta != nullptr && freeMeta != nullptr) {
89 freeMeta(meta);
90 }
91
92 ALOGV("Created an extractor '%s' with confidence %.2f",
93 ret != nullptr ? ret->name() : "<null>", confidence);
94
95 return CreateIMediaExtractorFromMediaExtractor(ret, source, plugin);
96 }
97
98 //static
LoadPlugins(const::std::string & apkPath)99 void MediaExtractorFactory::LoadPlugins(const ::std::string& apkPath) {
100 // TODO: Verify apk path with package manager in extractor process.
101 ALOGV("Load plugins from: %s", apkPath.c_str());
102 UpdateExtractors(apkPath.empty() ? nullptr : apkPath.c_str());
103 }
104
105 struct ExtractorPlugin : public RefBase {
106 MediaExtractor::ExtractorDef def;
107 void *libHandle;
108 String8 libPath;
109 String8 uuidString;
110
ExtractorPluginandroid::ExtractorPlugin111 ExtractorPlugin(MediaExtractor::ExtractorDef definition, void *handle, String8 &path)
112 : def(definition), libHandle(handle), libPath(path) {
113 for (size_t i = 0; i < sizeof MediaExtractor::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<List<sp<ExtractorPlugin>>> MediaExtractorFactory::gPlugins;
127 bool MediaExtractorFactory::gPluginsRegistered = false;
128
129 // static
sniff(DataSourceBase * source,float * confidence,void ** meta,MediaExtractor::FreeMetaFunc * freeMeta,sp<ExtractorPlugin> & plugin)130 MediaExtractor::CreatorFunc MediaExtractorFactory::sniff(
131 DataSourceBase *source, float *confidence, void **meta,
132 MediaExtractor::FreeMetaFunc *freeMeta, sp<ExtractorPlugin> &plugin) {
133 *confidence = 0.0f;
134 *meta = nullptr;
135
136 std::shared_ptr<List<sp<ExtractorPlugin>>> plugins;
137 {
138 Mutex::Autolock autoLock(gPluginMutex);
139 if (!gPluginsRegistered) {
140 return NULL;
141 }
142 plugins = gPlugins;
143 }
144
145 MediaExtractor::CreatorFunc curCreator = NULL;
146 MediaExtractor::CreatorFunc bestCreator = NULL;
147 for (auto it = plugins->begin(); it != plugins->end(); ++it) {
148 float newConfidence;
149 void *newMeta = nullptr;
150 MediaExtractor::FreeMetaFunc newFreeMeta = nullptr;
151 if ((curCreator = (*it)->def.sniff(source, &newConfidence, &newMeta, &newFreeMeta))) {
152 if (newConfidence > *confidence) {
153 *confidence = newConfidence;
154 if (*meta != nullptr && *freeMeta != nullptr) {
155 (*freeMeta)(*meta);
156 }
157 *meta = newMeta;
158 *freeMeta = newFreeMeta;
159 plugin = *it;
160 bestCreator = curCreator;
161 } else {
162 if (newMeta != nullptr && newFreeMeta != nullptr) {
163 newFreeMeta(newMeta);
164 }
165 }
166 }
167 }
168
169 return bestCreator;
170 }
171
172 // static
RegisterExtractor(const sp<ExtractorPlugin> & plugin,List<sp<ExtractorPlugin>> & pluginList)173 void MediaExtractorFactory::RegisterExtractor(const sp<ExtractorPlugin> &plugin,
174 List<sp<ExtractorPlugin>> &pluginList) {
175 // sanity check check struct version, uuid, name
176 if (plugin->def.def_version == 0
177 || plugin->def.def_version > MediaExtractor::EXTRACTORDEF_VERSION) {
178 ALOGE("don't understand extractor format %u, ignoring.", plugin->def.def_version);
179 return;
180 }
181 if (memcmp(&plugin->def.extractor_uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
182 ALOGE("invalid UUID, ignoring");
183 return;
184 }
185 if (plugin->def.extractor_name == NULL || strlen(plugin->def.extractor_name) == 0) {
186 ALOGE("extractors should have a name, ignoring");
187 return;
188 }
189
190 for (auto it = pluginList.begin(); it != pluginList.end(); ++it) {
191 if (memcmp(&((*it)->def.extractor_uuid), &plugin->def.extractor_uuid, 16) == 0) {
192 // there's already an extractor with the same uuid
193 if ((*it)->def.extractor_version < plugin->def.extractor_version) {
194 // this one is newer, replace the old one
195 ALOGW("replacing extractor '%s' version %u with version %u",
196 plugin->def.extractor_name,
197 (*it)->def.extractor_version,
198 plugin->def.extractor_version);
199 pluginList.erase(it);
200 break;
201 } else {
202 ALOGW("ignoring extractor '%s' version %u in favor of version %u",
203 plugin->def.extractor_name,
204 plugin->def.extractor_version,
205 (*it)->def.extractor_version);
206 return;
207 }
208 }
209 }
210 ALOGV("registering extractor for %s", plugin->def.extractor_name);
211 pluginList.push_back(plugin);
212 }
213
214 //static
RegisterExtractorsInApk(const char * apkPath,List<sp<ExtractorPlugin>> & pluginList)215 void MediaExtractorFactory::RegisterExtractorsInApk(
216 const char *apkPath, List<sp<ExtractorPlugin>> &pluginList) {
217 ALOGV("search for plugins at %s", apkPath);
218 ZipArchiveHandle zipHandle;
219 int32_t ret = OpenArchive(apkPath, &zipHandle);
220 if (ret == 0) {
221 char abi[PROPERTY_VALUE_MAX];
222 property_get("ro.product.cpu.abi", abi, "arm64-v8a");
223 String8 prefix8 = String8::format("lib/%s/", abi);
224 ZipString prefix(prefix8.c_str());
225 ZipString suffix("extractor.so");
226 void* cookie;
227 ret = StartIteration(zipHandle, &cookie, &prefix, &suffix);
228 if (ret == 0) {
229 ZipEntry entry;
230 ZipString name;
231 while (Next(cookie, &entry, &name) == 0) {
232 String8 libPath = String8(apkPath) + "!/" +
233 String8(reinterpret_cast<const char*>(name.name), name.name_length);
234 // TODO: Open with a linker namespace so that it can be linked with sub-libraries
235 // within the apk instead of system libraries already loaded.
236 void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
237 if (libHandle) {
238 MediaExtractor::GetExtractorDef getDef =
239 (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
240 if (getDef) {
241 ALOGV("registering sniffer for %s", libPath.string());
242 RegisterExtractor(
243 new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
244 } else {
245 ALOGW("%s does not contain sniffer", libPath.string());
246 dlclose(libHandle);
247 }
248 } else {
249 ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
250 }
251 }
252 EndIteration(cookie);
253 } else {
254 ALOGW("couldn't find plugins from %s, %d", apkPath, ret);
255 }
256 CloseArchive(zipHandle);
257 } else {
258 ALOGW("couldn't open(%s) %d", apkPath, ret);
259 }
260 }
261
262 //static
RegisterExtractorsInSystem(const char * libDirPath,List<sp<ExtractorPlugin>> & pluginList)263 void MediaExtractorFactory::RegisterExtractorsInSystem(
264 const char *libDirPath, List<sp<ExtractorPlugin>> &pluginList) {
265 ALOGV("search for plugins at %s", libDirPath);
266 DIR *libDir = opendir(libDirPath);
267 if (libDir) {
268 struct dirent* libEntry;
269 while ((libEntry = readdir(libDir))) {
270 String8 libPath = String8(libDirPath) + "/" + libEntry->d_name;
271 void *libHandle = dlopen(libPath.string(), RTLD_NOW | RTLD_LOCAL);
272 if (libHandle) {
273 MediaExtractor::GetExtractorDef getDef =
274 (MediaExtractor::GetExtractorDef) dlsym(libHandle, "GETEXTRACTORDEF");
275 if (getDef) {
276 ALOGV("registering sniffer for %s", libPath.string());
277 RegisterExtractor(
278 new ExtractorPlugin(getDef(), libHandle, libPath), pluginList);
279 } else {
280 ALOGW("%s does not contain sniffer", libPath.string());
281 dlclose(libHandle);
282 }
283 } else {
284 ALOGW("couldn't dlopen(%s) %s", libPath.string(), strerror(errno));
285 }
286 }
287
288 closedir(libDir);
289 } else {
290 ALOGE("couldn't opendir(%s)", libDirPath);
291 }
292 }
293
294 // static
UpdateExtractors(const char * newUpdateApkPath)295 void MediaExtractorFactory::UpdateExtractors(const char *newUpdateApkPath) {
296 Mutex::Autolock autoLock(gPluginMutex);
297 if (newUpdateApkPath != nullptr) {
298 gPluginsRegistered = false;
299 }
300 if (gPluginsRegistered) {
301 return;
302 }
303
304 std::shared_ptr<List<sp<ExtractorPlugin>>> newList(new List<sp<ExtractorPlugin>>());
305
306 RegisterExtractorsInSystem("/system/lib"
307 #ifdef __LP64__
308 "64"
309 #endif
310 "/extractors", *newList);
311
312 RegisterExtractorsInSystem("/vendor/lib"
313 #ifdef __LP64__
314 "64"
315 #endif
316 "/extractors", *newList);
317
318 if (newUpdateApkPath != nullptr) {
319 RegisterExtractorsInApk(newUpdateApkPath, *newList);
320 }
321
322 gPlugins = newList;
323 gPluginsRegistered = true;
324 }
325
dump(int fd,const Vector<String16> &)326 status_t MediaExtractorFactory::dump(int fd, const Vector<String16>&) {
327 Mutex::Autolock autoLock(gPluginMutex);
328 String8 out;
329 out.append("Available extractors:\n");
330 if (gPluginsRegistered) {
331 for (auto it = gPlugins->begin(); it != gPlugins->end(); ++it) {
332 out.appendFormat(" %25s: uuid(%s), version(%u), path(%s)\n",
333 (*it)->def.extractor_name,
334 (*it)->uuidString.c_str(),
335 (*it)->def.extractor_version,
336 (*it)->libPath.c_str());
337 }
338 } else {
339 out.append(" (no plugins registered)\n");
340 }
341 write(fd, out.string(), out.size());
342 return OK;
343 }
344
345
346 } // namespace android
347