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 37namespace android { 38 39namespace { // Local variables and functions 40 41const 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. 45const char *kConfigLocationList[] = 46 {"/odm/etc", "/vendor/etc", "/etc"}; 47constexpr int kConfigLocationListSize = 48 (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0])); 49 50bool 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. 64std::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. 74bool 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 85MediaCodecsXmlParser::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 102void 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 126MediaCodecsXmlParser::~MediaCodecsXmlParser() { 127} 128 129status_t MediaCodecsXmlParser::initCheck() const { 130 return mInitCheck; 131} 132 133void 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 185void MediaCodecsXmlParser::StartElementHandlerWrapper( 186 void *me, const char *name, const char **attrs) { 187 static_cast<MediaCodecsXmlParser *>(me)->startElementHandler(name, attrs); 188} 189 190// static 191void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) { 192 static_cast<MediaCodecsXmlParser *>(me)->endElementHandler(name); 193} 194 195status_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 237void 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 335void 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 412status_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 463status_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 538status_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 567status_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 617static 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 623static status_t limitError(const AString &name, const char *msg) { 624 ALOGE("limit '%s' %s", name.c_str(), msg); 625 return -EINVAL; 626} 627 628static 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 634status_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 781status_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 837void MediaCodecsXmlParser::getGlobalSettings( 838 std::map<AString, AString> *settings) const { 839 settings->clear(); 840 settings->insert(mGlobalSettings.begin(), mGlobalSettings.end()); 841} 842 843status_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 852status_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