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