• 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 <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