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