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