blob: 6248e906c3abe75217465c5f5f31e547e18d2562 [file] [log] [blame]
/*
* Copyright 2012, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaCodecList"
#include <utils/Log.h>
#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <utils/threads.h>
#include <libexpat/expat.h>
namespace android {
static Mutex sInitMutex;
// static
MediaCodecList *MediaCodecList::sCodecList;
// static
const MediaCodecList *MediaCodecList::getInstance() {
Mutex::Autolock autoLock(sInitMutex);
if (sCodecList == NULL) {
sCodecList = new MediaCodecList;
}
return sCodecList->initCheck() == OK ? sCodecList : NULL;
}
MediaCodecList::MediaCodecList()
: mInitCheck(NO_INIT) {
FILE *file = fopen("/etc/media_codecs.xml", "r");
if (file == NULL) {
ALOGW("unable to open media codecs configuration xml file.");
return;
}
parseXMLFile(file);
if (mInitCheck == OK) {
// These are currently still used by the video editing suite.
addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
addMediaCodec(
false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
}
#if 0
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
const CodecInfo &info = mCodecInfos.itemAt(i);
AString line = info.mName;
line.append(" supports ");
for (size_t j = 0; j < mTypes.size(); ++j) {
uint32_t value = mTypes.valueAt(j);
if (info.mTypes & (1ul << value)) {
line.append(mTypes.keyAt(j));
line.append(" ");
}
}
ALOGI("%s", line.c_str());
}
#endif
fclose(file);
file = NULL;
}
MediaCodecList::~MediaCodecList() {
}
status_t MediaCodecList::initCheck() const {
return mInitCheck;
}
void MediaCodecList::parseXMLFile(FILE *file) {
mInitCheck = OK;
mCurrentSection = SECTION_TOPLEVEL;
mDepth = 0;
XML_Parser parser = ::XML_ParserCreate(NULL);
CHECK(parser != NULL);
::XML_SetUserData(parser, this);
::XML_SetElementHandler(
parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
const int BUFF_SIZE = 512;
while (mInitCheck == OK) {
void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
if (buff == NULL) {
ALOGE("failed to in call to XML_GetBuffer()");
mInitCheck = UNKNOWN_ERROR;
break;
}
int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
if (bytes_read < 0) {
ALOGE("failed in call to read");
mInitCheck = ERROR_IO;
break;
}
if (::XML_ParseBuffer(parser, bytes_read, bytes_read == 0)
!= XML_STATUS_OK) {
mInitCheck = ERROR_MALFORMED;
break;
}
if (bytes_read == 0) {
break;
}
}
::XML_ParserFree(parser);
if (mInitCheck == OK) {
for (size_t i = mCodecInfos.size(); i-- > 0;) {
CodecInfo *info = &mCodecInfos.editItemAt(i);
if (info->mTypes == 0) {
// No types supported by this component???
ALOGW("Component %s does not support any type of media?",
info->mName.c_str());
mCodecInfos.removeAt(i);
}
}
}
if (mInitCheck != OK) {
mCodecInfos.clear();
mCodecQuirks.clear();
}
}
// static
void MediaCodecList::StartElementHandlerWrapper(
void *me, const char *name, const char **attrs) {
static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
}
// static
void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
static_cast<MediaCodecList *>(me)->endElementHandler(name);
}
void MediaCodecList::startElementHandler(
const char *name, const char **attrs) {
if (mInitCheck != OK) {
return;
}
switch (mCurrentSection) {
case SECTION_TOPLEVEL:
{
if (!strcmp(name, "Decoders")) {
mCurrentSection = SECTION_DECODERS;
} else if (!strcmp(name, "Encoders")) {
mCurrentSection = SECTION_ENCODERS;
}
break;
}
case SECTION_DECODERS:
{
if (!strcmp(name, "MediaCodec")) {
mInitCheck =
addMediaCodecFromAttributes(false /* encoder */, attrs);
mCurrentSection = SECTION_DECODER;
}
break;
}
case SECTION_ENCODERS:
{
if (!strcmp(name, "MediaCodec")) {
mInitCheck =
addMediaCodecFromAttributes(true /* encoder */, attrs);
mCurrentSection = SECTION_ENCODER;
}
break;
}
case SECTION_DECODER:
case SECTION_ENCODER:
{
if (!strcmp(name, "Quirk")) {
mInitCheck = addQuirk(attrs);
} else if (!strcmp(name, "Type")) {
mInitCheck = addTypeFromAttributes(attrs);
}
break;
}
default:
break;
}
++mDepth;
}
void MediaCodecList::endElementHandler(const char *name) {
if (mInitCheck != OK) {
return;
}
switch (mCurrentSection) {
case SECTION_DECODERS:
{
if (!strcmp(name, "Decoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
}
case SECTION_ENCODERS:
{
if (!strcmp(name, "Encoders")) {
mCurrentSection = SECTION_TOPLEVEL;
}
break;
}
case SECTION_DECODER:
{
if (!strcmp(name, "MediaCodec")) {
mCurrentSection = SECTION_DECODERS;
}
break;
}
case SECTION_ENCODER:
{
if (!strcmp(name, "MediaCodec")) {
mCurrentSection = SECTION_ENCODERS;
}
break;
}
default:
break;
}
--mDepth;
}
status_t MediaCodecList::addMediaCodecFromAttributes(
bool encoder, const char **attrs) {
const char *name = NULL;
const char *type = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
if (!strcmp(attrs[i], "name")) {
if (attrs[i + 1] == NULL) {
return -EINVAL;
}
name = attrs[i + 1];
++i;
} else if (!strcmp(attrs[i], "type")) {
if (attrs[i + 1] == NULL) {
return -EINVAL;
}
type = attrs[i + 1];
++i;
} else {
return -EINVAL;
}
++i;
}
if (name == NULL) {
return -EINVAL;
}
addMediaCodec(encoder, name, type);
return OK;
}
void MediaCodecList::addMediaCodec(
bool encoder, const char *name, const char *type) {
mCodecInfos.push();
CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
info->mName = name;
info->mIsEncoder = encoder;
info->mTypes = 0;
info->mQuirks = 0;
if (type != NULL) {
addType(type);
}
}
status_t MediaCodecList::addQuirk(const char **attrs) {
const char *name = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
if (!strcmp(attrs[i], "name")) {
if (attrs[i + 1] == NULL) {
return -EINVAL;
}
name = attrs[i + 1];
++i;
} else {
return -EINVAL;
}
++i;
}
if (name == NULL) {
return -EINVAL;
}
uint32_t bit;
ssize_t index = mCodecQuirks.indexOfKey(name);
if (index < 0) {
bit = mCodecQuirks.size();
if (bit == 32) {
ALOGW("Too many distinct quirk names in configuration.");
return OK;
}
mCodecQuirks.add(name, bit);
} else {
bit = mCodecQuirks.valueAt(index);
}
CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
info->mQuirks |= 1ul << bit;
return OK;
}
status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
const char *name = NULL;
size_t i = 0;
while (attrs[i] != NULL) {
if (!strcmp(attrs[i], "name")) {
if (attrs[i + 1] == NULL) {
return -EINVAL;
}
name = attrs[i + 1];
++i;
} else {
return -EINVAL;
}
++i;
}
if (name == NULL) {
return -EINVAL;
}
addType(name);
return OK;
}
void MediaCodecList::addType(const char *name) {
uint32_t bit;
ssize_t index = mTypes.indexOfKey(name);
if (index < 0) {
bit = mTypes.size();
if (bit == 32) {
ALOGW("Too many distinct type names in configuration.");
return;
}
mTypes.add(name, bit);
} else {
bit = mTypes.valueAt(index);
}
CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
info->mTypes |= 1ul << bit;
}
ssize_t MediaCodecList::findCodecByType(
const char *type, bool encoder, size_t startIndex) const {
ssize_t typeIndex = mTypes.indexOfKey(type);
if (typeIndex < 0) {
return -ENOENT;
}
uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
while (startIndex < mCodecInfos.size()) {
const CodecInfo &info = mCodecInfos.itemAt(startIndex);
if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
return startIndex;
}
++startIndex;
}
return -ENOENT;
}
ssize_t MediaCodecList::findCodecByName(const char *name) const {
for (size_t i = 0; i < mCodecInfos.size(); ++i) {
const CodecInfo &info = mCodecInfos.itemAt(i);
if (info.mName == name) {
return i;
}
}
return -ENOENT;
}
size_t MediaCodecList::countCodecs() const {
return mCodecInfos.size();
}
const char *MediaCodecList::getCodecName(size_t index) const {
if (index >= mCodecInfos.size()) {
return NULL;
}
const CodecInfo &info = mCodecInfos.itemAt(index);
return info.mName.c_str();
}
bool MediaCodecList::isEncoder(size_t index) const {
if (index >= mCodecInfos.size()) {
return NULL;
}
const CodecInfo &info = mCodecInfos.itemAt(index);
return info.mIsEncoder;
}
bool MediaCodecList::codecHasQuirk(
size_t index, const char *quirkName) const {
if (index >= mCodecInfos.size()) {
return NULL;
}
const CodecInfo &info = mCodecInfos.itemAt(index);
if (info.mQuirks != 0) {
ssize_t index = mCodecQuirks.indexOfKey(quirkName);
if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
return true;
}
}
return false;
}
status_t MediaCodecList::getSupportedTypes(
size_t index, Vector<AString> *types) const {
types->clear();
if (index >= mCodecInfos.size()) {
return -ERANGE;
}
const CodecInfo &info = mCodecInfos.itemAt(index);
for (size_t i = 0; i < mTypes.size(); ++i) {
uint32_t typeMask = 1ul << mTypes.valueAt(i);
if (info.mTypes & typeMask) {
types->push(mTypes.keyAt(i));
}
}
return OK;
}
status_t MediaCodecList::getCodecCapabilities(
size_t index, const char *type,
Vector<ProfileLevel> *profileLevels,
Vector<uint32_t> *colorFormats,
uint32_t *flags) const {
profileLevels->clear();
colorFormats->clear();
if (index >= mCodecInfos.size()) {
return -ERANGE;
}
const CodecInfo &info = mCodecInfos.itemAt(index);
OMXClient client;
status_t err = client.connect();
if (err != OK) {
return err;
}
CodecCapabilities caps;
err = QueryCodec(
client.interface(),
info.mName.c_str(), type, info.mIsEncoder, &caps);
if (err != OK) {
return err;
}
for (size_t i = 0; i < caps.mProfileLevels.size(); ++i) {
const CodecProfileLevel &src = caps.mProfileLevels.itemAt(i);
ProfileLevel profileLevel;
profileLevel.mProfile = src.mProfile;
profileLevel.mLevel = src.mLevel;
profileLevels->push(profileLevel);
}
for (size_t i = 0; i < caps.mColorFormats.size(); ++i) {
colorFormats->push(caps.mColorFormats.itemAt(i));
}
*flags = caps.mFlags;
return OK;
}
} // namespace android