/*
 * Copyright 2018 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 "MetaDataUtils"
#include <utils/Log.h>

#include <media/stagefright/foundation/avc_utils.h>
#include <media/stagefright/foundation/base64.h>
#include <media/stagefright/foundation/ABitReader.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ByteUtils.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataUtils.h>
#include <media/NdkMediaFormat.h>

namespace android {

bool MakeAVCCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
    if (data == nullptr || size == 0) {
        return false;
    }

    int32_t width;
    int32_t height;
    int32_t sarWidth;
    int32_t sarHeight;
    sp<ABuffer> accessUnit = new ABuffer((void*)data,  size);
    sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
    if (csd == nullptr) {
        return false;
    }
    meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);

    meta.setData(kKeyAVCC, kTypeAVCC, csd->data(), csd->size());
    meta.setInt32(kKeyWidth, width);
    meta.setInt32(kKeyHeight, height);
    if (sarWidth > 0 && sarHeight > 0) {
        meta.setInt32(kKeySARWidth, sarWidth);
        meta.setInt32(kKeySARHeight, sarHeight);
    }
    return true;
}

bool MakeAVCCodecSpecificData(AMediaFormat *meta, const uint8_t *data, size_t size) {
    if (meta == nullptr || data == nullptr || size == 0) {
        return false;
    }

    int32_t width;
    int32_t height;
    int32_t sarWidth;
    int32_t sarHeight;
    sp<ABuffer> accessUnit = new ABuffer((void*)data,  size);
    sp<ABuffer> csd = MakeAVCCodecSpecificData(accessUnit, &width, &height, &sarWidth, &sarHeight);
    if (csd == nullptr) {
        return false;
    }
    AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_VIDEO_AVC);

    AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_AVC, csd->data(), csd->size());
    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_WIDTH, width);
    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_HEIGHT, height);
    if (sarWidth > 0 && sarHeight > 0) {
        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_WIDTH, sarWidth);
        AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAR_HEIGHT, sarHeight);
    }
    return true;
}

// Check if the next 24 bits are VP9 SYNC_CODE
static bool isVp9SyncCode(ABitReader &bits) {
    if (bits.numBitsLeft() < 24) {
        return false;
    }
    return bits.getBits(24) == 0x498342;
}

// This parses bitdepth and subsampling in a VP9 uncompressed header
// (refer section bitdepth_colorspace_sampling in 6.2 of the VP9 bitstream spec)
static bool getVp9BitdepthChromaSubSampling(ABitReader &bits,
        int32_t profile,
        int32_t *bitDepth,
        int32_t *chromaSubsampling) {
    if (profile >= 2) {
        if (bits.numBitsLeft() < 1) {
            return false;
        }
        *bitDepth = bits.getBits(1) ? 12 : 10;
    } else {
        *bitDepth = 8;
    }

    uint32_t colorspace;
    if (!bits.getBitsGraceful(3, &colorspace)) {
        return false;
    }

    *chromaSubsampling = -1;
    if (colorspace != 7 /*SRGB*/) {
        // Skip yuv_range_flag
        if (!bits.skipBits(1)) {
            return false;
        }
        // Check for subsampling only for profiles 1 and 3.
        if (profile == 1 || profile == 3) {
            uint32_t ss_x;
            uint32_t ss_y;
            if (bits.getBitsGraceful(1, &ss_x) && bits.getBitsGraceful(1, &ss_y)) {
                *chromaSubsampling = ss_x << 1 & ss_y;
            } else {
                return false;
            }
        } else {
            *chromaSubsampling = 3;
        }
    } else {
        if (profile == 1 || profile == 3) {
            *chromaSubsampling = 0;
        }
    }
    return true;
}

/**
 * Build VP9 Codec Feature Metadata (CodecPrivate) to set CSD for VP9 codec.
 * For reference:
 * https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate.
 *
 * @param meta          A pointer to AMediaFormat object.
 * @param profile       The profile value of the VP9 stream.
 * @param level         The VP9 codec level. If the level is unknown, pass -1 to this parameter.
 * @param bitDepth      The bit depth of the luma and color components of the VP9 stream.
 * @param chromaSubsampling  The chroma subsampling of the VP9 stream. If chromaSubsampling is
 *                           unknown, pass -1 to this parameter.
 * @return true if CodecPrivate is set as CSD of AMediaFormat object.
 *
 */
