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