MediaCodecList.cpp revision cf1f53baa636f1782ff924d6003c70c6b8542c0b
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 <media/stagefright/MediaCodecList.h>
22
23#include <media/stagefright/foundation/ADebug.h>
24#include <media/stagefright/foundation/AMessage.h>
25#include <media/stagefright/MediaErrors.h>
26#include <media/stagefright/OMXClient.h>
27#include <media/stagefright/OMXCodec.h>
28#include <utils/threads.h>
29
30#include <libexpat/expat.h>
31
32namespace android {
33
34static Mutex sInitMutex;
35
36// static
37MediaCodecList *MediaCodecList::sCodecList;
38
39// static
40const MediaCodecList *MediaCodecList::getInstance() {
41    Mutex::Autolock autoLock(sInitMutex);
42
43    if (sCodecList == NULL) {
44        sCodecList = new MediaCodecList;
45    }
46
47    return sCodecList->initCheck() == OK ? sCodecList : NULL;
48}
49
50MediaCodecList::MediaCodecList()
51    : mInitCheck(NO_INIT) {
52    parseTopLevelXMLFile("/etc/media_codecs.xml");
53}
54
55void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
56    // get href_base
57    char *href_base_end = strrchr(codecs_xml, '/');
58    if (href_base_end != NULL) {
59        mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
60    }
61
62    mInitCheck = OK;
63    mCurrentSection = SECTION_TOPLEVEL;
64    mDepth = 0;
65
66    parseXMLFile(codecs_xml);
67
68    if (mInitCheck != OK) {
69        mCodecInfos.clear();
70        mCodecQuirks.clear();
71        return;
72    }
73
74    for (size_t i = mCodecInfos.size(); i-- > 0;) {
75        CodecInfo *info = &mCodecInfos.editItemAt(i);
76
77        if (info->mTypes == 0) {
78            // No types supported by this component???
79            ALOGW("Component %s does not support any type of media?",
80                  info->mName.c_str());
81
82            mCodecInfos.removeAt(i);
83#if LOG_NDEBUG == 0
84        } else {
85            for (size_t type_ix = 0; type_ix < mTypes.size(); ++type_ix) {
86                uint32_t typeMask = 1ul << mTypes.valueAt(type_ix);
87                if (info->mTypes & typeMask) {
88                    AString mime = mTypes.keyAt(type_ix);
89                    uint32_t bit = mTypes.valueAt(type_ix);
90
91                    ALOGV("%s codec info for %s: %s", info->mName.c_str(), mime.c_str(),
92                            info->mCaps.editValueFor(bit)->debugString().c_str());
93                }
94            }
95#endif
96        }
97    }
98
99#if 0
100    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
101        const CodecInfo &info = mCodecInfos.itemAt(i);
102
103        AString line = info.mName;
104        line.append(" supports ");
105        for (size_t j = 0; j < mTypes.size(); ++j) {
106            uint32_t value = mTypes.valueAt(j);
107
108            if (info.mTypes & (1ul << value)) {
109                line.append(mTypes.keyAt(j));
110                line.append(" ");
111            }
112        }
113
114        ALOGI("%s", line.c_str());
115    }
116#endif
117}
118
119MediaCodecList::~MediaCodecList() {
120}
121
122status_t MediaCodecList::initCheck() const {
123    return mInitCheck;
124}
125
126void MediaCodecList::parseXMLFile(const char *path) {
127    FILE *file = fopen(path, "r");
128
129    if (file == NULL) {
130        ALOGW("unable to open media codecs configuration xml file: %s", path);
131        mInitCheck = NAME_NOT_FOUND;
132        return;
133    }
134
135    XML_Parser parser = ::XML_ParserCreate(NULL);
136    CHECK(parser != NULL);
137
138    ::XML_SetUserData(parser, this);
139    ::XML_SetElementHandler(
140            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
141
142    const int BUFF_SIZE = 512;
143    while (mInitCheck == OK) {
144        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
145        if (buff == NULL) {
146            ALOGE("failed in call to XML_GetBuffer()");
147            mInitCheck = UNKNOWN_ERROR;
148            break;
149        }
150
151        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
152        if (bytes_read < 0) {
153            ALOGE("failed in call to read");
154            mInitCheck = ERROR_IO;
155            break;
156        }
157
158        XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
159        if (status != XML_STATUS_OK) {
160            ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
161            mInitCheck = ERROR_MALFORMED;
162            break;
163        }
164
165        if (bytes_read == 0) {
166            break;
167        }
168    }
169
170    ::XML_ParserFree(parser);
171
172    fclose(file);
173    file = NULL;
174}
175
176// static
177void MediaCodecList::StartElementHandlerWrapper(
178        void *me, const char *name, const char **attrs) {
179    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
180}
181
182// static
183void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
184    static_cast<MediaCodecList *>(me)->endElementHandler(name);
185}
186
187status_t MediaCodecList::includeXMLFile(const char **attrs) {
188    const char *href = NULL;
189    size_t i = 0;
190    while (attrs[i] != NULL) {
191        if (!strcmp(attrs[i], "href")) {
192            if (attrs[i + 1] == NULL) {
193                return -EINVAL;
194            }
195            href = attrs[i + 1];
196            ++i;
197        } else {
198            return -EINVAL;
199        }
200        ++i;
201    }
202
203    // For security reasons and for simplicity, file names can only contain
204    // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
205    for (i = 0; href[i] != '\0'; i++) {
206        if (href[i] == '.' || href[i] == '_' ||
207                (href[i] >= '0' && href[i] <= '9') ||
208                (href[i] >= 'A' && href[i] <= 'Z') ||
209                (href[i] >= 'a' && href[i] <= 'z')) {
210            continue;
211        }
212        ALOGE("invalid include file name: %s", href);
213        return -EINVAL;
214    }
215
216    AString filename = href;
217    if (!filename.startsWith("media_codecs_") ||
218        !filename.endsWith(".xml")) {
219        ALOGE("invalid include file name: %s", href);
220        return -EINVAL;
221    }
222    filename.insert(mHrefBase, 0);
223
224    parseXMLFile(filename.c_str());
225    return mInitCheck;
226}
227
228void MediaCodecList::startElementHandler(
229        const char *name, const char **attrs) {
230    if (mInitCheck != OK) {
231        return;
232    }
233
234    bool inType = true;
235
236    if (!strcmp(name, "Include")) {
237        mInitCheck = includeXMLFile(attrs);
238        if (mInitCheck == OK) {
239            mPastSections.push(mCurrentSection);
240            mCurrentSection = SECTION_INCLUDE;
241        }
242        ++mDepth;
243        return;
244    }
245
246    switch (mCurrentSection) {
247        case SECTION_TOPLEVEL:
248        {
249            if (!strcmp(name, "Decoders")) {
250                mCurrentSection = SECTION_DECODERS;
251            } else if (!strcmp(name, "Encoders")) {
252                mCurrentSection = SECTION_ENCODERS;
253            }
254            break;
255        }
256
257        case SECTION_DECODERS:
258        {
259            if (!strcmp(name, "MediaCodec")) {
260                mInitCheck =
261                    addMediaCodecFromAttributes(false /* encoder */, attrs);
262
263                mCurrentSection = SECTION_DECODER;
264            }
265            break;
266        }
267
268        case SECTION_ENCODERS:
269        {
270            if (!strcmp(name, "MediaCodec")) {
271                mInitCheck =
272                    addMediaCodecFromAttributes(true /* encoder */, attrs);
273
274                mCurrentSection = SECTION_ENCODER;
275            }
276            break;
277        }
278
279        case SECTION_DECODER:
280        case SECTION_ENCODER:
281        {
282            if (!strcmp(name, "Quirk")) {
283                mInitCheck = addQuirk(attrs);
284            } else if (!strcmp(name, "Type")) {
285                mInitCheck = addTypeFromAttributes(attrs);
286                mCurrentSection =
287                    (mCurrentSection == SECTION_DECODER
288                            ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
289            }
290        }
291        inType = false;
292        // fall through
293
294        case SECTION_DECODER_TYPE:
295        case SECTION_ENCODER_TYPE:
296        {
297            CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
298            // ignore limits and features specified outside of type
299            bool outside = !inType && info->mSoleType == 0;
300            if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
301                ALOGW("ignoring %s specified outside of a Type", name);
302            } else if (!strcmp(name, "Limit")) {
303                mInitCheck = addLimit(attrs);
304            } else if (!strcmp(name, "Feature")) {
305                mInitCheck = addFeature(attrs);
306            }
307            break;
308        }
309
310        default:
311            break;
312    }
313
314    ++mDepth;
315}
316
317void MediaCodecList::endElementHandler(const char *name) {
318    if (mInitCheck != OK) {
319        return;
320    }
321
322    switch (mCurrentSection) {
323        case SECTION_DECODERS:
324        {
325            if (!strcmp(name, "Decoders")) {
326                mCurrentSection = SECTION_TOPLEVEL;
327            }
328            break;
329        }
330
331        case SECTION_ENCODERS:
332        {
333            if (!strcmp(name, "Encoders")) {
334                mCurrentSection = SECTION_TOPLEVEL;
335            }
336            break;
337        }
338
339        case SECTION_DECODER_TYPE:
340        case SECTION_ENCODER_TYPE:
341        {
342            if (!strcmp(name, "Type")) {
343                mCurrentSection =
344                    (mCurrentSection == SECTION_DECODER_TYPE
345                            ? SECTION_DECODER : SECTION_ENCODER);
346
347                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
348                info->mCurrentCaps = NULL;
349            }
350            break;
351        }
352
353        case SECTION_DECODER:
354        {
355            if (!strcmp(name, "MediaCodec")) {
356                mCurrentSection = SECTION_DECODERS;
357
358                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
359                info->mCurrentCaps = NULL;
360            }
361            break;
362        }
363
364        case SECTION_ENCODER:
365        {
366            if (!strcmp(name, "MediaCodec")) {
367                mCurrentSection = SECTION_ENCODERS;
368
369                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
370                info->mCurrentCaps = NULL;
371            }
372            break;
373        }
374
375        case SECTION_INCLUDE:
376        {
377            if (!strcmp(name, "Include") && mPastSections.size() > 0) {
378                mCurrentSection = mPastSections.top();
379                mPastSections.pop();
380            }
381            break;
382        }
383
384        default:
385            break;
386    }
387
388    --mDepth;
389}
390
391status_t MediaCodecList::addMediaCodecFromAttributes(
392        bool encoder, const char **attrs) {
393    const char *name = NULL;
394    const char *type = NULL;
395
396    size_t i = 0;
397    while (attrs[i] != NULL) {
398        if (!strcmp(attrs[i], "name")) {
399            if (attrs[i + 1] == NULL) {
400                return -EINVAL;
401            }
402            name = attrs[i + 1];
403            ++i;
404        } else if (!strcmp(attrs[i], "type")) {
405            if (attrs[i + 1] == NULL) {
406                return -EINVAL;
407            }
408            type = attrs[i + 1];
409            ++i;
410        } else {
411            return -EINVAL;
412        }
413
414        ++i;
415    }
416
417    if (name == NULL) {
418        return -EINVAL;
419    }
420
421    addMediaCodec(encoder, name, type);
422
423    return OK;
424}
425
426void MediaCodecList::addMediaCodec(
427        bool encoder, const char *name, const char *type) {
428    mCodecInfos.push();
429    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
430    info->mName = name;
431    info->mIsEncoder = encoder;
432    info->mSoleType = 0;
433    info->mTypes = 0;
434    info->mQuirks = 0;
435    info->mCurrentCaps = NULL;
436
437    if (type != NULL) {
438        addType(type);
439        // if type was specified in attributes, we do not allow
440        // subsequent types
441        info->mSoleType = info->mTypes;
442    }
443}
444
445status_t MediaCodecList::addQuirk(const char **attrs) {
446    const char *name = NULL;
447
448    size_t i = 0;
449    while (attrs[i] != NULL) {
450        if (!strcmp(attrs[i], "name")) {
451            if (attrs[i + 1] == NULL) {
452                return -EINVAL;
453            }
454            name = attrs[i + 1];
455            ++i;
456        } else {
457            return -EINVAL;
458        }
459
460        ++i;
461    }
462
463    if (name == NULL) {
464        return -EINVAL;
465    }
466
467    uint32_t bit;
468    ssize_t index = mCodecQuirks.indexOfKey(name);
469    if (index < 0) {
470        bit = mCodecQuirks.size();
471
472        if (bit == 32) {
473            ALOGW("Too many distinct quirk names in configuration.");
474            return OK;
475        }
476
477        mCodecQuirks.add(name, bit);
478    } else {
479        bit = mCodecQuirks.valueAt(index);
480    }
481
482    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
483    info->mQuirks |= 1ul << bit;
484
485    return OK;
486}
487
488status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
489    const char *name = NULL;
490
491    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
492    if (info->mSoleType != 0) {
493        ALOGE("Codec '%s' already had its type specified", info->mName.c_str());
494        return -EINVAL;
495    }
496
497    size_t i = 0;
498    while (attrs[i] != NULL) {
499        if (!strcmp(attrs[i], "name")) {
500            if (attrs[i + 1] == NULL) {
501                return -EINVAL;
502            }
503            name = attrs[i + 1];
504            ++i;
505        } else {
506            return -EINVAL;
507        }
508
509        ++i;
510    }
511
512    if (name == NULL) {
513        return -EINVAL;
514    }
515
516    addType(name);
517
518    return OK;
519}
520
521void MediaCodecList::addType(const char *name) {
522    uint32_t bit;
523    ssize_t index = mTypes.indexOfKey(name);
524    if (index < 0) {
525        bit = mTypes.size();
526
527        if (bit == 32) {
528            ALOGW("Too many distinct type names in configuration.");
529            return;
530        }
531
532        mTypes.add(name, bit);
533    } else {
534        bit = mTypes.valueAt(index);
535    }
536
537    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
538    info->mTypes |= 1ul << bit;
539    if (info->mCaps.indexOfKey(bit) < 0) {
540        AMessage *msg = new AMessage();
541        info->mCaps.add(bit, msg);
542    }
543    info->mCurrentCaps = info->mCaps.editValueFor(bit);
544}
545
546ssize_t MediaCodecList::findCodecByType(
547        const char *type, bool encoder, size_t startIndex) const {
548    ssize_t typeIndex = mTypes.indexOfKey(type);
549
550    if (typeIndex < 0) {
551        return -ENOENT;
552    }
553
554    uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
555
556    while (startIndex < mCodecInfos.size()) {
557        const CodecInfo &info = mCodecInfos.itemAt(startIndex);
558
559        if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
560            return startIndex;
561        }
562
563        ++startIndex;
564    }
565
566    return -ENOENT;
567}
568
569static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
570    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
571            (found ? "" : "no "), attr);
572    return -EINVAL;
573}
574
575static status_t limitError(AString name, const char *msg) {
576    ALOGE("limit '%s' %s", name.c_str(), msg);
577    return -EINVAL;
578}
579
580static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
581    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
582            attr, value.c_str());
583    return -EINVAL;
584}
585
586status_t MediaCodecList::addLimit(const char **attrs) {
587    sp<AMessage> msg = new AMessage();
588
589    size_t i = 0;
590    while (attrs[i] != NULL) {
591        if (attrs[i + 1] == NULL) {
592            return -EINVAL;
593        }
594
595        // attributes with values
596        if (!strcmp(attrs[i], "name")
597                || !strcmp(attrs[i], "default")
598                || !strcmp(attrs[i], "in")
599                || !strcmp(attrs[i], "max")
600                || !strcmp(attrs[i], "min")
601                || !strcmp(attrs[i], "range")
602                || !strcmp(attrs[i], "ranges")
603                || !strcmp(attrs[i], "scale")
604                || !strcmp(attrs[i], "value")) {
605            msg->setString(attrs[i], attrs[i + 1]);
606            ++i;
607        } else {
608            return -EINVAL;
609        }
610        ++i;
611    }
612
613    AString name;
614    if (!msg->findString("name", &name)) {
615        ALOGE("limit with no 'name' attribute");
616        return -EINVAL;
617    }
618
619    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
620
621    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range
622    // quality: range + default + [scale]
623    // complexity: range + default
624    bool found;
625    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
626            || name == "blocks-per-second" || name == "complexity"
627            || name == "frame-rate" || name == "quality" || name == "size") {
628        AString min, max;
629        if (msg->findString("min", &min) && msg->findString("max", &max)) {
630            min.append("-");
631            min.append(max);
632            if (msg->contains("range") || msg->contains("value")) {
633                return limitError(name, "has 'min' and 'max' as well as 'range' or "
634                        "'value' attributes");
635            }
636            msg->setString("range", min);
637        } else if (msg->contains("min") || msg->contains("max")) {
638            return limitError(name, "has only 'min' or 'max' attribute");
639        } else if (msg->findString("value", &max)) {
640            min = max;
641            min.append("-");
642            min.append(max);
643            if (msg->contains("range")) {
644                return limitError(name, "has both 'range' and 'value' attributes");
645            }
646            msg->setString("range", min);
647        }
648
649        AString range, scale = "linear", def, in_;
650        if (!msg->findString("range", &range)) {
651            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
652        }
653
654        if ((name == "quality" || name == "complexity") ^
655                (found = msg->findString("default", &def))) {
656            return limitFoundMissingAttr(name, "default", found);
657        }
658        if (name != "quality" && msg->findString("scale", &scale)) {
659            return limitFoundMissingAttr(name, "scale");
660        }
661        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
662            return limitFoundMissingAttr(name, "in", found);
663        }
664
665        if (name == "aspect-ratio") {
666            if (!(in_ == "pixels") && !(in_ == "blocks")) {
667                return limitInvalidAttr(name, "in", in_);
668            }
669            in_.erase(5, 1); // (pixel|block)-aspect-ratio
670            in_.append("-");
671            in_.append(name);
672            name = in_;
673        }
674        if (name == "quality") {
675            info->mCurrentCaps->setString("quality-scale", scale);
676        }
677        if (name == "quality" || name == "complexity") {
678            AString tag = name;
679            tag.append("-default");
680            info->mCurrentCaps->setString(tag.c_str(), def);
681        }
682        AString tag = name;
683        tag.append("-range");
684        info->mCurrentCaps->setString(tag.c_str(), range);
685    } else {
686        AString max, value, ranges;
687        if (msg->contains("default")) {
688            return limitFoundMissingAttr(name, "default");
689        } else if (msg->contains("in")) {
690            return limitFoundMissingAttr(name, "in");
691        } else if ((name == "channel-count") ^
692                (found = msg->findString("max", &max))) {
693            return limitFoundMissingAttr(name, "max", found);
694        } else if (msg->contains("min")) {
695            return limitFoundMissingAttr(name, "min");
696        } else if (msg->contains("range")) {
697            return limitFoundMissingAttr(name, "range");
698        } else if ((name == "sample-rate") ^
699                (found = msg->findString("ranges", &ranges))) {
700            return limitFoundMissingAttr(name, "ranges", found);
701        } else if (msg->contains("scale")) {
702            return limitFoundMissingAttr(name, "scale");
703        } else if ((name == "alignment" || name == "block-size") ^
704                (found = msg->findString("value", &value))) {
705            return limitFoundMissingAttr(name, "value", found);
706        }
707
708        if (max.size()) {
709            AString tag = "max-";
710            tag.append(name);
711            info->mCurrentCaps->setString(tag.c_str(), max);
712        } else if (value.size()) {
713            info->mCurrentCaps->setString(name.c_str(), value);
714        } else if (ranges.size()) {
715            AString tag = name;
716            tag.append("-ranges");
717            info->mCurrentCaps->setString(tag.c_str(), ranges);
718        } else {
719            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
720        }
721    }
722    return OK;
723}
724
725static bool parseBoolean(const char *s) {
726    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
727        return true;
728    }
729    char *end;
730    unsigned long res = strtoul(s, &end, 10);
731    return *s != '\0' && *end == '\0' && res > 0;
732}
733
734status_t MediaCodecList::addFeature(const char **attrs) {
735    size_t i = 0;
736    const char *name = NULL;
737    int32_t optional = -1;
738    int32_t required = -1;
739
740    while (attrs[i] != NULL) {
741        if (attrs[i + 1] == NULL) {
742            return -EINVAL;
743        }
744
745        // attributes with values
746        if (!strcmp(attrs[i], "name")) {
747            name = attrs[i + 1];
748            ++i;
749        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
750            int value = (int)parseBoolean(attrs[i + 1]);
751            if (!strcmp(attrs[i], "optional")) {
752                optional = value;
753            } else {
754                required = value;
755            }
756            ++i;
757        } else {
758            return -EINVAL;
759        }
760        ++i;
761    }
762    if (name == NULL) {
763        ALOGE("feature with no 'name' attribute");
764        return -EINVAL;
765    }
766
767    if (optional == required && optional != -1) {
768        ALOGE("feature '%s' is both/neither optional and required", name);
769        return -EINVAL;
770    }
771
772    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
773    AString tag = "feature-";
774    tag.append(name);
775    info->mCurrentCaps->setInt32(tag.c_str(), (required == 1) || (optional == 0));
776    return OK;
777}
778
779ssize_t MediaCodecList::findCodecByName(const char *name) const {
780    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
781        const CodecInfo &info = mCodecInfos.itemAt(i);
782
783        if (info.mName == name) {
784            return i;
785        }
786    }
787
788    return -ENOENT;
789}
790
791size_t MediaCodecList::countCodecs() const {
792    return mCodecInfos.size();
793}
794
795const char *MediaCodecList::getCodecName(size_t index) const {
796    if (index >= mCodecInfos.size()) {
797        return NULL;
798    }
799
800    const CodecInfo &info = mCodecInfos.itemAt(index);
801    return info.mName.c_str();
802}
803
804bool MediaCodecList::isEncoder(size_t index) const {
805    if (index >= mCodecInfos.size()) {
806        return NULL;
807    }
808
809    const CodecInfo &info = mCodecInfos.itemAt(index);
810    return info.mIsEncoder;
811}
812
813bool MediaCodecList::codecHasQuirk(
814        size_t index, const char *quirkName) const {
815    if (index >= mCodecInfos.size()) {
816        return NULL;
817    }
818
819    const CodecInfo &info = mCodecInfos.itemAt(index);
820
821    if (info.mQuirks != 0) {
822        ssize_t index = mCodecQuirks.indexOfKey(quirkName);
823        if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
824            return true;
825        }
826    }
827
828    return false;
829}
830
831status_t MediaCodecList::getSupportedTypes(
832        size_t index, Vector<AString> *types) const {
833    types->clear();
834
835    if (index >= mCodecInfos.size()) {
836        return -ERANGE;
837    }
838
839    const CodecInfo &info = mCodecInfos.itemAt(index);
840
841    for (size_t i = 0; i < mTypes.size(); ++i) {
842        uint32_t typeMask = 1ul << mTypes.valueAt(i);
843
844        if (info.mTypes & typeMask) {
845            types->push(mTypes.keyAt(i));
846        }
847    }
848
849    return OK;
850}
851
852status_t MediaCodecList::getCodecCapabilities(
853        size_t index, const char *type,
854        Vector<ProfileLevel> *profileLevels,
855        Vector<uint32_t> *colorFormats,
856        uint32_t *flags,
857        sp<AMessage> *capabilities) const {
858    profileLevels->clear();
859    colorFormats->clear();
860
861    if (index >= mCodecInfos.size()) {
862        return -ERANGE;
863    }
864
865    const CodecInfo &info = mCodecInfos.itemAt(index);
866
867    ssize_t typeIndex = mTypes.indexOfKey(type);
868    if (typeIndex < 0) {
869        return -EINVAL;
870    }
871    // essentially doing valueFor without the CHECK abort
872    typeIndex = mTypes.valueAt(typeIndex);
873
874    OMXClient client;
875    status_t err = client.connect();
876    if (err != OK) {
877        return err;
878    }
879
880    CodecCapabilities caps;
881    err = QueryCodec(
882            client.interface(),
883            info.mName.c_str(), type, info.mIsEncoder, &caps);
884
885    if (err != OK) {
886        return err;
887    }
888
889    for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
890        const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
891
892        ProfileLevel profileLevel;
893        profileLevel.mProfile = src.mProfile;
894        profileLevel.mLevel = src.mLevel;
895        profileLevels->push(profileLevel);
896    }
897
898    for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
899        colorFormats->push(caps.mColorFormats.itemAt(i));
900    }
901
902    *flags = caps.mFlags;
903
904    // TODO this check will be removed once JNI side is merged
905    if (capabilities != NULL) {
906        *capabilities = info.mCaps.valueFor(typeIndex);
907    }
908
909    return OK;
910}
911
912}  // namespace android
913