/* ------------------------------------------------------------------
 * Copyright (C) 1998-2010 PacketVideo
 *
 * 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 "pvmf_omx_basedec_port.h"
#include "pvmf_omx_basedec_node.h"


OSCL_EXPORT_REF PVMFOMXDecPort::PVMFOMXDecPort(int32 aTag, PVMFNodeInterface* aNode, const char*name)
        : PvmfPortBaseImpl(aTag, aNode, name)
{
    iOMXNode = (PVMFOMXBaseDecNode *) aNode;
    Construct();
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMFOMXDecPort::Construct()
{
    iLogger = PVLogger::GetLoggerObject(PortName());
    oscl_memset(&iStats, 0, sizeof(PvmfPortBaseImplStats));
    iNumFramesGenerated = 0;
    iNumFramesConsumed = 0;
    iTrackConfig = NULL;
    iTrackConfigSize = 0;
    iDspWidth = 0;
    iDspHeight = 0;
    if ((oscl_strcmp(PortName(), PVMF_OMX_VIDEO_DEC_INPUT_PORT_NAME) == 0) || (oscl_strcmp(PortName(), PVMF_OMX_VIDEO_DEC_OUTPUT_PORT_NAME) == 0))
    {
        PvmiCapabilityAndConfigPortFormatImpl::Construct(
            PVMF_OMX_VIDEO_DEC_PORT_INPUT_FORMATS
            , PVMF_OMX_VIDEO_DEC_PORT_INPUT_FORMATS_VALTYPE);
    }
    else if ((oscl_strcmp(PortName(), PVMF_OMX_AUDIO_DEC_INPUT_PORT_NAME) == 0) || (oscl_strcmp(PortName(), PVMF_OMX_AUDIO_DEC_OUTPUT_PORT_NAME) == 0))
    {
        PvmiCapabilityAndConfigPortFormatImpl::Construct(
            PVMF_OMX_AUDIO_DEC_PORT_INPUT_FORMATS
            , PVMF_OMX_AUDIO_DEC_PORT_INPUT_FORMATS_VALTYPE);
    }
}


OSCL_EXPORT_REF PVMFOMXDecPort::~PVMFOMXDecPort()
{
    if (iTrackConfig != NULL)
    {
        OSCL_FREE(iTrackConfig);
        iTrackConfigSize = 0;
    }

    Disconnect();
    ClearMsgQueues();
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF bool PVMFOMXDecPort::IsFormatSupported(PVMFFormatType aFmt)
{
    bool isSupported = false;

    if ((oscl_strcmp(PortName(), PVMF_OMX_VIDEO_DEC_INPUT_PORT_NAME) == 0) || (oscl_strcmp(PortName(), PVMF_OMX_VIDEO_DEC_OUTPUT_PORT_NAME) == 0))
    {
        isSupported = ((aFmt == PVMF_MIME_YUV420) ||
                       (aFmt == PVMF_MIME_H264_VIDEO_RAW) ||
                       (aFmt == PVMF_MIME_H264_VIDEO_MP4) ||
                       (aFmt == PVMF_MIME_H264_VIDEO) ||
                       (aFmt == PVMF_MIME_M4V) ||
                       (aFmt == PVMF_MIME_H2631998) ||
                       (aFmt == PVMF_MIME_H2632000) ||
                       (aFmt == PVMF_MIME_WMV) ||
                       (aFmt == PVMF_MIME_REAL_VIDEO)
                      );
    }
    else if ((oscl_strcmp(PortName(), PVMF_OMX_AUDIO_DEC_INPUT_PORT_NAME) == 0) || (oscl_strcmp(PortName(), PVMF_OMX_AUDIO_DEC_OUTPUT_PORT_NAME) == 0))
    {
        isSupported = ((aFmt == PVMF_MIME_PCM16) ||
                       (aFmt == PVMF_MIME_LATM) ||
                       (aFmt == PVMF_MIME_MPEG4_AUDIO) ||
                       (aFmt == PVMF_MIME_3640) ||
                       (aFmt == PVMF_MIME_ADIF) ||
                       (aFmt == PVMF_MIME_ASF_MPEG4_AUDIO) ||
                       (aFmt == PVMF_MIME_AAC_SIZEHDR) ||
                       (aFmt == PVMF_MIME_AMR_IF2) ||
                       (aFmt == PVMF_MIME_AMR_IETF) ||
                       (aFmt == PVMF_MIME_AMR) ||
                       (aFmt == PVMF_MIME_AMRWB_IETF) ||
                       (aFmt == PVMF_MIME_AMRWB) ||
                       (aFmt == PVMF_MIME_MP3) ||
                       (aFmt == PVMF_MIME_WMA) ||
                       (aFmt == PVMF_MIME_REAL_AUDIO)
                      );
    }

    return isSupported;
}

////////////////////////////////////////////////////////////////////////////
OSCL_EXPORT_REF void PVMFOMXDecPort::FormatUpdated()
{
    PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_INFO
                    , (0, "%s::FormatUpdated %s", PortName(), iFormat.getMIMEStrPtr()));
}

OSCL_EXPORT_REF bool
PVMFOMXDecPort::releaseParametersSync(PvmiKvp*& aParameters, int& aNumParamElements)
{
    if ((iConnectedPort) && (iTag == PVMF_OMX_DEC_NODE_PORT_TYPE_OUTPUT))
    {
        OsclAny* temp = NULL;
        iConnectedPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);

        PvmiCapabilityAndConfig *config = (PvmiCapabilityAndConfig*)temp;

        if (config != NULL)
        {
            config->releaseParameters(NULL, aParameters, aNumParamElements);

        }
        return true;
    }
    return false;
}

OSCL_EXPORT_REF bool
PVMFOMXDecPort::pvmiGetBufferAllocatorSpecificInfoSync(PvmiKeyType aIdentifier, PvmiKvp*& aParameters, int& aNumParamElements)
{
    if ((iConnectedPort) && (iTag == PVMF_OMX_DEC_NODE_PORT_TYPE_OUTPUT))
    {
        OsclAny* temp = NULL;
        iConnectedPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);

        PvmiCapabilityAndConfig *config = (PvmiCapabilityAndConfig*)temp;

        if (config != NULL)
        {
            PVMFStatus status =
                config->getParametersSync(NULL, (PvmiKeyType)aIdentifier, aParameters, aNumParamElements, NULL);

            if (PVMFSuccess == status)
            {
                // config->releaseParameters(NULL, aParameters, aNumParamElements);
                return true;
            }
            else
                return false;
        }

    }
    return false;
}

OSCL_EXPORT_REF bool
PVMFOMXDecPort::pvmiSetPortFormatSpecificInfoSync(OsclRefCounterMemFrag& aMemFrag, PvmiKeyType KvpKey)
{
    if ((iConnectedPort) &&
            (iTag == PVMF_OMX_DEC_NODE_PORT_TYPE_OUTPUT))
    {
        OsclAny* temp = NULL;
        iConnectedPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);

        PvmiCapabilityAndConfig *config = (PvmiCapabilityAndConfig*)temp;

        /*
         * Create PvmiKvp for capability settings
         */
        if ((config) && (aMemFrag.getMemFragSize() > 0))
        {
            PvmiKvp kvp;

            kvp.length = oscl_strlen(KvpKey) + 1; // +1 for \0
            kvp.key = KvpKey;
            if (kvp.key == NULL)
            {
                return false;
            }

            kvp.value.key_specific_value = (OsclAny*)(aMemFrag.getMemFragPtr());
            kvp.capacity = aMemFrag.getMemFragSize();
            PvmiKvp* retKvp = NULL; // for return value
            int32 err;
            OSCL_TRY(err, config->setParametersSync(NULL, &kvp, 1, retKvp););
            OSCL_FIRST_CATCH_ANY(err, OSCL_LEAVE(-1));
        }
        return true;
    }
    return false;
}

