MediaCodecList.cpp revision ecdd39c5af016e2fa57cbfd837aa670b706dabd3
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 <utils/threads.h>
26
27#include <expat.h>
28
29namespace android {
30
31static Mutex sInitMutex;
32
33// static
34MediaCodecList *MediaCodecList::sCodecList;
35
36// static
37const MediaCodecList *MediaCodecList::getInstance() {
38    Mutex::Autolock autoLock(sInitMutex);
39
40    if (sCodecList == NULL) {
41        sCodecList = new MediaCodecList;
42    }
43
44    return sCodecList->initCheck() == OK ? sCodecList : NULL;
45}
46
47MediaCodecList::MediaCodecList()
48    : mInitCheck(NO_INIT) {
49    FILE *file = fopen("/etc/media_codecs.xml", "r");
50
51    if (file == NULL) {
52        ALOGW("unable to open media codecs configuration xml file.");
53        return;
54    }
55
56    parseXMLFile(file);
57
58    if (mInitCheck == OK) {
59        // These are currently still used by the video editing suite.
60
61        addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
62        addMediaCodec(true /* encoder */, "AVCEncoder", "video/avc");
63
64        addMediaCodec(true /* encoder */, "M4vH263Encoder");
65        addType("video/3gpp");
66        addType("video/mp4v-es");
67
68        addMediaCodec(
69                false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
70    }
71
72#if 0
73    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
74        const CodecInfo &info = mCodecInfos.itemAt(i);
75
76        AString line = info.mName;
77        line.append(" supports ");
78        for (size_t j = 0; j < mTypes.size(); ++j) {
79            uint32_t value = mTypes.valueAt(j);
80
81            if (info.mTypes & (1ul << value)) {
82                line.append(mTypes.keyAt(j));
83                line.append(" ");
84            }
85        }
86
87        ALOGI("%s", line.c_str());
88    }
89#endif
90
91    fclose(file);
92    file = NULL;
93}
94
95MediaCodecList::~MediaCodecList() {
96}
97
98status_t MediaCodecList::initCheck() const {
99    return mInitCheck;
100}
101
102void MediaCodecList::parseXMLFile(FILE *file) {
103    mInitCheck = OK;
104    mCurrentSection = SECTION_TOPLEVEL;
105    mDepth = 0;
106
107    XML_Parser parser = ::XML_ParserCreate(NULL);
108    CHECK(parser != NULL);
109
110    ::XML_SetUserData(parser, this);
111    ::XML_SetElementHandler(
112            parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
113
114    const int BUFF_SIZE = 512;
115    while (mInitCheck == OK) {
116        void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
117        if (buff == NULL) {
118            ALOGE("failed to in call to XML_GetBuffer()");
119            mInitCheck = UNKNOWN_ERROR;
120            break;
121        }
122
123        int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
124        if (bytes_read < 0) {
125            ALOGE("failed in call to read");
126            mInitCheck = ERROR_IO;
127            break;
128        }
129
130        if (::XML_ParseBuffer(parser, bytes_read, bytes_read == 0)
131                != XML_STATUS_OK) {
132            mInitCheck = ERROR_MALFORMED;
133            break;
134        }
135
136        if (bytes_read == 0) {
137            break;
138        }
139    }
140
141    ::XML_ParserFree(parser);
142
143    if (mInitCheck == OK) {
144        for (size_t i = mCodecInfos.size(); i-- > 0;) {
145            CodecInfo *info = &mCodecInfos.editItemAt(i);
146
147            if (info->mTypes == 0) {
148                // No types supported by this component???
149
150                ALOGW("Component %s does not support any type of media?",
151                      info->mName.c_str());
152
153                mCodecInfos.removeAt(i);
154            }
155        }
156    }
157
158    if (mInitCheck != OK) {
159        mCodecInfos.clear();
160        mCodecQuirks.clear();
161    }
162}
163
164// static
165void MediaCodecList::StartElementHandlerWrapper(
166        void *me, const char *name, const char **attrs) {
167    static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
168}
169
170// static
171void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
172    static_cast<MediaCodecList *>(me)->endElementHandler(name);
173}
174
175void MediaCodecList::startElementHandler(
176        const char *name, const char **attrs) {
177    if (mInitCheck != OK) {
178        return;
179    }
180
181    switch (mCurrentSection) {
182        case SECTION_TOPLEVEL:
183        {
184            if (!strcmp(name, "Decoders")) {
185                mCurrentSection = SECTION_DECODERS;
186            } else if (!strcmp(name, "Encoders")) {
187                mCurrentSection = SECTION_ENCODERS;
188            }
189            break;
190        }
191
192        case SECTION_DECODERS:
193        {
194            if (!strcmp(name, "MediaCodec")) {
195                mInitCheck =
196                    addMediaCodecFromAttributes(false /* encoder */, attrs);
197
198                mCurrentSection = SECTION_DECODER;
199            }
200            break;
201        }
202
203        case SECTION_ENCODERS:
204        {
205            if (!strcmp(name, "MediaCodec")) {
206                mInitCheck =
207                    addMediaCodecFromAttributes(true /* encoder */, attrs);
208
209                mCurrentSection = SECTION_ENCODER;
210            }
211            break;
212        }
213
214        case SECTION_DECODER:
215        case SECTION_ENCODER:
216        {
217            if (!strcmp(name, "Quirk")) {
218                mInitCheck = addQuirk(attrs);
219            } else if (!strcmp(name, "Type")) {
220                mInitCheck = addTypeFromAttributes(attrs);
221            }
222            break;
223        }
224
225        default:
226            break;
227    }
228
229    ++mDepth;
230}
231
232void MediaCodecList::endElementHandler(const char *name) {
233    if (mInitCheck != OK) {
234        return;
235    }
236
237    switch (mCurrentSection) {
238        case SECTION_DECODERS:
239        {
240            if (!strcmp(name, "Decoders")) {
241                mCurrentSection = SECTION_TOPLEVEL;
242            }
243            break;
244        }
245
246        case SECTION_ENCODERS:
247        {
248            if (!strcmp(name, "Encoders")) {
249                mCurrentSection = SECTION_TOPLEVEL;
250            }
251            break;
252        }
253
254        case SECTION_DECODER:
255        {
256            if (!strcmp(name, "MediaCodec")) {
257                mCurrentSection = SECTION_DECODERS;
258            }
259            break;
260        }
261
262        case SECTION_ENCODER:
263        {
264            if (!strcmp(name, "MediaCodec")) {
265                mCurrentSection = SECTION_ENCODERS;
266            }
267            break;
268        }
269
270        default:
271            break;
272    }
273
274    --mDepth;
275}
276
277status_t MediaCodecList::addMediaCodecFromAttributes(
278        bool encoder, const char **attrs) {
279    const char *name = NULL;
280    const char *type = NULL;
281
282    size_t i = 0;
283    while (attrs[i] != NULL) {
284        if (!strcmp(attrs[i], "name")) {
285            if (attrs[i + 1] == NULL) {
286                return -EINVAL;
287            }
288            name = attrs[i + 1];
289            ++i;
290        } else if (!strcmp(attrs[i], "type")) {
291            if (attrs[i + 1] == NULL) {
292                return -EINVAL;
293            }
294            type = attrs[i + 1];
295            ++i;
296        } else {
297            return -EINVAL;
298        }
299
300        ++i;
301    }
302
303    if (name == NULL) {
304        return -EINVAL;
305    }
306
307    addMediaCodec(encoder, name, type);
308
309    return OK;
310}
311
312void MediaCodecList::addMediaCodec(
313        bool encoder, const char *name, const char *type) {
314    mCodecInfos.push();
315    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
316    info->mName = name;
317    info->mIsEncoder = encoder;
318    info->mTypes = 0;
319    info->mQuirks = 0;
320
321    if (type != NULL) {
322        addType(type);
323    }
324}
325
326status_t MediaCodecList::addQuirk(const char **attrs) {
327    const char *name = NULL;
328
329    size_t i = 0;
330    while (attrs[i] != NULL) {
331        if (!strcmp(attrs[i], "name")) {
332            if (attrs[i + 1] == NULL) {
333                return -EINVAL;
334            }
335            name = attrs[i + 1];
336            ++i;
337        } else {
338            return -EINVAL;
339        }
340
341        ++i;
342    }
343
344    if (name == NULL) {
345        return -EINVAL;
346    }
347
348    uint32_t bit;
349    ssize_t index = mCodecQuirks.indexOfKey(name);
350    if (index < 0) {
351        bit = mCodecQuirks.size();
352
353        if (bit == 32) {
354            ALOGW("Too many distinct quirk names in configuration.");
355            return OK;
356        }
357
358        mCodecQuirks.add(name, bit);
359    } else {
360        bit = mCodecQuirks.valueAt(index);
361    }
362
363    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
364    info->mQuirks |= 1ul << bit;
365
366    return OK;
367}
368
369status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
370    const char *name = NULL;
371
372    size_t i = 0;
373    while (attrs[i] != NULL) {
374        if (!strcmp(attrs[i], "name")) {
375            if (attrs[i + 1] == NULL) {
376                return -EINVAL;
377            }
378            name = attrs[i + 1];
379            ++i;
380        } else {
381            return -EINVAL;
382        }
383
384        ++i;
385    }
386
387    if (name == NULL) {
388        return -EINVAL;
389    }
390
391    addType(name);
392
393    return OK;
394}
395
396void MediaCodecList::addType(const char *name) {
397    uint32_t bit;
398    ssize_t index = mTypes.indexOfKey(name);
399    if (index < 0) {
400        bit = mTypes.size();
401
402        if (bit == 32) {
403            ALOGW("Too many distinct type names in configuration.");
404            return;
405        }
406
407        mTypes.add(name, bit);
408    } else {
409        bit = mTypes.valueAt(index);
410    }
411
412    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
413    info->mTypes |= 1ul << bit;
414}
415
416ssize_t MediaCodecList::findCodecByType(
417        const char *type, bool encoder, size_t startIndex) const {
418    ssize_t typeIndex = mTypes.indexOfKey(type);
419
420    if (typeIndex < 0) {
421        return -ENOENT;
422    }
423
424    uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
425
426    while (startIndex < mCodecInfos.size()) {
427        const CodecInfo &info = mCodecInfos.itemAt(startIndex);
428
429        if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
430            return startIndex;
431        }
432
433        ++startIndex;
434    }
435
436    return -ENOENT;
437}
438
439ssize_t MediaCodecList::findCodecByName(const char *name) const {
440    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
441        const CodecInfo &info = mCodecInfos.itemAt(i);
442
443        if (info.mName == name) {
444            return i;
445        }
446    }
447
448    return -ENOENT;
449}
450
451const char *MediaCodecList::getCodecName(size_t index) const {
452    if (index >= mCodecInfos.size()) {
453        return NULL;
454    }
455
456    const CodecInfo &info = mCodecInfos.itemAt(index);
457    return info.mName.c_str();
458}
459
460bool MediaCodecList::codecHasQuirk(
461        size_t index, const char *quirkName) const {
462    if (index >= mCodecInfos.size()) {
463        return NULL;
464    }
465
466    const CodecInfo &info = mCodecInfos.itemAt(index);
467
468    if (info.mQuirks != 0) {
469        ssize_t index = mCodecQuirks.indexOfKey(quirkName);
470        if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
471            return true;
472        }
473    }
474
475    return false;
476}
477
478}  // namespace android
479