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