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