static bool MakeVP9CodecPrivate(AMediaFormat* meta, int32_t profile, int32_t level,
                                int32_t bitDepth, int32_t chromaSubsampling) {
    if (meta == nullptr) {
        return false;
    }

    std::vector<uint8_t> codecPrivate;
    // Construct CodecPrivate in WebM format (ID | Length | Data).
    // Helper lambda to add a field to the codec private data
    auto addField = [&codecPrivate](uint8_t id, uint8_t value) {
        codecPrivate.push_back(id);
        codecPrivate.push_back(0x01);  // Length is always 1
        codecPrivate.push_back(value);
    };

    // Add fields
    addField(0x01, static_cast<uint8_t>(profile));
    if (level >= 0) {
        addField(0x02, static_cast<uint8_t>(level));
    }
    addField(0x03, static_cast<uint8_t>(bitDepth));
    if (chromaSubsampling >= 0) {
        addField(0x04, static_cast<uint8_t>(chromaSubsampling));
    }
    // Set CSD in the meta format
    AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, codecPrivate.data(), codecPrivate.size());
    return true;
}

// The param data contains the first frame data, starting with the uncompressed frame
// header. This uncompressed header (refer section 6.2 of the VP9 bitstream spec) is
// used to parse profile, bitdepth and subsampling.
bool MakeVP9CodecSpecificDataFromFirstFrame(AMediaFormat* meta, const uint8_t* data, size_t size) {
    if (meta == nullptr || data == nullptr || size == 0) {
        return false;
    }

    ABitReader bits(data, size);

    // First 2 bits of the uncompressed header should be the frame_marker.
    if (bits.getBits(2) != 0b10) {
        return false;
    }

    int32_t profileLowBit = bits.getBits(1);
    int32_t profileHighBit = bits.getBits(1);
    int32_t profile = profileHighBit * 2 + profileLowBit;

    // One reserved '0' bit if profile is 3.
    if (profile == 3 && bits.getBits(1) != 0) {
        return false;
    }

    // If show_existing_frame is set, we get no more data. Since this is
    // expected to be the first frame, we can return false which will cascade
    // into ERROR_MALFORMED.
    if (bits.getBits(1)) {
        return false;
    }

    int32_t frame_type = bits.getBits(1);

    // Upto 7 bits could be read till now, which were guaranteed to be available
    // since size > 0. Check for bits available before reading them from now on.
    if (bits.numBitsLeft() < 2) {
        return false;
    }

    int32_t show_frame = bits.getBits(1);
    int32_t error_resilient_mode = bits.getBits(1);
    int32_t bitDepth = 8;
    int32_t chromaSubsampling = -1;

    if (frame_type == 0 /* KEY_FRAME */) {
        // Check for sync code.
        if (!isVp9SyncCode(bits)) {
            return false;
        }

        if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) {
            return false;
        }
    } else {
        int32_t intra_only = 0;
        if (!show_frame) {
            if (bits.numBitsLeft() < 1) {
                return false;
            }
            intra_only = bits.getBits(1);
        }

        if (!error_resilient_mode) {
            if (bits.numBitsLeft() < 2) {
                return false;
            }
            // ignore reset_frame_context
            bits.skipBits(2);
        }

        if (!intra_only) {
            // Require first frame to be either KEY_FRAME or INTER_FRAME with intra_only set to true
            return false;
        }

        // Check for sync code.
        if (!isVp9SyncCode(bits)) {
            return false;
        }

        if (profile > 0) {
            if (!getVp9BitdepthChromaSubSampling(bits, profile, &bitDepth, &chromaSubsampling)) {
                return false;
            }
        } else {
            bitDepth = 8;
            chromaSubsampling = 3;
        }
    }
    int32_t csdSize = 6;
    if (chromaSubsampling != -1) {
        csdSize += 3;
    }
    // As level is not present in first frame build CodecPrivate without it.
    return MakeVP9CodecPrivate(meta, profile, -1, bitDepth, chromaSubsampling);
}

bool MakeVP9CodecPrivateFromVpcC(AMediaFormat* meta, const uint8_t* csdData, size_t size) {
    if (meta == nullptr || csdData == nullptr || size < 12) {
        return false;
    }

    // Check the first 4 bytes (VersionAndFlags) if they match the required value.
    if (csdData[0] != 0x01 || csdData[1] != 0x00 || csdData[2] != 0x00 || csdData[3] != 0x00) {
        return false;
    }

    // Create VP9 Codec Feature Metadata (CodecPrivate) that can be parsed.
    // https://www.webmproject.org/docs/container/#vp9-codec-feature-metadata-codecprivate
    const uint8_t* vpcCData = csdData + 4;  // Skip the first 4 bytes (VersionAndFlags)

    int32_t profile = vpcCData[0];
    int32_t level = vpcCData[1];
    int32_t bitDepth = (vpcCData[2] >> 4) & 0x0F;           // Bit Depth (4 bits).
    int32_t chromaSubsampling = (vpcCData[2] >> 1) & 0x07;  // Chroma Subsampling (3 bits).
    return MakeVP9CodecPrivate(meta, profile, level, bitDepth, chromaSubsampling);
}

