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