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 #include "StagefrightPluginLoader.h" 23 24 #include <binder/IServiceManager.h> 25 26 #include <media/IMediaCodecList.h> 27 #include <media/IMediaPlayerService.h> 28 #include <media/MediaCodecInfo.h> 29 30 #include <media/stagefright/foundation/ADebug.h> 31 #include <media/stagefright/foundation/AMessage.h> 32 #include <media/stagefright/foundation/MediaDefs.h> 33 #include <media/stagefright/MediaCodecList.h> 34 #include <media/stagefright/MediaErrors.h> 35 #include <media/stagefright/OmxInfoBuilder.h> 36 #include <media/stagefright/omx/OMXUtils.h> 37 #include <xmlparser/include/media/stagefright/xmlparser/MediaCodecsXmlParser.h> 38 39 #include <sys/stat.h> 40 #include <utils/threads.h> 41 42 #include <cutils/properties.h> 43 44 #include <algorithm> 45 46 namespace android { 47 48 namespace { 49 50 Mutex sInitMutex; 51 52 Mutex sRemoteInitMutex; 53 54 constexpr const char* kProfilingResults = 55 MediaCodecsXmlParser::defaultProfilingResultsXmlPath; 56 57 bool isProfilingNeeded() { 58 int8_t value = property_get_bool("debug.stagefright.profilecodec", 0); 59 if (value == 0) { 60 return false; 61 } 62 63 bool profilingNeeded = true; 64 FILE *resultsFile = fopen(kProfilingResults, "r"); 65 if (resultsFile) { 66 AString currentVersion = getProfilingVersionString(); 67 size_t currentVersionSize = currentVersion.size(); 68 char *versionString = new char[currentVersionSize + 1]; 69 fgets(versionString, currentVersionSize + 1, resultsFile); 70 if (strcmp(versionString, currentVersion.c_str()) == 0) { 71 // profiling result up to date 72 profilingNeeded = false; 73 } 74 fclose(resultsFile); 75 delete[] versionString; 76 } 77 return profilingNeeded; 78 } 79 80 OmxInfoBuilder sOmxInfoBuilder; 81 82 Mutex sCodec2InfoBuilderMutex; 83 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder; 84 85 MediaCodecListBuilderBase *GetCodec2InfoBuilder() { 86 Mutex::Autolock _l(sCodec2InfoBuilderMutex); 87 if (!sCodec2InfoBuilder) { 88 sCodec2InfoBuilder.reset( 89 StagefrightPluginLoader::GetCCodecInstance()->createBuilder()); 90 } 91 return sCodec2InfoBuilder.get(); 92 } 93 94 std::vector<MediaCodecListBuilderBase *> GetBuilders() { 95 std::vector<MediaCodecListBuilderBase *> builders; 96 // if plugin provides the input surface, we cannot use OMX video encoders. 97 // In this case, rely on plugin to provide list of OMX codecs that are usable. 98 sp<PersistentSurface> surfaceTest = 99 StagefrightPluginLoader::GetCCodecInstance()->createInputSurface(); 100 if (surfaceTest == nullptr) { 101 builders.push_back(&sOmxInfoBuilder); 102 } 103 builders.push_back(GetCodec2InfoBuilder()); 104 return builders; 105 } 106 107 } // unnamed namespace 108 109 // static 110 sp<IMediaCodecList> MediaCodecList::sCodecList; 111 112 // static 113 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) { 114 ALOGV("Enter profilerThreadWrapper."); 115 remove(kProfilingResults); // remove previous result so that it won't be loaded to 116 // the new MediaCodecList 117 sp<MediaCodecList> codecList(new MediaCodecList(GetBuilders())); 118 if (codecList->initCheck() != OK) { 119 ALOGW("Failed to create a new MediaCodecList, skipping codec profiling."); 120 return nullptr; 121 } 122 123 const auto& infos = codecList->mCodecInfos; 124 ALOGV("Codec profiling started."); 125 profileCodecs(infos, kProfilingResults); 126 ALOGV("Codec profiling completed."); 127 codecList = new MediaCodecList(GetBuilders()); 128 if (codecList->initCheck() != OK) { 129 ALOGW("Failed to parse profiling results."); 130 return nullptr; 131 } 132 133 { 134 Mutex::Autolock autoLock(sInitMutex); 135 sCodecList = codecList; 136 } 137 return nullptr; 138 } 139 140 // static 141 sp<IMediaCodecList> MediaCodecList::getLocalInstance() { 142 Mutex::Autolock autoLock(sInitMutex); 143 144 if (sCodecList == nullptr) { 145 MediaCodecList *codecList = new MediaCodecList(GetBuilders()); 146 if (codecList->initCheck() == OK) { 147 sCodecList = codecList; 148 149 if (isProfilingNeeded()) { 150 ALOGV("Codec profiling needed, will be run in separated thread."); 151 pthread_t profiler; 152 if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) { 153 ALOGW("Failed to create thread for codec profiling."); 154 } 155 } 156 } else { 157 // failure to initialize may be temporary. retry on next call. 158 delete codecList; 159 } 160 } 161 162 return sCodecList; 163 } 164 165 sp<IMediaCodecList> MediaCodecList::sRemoteList; 166 167 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver; 168 169 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) { 170 Mutex::Autolock _l(sRemoteInitMutex); 171 sRemoteList.clear(); 172 sBinderDeathObserver.clear(); 173 } 174 175 // static 176 sp<IMediaCodecList> MediaCodecList::getInstance() { 177 Mutex::Autolock _l(sRemoteInitMutex); 178 if (sRemoteList == nullptr) { 179 sp<IBinder> binder = 180 defaultServiceManager()->getService(String16("media.player")); 181 sp<IMediaPlayerService> service = 182 interface_cast<IMediaPlayerService>(binder); 183 if (service.get() != nullptr) { 184 sRemoteList = service->getCodecList(); 185 if (sRemoteList != nullptr) { 186 sBinderDeathObserver = new BinderDeathObserver(); 187 binder->linkToDeath(sBinderDeathObserver.get()); 188 } 189 } 190 if (sRemoteList == nullptr) { 191 // if failed to get remote list, create local list 192 sRemoteList = getLocalInstance(); 193 } 194 } 195 return sRemoteList; 196 } 197 198 MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) { 199 mGlobalSettings = new AMessage(); 200 mCodecInfos.clear(); 201 MediaCodecListWriter writer; 202 for (MediaCodecListBuilderBase *builder : builders) { 203 if (builder == nullptr) { 204 ALOGD("ignored a null builder"); 205 continue; 206 } 207 mInitCheck = builder->buildMediaCodecList(&writer); 208 if (mInitCheck != OK) { 209 break; 210 } 211 } 212 writer.writeGlobalSettings(mGlobalSettings); 213 writer.writeCodecInfos(&mCodecInfos); 214 std::stable_sort( 215 mCodecInfos.begin(), 216 mCodecInfos.end(), 217 [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) { 218 if (info2 == nullptr) { 219 return false; 220 } else if (info1 == nullptr) { 221 return true; 222 } else { 223 return info1->rank() < info2->rank(); 224 } 225 }); 226 } 227 228 MediaCodecList::~MediaCodecList() { 229 } 230 231 status_t MediaCodecList::initCheck() const { 232 return mInitCheck; 233 } 234 235 // legacy method for non-advanced codecs 236 ssize_t MediaCodecList::findCodecByType( 237 const char *type, bool encoder, size_t startIndex) const { 238 static const char *advancedFeatures[] = { 239 "feature-secure-playback", 240 "feature-tunneled-playback", 241 }; 242 243 size_t numCodecInfos = mCodecInfos.size(); 244 for (; startIndex < numCodecInfos; ++startIndex) { 245 const MediaCodecInfo &info = *mCodecInfos[startIndex]; 246 247 if (info.isEncoder() != encoder) { 248 continue; 249 } 250 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type); 251 if (capabilities == nullptr) { 252 continue; 253 } 254 const sp<AMessage> &details = capabilities->getDetails(); 255 256 int32_t required; 257 bool isAdvanced = false; 258 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) { 259 if (details->findInt32(advancedFeatures[ix], &required) && 260 required != 0) { 261 isAdvanced = true; 262 break; 263 } 264 } 265 266 if (!isAdvanced) { 267 return startIndex; 268 } 269 } 270 271 return -ENOENT; 272 } 273 274 ssize_t MediaCodecList::findCodecByName(const char *name) const { 275 for (size_t i = 0; i < mCodecInfos.size(); ++i) { 276 if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) { 277 return i; 278 } 279 } 280 281 return -ENOENT; 282 } 283 284 size_t MediaCodecList::countCodecs() const { 285 return mCodecInfos.size(); 286 } 287 288 const sp<AMessage> MediaCodecList::getGlobalSettings() const { 289 return mGlobalSettings; 290 } 291 292 //static 293 bool MediaCodecList::isSoftwareCodec(const AString &componentName) { 294 return componentName.startsWithIgnoreCase("OMX.google.") 295 || componentName.startsWithIgnoreCase("c2.android.") 296 || (!componentName.startsWithIgnoreCase("OMX.") 297 && !componentName.startsWithIgnoreCase("c2.")); 298 } 299 300 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) { 301 // sort order 1: software codecs are first (lower) 302 bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1); 303 bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2); 304 if (isSoftwareCodec1 != isSoftwareCodec2) { 305 return isSoftwareCodec2 - isSoftwareCodec1; 306 } 307 308 // sort order 2: Codec 2.0 codecs are first (lower) 309 bool isC2_1 = name1->startsWithIgnoreCase("c2."); 310 bool isC2_2 = name2->startsWithIgnoreCase("c2."); 311 if (isC2_1 != isC2_2) { 312 return isC2_2 - isC2_1; 313 } 314 315 // sort order 3: OMX codecs are first (lower) 316 bool isOMX1 = name1->startsWithIgnoreCase("OMX."); 317 bool isOMX2 = name2->startsWithIgnoreCase("OMX."); 318 return isOMX2 - isOMX1; 319 } 320 321 //static 322 void MediaCodecList::findMatchingCodecs( 323 const char *mime, bool encoder, uint32_t flags, 324 Vector<AString> *matches) { 325 matches->clear(); 326 327 const sp<IMediaCodecList> list = getInstance(); 328 if (list == nullptr) { 329 return; 330 } 331 332 size_t index = 0; 333 for (;;) { 334 ssize_t matchIndex = 335 list->findCodecByType(mime, encoder, index); 336 337 if (matchIndex < 0) { 338 break; 339 } 340 341 index = matchIndex + 1; 342 343 const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex); 344 CHECK(info != nullptr); 345 AString componentName = info->getCodecName(); 346 347 if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) { 348 ALOGV("skipping SW codec '%s'", componentName.c_str()); 349 } else { 350 matches->push(componentName); 351 ALOGV("matching '%s'", componentName.c_str()); 352 } 353 } 354 355 if (flags & kPreferSoftwareCodecs || 356 property_get_bool("debug.stagefright.swcodec", false)) { 357 matches->sort(compareSoftwareCodecsFirst); 358 } 359 } 360 361 } // namespace android 362