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