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