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