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