OSCL_EXPORT_REF PVMFStatus PVMFOMXDecPort::Connect(PVMFPortInterface* aPort)
{
    if (!aPort)
    {
        return PVMFErrArgument;
    }

    if (iConnectedPort)
    {
        return PVMFFailure;
    }

    OsclAny* temp;
    aPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);
    PvmiCapabilityAndConfig *config = (PvmiCapabilityAndConfig*)temp;

    if (config != NULL)
    {
        PvmiKvp* kvp;
        int numKvp = 0;
        PVMFStatus status =
            config->getParametersSync(NULL, (PvmiKeyType)PVMF_FORMAT_SPECIFIC_INFO_KEY, kvp, numKvp, NULL);
        if (status == PVMFSuccess)
        {
            if (iTrackConfig != NULL)
            {
                OSCL_FREE(iTrackConfig);
                iTrackConfigSize = 0;
            }
            if (kvp)
            {
                iTrackConfigSize = kvp->capacity;
                iTrackConfig = (uint8*)(OSCL_MALLOC(sizeof(uint8) * iTrackConfigSize));
                oscl_memcpy(iTrackConfig, kvp->value.key_specific_value, iTrackConfigSize);
            }
            config->releaseParameters(NULL, kvp, numKvp);
        }
        else
        {
            PVLOGGER_LOGMSG(PVLOGMSG_INST_MLDBG, iLogger, PVLOGMSG_ERR, (0,
                            "PVMFOMXDecPort::Connect: Error calling getParametersSync for PVMF_FORMAT_SPECIFIC_INFO_KEY"));
        }

    }

    // Automatically connect the peer.

    if (aPort->PeerConnect(this) != PVMFSuccess)
    {
        return PVMFFailure;
    }

    iConnectedPort = aPort;
    PortActivity(PVMF_PORT_ACTIVITY_CONNECT);
    return PVMFSuccess;
}

