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