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