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