Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2096abe9 authored by Lajos Molnar's avatar Lajos Molnar Committed by Android (Google) Code Review
Browse files

Merge "Add MediaCodecList capabilities" into lmp-dev

parents 4287942b 513d967d
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -25,9 +25,12 @@
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/Vector.h>
#include <utils/StrongPointer.h>

namespace android {

struct AMessage;

struct MediaCodecList {
    static const MediaCodecList *getInstance();

@@ -51,15 +54,19 @@ struct MediaCodecList {
            size_t index, const char *type,
            Vector<ProfileLevel> *profileLevels,
            Vector<uint32_t> *colorFormats,
            uint32_t *flags) const;
            uint32_t *flags,
            // TODO default argument is only for compatibility with existing JNI
            sp<AMessage> *capabilities = NULL) const;

private:
    enum Section {
        SECTION_TOPLEVEL,
        SECTION_DECODERS,
        SECTION_DECODER,
        SECTION_DECODER_TYPE,
        SECTION_ENCODERS,
        SECTION_ENCODER,
        SECTION_ENCODER_TYPE,
        SECTION_INCLUDE,
    };

@@ -67,7 +74,10 @@ private:
        AString mName;
        bool mIsEncoder;
        uint32_t mTypes;
        uint32_t mSoleType;
        uint32_t mQuirks;
        KeyedVector<uint32_t, sp<AMessage> > mCaps;
        sp<AMessage> mCurrentCaps;
    };

    static MediaCodecList *sCodecList;
@@ -103,6 +113,8 @@ private:

    status_t addQuirk(const char **attrs);
    status_t addTypeFromAttributes(const char **attrs);
    status_t addLimit(const char **attrs);
    status_t addFeature(const char **attrs);
    void addType(const char *name);

    DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
+3 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ struct AMessage : public RefBase {
    void setDouble(const char *name, double value);
    void setPointer(const char *name, void *value);
    void setString(const char *name, const char *s, ssize_t len = -1);
    void setString(const char *name, const AString &s);
    void setObject(const char *name, const sp<RefBase> &obj);
    void setBuffer(const char *name, const sp<ABuffer> &buffer);
    void setMessage(const char *name, const sp<AMessage> &obj);
@@ -58,6 +59,8 @@ struct AMessage : public RefBase {
            const char *name,
            int32_t left, int32_t top, int32_t right, int32_t bottom);

    bool contains(const char *name) const;

    bool findInt32(const char *name, int32_t *value) const;
    bool findInt64(const char *name, int64_t *value) const;
    bool findSize(const char *name, size_t *value) const;
+3 −0
Original line number Diff line number Diff line
@@ -70,6 +70,9 @@ struct AString {
    size_t hash() const;

    bool operator==(const AString &other) const;
    bool operator!=(const AString &other) const {
        return !operator==(other);
    }
    bool operator<(const AString &other) const;
    bool operator>(const AString &other) const;

+294 −1
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@
#include <media/stagefright/MediaCodecList.h>

#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
@@ -79,6 +80,19 @@ void MediaCodecList::parseTopLevelXMLFile(const char *codecs_xml) {
                  info->mName.c_str());

            mCodecInfos.removeAt(i);
#if LOG_NDEBUG == 0
        } else {
            for (size_t type_ix = 0; type_ix < mTypes.size(); ++type_ix) {
                uint32_t typeMask = 1ul << mTypes.valueAt(type_ix);
                if (info->mTypes & typeMask) {
                    AString mime = mTypes.keyAt(type_ix);
                    uint32_t bit = mTypes.valueAt(type_ix);

                    ALOGV("%s codec info for %s: %s", info->mName.c_str(), mime.c_str(),
                            info->mCaps.editValueFor(bit)->debugString().c_str());
                }
            }
#endif
        }
    }

