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