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 20 #include <media/stagefright/xmlparser/MediaCodecsXmlParser.h> 21 22 #include <utils/Log.h> 23 #include <media/stagefright/MediaErrors.h> 24 #include <media/stagefright/omx/OMXUtils.h> 25 #include <sys/stat.h> 26 #include <expat.h> 27 28 #include <cctype> 29 #include <algorithm> 30 31 namespace android { 32 33 namespace { 34 35 /** 36 * Search for a file in a list of search directories. 37 * 38 * For each string `searchDir` in `searchDirs`, `searchDir/fileName` will be 39 * tested whether it is a valid file name or not. If it is a valid file name, 40 * the concatenated name (`searchDir/fileName`) will be stored in the output 41 * variable `outPath`, and the function will return `true`. Otherwise, the 42 * search continues until the `nullptr` element in `searchDirs` is reached, at 43 * which point the function returns `false`. 44 * 45 * \param[in] searchDirs Null-terminated array of search paths. 46 * \param[in] fileName Name of the file to search. 47 * \param[out] outPath Full path of the file. `outPath` will hold a valid file 48 * name if the return value of this function is `true`. 49 * \return `true` if some element in `searchDirs` combined with `fileName` is a 50 * valid file name; `false` otherwise. 51 */ 52 bool findFileInDirs( 53 const char* const* searchDirs, 54 const char *fileName, 55 std::string *outPath) { 56 for (; *searchDirs != nullptr; ++searchDirs) { 57 *outPath = std::string(*searchDirs) + "/" + fileName; 58 struct stat fileStat; 59 if (stat(outPath->c_str(), &fileStat) == 0 && 60 S_ISREG(fileStat.st_mode)) { 61 return true; 62 } 63 } 64 return false; 65 } 66 67 bool strnEq(const char* s1, const char* s2, size_t count) { 68 return strncmp(s1, s2, count) == 0; 69 } 70 71 bool strEq(const char* s1, const char* s2) { 72 return strcmp(s1, s2) == 0; 73 } 74 75 bool striEq(const char* s1, const char* s2) { 76 return strcasecmp(s1, s2) == 0; 77 } 78 79 bool strHasPrefix(const char* test, const char* prefix) { 80 return strnEq(test, prefix, strlen(prefix)); 81 } 82 83 bool parseBoolean(const char* s) { 84 return striEq(s, "y") || 85 striEq(s, "yes") || 86 striEq(s, "t") || 87 striEq(s, "true") || 88 striEq(s, "1"); 89 } 90 91 status_t limitFoundMissingAttr(const char* name, const char *attr, bool found = true) { 92 ALOGE("limit '%s' with %s'%s' attribute", name, 93 (found ? "" : "no "), attr); 94 return -EINVAL; 95 } 96 97 status_t limitError(const char* name, const char *msg) { 98 ALOGE("limit '%s' %s", name, msg); 99 return -EINVAL; 100 } 101 102 status_t limitInvalidAttr(const char* name, const char* attr, const char* value) { 103 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name, 104 attr, value); 105 return -EINVAL; 106 } 107 108 }; // unnamed namespace 109 110 constexpr char const* MediaCodecsXmlParser::defaultSearchDirs[]; 111 constexpr char const* MediaCodecsXmlParser::defaultMainXmlName; 112 constexpr char const* MediaCodecsXmlParser::defaultPerformanceXmlName; 113 constexpr char const* MediaCodecsXmlParser::defaultProfilingResultsXmlPath; 114 115 MediaCodecsXmlParser::MediaCodecsXmlParser( 116 const char* const* searchDirs, 117 const char* mainXmlName, 118 const char* performanceXmlName, 119 const char* profilingResultsXmlPath) : 120 mParsingStatus(NO_INIT), 121 mUpdate(false), 122 mCodecCounter(0) { 123 std::string path; 124 if (findFileInDirs(searchDirs, mainXmlName, &path)) { 125 parseTopLevelXMLFile(path.c_str(), false); 126 } else { 127 ALOGE("Cannot find %s", mainXmlName); 128 mParsingStatus = NAME_NOT_FOUND; 129 } 130 if (findFileInDirs(searchDirs, performanceXmlName, &path)) { 131 parseTopLevelXMLFile(path.c_str(), true); 132 } 133 if (profilingResultsXmlPath != nullptr) { 134 parseTopLevelXMLFile(profilingResultsXmlPath, true); 135 } 136 } 137 138 bool MediaCodecsXmlParser::parseTopLevelXMLFile( 139 const char *codecs_xml, 140 bool ignore_errors) { 141 // get href_base 142 const char *href_base_end = strrchr(codecs_xml, '/'); 143 if (href_base_end != nullptr) { 144 mHrefBase = std::string(codecs_xml, href_base_end - codecs_xml + 1); 145 } 146 147 mParsingStatus = OK; // keeping this here for safety 148 mCurrentSection = SECTION_TOPLEVEL; 149 150 parseXMLFile(codecs_xml); 151 152 if (mParsingStatus != OK) { 153 ALOGW("parseTopLevelXMLFile(%s) failed", codecs_xml); 154 if (ignore_errors) { 155 mParsingStatus = OK; 156 return false; 157 } 158 mCodecMap.clear(); 159 return false; 160 } 161 return true; 162 } 163 164 MediaCodecsXmlParser::~MediaCodecsXmlParser() { 165 } 166 167 void MediaCodecsXmlParser::parseXMLFile(const char *path) { 168 FILE *file = fopen(path, "r"); 169 170 if (file == nullptr) { 171 ALOGW("unable to open media codecs configuration xml file: %s", path); 172 mParsingStatus = NAME_NOT_FOUND; 173 return; 174 } 175 176 XML_Parser parser = ::XML_ParserCreate(nullptr); 177 LOG_FATAL_IF(parser == nullptr, "XML_MediaCodecsXmlParserCreate() failed."); 178 179 ::XML_SetUserData(parser, this); 180 ::XML_SetElementHandler( 181 parser, StartElementHandlerWrapper, EndElementHandlerWrapper); 182 183 static constexpr int BUFF_SIZE = 512; 184 while (mParsingStatus == OK) { 185 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE); 186 if (buff == nullptr) { 187 ALOGE("failed in call to XML_GetBuffer()"); 188 mParsingStatus = UNKNOWN_ERROR; 189 break; 190 } 191 192 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file); 193 if (bytes_read < 0) { 194 ALOGE("failed in call to read"); 195 mParsingStatus = ERROR_IO; 196 break; 197 } 198 199 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0); 200 if (status != XML_STATUS_OK) { 201 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser))); 202 mParsingStatus = ERROR_MALFORMED; 203 break; 204 } 205 206 if (bytes_read == 0) { 207 break; 208 } 209 } 210 211 ::XML_ParserFree(parser); 212 213 fclose(file); 214 file = nullptr; 215 } 216 217 // static 218 void MediaCodecsXmlParser::StartElementHandlerWrapper( 219 void *me, const char *name, const char **attrs) { 220 static_cast<MediaCodecsXmlParser*>(me)->startElementHandler(name, attrs); 221 } 222 223 // static 224 void MediaCodecsXmlParser::EndElementHandlerWrapper(void *me, const char *name) { 225 static_cast<MediaCodecsXmlParser*>(me)->endElementHandler(name); 226 } 227 228 status_t MediaCodecsXmlParser::includeXMLFile(const char **attrs) { 229 const char *href = nullptr; 230 size_t i = 0; 231 while (attrs[i] != nullptr) { 232 if (strEq(attrs[i], "href")) { 233 if (attrs[++i] == nullptr) { 234 return -EINVAL; 235 } 236 href = attrs[i]; 237 } else { 238 ALOGE("includeXMLFile: unrecognized attribute: %s", attrs[i]); 239 return -EINVAL; 240 } 241 ++i; 242 } 243 244 // For security reasons and for simplicity, file names can only contain 245 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml 246 for (i = 0; href[i] != '\0'; i++) { 247 if (href[i] == '.' || href[i] == '_' || 248 (href[i] >= '0' && href[i] <= '9') || 249 (href[i] >= 'A' && href[i] <= 'Z') || 250 (href[i] >= 'a' && href[i] <= 'z')) { 251 continue; 252 } 253 ALOGE("invalid include file name: %s", href); 254 return -EINVAL; 255 } 256 257 std::string filename = href; 258 if (filename.compare(0, 13, "media_codecs_") != 0 || 259 filename.compare(filename.size() - 4, 4, ".xml") != 0) { 260 ALOGE("invalid include file name: %s", href); 261 return -EINVAL; 262 } 263 filename.insert(0, mHrefBase); 264 265 parseXMLFile(filename.c_str()); 266 return mParsingStatus; 267 } 268 269 void MediaCodecsXmlParser::startElementHandler( 270 const char *name, const char **attrs) { 271 if (mParsingStatus != OK) { 272 return; 273 } 274 275 bool inType = true; 276 277 if (strEq(name, "Include")) { 278 mParsingStatus = includeXMLFile(attrs); 279 if (mParsingStatus == OK) { 280 mSectionStack.push_back(mCurrentSection); 281 mCurrentSection = SECTION_INCLUDE; 282 } 283 return; 284 } 285 286 switch (mCurrentSection) { 287 case SECTION_TOPLEVEL: 288 { 289 if (strEq(name, "Decoders")) { 290 mCurrentSection = SECTION_DECODERS; 291 } else if (strEq(name, "Encoders")) { 292 mCurrentSection = SECTION_ENCODERS; 293 } else if (strEq(name, "Settings")) { 294 mCurrentSection = SECTION_SETTINGS; 295 } 296 break; 297 } 298 299 case SECTION_SETTINGS: 300 { 301 if (strEq(name, "Setting")) { 302 mParsingStatus = addSettingFromAttributes(attrs); 303 } 304 break; 305 } 306 307 case SECTION_DECODERS: 308 { 309 if (strEq(name, "MediaCodec")) { 310 mParsingStatus = 311 addMediaCodecFromAttributes(false /* encoder */, attrs); 312 313 mCurrentSection = SECTION_DECODER; 314 } 315 break; 316 } 317 318 case SECTION_ENCODERS: 319 { 320 if (strEq(name, "MediaCodec")) { 321 mParsingStatus = 322 addMediaCodecFromAttributes(true /* encoder */, attrs); 323 324 mCurrentSection = SECTION_ENCODER; 325 } 326 break; 327 } 328 329 case SECTION_DECODER: 330 case SECTION_ENCODER: 331 { 332 if (strEq(name, "Quirk")) { 333 mParsingStatus = addQuirk(attrs); 334 } else if (strEq(name, "Type")) { 335 mParsingStatus = addTypeFromAttributes(attrs, 336 (mCurrentSection == SECTION_ENCODER)); 337 mCurrentSection = 338 (mCurrentSection == SECTION_DECODER ? 339 SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); 340 } 341 } 342 inType = false; 343 // fall through 344 345 case SECTION_DECODER_TYPE: 346 case SECTION_ENCODER_TYPE: 347 { 348 // ignore limits and features specified outside of type 349 bool outside = !inType && 350 mCurrentType == mCurrentCodec->second.typeMap.end(); 351 if (outside && 352 (strEq(name, "Limit") || strEq(name, "Feature"))) { 353 ALOGW("ignoring %s specified outside of a Type", name); 354 } else if (strEq(name, "Limit")) { 355 mParsingStatus = addLimit(attrs); 356 } else if (strEq(name, "Feature")) { 357 mParsingStatus = addFeature(attrs); 358 } 359 break; 360 } 361 362 default: 363 break; 364 } 365 366 } 367 368 void MediaCodecsXmlParser::endElementHandler(const char *name) { 369 if (mParsingStatus != OK) { 370 return; 371 } 372 373 switch (mCurrentSection) { 374 case SECTION_SETTINGS: 375 { 376 if (strEq(name, "Settings")) { 377 mCurrentSection = SECTION_TOPLEVEL; 378 } 379 break; 380 } 381 382 case SECTION_DECODERS: 383 { 384 if (strEq(name, "Decoders")) { 385 mCurrentSection = SECTION_TOPLEVEL; 386 } 387 break; 388 } 389 390 case SECTION_ENCODERS: 391 { 392 if (strEq(name, "Encoders")) { 393 mCurrentSection = SECTION_TOPLEVEL; 394 } 395 break; 396 } 397 398 case SECTION_DECODER_TYPE: 399 case SECTION_ENCODER_TYPE: 400 { 401 if (strEq(name, "Type")) { 402 mCurrentSection = 403 (mCurrentSection == SECTION_DECODER_TYPE ? 404 SECTION_DECODER : SECTION_ENCODER); 405 406 mCurrentType = mCurrentCodec->second.typeMap.end(); 407 } 408 break; 409 } 410 411 case SECTION_DECODER: 412 { 413 if (strEq(name, "MediaCodec")) { 414 mCurrentSection = SECTION_DECODERS; 415 mCurrentName.clear(); 416 } 417 break; 418 } 419 420 case SECTION_ENCODER: 421 { 422 if (strEq(name, "MediaCodec")) { 423 mCurrentSection = SECTION_ENCODERS; 424 mCurrentName.clear(); 425 } 426 break; 427 } 428 429 case SECTION_INCLUDE: 430 { 431 if (strEq(name, "Include") && (mSectionStack.size() > 0)) { 432 mCurrentSection = mSectionStack.back(); 433 mSectionStack.pop_back(); 434 } 435 break; 436 } 437 438 default: 439 break; 440 } 441 442 } 443 444 status_t MediaCodecsXmlParser::addSettingFromAttributes(const char **attrs) { 445 const char *name = nullptr; 446 const char *value = nullptr; 447 const char *update = nullptr; 448 449 size_t i = 0; 450 while (attrs[i] != nullptr) { 451 if (strEq(attrs[i], "name")) { 452 if (attrs[++i] == nullptr) { 453 ALOGE("addSettingFromAttributes: name is null"); 454 return -EINVAL; 455 } 456 name = attrs[i]; 457 } else if (strEq(attrs[i], "value")) { 458 if (attrs[++i] == nullptr) { 459 ALOGE("addSettingFromAttributes: value is null"); 460 return -EINVAL; 461 } 462 value = attrs[i]; 463 } else if (strEq(attrs[i], "update")) { 464 if (attrs[++i] == nullptr) { 465 ALOGE("addSettingFromAttributes: update is null"); 466 return -EINVAL; 467 } 468 update = attrs[i]; 469 } else { 470 ALOGE("addSettingFromAttributes: unrecognized attribute: %s", attrs[i]); 471 return -EINVAL; 472 } 473 ++i; 474 } 475 476 if (name == nullptr || value == nullptr) { 477 ALOGE("addSettingFromAttributes: name or value unspecified"); 478 return -EINVAL; 479 } 480 481 // Boolean values are converted to "0" or "1". 482 if (strHasPrefix(name, "supports-")) { 483 value = parseBoolean(value) ? "1" : "0"; 484 } 485 486 mUpdate = (update != nullptr) && parseBoolean(update); 487 auto attribute = mServiceAttributeMap.find(name); 488 if (attribute == mServiceAttributeMap.end()) { // New attribute name 489 if (mUpdate) { 490 ALOGE("addSettingFromAttributes: updating non-existing setting"); 491 return -EINVAL; 492 } 493 mServiceAttributeMap.insert(Attribute(name, value)); 494 } else { // Existing attribute name 495 if (!mUpdate) { 496 ALOGE("addSettingFromAttributes: adding existing setting"); 497 } 498 attribute->second = value; 499 } 500 501 return OK; 502 } 503 504 status_t MediaCodecsXmlParser::addMediaCodecFromAttributes( 505 bool encoder, const char **attrs) { 506 const char *name = nullptr; 507 const char *type = nullptr; 508 const char *update = nullptr; 509 510 size_t i = 0; 511 while (attrs[i] != nullptr) { 512 if (strEq(attrs[i], "name")) { 513 if (attrs[++i] == nullptr) { 514 ALOGE("addMediaCodecFromAttributes: name is null"); 515 return -EINVAL; 516 } 517 name = attrs[i]; 518 } else if (strEq(attrs[i], "type")) { 519 if (attrs[++i] == nullptr) { 520 ALOGE("addMediaCodecFromAttributes: type is null"); 521 return -EINVAL; 522 } 523 type = attrs[i]; 524 } else if (strEq(attrs[i], "update")) { 525 if (attrs[++i] == nullptr) { 526 ALOGE("addMediaCodecFromAttributes: update is null"); 527 return -EINVAL; 528 } 529 update = attrs[i]; 530 } else { 531 ALOGE("addMediaCodecFromAttributes: unrecognized attribute: %s", attrs[i]); 532 return -EINVAL; 533 } 534 ++i; 535 } 536 537 if (name == nullptr) { 538 ALOGE("addMediaCodecFromAttributes: name not found"); 539 return -EINVAL; 540 } 541 542 mUpdate = (update != nullptr) && parseBoolean(update); 543 mCurrentCodec = mCodecMap.find(name); 544 if (mCurrentCodec == mCodecMap.end()) { // New codec name 545 if (mUpdate) { 546 ALOGE("addMediaCodecFromAttributes: updating non-existing codec"); 547 return -EINVAL; 548 } 549 // Create a new codec in mCodecMap 550 mCurrentCodec = mCodecMap.insert( 551 Codec(name, CodecProperties())).first; 552 if (type != nullptr) { 553 mCurrentType = mCurrentCodec->second.typeMap.insert( 554 Type(type, AttributeMap())).first; 555 } else { 556 mCurrentType = mCurrentCodec->second.typeMap.end(); 557 } 558 mCurrentCodec->second.isEncoder = encoder; 559 mCurrentCodec->second.order = mCodecCounter++; 560 } else { // Existing codec name 561 if (!mUpdate) { 562 ALOGE("addMediaCodecFromAttributes: adding existing codec"); 563 return -EINVAL; 564 } 565 if (type != nullptr) { 566 mCurrentType = mCurrentCodec->second.typeMap.find(type); 567 if (mCurrentType == mCurrentCodec->second.typeMap.end()) { 568 ALOGE("addMediaCodecFromAttributes: updating non-existing type"); 569 return -EINVAL; 570 } 571 } else { 572 // This should happen only when the codec has at most one type. 573 mCurrentType = mCurrentCodec->second.typeMap.begin(); 574 } 575 } 576 577 return OK; 578 } 579 580 status_t MediaCodecsXmlParser::addQuirk(const char **attrs) { 581 const char *name = nullptr; 582 583 size_t i = 0; 584 while (attrs[i] != nullptr) { 585 if (strEq(attrs[i], "name")) { 586 if (attrs[++i] == nullptr) { 587 ALOGE("addQuirk: name is null"); 588 return -EINVAL; 589 } 590 name = attrs[i]; 591 } else { 592 ALOGE("addQuirk: unrecognized attribute: %s", attrs[i]); 593 return -EINVAL; 594 } 595 ++i; 596 } 597 598 if (name == nullptr) { 599 ALOGE("addQuirk: name not found"); 600 return -EINVAL; 601 } 602 603 mCurrentCodec->second.quirkSet.emplace(name); 604 return OK; 605 } 606 607 status_t MediaCodecsXmlParser::addTypeFromAttributes(const char **attrs, bool encoder) { 608 const char *name = nullptr; 609 const char *update = nullptr; 610 611 size_t i = 0; 612 while (attrs[i] != nullptr) { 613 if (strEq(attrs[i], "name")) { 614 if (attrs[++i] == nullptr) { 615 ALOGE("addTypeFromAttributes: name is null"); 616 return -EINVAL; 617 } 618 name = attrs[i]; 619 } else if (strEq(attrs[i], "update")) { 620 if (attrs[++i] == nullptr) { 621 ALOGE("addTypeFromAttributes: update is null"); 622 return -EINVAL; 623 } 624 update = attrs[i]; 625 } else { 626 ALOGE("addTypeFromAttributes: unrecognized attribute: %s", attrs[i]); 627 return -EINVAL; 628 } 629 ++i; 630 } 631 632 if (name == nullptr) { 633 return -EINVAL; 634 } 635 636 mCurrentCodec->second.isEncoder = encoder; 637 mCurrentType = mCurrentCodec->second.typeMap.find(name); 638 if (!mUpdate) { 639 if (mCurrentType != mCurrentCodec->second.typeMap.end()) { 640 ALOGE("addTypeFromAttributes: re-defining existing type without update"); 641 return -EINVAL; 642 } 643 mCurrentType = mCurrentCodec->second.typeMap.insert( 644 Type(name, AttributeMap())).first; 645 } else if (mCurrentType == mCurrentCodec->second.typeMap.end()) { 646 ALOGE("addTypeFromAttributes: updating non-existing type"); 647 } 648 return OK; 649 } 650 651 status_t MediaCodecsXmlParser::addLimit(const char **attrs) { 652 const char* a_name = nullptr; 653 const char* a_default = nullptr; 654 const char* a_in = nullptr; 655 const char* a_max = nullptr; 656 const char* a_min = nullptr; 657 const char* a_range = nullptr; 658 const char* a_ranges = nullptr; 659 const char* a_scale = nullptr; 660 const char* a_value = nullptr; 661 662 size_t i = 0; 663 while (attrs[i] != nullptr) { 664 if (strEq(attrs[i], "name")) { 665 if (attrs[++i] == nullptr) { 666 ALOGE("addLimit: name is null"); 667 return -EINVAL; 668 } 669 a_name = attrs[i]; 670 } else if (strEq(attrs[i], "default")) { 671 if (attrs[++i] == nullptr) { 672 ALOGE("addLimit: default is null"); 673 return -EINVAL; 674 } 675 a_default = attrs[i]; 676 } else if (strEq(attrs[i], "in")) { 677 if (attrs[++i] == nullptr) { 678 ALOGE("addLimit: in is null"); 679 return -EINVAL; 680 } 681 a_in = attrs[i]; 682 } else if (strEq(attrs[i], "max")) { 683 if (attrs[++i] == nullptr) { 684 ALOGE("addLimit: max is null"); 685 return -EINVAL; 686 } 687 a_max = attrs[i]; 688 } else if (strEq(attrs[i], "min")) { 689 if (attrs[++i] == nullptr) { 690 ALOGE("addLimit: min is null"); 691 return -EINVAL; 692 } 693 a_min = attrs[i]; 694 } else if (strEq(attrs[i], "range")) { 695 if (attrs[++i] == nullptr) { 696 ALOGE("addLimit: range is null"); 697 return -EINVAL; 698 } 699 a_range = attrs[i]; 700 } else if (strEq(attrs[i], "ranges")) { 701 if (attrs[++i] == nullptr) { 702 ALOGE("addLimit: ranges is null"); 703 return -EINVAL; 704 } 705 a_ranges = attrs[i]; 706 } else if (strEq(attrs[i], "scale")) { 707 if (attrs[++i] == nullptr) { 708 ALOGE("addLimit: scale is null"); 709 return -EINVAL; 710 } 711 a_scale = attrs[i]; 712 } else if (strEq(attrs[i], "value")) { 713 if (attrs[++i] == nullptr) { 714 ALOGE("addLimit: value is null"); 715 return -EINVAL; 716 } 717 a_value = attrs[i]; 718 } else { 719 ALOGE("addLimit: unrecognized limit: %s", attrs[i]); 720 return -EINVAL; 721 } 722 ++i; 723 } 724 725 if (a_name == nullptr) { 726 ALOGE("limit with no 'name' attribute"); 727 return -EINVAL; 728 } 729 730 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio, 731 // measured-frame-rate, measured-blocks-per-second: range 732 // quality: range + default + [scale] 733 // complexity: range + default 734 if (mCurrentType == mCurrentCodec->second.typeMap.end()) { 735 ALOGW("ignoring null type"); 736 return OK; 737 } 738 739 std::string range; 740 if (strEq(a_name, "aspect-ratio") || 741 strEq(a_name, "bitrate") || 742 strEq(a_name, "block-count") || 743 strEq(a_name, "blocks-per-second") || 744 strEq(a_name, "complexity") || 745 strEq(a_name, "frame-rate") || 746 strEq(a_name, "quality") || 747 strEq(a_name, "size") || 748 strEq(a_name, "measured-blocks-per-second") || 749 strHasPrefix(a_name, "measured-frame-rate-")) { 750 // "range" is specified in exactly one of the following forms: 751 // 1) min-max 752 // 2) value-value 753 // 3) range 754 if (a_min != nullptr && a_max != nullptr) { 755 // min-max 756 if (a_range != nullptr || a_value != nullptr) { 757 return limitError(a_name, "has 'min' and 'max' as well as 'range' or " 758 "'value' attributes"); 759 } 760 range = a_min; 761 range += '-'; 762 range += a_max; 763 } else if (a_min != nullptr || a_max != nullptr) { 764 return limitError(a_name, "has only 'min' or 'max' attribute"); 765 } else if (a_value != nullptr) { 766 // value-value 767 if (a_range != nullptr) { 768 return limitError(a_name, "has both 'range' and 'value' attributes"); 769 } 770 range = a_value; 771 range += '-'; 772 range += a_value; 773 } else if (a_range == nullptr) { 774 return limitError(a_name, "with no 'range', 'value' or 'min'/'max' attributes"); 775 } else { 776 // range 777 range = a_range; 778 } 779 780 // "aspect-ratio" requires some special treatment. 781 if (strEq(a_name, "aspect-ratio")) { 782 // "aspect-ratio" must have "in". 783 if (a_in == nullptr) { 784 return limitFoundMissingAttr(a_name, "in", false); 785 } 786 // "in" must be either "pixels" or "blocks". 787 if (!strEq(a_in, "pixels") && !strEq(a_in, "blocks")) { 788 return limitInvalidAttr(a_name, "in", a_in); 789 } 790 // name will be "pixel-aspect-ratio-range" or 791 // "block-aspect-ratio-range". 792 mCurrentType->second[ 793 std::string(a_in).substr(0, strlen(a_in) - 1) + 794 "-aspect-ratio-range"] = range; 795 } else { 796 // For everything else (apart from "aspect-ratio"), simply append 797 // "-range" to the name for the range-type property. 798 mCurrentType->second[std::string(a_name) + "-range"] = range; 799 800 // Only "quality" may have "scale". 801 if (!strEq(a_name, "quality") && a_scale != nullptr) { 802 return limitFoundMissingAttr(a_name, "scale"); 803 } else if (strEq(a_name, "quality")) { 804 // The default value of "quality-scale" is "linear". 805 mCurrentType->second["quality-scale"] = a_scale == nullptr ? 806 "linear" : a_scale; 807 } 808 809 // "quality" and "complexity" must have "default". 810 // Other limits must not have "default". 811 if (strEq(a_name, "quality") || strEq(a_name, "complexity")) { 812 if (a_default == nullptr) { 813 return limitFoundMissingAttr(a_name, "default", false); 814 } 815 // name will be "quality-default" or "complexity-default". 816 mCurrentType->second[std::string(a_name) + "-default"] = a_default; 817 } else if (a_default != nullptr) { 818 return limitFoundMissingAttr(a_name, "default", true); 819 } 820 } 821 } else { 822 if (a_default != nullptr) { 823 return limitFoundMissingAttr(a_name, "default"); 824 } 825 if (a_in != nullptr) { 826 return limitFoundMissingAttr(a_name, "in"); 827 } 828 if (a_scale != nullptr) { 829 return limitFoundMissingAttr(a_name, "scale"); 830 } 831 if (a_range != nullptr) { 832 return limitFoundMissingAttr(a_name, "range"); 833 } 834 if (a_min != nullptr) { 835 return limitFoundMissingAttr(a_name, "min"); 836 } 837 838 if (a_max != nullptr) { 839 // "max" must exist if and only if name is "channel-count" or 840 // "concurrent-instances". 841 // "min" is not ncessary. 842 if (strEq(a_name, "channel-count") || 843 strEq(a_name, "concurrent-instances")) { 844 mCurrentType->second[std::string("max-") + a_name] = a_max; 845 } else { 846 return limitFoundMissingAttr(a_name, "max", false); 847 } 848 } else if (strEq(a_name, "channel-count") || 849 strEq(a_name, "concurrent-instances")) { 850 return limitFoundMissingAttr(a_name, "max"); 851 } 852 853 if (a_ranges != nullptr) { 854 // "ranges" must exist if and only if name is "sample-rate". 855 if (strEq(a_name, "sample-rate")) { 856 mCurrentType->second["sample-rate-ranges"] = a_ranges; 857 } else { 858 return limitFoundMissingAttr(a_name, "ranges", false); 859 } 860 } else if (strEq(a_name, "sample-rate")) { 861 return limitFoundMissingAttr(a_name, "ranges"); 862 } 863 864 if (a_value != nullptr) { 865 // "value" must exist if and only if name is "alignment" or 866 // "block-size". 867 if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) { 868 mCurrentType->second[a_name] = a_value; 869 } else { 870 return limitFoundMissingAttr(a_name, "value", false); 871 } 872 } else if (strEq(a_name, "alignment") || strEq(a_name, "block-size")) { 873 return limitFoundMissingAttr(a_name, "value", false); 874 } 875 876 } 877 878 return OK; 879 } 880 881 status_t MediaCodecsXmlParser::addFeature(const char **attrs) { 882 size_t i = 0; 883 const char *name = nullptr; 884 int32_t optional = -1; 885 int32_t required = -1; 886 const char *value = nullptr; 887 888 while (attrs[i] != nullptr) { 889 if (strEq(attrs[i], "name")) { 890 if (attrs[++i] == nullptr) { 891 ALOGE("addFeature: name is null"); 892 return -EINVAL; 893 } 894 name = attrs[i]; 895 } else if (strEq(attrs[i], "optional")) { 896 if (attrs[++i] == nullptr) { 897 ALOGE("addFeature: optional is null"); 898 return -EINVAL; 899 } 900 optional = parseBoolean(attrs[i]) ? 1 : 0; 901 } else if (strEq(attrs[i], "required")) { 902 if (attrs[++i] == nullptr) { 903 ALOGE("addFeature: required is null"); 904 return -EINVAL; 905 } 906 required = parseBoolean(attrs[i]) ? 1 : 0; 907 } else if (strEq(attrs[i], "value")) { 908 if (attrs[++i] == nullptr) { 909 ALOGE("addFeature: value is null"); 910 return -EINVAL; 911 } 912 value = attrs[i]; 913 } else { 914 ALOGE("addFeature: unrecognized attribute: %s", attrs[i]); 915 return -EINVAL; 916 } 917 ++i; 918 } 919 920 // Every feature must have a name. 921 if (name == nullptr) { 922 ALOGE("feature with no 'name' attribute"); 923 return -EINVAL; 924 } 925 926 if (mCurrentType == mCurrentCodec->second.typeMap.end()) { 927 ALOGW("ignoring null type"); 928 return OK; 929 } 930 931 if ((optional != -1) || (required != -1)) { 932 if (optional == required) { 933 ALOGE("feature '%s' is both/neither optional and required", name); 934 return -EINVAL; 935 } 936 if ((optional == 1) || (required == 1)) { 937 if (value != nullptr) { 938 ALOGE("feature '%s' cannot have extra 'value'", name); 939 return -EINVAL; 940 } 941 mCurrentType->second[std::string("feature-") + name] = 942 optional == 1 ? "0" : "1"; 943 return OK; 944 } 945 } 946 mCurrentType->second[std::string("feature-") + name] = value == nullptr ? 947 "0" : value; 948 return OK; 949 } 950 951 const MediaCodecsXmlParser::AttributeMap& 952 MediaCodecsXmlParser::getServiceAttributeMap() const { 953 return mServiceAttributeMap; 954 } 955 956 const MediaCodecsXmlParser::CodecMap& 957 MediaCodecsXmlParser::getCodecMap() const { 958 return mCodecMap; 959 } 960 961 const MediaCodecsXmlParser::RoleMap& 962 MediaCodecsXmlParser::getRoleMap() const { 963 if (mRoleMap.empty()) { 964 generateRoleMap(); 965 } 966 return mRoleMap; 967 } 968 969 const char* MediaCodecsXmlParser::getCommonPrefix() const { 970 if (mCommonPrefix.empty()) { 971 generateCommonPrefix(); 972 } 973 return mCommonPrefix.data(); 974 } 975 976 status_t MediaCodecsXmlParser::getParsingStatus() const { 977 return mParsingStatus; 978 } 979 980 void MediaCodecsXmlParser::generateRoleMap() const { 981 for (const auto& codec : mCodecMap) { 982 const auto& codecName = codec.first; 983 bool isEncoder = codec.second.isEncoder; 984 size_t order = codec.second.order; 985 const auto& typeMap = codec.second.typeMap; 986 for (const auto& type : typeMap) { 987 const auto& typeName = type.first; 988 const char* roleName = GetComponentRole(isEncoder, typeName.data()); 989 if (roleName == nullptr) { 990 ALOGE("Cannot find the role for %s of type %s", 991 isEncoder ? "an encoder" : "a decoder", 992 typeName.data()); 993 continue; 994 } 995 const auto& typeAttributeMap = type.second; 996 997 auto roleIterator = mRoleMap.find(roleName); 998 std::multimap<size_t, NodeInfo>* nodeList; 999 if (roleIterator == mRoleMap.end()) { 1000 RoleProperties roleProperties; 1001 roleProperties.type = typeName; 1002 roleProperties.isEncoder = isEncoder; 1003 auto insertResult = mRoleMap.insert( 1004 std::make_pair(roleName, roleProperties)); 1005 if (!insertResult.second) { 1006 ALOGE("Cannot add role %s", roleName); 1007 continue; 1008 } 1009 nodeList = &insertResult.first->second.nodeList; 1010 } else { 1011 if (roleIterator->second.type != typeName) { 1012 ALOGE("Role %s has mismatching types: %s and %s", 1013 roleName, 1014 roleIterator->second.type.data(), 1015 typeName.data()); 1016 continue; 1017 } 1018 if (roleIterator->second.isEncoder != isEncoder) { 1019 ALOGE("Role %s cannot be both an encoder and a decoder", 1020 roleName); 1021 continue; 1022 } 1023 nodeList = &roleIterator->second.nodeList; 1024 } 1025 1026 NodeInfo nodeInfo; 1027 nodeInfo.name = codecName; 1028 nodeInfo.attributeList.reserve(typeAttributeMap.size()); 1029 for (const auto& attribute : typeAttributeMap) { 1030 nodeInfo.attributeList.push_back( 1031 Attribute{attribute.first, attribute.second}); 1032 } 1033 nodeList->insert(std::make_pair( 1034 std::move(order), std::move(nodeInfo))); 1035 } 1036 } 1037 } 1038 1039 void MediaCodecsXmlParser::generateCommonPrefix() const { 1040 if (mCodecMap.empty()) { 1041 return; 1042 } 1043 auto i = mCodecMap.cbegin(); 1044 auto first = i->first.cbegin(); 1045 auto last = i->first.cend(); 1046 for (++i; i != mCodecMap.cend(); ++i) { 1047 last = std::mismatch( 1048 first, last, i->first.cbegin(), i->first.cend()).first; 1049 } 1050 mCommonPrefix.insert(mCommonPrefix.begin(), first, last); 1051 } 1052 1053 } // namespace android 1054 1055