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;
81
82 Mutex sCodec2InfoBuilderMutex;
83 std::unique_ptr<MediaCodecListBuilderBase> sCodec2InfoBuilder;
84
GetCodec2InfoBuilder()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
GetBuilders()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
profilerThreadWrapper(void *)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
getLocalInstance()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
binderDied(const wp<IBinder> & who __unused)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
getInstance()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
MediaCodecList(std::vector<MediaCodecListBuilderBase * > builders)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
~MediaCodecList()228 MediaCodecList::~MediaCodecList() {
229 }
230
initCheck() const231 status_t MediaCodecList::initCheck() const {
232 return mInitCheck;
233 }
234
235 // legacy method for non-advanced codecs
findCodecByType(const char * type,bool encoder,size_t startIndex) const236 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
findCodecByName(const char * name) const274 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
countCodecs() const284 size_t MediaCodecList::countCodecs() const {
285 return mCodecInfos.size();
286 }
287
getGlobalSettings() const288 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
289 return mGlobalSettings;
290 }
291
292 //static
isSoftwareCodec(const AString & componentName)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
compareSoftwareCodecsFirst(const AString * name1,const AString * name2)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
findMatchingCodecs(const char * mime,bool encoder,uint32_t flags,Vector<AString> * matches)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