blob: 345c432b4bd4470de4732afe8bd33249de758693 [file] [log] [blame]
/* ------------------------------------------------------------------
* Copyright (C) 2008 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 "oscl_types.h"
#include "avc_dec.h"
#include "avcdec_int.h"
#include "pv_omxcore.h"
/*************************************/
/* functions needed for video engine */
/*************************************/
// init static members of AvcDecoder class
OMX_TICKS AvcDecoder_OMX::CurrInputTimestamp = 0;
uint8* AvcDecoder_OMX::pDpbBuffer = NULL;
uint32 AvcDecoder_OMX::FrameSize = 0;
OMX_U32 AvcDecoder_OMX::iAvcDecoderCounterInstance = 0;
OMX_TICKS AvcDecoder_OMX::DisplayTimestampArray[] = {0};
AVCHandle AvcDecoder_OMX::AvcHandle;
AVCDecSPSInfo AvcDecoder_OMX::SeqInfo;
/* These two functions are for callback functions of AvcHandle */
int32 CBAVC_Malloc_OMX(void* aUserData, int32 aSize, int32 aAttribute)
{
OSCL_UNUSED_ARG(aUserData);
OSCL_UNUSED_ARG(aAttribute);
void* pPtr;
pPtr = oscl_malloc(aSize);
return (int32) pPtr;
}
void CBAVC_Free_OMX(void* aUserData, int32 aMem)
{
OSCL_UNUSED_ARG(aUserData);
oscl_free((uint8*) aMem);
}
AVCDec_Status CBAVCDec_GetData_OMX(void* aUserData, uint8** aBuffer, uint* aSize)
{
OSCL_UNUSED_ARG(aUserData);
OSCL_UNUSED_ARG(aBuffer);
OSCL_UNUSED_ARG(aSize);
return AVCDEC_FAIL; /* nothing for now */
}
int32 AvcDecoder_OMX::AllocateBuffer_OMX(void* aUserData, int32 i, uint8** aYuvBuffer)
{
OSCL_UNUSED_ARG(aUserData);
//printf("Index %d\n", i);
*aYuvBuffer = pDpbBuffer + i * FrameSize;
//Store the input timestamp at the correct index
DisplayTimestampArray[i] = CurrInputTimestamp;
return 1;
}
void UnbindBuffer_OMX(void* aUserData, int32 i)
{
OSCL_UNUSED_ARG(aUserData);
OSCL_UNUSED_ARG(i);
return;
}
int32 AvcDecoder_OMX::ActivateSPS_OMX(void* aUserData, uint aSizeInMbs, uint aNumBuffers)
{
OSCL_UNUSED_ARG(aUserData);
PVAVCDecGetSeqInfo(&(AvcHandle), &(SeqInfo));
if (pDpbBuffer)
{
oscl_free(pDpbBuffer);
pDpbBuffer = NULL;
}
FrameSize = (aSizeInMbs << 7) * 3;
pDpbBuffer = (uint8*) oscl_malloc(aNumBuffers * (FrameSize));
return 1;
}
/* initialize video decoder */
OMX_BOOL AvcDecoder_OMX::InitializeVideoDecode_OMX()
{
/* Initialize AvcHandle */
AvcHandle.AVCObject = NULL;
AvcHandle.userData = NULL;
AvcHandle.CBAVC_DPBAlloc = ActivateSPS_OMX;
AvcHandle.CBAVC_FrameBind = AllocateBuffer_OMX;
AvcHandle.CBAVC_FrameUnbind = UnbindBuffer_OMX;
AvcHandle.CBAVC_Malloc = CBAVC_Malloc_OMX;
AvcHandle.CBAVC_Free = CBAVC_Free_OMX;
return OMX_TRUE;
}
OMX_BOOL AvcDecoder_OMX::FlushOutput_OMX(OMX_U8* aOutBuffer, OMX_U32* aOutputLength, OMX_TICKS* aOutTimestamp, OMX_S32 OldWidth, OMX_S32 OldHeight)
{
AVCFrameIO Output;
AVCDec_Status Status;
int32 Index, Release, FrameSize;
OMX_S32 OldFrameSize = ((OldWidth + 15) & (~15)) * ((OldHeight + 15) & (~15));
Output.YCbCr[0] = Output.YCbCr[1] = Output.YCbCr[2] = NULL;
Status = PVAVCDecGetOutput(&(AvcHandle), &Index, &Release, &Output);
if (Status == AVCDEC_FAIL)
{
return OMX_FALSE;
}
*aOutTimestamp = DisplayTimestampArray[Index];
*aOutputLength = 0; // init to 0
if (Output.YCbCr[0])
{
FrameSize = Output.pitch * Output.height;
// it should not happen that the frame size is smaller than available buffer size, but check just in case
if (FrameSize <= OldFrameSize)
{
*aOutputLength = (Output.pitch * Output.height * 3) >> 1;
oscl_memcpy(aOutBuffer, Output.YCbCr[0], FrameSize);
oscl_memcpy(aOutBuffer + FrameSize, Output.YCbCr[1], FrameSize >> 2);
oscl_memcpy(aOutBuffer + FrameSize + FrameSize / 4, Output.YCbCr[2], FrameSize >> 2);
}
// else, the frame length is reported as zero, and there is no copying
}
return OMX_TRUE;
}
/* Initialization routine */
OMX_ERRORTYPE AvcDecoder_OMX::AvcDecInit_OMX()
{
if (OMX_FALSE == InitializeVideoDecode_OMX())
{
return OMX_ErrorInsufficientResources;
}
//Set up the cleanup object in order to do clean up work automatically
pCleanObject = OSCL_NEW(AVCCleanupObject_OMX, (&AvcHandle));
DecodeSliceFlag = OMX_FALSE;
pNalBufferTemp = NULL;
return OMX_ErrorNone;
}
/*Decode routine */
OMX_BOOL AvcDecoder_OMX::AvcDecodeVideo_OMX(OMX_U8* aOutBuffer, OMX_U32* aOutputLength,
OMX_U8** aInputBuf, OMX_U32* aInBufSize,
OMX_PARAM_PORTDEFINITIONTYPE* aPortParam,
OMX_S32* iFrameCount, OMX_BOOL aMarkerFlag, OMX_TICKS* aOutTimestamp, OMX_BOOL *aResizeFlag)
{
AVCDec_Status Status;
OMX_S32 Width, Height;
OMX_S32 crop_top, crop_bottom, crop_right, crop_left;
uint8* pNalBuffer;
int32 NalSize, NalType, NalRefId, PicType;
AVCDecObject* pDecVid;
static int32 FrameNo;
*aResizeFlag = OMX_FALSE;
OMX_U32 OldWidth, OldHeight;
OldWidth = aPortParam->format.video.nFrameWidth;
OldHeight = aPortParam->format.video.nFrameHeight;
if (OMX_TRUE == DecodeSliceFlag)
{
Status = (AVCDec_Status) FlushOutput_OMX(aOutBuffer, aOutputLength, aOutTimestamp, OldWidth, OldHeight);
if ((Status = PVAVCDecodeSlice(&(AvcHandle), pNalBufferTemp, NalSizeTemp)) == AVCDEC_PICTURE_OUTPUT_READY)
{
DecodeSliceFlag = OMX_TRUE;
}
else
{
if (pNalBufferTemp)
oscl_free(pNalBufferTemp);
pNalBufferTemp = NULL;
DecodeSliceFlag = OMX_FALSE;
}
return OMX_TRUE;
}
if (!aMarkerFlag)
{
if (AVCDEC_FAIL == GetNextFullNAL_OMX(&pNalBuffer, &NalSize, *aInputBuf, aInBufSize))
{
Status = (AVCDec_Status) FlushOutput_OMX(aOutBuffer, aOutputLength, aOutTimestamp, OldWidth, OldHeight);
if (AVCDEC_FAIL != Status)
{
return OMX_TRUE;
}
else
{
return OMX_FALSE;
}
}
}
else
{
pNalBuffer = *aInputBuf;
NalSize = *aInBufSize;
//Assuming that the buffer with marker bit contains one full NAL
*aInBufSize = 0;
}
if (AVCDEC_FAIL == PVAVCDecGetNALType(pNalBuffer, NalSize, &NalType, &NalRefId))
{
return OMX_FALSE;
}
if (AVC_NALTYPE_SPS == (AVCNalUnitType)NalType)
{
if (PVAVCDecSeqParamSet(&(AvcHandle), pNalBuffer, NalSize) != AVCDEC_SUCCESS)
{
return OMX_FALSE;
}
pDecVid = (AVCDecObject*) AvcHandle.AVCObject;
Width = (pDecVid->seqParams[0]->pic_width_in_mbs_minus1 + 1) * 16;
Height = (pDecVid->seqParams[0]->pic_height_in_map_units_minus1 + 1) * 16;
if (pDecVid->seqParams[0]->frame_cropping_flag)
{
crop_left = 2 * pDecVid->seqParams[0]->frame_crop_left_offset;
crop_right = Width - (2 * pDecVid->seqParams[0]->frame_crop_right_offset + 1);
if (pDecVid->seqParams[0]->frame_mbs_only_flag)
{
crop_top = 2 * pDecVid->seqParams[0]->frame_crop_top_offset;
crop_bottom = Height - (2 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
}
else
{
crop_top = 4 * pDecVid->seqParams[0]->frame_crop_top_offset;
crop_bottom = Height - (4 * pDecVid->seqParams[0]->frame_crop_bottom_offset + 1);
}
}
else /* no cropping flag, just give the first and last pixel */
{
crop_bottom = Height - 1;
crop_right = Width - 1;
crop_top = crop_left = 0;
}
aPortParam->format.video.nFrameWidth = crop_right - crop_left + 1;
aPortParam->format.video.nFrameHeight = crop_bottom - crop_top + 1;
//if( (OldWidth != aPortParam->format.video.nFrameWidth) || (OldHeight != aPortParam->format.video.nFrameHeight))
// FORCE RESIZE ALWAYS FOR SPS
*aResizeFlag = OMX_TRUE;
*iFrameCount = 1;
}
else if (AVC_NALTYPE_PPS == (AVCNalUnitType) NalType)
{
if (PVAVCDecPicParamSet(&(AvcHandle), pNalBuffer, NalSize) != AVCDEC_SUCCESS)
{
return OMX_FALSE;
}
}
else if (AVC_NALTYPE_SLICE == (AVCNalUnitType) NalType ||
AVC_NALTYPE_IDR == (AVCNalUnitType) NalType)
{
if ((Status = PVAVCDecodeSlice(&(AvcHandle), pNalBuffer, NalSize)) == AVCDEC_PICTURE_OUTPUT_READY)
{
Status = (AVCDec_Status) FlushOutput_OMX(aOutBuffer, aOutputLength, aOutTimestamp, OldWidth, OldHeight);
if ((Status = PVAVCDecodeSlice(&(AvcHandle), pNalBuffer, NalSize)) == AVCDEC_PICTURE_OUTPUT_READY)
{
pNalBufferTemp = (uint8*) oscl_malloc(NalSize);
oscl_memcpy(pNalBufferTemp, pNalBuffer, NalSize);
NalSizeTemp = NalSize;
DecodeSliceFlag = OMX_TRUE;
}
}
if (Status == AVCDEC_PICTURE_READY)
{
FrameNo++;
//printf("decode frame %d \n", FrameNo);
}
}
else if ((AVCNalUnitType)NalType == AVC_NALTYPE_SEI)
{
if (PVAVCDecSEI(&(AvcHandle), pNalBuffer, NalSize) != AVCDEC_SUCCESS)
{
return OMX_FALSE;
}
}
else if ((AVCNalUnitType)NalType == AVC_NALTYPE_AUD)
{
PicType = pNalBuffer[1] >> 5;
}
else if ((AVCNalUnitType)NalType == AVC_NALTYPE_EOSTREAM) // end of stream
{
return OMX_TRUE;
}
else
{
printf("\nNAL_type = %d, unsupported nal type or not sure what to do for this type\n", NalType);
}
return OMX_TRUE;
}
OMX_ERRORTYPE AvcDecoder_OMX::AvcDecDeinit_OMX()
{
if (pCleanObject)
{
OSCL_DELETE(pCleanObject);
pCleanObject = NULL;
}
if (pDpbBuffer)
{
oscl_free(pDpbBuffer);
pDpbBuffer = NULL;
}
return OMX_ErrorNone;
}
AVCDec_Status AvcDecoder_OMX::GetNextFullNAL_OMX(uint8** aNalBuffer, int32* aNalSize, OMX_U8* aInputBuf, OMX_U32* aInBufSize)
{
uint32 BuffConsumed;
uint8* pBuff = aInputBuf;
OMX_U32 InputSize;
*aNalSize = *aInBufSize;
InputSize = *aInBufSize;
AVCDec_Status ret_val = PVAVCAnnexBGetNALUnit(pBuff, aNalBuffer, aNalSize);
if (ret_val == AVCDEC_FAIL)
{
return AVCDEC_FAIL;
}
BuffConsumed = ((*aNalSize) + (int32)(*aNalBuffer - pBuff));
aInputBuf += BuffConsumed;
*aInBufSize = InputSize - BuffConsumed;
return AVCDEC_SUCCESS;
}
AVCCleanupObject_OMX::~AVCCleanupObject_OMX()
{
PVAVCCleanUpDecoder(ipavcHandle);
}