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
isProfilingNeeded()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{true /* allowSurfaceEncoders */};
81 OmxInfoBuilder sOmxNoSurfaceEncoderInfoBuilder{false /* allowSurfaceEncoders */};
82
83 Mutex sCodec2InfoBuilderMutex;
84 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
85
GetCodec2InfoBuilder()86 MediaCodecListBuilderBase *GetCodec2InfoBuilder() {
87 Mutex::Autolock _l(sCodec2InfoBuilderMutex);
88 if (!sCodec2InfoBuilder) {
89 sCodec2InfoBuilder.reset(
90 StagefrightPluginLoader::GetCCodecInstance()->createBuilder());
91 }
92 return sCodec2InfoBuilder.get();
93 }
94
GetBuilders()95 std::vector<MediaCodecListBuilderBase *> GetBuilders() {
96 std::vector<MediaCodecListBuilderBase *> builders;
97 // if plugin provides the input surface, we cannot use OMX video encoders.
98 // In this case, rely on plugin to provide list of OMX codecs that are usable.
99 sp<PersistentSurface> surfaceTest =
100 StagefrightPluginLoader::GetCCodecInstance()->createInputSurface();
101 if (surfaceTest == nullptr) {
102 ALOGD("Allowing all OMX codecs");
103 builders.push_back(&sOmxInfoBuilder);
104 } else {
105 ALOGD("Allowing only non-surface-encoder OMX codecs");
106 builders.push_back(&sOmxNoSurfaceEncoderInfoBuilder);
107 }
108 builders.push_back(GetCodec2InfoBuilder());
109 return builders;
110 }
111
112 } // unnamed namespace
113
114 // static
115 sp<IMediaCodecList> MediaCodecList::sCodecList;
116
117 // static
profilerThreadWrapper(void *)118 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
119 ALOGV("Enter profilerThreadWrapper.");
120 remove(kProfilingResults); // remove previous result so that it won't be loaded to
121 // the new MediaCodecList
122 sp<MediaCodecList> codecList(new MediaCodecList(GetBuilders()));
123 if (codecList->initCheck() != OK) {
124 ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
125 return nullptr;
126 }
127
128 const auto& infos = codecList->mCodecInfos;
129 ALOGV("Codec profiling started.");
130 profileCodecs(infos, kProfilingResults);
131 ALOGV("Codec profiling completed.");
132 codecList = new MediaCodecList(GetBuilders());
133 if (codecList->initCheck() != OK) {
134 ALOGW("Failed to parse profiling results.");
135 return nullptr;
136 }
137
138 {
139 Mutex::Autolock autoLock(sInitMutex);
140 sCodecList = codecList;
141 }
142 return nullptr;
143 }
144
145 // static
getLocalInstance()146 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
147 Mutex::Autolock autoLock(sInitMutex);
148
149 if (sCodecList == nullptr) {
150 MediaCodecList *codecList = new MediaCodecList(GetBuilders());
151 if (codecList->initCheck() == OK) {
152 sCodecList = codecList;
153
154 if (isProfilingNeeded()) {
155 ALOGV("Codec profiling needed, will be run in separated thread.");
156 pthread_t profiler;
157 if (pthread_create(&profiler, nullptr, profilerThreadWrapper, nullptr) != 0) {
158 ALOGW("Failed to create thread for codec profiling.");
159 }
160 }
161 } else {
162 // failure to initialize may be temporary. retry on next call.
163 delete codecList;
164 }
165 }
166
167 return sCodecList;
168 }
169
170 sp<IMediaCodecList> MediaCodecList::sRemoteList;
171
172 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
173 sp<IBinder> MediaCodecList::sMediaPlayer; // kept since linked to death
174
binderDied(const wp<IBinder> & who __unused)175 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
176 Mutex::Autolock _l(sRemoteInitMutex);
177 sRemoteList.clear();
178 sBinderDeathObserver.clear();
179 }
180
181 // static
getInstance()182 sp<IMediaCodecList> MediaCodecList::getInstance() {
183 Mutex::Autolock _l(sRemoteInitMutex);
184 if (sRemoteList == nullptr) {
185 sMediaPlayer = defaultServiceManager()->getService(String16("media.player"));
186 sp<IMediaPlayerService> service =
187 interface_cast<IMediaPlayerService>(sMediaPlayer);
188 if (service.get() != nullptr) {
189 sRemoteList = service->getCodecList();
190 if (sRemoteList != nullptr) {
191 sBinderDeathObserver = new BinderDeathObserver();
192 sMediaPlayer->linkToDeath(sBinderDeathObserver.get());
193 }
194 }
195 if (sRemoteList == nullptr) {
196 // if failed to get remote list, create local list
197 sRemoteList = getLocalInstance();
198 }
199 }
200 return sRemoteList;
201 }
202
MediaCodecList(std::vector<MediaCodecListBuilderBase * > builders)203 MediaCodecList::MediaCodecList(std::vector<MediaCodecListBuilderBase*> builders) {
204 mGlobalSettings = new AMessage();
205 mCodecInfos.clear();
206 MediaCodecListWriter writer;
207 for (MediaCodecListBuilderBase *builder : builders) {
208 if (builder == nullptr) {
209 ALOGD("ignored a null builder");
210 continue;
211 }
212 mInitCheck = builder->buildMediaCodecList(&writer);
213 if (mInitCheck != OK) {
214 break;
215 }
216 }
217 writer.writeGlobalSettings(mGlobalSettings);
218 writer.writeCodecInfos(&mCodecInfos);
219 std::stable_sort(
220 mCodecInfos.begin(),
221 mCodecInfos.end(),
222 [](const sp<MediaCodecInfo> &info1, const sp<MediaCodecInfo> &info2) {
223 // null is lowest
224 return info1 == nullptr
225 || (info2 != nullptr && info1->getRank() < info2->getRank());
226 });
227
228 // remove duplicate entries
229 bool dedupe = property_get_bool("debug.stagefright.dedupe-codecs", true);
230 if (dedupe) {
231 std::set<std::string> codecsSeen;
232 for (auto it = mCodecInfos.begin(); it != mCodecInfos.end(); ) {
233 std::string codecName = (*it)->getCodecName();
234 if (codecsSeen.count(codecName) == 0) {
235 codecsSeen.emplace(codecName);
236 it++;
237 } else {
238 it = mCodecInfos.erase(it);
239 }
240 }
241 }
242 }
243
~MediaCodecList()244 MediaCodecList::~MediaCodecList() {
245 }
246
initCheck() const247 status_t MediaCodecList::initCheck() const {
248 return mInitCheck;
249 }
250
251 // legacy method for non-advanced codecs
findCodecByType(const char * type,bool encoder,size_t startIndex) const252 ssize_t MediaCodecList::findCodecByType(
253 const char *type, bool encoder, size_t startIndex) const {
254 static const char *advancedFeatures[] = {
255 "feature-secure-playback",
256 "feature-tunneled-playback",
257 };
258
259 size_t numCodecInfos = mCodecInfos.size();
260 for (; startIndex < numCodecInfos; ++startIndex) {
261 const MediaCodecInfo &info = *mCodecInfos[startIndex];
262
263 if (info.isEncoder() != encoder) {
264 continue;
265 }
266 sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
267 if (capabilities == nullptr) {
268 continue;
269 }
270 const sp<AMessage> &details = capabilities->getDetails();
271
272 int32_t required;
273 bool isAdvanced = false;
274 for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
275 if (details->findInt32(advancedFeatures[ix], &required) &&
276 required != 0) {
277 isAdvanced = true;
278 break;
279 }
280 }
281
282 if (!isAdvanced) {
283 return startIndex;
284 }
285 }
286
287 return -ENOENT;
288 }
289
findCodecByName(const char * name) const290 ssize_t MediaCodecList::findCodecByName(const char *name) const {
291 Vector<AString> aliases;
292 for (size_t i = 0; i < mCodecInfos.size(); ++i) {
293 if (strcmp(mCodecInfos[i]->getCodecName(), name) == 0) {
294 return i;
295 }
296 mCodecInfos[i]->getAliases(&aliases);
297 for (const AString &alias : aliases) {
298 if (alias == name) {
299 return i;
300 }
301 }
302 }
303
304 return -ENOENT;
305 }
306
countCodecs() const307 size_t MediaCodecList::countCodecs() const {
308 return mCodecInfos.size();
309 }
310
getGlobalSettings() const311 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
312 return mGlobalSettings;
313 }
314
315 //static
isSoftwareCodec(const AString & componentName)316 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
317 return componentName.startsWithIgnoreCase("OMX.google.")
318 || componentName.startsWithIgnoreCase("c2.android.")
319 || (!componentName.startsWithIgnoreCase("OMX.")
320 && !componentName.startsWithIgnoreCase("c2."));
321 }
322
compareSoftwareCodecsFirst(const AString * name1,const AString * name2)323 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
324 // sort order 1: software codecs are first (lower)
325 bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
326 bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
327 if (isSoftwareCodec1 != isSoftwareCodec2) {
328 return isSoftwareCodec2 - isSoftwareCodec1;
329 }
330
331 // sort order 2: Codec 2.0 codecs are first (lower)
332 bool isC2_1 = name1->startsWithIgnoreCase("c2.");
333 bool isC2_2 = name2->startsWithIgnoreCase("c2.");
334 if (isC2_1 != isC2_2) {
335 return isC2_2 - isC2_1;
336 }
337
338 // sort order 3: OMX codecs are first (lower)
339 bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
340 bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
341 return isOMX2 - isOMX1;
342 }
343
344 //static
findMatchingCodecs(const char * mime,bool encoder,uint32_t flags,Vector<AString> * matches)345 void MediaCodecList::findMatchingCodecs(
346 const char *mime, bool encoder, uint32_t flags,
347 Vector<AString> *matches) {
348 matches->clear();
349
350 const sp<IMediaCodecList> list = getInstance();
351 if (list == nullptr) {
352 return;
353 }
354
355 size_t index = 0;
356 for (;;) {
357 ssize_t matchIndex =
358 list->findCodecByType(mime, encoder, index);
359
360 if (matchIndex < 0) {
361 break;
362 }
363
364 index = matchIndex + 1;
365
366 const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
367 CHECK(info != nullptr);
368 AString componentName = info->getCodecName();
369
370 if ((flags & kHardwareCodecsOnly) && isSoftwareCodec(componentName)) {
371 ALOGV("skipping SW codec '%s'", componentName.c_str());
372 } else {
373 matches->push(componentName);
374 ALOGV("matching '%s'", componentName.c_str());
375 }
376 }
377
378 if (flags & kPreferSoftwareCodecs ||
379 property_get_bool("debug.stagefright.swcodec", false)) {
380 matches->sort(compareSoftwareCodecsFirst);
381 }
382 }
383
384 } // namespace android
385