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