MediaCodecList.cpp revision d74110cdef2becd4f7fd2334c34c3ca73f56b355
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/MediaErrors.h>
25#include <media/stagefright/OMXClient.h>
26#include <media/stagefright/OMXCodec.h>
27#include <utils/threads.h>
28
29#include <libexpat/expat.h>
30
31namespace android {
32
33static Mutex sInitMutex;
34
35// static
36MediaCodecList *MediaCodecList::sCodecList;
37
38// static
39const MediaCodecList *MediaCodecList::getInstance() {
40    Mutex::Autolock autoLock(sInitMutex);
41
42    if (sCodecList == NULL) {
43        sCodecList = new MediaCodecList;
44    }
45
46    return sCodecList->initCheck() == OK ? sCodecList : NULL;
47}
48
49MediaCodecList::MediaCodecList()
50    : mInitCheck(NO_INIT) {
51    parseTopLevelXMLFile("/etc/media_codecs.xml");
52}
53
54void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
55    // get href_base
56    char *href_base_end = strrchr(codecs_xml, '/');
57    if (href_base_end != NULL) {
58        mHrefBase = AString(codecs_xml, href_base_end - codecs_xml + 1);
59    }
60
61    mInitCheck = OK;
62    mCurrentSection = SECTION_TOPLEVEL;
63    mDepth = 0;
64
65    parseXMLFile(codecs_xml);
66
67    if (mInitCheck != OK) {
68        mCodecInfos.clear();
69        mCodecQuirks.clear();
70        return;
71    }
72
73    // These are currently still used by the video editing suite.
74    addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
75    addMediaCodec(
76            false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
77
78    for (size_t i = mCodecInfos.size(); i-- > 0;) {
79        CodecInfo *info = &mCodecInfos.editItemAt(i);
80
81        if (info->mTypes == 0) {
82            // No types supported by this component???
83            ALOGW("Component %s does not support any type of media?",
84                  info->mName.c_str());
85
86            mCodecInfos.removeAt(i);
87        }
88    }
89
90#if 0
91    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
92        const CodecInfo &info = mCodecInfos.itemAt(i);
93
94        AString line = info.mName;
95        line.append(" supports ");
96        for (size_t j = 0; j < mTypes.size(); ++j) {
97            uint32_t value = mTypes.valueAt(j);
98
99            if (info.mTypes & (1ul << value)) {
100                line.append(mTypes.keyAt(j));
101                line.append(" ");
102            }
103        }
104
105        ALOGI("%s", line.c_str());
106    }
107#endif
108}
109
110MediaCodecList::~MediaCodecList() {
111}
112
113status_t MediaCodecList::initCheck() const {
114    return mInitCheck;
115}
116
117void MediaCodecList::parseXMLFile(const char *path) {
118    FILE *file = fopen(path, "r");
119
120    if (file == NULL) {
121        ALOGW("unable to open media codecs configuration xml file: %s", path);
122        mInitCheck = NAME_NOT_FOUND;
123        return;
124    }
125
126    XML_Parser parser = ::XML_ParserCreate(NULL);
127    CHECK(parser != NULL);
128
129    ::XML_SetUserData(parser, this);
130    ::XML_SetElementHandler(
131            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
132
133    const int BUFF_SIZE = 512;
134    while (mInitCheck == OK) {
135        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
136        if (buff == NULL) {
137            ALOGE("failed in call to XML_GetBuffer()");
138            mInitCheck = UNKNOWN_ERROR;
139            break;
140        }
141
142        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
143        if (bytes_read < 0) {
144            ALOGE("failed in call to read");
145            mInitCheck = ERROR_IO;
146            break;
147        }
148
149        XML_Status status = ::XML_ParseBuffer(parser, bytes_read, bytes_read == 0);
150        if (status != XML_STATUS_OK) {
151            ALOGE("malformed (%s)", ::XML_ErrorString(::XML_GetErrorCode(parser)));
152            mInitCheck = ERROR_MALFORMED;
153            break;
154        }
155
156        if (bytes_read == 0) {
157            break;
158        }
159    }
160
161    ::XML_ParserFree(parser);
162
163    fclose(file);
164    file = NULL;
165}
166
167// static
168void MediaCodecList::StartElementHandlerWrapper(
169        void *me, const char *name, const char **attrs) {
170    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
171}
172
173// static
174void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
175    static_cast<MediaCodecList *>(me)->endElementHandler(name);
176}
177
178status_t MediaCodecList::includeXMLFile(const char **attrs) {
179    const char *href = NULL;
180    size_t i = 0;
181    while (attrs[i] != NULL) {
182        if (!strcmp(attrs[i], "href")) {
183            if (attrs[i + 1] == NULL) {
184                return -EINVAL;
185            }
186            href = attrs[i + 1];
187            ++i;
188        } else {
189            return -EINVAL;
190        }
191        ++i;
192    }
193
194    // For security reasons and for simplicity, file names can only contain
195    // [a-zA-Z0-9_.] and must start with  media_codecs_ and end with .xml
196    for (i = 0; href[i] != '\0'; i++) {
197        if (href[i] == '.' || href[i] == '_' ||
198                (href[i] >= '0' && href[i] <= '9') ||
199                (href[i] >= 'A' && href[i] <= 'Z') ||
200                (href[i] >= 'a' && href[i] <= 'z')) {
201            continue;
202        }
203        ALOGE("invalid include file name: %s", href);
204        return -EINVAL;
205    }
206
207    AString filename = href;
208    if (!filename.startsWith("media_codecs_") ||
209        !filename.endsWith(".xml")) {
210        ALOGE("invalid include file name: %s", href);
211        return -EINVAL;
212    }
213    filename.insert(mHrefBase, 0);
214
215    parseXMLFile(filename.c_str());
216    return mInitCheck;
217}
218
219void MediaCodecList::startElementHandler(
220        const char *name, const char **attrs) {
221    if (mInitCheck != OK) {
222        return;
223    }
224
225    if (!strcmp(name, "Include")) {
226        mInitCheck = includeXMLFile(attrs);
227        if (mInitCheck == OK) {
228            mPastSections.push(mCurrentSection);
229            mCurrentSection = SECTION_INCLUDE;
230        }
231        ++mDepth;
232        return;
233    }
234
235    switch (mCurrentSection) {
236        case SECTION_TOPLEVEL:
237        {
238            if (!strcmp(name, "Decoders")) {
239                mCurrentSection = SECTION_DECODERS;
240            } else if (!strcmp(name, "Encoders")) {
241                mCurrentSection = SECTION_ENCODERS;
242            }
243            break;
244        }
245
246        case SECTION_DECODERS:
247        {
248            if (!strcmp(name, "MediaCodec")) {
249                mInitCheck =
250                    addMediaCodecFromAttributes(false /* encoder */, attrs);
251
252                mCurrentSection = SECTION_DECODER;
253            }
254            break;
255        }
256
257        case SECTION_ENCODERS:
258        {
259            if (!strcmp(name, "MediaCodec")) {
260                mInitCheck =
261                    addMediaCodecFromAttributes(true /* encoder */, attrs);
262
263                mCurrentSection = SECTION_ENCODER;
264            }
265            break;
266        }
267
268        case SECTION_DECODER:
269        case SECTION_ENCODER:
270        {
271            if (!strcmp(name, "Quirk")) {
272                mInitCheck = addQuirk(attrs);
273            } else if (!strcmp(name, "Type")) {
274                mInitCheck = addTypeFromAttributes(attrs);
275            }
276            break;
277        }
278
279        default:
280            break;
281    }
282
283    ++mDepth;
284}
285
286void MediaCodecList::endElementHandler(const char *name) {
287    if (mInitCheck != OK) {
288        return;
289    }
290
291    switch (mCurrentSection) {
292        case SECTION_DECODERS:
293        {
294            if (!strcmp(name, "Decoders")) {
295                mCurrentSection = SECTION_TOPLEVEL;
296            }
297            break;
298        }
299
300        case SECTION_ENCODERS:
301        {
302            if (!strcmp(name, "Encoders")) {
303                mCurrentSection = SECTION_TOPLEVEL;
304            }
305            break;
306        }
307
308        case SECTION_DECODER:
309        {
310            if (!strcmp(name, "MediaCodec")) {
311                mCurrentSection = SECTION_DECODERS;
312            }
313            break;
314        }
315
316        case SECTION_ENCODER:
317        {
318            if (!strcmp(name, "MediaCodec")) {
319                mCurrentSection = SECTION_ENCODERS;
320            }
321            break;
322        }
323
324        case SECTION_INCLUDE:
325        {
326            if (!strcmp(name, "Include") && mPastSections.size() > 0) {
327                mCurrentSection = mPastSections.top();
328                mPastSections.pop();
329            }
330            break;
331        }
332
333        default:
334            break;
335    }
336
337    --mDepth;
338}
339
340status_t MediaCodecList::addMediaCodecFromAttributes(
341        bool encoder, const char **attrs) {
342    const char *name = NULL;
343    const char *type = NULL;
344
345    size_t i = 0;
346    while (attrs[i] != NULL) {
347        if (!strcmp(attrs[i], "name")) {
348            if (attrs[i + 1] == NULL) {
349                return -EINVAL;
350            }
351            name = attrs[i + 1];
352            ++i;
353        } else if (!strcmp(attrs[i], "type")) {
354            if (attrs[i + 1] == NULL) {
355                return -EINVAL;
356            }
357            type = attrs[i + 1];
358            ++i;
359        } else {
360            return -EINVAL;
361        }
362
363        ++i;
364    }
365
366    if (name == NULL) {
367        return -EINVAL;
368    }
369
370    addMediaCodec(encoder, name, type);
371
372    return OK;
373}
374
375void MediaCodecList::addMediaCodec(
376        bool encoder, const char *name, const char *type) {
377    mCodecInfos.push();
378    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
379    info->mName = name;
380    info->mIsEncoder = encoder;
381    info->mTypes = 0;
382    info->mQuirks = 0;
383
384    if (type != NULL) {
385        addType(type);
386    }
387}
388
389status_t MediaCodecList::addQuirk(const char **attrs) {
390    const char *name = NULL;
391
392    size_t i = 0;
393    while (attrs[i] != NULL) {
394        if (!strcmp(attrs[i], "name")) {
395            if (attrs[i + 1] == NULL) {
396                return -EINVAL;
397            }
398            name = attrs[i + 1];
399            ++i;
400        } else {
401            return -EINVAL;
402        }
403
404        ++i;
405    }
406
407    if (name == NULL) {
408        return -EINVAL;
409    }
410
411    uint32_t bit;
412    ssize_t index = mCodecQuirks.indexOfKey(name);
413    if (index < 0) {
414        bit = mCodecQuirks.size();
415
416        if (bit == 32) {
417            ALOGW("Too many distinct quirk names in configuration.");
418            return OK;
419        }
420
421        mCodecQuirks.add(name, bit);
422    } else {
423        bit = mCodecQuirks.valueAt(index);
424    }
425
426    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
427    info->mQuirks |= 1ul << bit;
428
429    return OK;
430}
431
432status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
433    const char *name = NULL;
434
435    size_t i = 0;
436    while (attrs[i] != NULL) {
437        if (!strcmp(attrs[i], "name")) {
438            if (attrs[i + 1] == NULL) {
439                return -EINVAL;
440            }
441            name = attrs[i + 1];
442            ++i;
443        } else {
444            return -EINVAL;
445        }
446
447        ++i;
448    }
449
450    if (name == NULL) {
451        return -EINVAL;
452    }
453
454    addType(name);
455
456    return OK;
457}
458
459void MediaCodecList::addType(const char *name) {
460    uint32_t bit;
461    ssize_t index = mTypes.indexOfKey(name);
462    if (index < 0) {
463        bit = mTypes.size();
464
465        if (bit == 32) {
466            ALOGW("Too many distinct type names in configuration.");
467            return;
468        }
469
470        mTypes.add(name, bit);
471    } else {
472        bit = mTypes.valueAt(index);
473    }
474
475    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
476    info->mTypes |= 1ul << bit;
477}
478
479ssize_t MediaCodecList::findCodecByType(
480        const char *type, bool encoder, size_t startIndex) const {
481    ssize_t typeIndex = mTypes.indexOfKey(type);
482
483    if (typeIndex < 0) {
484        return -ENOENT;
485    }
486
487    uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
488
489    while (startIndex < mCodecInfos.size()) {
490        const CodecInfo &info = mCodecInfos.itemAt(startIndex);
491
492        if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
493            return startIndex;
494        }
495
496        ++startIndex;
497    }
498
499    return -ENOENT;
500}
501
502ssize_t MediaCodecList::findCodecByName(const char *name) const {
503    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
504        const CodecInfo &info = mCodecInfos.itemAt(i);
505
506        if (info.mName == name) {
507            return i;
508        }
509    }
510
511    return -ENOENT;
512}
513
514size_t MediaCodecList::countCodecs() const {
515    return mCodecInfos.size();
516}
517
518const char *MediaCodecList::getCodecName(size_t index) const {
519    if (index >= mCodecInfos.size()) {
520        return NULL;
521    }
522
523    const CodecInfo &info = mCodecInfos.itemAt(index);
524    return info.mName.c_str();
525}
526
527bool MediaCodecList::isEncoder(size_t index) const {
528    if (index >= mCodecInfos.size()) {
529        return NULL;
530    }
531
532    const CodecInfo &info = mCodecInfos.itemAt(index);
533    return info.mIsEncoder;
534}
535
536bool MediaCodecList::codecHasQuirk(
537        size_t index, const char *quirkName) const {
538    if (index >= mCodecInfos.size()) {
539        return NULL;
540    }
541
542    const CodecInfo &info = mCodecInfos.itemAt(index);
543
544    if (info.mQuirks != 0) {
545        ssize_t index = mCodecQuirks.indexOfKey(quirkName);
546        if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
547            return true;
548        }
549    }
550
551    return false;
552}
553
554status_t MediaCodecList::getSupportedTypes(
555        size_t index, Vector<AString> *types) const {
556    types->clear();
557
558    if (index >= mCodecInfos.size()) {
559        return -ERANGE;
560    }
561
562    const CodecInfo &info = mCodecInfos.itemAt(index);
563
564    for (size_t i = 0; i < mTypes.size(); ++i) {
565        uint32_t typeMask = 1ul << mTypes.valueAt(i);
566
567        if (info.mTypes & typeMask) {
568            types->push(mTypes.keyAt(i));
569        }
570    }
571
572    return OK;
573}
574
575status_t MediaCodecList::getCodecCapabilities(
576        size_t index, const char *type,
577        Vector<ProfileLevel> *profileLevels,
578        Vector<uint32_t> *colorFormats,
579        uint32_t *flags) const {
580    profileLevels->clear();
581    colorFormats->clear();
582
583    if (index >= mCodecInfos.size()) {
584        return -ERANGE;
585    }
586
587    const CodecInfo &info = mCodecInfos.itemAt(index);
588
589    OMXClient client;
590    status_t err = client.connect();
591    if (err != OK) {
592        return err;
593    }
594
595    CodecCapabilities caps;
596    err = QueryCodec(
597            client.interface(),
598            info.mName.c_str(), type, info.mIsEncoder, &caps);
599
600    if (err != OK) {
601        return err;
602    }
603
604    for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
605        const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
606
607        ProfileLevel profileLevel;
608        profileLevel.mProfile = src.mProfile;
609        profileLevel.mLevel = src.mLevel;
610        profileLevels->push(profileLevel);
611    }
612
613    for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
614        colorFormats->push(caps.mColorFormats.itemAt(i));
615    }
616
617    *flags = caps.mFlags;
618
619    return OK;
620}
621
622}  // namespace android
623