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

Commit ebf66ea2 authored by Andreas Huber's avatar Andreas Huber
Browse files

Better (proper) parsing of the AVCDecoderConfigurationRecord, respect hardware...

Better (proper) parsing of the AVCDecoderConfigurationRecord, respect hardware decoder profile/level limits.
parent 4f5e602e
Loading
Loading
Loading
Loading
+97 −20
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/MmapSource.h>
#include <media/stagefright/OMXCodec.h>
#include <media/stagefright/Utils.h>
#include <utils/Vector.h>

#include <OMX_Audio.h>
@@ -116,6 +117,39 @@ static const char *GetCodec(const CodecInfo *info, size_t numInfos,
    return NULL;
}

enum {
    kAVCProfileBaseline      = 0x42,
    kAVCProfileMain          = 0x4d,
    kAVCProfileExtended      = 0x58,
    kAVCProfileHigh          = 0x64,
    kAVCProfileHigh10        = 0x6e,
    kAVCProfileHigh422       = 0x7a,
    kAVCProfileHigh444       = 0xf4,
    kAVCProfileCAVLC444Intra = 0x2c
};

static const char *AVCProfileToString(uint8_t profile) {
    switch (profile) {
        case kAVCProfileBaseline:
            return "Baseline";
        case kAVCProfileMain:
            return "Main";
        case kAVCProfileExtended:
            return "Extended";
        case kAVCProfileHigh:
            return "High";
        case kAVCProfileHigh10:
            return "High 10";
        case kAVCProfileHigh422:
            return "High 422";
        case kAVCProfileHigh444:
            return "High 444";
        case kAVCProfileCAVLC444Intra:
            return "CAVLC 444 Intra";
        default:   return "Unknown";
    }
}

// static
sp<OMXCodec> OMXCodec::Create(
        const sp<IOMX> &omx,
@@ -189,29 +223,72 @@ sp<OMXCodec> OMXCodec::Create(
    } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
        printf("found avcc of size %d\n", size);

        const uint8_t *ptr = (const uint8_t *)data + 6;
        // Parse the AVCDecoderConfigurationRecord

        const uint8_t *ptr = (const uint8_t *)data;

        CHECK(size >= 7);
        CHECK_EQ(ptr[0], 1);  // configurationVersion == 1
        uint8_t profile = ptr[1];
        uint8_t level = ptr[3];

        CHECK((ptr[4] >> 2) == 0x3f);  // reserved

        size_t lengthSize = 1 + (ptr[4] & 3);

        // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
        // violates it...
        // CHECK((ptr[5] >> 5) == 7);  // reserved

        size_t numSeqParameterSets = ptr[5] & 31;

        ptr += 6;
        size -= 6;
        while (size >= 2) {
            size_t length = ptr[0] << 8 | ptr[1];

        for (size_t i = 0; i < numSeqParameterSets; ++i) {
            CHECK(size >= 2);
            size_t length = U16_AT(ptr);

            ptr += 2;
            size -= 2;

            // printf("length = %d, size = %d\n", length, size);

            CHECK(size >= length);

            codec->addCodecSpecificData(ptr, length);

            ptr += length;
            size -= length;

            if (size <= 1) {
                break;
        }

            ptr++;  // XXX skip trailing 0x01 byte???
        CHECK(size >= 1);
        size_t numPictureParameterSets = *ptr;
        ++ptr;
        --size;

        for (size_t i = 0; i < numPictureParameterSets; ++i) {
            CHECK(size >= 2);
            size_t length = U16_AT(ptr);

            ptr += 2;
            size -= 2;

            CHECK(size >= length);

            codec->addCodecSpecificData(ptr, length);

            ptr += length;
            size -= length;
        }

        LOGI("AVC profile = %d (%s), level = %d",
             (int)profile, AVCProfileToString(profile), (int)level / 10);

        if (!strcmp(componentName, "OMX.TI.Video.Decoder")
            && (profile != kAVCProfileBaseline || level > 39)) {
            // This stream exceeds the decoder's capabilities.

            LOGE("Profile and/or level exceed the decoder's capabilities.");
            return NULL;
        }
    }