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