/* * Copyright 2012, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "MediaCodecList" #include #include "MediaCodecListOverrides.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { namespace { Mutex sInitMutex; Mutex sRemoteInitMutex; constexpr const char* kProfilingResults = MediaCodecsXmlParser::defaultProfilingResultsXmlPath; bool isProfilingNeeded() { int8_t value = property_get_bool("debug.stagefright.profilecodec", 0); if (value == 0) { return false; } bool profilingNeeded = true; FILE *resultsFile = fopen(kProfilingResults, "r"); if (resultsFile) { AString currentVersion = getProfilingVersionString(); size_t currentVersionSize = currentVersion.size(); char *versionString = new char[currentVersionSize + 1]; fgets(versionString, currentVersionSize + 1, resultsFile); if (strcmp(versionString, currentVersion.c_str()) == 0) { // profiling result up to date profilingNeeded = false; } fclose(resultsFile); delete[] versionString; } return profilingNeeded; } OmxInfoBuilder sOmxInfoBuilder{true /* allowSurfaceEncoders */}; OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */}; Mutex sCodec2InfoBuilderMutex; std::unique_ptr sCodec2InfoBuilder; MediaCodecListBuilderBase *GetCodec2InfoBuilder() { Mutex::Autolock _l(sCodec2InfoBuilderMutex); if (!sCodec2InfoBuilder) { sCodec2InfoBuilder.reset(new Codec2InfoBuilder); } return sCodec2InfoBuilder.get(); } std::vector GetBuilders() { std::vector builders; // if plugin provides the input surface, we cannot use OMX video encoders. // In this case, rely on plugin to provide list of OMX codecs that are usable. sp surfaceTest = CCodec::CreateInputSurface(); if (surfaceTest == nullptr) { ALOGD("Allowing all OMX codecs"); builders.push_back(&sOmxInfoBuilder); } else { ALOGD("Allowing only non-surface-encoder OMX codecs"); builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder); } builders.push_back(GetCodec2InfoBuilder()); return builders; } } // unnamed namespace // static sp MediaCodecList::sCodecList; // static void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) { ALOGV("Enter profilerThreadWrapper."); remove(kProfilingResults); // remove previous result so that it won't be loaded to // the new MediaCodecList sp codecList(new MediaCodecList(GetBuilders())); if (codecList->initCheck() != OK) { ALOGW("Failed to create a new MediaCodecList, skipping codec profiling."); return nullptr; } const auto& infos = codecList->mCodecInfos; ALOGV("Codec profiling started."); profileCodecs(infos, kProfilingResults); ALOGV("Codec profiling completed."); codecList = new MediaCodecList(GetBuilders()); if (codecList->initCheck() != OK) { ALOGW("Failed to parse profiling results."); return nullptr; } { Mutex::Autolock autoLock(sInitMutex); sCodecList = codecList; } return nullptr; } // static sp MediaCodecList::getLocalInstance() { Mutex::Autolock autoLock(sInitMutex); if (sCodecList == nullptr) { MediaCodecList *codecList = new MediaCodecList(GetBuilders()); if (codecList->initCheck() == OK) { sCodecList = codecList; if (isProfilingNeeded()) { ALOGV("Codec profiling needed, will be run in separated thread."); pthread_t profiler; if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) { ALOGW("Failed to create thread for codec profiling."); } } } else { // failure to initialize may be temporary. retry on next call. delete codecList; } } return sCodecList; } sp MediaCodecList::sRemoteList; sp MediaCodecList::sBinderDeathObserver; sp MediaCodecList::sMediaPlayer; // kept since linked to death void MediaCodecList::BinderDeathObserver::binderDied(const wp &who __unused) { Mutex::Autolock _l(sRemoteInitMutex); sRemoteList.clear(); sBinderDeathObserver.clear(); } // static sp MediaCodecList::getInstance() { Mutex::Autolock _l(sRemoteInitMutex); if (sRemoteList == nullptr) { sMediaPlayer = defaultServiceManager()->getService(String16("media.player")); sp service = interface_cast(sMediaPlayer); if (service.get() != nullptr) { sRemoteList = service->getCodecList(); if (sRemoteList != nullptr) { sBinderDeathObserver = new BinderDeathObserver(); sMediaPlayer->linkToDeath(sBinderDeathObserver.get()); } } if (sRemoteList == nullptr) { // if failed to get remote list, create local list sRemoteList = getLocalInstance(); } } return sRemoteList; } MediaCodecList::MediaCodecList(std::vector builders) { mGlobalSettings = new AMessage(); mCodecInfos.clear(); MediaCodecListWriter writer; for (MediaCodecListBuilderBase *builder : builders) { if (builder == nullptr) { ALOGD("ignored a null builder"); continue; } mInitCheck = builder->buildMediaCodecList(&writer); if (mInitCheck != OK) { break; } } writer.writeGlobalSettings(mGlobalSettings); writer.writeCodecInfos(&mCodecInfos); std::stable_sort( mCodecInfos.begin(), mCodecInfos.end(), [](const sp &info1, const sp &info2) { // null is lowest return info1 == nullptr || (info2 != nullptr && info1->getRank() < info2->getRank()); }); // remove duplicate entries bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true); if (dedupe) { std::set codecsSeen; for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) { std::string codecName = (*it)->getCodecName(); if (codecsSeen.count(codecName) == 0) { codecsSeen.emplace(codecName); it++; } else { it = mCodecInfos.erase(it); } } } } MediaCodecList::~MediaCodecList() { } status_t MediaCodecList::initCheck() const { return mInitCheck; } // legacy method for non-advanced codecs ssize_t MediaCodecList::findCodecByType( const char *type, bool encoder, size_t startIndex) const { static const char *advancedFeatures[] = { "feature-secure-playback", "feature-tunneled-playback", }; size_t numCodecInfos = mCodecInfos.size(); for (; startIndex < numCodecInfos; ++startIndex) { const MediaCodecInfo &info = *mCodecInfos[startIndex]; if (info.isEncoder() != encoder) { continue; } sp capabilities = info.getCapabilitiesFor(type); if (capabilities == nullptr) { continue; } const sp &details = capabilities->getDetails(); int32_t required; bool isAdvanced = false; for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) { if (details->findInt32(advancedFeatures[ix], &required) && required != 0) { isAdvanced = true; break; } } if (!isAdvanced) { return startIndex; } } return -ENOENT; } ssize_t MediaCodecList::findCodecByName(const char *name) const { Vector aliases; for (size_t i = 0; i < mCodecInfos.size(); ++i) { if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) { return i; } mCodecInfos[i]->getAliases(&aliases); for (const AString &alias : aliases) { if (alias == name) { return i; } } } return -ENOENT; } size_t MediaCodecList::countCodecs() const { return mCodecInfos.size(); } const sp MediaCodecList::getGlobalSettings() const { return mGlobalSettings; } //static bool MediaCodecList::isSoftwareCodec(const AString &componentName) { return componentName.startsWithIgnoreCase("OMX.google.") || componentName.startsWithIgnoreCase("c2.android.") || (!componentName.startsWithIgnoreCase("OMX.") && !componentName.startsWithIgnoreCase("c2.")); } static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) { // sort order 1: software codecs are first (lower) bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1); bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2); if (isSoftwareCodec1 != isSoftwareCodec2) { return isSoftwareCodec2 - isSoftwareCodec1; } // sort order 2: Codec 2.0 codecs are first (lower) bool isC2_1 = name1->startsWithIgnoreCase("c2."); bool isC2_2 = name2->startsWithIgnoreCase("c2."); if (isC2_1 != isC2_2) { return isC2_2 - isC2_1; } // sort order 3: OMX codecs are first (lower) bool isOMX1 = name1->startsWithIgnoreCase("OMX."); bool isOMX2 = name2->startsWithIgnoreCase("OMX."); return isOMX2 - isOMX1; } //static void MediaCodecList::findMatchingCodecs( const char *mime, bool encoder, uint32_t flags, Vector *matches) { matches->clear(); const sp list = getInstance(); if (list == nullptr) { return; } size_t index = 0; for (;;) { ssize_t matchIndex = list->findCodecByType(mime, encoder, index); if (matchIndex < 0) { break; } index = matchIndex + 1; const sp info = list->getCodecInfo(matchIndex); CHECK(info != nullptr); AString componentName = info->getCodecName(); if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) { ALOGV("skipping SW codec '%s'", componentName.c_str()); } else { matches->push(componentName); ALOGV("matching '%s'", componentName.c_str()); } } if (flags & kPreferSoftwareCodecs || property_get_bool("debug.stagefright.swcodec", false)) { matches->sort(compareSoftwareCodecsFirst); } } } // namespace android