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