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