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 
23 #include <binder/IServiceManager.h>
24 
25 #include <media/IMediaCodecList.h>
26 #include <media/IMediaPlayerService.h>
27 #include <media/IResourceManagerService.h>
28 #include <media/MediaCodecInfo.h>
29 #include <media/MediaResourcePolicy.h>
30 
31 #include <media/stagefright/foundation/ADebug.h>
32 #include <media/stagefright/foundation/AMessage.h>
33 #include <media/stagefright/ACodec.h>
34 #include <media/stagefright/MediaCodec.h>
35 #include <media/stagefright/MediaCodecList.h>
36 #include <media/stagefright/MediaErrors.h>
37 #include <media/stagefright/OMXClient.h>
38 
39 #include <sys/stat.h>
40 #include <utils/threads.h>
41 
42 #include <cutils/properties.h>
43 #include <expat.h>
44 
45 namespace android {
46 
47 const char *kMaxEncoderInputBuffers = "max-video-encoder-input-buffers";
48 
49 static Mutex sInitMutex;
50 
parseBoolean(const char * s)51 static bool parseBoolean(const char *s) {
52     if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
53         return true;
54     }
55     char *end;
56     unsigned long res = strtoul(s, &end, 10);
57     return *s != '\0' && *end == '\0' && res > 0;
58 }
59 
isProfilingNeeded()60 static bool isProfilingNeeded() {
61     int8_t value = property_get_bool("debug.stagefright.profilecodec", 0);
62     if (value == 0) {
63         return false;
64     }
65 
66     bool profilingNeeded = true;
67     FILE *resultsFile = fopen(kProfilingResults, "r");
68     if (resultsFile) {
69         AString currentVersion = getProfilingVersionString();
70         size_t currentVersionSize = currentVersion.size();
71         char *versionString = new char[currentVersionSize + 1];
72         fgets(versionString, currentVersionSize + 1, resultsFile);
73         if (strcmp(versionString, currentVersion.c_str()) == 0) {
74             // profiling result up to date
75             profilingNeeded = false;
76         }
77         fclose(resultsFile);
78         delete[] versionString;
79     }
80     return profilingNeeded;
81 }
82 
83 // static
84 sp<IMediaCodecList> MediaCodecList::sCodecList;
85 
86 // static
profilerThreadWrapper(void *)87 void *MediaCodecList::profilerThreadWrapper(void * /*arg*/) {
88     ALOGV("Enter profilerThreadWrapper.");
89     remove(kProfilingResults);  // remove previous result so that it won't be loaded to
90                                 // the new MediaCodecList
91     MediaCodecList *codecList = new MediaCodecList();
92     if (codecList->initCheck() != OK) {
93         ALOGW("Failed to create a new MediaCodecList, skipping codec profiling.");
94         delete codecList;
95         return NULL;
96     }
97 
98     Vector<sp<MediaCodecInfo>> infos;
99     for (size_t i = 0; i < codecList->countCodecs(); ++i) {
100         infos.push_back(codecList->getCodecInfo(i));
101     }
102     ALOGV("Codec profiling started.");
103     profileCodecs(infos);
104     ALOGV("Codec profiling completed.");
105     codecList->parseTopLevelXMLFile(kProfilingResults, true /* ignore_errors */);
106 
107     {
108         Mutex::Autolock autoLock(sInitMutex);
109         sCodecList = codecList;
110     }
111     return NULL;
112 }
113 
114 // static
getLocalInstance()115 sp<IMediaCodecList> MediaCodecList::getLocalInstance() {
116     Mutex::Autolock autoLock(sInitMutex);
117 
118     if (sCodecList == NULL) {
119         MediaCodecList *codecList = new MediaCodecList;
120         if (codecList->initCheck() == OK) {
121             sCodecList = codecList;
122 
123             if (isProfilingNeeded()) {
124                 ALOGV("Codec profiling needed, will be run in separated thread.");
125                 pthread_t profiler;
126                 if (pthread_create(&profiler, NULL, profilerThreadWrapper, NULL) != 0) {
127                     ALOGW("Failed to create thread for codec profiling.");
128                 }
129             }
130         } else {
131             // failure to initialize may be temporary. retry on next call.
132             delete codecList;
133         }
134     }
135 
136     return sCodecList;
137 }
138 
139 static Mutex sRemoteInitMutex;
140 
141 sp<IMediaCodecList> MediaCodecList::sRemoteList;
142 
143 sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
144 
binderDied(const wp<IBinder> & who __unused)145 void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
146     Mutex::Autolock _l(sRemoteInitMutex);
147     sRemoteList.clear();
148     sBinderDeathObserver.clear();
149 }
150 
151 // static
getInstance()152 sp<IMediaCodecList> MediaCodecList::getInstance() {
153     Mutex::Autolock _l(sRemoteInitMutex);
154     if (sRemoteList == NULL) {
155         sp<IBinder> binder =
156             defaultServiceManager()->getService(String16("media.player"));
157         sp<IMediaPlayerService> service =
158             interface_cast<IMediaPlayerService>(binder);
159         if (service.get() != NULL) {
160             sRemoteList = service->getCodecList();
161             if (sRemoteList != NULL) {
162                 sBinderDeathObserver = new BinderDeathObserver();
163                 binder->linkToDeath(sBinderDeathObserver.get());
164             }
165         }
166         if (sRemoteList == NULL) {
167             // if failed to get remote list, create local list
168             sRemoteList = getLocalInstance();
169         }
170     }
171     return sRemoteList;
172 }
173 
MediaCodecList()174 MediaCodecList::MediaCodecList()
175     : mInitCheck(NO_INIT),
176       mUpdate(false),
177       mGlobalSettings(new AMessage()) {
178     parseTopLevelXMLFile("/etc/media_codecs.xml");
179     parseTopLevelXMLFile("/etc/media_codecs_performance.xml", true/* ignore_errors */);
180     parseTopLevelXMLFile(kProfilingResults, true/* ignore_errors */);
181 }
182 
parseTopLevelXMLFile(const char * codecs_xml,bool ignore_errors)183 void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml, bool ignore_errors) {
184     // get href_base
185     char *href_base_end = strrchr(codecs_xml, '/');
186     if (href_base_end != NULL) {
187         mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
188     }
189 
190     mInitCheck = OK; // keeping this here for safety
191     mCurrentSection = SECTION_TOPLEVEL;
192     mDepth = 0;
193 
194     OMXClient client;
195     mInitCheck = client.connect();
196     if (mInitCheck != OK) {
197         return;  // this may fail if IMediaPlayerService is not available.
198     }
199     mOMX = client.interface();
200     parseXMLFile(codecs_xml);
201     mOMX.clear();
202 
203     if (mInitCheck != OK) {
204         if (ignore_errors) {
205             mInitCheck = OK;
206             return;
207         }
208         mCodecInfos.clear();
209         return;
210     }
211 
212     Vector<MediaResourcePolicy> policies;
213     AString value;
214     if (mGlobalSettings->findString(kPolicySupportsMultipleSecureCodecs, &value)) {
215         policies.push_back(
216                 MediaResourcePolicy(
217                         String8(kPolicySupportsMultipleSecureCodecs),
218                         String8(value.c_str())));
219     }
220     if (mGlobalSettings->findString(kPolicySupportsSecureWithNonSecureCodec, &value)) {
221         policies.push_back(
222                 MediaResourcePolicy(
223                         String8(kPolicySupportsSecureWithNonSecureCodec),
224                         String8(value.c_str())));
225     }
226     if (policies.size() > 0) {
227         sp<IServiceManager> sm = defaultServiceManager();
228         sp<IBinder> binder = sm->getService(String16("media.resource_manager"));
229         sp<IResourceManagerService> service = interface_cast<IResourceManagerService>(binder);
230         if (service == NULL) {
231             ALOGE("MediaCodecList: failed to get ResourceManagerService");
232         } else {
233             service->config(policies);
234         }
235     }
236 
237     for (size_t i = mCodecInfos.size(); i > 0;) {
238         i--;
239         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
240         if (info.mCaps.size() == 0) {
241             // No types supported by this component???
242             ALOGW("Component %s does not support any type of media?",
243                   info.mName.c_str());
244 
245             mCodecInfos.removeAt(i);
246 #if LOG_NDEBUG == 0
247         } else {
248             for (size_t type_ix = 0; type_ix < info.mCaps.size(); ++type_ix) {
249                 AString mime = info.mCaps.keyAt(type_ix);
250                 const sp<MediaCodecInfo::Capabilities> &caps = info.mCaps.valueAt(type_ix);
251 
252                 ALOGV("%s codec info for %s: %s", info.mName.c_str(), mime.c_str(),
253                         caps->getDetails()->debugString().c_str());
254                 ALOGV("    flags=%d", caps->getFlags());
255                 {
256                     Vector<uint32_t> colorFormats;
257                     caps->getSupportedColorFormats(&colorFormats);
258                     AString nice;
259                     for (size_t ix = 0; ix < colorFormats.size(); ix++) {
260                         if (ix > 0) {
261                             nice.append(", ");
262                         }
263                         nice.append(colorFormats.itemAt(ix));
264                     }
265                     ALOGV("    colors=[%s]", nice.c_str());
266                 }
267                 {
268                     Vector<MediaCodecInfo::ProfileLevel> profileLevels;
269                     caps->getSupportedProfileLevels(&profileLevels);
270                     AString nice;
271                     for (size_t ix = 0; ix < profileLevels.size(); ix++) {
272                         if (ix > 0) {
273                             nice.append(", ");
274                         }
275                         const MediaCodecInfo::ProfileLevel &pl =
276                             profileLevels.itemAt(ix);
277                         nice.append(pl.mProfile);
278                         nice.append("/");
279                         nice.append(pl.mLevel);
280                     }
281                     ALOGV("    levels=[%s]", nice.c_str());
282                 }
283                 {
284                     AString quirks;
285                     for (size_t ix = 0; ix < info.mQuirks.size(); ix++) {
286                         if (ix > 0) {
287                             quirks.append(", ");
288                         }
289                         quirks.append(info.mQuirks[ix]);
290                     }
291                     ALOGV("    quirks=[%s]", quirks.c_str());
292                 }
293             }
294 #endif
295         }
296     }
297 
298 #if 0
299     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
300         const CodecInfo &info = mCodecInfos.itemAt(i);
301 
302         AString line = info.mName;
303         line.append(" supports ");
304         for (size_t j = 0; j < mTypes.size(); ++j) {
305             uint32_t value = mTypes.valueAt(j);
306 
307             if (info.mTypes & (1ul << value)) {
308                 line.append(mTypes.keyAt(j));
309                 line.append(" ");
310             }
311         }
312 
313         ALOGI("%s", line.c_str());
314     }
315 #endif
316 }
317 
~MediaCodecList()318 MediaCodecList::~MediaCodecList() {
319 }
320 
initCheck() const321 status_t MediaCodecList::initCheck() const {
322     return mInitCheck;
323 }
324 
parseXMLFile(const char * path)325 void MediaCodecList::parseXMLFile(const char *path) {
326     FILE *file = fopen(path, "r");
327 
328     if (file == NULL) {
329         ALOGW("unable to open media codecs configuration xml file: %s", path);
330         mInitCheck = NAME_NOT_FOUND;
331         return;
332     }
333 
334     XML_Parser parser = ::XML_ParserCreate(NULL);
335     CHECK(parser != NULL);
336 
337     ::XML_SetUserData(parser, this);
338     ::XML_SetElementHandler(
339             parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
340 
341     const int BUFF_SIZE = 512;
342     while (mInitCheck == OK) {
343         void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
344         if (buff == NULL) {
345             ALOGE("failed in call to XML_GetBuffer()");
346             mInitCheck = UNKNOWN_ERROR;
347             break;
348         }
349 
350         int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
351         if (bytes_read < 0) {
352             ALOGE("failed in call to read");
353             mInitCheck = ERROR_IO;
354             break;
355         }
356 
357         XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
358         if (status != XML_STATUS_OK) {
359             ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
360             mInitCheck = ERROR_MALFORMED;
361             break;
362         }
363 
364         if (bytes_read == 0) {
365             break;
366         }
367     }
368 
369     ::XML_ParserFree(parser);
370 
371     fclose(file);
372     file = NULL;
373 }
374 
375 // static
StartElementHandlerWrapper(void * me,const char * name,const char ** attrs)376 void MediaCodecList::StartElementHandlerWrapper(
377         void *me, const char *name, const char **attrs) {
378     static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
379 }
380 
381 // static
EndElementHandlerWrapper(void * me,const char * name)382 void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
383     static_cast<MediaCodecList *>(me)->endElementHandler(name);
384 }
385 
includeXMLFile(const char ** attrs)386 status_t MediaCodecList::includeXMLFile(const char **attrs) {
387     const char *href = NULL;
388     size_t i = 0;
389     while (attrs[i] != NULL) {
390         if (!strcmp(attrs[i], "href")) {
391             if (attrs[i + 1] == NULL) {
392                 return -EINVAL;
393             }
394             href = attrs[i + 1];
395             ++i;
396         } else {
397             return -EINVAL;
398         }
399         ++i;
400     }
401 
402     // For security reasons and for simplicity, file names can only contain
403     // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
404     for (i = 0; href[i] != '\0'; i++) {
405         if (href[i] == '.' || href[i] == '_' ||
406                 (href[i] >= '0' && href[i] <= '9') ||
407                 (href[i] >= 'A' && href[i] <= 'Z') ||
408                 (href[i] >= 'a' && href[i] <= 'z')) {
409             continue;
410         }
411         ALOGE("invalid include file name: %s", href);
412         return -EINVAL;
413     }
414 
415     AString filename = href;
416     if (!filename.startsWith("media_codecs_") ||
417         !filename.endsWith(".xml")) {
418         ALOGE("invalid include file name: %s", href);
419         return -EINVAL;
420     }
421     filename.insert(mHrefBase, 0);
422 
423     parseXMLFile(filename.c_str());
424     return mInitCheck;
425 }
426 
startElementHandler(const char * name,const char ** attrs)427 void MediaCodecList::startElementHandler(
428         const char *name, const char **attrs) {
429     if (mInitCheck != OK) {
430         return;
431     }
432 
433     bool inType = true;
434 
435     if (!strcmp(name, "Include")) {
436         mInitCheck = includeXMLFile(attrs);
437         if (mInitCheck == OK) {
438             mPastSections.push(mCurrentSection);
439             mCurrentSection = SECTION_INCLUDE;
440         }
441         ++mDepth;
442         return;
443     }
444 
445     switch (mCurrentSection) {
446         case SECTION_TOPLEVEL:
447         {
448             if (!strcmp(name, "Decoders")) {
449                 mCurrentSection = SECTION_DECODERS;
450             } else if (!strcmp(name, "Encoders")) {
451                 mCurrentSection = SECTION_ENCODERS;
452             } else if (!strcmp(name, "Settings")) {
453                 mCurrentSection = SECTION_SETTINGS;
454             }
455             break;
456         }
457 
458         case SECTION_SETTINGS:
459         {
460             if (!strcmp(name, "Setting")) {
461                 mInitCheck = addSettingFromAttributes(attrs);
462             }
463             break;
464         }
465 
466         case SECTION_DECODERS:
467         {
468             if (!strcmp(name, "MediaCodec")) {
469                 mInitCheck =
470                     addMediaCodecFromAttributes(false /* encoder */, attrs);
471 
472                 mCurrentSection = SECTION_DECODER;
473             }
474             break;
475         }
476 
477         case SECTION_ENCODERS:
478         {
479             if (!strcmp(name, "MediaCodec")) {
480                 mInitCheck =
481                     addMediaCodecFromAttributes(true /* encoder */, attrs);
482 
483                 mCurrentSection = SECTION_ENCODER;
484             }
485             break;
486         }
487 
488         case SECTION_DECODER:
489         case SECTION_ENCODER:
490         {
491             if (!strcmp(name, "Quirk")) {
492                 mInitCheck = addQuirk(attrs);
493             } else if (!strcmp(name, "Type")) {
494                 mInitCheck = addTypeFromAttributes(attrs);
495                 mCurrentSection =
496                     (mCurrentSection == SECTION_DECODER
497                             ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
498             }
499         }
500         inType = false;
501         // fall through
502 
503         case SECTION_DECODER_TYPE:
504         case SECTION_ENCODER_TYPE:
505         {
506             // ignore limits and features specified outside of type
507             bool outside = !inType && !mCurrentInfo->mHasSoleMime;
508             if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
509                 ALOGW("ignoring %s specified outside of a Type", name);
510             } else if (!strcmp(name, "Limit")) {
511                 mInitCheck = addLimit(attrs);
512             } else if (!strcmp(name, "Feature")) {
513                 mInitCheck = addFeature(attrs);
514             }
515             break;
516         }
517 
518         default:
519             break;
520     }
521 
522     ++mDepth;
523 }
524 
endElementHandler(const char * name)525 void MediaCodecList::endElementHandler(const char *name) {
526     if (mInitCheck != OK) {
527         return;
528     }
529 
530     switch (mCurrentSection) {
531         case SECTION_SETTINGS:
532         {
533             if (!strcmp(name, "Settings")) {
534                 mCurrentSection = SECTION_TOPLEVEL;
535             }
536             break;
537         }
538 
539         case SECTION_DECODERS:
540         {
541             if (!strcmp(name, "Decoders")) {
542                 mCurrentSection = SECTION_TOPLEVEL;
543             }
544             break;
545         }
546 
547         case SECTION_ENCODERS:
548         {
549             if (!strcmp(name, "Encoders")) {
550                 mCurrentSection = SECTION_TOPLEVEL;
551             }
552             break;
553         }
554 
555         case SECTION_DECODER_TYPE:
556         case SECTION_ENCODER_TYPE:
557         {
558             if (!strcmp(name, "Type")) {
559                 mCurrentSection =
560                     (mCurrentSection == SECTION_DECODER_TYPE
561                             ? SECTION_DECODER : SECTION_ENCODER);
562 
563                 mCurrentInfo->complete();
564             }
565             break;
566         }
567 
568         case SECTION_DECODER:
569         {
570             if (!strcmp(name, "MediaCodec")) {
571                 mCurrentSection = SECTION_DECODERS;
572                 mCurrentInfo->complete();
573                 mCurrentInfo = NULL;
574             }
575             break;
576         }
577 
578         case SECTION_ENCODER:
579         {
580             if (!strcmp(name, "MediaCodec")) {
581                 mCurrentSection = SECTION_ENCODERS;
582                 mCurrentInfo->complete();;
583                 mCurrentInfo = NULL;
584             }
585             break;
586         }
587 
588         case SECTION_INCLUDE:
589         {
590             if (!strcmp(name, "Include") && mPastSections.size() > 0) {
591                 mCurrentSection = mPastSections.top();
592                 mPastSections.pop();
593             }
594             break;
595         }
596 
597         default:
598             break;
599     }
600 
601     --mDepth;
602 }
603 
addSettingFromAttributes(const char ** attrs)604 status_t MediaCodecList::addSettingFromAttributes(const char **attrs) {
605     const char *name = NULL;
606     const char *value = NULL;
607     const char *update = NULL;
608 
609     size_t i = 0;
610     while (attrs[i] != NULL) {
611         if (!strcmp(attrs[i], "name")) {
612             if (attrs[i + 1] == NULL) {
613                 return -EINVAL;
614             }
615             name = attrs[i + 1];
616             ++i;
617         } else if (!strcmp(attrs[i], "value")) {
618             if (attrs[i + 1] == NULL) {
619                 return -EINVAL;
620             }
621             value = attrs[i + 1];
622             ++i;
623         } else if (!strcmp(attrs[i], "update")) {
624             if (attrs[i + 1] == NULL) {
625                 return -EINVAL;
626             }
627             update = attrs[i + 1];
628             ++i;
629         } else {
630             return -EINVAL;
631         }
632 
633         ++i;
634     }
635 
636     if (name == NULL || value == NULL) {
637         return -EINVAL;
638     }
639 
640     mUpdate = (update != NULL) && parseBoolean(update);
641     if (mUpdate != mGlobalSettings->contains(name)) {
642         return -EINVAL;
643     }
644 
645     mGlobalSettings->setString(name, value);
646     return OK;
647 }
648 
setCurrentCodecInfo(bool encoder,const char * name,const char * type)649 void MediaCodecList::setCurrentCodecInfo(bool encoder, const char *name, const char *type) {
650     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
651         if (AString(name) == mCodecInfos[i]->getCodecName()) {
652             if (mCodecInfos[i]->getCapabilitiesFor(type) == NULL) {
653                 ALOGW("Overrides with an unexpected mime %s", type);
654                 // Create a new MediaCodecInfo (but don't add it to mCodecInfos) to hold the
655                 // overrides we don't want.
656                 mCurrentInfo = new MediaCodecInfo(name, encoder, type);
657             } else {
658                 mCurrentInfo = mCodecInfos.editItemAt(i);
659                 mCurrentInfo->updateMime(type);  // to set the current cap
660             }
661             return;
662         }
663     }
664     mCurrentInfo = new MediaCodecInfo(name, encoder, type);
665     // The next step involves trying to load the codec, which may
666     // fail.  Only list the codec if this succeeds.
667     // However, keep mCurrentInfo object around until parsing
668     // of full codec info is completed.
669     if (initializeCapabilities(type) == OK) {
670         mCodecInfos.push_back(mCurrentInfo);
671     }
672 }
673 
addMediaCodecFromAttributes(bool encoder,const char ** attrs)674 status_t MediaCodecList::addMediaCodecFromAttributes(
675         bool encoder, const char **attrs) {
676     const char *name = NULL;
677     const char *type = NULL;
678     const char *update = NULL;
679 
680     size_t i = 0;
681     while (attrs[i] != NULL) {
682         if (!strcmp(attrs[i], "name")) {
683             if (attrs[i + 1] == NULL) {
684                 return -EINVAL;
685             }
686             name = attrs[i + 1];
687             ++i;
688         } else if (!strcmp(attrs[i], "type")) {
689             if (attrs[i + 1] == NULL) {
690                 return -EINVAL;
691             }
692             type = attrs[i + 1];
693             ++i;
694         } else if (!strcmp(attrs[i], "update")) {
695             if (attrs[i + 1] == NULL) {
696                 return -EINVAL;
697             }
698             update = attrs[i + 1];
699             ++i;
700         } else {
701             return -EINVAL;
702         }
703 
704         ++i;
705     }
706 
707     if (name == NULL) {
708         return -EINVAL;
709     }
710 
711     mUpdate = (update != NULL) && parseBoolean(update);
712     ssize_t index = -1;
713     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
714         if (AString(name) == mCodecInfos[i]->getCodecName()) {
715             index = i;
716         }
717     }
718     if (mUpdate != (index >= 0)) {
719         return -EINVAL;
720     }
721 
722     if (index >= 0) {
723         // existing codec
724         mCurrentInfo = mCodecInfos.editItemAt(index);
725         if (type != NULL) {
726             // existing type
727             if (mCodecInfos[index]->getCapabilitiesFor(type) == NULL) {
728                 return -EINVAL;
729             }
730             mCurrentInfo->updateMime(type);
731         }
732     } else {
733         // new codec
734         mCurrentInfo = new MediaCodecInfo(name, encoder, type);
735         // The next step involves trying to load the codec, which may
736         // fail.  Only list the codec if this succeeds.
737         // However, keep mCurrentInfo object around until parsing
738         // of full codec info is completed.
739         if (initializeCapabilities(type) == OK) {
740             mCodecInfos.push_back(mCurrentInfo);
741         }
742     }
743 
744     return OK;
745 }
746 
initializeCapabilities(const char * type)747 status_t MediaCodecList::initializeCapabilities(const char *type) {
748     if (type == NULL) {
749         return OK;
750     }
751 
752     ALOGV("initializeCapabilities %s:%s",
753             mCurrentInfo->mName.c_str(), type);
754 
755     sp<MediaCodecInfo::Capabilities> caps;
756     status_t err = MediaCodec::QueryCapabilities(
757             mCurrentInfo->mName,
758             type,
759             mCurrentInfo->mIsEncoder,
760             &caps);
761     if (err != OK) {
762         return err;
763     } else if (caps == NULL) {
764         ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
765                 mCurrentInfo->mName.c_str(), type,
766                 mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
767         return UNKNOWN_ERROR;
768     }
769 
770     return mCurrentInfo->initializeCapabilities(caps);
771 }
772 
addQuirk(const char ** attrs)773 status_t MediaCodecList::addQuirk(const char **attrs) {
774     const char *name = NULL;
775 
776     size_t i = 0;
777     while (attrs[i] != NULL) {
778         if (!strcmp(attrs[i], "name")) {
779             if (attrs[i + 1] == NULL) {
780                 return -EINVAL;
781             }
782             name = attrs[i + 1];
783             ++i;
784         } else {
785             return -EINVAL;
786         }
787 
788         ++i;
789     }
790 
791     if (name == NULL) {
792         return -EINVAL;
793     }
794 
795     mCurrentInfo->addQuirk(name);
796     return OK;
797 }
798 
addTypeFromAttributes(const char ** attrs)799 status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
800     const char *name = NULL;
801     const char *update = NULL;
802 
803     size_t i = 0;
804     while (attrs[i] != NULL) {
805         if (!strcmp(attrs[i], "name")) {
806             if (attrs[i + 1] == NULL) {
807                 return -EINVAL;
808             }
809             name = attrs[i + 1];
810             ++i;
811         } else if (!strcmp(attrs[i], "update")) {
812             if (attrs[i + 1] == NULL) {
813                 return -EINVAL;
814             }
815             update = attrs[i + 1];
816             ++i;
817         } else {
818             return -EINVAL;
819         }
820 
821         ++i;
822     }
823 
824     if (name == NULL) {
825         return -EINVAL;
826     }
827 
828     bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
829     if (mUpdate != isExistingType) {
830         return -EINVAL;
831     }
832 
833     status_t ret;
834     if (mUpdate) {
835         ret = mCurrentInfo->updateMime(name);
836     } else {
837         ret = mCurrentInfo->addMime(name);
838     }
839 
840     if (ret != OK) {
841         return ret;
842     }
843 
844     // The next step involves trying to load the codec, which may
845     // fail.  Handle this gracefully (by not reporting such mime).
846     if (!mUpdate && initializeCapabilities(name) != OK) {
847         mCurrentInfo->removeMime(name);
848     }
849     return OK;
850 }
851 
852 // legacy method for non-advanced codecs
findCodecByType(const char * type,bool encoder,size_t startIndex) const853 ssize_t MediaCodecList::findCodecByType(
854         const char *type, bool encoder, size_t startIndex) const {
855     static const char *advancedFeatures[] = {
856         "feature-secure-playback",
857         "feature-tunneled-playback",
858     };
859 
860     size_t numCodecs = mCodecInfos.size();
861     for (; startIndex < numCodecs; ++startIndex) {
862         const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
863 
864         if (info.isEncoder() != encoder) {
865             continue;
866         }
867         sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
868         if (capabilities == NULL) {
869             continue;
870         }
871         const sp<AMessage> &details = capabilities->getDetails();
872 
873         int32_t required;
874         bool isAdvanced = false;
875         for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
876             if (details->findInt32(advancedFeatures[ix], &required) &&
877                     required != 0) {
878                 isAdvanced = true;
879                 break;
880             }
881         }
882 
883         if (!isAdvanced) {
884             return startIndex;
885         }
886     }
887 
888     return -ENOENT;
889 }
890 
limitFoundMissingAttr(AString name,const char * attr,bool found=true)891 static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
892     ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
893             (found ? "" : "no "), attr);
894     return -EINVAL;
895 }
896 
limitError(AString name,const char * msg)897 static status_t limitError(AString name, const char *msg) {
898     ALOGE("limit '%s' %s", name.c_str(), msg);
899     return -EINVAL;
900 }
901 
limitInvalidAttr(AString name,const char * attr,AString value)902 static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
903     ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
904             attr, value.c_str());
905     return -EINVAL;
906 }
907 
addLimit(const char ** attrs)908 status_t MediaCodecList::addLimit(const char **attrs) {
909     sp<AMessage> msg = new AMessage();
910 
911     size_t i = 0;
912     while (attrs[i] != NULL) {
913         if (attrs[i + 1] == NULL) {
914             return -EINVAL;
915         }
916 
917         // attributes with values
918         if (!strcmp(attrs[i], "name")
919                 || !strcmp(attrs[i], "default")
920                 || !strcmp(attrs[i], "in")
921                 || !strcmp(attrs[i], "max")
922                 || !strcmp(attrs[i], "min")
923                 || !strcmp(attrs[i], "range")
924                 || !strcmp(attrs[i], "ranges")
925                 || !strcmp(attrs[i], "scale")
926                 || !strcmp(attrs[i], "value")) {
927             msg->setString(attrs[i], attrs[i + 1]);
928             ++i;
929         } else {
930             return -EINVAL;
931         }
932         ++i;
933     }
934 
935     AString name;
936     if (!msg->findString("name", &name)) {
937         ALOGE("limit with no 'name' attribute");
938         return -EINVAL;
939     }
940 
941     // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
942     // measured-frame-rate, measured-blocks-per-second: range
943     // quality: range + default + [scale]
944     // complexity: range + default
945     bool found;
946 
947     if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
948             || name == "blocks-per-second" || name == "complexity"
949             || name == "frame-rate" || name == "quality" || name == "size"
950             || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
951         AString min, max;
952         if (msg->findString("min", &min) && msg->findString("max", &max)) {
953             min.append("-");
954             min.append(max);
955             if (msg->contains("range") || msg->contains("value")) {
956                 return limitError(name, "has 'min' and 'max' as well as 'range' or "
957                         "'value' attributes");
958             }
959             msg->setString("range", min);
960         } else if (msg->contains("min") || msg->contains("max")) {
961             return limitError(name, "has only 'min' or 'max' attribute");
962         } else if (msg->findString("value", &max)) {
963             min = max;
964             min.append("-");
965             min.append(max);
966             if (msg->contains("range")) {
967                 return limitError(name, "has both 'range' and 'value' attributes");
968             }
969             msg->setString("range", min);
970         }
971 
972         AString range, scale = "linear", def, in_;
973         if (!msg->findString("range", &range)) {
974             return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
975         }
976 
977         if ((name == "quality" || name == "complexity") ^
978                 (found = msg->findString("default", &def))) {
979             return limitFoundMissingAttr(name, "default", found);
980         }
981         if (name != "quality" && msg->findString("scale", &scale)) {
982             return limitFoundMissingAttr(name, "scale");
983         }
984         if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
985             return limitFoundMissingAttr(name, "in", found);
986         }
987 
988         if (name == "aspect-ratio") {
989             if (!(in_ == "pixels") && !(in_ == "blocks")) {
990                 return limitInvalidAttr(name, "in", in_);
991             }
992             in_.erase(5, 1); // (pixel|block)-aspect-ratio
993             in_.append("-");
994             in_.append(name);
995             name = in_;
996         }
997         if (name == "quality") {
998             mCurrentInfo->addDetail("quality-scale", scale);
999         }
1000         if (name == "quality" || name == "complexity") {
1001             AString tag = name;
1002             tag.append("-default");
1003             mCurrentInfo->addDetail(tag, def);
1004         }
1005         AString tag = name;
1006         tag.append("-range");
1007         mCurrentInfo->addDetail(tag, range);
1008     } else {
1009         AString max, value, ranges;
1010         if (msg->contains("default")) {
1011             return limitFoundMissingAttr(name, "default");
1012         } else if (msg->contains("in")) {
1013             return limitFoundMissingAttr(name, "in");
1014         } else if ((name == "channel-count" || name == "concurrent-instances") ^
1015                 (found = msg->findString("max", &max))) {
1016             return limitFoundMissingAttr(name, "max", found);
1017         } else if (msg->contains("min")) {
1018             return limitFoundMissingAttr(name, "min");
1019         } else if (msg->contains("range")) {
1020             return limitFoundMissingAttr(name, "range");
1021         } else if ((name == "sample-rate") ^
1022                 (found = msg->findString("ranges", &ranges))) {
1023             return limitFoundMissingAttr(name, "ranges", found);
1024         } else if (msg->contains("scale")) {
1025             return limitFoundMissingAttr(name, "scale");
1026         } else if ((name == "alignment" || name == "block-size") ^
1027                 (found = msg->findString("value", &value))) {
1028             return limitFoundMissingAttr(name, "value", found);
1029         }
1030 
1031         if (max.size()) {
1032             AString tag = "max-";
1033             tag.append(name);
1034             mCurrentInfo->addDetail(tag, max);
1035         } else if (value.size()) {
1036             mCurrentInfo->addDetail(name, value);
1037         } else if (ranges.size()) {
1038             AString tag = name;
1039             tag.append("-ranges");
1040             mCurrentInfo->addDetail(tag, ranges);
1041         } else {
1042             ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
1043         }
1044     }
1045     return OK;
1046 }
1047 
addFeature(const char ** attrs)1048 status_t MediaCodecList::addFeature(const char **attrs) {
1049     size_t i = 0;
1050     const char *name = NULL;
1051     int32_t optional = -1;
1052     int32_t required = -1;
1053     const char *value = NULL;
1054 
1055     while (attrs[i] != NULL) {
1056         if (attrs[i + 1] == NULL) {
1057             return -EINVAL;
1058         }
1059 
1060         // attributes with values
1061         if (!strcmp(attrs[i], "name")) {
1062             name = attrs[i + 1];
1063             ++i;
1064         } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
1065             int value = (int)parseBoolean(attrs[i + 1]);
1066             if (!strcmp(attrs[i], "optional")) {
1067                 optional = value;
1068             } else {
1069                 required = value;
1070             }
1071             ++i;
1072         } else if (!strcmp(attrs[i], "value")) {
1073             value = attrs[i + 1];
1074             ++i;
1075         } else {
1076             return -EINVAL;
1077         }
1078         ++i;
1079     }
1080     if (name == NULL) {
1081         ALOGE("feature with no 'name' attribute");
1082         return -EINVAL;
1083     }
1084 
1085     if (optional == required && optional != -1) {
1086         ALOGE("feature '%s' is both/neither optional and required", name);
1087         return -EINVAL;
1088     }
1089 
1090     if ((optional != -1 || required != -1) && (value != NULL)) {
1091         ALOGE("feature '%s' has both a value and optional/required attribute", name);
1092         return -EINVAL;
1093     }
1094 
1095     if (value != NULL) {
1096         mCurrentInfo->addFeature(name, value);
1097     } else {
1098         mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
1099     }
1100     return OK;
1101 }
1102 
findCodecByName(const char * name) const1103 ssize_t MediaCodecList::findCodecByName(const char *name) const {
1104     for (size_t i = 0; i < mCodecInfos.size(); ++i) {
1105         const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
1106 
1107         if (info.mName == name) {
1108             return i;
1109         }
1110     }
1111 
1112     return -ENOENT;
1113 }
1114 
countCodecs() const1115 size_t MediaCodecList::countCodecs() const {
1116     return mCodecInfos.size();
1117 }
1118 
getGlobalSettings() const1119 const sp<AMessage> MediaCodecList::getGlobalSettings() const {
1120     return mGlobalSettings;
1121 }
1122 
1123 //static
isSoftwareCodec(const AString & componentName)1124 bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
1125     return componentName.startsWithIgnoreCase("OMX.google.")
1126         || !componentName.startsWithIgnoreCase("OMX.");
1127 }
1128 
compareSoftwareCodecsFirst(const AString * name1,const AString * name2)1129 static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
1130     // sort order 1: software codecs are first (lower)
1131     bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
1132     bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
1133     if (isSoftwareCodec1 != isSoftwareCodec2) {
1134         return isSoftwareCodec2 - isSoftwareCodec1;
1135     }
1136 
1137     // sort order 2: OMX codecs are first (lower)
1138     bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
1139     bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
1140     return isOMX2 - isOMX1;
1141 }
1142 
1143 //static
findMatchingCodecs(const char * mime,bool encoder,uint32_t flags,Vector<AString> * matches)1144 void MediaCodecList::findMatchingCodecs(
1145         const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
1146     matches->clear();
1147 
1148     const sp<IMediaCodecList> list = getInstance();
1149     if (list == NULL) {
1150         return;
1151     }
1152 
1153     size_t index = 0;
1154     for (;;) {
1155         ssize_t matchIndex =
1156             list->findCodecByType(mime, encoder, index);
1157 
1158         if (matchIndex < 0) {
1159             break;
1160         }
1161 
1162         index = matchIndex + 1;
1163 
1164         const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
1165         CHECK(info != NULL);
1166         AString componentName = info->getCodecName();
1167 
1168         if (!((flags & kHardwareCodecsOnly) && !isSoftwareCodec(componentName))) {
1169             matches->push(componentName);
1170             ALOGV("matching '%s'", componentName.c_str());
1171         }
1172     }
1173 
1174     if (flags & kPreferSoftwareCodecs) {
1175         matches->sort(compareSoftwareCodecsFirst);
1176     }
1177 }
1178 
1179 // static
getQuirksFor(const char * componentName)1180 uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
1181     const sp<IMediaCodecList> list = getInstance();
1182     if (list == NULL) {
1183         return 0;
1184     }
1185 
1186     ssize_t ix = list->findCodecByName(componentName);
1187     if (ix < 0) {
1188         return 0;
1189     }
1190 
1191     const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
1192 
1193     uint32_t quirks = 0;
1194     if (info->hasQuirk("requires-allocate-on-input-ports")) {
1195         quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
1196     }
1197     if (info->hasQuirk("requires-allocate-on-output-ports")) {
1198         quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
1199     }
1200 
1201     return quirks;
1202 }
1203 
1204 }  // namespace android
1205