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