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 
     23 #include <binder/IServiceManager.h>
     24 
     25 #include <media/IMediaCodecList.h>
     26 #include <media/IMediaPlayerService.h>
     27 #include <media/IResourceManagerService.h>
     28 #include <media/MediaCodecInfo.h>
     29 #include <media/MediaResourcePolicy.h>
     30 
     31 #include <media/stagefright/foundation/ADebug.h>
     32 #include <media/stagefright/foundation/AMessage.h>
     33 #include <media/stagefright/ACodec.h>
     34 #include <media/stagefright/MediaCodec.h>
     35 #include <media/stagefright/MediaCodecList.h>
     36 #include <media/stagefright/MediaErrors.h>
     37 #include <media/stagefright/OMXClient.h>
     38 
     39 #include <sys/stat.h>
     40 #include <utils/threads.h>
     41 
     42 #include <cutils/properties.h>
     43 #include <expat.h>
     44 
     45 namespace android {
     46 
     47 const char *kMaxEncoderInputBuffers = "max-video-encoder-input-buffers";
     48 
     49 static Mutex sInitMutex;
     50 
     51 static bool parseBoolean(const char *s) {
     52     if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
     53         return true;
     54     }
     55     char *end;
     56     unsigned long res = strtoul(s, &end, 10);
     57     return *s != '\0' && *end == '\0' && res > 0;
     58 }
     59 
     60 static 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 // static
     84 sp<IMediaCodecList> MediaCodecList::sCodecList;
     85 
     86 // static
     87 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
     88     ALOGV("Enter profilerThreadWrapper.");
     89     remove(kProfilingResults);  // remove previous result so that it won't be loaded to
     90                                 // the new MediaCodecList
     91     MediaCodecList *codecList = new MediaCodecList();
     92     if (codecList->initCheck() != OK) {
     93         ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
     94         delete codecList;
     95         return NULL;
     96     }
     97 
     98     Vector<sp<MediaCodecInfo>> infos;
     99     for (size_t i = 0; i < codecList->countCodecs(); ++i) {
    100         infos.push_back(codecList->getCodecInfo(i));
    101     }
    102     ALOGV("Codec profiling started.");
    103     profileCodecs(infos);
    104     ALOGV("Codec profiling completed.");
    105     codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
    106 
    107     {
    108         Mutex::Autolock autoLock(sInitMutex);
    109         sCodecList = codecList;
    110     }
    111     return NULL;
    112 }
    113 
    114 // static
    115 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
    116     Mutex::Autolock autoLock(sInitMutex);
    117 
    118     if (sCodecList == NULL) {
    119         MediaCodecList *codecList = new MediaCodecList;
    120         if (codecList->initCheck() == OK) {
    121             sCodecList = codecList;
    122 
    123             if (isProfilingNeeded()) {
    124                 ALOGV("Codec profiling needed, will be run in separated thread.");
    125                 pthread_t profiler;
    126                 if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
    127                     ALOGW("Failed to create thread for codec profiling.");
    128                 }
    129             }
    130         } else {
    131             // failure to initialize may be temporary. retry on next call.
    132             delete codecList;
    133         }
    134     }
    135 
    136     return sCodecList;
    137 }
    138 
    139 static Mutex sRemoteInitMutex;
    140 
    141 sp<IMediaCodecList> MediaCodecList::sRemoteList;
    142 
    143 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
    144 
    145 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
    146     Mutex::Autolock _l(sRemoteInitMutex);
    147     sRemoteList.clear();
    148     sBinderDeathObserver.clear();
    149 }
    150 
    151 // static
    152 sp<IMediaCodecList> MediaCodecList::getInstance() {
    153     Mutex::Autolock _l(sRemoteInitMutex);
    154     if (sRemoteList == NULL) {
    155         sp<IBinder> binder =
    156             defaultServiceManager()->getService(String16("media.player"));
    157         sp<IMediaPlayerService> service =
    158             interface_cast<IMediaPlayerService>(binder);
    159         if (service.get() != NULL) {
    160             sRemoteList = service->getCodecList();
    161             if (sRemoteList != NULL) {
    162                 sBinderDeathObserver = new BinderDeathObserver();
    163                 binder->linkToDeath(sBinderDeathObserver.get());
    164             }
    165         }
    166         if (sRemoteList == NULL) {
    167             // if failed to get remote list, create local list
    168             sRemoteList = getLocalInstance();
    169         }
    170     }
    171     return sRemoteList;
    172 }
    173 
    174 MediaCodecList::MediaCodecList()
    175     : mInitCheck(NO_INIT),
    176       mUpdate(false),
    177       mGlobalSettings(new AMessage()) {
    178     parseTopLevelXMLFile("/etc/media_codecs.xml");
    179     parseTopLevelXMLFile("/etc/media_codecs_performance.xml", true/* ignore_errors */);
    180     parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
    181 }
    182 
    183 void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
    184     // get href_base
    185     char *href_base_end = strrchr(codecs_xml, '/');
    186     if (href_base_end != NULL) {
    187         mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
    188     }
    189 
    190     mInitCheck = OK; // keeping this here for safety
    191     mCurrentSection = SECTION_TOPLEVEL;
    192     mDepth = 0;
    193 
    194     OMXClient client;
    195     mInitCheck = client.connect();
    196     if (mInitCheck != OK) {
    197         return;  // this may fail if IMediaPlayerService is not available.
    198     }
    199     mOMX = client.interface();
    200     parseXMLFile(codecs_xml);
    201     mOMX.clear();
    202 
    203     if (mInitCheck != OK) {
    204         if (ignore_errors) {
    205             mInitCheck = OK;
    206             return;
    207         }
    208         mCodecInfos.clear();
    209         return;
    210     }
    211 
    212     Vector<MediaResourcePolicy> policies;
    213     AString value;
    214     if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
    215         policies.push_back(
    216                 MediaResourcePolicy(
    217                         String8(kPolicySupportsMultipleSecureCodecs),
    218                         String8(value.c_str())));
    219     }
    220     if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
    221         policies.push_back(
    222                 MediaResourcePolicy(
    223                         String8(kPolicySupportsSecureWithNonSecureCodec),
    224                         String8(value.c_str())));
    225     }
    226     if (policies.size() > 0) {
    227         sp<IServiceManager> sm = defaultServiceManager();
    228         sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
    229         sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
    230         if (service == NULL) {
    231             ALOGE("MediaCodecList: failed to get ResourceManagerService");
    232         } else {
    233             service->config(policies);
    234         }
    235     }
    236 
    237     for (size_t i = mCodecInfos.size(); i > 0;) {
    238         i--;
    239         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
    240         if (info.mCaps.size() == 0) {
    241             // No types supported by this component???
    242             ALOGW("Component %s does not support any type of media?",
    243                   info.mName.c_str());
    244 
    245             mCodecInfos.removeAt(i);
    246 #if LOG_NDEBUG == 0
    247         } else {
    248             for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
    249                 AString mime = info.mCaps.keyAt(type_ix);
    250                 const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
    251 
    252                 ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
    253                         caps->getDetails()->debugString().c_str());
    254                 ALOGV("    flags=%d", caps->getFlags());
    255                 {
    256                     Vector<uint32_t> colorFormats;
    257                     caps->getSupportedColorFormats(&colorFormats);
    258                     AString nice;
    259                     for (size_t ix = 0; ix < colorFormats.size(); ix++) {
    260                         if (ix > 0) {
    261                             nice.append(", ");
    262                         }
    263                         nice.append(colorFormats.itemAt(ix));
    264                     }
    265                     ALOGV("    colors=[%s]", nice.c_str());
    266                 }
    267                 {
    268                     Vector<MediaCodecInfo::ProfileLevel> profileLevels;
    269                     caps->getSupportedProfileLevels(&profileLevels);
    270                     AString nice;
    271                     for (size_t ix = 0; ix < profileLevels.size(); ix++) {
    272                         if (ix > 0) {
    273                             nice.append(", ");
    274                         }
    275                         const MediaCodecInfo::ProfileLevel &pl =
    276                             profileLevels.itemAt(ix);
    277                         nice.append(pl.mProfile);
    278                         nice.append("/");
    279                         nice.append(pl.mLevel);
    280                     }
    281                     ALOGV("    levels=[%s]", nice.c_str());
    282                 }
    283                 {
    284                     AString quirks;
    285                     for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
    286                         if (ix > 0) {
    287                             quirks.append(", ");
    288                         }
    289                         quirks.append(info.mQuirks[ix]);
    290                     }
    291                     ALOGV("    quirks=[%s]", quirks.c_str());
    292                 }
    293             }
    294 #endif
    295         }
    296     }
    297 
    298 #if 0
    299     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
    300         const CodecInfo &info = mCodecInfos.itemAt(i);
    301 
    302         AString line = info.mName;
    303         line.append(" supports ");
    304         for (size_t j = 0; j < mTypes.size(); ++j) {
    305             uint32_t value = mTypes.valueAt(j);
    306 
    307             if (info.mTypes & (1ul << value)) {
    308                 line.append(mTypes.keyAt(j));
    309                 line.append(" ");
    310             }
    311         }
    312 
    313         ALOGI("%s", line.c_str());
    314     }
    315 #endif
    316 }
    317 
    318 MediaCodecList::~MediaCodecList() {
    319 }
    320 
    321 status_t MediaCodecList::initCheck() const {
    322     return mInitCheck;
    323 }
    324 
    325 void MediaCodecList::parseXMLFile(const char *path) {
    326     FILE *file = fopen(path, "r");
    327 
    328     if (file == NULL) {
    329         ALOGW("unable to open media codecs configuration xml file: %s", path);
    330         mInitCheck = NAME_NOT_FOUND;
    331         return;
    332     }
    333 
    334     XML_Parser parser = ::XML_ParserCreate(NULL);
    335     CHECK(parser != NULL);
    336 
    337     ::XML_SetUserData(parser, this);
    338     ::XML_SetElementHandler(
    339             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
    340 
    341     const int BUFF_SIZE = 512;
    342     while (mInitCheck == OK) {
    343         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
    344         if (buff == NULL) {
    345             ALOGE("failed in call to XML_GetBuffer()");
    346             mInitCheck = UNKNOWN_ERROR;
    347             break;
    348         }
    349 
    350         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
    351         if (bytes_read < 0) {
    352             ALOGE("failed in call to read");
    353             mInitCheck = ERROR_IO;
    354             break;
    355         }
    356 
    357         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
    358         if (status != XML_STATUS_OK) {
    359             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
    360             mInitCheck = ERROR_MALFORMED;
    361             break;
    362         }
    363 
    364         if (bytes_read == 0) {
    365             break;
    366         }
    367     }
    368 
    369     ::XML_ParserFree(parser);
    370 
    371     fclose(file);
    372     file = NULL;
    373 }
    374 
    375 // static
    376 void MediaCodecList::StartElementHandlerWrapper(
    377         void *me, const char *name, const char **attrs) {
    378     static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
    379 }
    380 
    381 // static
    382 void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
    383     static_cast<MediaCodecList *>(me)->endElementHandler(name);
    384 }
    385 
    386 status_t MediaCodecList::includeXMLFile(const char **attrs) {
    387     const char *href = NULL;
    388     size_t i = 0;
    389     while (attrs[i] != NULL) {
    390         if (!strcmp(attrs[i], "href")) {
    391             if (attrs[i + 1] == NULL) {
    392                 return -EINVAL;
    393             }
    394             href = attrs[i + 1];
    395             ++i;
    396         } else {
    397             return -EINVAL;
    398         }
    399         ++i;
    400     }
    401 
    402     // For security reasons and for simplicity, file names can only contain
    403     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
    404     for (i = 0; href[i] != '\0'; i++) {
    405         if (href[i] == '.' || href[i] == '_' ||
    406                 (href[i] >= '0' && href[i] <= '9') ||
    407                 (href[i] >= 'A' && href[i] <= 'Z') ||
    408                 (href[i] >= 'a' && href[i] <= 'z')) {
    409             continue;
    410         }
    411         ALOGE("invalid include file name: %s", href);
    412         return -EINVAL;
    413     }
    414 
    415     AString filename = href;
    416     if (!filename.startsWith("media_codecs_") ||
    417         !filename.endsWith(".xml")) {
    418         ALOGE("invalid include file name: %s", href);
    419         return -EINVAL;
    420     }
    421     filename.insert(mHrefBase, 0);
    422 
    423     parseXMLFile(filename.c_str());
    424     return mInitCheck;
    425 }
    426 
    427 void MediaCodecList::startElementHandler(
    428         const char *name, const char **attrs) {
    429     if (mInitCheck != OK) {
    430         return;
    431     }
    432 
    433     bool inType = true;
    434 
    435     if (!strcmp(name, "Include")) {
    436         mInitCheck = includeXMLFile(attrs);
    437         if (mInitCheck == OK) {
    438             mPastSections.push(mCurrentSection);
    439             mCurrentSection = SECTION_INCLUDE;
    440         }
    441         ++mDepth;
    442         return;
    443     }
    444 
    445     switch (mCurrentSection) {
    446         case SECTION_TOPLEVEL:
    447         {
    448             if (!strcmp(name, "Decoders")) {
    449                 mCurrentSection = SECTION_DECODERS;
    450             } else if (!strcmp(name, "Encoders")) {
    451                 mCurrentSection = SECTION_ENCODERS;
    452             } else if (!strcmp(name, "Settings")) {
    453                 mCurrentSection = SECTION_SETTINGS;
    454             }
    455             break;
    456         }
    457 
    458         case SECTION_SETTINGS:
    459         {
    460             if (!strcmp(name, "Setting")) {
    461                 mInitCheck = addSettingFromAttributes(attrs);
    462             }
    463             break;
    464         }
    465 
    466         case SECTION_DECODERS:
    467         {
    468             if (!strcmp(name, "MediaCodec")) {
    469                 mInitCheck =
    470                     addMediaCodecFromAttributes(false /* encoder */, attrs);
    471 
    472                 mCurrentSection = SECTION_DECODER;
    473             }
    474             break;
    475         }
    476 
    477         case SECTION_ENCODERS:
    478         {
    479             if (!strcmp(name, "MediaCodec")) {
    480                 mInitCheck =
    481                     addMediaCodecFromAttributes(true /* encoder */, attrs);
    482 
    483                 mCurrentSection = SECTION_ENCODER;
    484             }
    485             break;
    486         }
    487 
    488         case SECTION_DECODER:
    489         case SECTION_ENCODER:
    490         {
    491             if (!strcmp(name, "Quirk")) {
    492                 mInitCheck = addQuirk(attrs);
    493             } else if (!strcmp(name, "Type")) {
    494                 mInitCheck = addTypeFromAttributes(attrs);
    495                 mCurrentSection =
    496                     (mCurrentSection == SECTION_DECODER
    497                             ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
    498             }
    499         }
    500         inType = false;
    501         // fall through
    502 
    503         case SECTION_DECODER_TYPE:
    504         case SECTION_ENCODER_TYPE:
    505         {
    506             // ignore limits and features specified outside of type
    507             bool outside = !inType && !mCurrentInfo->mHasSoleMime;
    508             if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
    509                 ALOGW("ignoring %s specified outside of a Type", name);
    510             } else if (!strcmp(name, "Limit")) {
    511                 mInitCheck = addLimit(attrs);
    512             } else if (!strcmp(name, "Feature")) {
    513                 mInitCheck = addFeature(attrs);
    514             }
    515             break;
    516         }
    517 
    518         default:
    519             break;
    520     }
    521 
    522     ++mDepth;
    523 }
    524 
    525 void MediaCodecList::endElementHandler(const char *name) {
    526     if (mInitCheck != OK) {
    527         return;
    528     }
    529 
    530     switch (mCurrentSection) {
    531         case SECTION_SETTINGS:
    532         {
    533             if (!strcmp(name, "Settings")) {
    534                 mCurrentSection = SECTION_TOPLEVEL;
    535             }
    536             break;
    537         }
    538 
    539         case SECTION_DECODERS:
    540         {
    541             if (!strcmp(name, "Decoders")) {
    542                 mCurrentSection = SECTION_TOPLEVEL;
    543             }
    544             break;
    545         }
    546 
    547         case SECTION_ENCODERS:
    548         {
    549             if (!strcmp(name, "Encoders")) {
    550                 mCurrentSection = SECTION_TOPLEVEL;
    551             }
    552             break;
    553         }
    554 
    555         case SECTION_DECODER_TYPE:
    556         case SECTION_ENCODER_TYPE:
    557         {
    558             if (!strcmp(name, "Type")) {
    559                 mCurrentSection =
    560                     (mCurrentSection == SECTION_DECODER_TYPE
    561                             ? SECTION_DECODER : SECTION_ENCODER);
    562 
    563                 mCurrentInfo->complete();
    564             }
    565             break;
    566         }
    567 
    568         case SECTION_DECODER:
    569         {
    570             if (!strcmp(name, "MediaCodec")) {
    571                 mCurrentSection = SECTION_DECODERS;
    572                 mCurrentInfo->complete();
    573                 mCurrentInfo = NULL;
    574             }
    575             break;
    576         }
    577 
    578         case SECTION_ENCODER:
    579         {
    580             if (!strcmp(name, "MediaCodec")) {
    581                 mCurrentSection = SECTION_ENCODERS;
    582                 mCurrentInfo->complete();;
    583                 mCurrentInfo = NULL;
    584             }
    585             break;
    586         }
    587 
    588         case SECTION_INCLUDE:
    589         {
    590             if (!strcmp(name, "Include") && mPastSections.size() > 0) {
    591                 mCurrentSection = mPastSections.top();
    592                 mPastSections.pop();
    593             }
    594             break;
    595         }
    596 
    597         default:
    598             break;
    599     }
    600 
    601     --mDepth;
    602 }
    603 
    604 status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
    605     const char *name = NULL;
    606     const char *value = NULL;
    607     const char *update = NULL;
    608 
    609     size_t i = 0;
    610     while (attrs[i] != NULL) {
    611         if (!strcmp(attrs[i], "name")) {
    612             if (attrs[i + 1] == NULL) {
    613                 return -EINVAL;
    614             }
    615             name = attrs[i + 1];
    616             ++i;
    617         } else if (!strcmp(attrs[i], "value")) {
    618             if (attrs[i + 1] == NULL) {
    619                 return -EINVAL;
    620             }
    621             value = attrs[i + 1];
    622             ++i;
    623         } else if (!strcmp(attrs[i], "update")) {
    624             if (attrs[i + 1] == NULL) {
    625                 return -EINVAL;
    626             }
    627             update = attrs[i + 1];
    628             ++i;
    629         } else {
    630             return -EINVAL;
    631         }
    632 
    633         ++i;
    634     }
    635 
    636     if (name == NULL || value == NULL) {
    637         return -EINVAL;
    638     }
    639 
    640     mUpdate = (update != NULL) && parseBoolean(update);
    641     if (mUpdate != mGlobalSettings->contains(name)) {
    642         return -EINVAL;
    643     }
    644 
    645     mGlobalSettings->setString(name, value);
    646     return OK;
    647 }
    648 
    649 void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
    650     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
    651         if (AString(name) == mCodecInfos[i]->getCodecName()) {
    652             if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
    653                 ALOGW("Overrides with an unexpected mime %s", type);
    654                 // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
    655                 // overrides we don't want.
    656                 mCurrentInfo = new MediaCodecInfo(name, encoder, type);
    657             } else {
    658                 mCurrentInfo = mCodecInfos.editItemAt(i);
    659                 mCurrentInfo->updateMime(type);  // to set the current cap
    660             }
    661             return;
    662         }
    663     }
    664     mCurrentInfo = new MediaCodecInfo(name, encoder, type);
    665     // The next step involves trying to load the codec, which may
    666     // fail.  Only list the codec if this succeeds.
    667     // However, keep mCurrentInfo object around until parsing
    668     // of full codec info is completed.
    669     if (initializeCapabilities(type) == OK) {
    670         mCodecInfos.push_back(mCurrentInfo);
    671     }
    672 }
    673 
    674 status_t MediaCodecList::addMediaCodecFromAttributes(
    675         bool encoder, const char **attrs) {
    676     const char *name = NULL;
    677     const char *type = NULL;
    678     const char *update = NULL;
    679 
    680     size_t i = 0;
    681     while (attrs[i] != NULL) {
    682         if (!strcmp(attrs[i], "name")) {
    683             if (attrs[i + 1] == NULL) {
    684                 return -EINVAL;
    685             }
    686             name = attrs[i + 1];
    687             ++i;
    688         } else if (!strcmp(attrs[i], "type")) {
    689             if (attrs[i + 1] == NULL) {
    690                 return -EINVAL;
    691             }
    692             type = attrs[i + 1];
    693             ++i;
    694         } else if (!strcmp(attrs[i], "update")) {
    695             if (attrs[i + 1] == NULL) {
    696                 return -EINVAL;
    697             }
    698             update = attrs[i + 1];
    699             ++i;
    700         } else {
    701             return -EINVAL;
    702         }
    703 
    704         ++i;
    705     }
    706 
    707     if (name == NULL) {
    708         return -EINVAL;
    709     }
    710 
    711     mUpdate = (update != NULL) && parseBoolean(update);
    712     ssize_t index = -1;
    713     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
    714         if (AString(name) == mCodecInfos[i]->getCodecName()) {
    715             index = i;
    716         }
    717     }
    718     if (mUpdate != (index >= 0)) {
    719         return -EINVAL;
    720     }
    721 
    722     if (index >= 0) {
    723         // existing codec
    724         mCurrentInfo = mCodecInfos.editItemAt(index);
    725         if (type != NULL) {
    726             // existing type
    727             if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
    728                 return -EINVAL;
    729             }
    730             mCurrentInfo->updateMime(type);
    731         }
    732     } else {
    733         // new codec
    734         mCurrentInfo = new MediaCodecInfo(name, encoder, type);
    735         // The next step involves trying to load the codec, which may
    736         // fail.  Only list the codec if this succeeds.
    737         // However, keep mCurrentInfo object around until parsing
    738         // of full codec info is completed.
    739         if (initializeCapabilities(type) == OK) {
    740             mCodecInfos.push_back(mCurrentInfo);
    741         }
    742     }
    743 
    744     return OK;
    745 }
    746 
    747 status_t MediaCodecList::initializeCapabilities(const char *type) {
    748     if (type == NULL) {
    749         return OK;
    750     }
    751 
    752     ALOGV("initializeCapabilities %s:%s",
    753             mCurrentInfo->mName.c_str(), type);
    754 
    755     sp<MediaCodecInfo::Capabilities> caps;
    756     status_t err = MediaCodec::QueryCapabilities(
    757             mCurrentInfo->mName,
    758             type,
    759             mCurrentInfo->mIsEncoder,
    760             &caps);
    761     if (err != OK) {
    762         return err;
    763     } else if (caps == NULL) {
    764         ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
    765                 mCurrentInfo->mName.c_str(), type,
    766                 mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
    767         return UNKNOWN_ERROR;
    768     }
    769 
    770     return mCurrentInfo->initializeCapabilities(caps);
    771 }
    772 
    773 status_t MediaCodecList::addQuirk(const char **attrs) {
    774     const char *name = NULL;
    775 
    776     size_t i = 0;
    777     while (attrs[i] != NULL) {
    778         if (!strcmp(attrs[i], "name")) {
    779             if (attrs[i + 1] == NULL) {
    780                 return -EINVAL;
    781             }
    782             name = attrs[i + 1];
    783             ++i;
    784         } else {
    785             return -EINVAL;
    786         }
    787 
    788         ++i;
    789     }
    790 
    791     if (name == NULL) {
    792         return -EINVAL;
    793     }
    794 
    795     mCurrentInfo->addQuirk(name);
    796     return OK;
    797 }
    798 
    799 status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
    800     const char *name = NULL;
    801     const char *update = NULL;
    802 
    803     size_t i = 0;
    804     while (attrs[i] != NULL) {
    805         if (!strcmp(attrs[i], "name")) {
    806             if (attrs[i + 1] == NULL) {
    807                 return -EINVAL;
    808             }
    809             name = attrs[i + 1];
    810             ++i;
    811         } else if (!strcmp(attrs[i], "update")) {
    812             if (attrs[i + 1] == NULL) {
    813                 return -EINVAL;
    814             }
    815             update = attrs[i + 1];
    816             ++i;
    817         } else {
    818             return -EINVAL;
    819         }
    820 
    821         ++i;
    822     }
    823 
    824     if (name == NULL) {
    825         return -EINVAL;
    826     }
    827 
    828     bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
    829     if (mUpdate != isExistingType) {
    830         return -EINVAL;
    831     }
    832 
    833     status_t ret;
    834     if (mUpdate) {
    835         ret = mCurrentInfo->updateMime(name);
    836     } else {
    837         ret = mCurrentInfo->addMime(name);
    838     }
    839 
    840     if (ret != OK) {
    841         return ret;
    842     }
    843 
    844     // The next step involves trying to load the codec, which may
    845     // fail.  Handle this gracefully (by not reporting such mime).
    846     if (!mUpdate && initializeCapabilities(name) != OK) {
    847         mCurrentInfo->removeMime(name);
    848     }
    849     return OK;
    850 }
    851 
    852 // legacy method for non-advanced codecs
    853 ssize_t MediaCodecList::findCodecByType(
    854         const char *type, bool encoder, size_t startIndex) const {
    855     static const char *advancedFeatures[] = {
    856         "feature-secure-playback",
    857         "feature-tunneled-playback",
    858     };
    859 
    860     size_t numCodecs = mCodecInfos.size();
    861     for (; startIndex < numCodecs; ++startIndex) {
    862         const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
    863 
    864         if (info.isEncoder() != encoder) {
    865             continue;
    866         }
    867         sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
    868         if (capabilities == NULL) {
    869             continue;
    870         }
    871         const sp<AMessage> &details = capabilities->getDetails();
    872 
    873         int32_t required;
    874         bool isAdvanced = false;
    875         for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
    876             if (details->findInt32(advancedFeatures[ix], &required) &&
    877                     required != 0) {
    878                 isAdvanced = true;
    879                 break;
    880             }
    881         }
    882 
    883         if (!isAdvanced) {
    884             return startIndex;
    885         }
    886     }
    887 
    888     return -ENOENT;
    889 }
    890 
    891 static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
    892     ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
    893             (found ? "" : "no "), attr);
    894     return -EINVAL;
    895 }
    896 
    897 static status_t limitError(AString name, const char *msg) {
    898     ALOGE("limit '%s' %s", name.c_str(), msg);
    899     return -EINVAL;
    900 }
    901 
    902 static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
    903     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
    904             attr, value.c_str());
    905     return -EINVAL;
    906 }
    907 
    908 status_t MediaCodecList::addLimit(const char **attrs) {
    909     sp<AMessage> msg = new AMessage();
    910 
    911     size_t i = 0;
    912     while (attrs[i] != NULL) {
    913         if (attrs[i + 1] == NULL) {
    914             return -EINVAL;
    915         }
    916 
    917         // attributes with values
    918         if (!strcmp(attrs[i], "name")
    919                 || !strcmp(attrs[i], "default")
    920                 || !strcmp(attrs[i], "in")
    921                 || !strcmp(attrs[i], "max")
    922                 || !strcmp(attrs[i], "min")
    923                 || !strcmp(attrs[i], "range")
    924                 || !strcmp(attrs[i], "ranges")
    925                 || !strcmp(attrs[i], "scale")
    926                 || !strcmp(attrs[i], "value")) {
    927             msg->setString(attrs[i], attrs[i + 1]);
    928             ++i;
    929         } else {
    930             return -EINVAL;
    931         }
    932         ++i;
    933     }
    934 
    935     AString name;
    936     if (!msg->findString("name", &name)) {
    937         ALOGE("limit with no 'name' attribute");
    938         return -EINVAL;
    939     }
    940 
    941     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
    942     // measured-frame-rate, measured-blocks-per-second: range
    943     // quality: range + default + [scale]
    944     // complexity: range + default
    945     bool found;
    946 
    947     if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
    948             || name == "blocks-per-second" || name == "complexity"
    949             || name == "frame-rate" || name == "quality" || name == "size"
    950             || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
    951         AString min, max;
    952         if (msg->findString("min", &min) && msg->findString("max", &max)) {
    953             min.append("-");
    954             min.append(max);
    955             if (msg->contains("range") || msg->contains("value")) {
    956                 return limitError(name, "has 'min' and 'max' as well as 'range' or "
    957                         "'value' attributes");
    958             }
    959             msg->setString("range", min);
    960         } else if (msg->contains("min") || msg->contains("max")) {
    961             return limitError(name, "has only 'min' or 'max' attribute");
    962         } else if (msg->findString("value", &max)) {
    963             min = max;
    964             min.append("-");
    965             min.append(max);
    966             if (msg->contains("range")) {
    967                 return limitError(name, "has both 'range' and 'value' attributes");
    968             }
    969             msg->setString("range", min);
    970         }
    971 
    972         AString range, scale = "linear", def, in_;
    973         if (!msg->findString("range", &range)) {
    974             return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
    975         }
    976 
    977         if ((name == "quality" || name == "complexity") ^
    978                 (found = msg->findString("default", &def))) {
    979             return limitFoundMissingAttr(name, "default", found);
    980         }
    981         if (name != "quality" && msg->findString("scale", &scale)) {
    982             return limitFoundMissingAttr(name, "scale");
    983         }
    984         if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
    985             return limitFoundMissingAttr(name, "in", found);
    986         }
    987 
    988         if (name == "aspect-ratio") {
    989             if (!(in_ == "pixels") && !(in_ == "blocks")) {
    990                 return limitInvalidAttr(name, "in", in_);
    991             }
    992             in_.erase(5, 1); // (pixel|block)-aspect-ratio
    993             in_.append("-");
    994             in_.append(name);
    995             name = in_;
    996         }
    997         if (name == "quality") {
    998             mCurrentInfo->addDetail("quality-scale", scale);
    999         }
   1000         if (name == "quality" || name == "complexity") {
   1001             AString tag = name;
   1002             tag.append("-default");
   1003             mCurrentInfo->addDetail(tag, def);
   1004         }
   1005         AString tag = name;
   1006         tag.append("-range");
   1007         mCurrentInfo->addDetail(tag, range);
   1008     } else {
   1009         AString max, value, ranges;
   1010         if (msg->contains("default")) {
   1011             return limitFoundMissingAttr(name, "default");
   1012         } else if (msg->contains("in")) {
   1013             return limitFoundMissingAttr(name, "in");
   1014         } else if ((name == "channel-count" || name == "concurrent-instances") ^
   1015                 (found = msg->findString("max", &max))) {
   1016             return limitFoundMissingAttr(name, "max", found);
   1017         } else if (msg->contains("min")) {
   1018             return limitFoundMissingAttr(name, "min");
   1019         } else if (msg->contains("range")) {
   1020             return limitFoundMissingAttr(name, "range");
   1021         } else if ((name == "sample-rate") ^
   1022                 (found = msg->findString("ranges", &ranges))) {
   1023             return limitFoundMissingAttr(name, "ranges", found);
   1024         } else if (msg->contains("scale")) {
   1025             return limitFoundMissingAttr(name, "scale");
   1026         } else if ((name == "alignment" || name == "block-size") ^
   1027                 (found = msg->findString("value", &value))) {
   1028             return limitFoundMissingAttr(name, "value", found);
   1029         }
   1030 
   1031         if (max.size()) {
   1032             AString tag = "max-";
   1033             tag.append(name);
   1034             mCurrentInfo->addDetail(tag, max);
   1035         } else if (value.size()) {
   1036             mCurrentInfo->addDetail(name, value);
   1037         } else if (ranges.size()) {
   1038             AString tag = name;
   1039             tag.append("-ranges");
   1040             mCurrentInfo->addDetail(tag, ranges);
   1041         } else {
   1042             ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
   1043         }
   1044     }
   1045     return OK;
   1046 }
   1047 
   1048 status_t MediaCodecList::addFeature(const char **attrs) {
   1049     size_t i = 0;
   1050     const char *name = NULL;
   1051     int32_t optional = -1;
   1052     int32_t required = -1;
   1053     const char *value = NULL;
   1054 
   1055     while (attrs[i] != NULL) {
   1056         if (attrs[i + 1] == NULL) {
   1057             return -EINVAL;
   1058         }
   1059 
   1060         // attributes with values
   1061         if (!strcmp(attrs[i], "name")) {
   1062             name = attrs[i + 1];
   1063             ++i;
   1064         } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
   1065             int value = (int)parseBoolean(attrs[i + 1]);
   1066             if (!strcmp(attrs[i], "optional")) {
   1067                 optional = value;
   1068             } else {
   1069                 required = value;
   1070             }
   1071             ++i;
   1072         } else if (!strcmp(attrs[i], "value")) {
   1073             value = attrs[i + 1];
   1074             ++i;
   1075         } else {
   1076             return -EINVAL;
   1077         }
   1078         ++i;
   1079     }
   1080     if (name == NULL) {
   1081         ALOGE("feature with no 'name' attribute");
   1082         return -EINVAL;
   1083     }
   1084 
   1085     if (optional == required && optional != -1) {
   1086         ALOGE("feature '%s' is both/neither optional and required", name);
   1087         return -EINVAL;
   1088     }
   1089 
   1090     if ((optional != -1 || required != -1) && (value != NULL)) {
   1091         ALOGE("feature '%s' has both a value and optional/required attribute", name);
   1092         return -EINVAL;
   1093     }
   1094 
   1095     if (value != NULL) {
   1096         mCurrentInfo->addFeature(name, value);
   1097     } else {
   1098         mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
   1099     }
   1100     return OK;
   1101 }
   1102 
   1103 ssize_t MediaCodecList::findCodecByName(const char *name) const {
   1104     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
   1105         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
   1106 
   1107         if (info.mName == name) {
   1108             return i;
   1109         }
   1110     }
   1111 
   1112     return -ENOENT;
   1113 }
   1114 
   1115 size_t MediaCodecList::countCodecs() const {
   1116     return mCodecInfos.size();
   1117 }
   1118 
   1119 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
   1120     return mGlobalSettings;
   1121 }
   1122 
   1123 //static
   1124 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
   1125     return componentName.startsWithIgnoreCase("OMX.google.")
   1126         || !componentName.startsWithIgnoreCase("OMX.");
   1127 }
   1128 
   1129 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
   1130     // sort order 1: software codecs are first (lower)
   1131     bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
   1132     bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
   1133     if (isSoftwareCodec1 != isSoftwareCodec2) {
   1134         return isSoftwareCodec2 - isSoftwareCodec1;
   1135     }
   1136 
   1137     // sort order 2: OMX codecs are first (lower)
   1138     bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
   1139     bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
   1140     return isOMX2 - isOMX1;
   1141 }
   1142 
   1143 //static
   1144 void MediaCodecList::findMatchingCodecs(
   1145         const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
   1146     matches->clear();
   1147 
   1148     const sp<IMediaCodecList> list = getInstance();
   1149     if (list == NULL) {
   1150         return;
   1151     }
   1152 
   1153     size_t index = 0;
   1154     for (;;) {
   1155         ssize_t matchIndex =
   1156             list->findCodecByType(mime, encoder, index);
   1157 
   1158         if (matchIndex < 0) {
   1159             break;
   1160         }
   1161 
   1162         index = matchIndex + 1;
   1163 
   1164         const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
   1165         CHECK(info != NULL);
   1166         AString componentName = info->getCodecName();
   1167 
   1168         if (!((flags & kHardwareCodecsOnly) && !isSoftwareCodec(componentName))) {
   1169             matches->push(componentName);
   1170             ALOGV("matching '%s'", componentName.c_str());
   1171         }
   1172     }
   1173 
   1174     if (flags & kPreferSoftwareCodecs) {
   1175         matches->sort(compareSoftwareCodecsFirst);
   1176     }
   1177 }
   1178 
   1179 // static
   1180 uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
   1181     const sp<IMediaCodecList> list = getInstance();
   1182     if (list == NULL) {
   1183         return 0;
   1184     }
   1185 
   1186     ssize_t ix = list->findCodecByName(componentName);
   1187     if (ix < 0) {
   1188         return 0;
   1189     }
   1190 
   1191     const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
   1192 
   1193     uint32_t quirks = 0;
   1194     if (info->hasQuirk("requires-allocate-on-input-ports")) {
   1195         quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
   1196     }
   1197     if (info->hasQuirk("requires-allocate-on-output-ports")) {
   1198         quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
   1199     }
   1200 
   1201     return quirks;
   1202 }
   1203 
   1204 }  // namespace android
   1205