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