MediaCodecList.cpp revision 7f2262fc7d038a0d42def7d769bb9e816e292785
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/MediaCodecList.h> 35#include <media/stagefright/MediaErrors.h> 36#include <media/stagefright/OMXClient.h> 37#include <media/stagefright/OMXCodec.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 47const char *kMaxEncoderInputBuffers = "max-video-encoder-input-buffers"; 48 49static Mutex sInitMutex; 50 51static 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 60static 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 84sp<IMediaCodecList> MediaCodecList::sCodecList; 85 86// static 87void *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 115sp<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 139static Mutex sRemoteInitMutex; 140 141sp<IMediaCodecList> MediaCodecList::sRemoteList; 142 143sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver; 144 145void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) { 146 Mutex::Autolock _l(sRemoteInitMutex); 147 sRemoteList.clear(); 148 sBinderDeathObserver.clear(); 149} 150 151// static 152sp<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 174MediaCodecList::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 183void 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 318MediaCodecList::~MediaCodecList() { 319} 320 321status_t MediaCodecList::initCheck() const { 322 return mInitCheck; 323} 324 325void 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 376void MediaCodecList::StartElementHandlerWrapper( 377 void *me, const char *name, const char **attrs) { 378 static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs); 379} 380 381// static 382void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) { 383 static_cast<MediaCodecList *>(me)->endElementHandler(name); 384} 385 386status_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 427void 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 525void 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 604status_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 649void 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 674status_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 747status_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 CodecCapabilities caps; 756 status_t err = QueryCodec( 757 mOMX, 758 mCurrentInfo->mName.c_str(), 759 type, 760 mCurrentInfo->mIsEncoder, 761 &caps); 762 if (err != OK) { 763 return err; 764 } 765 766 return mCurrentInfo->initializeCapabilities(caps); 767} 768 769status_t MediaCodecList::addQuirk(const char **attrs) { 770 const char *name = NULL; 771 772 size_t i = 0; 773 while (attrs[i] != NULL) { 774 if (!strcmp(attrs[i], "name")) { 775 if (attrs[i + 1] == NULL) { 776 return -EINVAL; 777 } 778 name = attrs[i + 1]; 779 ++i; 780 } else { 781 return -EINVAL; 782 } 783 784 ++i; 785 } 786 787 if (name == NULL) { 788 return -EINVAL; 789 } 790 791 mCurrentInfo->addQuirk(name); 792 return OK; 793} 794 795status_t MediaCodecList::addTypeFromAttributes(const char **attrs) { 796 const char *name = NULL; 797 const char *update = NULL; 798 799 size_t i = 0; 800 while (attrs[i] != NULL) { 801 if (!strcmp(attrs[i], "name")) { 802 if (attrs[i + 1] == NULL) { 803 return -EINVAL; 804 } 805 name = attrs[i + 1]; 806 ++i; 807 } else if (!strcmp(attrs[i], "update")) { 808 if (attrs[i + 1] == NULL) { 809 return -EINVAL; 810 } 811 update = attrs[i + 1]; 812 ++i; 813 } else { 814 return -EINVAL; 815 } 816 817 ++i; 818 } 819 820 if (name == NULL) { 821 return -EINVAL; 822 } 823 824 bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL); 825 if (mUpdate != isExistingType) { 826 return -EINVAL; 827 } 828 829 status_t ret; 830 if (mUpdate) { 831 ret = mCurrentInfo->updateMime(name); 832 } else { 833 ret = mCurrentInfo->addMime(name); 834 } 835 836 if (ret != OK) { 837 return ret; 838 } 839 840 // The next step involves trying to load the codec, which may 841 // fail. Handle this gracefully (by not reporting such mime). 842 if (!mUpdate && initializeCapabilities(name) != OK) { 843 mCurrentInfo->removeMime(name); 844 } 845 return OK; 846} 847 848// legacy method for non-advanced codecs 849ssize_t MediaCodecList::findCodecByType( 850 const char *type, bool encoder, size_t startIndex) const { 851 static const char *advancedFeatures[] = { 852 "feature-secure-playback", 853 "feature-tunneled-playback", 854 }; 855 856 size_t numCodecs = mCodecInfos.size(); 857 for (; startIndex < numCodecs; ++startIndex) { 858 const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get(); 859 860 if (info.isEncoder() != encoder) { 861 continue; 862 } 863 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type); 864 if (capabilities == NULL) { 865 continue; 866 } 867 const sp<AMessage> &details = capabilities->getDetails(); 868 869 int32_t required; 870 bool isAdvanced = false; 871 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) { 872 if (details->findInt32(advancedFeatures[ix], &required) && 873 required != 0) { 874 isAdvanced = true; 875 break; 876 } 877 } 878 879 if (!isAdvanced) { 880 return startIndex; 881 } 882 } 883 884 return -ENOENT; 885} 886 887static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) { 888 ALOGE("limit '%s' with %s'%s' attribute", name.c_str(), 889 (found ? "" : "no "), attr); 890 return -EINVAL; 891} 892 893static status_t limitError(AString name, const char *msg) { 894 ALOGE("limit '%s' %s", name.c_str(), msg); 895 return -EINVAL; 896} 897 898static status_t limitInvalidAttr(AString name, const char *attr, AString value) { 899 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(), 900 attr, value.c_str()); 901 return -EINVAL; 902} 903 904status_t MediaCodecList::addLimit(const char **attrs) { 905 sp<AMessage> msg = new AMessage(); 906 907 size_t i = 0; 908 while (attrs[i] != NULL) { 909 if (attrs[i + 1] == NULL) { 910 return -EINVAL; 911 } 912 913 // attributes with values 914 if (!strcmp(attrs[i], "name") 915 || !strcmp(attrs[i], "default") 916 || !strcmp(attrs[i], "in") 917 || !strcmp(attrs[i], "max") 918 || !strcmp(attrs[i], "min") 919 || !strcmp(attrs[i], "range") 920 || !strcmp(attrs[i], "ranges") 921 || !strcmp(attrs[i], "scale") 922 || !strcmp(attrs[i], "value")) { 923 msg->setString(attrs[i], attrs[i + 1]); 924 ++i; 925 } else { 926 return -EINVAL; 927 } 928 ++i; 929 } 930 931 AString name; 932 if (!msg->findString("name", &name)) { 933 ALOGE("limit with no 'name' attribute"); 934 return -EINVAL; 935 } 936 937 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio, 938 // measured-frame-rate, measured-blocks-per-second: range 939 // quality: range + default + [scale] 940 // complexity: range + default 941 bool found; 942 943 if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" 944 || name == "blocks-per-second" || name == "complexity" 945 || name == "frame-rate" || name == "quality" || name == "size" 946 || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) { 947 AString min, max; 948 if (msg->findString("min", &min) && msg->findString("max", &max)) { 949 min.append("-"); 950 min.append(max); 951 if (msg->contains("range") || msg->contains("value")) { 952 return limitError(name, "has 'min' and 'max' as well as 'range' or " 953 "'value' attributes"); 954 } 955 msg->setString("range", min); 956 } else if (msg->contains("min") || msg->contains("max")) { 957 return limitError(name, "has only 'min' or 'max' attribute"); 958 } else if (msg->findString("value", &max)) { 959 min = max; 960 min.append("-"); 961 min.append(max); 962 if (msg->contains("range")) { 963 return limitError(name, "has both 'range' and 'value' attributes"); 964 } 965 msg->setString("range", min); 966 } 967 968 AString range, scale = "linear", def, in_; 969 if (!msg->findString("range", &range)) { 970 return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes"); 971 } 972 973 if ((name == "quality" || name == "complexity") ^ 974 (found = msg->findString("default", &def))) { 975 return limitFoundMissingAttr(name, "default", found); 976 } 977 if (name != "quality" && msg->findString("scale", &scale)) { 978 return limitFoundMissingAttr(name, "scale"); 979 } 980 if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) { 981 return limitFoundMissingAttr(name, "in", found); 982 } 983 984 if (name == "aspect-ratio") { 985 if (!(in_ == "pixels") && !(in_ == "blocks")) { 986 return limitInvalidAttr(name, "in", in_); 987 } 988 in_.erase(5, 1); // (pixel|block)-aspect-ratio 989 in_.append("-"); 990 in_.append(name); 991 name = in_; 992 } 993 if (name == "quality") { 994 mCurrentInfo->addDetail("quality-scale", scale); 995 } 996 if (name == "quality" || name == "complexity") { 997 AString tag = name; 998 tag.append("-default"); 999 mCurrentInfo->addDetail(tag, def); 1000 } 1001 AString tag = name; 1002 tag.append("-range"); 1003 mCurrentInfo->addDetail(tag, range); 1004 } else { 1005 AString max, value, ranges; 1006 if (msg->contains("default")) { 1007 return limitFoundMissingAttr(name, "default"); 1008 } else if (msg->contains("in")) { 1009 return limitFoundMissingAttr(name, "in"); 1010 } else if ((name == "channel-count" || name == "concurrent-instances") ^ 1011 (found = msg->findString("max", &max))) { 1012 return limitFoundMissingAttr(name, "max", found); 1013 } else if (msg->contains("min")) { 1014 return limitFoundMissingAttr(name, "min"); 1015 } else if (msg->contains("range")) { 1016 return limitFoundMissingAttr(name, "range"); 1017 } else if ((name == "sample-rate") ^ 1018 (found = msg->findString("ranges", &ranges))) { 1019 return limitFoundMissingAttr(name, "ranges", found); 1020 } else if (msg->contains("scale")) { 1021 return limitFoundMissingAttr(name, "scale"); 1022 } else if ((name == "alignment" || name == "block-size") ^ 1023 (found = msg->findString("value", &value))) { 1024 return limitFoundMissingAttr(name, "value", found); 1025 } 1026 1027 if (max.size()) { 1028 AString tag = "max-"; 1029 tag.append(name); 1030 mCurrentInfo->addDetail(tag, max); 1031 } else if (value.size()) { 1032 mCurrentInfo->addDetail(name, value); 1033 } else if (ranges.size()) { 1034 AString tag = name; 1035 tag.append("-ranges"); 1036 mCurrentInfo->addDetail(tag, ranges); 1037 } else { 1038 ALOGW("Ignoring unrecognized limit '%s'", name.c_str()); 1039 } 1040 } 1041 return OK; 1042} 1043 1044status_t MediaCodecList::addFeature(const char **attrs) { 1045 size_t i = 0; 1046 const char *name = NULL; 1047 int32_t optional = -1; 1048 int32_t required = -1; 1049 const char *value = NULL; 1050 1051 while (attrs[i] != NULL) { 1052 if (attrs[i + 1] == NULL) { 1053 return -EINVAL; 1054 } 1055 1056 // attributes with values 1057 if (!strcmp(attrs[i], "name")) { 1058 name = attrs[i + 1]; 1059 ++i; 1060 } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) { 1061 int value = (int)parseBoolean(attrs[i + 1]); 1062 if (!strcmp(attrs[i], "optional")) { 1063 optional = value; 1064 } else { 1065 required = value; 1066 } 1067 ++i; 1068 } else if (!strcmp(attrs[i], "value")) { 1069 value = attrs[i + 1]; 1070 ++i; 1071 } else { 1072 return -EINVAL; 1073 } 1074 ++i; 1075 } 1076 if (name == NULL) { 1077 ALOGE("feature with no 'name' attribute"); 1078 return -EINVAL; 1079 } 1080 1081 if (optional == required && optional != -1) { 1082 ALOGE("feature '%s' is both/neither optional and required", name); 1083 return -EINVAL; 1084 } 1085 1086 if ((optional != -1 || required != -1) && (value != NULL)) { 1087 ALOGE("feature '%s' has both a value and optional/required attribute", name); 1088 return -EINVAL; 1089 } 1090 1091 if (value != NULL) { 1092 mCurrentInfo->addFeature(name, value); 1093 } else { 1094 mCurrentInfo->addFeature(name, (required == 1) || (optional == 0)); 1095 } 1096 return OK; 1097} 1098 1099ssize_t MediaCodecList::findCodecByName(const char *name) const { 1100 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 1101 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); 1102 1103 if (info.mName == name) { 1104 return i; 1105 } 1106 } 1107 1108 return -ENOENT; 1109} 1110 1111size_t MediaCodecList::countCodecs() const { 1112 return mCodecInfos.size(); 1113} 1114 1115const sp<AMessage> MediaCodecList::getGlobalSettings() const { 1116 return mGlobalSettings; 1117} 1118 1119//static 1120bool MediaCodecList::isSoftwareCodec(const AString &componentName) { 1121 return componentName.startsWithIgnoreCase("OMX.google.") 1122 || !componentName.startsWithIgnoreCase("OMX."); 1123} 1124 1125static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) { 1126 // sort order 1: software codecs are first (lower) 1127 bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1); 1128 bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2); 1129 if (isSoftwareCodec1 != isSoftwareCodec2) { 1130 return isSoftwareCodec2 - isSoftwareCodec1; 1131 } 1132 1133 // sort order 2: OMX codecs are first (lower) 1134 bool isOMX1 = name1->startsWithIgnoreCase("OMX."); 1135 bool isOMX2 = name2->startsWithIgnoreCase("OMX."); 1136 return isOMX2 - isOMX1; 1137} 1138 1139//static 1140void MediaCodecList::findMatchingCodecs( 1141 const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) { 1142 matches->clear(); 1143 1144 const sp<IMediaCodecList> list = getInstance(); 1145 if (list == NULL) { 1146 return; 1147 } 1148 1149 size_t index = 0; 1150 for (;;) { 1151 ssize_t matchIndex = 1152 list->findCodecByType(mime, encoder, index); 1153 1154 if (matchIndex < 0) { 1155 break; 1156 } 1157 1158 index = matchIndex + 1; 1159 1160 const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex); 1161 CHECK(info != NULL); 1162 AString componentName = info->getCodecName(); 1163 1164 if (!((flags & kHardwareCodecsOnly) && !isSoftwareCodec(componentName))) { 1165 matches->push(componentName); 1166 ALOGV("matching '%s'", componentName.c_str()); 1167 } 1168 } 1169 1170 if (flags & kPreferSoftwareCodecs) { 1171 matches->sort(compareSoftwareCodecsFirst); 1172 } 1173} 1174 1175// static 1176uint32_t MediaCodecList::getQuirksFor(const char *componentName) { 1177 const sp<IMediaCodecList> list = getInstance(); 1178 if (list == NULL) { 1179 return 0; 1180 } 1181 1182 ssize_t ix = list->findCodecByName(componentName); 1183 if (ix < 0) { 1184 return 0; 1185 } 1186 1187 const sp<MediaCodecInfo> info = list->getCodecInfo(ix); 1188 1189 uint32_t quirks = 0; 1190 if (info->hasQuirk("requires-allocate-on-input-ports")) { 1191 quirks |= ACodec::kRequiresAllocateBufferOnInputPorts; 1192 } 1193 if (info->hasQuirk("requires-allocate-on-output-ports")) { 1194 quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts; 1195 } 1196 1197 return quirks; 1198} 1199 1200} // namespace android 1201