1 /*
2 * Copyright 2012, 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 "MediaCodecList"
19 #include <utils/Log.h>
20
21 #include <binder/IServiceManager.h>
22
23 #include <media/IMediaCodecList.h>
24 #include <media/IMediaPlayerService.h>
25 #include <media/MediaCodecInfo.h>
26
27 #include <media/stagefright/foundation/ADebug.h>
28 #include <media/stagefright/foundation/AMessage.h>
29 #include <media/stagefright/foundation/MediaDefs.h>
30 #include <media/stagefright/omx/OMXUtils.h>
31 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
32 #include <media/stagefright/CCodec.h>
33 #include <media/stagefright/Codec2InfoBuilder.h>
34 #include <media/stagefright/MediaCodecList.h>
35 #include <media/stagefright/MediaCodecListOverrides.h>
36 #include <media/stagefright/MediaErrors.h>
37 #include <media/stagefright/OmxInfoBuilder.h>
38 #include <media/stagefright/PersistentSurface.h>
39
40 #include <sys/stat.h>
41 #include <utils/threads.h>
42
43 #include <cutils/properties.h>
44
45 #include <algorithm>
46 #include <regex>
47
48 namespace android {
49
50 namespace {
51
52 Mutex sInitMutex;
53
54 Mutex sRemoteInitMutex;
55
56 constexpr const char* kProfilingResults =
57 MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
58
isProfilingNeeded()59 bool isProfilingNeeded() {
60 int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
61 if (value == 0) {
62 return false;
63 }
64
65 bool profilingNeeded = true;
66 FILE *resultsFile = fopen(kProfilingResults, "r");
67 if (resultsFile) {
68 AString currentVersion = getProfilingVersionString();
69 size_t currentVersionSize = currentVersion.size();
70 char *versionString = new char[currentVersionSize + 1];
71 fgets(versionString, currentVersionSize + 1, resultsFile);
72 if (strcmp(versionString, currentVersion.c_str()) == 0) {
73 // profiling result up to date
74 profilingNeeded = false;
75 }
76 fclose(resultsFile);
77 delete[] versionString;
78 }
79 return profilingNeeded;
80 }
81
82 OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */};
83 OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */};
84
85 Mutex sCodec2InfoBuilderMutex;
86 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
87
GetCodec2InfoBuilder()88 MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
89 Mutex::Autolock _l(sCodec2InfoBuilderMutex);
90 if (!sCodec2InfoBuilder) {
91 sCodec2InfoBuilder.reset(new Codec2InfoBuilder);
92 }
93 return sCodec2InfoBuilder.get();
94 }
95
GetBuilders()96 std::vector<MediaCodecListBuilderBase *> GetBuilders() {
97 std::vector<MediaCodecListBuilderBase *> builders;
98 // if plugin provides the input surface, we cannot use OMX video encoders.
99 // In this case, rely on plugin to provide list of OMX codecs that are usable.
100 sp<PersistentSurface> surfaceTest = CCodec::CreateInputSurface();
101 if (surfaceTest == nullptr) {
102 ALOGD("Allowing all OMX codecs");
103 builders.push_back(&sOmxInfoBuilder);
104 } else {
105 ALOGD("Allowing only non-surface-encoder OMX codecs");
106 builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
107 }
108 builders.push_back(GetCodec2InfoBuilder());
109 return builders;
110 }
111
112 } // unnamed namespace
113
114 // static
115 sp<IMediaCodecList> MediaCodecList::sCodecList;
116
117 // static
profilerThreadWrapper(void *)118 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
119 ALOGV("Enter profilerThreadWrapper.");
120 remove(kProfilingResults); // remove previous result so that it won't be loaded to
121 // the new MediaCodecList
122 sp<MediaCodecList> codecList(new MediaCodecList(GetBuilders()));
123 if (codecList->initCheck() != OK) {
124 ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
125 return nullptr;
126 }
127
128 const auto& infos = codecList->mCodecInfos;
129 ALOGV("Codec profiling started.");
130 profileCodecs(infos, kProfilingResults);
131 ALOGV("Codec profiling completed.");
132 codecList = new MediaCodecList(GetBuilders());
133 if (codecList->initCheck() != OK) {
134 ALOGW("Failed to parse profiling results.");
135 return nullptr;
136 }
137
138 {
139 Mutex::Autolock autoLock(sInitMutex);
140 sCodecList = codecList;
141 }
142 return nullptr;
143 }
144
145 // static
getLocalInstance()146 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
147 Mutex::Autolock autoLock(sInitMutex);
148
149 if (sCodecList == nullptr) {
150 MediaCodecList *codecList = new MediaCodecList(GetBuilders());
151 if (codecList->initCheck() == OK) {
152 sCodecList = codecList;
153
154 if (isProfilingNeeded()) {
155 ALOGV("Codec profiling needed, will be run in separated thread.");
156 pthread_t profiler;
157 if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
158 ALOGW("Failed to create thread for codec profiling.");
159 }
160 }
161 } else {
162 // failure to initialize may be temporary. retry on next call.
163 delete codecList;
164 }
165 }
166
167 return sCodecList;
168 }
169
170 sp<IMediaCodecList> MediaCodecList::sRemoteList;
171
172 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
173 sp<IBinder> MediaCodecList::sMediaPlayer; // kept since linked to death
174
binderDied(const wp<IBinder> & who __unused)175 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
176 Mutex::Autolock _l(sRemoteInitMutex);
177 sRemoteList.clear();
178 sBinderDeathObserver.clear();
179 }
180
181 // static
getInstance()182 sp<IMediaCodecList> MediaCodecList::getInstance() {
183 Mutex::Autolock _l(sRemoteInitMutex);
184 if (sRemoteList == nullptr) {
185 sMediaPlayer = defaultServiceManager()->getService(String16("media.player"));
186 sp<IMediaPlayerService> service =
187 interface_cast<IMediaPlayerService>(sMediaPlayer);
188 if (service.get() != nullptr) {
189 sRemoteList = service->getCodecList();
190 if (sRemoteList != nullptr) {
191 sBinderDeathObserver = new BinderDeathObserver();
192 sMediaPlayer->linkToDeath(sBinderDeathObserver.get());
193 }
194 }
195 if (sRemoteList == nullptr) {
196 // if failed to get remote list, create local list
197 sRemoteList = getLocalInstance();
198 }
199 }
200 return sRemoteList;
201 }
202
MediaCodecList(std::vector<MediaCodecListBuilderBase * > builders)203 MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {
204 mGlobalSettings = new AMessage();
205 mCodecInfos.clear();
206 MediaCodecListWriter writer;
207 for (MediaCodecListBuilderBase *builder : builders) {
208 if (builder == nullptr) {
209 ALOGD("ignored a null builder");
210 continue;
211 }
212 auto currentCheck = builder->buildMediaCodecList(&writer);
213 if (currentCheck != OK) {
214 ALOGD("ignored failed builder");
215 continue;
216 } else {
217 mInitCheck = currentCheck;
218 }
219 }
220 writer.writeGlobalSettings(mGlobalSettings);
221 writer.writeCodecInfos(&mCodecInfos);
222 std::stable_sort(
223 mCodecInfos.begin(),
224 mCodecInfos.end(),
225 [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {
226 // null is lowest
227 return info1 == nullptr
228 || (info2 != nullptr && info1->getRank() < info2->getRank());
229 });
230
231 // remove duplicate entries
232 bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true);
233 if (dedupe) {
234 std::set<std::string> codecsSeen;
235 for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) {
236 std::string codecName = (*it)->getCodecName();
237 if (codecsSeen.count(codecName) == 0) {
238 codecsSeen.emplace(codecName);
239 it++;
240 } else {
241 it = mCodecInfos.erase(it);
242 }
243 }
244 }
245 }
246
~MediaCodecList()247 MediaCodecList::~MediaCodecList() {
248 }
249
initCheck() const250 status_t MediaCodecList::initCheck() const {
251 return mInitCheck;
252 }
253
254 // legacy method for non-advanced codecs
findCodecByType(const char * type,bool encoder,size_t startIndex) const255 ssize_t MediaCodecList::findCodecByType(
256 const char *type, bool encoder, size_t startIndex) const {
257 static const char *advancedFeatures[] = {
258 "feature-secure-playback",
259 "feature-tunneled-playback",
260 };
261
262 size_t numCodecInfos = mCodecInfos.size();
263 for (; startIndex < numCodecInfos; ++startIndex) {
264 const MediaCodecInfo &info = *mCodecInfos[startIndex];
265
266 if (info.isEncoder() != encoder) {
267 continue;
268 }
269 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
270 if (capabilities == nullptr) {
271 continue;
272 }
273 const sp<AMessage> &details = capabilities->getDetails();
274
275 int32_t required;
276 bool isAdvanced = false;
277 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
278 if (details->findInt32(advancedFeatures[ix], &required) &&
279 required != 0) {
280 isAdvanced = true;
281 break;
282 }
283 }
284
285 if (!isAdvanced) {
286 return startIndex;
287 }
288 }
289
290 return -ENOENT;
291 }
292
findCodecByName(const char * name) const293 ssize_t MediaCodecList::findCodecByName(const char *name) const {
294 Vector<AString> aliases;
295 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
296 if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
297 return i;
298 }
299 mCodecInfos[i]->getAliases(&aliases);
300 for (const AString &alias : aliases) {
301 if (alias == name) {
302 return i;
303 }
304 }
305 }
306
307 return -ENOENT;
308 }
309
countCodecs() const310 size_t MediaCodecList::countCodecs() const {
311 return mCodecInfos.size();
312 }
313
getGlobalSettings() const314 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
315 return mGlobalSettings;
316 }
317
318 //static
isSoftwareCodec(const AString & componentName)319 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
320 return componentName.startsWithIgnoreCase("OMX.google.")
321 || componentName.startsWithIgnoreCase("c2.android.")
322 || (!componentName.startsWithIgnoreCase("OMX.")
323 && !componentName.startsWithIgnoreCase("c2."));
324 }
325
compareSoftwareCodecsFirst(const AString * name1,const AString * name2)326 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
327 // sort order 1: software codecs are first (lower)
328 bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
329 bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
330 if (isSoftwareCodec1 != isSoftwareCodec2) {
331 return isSoftwareCodec2 - isSoftwareCodec1;
332 }
333
334 // sort order 2: Codec 2.0 codecs are first (lower)
335 bool isC2_1 = name1->startsWithIgnoreCase("c2.");
336 bool isC2_2 = name2->startsWithIgnoreCase("c2.");
337 if (isC2_1 != isC2_2) {
338 return isC2_2 - isC2_1;
339 }
340
341 // sort order 3: OMX codecs are first (lower)
342 bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
343 bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
344 return isOMX2 - isOMX1;
345 }
346
347 //static
findMatchingCodecs(const char * mime,bool encoder,uint32_t flags,Vector<AString> * matches)348 void MediaCodecList::findMatchingCodecs(
349 const char *mime, bool encoder, uint32_t flags,
350 Vector<AString> *matches) {
351 sp<AMessage> format; // initializes as clear/null
352 findMatchingCodecs(mime, encoder, flags, format, matches);
353 }
354
355 //static
findMatchingCodecs(const char * mime,bool encoder,uint32_t flags,sp<AMessage> format,Vector<AString> * matches)356 void MediaCodecList::findMatchingCodecs(
357 const char *mime, bool encoder, uint32_t flags, sp<AMessage> format,
358 Vector<AString> *matches) {
359 matches->clear();
360
361 const sp<IMediaCodecList> list = getInstance();
362 if (list == nullptr) {
363 return;
364 }
365
366 size_t index = 0;
367 for (;;) {
368 ssize_t matchIndex =
369 list->findCodecByType(mime, encoder, index);
370
371 if (matchIndex < 0) {
372 break;
373 }
374
375 index = matchIndex + 1;
376
377 const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
378 CHECK(info != nullptr);
379
380 AString componentName = info->getCodecName();
381
382 if (!codecHandlesFormat(mime, info, format)) {
383 ALOGV("skipping codec '%s' which doesn't satisfy format %s",
384 componentName.c_str(), format->debugString(2).c_str());
385 continue;
386 }
387
388 if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
389 ALOGV("skipping SW codec '%s'", componentName.c_str());
390 continue;
391 }
392
393 matches->push(componentName);
394 ALOGV("matching '%s'", componentName.c_str());
395 }
396
397 if (flags & kPreferSoftwareCodecs ||
398 property_get_bool("debug.stagefright.swcodec", false)) {
399 matches->sort(compareSoftwareCodecsFirst);
400 }
401 }
402
403 /*static*/
codecHandlesFormat(const char * mime,sp<MediaCodecInfo> info,sp<AMessage> format)404 bool MediaCodecList::codecHandlesFormat(const char *mime, sp<MediaCodecInfo> info,
405 sp<AMessage> format) {
406
407 if (format == nullptr) {
408 ALOGD("codecHandlesFormat: no format, so no extra checks");
409 return true;
410 }
411
412 sp<MediaCodecInfo::Capabilities> capabilities = info->getCapabilitiesFor(mime);
413
414 // ... no capabilities listed means 'handle it all'
415 if (capabilities == nullptr) {
416 ALOGD("codecHandlesFormat: no capabilities for refinement");
417 return true;
418 }
419
420 const sp<AMessage> &details = capabilities->getDetails();
421
422 // if parsing the capabilities fails, ignore this particular codec
423 // currently video-centric evaluation
424 //
425 // TODO: like to make it handle the same set of properties from
426 // MediaCodecInfo::isFormatSupported()
427 // not yet done here are:
428 // profile, level, bitrate, features,
429
430 bool isVideo = false;
431 if (strncmp(mime, "video/", 6) == 0) {
432 isVideo = true;
433 }
434
435 if (isVideo) {
436 int width = -1;
437 int height = -1;
438
439 if (format->findInt32("height", &height) && format->findInt32("width", &width)) {
440
441 // is it within the supported size range of the codec?
442 AString sizeRange;
443 AString minSize,maxSize;
444 AString minWidth, minHeight;
445 AString maxWidth, maxHeight;
446 if (!details->findString("size-range", &sizeRange)
447 || !splitString(sizeRange, "-", &minSize, &maxSize)) {
448 ALOGW("Unable to parse size-range from codec info");
449 return false;
450 }
451 if (!splitString(minSize, "x", &minWidth, &minHeight)) {
452 if (!splitString(minSize, "*", &minWidth, &minHeight)) {
453 ALOGW("Unable to parse size-range/min-size from codec info");
454 return false;
455 }
456 }
457 if (!splitString(maxSize, "x", &maxWidth, &maxHeight)) {
458 if (!splitString(maxSize, "*", &maxWidth, &maxHeight)) {
459 ALOGW("Unable to fully parse size-range/max-size from codec info");
460 return false;
461 }
462 }
463
464 // strtol() returns 0 if unable to parse a number, which works for our later tests
465 int minW = strtol(minWidth.c_str(), NULL, 10);
466 int minH = strtol(minHeight.c_str(), NULL, 10);
467 int maxW = strtol(maxWidth.c_str(), NULL, 10);
468 int maxH = strtol(maxHeight.c_str(), NULL, 10);
469
470 if (minW == 0 || minH == 0 || maxW == 0 || maxH == 0) {
471 ALOGW("Unable to parse values from size-range from codec info");
472 return false;
473 }
474
475 // finally, comparison time
476 if (width < minW || width > maxW || height < minH || height > maxH) {
477 ALOGV("format %dx%d outside of allowed %dx%d-%dx%d",
478 width, height, minW, minH, maxW, maxH);
479 // at this point, it's a rejection, UNLESS
480 // the codec allows swapping width and height
481 int32_t swappable;
482 if (!details->findInt32("feature-can-swap-width-height", &swappable)
483 || swappable == 0) {
484 return false;
485 }
486 // NB: deliberate comparison of height vs width limits (and width vs height)
487 if (height < minW || height > maxW || width < minH || width > maxH) {
488 return false;
489 }
490 }
491
492 // @ 'alignment' [e.g. "2x2" which tells us that both dimensions must be even]
493 // no alignment == we're ok with anything
494 AString alignment, alignWidth, alignHeight;
495 if (details->findString("alignment", &alignment)) {
496 if (splitString(alignment, "x", &alignWidth, &alignHeight) ||
497 splitString(alignment, "*", &alignWidth, &alignHeight)) {
498 int wAlign = strtol(alignWidth.c_str(), NULL, 10);
499 int hAlign = strtol(alignHeight.c_str(), NULL, 10);
500 // strtol() returns 0 if failing to parse, treat as "no restriction"
501 if (wAlign > 0 && hAlign > 0) {
502 if ((width % wAlign) != 0 || (height % hAlign) != 0) {
503 ALOGV("format dimensions %dx%d not aligned to %dx%d",
504 width, height, wAlign, hAlign);
505 return false;
506 }
507 }
508 }
509 }
510 }
511
512 int32_t profile = -1;
513 if (format->findInt32("profile", &profile)) {
514 int32_t level = -1;
515 format->findInt32("level", &level);
516 Vector<MediaCodecInfo::ProfileLevel> profileLevels;
517 capabilities->getSupportedProfileLevels(&profileLevels);
518 auto it = profileLevels.begin();
519 for (; it != profileLevels.end(); ++it) {
520 if (profile != it->mProfile) {
521 continue;
522 }
523 if (level > -1 && level > it->mLevel) {
524 continue;
525 }
526 break;
527 }
528
529 if (it == profileLevels.end()) {
530 ALOGV("Codec does not support profile %d with level %d", profile, level);
531 return false;
532 }
533 }
534 }
535
536 // haven't found a reason to discard this one
537 return true;
538 }
539
540 } // namespace android
541