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