MediaCodecList.cpp revision 0d0da9b40469b847f9913dd97805b7bd99b1aeb9
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 (policies.size() > 0) { 220 sp<IServiceManager> sm = defaultServiceManager(); 221 sp<IBinder> binder = sm->getService(String16("media.resource_manager")); 222 sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder); 223 if (service == NULL) { 224 ALOGE("MediaCodecList: failed to get ResourceManagerService"); 225 } else { 226 service->config(policies); 227 } 228 } 229 230 for (size_t i = mCodecInfos.size(); i > 0;) { 231 i--; 232 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); 233 if (info.mCaps.size() == 0) { 234 // No types supported by this component??? 235 ALOGW("Component %s does not support any type of media?", 236 info.mName.c_str()); 237 238 mCodecInfos.removeAt(i); 239#if LOG_NDEBUG == 0 240 } else { 241 for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) { 242 AString mime = info.mCaps.keyAt(type_ix); 243 const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix); 244 245 ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(), 246 caps->getDetails()->debugString().c_str()); 247 ALOGV(" flags=%d", caps->getFlags()); 248 { 249 Vector<uint32_t> colorFormats; 250 caps->getSupportedColorFormats(&colorFormats); 251 AString nice; 252 for (size_t ix = 0; ix < colorFormats.size(); ix++) { 253 if (ix > 0) { 254 nice.append(", "); 255 } 256 nice.append(colorFormats.itemAt(ix)); 257 } 258 ALOGV(" colors=[%s]", nice.c_str()); 259 } 260 { 261 Vector<MediaCodecInfo::ProfileLevel> profileLevels; 262 caps->getSupportedProfileLevels(&profileLevels); 263 AString nice; 264 for (size_t ix = 0; ix < profileLevels.size(); ix++) { 265 if (ix > 0) { 266 nice.append(", "); 267 } 268 const MediaCodecInfo::ProfileLevel &pl = 269 profileLevels.itemAt(ix); 270 nice.append(pl.mProfile); 271 nice.append("/"); 272 nice.append(pl.mLevel); 273 } 274 ALOGV(" levels=[%s]", nice.c_str()); 275 } 276 { 277 AString quirks; 278 for (size_t ix = 0; ix < info.mQuirks.size(); ix++) { 279 if (ix > 0) { 280 quirks.append(", "); 281 } 282 quirks.append(info.mQuirks[ix]); 283 } 284 ALOGV(" quirks=[%s]", quirks.c_str()); 285 } 286 } 287#endif 288 } 289 } 290 291#if 0 292 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 293 const CodecInfo &info = mCodecInfos.itemAt(i); 294 295 AString line = info.mName; 296 line.append(" supports "); 297 for (size_t j = 0; j < mTypes.size(); ++j) { 298 uint32_t value = mTypes.valueAt(j); 299 300 if (info.mTypes & (1ul << value)) { 301 line.append(mTypes.keyAt(j)); 302 line.append(" "); 303 } 304 } 305 306 ALOGI("%s", line.c_str()); 307 } 308#endif 309} 310 311MediaCodecList::~MediaCodecList() { 312} 313 314status_t MediaCodecList::initCheck() const { 315 return mInitCheck; 316} 317 318void MediaCodecList::parseXMLFile(const char *path) { 319 FILE *file = fopen(path, "r"); 320 321 if (file == NULL) { 322 ALOGW("unable to open media codecs configuration xml file: %s", path); 323 mInitCheck = NAME_NOT_FOUND; 324 return; 325 } 326 327 XML_Parser parser = ::XML_ParserCreate(NULL); 328 CHECK(parser != NULL); 329 330 ::XML_SetUserData(parser, this); 331 ::XML_SetElementHandler( 332 parser, StartElementHandlerWrapper, EndElementHandlerWrapper); 333 334 const int BUFF_SIZE = 512; 335 while (mInitCheck == OK) { 336 void *buff = ::XML_GetBuffer(parser, BUFF_SIZE); 337 if (buff == NULL) { 338 ALOGE("failed in call to XML_GetBuffer()"); 339 mInitCheck = UNKNOWN_ERROR; 340 break; 341 } 342 343 int bytes_read = ::fread(buff, 1, BUFF_SIZE, file); 344 if (bytes_read < 0) { 345 ALOGE("failed in call to read"); 346 mInitCheck = ERROR_IO; 347 break; 348 } 349 350 XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0); 351 if (status != XML_STATUS_OK) { 352 ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser))); 353 mInitCheck = ERROR_MALFORMED; 354 break; 355 } 356 357 if (bytes_read == 0) { 358 break; 359 } 360 } 361 362 ::XML_ParserFree(parser); 363 364 fclose(file); 365 file = NULL; 366} 367 368// static 369void MediaCodecList::StartElementHandlerWrapper( 370 void *me, const char *name, const char **attrs) { 371 static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs); 372} 373 374// static 375void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) { 376 static_cast<MediaCodecList *>(me)->endElementHandler(name); 377} 378 379status_t MediaCodecList::includeXMLFile(const char **attrs) { 380 const char *href = NULL; 381 size_t i = 0; 382 while (attrs[i] != NULL) { 383 if (!strcmp(attrs[i], "href")) { 384 if (attrs[i + 1] == NULL) { 385 return -EINVAL; 386 } 387 href = attrs[i + 1]; 388 ++i; 389 } else { 390 return -EINVAL; 391 } 392 ++i; 393 } 394 395 // For security reasons and for simplicity, file names can only contain 396 // [a-zA-Z0-9_.] and must start with media_codecs_ and end with .xml 397 for (i = 0; href[i] != '\0'; i++) { 398 if (href[i] == '.' || href[i] == '_' || 399 (href[i] >= '0' && href[i] <= '9') || 400 (href[i] >= 'A' && href[i] <= 'Z') || 401 (href[i] >= 'a' && href[i] <= 'z')) { 402 continue; 403 } 404 ALOGE("invalid include file name: %s", href); 405 return -EINVAL; 406 } 407 408 AString filename = href; 409 if (!filename.startsWith("media_codecs_") || 410 !filename.endsWith(".xml")) { 411 ALOGE("invalid include file name: %s", href); 412 return -EINVAL; 413 } 414 filename.insert(mHrefBase, 0); 415 416 parseXMLFile(filename.c_str()); 417 return mInitCheck; 418} 419 420void MediaCodecList::startElementHandler( 421 const char *name, const char **attrs) { 422 if (mInitCheck != OK) { 423 return; 424 } 425 426 bool inType = true; 427 428 if (!strcmp(name, "Include")) { 429 mInitCheck = includeXMLFile(attrs); 430 if (mInitCheck == OK) { 431 mPastSections.push(mCurrentSection); 432 mCurrentSection = SECTION_INCLUDE; 433 } 434 ++mDepth; 435 return; 436 } 437 438 switch (mCurrentSection) { 439 case SECTION_TOPLEVEL: 440 { 441 if (!strcmp(name, "Decoders")) { 442 mCurrentSection = SECTION_DECODERS; 443 } else if (!strcmp(name, "Encoders")) { 444 mCurrentSection = SECTION_ENCODERS; 445 } else if (!strcmp(name, "Settings")) { 446 mCurrentSection = SECTION_SETTINGS; 447 } 448 break; 449 } 450 451 case SECTION_SETTINGS: 452 { 453 if (!strcmp(name, "Setting")) { 454 mInitCheck = addSettingFromAttributes(attrs); 455 } 456 break; 457 } 458 459 case SECTION_DECODERS: 460 { 461 if (!strcmp(name, "MediaCodec")) { 462 mInitCheck = 463 addMediaCodecFromAttributes(false /* encoder */, attrs); 464 465 mCurrentSection = SECTION_DECODER; 466 } 467 break; 468 } 469 470 case SECTION_ENCODERS: 471 { 472 if (!strcmp(name, "MediaCodec")) { 473 mInitCheck = 474 addMediaCodecFromAttributes(true /* encoder */, attrs); 475 476 mCurrentSection = SECTION_ENCODER; 477 } 478 break; 479 } 480 481 case SECTION_DECODER: 482 case SECTION_ENCODER: 483 { 484 if (!strcmp(name, "Quirk")) { 485 mInitCheck = addQuirk(attrs); 486 } else if (!strcmp(name, "Type")) { 487 mInitCheck = addTypeFromAttributes(attrs); 488 mCurrentSection = 489 (mCurrentSection == SECTION_DECODER 490 ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE); 491 } 492 } 493 inType = false; 494 // fall through 495 496 case SECTION_DECODER_TYPE: 497 case SECTION_ENCODER_TYPE: 498 { 499 // ignore limits and features specified outside of type 500 bool outside = !inType && !mCurrentInfo->mHasSoleMime; 501 if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) { 502 ALOGW("ignoring %s specified outside of a Type", name); 503 } else if (!strcmp(name, "Limit")) { 504 mInitCheck = addLimit(attrs); 505 } else if (!strcmp(name, "Feature")) { 506 mInitCheck = addFeature(attrs); 507 } 508 break; 509 } 510 511 default: 512 break; 513 } 514 515 ++mDepth; 516} 517 518void MediaCodecList::endElementHandler(const char *name) { 519 if (mInitCheck != OK) { 520 return; 521 } 522 523 switch (mCurrentSection) { 524 case SECTION_SETTINGS: 525 { 526 if (!strcmp(name, "Settings")) { 527 mCurrentSection = SECTION_TOPLEVEL; 528 } 529 break; 530 } 531 532 case SECTION_DECODERS: 533 { 534 if (!strcmp(name, "Decoders")) { 535 mCurrentSection = SECTION_TOPLEVEL; 536 } 537 break; 538 } 539 540 case SECTION_ENCODERS: 541 { 542 if (!strcmp(name, "Encoders")) { 543 mCurrentSection = SECTION_TOPLEVEL; 544 } 545 break; 546 } 547 548 case SECTION_DECODER_TYPE: 549 case SECTION_ENCODER_TYPE: 550 { 551 if (!strcmp(name, "Type")) { 552 mCurrentSection = 553 (mCurrentSection == SECTION_DECODER_TYPE 554 ? SECTION_DECODER : SECTION_ENCODER); 555 556 mCurrentInfo->complete(); 557 } 558 break; 559 } 560 561 case SECTION_DECODER: 562 { 563 if (!strcmp(name, "MediaCodec")) { 564 mCurrentSection = SECTION_DECODERS; 565 mCurrentInfo->complete(); 566 mCurrentInfo = NULL; 567 } 568 break; 569 } 570 571 case SECTION_ENCODER: 572 { 573 if (!strcmp(name, "MediaCodec")) { 574 mCurrentSection = SECTION_ENCODERS; 575 mCurrentInfo->complete();; 576 mCurrentInfo = NULL; 577 } 578 break; 579 } 580 581 case SECTION_INCLUDE: 582 { 583 if (!strcmp(name, "Include") && mPastSections.size() > 0) { 584 mCurrentSection = mPastSections.top(); 585 mPastSections.pop(); 586 } 587 break; 588 } 589 590 default: 591 break; 592 } 593 594 --mDepth; 595} 596 597status_t MediaCodecList::addSettingFromAttributes(const char **attrs) { 598 const char *name = NULL; 599 const char *value = NULL; 600 const char *update = NULL; 601 602 size_t i = 0; 603 while (attrs[i] != NULL) { 604 if (!strcmp(attrs[i], "name")) { 605 if (attrs[i + 1] == NULL) { 606 return -EINVAL; 607 } 608 name = attrs[i + 1]; 609 ++i; 610 } else if (!strcmp(attrs[i], "value")) { 611 if (attrs[i + 1] == NULL) { 612 return -EINVAL; 613 } 614 value = attrs[i + 1]; 615 ++i; 616 } else if (!strcmp(attrs[i], "update")) { 617 if (attrs[i + 1] == NULL) { 618 return -EINVAL; 619 } 620 update = attrs[i + 1]; 621 ++i; 622 } else { 623 return -EINVAL; 624 } 625 626 ++i; 627 } 628 629 if (name == NULL || value == NULL) { 630 return -EINVAL; 631 } 632 633 mUpdate = (update != NULL) && parseBoolean(update); 634 if (mUpdate != mGlobalSettings->contains(name)) { 635 return -EINVAL; 636 } 637 638 mGlobalSettings->setString(name, value); 639 return OK; 640} 641 642void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) { 643 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 644 if (AString(name) == mCodecInfos[i]->getCodecName()) { 645 if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) { 646 ALOGW("Overrides with an unexpected mime %s", type); 647 // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the 648 // overrides we don't want. 649 mCurrentInfo = new MediaCodecInfo(name, encoder, type); 650 } else { 651 mCurrentInfo = mCodecInfos.editItemAt(i); 652 mCurrentInfo->updateMime(type); // to set the current cap 653 } 654 return; 655 } 656 } 657 mCurrentInfo = new MediaCodecInfo(name, encoder, type); 658 // The next step involves trying to load the codec, which may 659 // fail. Only list the codec if this succeeds. 660 // However, keep mCurrentInfo object around until parsing 661 // of full codec info is completed. 662 if (initializeCapabilities(type) == OK) { 663 mCodecInfos.push_back(mCurrentInfo); 664 } 665} 666 667status_t MediaCodecList::addMediaCodecFromAttributes( 668 bool encoder, const char **attrs) { 669 const char *name = NULL; 670 const char *type = NULL; 671 const char *update = NULL; 672 673 size_t i = 0; 674 while (attrs[i] != NULL) { 675 if (!strcmp(attrs[i], "name")) { 676 if (attrs[i + 1] == NULL) { 677 return -EINVAL; 678 } 679 name = attrs[i + 1]; 680 ++i; 681 } else if (!strcmp(attrs[i], "type")) { 682 if (attrs[i + 1] == NULL) { 683 return -EINVAL; 684 } 685 type = attrs[i + 1]; 686 ++i; 687 } else if (!strcmp(attrs[i], "update")) { 688 if (attrs[i + 1] == NULL) { 689 return -EINVAL; 690 } 691 update = attrs[i + 1]; 692 ++i; 693 } else { 694 return -EINVAL; 695 } 696 697 ++i; 698 } 699 700 if (name == NULL) { 701 return -EINVAL; 702 } 703 704 mUpdate = (update != NULL) && parseBoolean(update); 705 ssize_t index = -1; 706 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 707 if (AString(name) == mCodecInfos[i]->getCodecName()) { 708 index = i; 709 } 710 } 711 if (mUpdate != (index >= 0)) { 712 return -EINVAL; 713 } 714 715 if (index >= 0) { 716 // existing codec 717 mCurrentInfo = mCodecInfos.editItemAt(index); 718 if (type != NULL) { 719 // existing type 720 if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) { 721 return -EINVAL; 722 } 723 mCurrentInfo->updateMime(type); 724 } 725 } else { 726 // new codec 727 mCurrentInfo = new MediaCodecInfo(name, encoder, type); 728 // The next step involves trying to load the codec, which may 729 // fail. Only list the codec if this succeeds. 730 // However, keep mCurrentInfo object around until parsing 731 // of full codec info is completed. 732 if (initializeCapabilities(type) == OK) { 733 mCodecInfos.push_back(mCurrentInfo); 734 } 735 } 736 737 return OK; 738} 739 740status_t MediaCodecList::initializeCapabilities(const char *type) { 741 if (type == NULL) { 742 return OK; 743 } 744 745 ALOGV("initializeCapabilities %s:%s", 746 mCurrentInfo->mName.c_str(), type); 747 748 CodecCapabilities caps; 749 status_t err = QueryCodec( 750 mOMX, 751 mCurrentInfo->mName.c_str(), 752 type, 753 mCurrentInfo->mIsEncoder, 754 &caps); 755 if (err != OK) { 756 return err; 757 } 758 759 return mCurrentInfo->initializeCapabilities(caps); 760} 761 762status_t MediaCodecList::addQuirk(const char **attrs) { 763 const char *name = NULL; 764 765 size_t i = 0; 766 while (attrs[i] != NULL) { 767 if (!strcmp(attrs[i], "name")) { 768 if (attrs[i + 1] == NULL) { 769 return -EINVAL; 770 } 771 name = attrs[i + 1]; 772 ++i; 773 } else { 774 return -EINVAL; 775 } 776 777 ++i; 778 } 779 780 if (name == NULL) { 781 return -EINVAL; 782 } 783 784 mCurrentInfo->addQuirk(name); 785 return OK; 786} 787 788status_t MediaCodecList::addTypeFromAttributes(const char **attrs) { 789 const char *name = NULL; 790 const char *update = NULL; 791 792 size_t i = 0; 793 while (attrs[i] != NULL) { 794 if (!strcmp(attrs[i], "name")) { 795 if (attrs[i + 1] == NULL) { 796 return -EINVAL; 797 } 798 name = attrs[i + 1]; 799 ++i; 800 } else if (!strcmp(attrs[i], "update")) { 801 if (attrs[i + 1] == NULL) { 802 return -EINVAL; 803 } 804 update = attrs[i + 1]; 805 ++i; 806 } else { 807 return -EINVAL; 808 } 809 810 ++i; 811 } 812 813 if (name == NULL) { 814 return -EINVAL; 815 } 816 817 bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL); 818 if (mUpdate != isExistingType) { 819 return -EINVAL; 820 } 821 822 status_t ret; 823 if (mUpdate) { 824 ret = mCurrentInfo->updateMime(name); 825 } else { 826 ret = mCurrentInfo->addMime(name); 827 } 828 829 if (ret != OK) { 830 return ret; 831 } 832 833 // The next step involves trying to load the codec, which may 834 // fail. Handle this gracefully (by not reporting such mime). 835 if (!mUpdate && initializeCapabilities(name) != OK) { 836 mCurrentInfo->removeMime(name); 837 } 838 return OK; 839} 840 841// legacy method for non-advanced codecs 842ssize_t MediaCodecList::findCodecByType( 843 const char *type, bool encoder, size_t startIndex) const { 844 static const char *advancedFeatures[] = { 845 "feature-secure-playback", 846 "feature-tunneled-playback", 847 }; 848 849 size_t numCodecs = mCodecInfos.size(); 850 for (; startIndex < numCodecs; ++startIndex) { 851 const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get(); 852 853 if (info.isEncoder() != encoder) { 854 continue; 855 } 856 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type); 857 if (capabilities == NULL) { 858 continue; 859 } 860 const sp<AMessage> &details = capabilities->getDetails(); 861 862 int32_t required; 863 bool isAdvanced = false; 864 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) { 865 if (details->findInt32(advancedFeatures[ix], &required) && 866 required != 0) { 867 isAdvanced = true; 868 break; 869 } 870 } 871 872 if (!isAdvanced) { 873 return startIndex; 874 } 875 } 876 877 return -ENOENT; 878} 879 880static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) { 881 ALOGE("limit '%s' with %s'%s' attribute", name.c_str(), 882 (found ? "" : "no "), attr); 883 return -EINVAL; 884} 885 886static status_t limitError(AString name, const char *msg) { 887 ALOGE("limit '%s' %s", name.c_str(), msg); 888 return -EINVAL; 889} 890 891static status_t limitInvalidAttr(AString name, const char *attr, AString value) { 892 ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(), 893 attr, value.c_str()); 894 return -EINVAL; 895} 896 897status_t MediaCodecList::addLimit(const char **attrs) { 898 sp<AMessage> msg = new AMessage(); 899 900 size_t i = 0; 901 while (attrs[i] != NULL) { 902 if (attrs[i + 1] == NULL) { 903 return -EINVAL; 904 } 905 906 // attributes with values 907 if (!strcmp(attrs[i], "name") 908 || !strcmp(attrs[i], "default") 909 || !strcmp(attrs[i], "in") 910 || !strcmp(attrs[i], "max") 911 || !strcmp(attrs[i], "min") 912 || !strcmp(attrs[i], "range") 913 || !strcmp(attrs[i], "ranges") 914 || !strcmp(attrs[i], "scale") 915 || !strcmp(attrs[i], "value")) { 916 msg->setString(attrs[i], attrs[i + 1]); 917 ++i; 918 } else { 919 return -EINVAL; 920 } 921 ++i; 922 } 923 924 AString name; 925 if (!msg->findString("name", &name)) { 926 ALOGE("limit with no 'name' attribute"); 927 return -EINVAL; 928 } 929 930 // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio, 931 // measured-frame-rate, measured-blocks-per-second: range 932 // quality: range + default + [scale] 933 // complexity: range + default 934 bool found; 935 936 if (name == "aspect-ratio" || name == "bitrate" || name == "block-count" 937 || name == "blocks-per-second" || name == "complexity" 938 || name == "frame-rate" || name == "quality" || name == "size" 939 || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) { 940 AString min, max; 941 if (msg->findString("min", &min) && msg->findString("max", &max)) { 942 min.append("-"); 943 min.append(max); 944 if (msg->contains("range") || msg->contains("value")) { 945 return limitError(name, "has 'min' and 'max' as well as 'range' or " 946 "'value' attributes"); 947 } 948 msg->setString("range", min); 949 } else if (msg->contains("min") || msg->contains("max")) { 950 return limitError(name, "has only 'min' or 'max' attribute"); 951 } else if (msg->findString("value", &max)) { 952 min = max; 953 min.append("-"); 954 min.append(max); 955 if (msg->contains("range")) { 956 return limitError(name, "has both 'range' and 'value' attributes"); 957 } 958 msg->setString("range", min); 959 } 960 961 AString range, scale = "linear", def, in_; 962 if (!msg->findString("range", &range)) { 963 return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes"); 964 } 965 966 if ((name == "quality" || name == "complexity") ^ 967 (found = msg->findString("default", &def))) { 968 return limitFoundMissingAttr(name, "default", found); 969 } 970 if (name != "quality" && msg->findString("scale", &scale)) { 971 return limitFoundMissingAttr(name, "scale"); 972 } 973 if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) { 974 return limitFoundMissingAttr(name, "in", found); 975 } 976 977 if (name == "aspect-ratio") { 978 if (!(in_ == "pixels") && !(in_ == "blocks")) { 979 return limitInvalidAttr(name, "in", in_); 980 } 981 in_.erase(5, 1); // (pixel|block)-aspect-ratio 982 in_.append("-"); 983 in_.append(name); 984 name = in_; 985 } 986 if (name == "quality") { 987 mCurrentInfo->addDetail("quality-scale", scale); 988 } 989 if (name == "quality" || name == "complexity") { 990 AString tag = name; 991 tag.append("-default"); 992 mCurrentInfo->addDetail(tag, def); 993 } 994 AString tag = name; 995 tag.append("-range"); 996 mCurrentInfo->addDetail(tag, range); 997 } else { 998 AString max, value, ranges; 999 if (msg->contains("default")) { 1000 return limitFoundMissingAttr(name, "default"); 1001 } else if (msg->contains("in")) { 1002 return limitFoundMissingAttr(name, "in"); 1003 } else if ((name == "channel-count" || name == "concurrent-instances") ^ 1004 (found = msg->findString("max", &max))) { 1005 return limitFoundMissingAttr(name, "max", found); 1006 } else if (msg->contains("min")) { 1007 return limitFoundMissingAttr(name, "min"); 1008 } else if (msg->contains("range")) { 1009 return limitFoundMissingAttr(name, "range"); 1010 } else if ((name == "sample-rate") ^ 1011 (found = msg->findString("ranges", &ranges))) { 1012 return limitFoundMissingAttr(name, "ranges", found); 1013 } else if (msg->contains("scale")) { 1014 return limitFoundMissingAttr(name, "scale"); 1015 } else if ((name == "alignment" || name == "block-size") ^ 1016 (found = msg->findString("value", &value))) { 1017 return limitFoundMissingAttr(name, "value", found); 1018 } 1019 1020 if (max.size()) { 1021 AString tag = "max-"; 1022 tag.append(name); 1023 mCurrentInfo->addDetail(tag, max); 1024 } else if (value.size()) { 1025 mCurrentInfo->addDetail(name, value); 1026 } else if (ranges.size()) { 1027 AString tag = name; 1028 tag.append("-ranges"); 1029 mCurrentInfo->addDetail(tag, ranges); 1030 } else { 1031 ALOGW("Ignoring unrecognized limit '%s'", name.c_str()); 1032 } 1033 } 1034 return OK; 1035} 1036 1037status_t MediaCodecList::addFeature(const char **attrs) { 1038 size_t i = 0; 1039 const char *name = NULL; 1040 int32_t optional = -1; 1041 int32_t required = -1; 1042 const char *value = NULL; 1043 1044 while (attrs[i] != NULL) { 1045 if (attrs[i + 1] == NULL) { 1046 return -EINVAL; 1047 } 1048 1049 // attributes with values 1050 if (!strcmp(attrs[i], "name")) { 1051 name = attrs[i + 1]; 1052 ++i; 1053 } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) { 1054 int value = (int)parseBoolean(attrs[i + 1]); 1055 if (!strcmp(attrs[i], "optional")) { 1056 optional = value; 1057 } else { 1058 required = value; 1059 } 1060 ++i; 1061 } else if (!strcmp(attrs[i], "value")) { 1062 value = attrs[i + 1]; 1063 ++i; 1064 } else { 1065 return -EINVAL; 1066 } 1067 ++i; 1068 } 1069 if (name == NULL) { 1070 ALOGE("feature with no 'name' attribute"); 1071 return -EINVAL; 1072 } 1073 1074 if (optional == required && optional != -1) { 1075 ALOGE("feature '%s' is both/neither optional and required", name); 1076 return -EINVAL; 1077 } 1078 1079 if ((optional != -1 || required != -1) && (value != NULL)) { 1080 ALOGE("feature '%s' has both a value and optional/required attribute", name); 1081 return -EINVAL; 1082 } 1083 1084 if (value != NULL) { 1085 mCurrentInfo->addFeature(name, value); 1086 } else { 1087 mCurrentInfo->addFeature(name, (required == 1) || (optional == 0)); 1088 } 1089 return OK; 1090} 1091 1092ssize_t MediaCodecList::findCodecByName(const char *name) const { 1093 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 1094 const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get(); 1095 1096 if (info.mName == name) { 1097 return i; 1098 } 1099 } 1100 1101 return -ENOENT; 1102} 1103 1104size_t MediaCodecList::countCodecs() const { 1105 return mCodecInfos.size(); 1106} 1107 1108const sp<AMessage> MediaCodecList::getGlobalSettings() const { 1109 return mGlobalSettings; 1110} 1111 1112} // namespace android 1113