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