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
45namespace android {
46
47static Mutex sInitMutex;
48
49static 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
58static 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
82sp<IMediaCodecList> MediaCodecList::sCodecList;
83
84// static
85void *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
113sp<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
137static Mutex sRemoteInitMutex;
138
139sp<IMediaCodecList> MediaCodecList::sRemoteList;
140
141sp<MediaCodecList::BinderDeathObserver> MediaCodecList::sBinderDeathObserver;
142
143void MediaCodecList::BinderDeathObserver::binderDied(const wp<IBinder> &who __unused) {
144    Mutex::Autolock _l(sRemoteInitMutex);
145    sRemoteList.clear();
146    sBinderDeathObserver.clear();
147}
148
149// static
150sp<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.
173static const char *kConfigLocationList[] =
174        {"/odm/etc", "/vendor/etc", "/etc"};
175static const int kConfigLocationListSize =
176        (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
177
178#define MEDIA_CODECS_CONFIG_FILE_PATH_MAX_LENGTH 128
179
180static 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
195MediaCodecList::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
210void 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
343MediaCodecList::~MediaCodecList() {
344}
345
346status_t MediaCodecList::initCheck() const {
347    return mInitCheck;
348}
349
350void 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
401void MediaCodecList::StartElementHandlerWrapper(
402        void *me, const char *name, const char **attrs) {
403    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
404}
405
406// static
407void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
408    static_cast<MediaCodecList *>(me)->endElementHandler(name);
409}
410
411status_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
452void 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
550void 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
629status_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
674void 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
699status_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
772status_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
798status_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
824status_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
878ssize_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
916static 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
922static status_t limitError(const AString &name, const char *msg) {
923    ALOGE("limit '%s' %s", name.c_str(), msg);
924    return -EINVAL;
925}
926
927static 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
933status_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
1073status_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
1128ssize_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
1140size_t MediaCodecList::countCodecs() const {
1141    return mCodecInfos.size();
1142}
1143
1144const sp<AMessage> MediaCodecList::getGlobalSettings() const {
1145    return mGlobalSettings;
1146}
1147
1148//static
1149bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
1150    return componentName.startsWithIgnoreCase("OMX.google.")
1151        || !componentName.startsWithIgnoreCase("OMX.");
1152}
1153
1154static 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
1169void 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
1207uint32_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