bool MakeAACCodecSpecificData(MetaDataBase &meta, const uint8_t *data, size_t size) {
    if (data == nullptr || size < 7) {
        return false;
    }

    ABitReader bits(data, size);

    // adts_fixed_header

    if (bits.getBits(12) != 0xfffu) {
        ALOGE("Wrong atds_fixed_header");
        return false;
    }

    bits.skipBits(4);  // ID, layer, protection_absent

    unsigned profile = bits.getBits(2);
    if (profile == 3u) {
        ALOGE("profile should not be 3");
        return false;
    }
    unsigned sampling_freq_index = bits.getBits(4);
    bits.getBits(1);  // private_bit
    unsigned channel_configuration = bits.getBits(3);
    if (channel_configuration == 0u) {
        ALOGE("channel_config should not be 0");
        return false;
    }

    if (!MakeAACCodecSpecificData(
            meta, profile, sampling_freq_index, channel_configuration)) {
        return false;
    }

    meta.setInt32(kKeyIsADTS, true);
    return true;
}

bool MakeAACCodecSpecificData(
        uint8_t *csd, /* out */
        size_t *esds_size, /* in/out */
        unsigned profile, /* in */
        unsigned sampling_freq_index, /* in */
        unsigned channel_configuration, /* in */
        int32_t *sampling_rate /* out */
) {
    if(sampling_freq_index > 11u) {
        return false;
    }
    static const int32_t kSamplingFreq[] = {
        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
        16000, 12000, 11025, 8000
    };
    *sampling_rate = kSamplingFreq[sampling_freq_index];

    static const uint8_t kStaticESDS[] = {
        0x03, 22,
        0x00, 0x00,     // ES_ID
        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag

        0x04, 17,
        0x40,                       // Audio ISO/IEC 14496-3
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,
        0x00, 0x00, 0x00, 0x00,

        0x05, 2,
        // AudioSpecificInfo follows

        // oooo offf fccc c000
        // o - audioObjectType
        // f - samplingFreqIndex
        // c - channelConfig
    };

    size_t csdSize = sizeof(kStaticESDS) + 2;
    if (csdSize > *esds_size) {
        return false;
    }
    memcpy(csd, kStaticESDS, sizeof(kStaticESDS));

    csd[sizeof(kStaticESDS)] =
        ((profile + 1) << 3) | (sampling_freq_index >> 1);

    csd[sizeof(kStaticESDS) + 1] =
        ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);

    *esds_size = csdSize;
    return true;
}

bool MakeAACCodecSpecificData(AMediaFormat *meta, unsigned profile, unsigned sampling_freq_index,
        unsigned channel_configuration) {

    if(sampling_freq_index > 11u) {
        return false;
    }

    uint8_t csd[2];
    csd[0] = ((profile + 1) << 3) | (sampling_freq_index >> 1);
    csd[1] = ((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);

    static const int32_t kSamplingFreq[] = {
        96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
        16000, 12000, 11025, 8000
    };
    int32_t sampleRate = kSamplingFreq[sampling_freq_index];

    AMediaFormat_setBuffer(meta, AMEDIAFORMAT_KEY_CSD_0, csd, sizeof(csd));
    AMediaFormat_setString(meta, AMEDIAFORMAT_KEY_MIME, MEDIA_MIMETYPE_AUDIO_AAC);
    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_SAMPLE_RATE, sampleRate);
    AMediaFormat_setInt32(meta, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channel_configuration);

    return true;
}

bool MakeAACCodecSpecificData(
        MetaDataBase &meta,
        unsigned profile, unsigned sampling_freq_index,
        unsigned channel_configuration) {

    uint8_t csd[24];
    size_t csdSize = sizeof(csd);
    int32_t sampleRate;

    if (!MakeAACCodecSpecificData(csd, &csdSize, profile, sampling_freq_index,
            channel_configuration, &sampleRate)) {
        return false;
    }

    meta.setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);

    meta.setInt32(kKeySampleRate, sampleRate);
    meta.setInt32(kKeyChannelCount, channel_configuration);
    meta.setData(kKeyESDS, 0, csd, csdSize);
    return true;
}