// needed for WMV port exchange
OSCL_EXPORT_REF void PVMFOMXDecPort::setParametersSync(PvmiMIOSession aSession,
        PvmiKvp* aParameters,
        int num_elements,
        PvmiKvp * & aRet_kvp)
{

    // if port connect needs format specific info
    if (aParameters && pv_mime_strcmp(aParameters->key, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
    {
        if (iTrackConfig != NULL)
        {
            OSCL_FREE(iTrackConfig);
            iTrackConfigSize = 0;
        }
        iTrackConfigSize = aParameters->capacity;
        iTrackConfig = (uint8*)(OSCL_MALLOC(sizeof(uint8) * iTrackConfigSize));
        oscl_memcpy(iTrackConfig, aParameters->value.key_specific_value, iTrackConfigSize);
        return;
    }
    else if (aParameters && pv_mime_strcmp(aParameters->key, PVMF_DECODER_KEY_FRAME_ONLY_MODE_KEY) == 0)
    {
        iOMXNode->SetKeyFrameOnlyModeFlag(aParameters->value.bool_value);
        return;
    }
    else if (aParameters && pv_mime_strcmp(aParameters->key, PVMF_SKIP_N_UNTIL_KEY_FRAME_KEY) == 0)
    {
        iOMXNode->SetSkipNUntilKeyFrame(aParameters->value.uint32_value);
        return;
    }

    // if port connect needs display width
    if (aParameters && pv_mime_strcmp(aParameters->key, MOUT_VIDEO_DISPLAY_WIDTH_KEY) == 0)
    {
        iDspWidth = aParameters->value.int32_value;
        return;
    }

    // if port connect needs display height
    if (aParameters && pv_mime_strcmp(aParameters->key, MOUT_VIDEO_DISPLAY_HEIGHT_KEY) == 0)
    {
        iDspHeight = aParameters->value.int32_value;
        return;
    }

    // call the base class function
    PvmiCapabilityAndConfigPortFormatImpl::setParametersSync(aSession, aParameters, num_elements, aRet_kvp);

}




OSCL_EXPORT_REF PVMFStatus PVMFOMXDecPort::verifyParametersSync(PvmiMIOSession aSession, PvmiKvp* aParameters, int num_elements)
{

    OSCL_UNUSED_ARG(aSession);



    // in case of WMV or WMA call the Node verify to make sure that the parameters are OK
    // (the node will also make sure that the downstream node is OK with the change
    // if necessary)
    if (iFormat == PVMF_MIME_WMV || iFormat == PVMF_MIME_WMA)
    {
        if (iOMXNode->VerifyParametersSync(aSession, aParameters, num_elements))
        {
            return PVMFSuccess;
        }
        else
        {
            return PVMFErrNotSupported;
        }
    }
    else
    {
        return PVMFSuccess;
    }
}

OSCL_EXPORT_REF PVMFStatus PVMFOMXDecPort::verifyConnectedPortParametersSync(const char* aFormatValType,
        OsclAny* aConfig)
{
    PVMFStatus status = PVMFErrNotSupported;
    OsclAny* temp;
    if (iConnectedPort)
    {
        iConnectedPort->QueryInterface(PVMI_CAPABILITY_AND_CONFIG_PVUUID, temp);
    }
    else
        return PVMFFailure;

    PvmiCapabilityAndConfig *capConfig = (PvmiCapabilityAndConfig*)temp;

    if (capConfig != NULL)
    {
        if (pv_mime_strcmp(aFormatValType, PVMF_FORMAT_SPECIFIC_INFO_KEY) == 0)
        {
            OsclRefCounterMemFrag* aFormatValue = (OsclRefCounterMemFrag*)aConfig;
            if (aFormatValue->getMemFragSize() > 0)
            {
                OsclMemAllocator alloc;
                PvmiKvp kvp;
                kvp.key = NULL;
                kvp.length = oscl_strlen(aFormatValType) + 1; // +1 for \0
                kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
                if (kvp.key == NULL)
                {
                    return PVMFErrNoMemory;
                }
                oscl_strncpy(kvp.key, aFormatValType, kvp.length);

                kvp.value.key_specific_value = (OsclAny*)(aFormatValue->getMemFragPtr());
                kvp.capacity = aFormatValue->getMemFragSize();
                int32 err;
                OSCL_TRY(err, status = capConfig->verifyParametersSync(NULL, &kvp, 1););
                /* ignore the error for now */
                alloc.deallocate((OsclAny*)(kvp.key));

                return status;
            }
        }
        else if (pv_mime_strcmp(aFormatValType, PVMF_BITRATE_VALUE_KEY) == 0 ||
                 pv_mime_strcmp(aFormatValType, PVMF_FRAMERATE_VALUE_KEY) == 0)
        {
            if (aConfig != NULL)
            {
                OsclMemAllocator alloc;
                PvmiKvp kvp;
                kvp.key = NULL;
                kvp.length = oscl_strlen(aFormatValType) + 1; // +1 for \0
                kvp.key = (PvmiKeyType)alloc.ALLOCATE(kvp.length);
                if (kvp.key == NULL)
                {
                    return PVMFErrNoMemory;
                }
                oscl_strncpy(kvp.key, aFormatValType, kvp.length);
                uint32* bitrate = (uint32*)aConfig;
                kvp.value.uint32_value = *bitrate;

                int32 err;
                OSCL_TRY(err, status = capConfig->verifyParametersSync(NULL, &kvp, 1););
                /* ignore the error for now */
                alloc.deallocate((OsclAny*)(kvp.key));

                return status;
            }
        }
        return PVMFErrArgument;
    }
    return PVMFFailure;
}




