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/MediaCodec.h>
35#include <media/stagefright/MediaCodecList.h>
36#include <media/stagefright/MediaErrors.h>
37#include <media/stagefright/OMXClient.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    sp<MediaCodecInfo::Capabilities> caps;
756    status_t err = MediaCodec::QueryCapabilities(
757            mCurrentInfo->mName,
758            type,
759            mCurrentInfo->mIsEncoder,
760            &caps);
761    if (err != OK) {
762        return err;
763    } else if (caps == NULL) {
764        ALOGE("MediaCodec::QueryCapabilities returned OK but no capabilities for '%s':'%s':'%s'",
765                mCurrentInfo->mName.c_str(), type,
766                mCurrentInfo->mIsEncoder ? "encoder" : "decoder");
767        return UNKNOWN_ERROR;
768    }
769
770    return mCurrentInfo->initializeCapabilities(caps);
771}
772
773status_t MediaCodecList::addQuirk(const char **attrs) {
774    const char *name = NULL;
775
776    size_t i = 0;
777    while (attrs[i] != NULL) {
778        if (!strcmp(attrs[i], "name")) {
779            if (attrs[i + 1] == NULL) {
780                return -EINVAL;
781            }
782            name = attrs[i + 1];
783            ++i;
784        } else {
785            return -EINVAL;
786        }
787
788        ++i;
789    }
790
791    if (name == NULL) {
792        return -EINVAL;
793    }
794
795    mCurrentInfo->addQuirk(name);
796    return OK;
797}
798
799status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
800    const char *name = NULL;
801    const char *update = NULL;
802
803    size_t i = 0;
804    while (attrs[i] != NULL) {
805        if (!strcmp(attrs[i], "name")) {
806            if (attrs[i + 1] == NULL) {
807                return -EINVAL;
808            }
809            name = attrs[i + 1];
810            ++i;
811        } else if (!strcmp(attrs[i], "update")) {
812            if (attrs[i + 1] == NULL) {
813                return -EINVAL;
814            }
815            update = attrs[i + 1];
816            ++i;
817        } else {
818            return -EINVAL;
819        }
820
821        ++i;
822    }
823
824    if (name == NULL) {
825        return -EINVAL;
826    }
827
828    bool isExistingType = (mCurrentInfo->getCapabilitiesFor(name) != NULL);
829    if (mUpdate != isExistingType) {
830        return -EINVAL;
831    }
832
833    status_t ret;
834    if (mUpdate) {
835        ret = mCurrentInfo->updateMime(name);
836    } else {
837        ret = mCurrentInfo->addMime(name);
838    }
839
840    if (ret != OK) {
841        return ret;
842    }
843
844    // The next step involves trying to load the codec, which may
845    // fail.  Handle this gracefully (by not reporting such mime).
846    if (!mUpdate && initializeCapabilities(name) != OK) {
847        mCurrentInfo->removeMime(name);
848    }
849    return OK;
850}
851
852// legacy method for non-advanced codecs
853ssize_t MediaCodecList::findCodecByType(
854        const char *type, bool encoder, size_t startIndex) const {
855    static const char *advancedFeatures[] = {
856        "feature-secure-playback",
857        "feature-tunneled-playback",
858    };
859
860    size_t numCodecs = mCodecInfos.size();
861    for (; startIndex < numCodecs; ++startIndex) {
862        const MediaCodecInfo &info = *mCodecInfos.itemAt(startIndex).get();
863
864        if (info.isEncoder() != encoder) {
865            continue;
866        }
867        sp<MediaCodecInfo::Capabilities> capabilities = info.getCapabilitiesFor(type);
868        if (capabilities == NULL) {
869            continue;
870        }
871        const sp<AMessage> &details = capabilities->getDetails();
872
873        int32_t required;
874        bool isAdvanced = false;
875        for (size_t ix = 0; ix < ARRAY_SIZE(advancedFeatures); ix++) {
876            if (details->findInt32(advancedFeatures[ix], &required) &&
877                    required != 0) {
878                isAdvanced = true;
879                break;
880            }
881        }
882
883        if (!isAdvanced) {
884            return startIndex;
885        }
886    }
887
888    return -ENOENT;
889}
890
891static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
892    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
893            (found ? "" : "no "), attr);
894    return -EINVAL;
895}
896
897static status_t limitError(AString name, const char *msg) {
898    ALOGE("limit '%s' %s", name.c_str(), msg);
899    return -EINVAL;
900}
901
902static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
903    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
904            attr, value.c_str());
905    return -EINVAL;
906}
907
908status_t MediaCodecList::addLimit(const char **attrs) {
909    sp<AMessage> msg = new AMessage();
910
911    size_t i = 0;
912    while (attrs[i] != NULL) {
913        if (attrs[i + 1] == NULL) {
914            return -EINVAL;
915        }
916
917        // attributes with values
918        if (!strcmp(attrs[i], "name")
919                || !strcmp(attrs[i], "default")
920                || !strcmp(attrs[i], "in")
921                || !strcmp(attrs[i], "max")
922                || !strcmp(attrs[i], "min")
923                || !strcmp(attrs[i], "range")
924                || !strcmp(attrs[i], "ranges")
925                || !strcmp(attrs[i], "scale")
926                || !strcmp(attrs[i], "value")) {
927            msg->setString(attrs[i], attrs[i + 1]);
928            ++i;
929        } else {
930            return -EINVAL;
931        }
932        ++i;
933    }
934
935    AString name;
936    if (!msg->findString("name", &name)) {
937        ALOGE("limit with no 'name' attribute");
938        return -EINVAL;
939    }
940
941    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio,
942    // measured-frame-rate, measured-blocks-per-second: range
943    // quality: range + default + [scale]
944    // complexity: range + default
945    bool found;
946
947    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
948            || name == "blocks-per-second" || name == "complexity"
949            || name == "frame-rate" || name == "quality" || name == "size"
950            || name == "measured-blocks-per-second" || name.startsWith("measured-frame-rate-")) {
951        AString min, max;
952        if (msg->findString("min", &min) && msg->findString("max", &max)) {
953            min.append("-");
954            min.append(max);
955            if (msg->contains("range") || msg->contains("value")) {
956                return limitError(name, "has 'min' and 'max' as well as 'range' or "
957                        "'value' attributes");
958            }
959            msg->setString("range", min);
960        } else if (msg->contains("min") || msg->contains("max")) {
961            return limitError(name, "has only 'min' or 'max' attribute");
962        } else if (msg->findString("value", &max)) {
963            min = max;
964            min.append("-");
965            min.append(max);
966            if (msg->contains("range")) {
967                return limitError(name, "has both 'range' and 'value' attributes");
968            }
969            msg->setString("range", min);
970        }
971
972        AString range, scale = "linear", def, in_;
973        if (!msg->findString("range", &range)) {
974            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
975        }
976
977        if ((name == "quality" || name == "complexity") ^
978                (found = msg->findString("default", &def))) {
979            return limitFoundMissingAttr(name, "default", found);
980        }
981        if (name != "quality" && msg->findString("scale", &scale)) {
982            return limitFoundMissingAttr(name, "scale");
983        }
984        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
985            return limitFoundMissingAttr(name, "in", found);
986        }
987
988        if (name == "aspect-ratio") {
989            if (!(in_ == "pixels") && !(in_ == "blocks")) {
990                return limitInvalidAttr(name, "in", in_);
991            }
992            in_.erase(5, 1); // (pixel|block)-aspect-ratio
993            in_.append("-");
994            in_.append(name);
995            name = in_;
996        }
997        if (name == "quality") {
998            mCurrentInfo->addDetail("quality-scale", scale);
999        }
1000        if (name == "quality" || name == "complexity") {
1001            AString tag = name;
1002            tag.append("-default");
1003            mCurrentInfo->addDetail(tag, def);
1004        }
1005        AString tag = name;
1006        tag.append("-range");
1007        mCurrentInfo->addDetail(tag, range);
1008    } else {
1009        AString max, value, ranges;
1010        if (msg->contains("default")) {
1011            return limitFoundMissingAttr(name, "default");
1012        } else if (msg->contains("in")) {
1013            return limitFoundMissingAttr(name, "in");
1014        } else if ((name == "channel-count" || name == "concurrent-instances") ^
1015                (found = msg->findString("max", &max))) {
1016            return limitFoundMissingAttr(name, "max", found);
1017        } else if (msg->contains("min")) {
1018            return limitFoundMissingAttr(name, "min");
1019        } else if (msg->contains("range")) {
1020            return limitFoundMissingAttr(name, "range");
1021        } else if ((name == "sample-rate") ^
1022                (found = msg->findString("ranges", &ranges))) {
1023            return limitFoundMissingAttr(name, "ranges", found);
1024        } else if (msg->contains("scale")) {
1025            return limitFoundMissingAttr(name, "scale");
1026        } else if ((name == "alignment" || name == "block-size") ^
1027                (found = msg->findString("value", &value))) {
1028            return limitFoundMissingAttr(name, "value", found);
1029        }
1030
1031        if (max.size()) {
1032            AString tag = "max-";
1033            tag.append(name);
1034            mCurrentInfo->addDetail(tag, max);
1035        } else if (value.size()) {
1036            mCurrentInfo->addDetail(name, value);
1037        } else if (ranges.size()) {
1038            AString tag = name;
1039            tag.append("-ranges");
1040            mCurrentInfo->addDetail(tag, ranges);
1041        } else {
1042            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
1043        }
1044    }
1045    return OK;
1046}
1047
1048status_t MediaCodecList::addFeature(const char **attrs) {
1049    size_t i = 0;
1050    const char *name = NULL;
1051    int32_t optional = -1;
1052    int32_t required = -1;
1053    const char *value = NULL;
1054
1055    while (attrs[i] != NULL) {
1056        if (attrs[i + 1] == NULL) {
1057            return -EINVAL;
1058        }
1059
1060        // attributes with values
1061        if (!strcmp(attrs[i], "name")) {
1062            name = attrs[i + 1];
1063            ++i;
1064        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
1065            int value = (int)parseBoolean(attrs[i + 1]);
1066            if (!strcmp(attrs[i], "optional")) {
1067                optional = value;
1068            } else {
1069                required = value;
1070            }
1071            ++i;
1072        } else if (!strcmp(attrs[i], "value")) {
1073            value = attrs[i + 1];
1074            ++i;
1075        } else {
1076            return -EINVAL;
1077        }
1078        ++i;
1079    }
1080    if (name == NULL) {
1081        ALOGE("feature with no 'name' attribute");
1082        return -EINVAL;
1083    }
1084
1085    if (optional == required && optional != -1) {
1086        ALOGE("feature '%s' is both/neither optional and required", name);
1087        return -EINVAL;
1088    }
1089
1090    if ((optional != -1 || required != -1) && (value != NULL)) {
1091        ALOGE("feature '%s' has both a value and optional/required attribute", name);
1092        return -EINVAL;
1093    }
1094
1095    if (value != NULL) {
1096        mCurrentInfo->addFeature(name, value);
1097    } else {
1098        mCurrentInfo->addFeature(name, (required == 1) || (optional == 0));
1099    }
1100    return OK;
1101}
1102
1103ssize_t MediaCodecList::findCodecByName(const char *name) const {
1104    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
1105        const MediaCodecInfo &info = *mCodecInfos.itemAt(i).get();
1106
1107        if (info.mName == name) {
1108            return i;
1109        }
1110    }
1111
1112    return -ENOENT;
1113}
1114
1115size_t MediaCodecList::countCodecs() const {
1116    return mCodecInfos.size();
1117}
1118
1119const sp<AMessage> MediaCodecList::getGlobalSettings() const {
1120    return mGlobalSettings;
1121}
1122
1123//static
1124bool MediaCodecList::isSoftwareCodec(const AString &componentName) {
1125    return componentName.startsWithIgnoreCase("OMX.google.")
1126        || !componentName.startsWithIgnoreCase("OMX.");
1127}
1128
1129static int compareSoftwareCodecsFirst(const AString *name1, const AString *name2) {
1130    // sort order 1: software codecs are first (lower)
1131    bool isSoftwareCodec1 = MediaCodecList::isSoftwareCodec(*name1);
1132    bool isSoftwareCodec2 = MediaCodecList::isSoftwareCodec(*name2);
1133    if (isSoftwareCodec1 != isSoftwareCodec2) {
1134        return isSoftwareCodec2 - isSoftwareCodec1;
1135    }
1136
1137    // sort order 2: OMX codecs are first (lower)
1138    bool isOMX1 = name1->startsWithIgnoreCase("OMX.");
1139    bool isOMX2 = name2->startsWithIgnoreCase("OMX.");
1140    return isOMX2 - isOMX1;
1141}
1142
1143//static
1144void MediaCodecList::findMatchingCodecs(
1145        const char *mime, bool encoder, uint32_t flags, Vector<AString> *matches) {
1146    matches->clear();
1147
1148    const sp<IMediaCodecList> list = getInstance();
1149    if (list == NULL) {
1150        return;
1151    }
1152
1153    size_t index = 0;
1154    for (;;) {
1155        ssize_t matchIndex =
1156            list->findCodecByType(mime, encoder, index);
1157
1158        if (matchIndex < 0) {
1159            break;
1160        }
1161
1162        index = matchIndex + 1;
1163
1164        const sp<MediaCodecInfo> info = list->getCodecInfo(matchIndex);
1165        CHECK(info != NULL);
1166        AString componentName = info->getCodecName();
1167
1168        if (!((flags & kHardwareCodecsOnly) && !isSoftwareCodec(componentName))) {
1169            matches->push(componentName);
1170            ALOGV("matching '%s'", componentName.c_str());
1171        }
1172    }
1173
1174    if (flags & kPreferSoftwareCodecs) {
1175        matches->sort(compareSoftwareCodecsFirst);
1176    }
1177}
1178
1179// static
1180uint32_t MediaCodecList::getQuirksFor(const char *componentName) {
1181    const sp<IMediaCodecList> list = getInstance();
1182    if (list == NULL) {
1183        return 0;
1184    }
1185
1186    ssize_t ix = list->findCodecByName(componentName);
1187    if (ix < 0) {
1188        return 0;
1189    }
1190
1191    const sp<MediaCodecInfo> info = list->getCodecInfo(ix);
1192
1193    uint32_t quirks = 0;
1194    if (info->hasQuirk("requires-allocate-on-input-ports")) {
1195        quirks |= ACodec::kRequiresAllocateBufferOnInputPorts;
1196    }
1197    if (info->hasQuirk("requires-allocate-on-output-ports")) {
1198        quirks |= ACodec::kRequiresAllocateBufferOnOutputPorts;
1199    }
1200
1201    return quirks;
1202}
1203
1204}  // namespace android
1205