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    FILE *file = fopen("/etc/media_codecs.xml", "r");
52
53    if (file == NULL) {
54        ALOGW("unable to open media codecs configuration xml file.");
55        return;
56    }
57
58    parseXMLFile(file);
59
60    if (mInitCheck == OK) {
61        // These are currently still used by the video editing suite.
62
63        addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
64
65        addMediaCodec(
66                false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
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
448size_t MediaCodecList::countCodecs() const {
449    return mCodecInfos.size();
450}
451
452const char *MediaCodecList::getCodecName(size_t index) const {
453    if (index >= mCodecInfos.size()) {
454        return NULL;
455    }
456
457    const CodecInfo &info = mCodecInfos.itemAt(index);
458    return info.mName.c_str();
459}
460
461bool MediaCodecList::isEncoder(size_t index) const {
462    if (index >= mCodecInfos.size()) {
463        return NULL;
464    }
465
466    const CodecInfo &info = mCodecInfos.itemAt(index);
467    return info.mIsEncoder;
468}
469
470bool MediaCodecList::codecHasQuirk(
471        size_t index, const char *quirkName) const {
472    if (index >= mCodecInfos.size()) {
473        return NULL;
474    }
475
476    const CodecInfo &info = mCodecInfos.itemAt(index);
477
478    if (info.mQuirks != 0) {
479        ssize_t index = mCodecQuirks.indexOfKey(quirkName);
480        if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
481            return true;
482        }
483    }
484
485    return false;
486}
487
488status_t MediaCodecList::getSupportedTypes(
489        size_t index, Vector<AString> *types) const {
490    types->clear();
491
492    if (index >= mCodecInfos.size()) {
493        return -ERANGE;
494    }
495
496    const CodecInfo &info = mCodecInfos.itemAt(index);
497
498    for (size_t i = 0; i < mTypes.size(); ++i) {
499        uint32_t typeMask = 1ul << mTypes.valueAt(i);
500
501        if (info.mTypes & typeMask) {
502            types->push(mTypes.keyAt(i));
503        }
504    }
505
506    return OK;
507}
508
509status_t MediaCodecList::getCodecCapabilities(
510        size_t index, const char *type,
511        Vector<ProfileLevel> *profileLevels,
512        Vector<uint32_t> *colorFormats) const {
513    profileLevels->clear();
514    colorFormats->clear();
515
516    if (index >= mCodecInfos.size()) {
517        return -ERANGE;
518    }
519
520    const CodecInfo &info = mCodecInfos.itemAt(index);
521
522    OMXClient client;
523    status_t err = client.connect();
524    if (err != OK) {
525        return err;
526    }
527
528    CodecCapabilities caps;
529    err = QueryCodec(
530            client.interface(),
531            info.mName.c_str(), type, info.mIsEncoder, &caps);
532
533    if (err != OK) {
534        return err;
535    }
536
537    for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
538        const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
539
540        ProfileLevel profileLevel;
541        profileLevel.mProfile = src.mProfile;
542        profileLevel.mLevel = src.mLevel;
543        profileLevels->push(profileLevel);
544    }
545
546    for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
547        colorFormats->push(caps.mColorFormats.itemAt(i));
548    }
549
550    return OK;
551}
552
553}  // namespace android
554