Home | History | Annotate | Download | only in libstagefright
      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 "MediaCodecListOverrides.h"
     22 #include "StagefrightPluginLoader.h"
     23 
     24 #include <binder/IServiceManager.h>
     25 
     26 #include <media/IMediaCodecList.h>
     27 #include <media/IMediaPlayerService.h>
     28 #include <media/MediaCodecInfo.h>
     29 
     30 #include <media/stagefright/foundation/ADebug.h>
     31 #include <media/stagefright/foundation/AMessage.h>
     32 #include <media/stagefright/foundation/MediaDefs.h>
     33 #include <media/stagefright/MediaCodecList.h>
     34 #include <media/stagefright/MediaErrors.h>
     35 #include <media/stagefright/OmxInfoBuilder.h>
     36 #include <media/stagefright/omx/OMXUtils.h>
     37 #include <xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h>
     38 
     39 #include <sys/stat.h>
     40 #include <utils/threads.h>
     41 
     42 #include <cutils/properties.h>
     43 
     44 #include <algorithm>
     45 
     46 namespace android {
     47 
     48 namespace {
     49 
     50 Mutex sInitMutex;
     51 
     52 Mutex sRemoteInitMutex;
     53 
     54 constexpr const char* kProfilingResults =
     55         MediaCodecsXmlParser::defaultProfilingResultsXmlPath;
     56 
     57 bool isProfilingNeeded() {
     58     int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
     59     if (value == 0) {
     60         return false;
     61     }
     62 
     63     bool profilingNeeded = true;
     64     FILE *resultsFile = fopen(kProfilingResults, "r");
     65     if (resultsFile) {
     66         AString currentVersion = getProfilingVersionString();
     67         size_t currentVersionSize = currentVersion.size();
     68         char *versionString = new char[currentVersionSize + 1];
     69         fgets(versionString, currentVersionSize + 1, resultsFile);
     70         if (strcmp(versionString, currentVersion.c_str()) == 0) {
     71             // profiling result up to date
     72             profilingNeeded = false;
     73         }
     74         fclose(resultsFile);
     75         delete[] versionString;
     76     }
     77     return profilingNeeded;
     78 }
     79 
     80 OmxInfoBuilder sOmxInfoBuilder;
     81 
     82 Mutex sCodec2InfoBuilderMutex;
     83 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
     84 
     85 MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
     86     Mutex::Autolock _l(sCodec2InfoBuilderMutex);
     87     if (!sCodec2InfoBuilder) {
     88         sCodec2InfoBuilder.reset(
     89                 StagefrightPluginLoader::GetCCodecInstance()->createBuilder());
     90     }
     91     return sCodec2InfoBuilder.get();
     92 }
     93 
     94 std::vector<MediaCodecListBuilderBase *> GetBuilders() {
     95     std::vector<MediaCodecListBuilderBase *> builders;
     96     // if plugin provides the input surface, we cannot use OMX video encoders.
     97     // In this case, rely on plugin to provide list of OMX codecs that are usable.
     98     sp<PersistentSurface> surfaceTest =
     99         StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
    100     if (surfaceTest == nullptr) {
    101         builders.push_back(&sOmxInfoBuilder);
    102     }
    103     builders.push_back(GetCodec2InfoBuilder());
    104     return builders;
    105 }
    106 
    107 }  // unnamed namespace
    108 
    109 // static
    110 sp<IMediaCodecList> MediaCodecList::sCodecList;
    111 
    112 // static
    113 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
    114     ALOGV("Enter profilerThreadWrapper.");
    115     remove(kProfilingResults);  // remove previous result so that it won't be loaded to
    116                                 // the new MediaCodecList
    117     sp<MediaCodecList> codecList(new MediaCodecList(GetBuilders()));
    118     if (codecList->initCheck() != OK) {
    119         ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
    120         return nullptr;
    121     }
    122 
    123     const auto& infos = codecList->mCodecInfos;
    124     ALOGV("Codec profiling started.");
    125     profileCodecs(infos, kProfilingResults);
    126     ALOGV("Codec profiling completed.");
    127     codecList = new MediaCodecList(GetBuilders());
    128     if (codecList->initCheck() != OK) {
    129         ALOGW("Failed to parse profiling results.");
    130         return nullptr;
    131     }
    132 
    133     {
    134         Mutex::Autolock autoLock(sInitMutex);
    135         sCodecList = codecList;
    136     }
    137     return nullptr;
    138 }
    139 
    140 // static
    141 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
    142     Mutex::Autolock autoLock(sInitMutex);
    143 
    144     if (sCodecList == nullptr) {
    145         MediaCodecList *codecList = new MediaCodecList(GetBuilders());
    146         if (codecList->initCheck() == OK) {
    147             sCodecList = codecList;
    148 
    149             if (isProfilingNeeded()) {
    150                 ALOGV("Codec profiling needed, will be run in separated thread.");
    151                 pthread_t profiler;
    152                 if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
    153                     ALOGW("Failed to create thread for codec profiling.");
    154                 }
    155             }
    156         } else {
    157             // failure to initialize may be temporary. retry on next call.
    158             delete codecList;
    159         }
    160     }
    161 
    162     return sCodecList;
    163 }
    164 
    165 sp<IMediaCodecList> MediaCodecList::sRemoteList;
    166 
    167 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
    168 
    169 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
    170     Mutex::Autolock _l(sRemoteInitMutex);
    171     sRemoteList.clear();
    172     sBinderDeathObserver.clear();
    173 }
    174 
    175 // static
    176 sp<IMediaCodecList> MediaCodecList::getInstance() {
    177     Mutex::Autolock _l(sRemoteInitMutex);
    178     if (sRemoteList == nullptr) {
    179         sp<IBinder> binder =
    180             defaultServiceManager()->getService(String16("media.player"));
    181         sp<IMediaPlayerService> service =
    182             interface_cast<IMediaPlayerService>(binder);
    183         if (service.get() != nullptr) {
    184             sRemoteList = service->getCodecList();
    185             if (sRemoteList != nullptr) {
    186                 sBinderDeathObserver = new BinderDeathObserver();
    187                 binder->linkToDeath(sBinderDeathObserver.get());
    188             }
    189         }
    190         if (sRemoteList == nullptr) {
    191             // if failed to get remote list, create local list
    192             sRemoteList = getLocalInstance();
    193         }
    194     }
    195     return sRemoteList;
    196 }
    197 
    198 MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {
    199     mGlobalSettings = new AMessage();
    200     mCodecInfos.clear();
    201     MediaCodecListWriter writer;
    202     for (MediaCodecListBuilderBase *builder : builders) {
    203         if (builder == nullptr) {
    204             ALOGD("ignored a null builder");
    205             continue;
    206         }
    207         mInitCheck = builder->buildMediaCodecList(&writer);
    208         if (mInitCheck != OK) {
    209             break;
    210         }
    211     }
    212     writer.writeGlobalSettings(mGlobalSettings);
    213     writer.writeCodecInfos(&mCodecInfos);
    214     std::stable_sort(
    215             mCodecInfos.begin(),
    216             mCodecInfos.end(),
    217             [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {
    218                 if (info2 == nullptr) {
    219                     return false;
    220                 } else if (info1 == nullptr) {
    221                     return true;
    222                 } else {
    223                     return info1->rank() < info2->rank();
    224                 }
    225             });
    226 }
    227 
    228 MediaCodecList::~MediaCodecList() {
    229 }
    230 
    231 status_t MediaCodecList::initCheck() const {
    232     return mInitCheck;
    233 }
    234 
    235 // legacy method for non-advanced codecs
    236 ssize_t MediaCodecList::findCodecByType(
    237         const char *type, bool encoder, size_t startIndex) const {
    238     static const char *advancedFeatures[] = {
    239         "feature-secure-playback",
    240         "feature-tunneled-playback",
    241     };
    242 
    243     size_t numCodecInfos = mCodecInfos.size();
    244     for (; startIndex < numCodecInfos; ++startIndex) {
    245         const MediaCodecInfo &info = *mCodecInfos[startIndex];
    246 
    247         if (info.isEncoder() != encoder) {
    248             continue;
    249         }
    250         sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
    251         if (capabilities == nullptr) {
    252             continue;
    253         }
    254         const sp<AMessage> &details = capabilities->getDetails();
    255 
    256         int32_t required;
    257         bool isAdvanced = false;
    258         for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
    259             if (details->findInt32(advancedFeatures[ix], &required) &&
    260                     required != 0) {
    261                 isAdvanced = true;
    262                 break;
    263             }
    264         }
    265 
    266         if (!isAdvanced) {
    267             return startIndex;
    268         }
    269     }
    270 
    271     return -ENOENT;
    272 }
    273 
    274 ssize_t MediaCodecList::findCodecByName(const char *name) const {
    275     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
    276         if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
    277             return i;
    278         }
    279     }
    280 
    281     return -ENOENT;
    282 }
    283 
    284 size_t MediaCodecList::countCodecs() const {
    285     return mCodecInfos.size();
    286 }
    287 
    288 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
    289     return mGlobalSettings;
    290 }
    291 
    292 //static
    293 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
    294     return componentName.startsWithIgnoreCase("OMX.google.")
    295             || componentName.startsWithIgnoreCase("c2.android.")
    296             || (!componentName.startsWithIgnoreCase("OMX.")
    297                     && !componentName.startsWithIgnoreCase("c2."));
    298 }
    299 
    300 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
    301     // sort order 1: software codecs are first (lower)
    302     bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
    303     bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
    304     if (isSoftwareCodec1 != isSoftwareCodec2) {
    305         return isSoftwareCodec2 - isSoftwareCodec1;
    306     }
    307 
    308     // sort order 2: Codec 2.0 codecs are first (lower)
    309     bool isC2_1 = name1->startsWithIgnoreCase("c2.");
    310     bool isC2_2 = name2->startsWithIgnoreCase("c2.");
    311     if (isC2_1 != isC2_2) {
    312         return isC2_2 - isC2_1;
    313     }
    314 
    315     // sort order 3: OMX codecs are first (lower)
    316     bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
    317     bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
    318     return isOMX2 - isOMX1;
    319 }
    320 
    321 //static
    322 void MediaCodecList::findMatchingCodecs(
    323         const char *mime, bool encoder, uint32_t flags,
    324         Vector<AString> *matches) {
    325     matches->clear();
    326 
    327     const sp<IMediaCodecList> list = getInstance();
    328     if (list == nullptr) {
    329         return;
    330     }
    331 
    332     size_t index = 0;
    333     for (;;) {
    334         ssize_t matchIndex =
    335             list->findCodecByType(mime, encoder, index);
    336 
    337         if (matchIndex < 0) {
    338             break;
    339         }
    340 
    341         index = matchIndex + 1;
    342 
    343         const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
    344         CHECK(info != nullptr);
    345         AString componentName = info->getCodecName();
    346 
    347         if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
    348             ALOGV("skipping SW codec '%s'", componentName.c_str());
    349         } else {
    350             matches->push(componentName);
    351             ALOGV("matching '%s'", componentName.c_str());
    352         }
    353     }
    354 
    355     if (flags & kPreferSoftwareCodecs ||
    356             property_get_bool("debug.stagefright.swcodec", false)) {
    357         matches->sort(compareSoftwareCodecsFirst);
    358     }
    359 }
    360 
    361 }  // namespace android
    362