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