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