/*
 * Copyright (C) 2011 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.
 */

#include "sles_allinclusive.h"

#include <media/IMediaPlayerService.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
#include <media/IOMX.h>
#include <media/stagefright/MediaDefs.h>


namespace android {

// listed in same order as VideoCodecIds[] in file "../devices.c" with ANDROID defined
static const char *kVideoMimeTypes[] = {
        MEDIA_MIMETYPE_VIDEO_MPEG2,
        MEDIA_MIMETYPE_VIDEO_H263,
        MEDIA_MIMETYPE_VIDEO_MPEG4,
        MEDIA_MIMETYPE_VIDEO_AVC,
        MEDIA_MIMETYPE_VIDEO_VPX
};
// must == kMaxVideoDecoders
static const size_t kNbVideoMimeTypes = sizeof(kVideoMimeTypes) / sizeof(kVideoMimeTypes[0]);

// codec capabilities in the following arrays maps to the mime types defined in kVideoMimeTypes
// CodecCapabilities is from OMXCodec.h
static Vector<CodecCapabilities> VideoDecoderCapabilities[kNbVideoMimeTypes];
static XAuint32 VideoDecoderNbProfLevel[kNbVideoMimeTypes];

static XAuint32 NbSupportedDecoderTypes = 0;


XAuint32 convertOpenMaxIlToAl(OMX_U32 ilVideoProfileOrLevel) {
    // For video codec profiles and levels, the number of trailing zeroes in OpenMAX IL
    // are equal to the matching OpenMAX AL constant value plus 1, for example:
    //    XA_VIDEOPROFILE_H263_BACKWARDCOMPATIBLE ((XAuint32) 0x00000003)
    //        matches
    //    OMX_VIDEO_H263ProfileBackwardCompatible  = 0x04
    return (XAuint32) (__builtin_ctz(ilVideoProfileOrLevel) + 1);
}


bool android_videoCodec_expose() {
    SL_LOGV("android_videoCodec_expose()");

    sp<IMediaPlayerService> service(IMediaDeathNotifier::getMediaPlayerService());
    if (service == NULL) {
        // no need to SL_LOGE; getMediaPlayerService already will have done so
        return false;
    }

    sp<IOMX> omx(service->getOMX());
    if (omx.get() == NULL) {
        ALOGE("android_videoCodec_expose() couldn't access OMX interface");
        return false;
    }

    // used to check whether no codecs were found, which is a sign of failure
    NbSupportedDecoderTypes = 0;
    for (size_t m = 0 ; m < kNbVideoMimeTypes ; m++) {
        // QueryCodecs is from OMXCodec.h
        if (OK == QueryCodecs(omx, kVideoMimeTypes[m], true /* queryDecoders */,
                true /* hwCodecOnly */, &VideoDecoderCapabilities[m])) {
            if (VideoDecoderCapabilities[m].empty()) {
                VideoDecoderNbProfLevel[m] = 0;
            } else {
                // get the number of profiles and levels for the first codec implementation
                // for a given decoder ID / MIME type
                Vector<CodecProfileLevel> &profileLevels =
                        VideoDecoderCapabilities[m].editItemAt(0).mProfileLevels;
#if 0   // Intentionally disabled example of making modifications to profile / level combinations
                if (VideoDecoderIds[m] == XA_VIDEOCODEC_AVC) {
                    // remove non-core profile / level combinations
                    for (size_t i = 0, size = profileLevels.size(); i < size; ) {
                        CodecProfileLevel profileLevel = profileLevels.itemAt(i);
                        if (profileLevel.mProfile == XA_VIDEOPROFILE_AVC_BASELINE) {
                            // either skip past this item and don't change vector size
                            ++i;
                        } else {
                            // or remove this item, decrement the vector size,
                            // and next time through the loop check a different item at same index
                            profileLevels.removeAt(i);
                            --size;
                        }
                    }
                }
#endif
                if ((VideoDecoderNbProfLevel[m] = profileLevels.size()) > 0) {
                    NbSupportedDecoderTypes++;
                } else {
                    VideoDecoderCapabilities[m].clear();
                }
            }
        }
    }

    return (NbSupportedDecoderTypes > 0);
}


void android_videoCodec_deinit() {
    SL_LOGV("android_videoCodec_deinit()");
    for (size_t m = 0 ; m < kNbVideoMimeTypes ; m++) {
        VideoDecoderCapabilities[m].clear();
    }
    // not needed
    // memset(VideoDecoderNbProfLevel, 0, sizeof(VideoDecoderNbProfLevel));
    // NbSupportedDecoderTypes = 0;
}


XAuint32 android_videoCodec_getNbDecoders() {
    return NbSupportedDecoderTypes;
}


void android_videoCodec_getDecoderIds(XAuint32 nbDecoders, XAuint32 *pDecoderIds) {
    XAuint32 *pIds = pDecoderIds;
    XAuint32 nbFound = 0;
    for (size_t m = 0 ; m < kNbVideoMimeTypes ; m++) {
        if (!VideoDecoderCapabilities[m].empty()) {
            *pIds = VideoDecoderIds[m];
            pIds++;
            nbFound++;
        }
        // range check: function can be called for fewer codecs than there are
        if (nbFound == nbDecoders) {
            break;
        }
    }
}


SLresult android_videoCodec_getProfileLevelCombinationNb(XAuint32 decoderId, XAuint32 *pNb)
{
    // translate a decoder ID to an index in the codec table
    size_t decoderIndex = 0;
    while (decoderIndex < kNbVideoMimeTypes) {
        if (decoderId == VideoDecoderIds[decoderIndex]) {
            *pNb = VideoDecoderNbProfLevel[decoderIndex];
            return XA_RESULT_SUCCESS;
        }
        decoderIndex++;
    }

    // spec doesn't allow a decoder to report zero profile/level combinations
    *pNb = 0;
    return XA_RESULT_PARAMETER_INVALID;
}


SLresult android_videoCodec_getProfileLevelCombination(XAuint32 decoderId, XAuint32 plIndex,
        XAVideoCodecDescriptor *pDescr)
{
    // translate a decoder ID to an index in the codec table
    size_t decoderIndex = 0;
    while (decoderIndex < kNbVideoMimeTypes) {
        if (decoderId == VideoDecoderIds[decoderIndex]) {
            // We only look at the first codec implementation for a given decoder ID / MIME type.
            // OpenMAX AL doesn't let you expose the capabilities of multiple codec implementations.
            if (!(plIndex < VideoDecoderCapabilities[decoderIndex].itemAt(0).mProfileLevels.size()))
            {
                // asking for invalid profile/level
                return XA_RESULT_PARAMETER_INVALID;
            }
            //     set the fields we know about
            pDescr->codecId = decoderId;
            pDescr->profileSetting = convertOpenMaxIlToAl(VideoDecoderCapabilities[decoderIndex].
                    itemAt(0).mProfileLevels.itemAt(plIndex).mProfile);
            pDescr->levelSetting =  convertOpenMaxIlToAl(VideoDecoderCapabilities[decoderIndex].
                    itemAt(0).mProfileLevels.itemAt(plIndex).mLevel);
            //     initialize the fields we don't know about
            pDescr->maxWidth = 0;
            pDescr->maxHeight = 0;
            pDescr->maxFrameRate = 0;
            pDescr->maxBitRate = 0;
            pDescr->rateControlSupported = 0;
            break;
        }
        decoderIndex++;
    }
    return (decoderIndex < kNbVideoMimeTypes) ? XA_RESULT_SUCCESS : XA_RESULT_PARAMETER_INVALID;
}

} // namespace android
