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