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