MediaCodecList.cpp revision 6ff58f04f78886b07c72c0118eb71a78d08f5651
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 <binder/IServiceManager.h> 22 23#include <media/IMediaCodecList.h> 24#include <media/IMediaPlayerService.h> 25#include <media/MediaCodecInfo.h> 26 27#include <media/stagefright/foundation/ADebug.h> 28#include <media/stagefright/foundation/AMessage.h> 29#include <media/stagefright/MediaCodecList.h> 30#include <media/stagefright/MediaErrors.h> 31#include <media/stagefright/OMXClient.h> 32#include <media/stagefright/OMXCodec.h> 33 34#include <utils/threads.h> 35 36#include <libexpat/expat.h> 37 38namespace android { 39 40static Mutex sInitMutex; 41 42static MediaCodecList *gCodecList = NULL; 43 44// static 45sp<IMediaCodecList> MediaCodecList::sCodecList; 46 47// static 48sp<IMediaCodecList> MediaCodecList::getLocalInstance() { 49 Mutex::Autolock autoLock(sInitMutex); 50 51 if (gCodecList == NULL) { 52 gCodecList = new MediaCodecList; 53 if (gCodecList->initCheck() == OK) { 54 sCodecList = gCodecList; 55 } 56 } 57 58 return sCodecList; 59} 60 61static Mutex sRemoteInitMutex; 62 63sp<IMediaCodecList> MediaCodecList::sRemoteList; 64 65// static 66sp<IMediaCodecList> MediaCodecList::getInstance() { 67 Mutex::Autolock _l(sRemoteInitMutex); 68 if (sRemoteList == NULL) { 69 sp<IBinder> binder = 70 defaultServiceManager()->getService(String16("media.player")); 71 sp<IMediaPlayerService> service = 72 interface_cast<IMediaPlayerService>(binder); 73 if (service.get() != NULL) { 74 sRemoteList = service->getCodecList(); 75 } 76 77 if (sRemoteList == NULL) { 78 // if failed to get remote list, create local list 79 sRemoteList = getLocalInstance(); 80 } 81 } 82 return sRemoteList; 83} 84 85MediaCodecList::MediaCodecList() 86 : mInitCheck(NO_INIT) { 87 parseTopLevelXMLFile("/etc/media_codecs.xml"); 88} 89 90void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) { 91 // get href_base 92 char *href_base_end = strrchr(codecs_xml, '/'); 93 if (href_base_end != NULL) { 94 mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1); 95 } 96 97 mInitCheck = OK; // keeping this here for safety 98 mCurrentSection = SECTION_TOPLEVEL; 99 mDepth = 0; 100 101 OMXClient client; 102 mInitCheck = client.connect(); 103 if (mInitCheck != OK) { 104 return; 105 } 106 mOMX = client.interface(); 107 parseXMLFile(codecs_xml); 108 mOMX.clear(); 109 110 if (mInitCheck != OK) { 111 mCodecInfos.clear(); 112 return; 113 } 114 115 for (size_t i = mCodecInfos.size(); i-- > 0;) { 116 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); 117 118 if (info.mCaps.size() == 0) { 119 // No types supported by this component??? 120 ALOGW("Component %s does not support any type of media?", 121 info.mName.c_str()); 122 123 mCodecInfos.removeAt(i); 124#if LOG_NDEBUG == 0 125 } else { 126 for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) { 127 AString mime = info.mCaps.keyAt(type_ix); 128 const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix); 129 130 ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(), 131 caps->getDetails()->debugString().c_str()); 132 ALOGV(" flags=%d", caps->getFlags()); 133 { 134 Vector<uint32_t> colorFormats; 135 caps->getSupportedColorFormats(&colorFormats); 136 AString nice; 137 for (size_t ix = 0; ix < colorFormats.size(); ix++) { 138 if (ix > 0) { 139 nice.append(", "); 140 } 141 nice.append(colorFormats.itemAt(ix)); 142 } 143 ALOGV(" colors=[%s]", nice.c_str()); 144 } 145 { 146 Vector<MediaCodecInfo::ProfileLevel> profileLevels; 147 caps->getSupportedProfileLevels(&profileLevels); 148 AString nice; 149 for (size_t ix = 0; ix < profileLevels.size(); ix++) { 150 if (ix > 0) { 151 nice.append(", "); 152 } 153 const MediaCodecInfo::ProfileLevel &pl = 154 profileLevels.itemAt(ix); 155 nice.append(pl.mProfile); 156 nice.append("/"); 157 nice.append(pl.mLevel); 158 } 159 ALOGV(" levels=[%s]", nice.c_str()); 160 } 161 } 162#endif 163 } 164 } 165 166#if 0 167 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 168 const CodecInfo &info = mCodecInfos.itemAt(i); 169 170 AString line = info.mName; 171 line.append(" supports "); 172 for (size_t j = 0; j < mTypes.size(); ++j) { 173 uint32_t value = mTypes.valueAt(j); 174 175 if (info.mTypes & (1ul << value)) { 176 line.append(mTypes.keyAt(j)); 177 line.append(" "); 178 } 179 } 180 181 ALOGI("%s", line.c_str()); 182 } 183#endif 184} 185 186MediaCodecList::~MediaCodecList() { 187} 188 189status_t MediaCodecList::initCheck() const { 190 return mInitCheck; 191} 192 193void MediaCodecList::parseXMLFile(const char *path) { 194 FILE *file = fopen(path, "r"); 195 196 if (file == NULL) { 197 ALOGW("unable to open media codecs configuration xml file: %s", path); 198 mInitCheck = NAME_NOT_FOUND; 199 return; 200 } 201 202 XML_Parser parser = ::XML_ParserCreate(NULL); 203 CHECK(parser != NULL); 204 205 ::XML_SetUserData(parser, this); 206 ::XML_SetElementHandler( 207 parser, StartElementHandlerWrapper, EndElementHandlerWrapper); 208 209 const int BUFF_SIZE = 512; 210 while (mInitCheck == OK) { 211 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE); 212 if (buff == NULL) { 213 ALOGE("failed in call to XML_GetBuffer()"); 214 mInitCheck = UNKNOWN_ERROR; 215 break; 216 } 217 218 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file); 219 if (bytes_read < 0) { 220 ALOGE("failed in call to read"); 221 mInitCheck = ERROR_IO; 222 break; 223 } 224 225 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0); 226 if (status != XML_STATUS_OK) { 227 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser))); 228 mInitCheck = ERROR_MALFORMED; 229 break; 230 } 231 232 if (bytes_read == 0) { 233 break; 234 } 235 } 236 237 ::XML_ParserFree(parser); 238 239 fclose(file); 240 file = NULL; 241} 242 243// static 244void MediaCodecList::StartElementHandlerWrapper( 245 void *me, const char *name, const char **attrs) { 246 static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs); 247} 248 249// static 250void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) { 251 static_cast<MediaCodecList *>(me)->endElementHandler(name); 252} 253 254status_t MediaCodecList::includeXMLFile(const char **attrs) { 255 const char *href = NULL; 256 size_t i = 0; 257 while (attrs[i] != NULL) { 258 if (!strcmp(attrs[i], "href")) { 259 if (attrs[i + 1] == NULL) { 260 return -EINVAL; 261 } 262 href = attrs[i + 1]; 263 ++i; 264 } else { 265 return -EINVAL; 266 } 267 ++i; 268 } 269 270 // For security reasons and for simplicity, file names can only contain 271 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml 272 for (i = 0; href[i] != '\0'; i++) { 273 if (href[i] == '.' || href[i] == '_' || 274 (href[i] >= '0' && href[i] <= '9') || 275 (href[i] >= 'A' && href[i] <= 'Z') || 276 (href[i] >= 'a' && href[i] <= 'z')) { 277 continue; 278 } 279 ALOGE("invalid include file name: %s", href); 280 return -EINVAL; 281 } 282 283 AString filename = href; 284 if (!filename.startsWith("media_codecs_") || 285 !filename.endsWith(".xml")) { 286 ALOGE("invalid include file name: %s", href); 287 return -EINVAL; 288 } 289 filename.insert(mHrefBase, 0); 290 291 parseXMLFile(filename.c_str()); 292 return mInitCheck; 293} 294 295void MediaCodecList::startElementHandler( 296 const char *name, const char **attrs) { 297 if (mInitCheck != OK) { 298 return; 299 } 300 301 bool inType = true; 302 303 if (!strcmp(name, "Include")) { 304 mInitCheck = includeXMLFile(attrs); 305 if (mInitCheck == OK) { 306 mPastSections.push(mCurrentSection); 307 mCurrentSection = SECTION_INCLUDE; 308 } 309 ++mDepth; 310 return; 311 } 312 313 switch (mCurrentSection) { 314 case SECTION_TOPLEVEL: 315 { 316 if (!strcmp(name, "Decoders")) { 317 mCurrentSection = SECTION_DECODERS; 318 } else if (!strcmp(name, "Encoders")) { 319 mCurrentSection = SECTION_ENCODERS; 320 } 321 break; 322 } 323 324 case SECTION_DECODERS: 325 { 326 if (!strcmp(name, "MediaCodec")) { 327 mInitCheck = 328 addMediaCodecFromAttributes(false /* encoder */, attrs); 329 330 mCurrentSection = SECTION_DECODER; 331 } 332 break; 333 } 334 335 case SECTION_ENCODERS: 336 { 337 if (!strcmp(name, "MediaCodec")) { 338 mInitCheck = 339 addMediaCodecFromAttributes(true /* encoder */, attrs); 340 341 mCurrentSection = SECTION_ENCODER; 342 } 343 break; 344 } 345 346 case SECTION_DECODER: 347 case SECTION_ENCODER: 348 { 349 if (!strcmp(name, "Quirk")) { 350 mInitCheck = addQuirk(attrs); 351 } else if (!strcmp(name, "Type")) { 352 mInitCheck = addTypeFromAttributes(attrs); 353 mCurrentSection = 354 (mCurrentSection == SECTION_DECODER 355 ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); 356 } 357 } 358 inType = false; 359 // fall through 360 361 case SECTION_DECODER_TYPE: 362 case SECTION_ENCODER_TYPE: 363 { 364 // ignore limits and features specified outside of type 365 bool outside = !inType && !mCurrentInfo->mHasSoleMime; 366 if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) { 367 ALOGW("ignoring %s specified outside of a Type", name); 368 } else if (!strcmp(name, "Limit")) { 369 mInitCheck = addLimit(attrs); 370 } else if (!strcmp(name, "Feature")) { 371 mInitCheck = addFeature(attrs); 372 } 373 break; 374 } 375 376 default: 377 break; 378 } 379 380 ++mDepth; 381} 382 383void MediaCodecList::endElementHandler(const char *name) { 384 if (mInitCheck != OK) { 385 return; 386 } 387 388 switch (mCurrentSection) { 389 case SECTION_DECODERS: 390 { 391 if (!strcmp(name, "Decoders")) { 392 mCurrentSection = SECTION_TOPLEVEL; 393 } 394 break; 395 } 396 397 case SECTION_ENCODERS: 398 { 399 if (!strcmp(name, "Encoders")) { 400 mCurrentSection = SECTION_TOPLEVEL; 401 } 402 break; 403 } 404 405 case SECTION_DECODER_TYPE: 406 case SECTION_ENCODER_TYPE: 407 { 408 if (!strcmp(name, "Type")) { 409 mCurrentSection = 410 (mCurrentSection == SECTION_DECODER_TYPE 411 ? SECTION_DECODER : SECTION_ENCODER); 412 413 mCurrentInfo->complete(); 414 } 415 break; 416 } 417 418 case SECTION_DECODER: 419 { 420 if (!strcmp(name, "MediaCodec")) { 421 mCurrentSection = SECTION_DECODERS; 422 mCurrentInfo->complete(); 423 mCurrentInfo = NULL; 424 } 425 break; 426 } 427 428 case SECTION_ENCODER: 429 { 430 if (!strcmp(name, "MediaCodec")) { 431 mCurrentSection = SECTION_ENCODERS; 432 mCurrentInfo->complete();; 433 mCurrentInfo = NULL; 434 } 435 break; 436 } 437 438 case SECTION_INCLUDE: 439 { 440 if (!strcmp(name, "Include") && mPastSections.size() > 0) { 441 mCurrentSection = mPastSections.top(); 442 mPastSections.pop(); 443 } 444 break; 445 } 446 447 default: 448 break; 449 } 450 451 --mDepth; 452} 453 454status_t MediaCodecList::addMediaCodecFromAttributes( 455 bool encoder, const char **attrs) { 456 const char *name = NULL; 457 const char *type = NULL; 458 459 size_t i = 0; 460 while (attrs[i] != NULL) { 461 if (!strcmp(attrs[i], "name")) { 462 if (attrs[i + 1] == NULL) { 463 return -EINVAL; 464 } 465 name = attrs[i + 1]; 466 ++i; 467 } else if (!strcmp(attrs[i], "type")) { 468 if (attrs[i + 1] == NULL) { 469 return -EINVAL; 470 } 471 type = attrs[i + 1]; 472 ++i; 473 } else { 474 return -EINVAL; 475 } 476 477 ++i; 478 } 479 480 if (name == NULL) { 481 return -EINVAL; 482 } 483 484 mCurrentInfo = new MediaCodecInfo(name, encoder, type); 485 // The next step involves trying to load the codec, which may 486 // fail. Only list the codec if this succeeds. 487 // However, keep mCurrentInfo object around until parsing 488 // of full codec info is completed. 489 if (initializeCapabilities(type) == OK) { 490 mCodecInfos.push_back(mCurrentInfo); 491 } 492 return OK; 493} 494 495status_t MediaCodecList::initializeCapabilities(const char *type) { 496 if (type == NULL) { 497 return OK; 498 } 499 500 ALOGV("initializeCapabilities %s:%s", 501 mCurrentInfo->mName.c_str(), type); 502 503 CodecCapabilities caps; 504 status_t err = QueryCodec( 505 mOMX, 506 mCurrentInfo->mName.c_str(), 507 type, 508 mCurrentInfo->mIsEncoder, 509 &caps); 510 if (err != OK) { 511 return err; 512 } 513 514 return mCurrentInfo->initializeCapabilities(caps); 515} 516 517status_t MediaCodecList::addQuirk(const char **attrs) { 518 const char *name = NULL; 519 520 size_t i = 0; 521 while (attrs[i] != NULL) { 522 if (!strcmp(attrs[i], "name")) { 523 if (attrs[i + 1] == NULL) { 524 return -EINVAL; 525 } 526 name = attrs[i + 1]; 527 ++i; 528 } else { 529 return -EINVAL; 530 } 531 532 ++i; 533 } 534 535 if (name == NULL) { 536 return -EINVAL; 537 } 538 539 mCurrentInfo->addQuirk(name); 540 return OK; 541} 542 543status_t MediaCodecList::addTypeFromAttributes(const char **attrs) { 544 const char *name = NULL; 545 546 size_t i = 0; 547 while (attrs[i] != NULL) { 548 if (!strcmp(attrs[i], "name")) { 549 if (attrs[i + 1] == NULL) { 550 return -EINVAL; 551 } 552 name = attrs[i + 1]; 553 ++i; 554 } else { 555 return -EINVAL; 556 } 557 558 ++i; 559 } 560 561 if (name == NULL) { 562 return -EINVAL; 563 } 564 565 status_t ret = mCurrentInfo->addMime(name); 566 if (ret != OK) { 567 return ret; 568 } 569 570 // The next step involves trying to load the codec, which may 571 // fail. Handle this gracefully (by not reporting such mime). 572 if (initializeCapabilities(name) != OK) { 573 mCurrentInfo->removeMime(name); 574 } 575 return OK; 576} 577 578// legacy method for non-advanced codecs 579ssize_t MediaCodecList::findCodecByType( 580 const char *type, bool encoder, size_t startIndex) const { 581 static const char *advancedFeatures[] = { 582 "feature-secure-playback", 583 "feature-tunneled-playback", 584 }; 585 586 size_t numCodecs = mCodecInfos.size(); 587 for (; startIndex < numCodecs; ++startIndex) { 588 const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get(); 589 590 if (info.isEncoder() != encoder) { 591 continue; 592 } 593 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type); 594 if (capabilities == NULL) { 595 continue; 596 } 597 const sp<AMessage> &details = capabilities->getDetails(); 598 599 int32_t required; 600 bool isAdvanced = false; 601 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) { 602 if (details->findInt32(advancedFeatures[ix], &required) && 603 required != 0) { 604 isAdvanced = true; 605 break; 606 } 607 } 608 609 if (!isAdvanced) { 610 return startIndex; 611 } 612 } 613 614 return -ENOENT; 615} 616 617static status_t limitFoundMissingAttr(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(AString name, const char *msg) { 624 ALOGE("limit '%s' %s", name.c_str(), msg); 625 return -EINVAL; 626} 627 628static status_t limitInvalidAttr(AString name, const char *attr, 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 MediaCodecList::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 return -EINVAL; 641 } 642 643 // attributes with values 644 if (!strcmp(attrs[i], "name") 645 || !strcmp(attrs[i], "default") 646 || !strcmp(attrs[i], "in") 647 || !strcmp(attrs[i], "max") 648 || !strcmp(attrs[i], "min") 649 || !strcmp(attrs[i], "range") 650 || !strcmp(attrs[i], "ranges") 651 || !strcmp(attrs[i], "scale") 652 || !strcmp(attrs[i], "value")) { 653 msg->setString(attrs[i], attrs[i + 1]); 654 ++i; 655 } else { 656 return -EINVAL; 657 } 658 ++i; 659 } 660 661 AString name; 662 if (!msg->findString("name", &name)) { 663 ALOGE("limit with no 'name' attribute"); 664 return -EINVAL; 665 } 666 667 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range 668 // quality: range + default + [scale] 669 // complexity: range + default 670 bool found; 671 672 if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" 673 || name == "blocks-per-second" || name == "complexity" 674 || name == "frame-rate" || name == "quality" || name == "size") { 675 AString min, max; 676 if (msg->findString("min", &min) && msg->findString("max", &max)) { 677 min.append("-"); 678 min.append(max); 679 if (msg->contains("range") || msg->contains("value")) { 680 return limitError(name, "has 'min' and 'max' as well as 'range' or " 681 "'value' attributes"); 682 } 683 msg->setString("range", min); 684 } else if (msg->contains("min") || msg->contains("max")) { 685 return limitError(name, "has only 'min' or 'max' attribute"); 686 } else if (msg->findString("value", &max)) { 687 min = max; 688 min.append("-"); 689 min.append(max); 690 if (msg->contains("range")) { 691 return limitError(name, "has both 'range' and 'value' attributes"); 692 } 693 msg->setString("range", min); 694 } 695 696 AString range, scale = "linear", def, in_; 697 if (!msg->findString("range", &range)) { 698 return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes"); 699 } 700 701 if ((name == "quality" || name == "complexity") ^ 702 (found = msg->findString("default", &def))) { 703 return limitFoundMissingAttr(name, "default", found); 704 } 705 if (name != "quality" && msg->findString("scale", &scale)) { 706 return limitFoundMissingAttr(name, "scale"); 707 } 708 if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) { 709 return limitFoundMissingAttr(name, "in", found); 710 } 711 712 if (name == "aspect-ratio") { 713 if (!(in_ == "pixels") && !(in_ == "blocks")) { 714 return limitInvalidAttr(name, "in", in_); 715 } 716 in_.erase(5, 1); // (pixel|block)-aspect-ratio 717 in_.append("-"); 718 in_.append(name); 719 name = in_; 720 } 721 if (name == "quality") { 722 mCurrentInfo->addDetail("quality-scale", scale); 723 } 724 if (name == "quality" || name == "complexity") { 725 AString tag = name; 726 tag.append("-default"); 727 mCurrentInfo->addDetail(tag, def); 728 } 729 AString tag = name; 730 tag.append("-range"); 731 mCurrentInfo->addDetail(tag, range); 732 } else { 733 AString max, value, ranges; 734 if (msg->contains("default")) { 735 return limitFoundMissingAttr(name, "default"); 736 } else if (msg->contains("in")) { 737 return limitFoundMissingAttr(name, "in"); 738 } else if ((name == "channel-count") ^ 739 (found = msg->findString("max", &max))) { 740 return limitFoundMissingAttr(name, "max", found); 741 } else if (msg->contains("min")) { 742 return limitFoundMissingAttr(name, "min"); 743 } else if (msg->contains("range")) { 744 return limitFoundMissingAttr(name, "range"); 745 } else if ((name == "sample-rate") ^ 746 (found = msg->findString("ranges", &ranges))) { 747 return limitFoundMissingAttr(name, "ranges", found); 748 } else if (msg->contains("scale")) { 749 return limitFoundMissingAttr(name, "scale"); 750 } else if ((name == "alignment" || name == "block-size") ^ 751 (found = msg->findString("value", &value))) { 752 return limitFoundMissingAttr(name, "value", found); 753 } 754 755 if (max.size()) { 756 AString tag = "max-"; 757 tag.append(name); 758 mCurrentInfo->addDetail(tag, max); 759 } else if (value.size()) { 760 mCurrentInfo->addDetail(name, value); 761 } else if (ranges.size()) { 762 AString tag = name; 763 tag.append("-ranges"); 764 mCurrentInfo->addDetail(tag, ranges); 765 } else { 766 ALOGW("Ignoring unrecognized limit '%s'", name.c_str()); 767 } 768 } 769 return OK; 770} 771 772static bool parseBoolean(const char *s) { 773 if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) { 774 return true; 775 } 776 char *end; 777 unsigned long res = strtoul(s, &end, 10); 778 return *s != '\0' && *end == '\0' && res > 0; 779} 780 781status_t MediaCodecList::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 787 while (attrs[i] != NULL) { 788 if (attrs[i + 1] == NULL) { 789 return -EINVAL; 790 } 791 792 // attributes with values 793 if (!strcmp(attrs[i], "name")) { 794 name = attrs[i + 1]; 795 ++i; 796 } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) { 797 int value = (int)parseBoolean(attrs[i + 1]); 798 if (!strcmp(attrs[i], "optional")) { 799 optional = value; 800 } else { 801 required = value; 802 } 803 ++i; 804 } else { 805 return -EINVAL; 806 } 807 ++i; 808 } 809 if (name == NULL) { 810 ALOGE("feature with no 'name' attribute"); 811 return -EINVAL; 812 } 813 814 if (optional == required && optional != -1) { 815 ALOGE("feature '%s' is both/neither optional and required", name); 816 return -EINVAL; 817 } 818 819 mCurrentInfo->addFeature(name, (required == 1) || (optional == 0)); 820 return OK; 821} 822 823ssize_t MediaCodecList::findCodecByName(const char *name) const { 824 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 825 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); 826 827 if (info.mName == name) { 828 return i; 829 } 830 } 831 832 return -ENOENT; 833} 834 835size_t MediaCodecList::countCodecs() const { 836 return mCodecInfos.size(); 837} 838 839} // namespace android 840