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