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