@@ -217,6 +231,8 @@ void MediaCodecList::startElementHandler(
        return;
    }

    bool inType = true;

    if (!strcmp(name, "Include")) {
        mInitCheck = includeXMLFile(attrs);
        if (mInitCheck == OK) {
@@ -267,6 +283,26 @@ void MediaCodecList::startElementHandler(
                mInitCheck = addQuirk(attrs);
            } else if (!strcmp(name, "Type")) {
                mInitCheck = addTypeFromAttributes(attrs);
                mCurrentSection =
                    (mCurrentSection == SECTION_DECODER
                            ? SECTION_DECODER_TYPE : SECTION_ENCODER_TYPE);
            }
        }
        inType = false;
        // fall through

        case SECTION_DECODER_TYPE:
        case SECTION_ENCODER_TYPE:
        {
            CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
            // ignore limits and features specified outside of type
            bool outside = !inType && info->mSoleType == 0;
            if (outside && (!strcmp(name, "Limit") || !strcmp(name, "Feature"))) {
                ALOGW("ignoring %s specified outside of a Type", name);
            } else if (!strcmp(name, "Limit")) {
                mInitCheck = addLimit(attrs);
            } else if (!strcmp(name, "Feature")) {
                mInitCheck = addFeature(attrs);
            }
            break;
        }
@@ -300,10 +336,27 @@ void MediaCodecList::endElementHandler(const char *name) {
            break;
        }

        case SECTION_DECODER_TYPE:
        case SECTION_ENCODER_TYPE:
        {
            if (!strcmp(name, "Type")) {
                mCurrentSection =
                    (mCurrentSection == SECTION_DECODER_TYPE
                            ? SECTION_DECODER : SECTION_ENCODER);

                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
                info->mCurrentCaps = NULL;
            }
            break;
        }

        case SECTION_DECODER:
        {
            if (!strcmp(name, "MediaCodec")) {
                mCurrentSection = SECTION_DECODERS;

                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
                info->mCurrentCaps = NULL;
            }
            break;
        }
@@ -312,6 +365,9 @@ void MediaCodecList::endElementHandler(const char *name) {
        {
            if (!strcmp(name, "MediaCodec")) {
                mCurrentSection = SECTION_ENCODERS;

                CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
                info->mCurrentCaps = NULL;
            }
            break;
        }
@@ -373,11 +429,16 @@ void MediaCodecList::addMediaCodec(
    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
    info->mName = name;
    info->mIsEncoder = encoder;
    info->mSoleType = 0;
    info->mTypes = 0;
    info->mQuirks = 0;
    info->mCurrentCaps = NULL;

    if (type != NULL) {
        addType(type);
        // if type was specified in attributes, we do not allow
        // subsequent types
        info->mSoleType = info->mTypes;
    }
}

@@ -427,6 +488,12 @@ status_t MediaCodecList::addQuirk(const char **attrs) {
status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
    const char *name = NULL;

    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
    if (info->mSoleType != 0) {
        ALOGE("Codec '%s' already had its type specified", info->mName.c_str());
        return -EINVAL;
    }

    size_t i = 0;
    while (attrs[i] != NULL) {
        if (!strcmp(attrs[i], "name")) {
@@ -469,6 +536,11 @@ void MediaCodecList::addType(const char *name) {

    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
    info->mTypes |= 1ul << bit;
    if (info->mCaps.indexOfKey(bit) < 0) {
        AMessage *msg = new AMessage();
        info->mCaps.add(bit, msg);
    }
    info->mCurrentCaps = info->mCaps.editValueFor(bit);
}

ssize_t MediaCodecList::findCodecByType(
@@ -494,6 +566,216 @@ ssize_t MediaCodecList::findCodecByType(
    return -ENOENT;
}

static status_t limitFoundMissingAttr(AString name, const char *attr, bool found = true) {
    ALOGE("limit '%s' with %s'%s' attribute", name.c_str(),
            (found ? "" : "no "), attr);
    return -EINVAL;
}

static status_t limitError(AString name, const char *msg) {
    ALOGE("limit '%s' %s", name.c_str(), msg);
    return -EINVAL;
}

static status_t limitInvalidAttr(AString name, const char *attr, AString value) {
    ALOGE("limit '%s' with invalid '%s' attribute (%s)", name.c_str(),
            attr, value.c_str());
    return -EINVAL;
}

status_t MediaCodecList::addLimit(const char **attrs) {
    sp<AMessage> msg = new AMessage();

    size_t i = 0;
    while (attrs[i] != NULL) {
        if (attrs[i + 1] == NULL) {
            return -EINVAL;
        }

        // attributes with values
        if (!strcmp(attrs[i], "name")
                || !strcmp(attrs[i], "default")
                || !strcmp(attrs[i], "in")
                || !strcmp(attrs[i], "max")
                || !strcmp(attrs[i], "min")
                || !strcmp(attrs[i], "range")
                || !strcmp(attrs[i], "ranges")
                || !strcmp(attrs[i], "scale")
                || !strcmp(attrs[i], "value")) {
            msg->setString(attrs[i], attrs[i + 1]);
            ++i;
        } else {
            return -EINVAL;
        }
        ++i;
    }

    AString name;
    if (!msg->findString("name", &name)) {
        ALOGE("limit with no 'name' attribute");
        return -EINVAL;
    }

    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);

    // size, blocks, bitrate, frame-rate, blocks-per-second, aspect-ratio: range
    // quality: range + default + [scale]
    // complexity: range + default
    bool found;
    if (name == "aspect-ratio" || name == "bitrate" || name == "block-count"
            || name == "blocks-per-second" || name == "complexity"
            || name == "frame-rate" || name == "quality" || name == "size") {
        AString min, max;
        if (msg->findString("min", &min) && msg->findString("max", &max)) {
            min.append("-");
            min.append(max);
            if (msg->contains("range") || msg->contains("value")) {
                return limitError(name, "has 'min' and 'max' as well as 'range' or "
                        "'value' attributes");
            }
            msg->setString("range", min);
        } else if (msg->contains("min") || msg->contains("max")) {
            return limitError(name, "has only 'min' or 'max' attribute");
        } else if (msg->findString("value", &max)) {
            min = max;
            min.append("-");
            min.append(max);
            if (msg->contains("range")) {
                return limitError(name, "has both 'range' and 'value' attributes");
            }
            msg->setString("range", min);
        }

        AString range, scale = "linear", def, in_;
        if (!msg->findString("range", &range)) {
            return limitError(name, "with no 'range', 'value' or 'min'/'max' attributes");
        }

        if ((name == "quality" || name == "complexity") ^
                (found = msg->findString("default", &def))) {
            return limitFoundMissingAttr(name, "default", found);
        }
        if (name != "quality" && msg->findString("scale", &scale)) {
            return limitFoundMissingAttr(name, "scale");
        }
        if ((name == "aspect-ratio") ^ (found = msg->findString("in", &in_))) {
            return limitFoundMissingAttr(name, "in", found);
        }

        if (name == "aspect-ratio") {
            if (!(in_ == "pixels") && !(in_ == "blocks")) {
                return limitInvalidAttr(name, "in", in_);
            }
            in_.erase(5, 1); // (pixel|block)-aspect-ratio
            in_.append("-");
            in_.append(name);
            name = in_;
        }
        if (name == "quality") {
            info->mCurrentCaps->setString("quality-scale", scale);
        }
        if (name == "quality" || name == "complexity") {
            AString tag = name;
            tag.append("-default");
            info->mCurrentCaps->setString(tag.c_str(), def);
        }
        AString tag = name;
        tag.append("-range");
        info->mCurrentCaps->setString(tag.c_str(), range);
    } else {
        AString max, value, ranges;
        if (msg->contains("default")) {
            return limitFoundMissingAttr(name, "default");
        } else if (msg->contains("in")) {
            return limitFoundMissingAttr(name, "in");
        } else if ((name == "channel-count") ^
                (found = msg->findString("max", &max))) {
            return limitFoundMissingAttr(name, "max", found);
        } else if (msg->contains("min")) {
            return limitFoundMissingAttr(name, "min");
        } else if (msg->contains("range")) {
            return limitFoundMissingAttr(name, "range");
        } else if ((name == "sample-rate") ^
                (found = msg->findString("ranges", &ranges))) {
            return limitFoundMissingAttr(name, "ranges", found);
        } else if (msg->contains("scale")) {
            return limitFoundMissingAttr(name, "scale");
        } else if ((name == "alignment" || name == "block-size") ^
                (found = msg->findString("value", &value))) {
            return limitFoundMissingAttr(name, "value", found);
        }

        if (max.size()) {
            AString tag = "max-";
            tag.append(name);
            info->mCurrentCaps->setString(tag.c_str(), max);
        } else if (value.size()) {
            info->mCurrentCaps->setString(name.c_str(), value);
        } else if (ranges.size()) {
            AString tag = name;
            tag.append("-ranges");
            info->mCurrentCaps->setString(tag.c_str(), ranges);
        } else {
            ALOGW("Ignoring unrecognized limit '%s'", name.c_str());
        }
    }
    return OK;
}

static bool parseBoolean(const char *s) {
    if (!strcasecmp(s, "true") || !strcasecmp(s, "yes") || !strcasecmp(s, "y")) {
        return true;
    }
    char *end;
    unsigned long res = strtoul(s, &end, 10);
    return *s != '\0' && *end == '\0' && res > 0;
}

status_t MediaCodecList::addFeature(const char **attrs) {
    size_t i = 0;
    const char *name = NULL;
    int32_t optional = -1;
    int32_t required = -1;

    while (attrs[i] != NULL) {
        if (attrs[i + 1] == NULL) {
            return -EINVAL;
        }

        // attributes with values
        if (!strcmp(attrs[i], "name")) {
            name = attrs[i + 1];
            ++i;
        } else if (!strcmp(attrs[i], "optional") || !strcmp(attrs[i], "required")) {
            int value = (int)parseBoolean(attrs[i + 1]);
            if (!strcmp(attrs[i], "optional")) {
                optional = value;
            } else {
                required = value;
            }
            ++i;
        } else {
            return -EINVAL;
        }
        ++i;
    }
    if (name == NULL) {
        ALOGE("feature with no 'name' attribute");
        return -EINVAL;
    }

    if (optional == required && optional != -1) {
        ALOGE("feature '%s' is both/neither optional and required", name);
        return -EINVAL;
    }

    CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
    AString tag = "feature-";
    tag.append(name);
    info->mCurrentCaps->setInt32(tag.c_str(), (required == 1) || (optional == 0));
    return OK;
}

ssize_t MediaCodecList::findCodecByName(const char *name) const {
    for (size_t i = 0; i < mCodecInfos.size(); ++i) {
        const CodecInfo &info = mCodecInfos.itemAt(i);
@@ -571,7 +853,8 @@ status_t MediaCodecList::getCodecCapabilities(
        size_t index, const char *type,
        Vector<ProfileLevel> *profileLevels,
        Vector<uint32_t> *colorFormats,
        uint32_t *flags) const {
        uint32_t *flags,
        sp<AMessage> *capabilities) const {
    profileLevels->clear();
    colorFormats->clear();

@@ -581,6 +864,11 @@ status_t MediaCodecList::getCodecCapabilities(

    const CodecInfo &info = mCodecInfos.itemAt(index);

    ssize_t typeIndex = mTypes.indexOfKey(type);
    if (typeIndex < 0) {
        return -EINVAL;
    }

    OMXClient client;
    status_t err = client.connect();
    if (err != OK) {
@@ -611,6 +899,11 @@ status_t MediaCodecList::getCodecCapabilities(

    *flags = caps.mFlags;

    // TODO this check will be removed once JNI side is merged
    if (capabilities != NULL) {
        *capabilities = info.mCaps.valueFor(typeIndex);
    }

    return OK;
}

+19 −0
Original line number Diff line number Diff line
@@ -127,6 +127,20 @@ const AMessage::Item *AMessage::findItem(
    return NULL;
}

bool AMessage::contains(const char *name) const {
    name = AAtomizer::Atomize(name);

    for (size_t i = 0; i < mNumItems; ++i) {
        const Item *item = &mItems[i];

        if (item->mName == name) {
            return true;
        }
    }

    return false;
}

#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME)                             \
void AMessage::set##NAME(const char *name, TYPENAME value) {            \
    Item *item = allocateItem(name);                                    \
@@ -160,6 +174,11 @@ void AMessage::setString(
    item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}

void AMessage::setString(
        const char *name, const AString &s) {
    setString(name, s.c_str(), s.size());
}

void AMessage::setObjectInternal(
        const char *name, const sp<RefBase> &obj, Type type) {
    Item *item = allocateItem(name);