static void extractAlbumArt(
        AMediaFormat *fileMeta, const void *data, size_t size) {
    ALOGV("extractAlbumArt from '%s'", (const char *)data);

    size_t inLen = strnlen((const char *)data, size);
    size_t flacSize = inLen / 4 * 3;
    uint8_t *flac = new uint8_t[flacSize];
    if (!decodeBase64(flac, &flacSize, (const char*)data)) {
        ALOGE("malformed base64 encoded data.");
        delete[] flac;
        return;
    }

    ALOGV("got flac of size %zu", flacSize);

    uint32_t picType;
    uint32_t typeLen;
    uint32_t descLen;
    uint32_t dataLen;
    char type[128];

    if (flacSize < 8) {
        delete[] flac;
        return;
    }

    picType = U32_AT(flac);

    if (picType != 3) {
        // This is not a front cover.
        delete[] flac;
        return;
    }

    typeLen = U32_AT(&flac[4]);
    if (typeLen > sizeof(type) - 1) {
        delete[] flac;
        return;
    }

    // we've already checked above that flacSize >= 8
    if (flacSize - 8 < typeLen) {
        delete[] flac;
        return;
    }

    memcpy(type, &flac[8], typeLen);
    type[typeLen] = '\0';

    ALOGV("picType = %d, type = '%s'", picType, type);

    if (!strcmp(type, "-->")) {
        // This is not inline cover art, but an external url instead.
        delete[] flac;
        return;
    }

    if (flacSize < 32 || flacSize - 32 < typeLen) {
        delete[] flac;
        return;
    }

    descLen = U32_AT(&flac[8 + typeLen]);
    if (flacSize - 32 - typeLen < descLen) {
        delete[] flac;
        return;
    }

    dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);

    // we've already checked above that (flacSize - 32 - typeLen - descLen) >= 0
    if (flacSize - 32 - typeLen - descLen < dataLen) {
        delete[] flac;
        return;
    }

    ALOGV("got image data, %zu trailing bytes",
         flacSize - 32 - typeLen - descLen - dataLen);

    AMediaFormat_setBuffer(fileMeta, AMEDIAFORMAT_KEY_ALBUMART,
            &flac[8 + typeLen + 4 + descLen + 20], dataLen);

    delete[] flac;
}

void parseVorbisComment(
        AMediaFormat *fileMeta, const char *comment, size_t commentLength) {
    // Haptic tag is only kept here as it will only be used in extractor to generate channel mask.
    struct {
        const char *const mTag;
        const char *mKey;
    } kMap[] = {
        { "TITLE", AMEDIAFORMAT_KEY_TITLE },
        { "ARTIST", AMEDIAFORMAT_KEY_ARTIST },
        { "ALBUMARTIST", AMEDIAFORMAT_KEY_ALBUMARTIST },
        { "ALBUM ARTIST", AMEDIAFORMAT_KEY_ALBUMARTIST },
        { "COMPILATION", AMEDIAFORMAT_KEY_COMPILATION },
        { "ALBUM", AMEDIAFORMAT_KEY_ALBUM },
        { "COMPOSER", AMEDIAFORMAT_KEY_COMPOSER },
        { "GENRE", AMEDIAFORMAT_KEY_GENRE },
        { "AUTHOR", AMEDIAFORMAT_KEY_AUTHOR },
        { "TRACKNUMBER", AMEDIAFORMAT_KEY_CDTRACKNUMBER },
        { "DISCNUMBER", AMEDIAFORMAT_KEY_DISCNUMBER },
        { "DATE", AMEDIAFORMAT_KEY_DATE },
        { "YEAR", AMEDIAFORMAT_KEY_YEAR },
        { "LYRICIST", AMEDIAFORMAT_KEY_LYRICIST },
        { "METADATA_BLOCK_PICTURE", AMEDIAFORMAT_KEY_ALBUMART },
        { "ANDROID_LOOP", AMEDIAFORMAT_KEY_LOOP },
        { "ANDROID_HAPTIC", AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT },
    };

        for (size_t j = 0; j < sizeof(kMap) / sizeof(kMap[0]); ++j) {
            size_t tagLen = strlen(kMap[j].mTag);
            if (!strncasecmp(kMap[j].mTag, comment, tagLen)
                    && comment[tagLen] == '=') {
                if (kMap[j].mKey == AMEDIAFORMAT_KEY_ALBUMART) {
                    extractAlbumArt(
                            fileMeta,
                            &comment[tagLen + 1],
                            commentLength - tagLen - 1);
                } else if (kMap[j].mKey == AMEDIAFORMAT_KEY_LOOP) {
                    if (!strcasecmp(&comment[tagLen + 1], "true")) {
                        AMediaFormat_setInt32(fileMeta, AMEDIAFORMAT_KEY_LOOP, 1);
                    }
                } else if (kMap[j].mKey == AMEDIAFORMAT_KEY_HAPTIC_CHANNEL_COUNT) {
                    char *end;
                    errno = 0;
                    const int hapticChannelCount = strtol(&comment[tagLen + 1], &end, 10);
                    if (errno == 0) {
                        AMediaFormat_setInt32(fileMeta, kMap[j].mKey, hapticChannelCount);
                    } else {
                        ALOGE("Error(%d) when parsing haptic channel count", errno);
                    }
                } else {
                    AMediaFormat_setString(fileMeta, kMap[j].mKey, &comment[tagLen + 1]);
                }
            }
        }

}

}  // namespace android
