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