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