Home | History | Annotate | Download | only in 1.0
      1 /*
      2  * Copyright 2017, 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 "MediaCodecsXmlParser"
     19 #include <utils/Log.h>
     20 
     21 #include <media/vndk/xmlparser/1.0/MediaCodecsXmlParser.h>
     22 
     23 #include <media/MediaCodecInfo.h>
     24 
     25 #include <media/stagefright/foundation/ADebug.h>
     26 #include <media/stagefright/foundation/AMessage.h>
     27 #include <media/stagefright/foundation/AUtils.h>
     28 #include <media/stagefright/MediaErrors.h>
     29 
     30 #include <sys/stat.h>
     31 
     32 #include <expat.h>
     33 #include <string>
     34 
     35 #define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 256
     36 
     37 namespace android {
     38 
     39 namespace { // Local variables and functions
     40 
     41 const char *kProfilingResults =
     42         "/data/misc/media/media_codecs_profiling_results.xml";
     43 
     44 // Treblized media codec list will be located in /odm/etc or /vendor/etc.
     45 const char *kConfigLocationList[] =
     46         {"/odm/etc", "/vendor/etc", "/etc"};
     47 constexpr int kConfigLocationListSize =
     48         (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
     49 
     50 bool findMediaCodecListFileFullPath(
     51         const char *file_name, std::string *out_path) {
     52     for (int i = 0; i < kConfigLocationListSize; i++) {
     53         *out_path = std::string(kConfigLocationList[i]) + "/" + file_name;
     54         struct stat file_stat;
     55         if (stat(out_path->c_str(), &file_stat) == 0 &&
     56                 S_ISREG(file_stat.st_mode)) {
     57             return true;
     58         }
     59     }
     60     return false;
     61 }
     62 
     63 // Find TypeInfo by name.
     64 std::vector<TypeInfo>::iterator findTypeInfo(
     65         CodecInfo &codecInfo, const AString &typeName) {
     66     return std::find_if(
     67             codecInfo.mTypes.begin(), codecInfo.mTypes.end(),
     68             [typeName](const auto &typeInfo) {
     69                 return typeInfo.mName == typeName;
     70             });
     71 }
     72 
     73 // Convert a string into a boolean value.
     74 bool ParseBoolean(const char *s) {
     75     if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
     76         return true;
     77     }
     78     char *end;
     79     unsigned long res = strtoul(s, &end, 10);
     80     return *s != '\0' && *end == '\0' && res > 0;
     81 }
     82 
     83 } // unnamed namespace
     84 
     85 MediaCodecsXmlParser::MediaCodecsXmlParser() :
     86     mInitCheck(NO_INIT),
     87     mUpdate(false) {
     88     std::string config_file_path;
     89     if (findMediaCodecListFileFullPath(
     90             "media_codecs.xml", &config_file_path)) {
     91         parseTopLevelXMLFile(config_file_path.c_str(), false);
     92     } else {
     93         mInitCheck = NAME_NOT_FOUND;
     94     }
     95     if (findMediaCodecListFileFullPath(
     96             "media_codecs_performance.xml", &config_file_path)) {
     97         parseTopLevelXMLFile(config_file_path.c_str(), true);
     98     }
     99     parseTopLevelXMLFile(kProfilingResults, true);
    100 }
    101 
    102 void MediaCodecsXmlParser::parseTopLevelXMLFile(
    103         const char *codecs_xml, bool ignore_errors) {
    104     // get href_base
    105     const char *href_base_end = strrchr(codecs_xml, '/');
    106     if (href_base_end != NULL) {
    107         mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
    108     }
    109 
    110     mInitCheck = OK; // keeping this here for safety
    111     mCurrentSection = SECTION_TOPLEVEL;
    112     mDepth = 0;
    113 
    114     parseXMLFile(codecs_xml);
    115 
    116     if (mInitCheck != OK) {
    117         if (ignore_errors) {
    118             mInitCheck = OK;
    119             return;
    120         }
    121         mCodecInfos.clear();
    122         return;
    123     }
    124 }
    125 
    126 MediaCodecsXmlParser::~MediaCodecsXmlParser() {
    127 }
    128 
    129 status_t MediaCodecsXmlParser::initCheck() const {
    130     return mInitCheck;
    131 }
    132 
    133 void MediaCodecsXmlParser::parseXMLFile(const char *path) {
    134     FILE *file = fopen(path, "r");
    135 
    136     if (file == NULL) {
    137         ALOGW("unable to open media codecs configuration xml file: %s", path);
    138         mInitCheck = NAME_NOT_FOUND;
    139         return;
    140     }
    141 
    142     ALOGV("Start parsing %s", path);
    143     XML_Parser parser = ::XML_ParserCreate(NULL);
    144     CHECK(parser != NULL);
    145 
    146     ::XML_SetUserData(parser, this);
    147     ::XML_SetElementHandler(
    148             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
    149 
    150     const int BUFF_SIZE = 512;
    151     while (mInitCheck == OK) {
    152         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
    153         if (buff == NULL) {
    154             ALOGE("failed in call to XML_GetBuffer()");
    155             mInitCheck = UNKNOWN_ERROR;
    156             break;
    157         }
    158 
    159         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
    160         if (bytes_read < 0) {
    161             ALOGE("failed in call to read");
    162             mInitCheck = ERROR_IO;
    163             break;
    164         }
    165 
    166         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
    167         if (status != XML_STATUS_OK) {
    168             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
    169             mInitCheck = ERROR_MALFORMED;
    170             break;
    171         }
    172 
    173         if (bytes_read == 0) {
    174             break;
    175         }
    176     }
    177 
    178     ::XML_ParserFree(parser);
    179 
    180     fclose(file);
    181     file = NULL;
    182 }
    183 
    184 // static
    185 void MediaCodecsXmlParser::StartElementHandlerWrapper(
    186         void *me, const char *name, const char **attrs) {
    187     static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs);
    188 }
    189 
    190 // static
    191 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) {
    192     static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name);
    193 }
    194 
    195 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) {
    196     const char *href = NULL;
    197     size_t i = 0;
    198     while (attrs[i] != NULL) {
    199         if (!strcmp(attrs[i], "href")) {
    200             if (attrs[i + 1] == NULL) {
    201                 return -EINVAL;
    202             }
    203             href = attrs[i + 1];
    204             ++i;
    205         } else {
    206             ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]);
    207             return -EINVAL;
    208         }
    209         ++i;
    210     }
    211 
    212     // For security reasons and for simplicity, file names can only contain
    213     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
    214     for (i = 0; href[i] != '\0'; i++) {
    215         if (href[i] == '.' || href[i] == '_' ||
    216                 (href[i] >= '0' && href[i] <= '9') ||
    217                 (href[i] >= 'A' && href[i] <= 'Z') ||
    218                 (href[i] >= 'a' && href[i] <= 'z')) {
    219             continue;
    220         }
    221         ALOGE("invalid include file name: %s", href);
    222         return -EINVAL;
    223     }
    224 
    225     AString filename = href;
    226     if (!filename.startsWith("media_codecs_") ||
    227         !filename.endsWith(".xml")) {
    228         ALOGE("invalid include file name: %s", href);
    229         return -EINVAL;
    230     }
    231     filename.insert(mHrefBase, 0);
    232 
    233     parseXMLFile(filename.c_str());
    234     return mInitCheck;
    235 }
    236 
    237 void MediaCodecsXmlParser::startElementHandler(
    238         const char *name, const char **attrs) {
    239     if (mInitCheck != OK) {
    240         return;
    241     }
    242 
    243     bool inType = true;
    244 
    245     if (!strcmp(name, "Include")) {
    246         mInitCheck = includeXMLFile(attrs);
    247         if (mInitCheck == OK) {
    248             mPastSections.push(mCurrentSection);
    249             mCurrentSection = SECTION_INCLUDE;
    250         }
    251         ++mDepth;
    252         return;
    253     }
    254 
    255     switch (mCurrentSection) {
    256         case SECTION_TOPLEVEL:
    257         {
    258             if (!strcmp(name, "Decoders")) {
    259                 mCurrentSection = SECTION_DECODERS;
    260             } else if (!strcmp(name, "Encoders")) {
    261                 mCurrentSection = SECTION_ENCODERS;
    262             } else if (!strcmp(name, "Settings")) {
    263                 mCurrentSection = SECTION_SETTINGS;
    264             }
    265             break;
    266         }
    267 
    268         case SECTION_SETTINGS:
    269         {
    270             if (!strcmp(name, "Setting")) {
    271                 mInitCheck = addSettingFromAttributes(attrs);
    272             }
    273             break;
    274         }
    275 
    276         case SECTION_DECODERS:
    277         {
    278             if (!strcmp(name, "MediaCodec")) {
    279                 mInitCheck =
    280                     addMediaCodecFromAttributes(false /* encoder */, attrs);
    281 
    282                 mCurrentSection = SECTION_DECODER;
    283             }
    284             break;
    285         }
    286 
    287         case SECTION_ENCODERS:
    288         {
    289             if (!strcmp(name, "MediaCodec")) {
    290                 mInitCheck =
    291                     addMediaCodecFromAttributes(true /* encoder */, attrs);
    292 
    293                 mCurrentSection = SECTION_ENCODER;
    294             }
    295             break;
    296         }
    297 
    298         case SECTION_DECODER:
    299         case SECTION_ENCODER:
    300         {
    301             if (!strcmp(name, "Quirk")) {
    302                 mInitCheck = addQuirk(attrs);
    303             } else if (!strcmp(name, "Type")) {
    304                 mInitCheck = addTypeFromAttributes(attrs, (mCurrentSection == SECTION_ENCODER));
    305                 mCurrentSection =
    306                     (mCurrentSection == SECTION_DECODER
    307                             ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
    308             }
    309         }
    310         inType = false;
    311         // fall through
    312 
    313         case SECTION_DECODER_TYPE:
    314         case SECTION_ENCODER_TYPE:
    315         {
    316             // ignore limits and features specified outside of type
    317             bool outside = !inType && mCurrentType == mCodecInfos[mCurrentName].mTypes.end();
    318             if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
    319                 ALOGW("ignoring %s specified outside of a Type", name);
    320             } else if (!strcmp(name, "Limit")) {
    321                 mInitCheck = addLimit(attrs);
    322             } else if (!strcmp(name, "Feature")) {
    323                 mInitCheck = addFeature(attrs);
    324             }
    325             break;
    326         }
    327 
    328         default:
    329             break;
    330     }
    331 
    332     ++mDepth;
    333 }
    334 
    335 void MediaCodecsXmlParser::endElementHandler(const char *name) {
    336     if (mInitCheck != OK) {
    337         return;
    338     }
    339 
    340     switch (mCurrentSection) {
    341         case SECTION_SETTINGS:
    342         {
    343             if (!strcmp(name, "Settings")) {
    344                 mCurrentSection = SECTION_TOPLEVEL;
    345             }
    346             break;
    347         }
    348 
    349         case SECTION_DECODERS:
    350         {
    351             if (!strcmp(name, "Decoders")) {
    352                 mCurrentSection = SECTION_TOPLEVEL;
    353             }
    354             break;
    355         }
    356 
    357         case SECTION_ENCODERS:
    358         {
    359             if (!strcmp(name, "Encoders")) {
    360                 mCurrentSection = SECTION_TOPLEVEL;
    361             }
    362             break;
    363         }
    364 
    365         case SECTION_DECODER_TYPE:
    366         case SECTION_ENCODER_TYPE:
    367         {
    368             if (!strcmp(name, "Type")) {
    369                 mCurrentSection =
    370                     (mCurrentSection == SECTION_DECODER_TYPE
    371                             ? SECTION_DECODER : SECTION_ENCODER);
    372 
    373                 mCurrentType = mCodecInfos[mCurrentName].mTypes.end();
    374             }
    375             break;
    376         }
    377 
    378         case SECTION_DECODER:
    379         {
    380             if (!strcmp(name, "MediaCodec")) {
    381                 mCurrentSection = SECTION_DECODERS;
    382                 mCurrentName.clear();
    383             }
    384             break;
    385         }
    386 
    387         case SECTION_ENCODER:
    388         {
    389             if (!strcmp(name, "MediaCodec")) {
    390                 mCurrentSection = SECTION_ENCODERS;
    391                 mCurrentName.clear();
    392             }
    393             break;
    394         }
    395 
    396         case SECTION_INCLUDE:
    397         {
    398             if (!strcmp(name, "Include") && mPastSections.size() > 0) {
    399                 mCurrentSection = mPastSections.top();
    400                 mPastSections.pop();
    401             }
    402             break;
    403         }
    404 
    405         default:
    406             break;
    407     }
    408 
    409     --mDepth;
    410 }
    411 
    412 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) {
    413     const char *name = NULL;
    414     const char *value = NULL;
    415     const char *update = NULL;
    416 
    417     size_t i = 0;
    418     while (attrs[i] != NULL) {
    419         if (!strcmp(attrs[i], "name")) {
    420             if (attrs[i + 1] == NULL) {
    421                 ALOGE("addSettingFromAttributes: name is null");
    422                 return -EINVAL;
    423             }
    424             name = attrs[i + 1];
    425             ++i;
    426         } else if (!strcmp(attrs[i], "value")) {
    427             if (attrs[i + 1] == NULL) {
    428                 ALOGE("addSettingFromAttributes: value is null");
    429                 return -EINVAL;
    430             }
    431             value = attrs[i + 1];
    432             ++i;
    433         } else if (!strcmp(attrs[i], "update")) {
    434             if (attrs[i + 1] == NULL) {
    435                 ALOGE("addSettingFromAttributes: update is null");
    436                 return -EINVAL;
    437             }
    438             update = attrs[i + 1];
    439             ++i;
    440         } else {
    441             ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]);
    442             return -EINVAL;
    443         }
    444 
    445         ++i;
    446     }
    447 
    448     if (name == NULL || value == NULL) {
    449         ALOGE("addSettingFromAttributes: name or value unspecified");
    450         return -EINVAL;
    451     }
    452 
    453     mUpdate = (update != NULL) && ParseBoolean(update);
    454     if (mUpdate != (mGlobalSettings.count(name) > 0)) {
    455         ALOGE("addSettingFromAttributes: updating non-existing setting");
    456         return -EINVAL;
    457     }
    458     mGlobalSettings[name] = value;
    459 
    460     return OK;
    461 }
    462 
    463 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes(
    464         bool encoder, const char **attrs) {
    465     const char *name = NULL;
    466     const char *type = NULL;
    467     const char *update = NULL;
    468 
    469     size_t i = 0;
    470     while (attrs[i] != NULL) {
    471         if (!strcmp(attrs[i], "name")) {
    472             if (attrs[i + 1] == NULL) {
    473                 ALOGE("addMediaCodecFromAttributes: name is null");
    474                 return -EINVAL;
    475             }
    476             name = attrs[i + 1];
    477             ++i;
    478         } else if (!strcmp(attrs[i], "type")) {
    479             if (attrs[i + 1] == NULL) {
    480                 ALOGE("addMediaCodecFromAttributes: type is null");
    481                 return -EINVAL;
    482             }
    483             type = attrs[i + 1];
    484             ++i;
    485         } else if (!strcmp(attrs[i], "update")) {
    486             if (attrs[i + 1] == NULL) {
    487                 ALOGE("addMediaCodecFromAttributes: update is null");
    488                 return -EINVAL;
    489             }
    490             update = attrs[i + 1];
    491             ++i;
    492         } else {
    493             ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]);
    494             return -EINVAL;
    495         }
    496 
    497         ++i;
    498     }
    499 
    500     if (name == NULL) {
    501         ALOGE("addMediaCodecFromAttributes: name not found");
    502         return -EINVAL;
    503     }
    504 
    505     mUpdate = (update != NULL) && ParseBoolean(update);
    506     if (mUpdate != (mCodecInfos.count(name) > 0)) {
    507         ALOGE("addMediaCodecFromAttributes: updating non-existing codec or vice versa");
    508         return -EINVAL;
    509     }
    510 
    511     CodecInfo *info = &mCodecInfos[name];
    512     if (mUpdate) {
    513         // existing codec
    514         mCurrentName = name;
    515         mCurrentType = info->mTypes.begin();
    516         if (type != NULL) {
    517             // existing type
    518             mCurrentType = findTypeInfo(*info, type);
    519             if (mCurrentType == info->mTypes.end()) {
    520                 ALOGE("addMediaCodecFromAttributes: updating non-existing type");
    521                 return -EINVAL;
    522             }
    523         }
    524     } else {
    525         // new codec
    526         mCurrentName = name;
    527         mQuirks[name].clear();
    528         info->mTypes.clear();
    529         info->mTypes.emplace_back();
    530         mCurrentType = --info->mTypes.end();
    531         mCurrentType->mName = type;
    532         info->mIsEncoder = encoder;
    533     }
    534 
    535     return OK;
    536 }
    537 
    538 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) {
    539     const char *name = NULL;
    540 
    541     size_t i = 0;
    542     while (attrs[i] != NULL) {
    543         if (!strcmp(attrs[i], "name")) {
    544             if (attrs[i + 1] == NULL) {
    545                 ALOGE("addQuirk: name is null");
    546                 return -EINVAL;
    547             }
    548             name = attrs[i + 1];
    549             ++i;
    550         } else {
    551             ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]);
    552             return -EINVAL;
    553         }
    554 
    555         ++i;
    556     }
    557 
    558     if (name == NULL) {
    559         ALOGE("addQuirk: name not found");
    560         return -EINVAL;
    561     }
    562 
    563     mQuirks[mCurrentName].emplace_back(name);
    564     return OK;
    565 }
    566 
    567 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) {
    568     const char *name = NULL;
    569     const char *update = NULL;
    570 
    571     size_t i = 0;
    572     while (attrs[i] != NULL) {
    573         if (!strcmp(attrs[i], "name")) {
    574             if (attrs[i + 1] == NULL) {
    575                 ALOGE("addTypeFromAttributes: name is null");
    576                 return -EINVAL;
    577             }
    578             name = attrs[i + 1];
    579             ++i;
    580         } else if (!strcmp(attrs[i], "update")) {
    581             if (attrs[i + 1] == NULL) {
    582                 ALOGE("addTypeFromAttributes: update is null");
    583                 return -EINVAL;
    584             }
    585             update = attrs[i + 1];
    586             ++i;
    587         } else {
    588             ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]);
    589             return -EINVAL;
    590         }
    591 
    592         ++i;
    593     }
    594 
    595     if (name == NULL) {
    596         return -EINVAL;
    597     }
    598 
    599     CodecInfo *info = &mCodecInfos[mCurrentName];
    600     info->mIsEncoder = encoder;
    601     mCurrentType = findTypeInfo(*info, name);
    602     if (!mUpdate) {
    603         if (mCurrentType != info->mTypes.end()) {
    604             ALOGE("addTypeFromAttributes: re-defining existing type without update");
    605             return -EINVAL;
    606         }
    607         info->mTypes.emplace_back();
    608         mCurrentType = --info->mTypes.end();
    609     } else if (mCurrentType == info->mTypes.end()) {
    610         ALOGE("addTypeFromAttributes: updating non-existing type");
    611         return -EINVAL;
    612     }
    613 
    614     return OK;
    615 }
    616 
    617 static status_t limitFoundMissingAttr(const AString &name, const char *attr, bool found = true) {
    618     ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
    619             (found ? "" : "no "), attr);
    620     return -EINVAL;
    621 }
    622 
    623 static status_t limitError(const AString &name, const char *msg) {
    624     ALOGE("limit '%s' %s", name.c_str(), msg);
    625     return -EINVAL;
    626 }
    627 
    628 static status_t limitInvalidAttr(const AString &name, const char *attr, const AString &value) {
    629     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
    630             attr, value.c_str());
    631     return -EINVAL;
    632 }
    633 
    634 status_t MediaCodecsXmlParser::addLimit(const char **attrs) {
    635     sp<AMessage> msg = new AMessage();
    636 
    637     size_t i = 0;
    638     while (attrs[i] != NULL) {
    639         if (attrs[i + 1] == NULL) {
    640             ALOGE("addLimit: limit is not given");
    641             return -EINVAL;
    642         }
    643 
    644         // attributes with values
    645         if (!strcmp(attrs[i], "name")
    646                 || !strcmp(attrs[i], "default")
    647                 || !strcmp(attrs[i], "in")
    648                 || !strcmp(attrs[i], "max")
    649                 || !strcmp(attrs[i], "min")
    650                 || !strcmp(attrs[i], "range")
    651                 || !strcmp(attrs[i], "ranges")
    652                 || !strcmp(attrs[i], "scale")
    653                 || !strcmp(attrs[i], "value")) {
    654             msg->setString(attrs[i], attrs[i + 1]);
    655             ++i;
    656         } else {
    657             ALOGE("addLimit: unrecognized limit: %s", attrs[i]);
    658             return -EINVAL;
    659         }
    660         ++i;
    661     }
    662 
    663     AString name;
    664     if (!msg->findString("name", &name)) {
    665         ALOGE("limit with no 'name' attribute");
    666         return -EINVAL;
    667     }
    668 
    669     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
    670     // measured-frame-rate, measured-blocks-per-second: range
    671     // quality: range + default + [scale]
    672     // complexity: range + default
    673     bool found;
    674     if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
    675         ALOGW("ignoring null type");
    676         return OK;
    677     }
    678 
    679     if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
    680             || name == "blocks-per-second" || name == "complexity"
    681             || name == "frame-rate" || name == "quality" || name == "size"
    682             || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
    683         AString min, max;
    684         if (msg->findString("min", &min) && msg->findString("max", &max)) {
    685             min.append("-");
    686             min.append(max);
    687             if (msg->contains("range") || msg->contains("value")) {
    688                 return limitError(name, "has 'min' and 'max' as well as 'range' or "
    689                         "'value' attributes");
    690             }
    691             msg->setString("range", min);
    692         } else if (msg->contains("min") || msg->contains("max")) {
    693             return limitError(name, "has only 'min' or 'max' attribute");
    694         } else if (msg->findString("value", &max)) {
    695             min = max;
    696             min.append("-");
    697             min.append(max);
    698             if (msg->contains("range")) {
    699                 return limitError(name, "has both 'range' and 'value' attributes");
    700             }
    701             msg->setString("range", min);
    702         }
    703 
    704         AString range, scale = "linear", def, in_;
    705         if (!msg->findString("range", &range)) {
    706             return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
    707         }
    708 
    709         if ((name == "quality" || name == "complexity") ^
    710                 (found = msg->findString("default", &def))) {
    711             return limitFoundMissingAttr(name, "default", found);
    712         }
    713         if (name != "quality" && msg->findString("scale", &scale)) {
    714             return limitFoundMissingAttr(name, "scale");
    715         }
    716         if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
    717             return limitFoundMissingAttr(name, "in", found);
    718         }
    719 
    720         if (name == "aspect-ratio") {
    721             if (!(in_ == "pixels") && !(in_ == "blocks")) {
    722                 return limitInvalidAttr(name, "in", in_);
    723             }
    724             in_.erase(5, 1); // (pixel|block)-aspect-ratio
    725             in_.append("-");
    726             in_.append(name);
    727             name = in_;
    728         }
    729         if (name == "quality") {
    730             mCurrentType->mDetails["quality-scale"] = scale;
    731         }
    732         if (name == "quality" || name == "complexity") {
    733             AString tag = name;
    734             tag.append("-default");
    735             mCurrentType->mDetails[tag] = def;
    736         }
    737         AString tag = name;
    738         tag.append("-range");
    739         mCurrentType->mDetails[tag] = range;
    740     } else {
    741         AString max, value, ranges;
    742         if (msg->contains("default")) {
    743             return limitFoundMissingAttr(name, "default");
    744         } else if (msg->contains("in")) {
    745             return limitFoundMissingAttr(name, "in");
    746         } else if ((name == "channel-count" || name == "concurrent-instances") ^
    747                 (found = msg->findString("max", &max))) {
    748             return limitFoundMissingAttr(name, "max", found);
    749         } else if (msg->contains("min")) {
    750             return limitFoundMissingAttr(name, "min");
    751         } else if (msg->contains("range")) {
    752             return limitFoundMissingAttr(name, "range");
    753         } else if ((name == "sample-rate") ^
    754                 (found = msg->findString("ranges", &ranges))) {
    755             return limitFoundMissingAttr(name, "ranges", found);
    756         } else if (msg->contains("scale")) {
    757             return limitFoundMissingAttr(name, "scale");
    758         } else if ((name == "alignment" || name == "block-size") ^
    759                 (found = msg->findString("value", &value))) {
    760             return limitFoundMissingAttr(name, "value", found);
    761         }
    762 
    763         if (max.size()) {
    764             AString tag = "max-";
    765             tag.append(name);
    766             mCurrentType->mDetails[tag] = max;
    767         } else if (value.size()) {
    768             mCurrentType->mDetails[name] = value;
    769         } else if (ranges.size()) {
    770             AString tag = name;
    771             tag.append("-ranges");
    772             mCurrentType->mDetails[tag] = ranges;
    773         } else {
    774             ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
    775         }
    776     }
    777 
    778     return OK;
    779 }
    780 
    781 status_t MediaCodecsXmlParser::addFeature(const char **attrs) {
    782     size_t i = 0;
    783     const char *name = NULL;
    784     int32_t optional = -1;
    785     int32_t required = -1;
    786     const char *value = NULL;
    787 
    788     while (attrs[i] != NULL) {
    789         if (attrs[i + 1] == NULL) {
    790             ALOGE("addFeature: feature is not given");
    791             return -EINVAL;
    792         }
    793 
    794         // attributes with values
    795         if (!strcmp(attrs[i], "name")) {
    796             name = attrs[i + 1];
    797             ++i;
    798         } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
    799             int value = (int)ParseBoolean(attrs[i + 1]);
    800             if (!strcmp(attrs[i], "optional")) {
    801                 optional = value;
    802             } else {
    803                 required = value;
    804             }
    805             ++i;
    806         } else if (!strcmp(attrs[i], "value")) {
    807             value = attrs[i + 1];
    808             ++i;
    809         } else {
    810             ALOGE("addFeature: unrecognized attribute: %s", attrs[i]);
    811             return -EINVAL;
    812         }
    813         ++i;
    814     }
    815     if (name == NULL) {
    816         ALOGE("feature with no 'name' attribute");
    817         return -EINVAL;
    818     }
    819 
    820     if (optional == required && optional != -1) {
    821         ALOGE("feature '%s' is both/neither optional and required", name);
    822         return -EINVAL;
    823     }
    824 
    825     if (mCurrentType == mCodecInfos[mCurrentName].mTypes.end()) {
    826         ALOGW("ignoring null type");
    827         return OK;
    828     }
    829     if (value != NULL) {
    830         mCurrentType->mStringFeatures[name] = value;
    831     } else {
    832         mCurrentType->mBoolFeatures[name] = (required == 1) || (optional == 0);
    833     }
    834     return OK;
    835 }
    836 
    837 void MediaCodecsXmlParser::getGlobalSettings(
    838         std::map<AString, AString> *settings) const {
    839     settings->clear();
    840     settings->insert(mGlobalSettings.begin(), mGlobalSettings.end());
    841 }
    842 
    843 status_t MediaCodecsXmlParser::getCodecInfo(const char *name, CodecInfo *info) const {
    844     if (mCodecInfos.count(name) == 0) {
    845         ALOGE("Codec not found with name '%s'", name);
    846         return NAME_NOT_FOUND;
    847     }
    848     *info = mCodecInfos.at(name);
    849     return OK;
    850 }
    851 
    852 status_t MediaCodecsXmlParser::getQuirks(const char *name, std::vector<AString> *quirks) const {
    853     if (mQuirks.count(name) == 0) {
    854         ALOGE("Codec not found with name '%s'", name);
    855         return NAME_NOT_FOUND;
    856     }
    857     quirks->clear();
    858     quirks->insert(quirks->end(), mQuirks.at(name).begin(), mQuirks.at(name).end());
    859     return OK;
    860 }
    861 
    862 }  // namespace android
    863