• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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