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