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