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