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