| /* ------------------------------------------------------------------ |
| * Copyright (C) 1998-2009 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 "avc_enc.h" |
| |
| |
| #define MAX_SUPPORTED_LAYER 1 |
| |
| /* global static functions */ |
| |
| void CbAvcEncDebugLog_OMX(uint32 *userData, AVCLogType type, char *string1, int val1, int val2) |
| { |
| OSCL_UNUSED_ARG(userData); |
| OSCL_UNUSED_ARG(type); |
| OSCL_UNUSED_ARG(string1); |
| OSCL_UNUSED_ARG(val1); |
| OSCL_UNUSED_ARG(val2); |
| |
| return; |
| } |
| |
| int CbAvcEncMalloc_OMX(void *userData, int32 size, int attribute) |
| { |
| OSCL_UNUSED_ARG(userData); |
| OSCL_UNUSED_ARG(attribute); |
| |
| uint8 *mem; |
| |
| mem = (uint8*) oscl_malloc(size); |
| |
| return (int)mem; |
| } |
| |
| void CbAvcEncFree_OMX(void *userData, int mem) |
| { |
| OSCL_UNUSED_ARG(userData); |
| |
| oscl_free((void*)mem); |
| |
| return; |
| } |
| |
| |
| int CbAvcEncDPBAlloc_OMX(void *userData, uint frame_size_in_mbs, uint num_buffers) |
| { |
| AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData; |
| |
| return pAvcEnc->AVC_DPBAlloc(frame_size_in_mbs, num_buffers); |
| } |
| |
| |
| void CbAvcEncFrameUnbind_OMX(void *userData, int indx) |
| { |
| AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData; |
| |
| pAvcEnc->AVC_FrameUnbind(indx); |
| |
| return; |
| } |
| |
| int CbAvcEncFrameBind_OMX(void *userData, int indx, uint8 **yuv) |
| { |
| AvcEncoder_OMX* pAvcEnc = (AvcEncoder_OMX*) userData; |
| |
| return pAvcEnc->AVC_FrameBind(indx, yuv); |
| } |
| |
| |
| |
| //Class constructor function |
| AvcEncoder_OMX::AvcEncoder_OMX() |
| { |
| iInitialized = OMX_FALSE; |
| iSpsPpsHeaderFlag = OMX_FALSE; |
| iReadyForNextFrame = OMX_TRUE; |
| |
| oscl_memset((void *)&iAvcHandle, 0, sizeof(AVCHandle)); |
| |
| iAvcHandle.CBAVC_DPBAlloc = &CbAvcEncDPBAlloc_OMX; |
| iAvcHandle.CBAVC_FrameBind = &CbAvcEncFrameBind_OMX; |
| iAvcHandle.CBAVC_FrameUnbind = &CbAvcEncFrameUnbind_OMX; |
| iAvcHandle.CBAVC_Free = &CbAvcEncFree_OMX; |
| iAvcHandle.CBAVC_Malloc = &CbAvcEncMalloc_OMX; |
| iAvcHandle.CBAVC_DebugLog = &CbAvcEncDebugLog_OMX; |
| iAvcHandle.userData = this; |
| |
| ccRGBtoYUV = NULL; |
| iYUVIn = NULL; |
| iFramePtr = NULL; |
| iDPB = NULL; |
| iFrameUsed = NULL; |
| } |
| |
| |
| //Class destructor function |
| AvcEncoder_OMX::~AvcEncoder_OMX() |
| { |
| //In case encoder cleanup has not been done yet, do it here |
| AvcEncDeinit(); |
| } |
| |
| |
| /* Encoder Initialization routine */ |
| OMX_ERRORTYPE AvcEncoder_OMX::AvcEncInit(OMX_VIDEO_PORTDEFINITIONTYPE aInputParam, |
| OMX_CONFIG_ROTATIONTYPE aInputOrientationType, |
| OMX_VIDEO_PORTDEFINITIONTYPE aEncodeParam, |
| OMX_VIDEO_PARAM_AVCTYPE aEncodeAvcParam, |
| OMX_VIDEO_PARAM_BITRATETYPE aRateControlType, |
| OMX_VIDEO_PARAM_QUANTIZATIONTYPE aQuantType, |
| OMX_VIDEO_PARAM_MOTIONVECTORTYPE aSearchRange, |
| OMX_VIDEO_PARAM_INTRAREFRESHTYPE aIntraRefresh, |
| OMX_VIDEO_PARAM_VBSMCTYPE aVbsmcType) |
| { |
| |
| AVCEncParams aEncOption; /* encoding options */ |
| |
| uint32* slice_group = NULL; |
| int32 numTotalMBs; |
| int32 Ysize16; |
| int32 total_mb; |
| int32 run_length; |
| |
| iSrcWidth = aInputParam.nFrameWidth; |
| iSrcHeight = aInputParam.nFrameHeight; |
| iFrameOrientation = aInputOrientationType.nRotation; |
| |
| |
| if ((OMX_COLOR_FormatYUV420Planar == aInputParam.eColorFormat) || |
| (OMX_COLOR_Format24bitRGB888 == aInputParam.eColorFormat) || |
| (OMX_COLOR_Format12bitRGB444 == aInputParam.eColorFormat) || |
| (OMX_COLOR_FormatYUV420SemiPlanar == aInputParam.eColorFormat)) |
| { |
| iVideoFormat = aInputParam.eColorFormat; |
| } |
| else |
| { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| |
| if (OMX_TRUE == iInitialized) |
| { |
| /* clean up before re-initialized */ |
| PVAVCCleanUpEncoder(&iAvcHandle); |
| iAvcHandle.AVCObject = NULL; |
| |
| if (iYUVIn) |
| { |
| oscl_free(iYUVIn); |
| iYUVIn = NULL; |
| } |
| |
| } |
| |
| //Verify the input compression format |
| if (OMX_VIDEO_CodingUnused != aInputParam.eCompressionFormat) |
| { |
| //Input port must have no compression supported |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| // allocate iYUVIn |
| if ((iSrcWidth & 0xF) || (iSrcHeight & 0xF) || (OMX_COLOR_FormatYUV420Planar != iVideoFormat)) /* Not multiple of 16 */ |
| { |
| iYUVIn = (uint8*) oscl_malloc(((((iSrcWidth + 15) >> 4) * ((iSrcHeight + 15) >> 4)) * 3) << 7); |
| if (NULL == iYUVIn) |
| { |
| return OMX_ErrorInsufficientResources; |
| } |
| } |
| |
| /* Initialize the color conversion */ |
| if (OMX_COLOR_Format24bitRGB888 == iVideoFormat) |
| { |
| ccRGBtoYUV = CCRGB24toYUV420::New(); |
| ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth, iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0)); |
| } |
| if (OMX_COLOR_Format12bitRGB444 == iVideoFormat) |
| { |
| ccRGBtoYUV = CCRGB12toYUV420::New(); |
| ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth, iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0)); |
| } |
| if (OMX_COLOR_FormatYUV420SemiPlanar == iVideoFormat) |
| { |
| ccRGBtoYUV = CCYUV420SEMItoYUV420::New(); |
| ccRGBtoYUV->Init(iSrcWidth, iSrcHeight, iSrcWidth, iSrcWidth , iSrcHeight, ((iSrcWidth + 15) >> 4) << 4, (iFrameOrientation == 180 ? CCBOTTOM_UP : 0)); |
| } |
| |
| aEncOption.width = aEncodeParam.nFrameWidth; |
| aEncOption.height = aEncodeParam.nFrameHeight; |
| aEncOption.frame_rate = (uint32)(1000 * ((aEncodeParam.xFramerate >> 16) + (OsclFloat)(aEncodeParam.xFramerate & 0xFFFF) / (1 << 16))); |
| |
| switch (aRateControlType.eControlRate) |
| { |
| case OMX_Video_ControlRateDisable: |
| { |
| aEncOption.rate_control = AVC_OFF; |
| aEncOption.bitrate = 48000; // default |
| } |
| break; |
| |
| //Both the below cases have same body |
| case OMX_Video_ControlRateConstant: |
| case OMX_Video_ControlRateVariable: |
| { |
| aEncOption.rate_control = AVC_ON; |
| aEncOption.bitrate = aEncodeParam.nBitrate; |
| } |
| break; |
| |
| default: |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| //Set the profile of encoder |
| switch (aEncodeAvcParam.eProfile) |
| { |
| case OMX_VIDEO_AVCProfileBaseline: |
| { |
| aEncOption.profile = AVC_BASELINE; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileMain: |
| { |
| aEncOption.profile = AVC_MAIN; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileExtended: |
| { |
| aEncOption.profile = AVC_EXTENDED; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileHigh: |
| { |
| aEncOption.profile = AVC_HIGH; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileHigh10: |
| { |
| aEncOption.profile = AVC_HIGH10; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileHigh422: |
| { |
| aEncOption.profile = AVC_HIGH422; |
| } |
| break; |
| |
| case OMX_VIDEO_AVCProfileHigh444: |
| { |
| aEncOption.profile = AVC_HIGH444; |
| } |
| break; |
| |
| default: |
| { |
| aEncOption.profile = AVC_BASELINE; |
| } |
| break; |
| } |
| |
| |
| //Set the level of avc encoder |
| switch (aEncodeAvcParam.eLevel) |
| { |
| case OMX_VIDEO_AVCLevel1: |
| { |
| aEncOption.level = AVC_LEVEL1; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel1b: |
| { |
| aEncOption.level = AVC_LEVEL1_B; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel11: |
| { |
| aEncOption.level = AVC_LEVEL1_1; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel12: |
| { |
| aEncOption.level = AVC_LEVEL1_2; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel13: |
| { |
| aEncOption.level = AVC_LEVEL1_3; |
| } |
| break; |
| |
| |
| case OMX_VIDEO_AVCLevel2: |
| { |
| aEncOption.level = AVC_LEVEL2; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel21: |
| { |
| aEncOption.level = AVC_LEVEL2_1; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel22: |
| { |
| aEncOption.level = AVC_LEVEL2_2; |
| } |
| break; |
| |
| |
| case OMX_VIDEO_AVCLevel3: |
| { |
| aEncOption.level = AVC_LEVEL3; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel31: |
| { |
| aEncOption.level = AVC_LEVEL3_1; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel32: |
| { |
| aEncOption.level = AVC_LEVEL3_2; |
| } |
| break; |
| |
| |
| case OMX_VIDEO_AVCLevel4: |
| { |
| aEncOption.level = AVC_LEVEL4; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel41: |
| { |
| aEncOption.level = AVC_LEVEL4_1; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel42: |
| { |
| aEncOption.level = AVC_LEVEL4_2; |
| } |
| break; |
| |
| |
| case OMX_VIDEO_AVCLevel5: |
| { |
| aEncOption.level = AVC_LEVEL5; |
| } |
| break; |
| case OMX_VIDEO_AVCLevel51: |
| { |
| aEncOption.level = AVC_LEVEL5_1; |
| } |
| break; |
| |
| default: |
| { |
| aEncOption.level = AVC_LEVEL5_1; |
| } |
| break; |
| } |
| |
| aEncOption.initQP = aQuantType.nQpP; |
| |
| aEncOption.init_CBP_removal_delay = 1600; //Default value |
| aEncOption.CPB_size = ((uint32)(2 * aEncOption.bitrate)); |
| if (OMX_VIDEO_PictureTypeI == aEncodeAvcParam.nAllowedPictureTypes) // I-only |
| { |
| aEncOption.idr_period = 1; |
| } |
| else |
| { |
| //IPPPPPPPPPP, no I frame for a long period of time |
| if (0xFFFFFFFF == aEncodeAvcParam.nPFrames) |
| { |
| aEncOption.idr_period = -1; |
| } |
| else |
| { |
| aEncOption.idr_period = aEncodeAvcParam.nPFrames + 1; |
| } |
| } |
| |
| if ((OMX_VIDEO_IntraRefreshCyclic == aIntraRefresh.eRefreshMode) || |
| (OMX_VIDEO_IntraRefreshBoth == aIntraRefresh.eRefreshMode)) |
| { |
| aEncOption.intramb_refresh = aIntraRefresh.nCirMBs; |
| } |
| |
| |
| if ((OMX_VIDEO_IntraRefreshAdaptive == aIntraRefresh.eRefreshMode) || |
| (OMX_VIDEO_IntraRefreshBoth == aIntraRefresh.eRefreshMode)) |
| { |
| aEncOption.auto_scd = AVC_ON; |
| } |
| else |
| { |
| aEncOption.auto_scd = AVC_OFF; |
| } |
| |
| aEncOption.out_of_band_param_set = AVC_ON; //Default value |
| |
| /* default values */ |
| aEncOption.poc_type = 2; |
| aEncOption.log2_max_poc_lsb_minus_4 = 12; |
| aEncOption.delta_poc_zero_flag = 0; |
| aEncOption.offset_poc_non_ref = 0; |
| aEncOption.offset_top_bottom = 0; |
| aEncOption.num_ref_in_cycle = 0; |
| aEncOption.offset_poc_ref = NULL; |
| |
| aEncOption.num_ref_frame = 1; //We only support this value |
| |
| //Since FMO is disabled in our case, num of slice group is always 1 |
| #if (defined(TEST_FULL_AVC_FRAME_MODE) || defined(TEST_FULL_AVC_FRAME_MODE_SC)) |
| aEncOption.num_slice_group = 4; |
| aEncOption.fmo_type = 6; |
| aEncOption.use_overrun_buffer = AVC_OFF; // since we are to be outputing full frame buffers |
| #else |
| aEncOption.num_slice_group = 1; |
| aEncOption.fmo_type = 0; //Disabled in this case |
| aEncOption.use_overrun_buffer = AVC_ON; |
| #endif |
| |
| aEncOption.db_filter = AVC_ON; |
| |
| if (OMX_VIDEO_AVCLoopFilterEnable == aEncodeAvcParam.eLoopFilterMode) |
| { |
| aEncOption.disable_db_idc = 0; |
| } |
| else if (OMX_VIDEO_AVCLoopFilterDisable == aEncodeAvcParam.eLoopFilterMode) |
| { |
| aEncOption.disable_db_idc = 1; |
| } |
| else if (OMX_VIDEO_AVCLoopFilterDisableSliceBoundary == aEncodeAvcParam.eLoopFilterMode) |
| { |
| aEncOption.disable_db_idc = 2; |
| } |
| else |
| { |
| return OMX_ErrorUnsupportedSetting; |
| } |
| |
| aEncOption.alpha_offset = 0; |
| aEncOption.beta_offset = 0; |
| aEncOption.constrained_intra_pred = (OMX_TRUE == aEncodeAvcParam.bconstIpred) ? AVC_ON : AVC_OFF; |
| |
| aEncOption.data_par = AVC_OFF; |
| aEncOption.fullsearch = AVC_OFF; |
| aEncOption.search_range = (aSearchRange.sXSearchRange <= aSearchRange.sYSearchRange ? aSearchRange.sXSearchRange : aSearchRange.sYSearchRange); |
| |
| if (OMX_Video_MotionVectorPixel == aSearchRange.eAccuracy) |
| { |
| aEncOption.sub_pel = AVC_OFF; |
| } |
| else |
| { |
| aEncOption.sub_pel = AVC_ON; |
| } |
| |
| if (aVbsmcType.b16x16 != OMX_TRUE) |
| { |
| return OMX_ErrorBadParameter; |
| } |
| |
| aEncOption.submb_pred = AVC_OFF; // default for now, ignore aVbsmcType.8x16, 16x8, 8x8, etc. |
| aEncOption.rdopt_mode = AVC_OFF; |
| aEncOption.bidir_pred = AVC_OFF; |
| |
| Ysize16 = (((aEncOption.width + 15) >> 4) << 4) * (((aEncOption.height + 15) >> 4) << 4); |
| numTotalMBs = Ysize16 >> 8; |
| slice_group = (uint*) oscl_malloc(sizeof(uint) * numTotalMBs); |
| |
| int32 idx = 0; |
| int32 ii; |
| for (ii = 0; ii < numTotalMBs; ii++) |
| { |
| slice_group[ii] = idx++; |
| if (idx >= aEncOption.num_slice_group) |
| { |
| idx = 0; |
| } |
| } |
| aEncOption.slice_group = slice_group; |
| |
| if (aEncOption.num_slice_group > 1) /* divide slice group equally */ |
| { |
| run_length = numTotalMBs / aEncOption.num_slice_group; |
| total_mb = 0; |
| for (idx = 0; idx < aEncOption.num_slice_group; idx++) |
| { |
| aEncOption.run_length_minus1[idx] = run_length - 1; |
| total_mb += run_length; |
| } |
| if (total_mb < numTotalMBs) |
| { |
| aEncOption.run_length_minus1[aEncOption.num_slice_group-1] += (numTotalMBs - total_mb); |
| } |
| } |
| |
| /***** Initialize the encoder *****/ |
| if (AVCENC_SUCCESS != PVAVCEncInitialize(&iAvcHandle, &aEncOption, NULL, NULL)) |
| { |
| iInitialized = OMX_FALSE; |
| return OMX_ErrorBadParameter; |
| } |
| |
| iIDR = OMX_TRUE; |
| iDispOrd = 0; |
| iInitialized = OMX_TRUE; |
| iReadyForNextFrame = OMX_TRUE; |
| |
| oscl_free(slice_group); |
| |
| return OMX_ErrorNone; |
| |
| } |
| |
| |
| AVCEnc_Status AvcEncoder_OMX::AvcEncodeSendInput(OMX_U8* aInBuffer, |
| OMX_U32* aInBufSize, |
| OMX_TICKS aInTimeStamp) |
| { |
| AVCEnc_Status AvcStatus; |
| |
| if (OMX_COLOR_FormatYUV420Planar == iVideoFormat) |
| { |
| /* Input Buffer Size Check |
| * Input buffer size should be equal to one frame, otherwise drop the frame |
| * as it is a corrupt data and don't encode it */ |
| if (*aInBufSize < (OMX_U32)((iSrcWidth * iSrcHeight * 3) >> 1)) |
| { |
| //Mark the inpur buffer consumed to indicate corrupt frame |
| *aInBufSize = 0; |
| return AVCENC_FAIL; |
| } |
| |
| if (iYUVIn) /* iSrcWidth is not multiple of 4 or iSrcHeight is odd number */ |
| { |
| CopyToYUVIn(aInBuffer, iSrcWidth, iSrcHeight, |
| ((iSrcWidth + 15) >> 4) << 4, ((iSrcHeight + 15) >> 4) << 4); |
| iVideoIn = iYUVIn; |
| } |
| else /* otherwise, we can just use aVidIn->iSource */ |
| { |
| iVideoIn = aInBuffer; |
| } |
| } |
| else if (OMX_COLOR_Format12bitRGB444 == iVideoFormat) |
| { |
| if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 2)) |
| { |
| //Mark the inpur buffer consumed to indicate corrupt frame |
| *aInBufSize = 0; |
| return AVCENC_FAIL; |
| } |
| ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn); |
| iVideoIn = iYUVIn; |
| } |
| else if (OMX_COLOR_Format24bitRGB888 == iVideoFormat) |
| { |
| if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 3)) |
| { |
| //Mark the inpur buffer consumed to indicate corrupt frame |
| *aInBufSize = 0; |
| return AVCENC_FAIL; |
| } |
| ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn); |
| iVideoIn = iYUVIn; |
| } |
| else if (OMX_COLOR_FormatYUV420SemiPlanar == iVideoFormat) |
| { |
| if (*aInBufSize < (OMX_U32)(iSrcWidth * iSrcHeight * 3) >> 1) |
| { |
| //Mark the inpur buffer consumed to indicate corrupt frame |
| *aInBufSize = 0; |
| return AVCENC_FAIL; |
| } |
| ccRGBtoYUV->Convert((uint8*)aInBuffer, iYUVIn); |
| iVideoIn = iYUVIn; |
| } |
| |
| /* assign with backward-P or B-Vop this timestamp must be re-ordered */ |
| // Encoder uses 32 bit timestamps internally - keep track of 64 bit value as well |
| iTimeStamp = Oscl_Int64_Utils::get_uint64_lower32(aInTimeStamp / 1000); //timestamp in millisec |
| iTimeStamp64 = aInTimeStamp; |
| |
| iVidIn.height = ((iSrcHeight + 15) >> 4) << 4; |
| iVidIn.pitch = ((iSrcWidth + 15) >> 4) << 4; |
| iVidIn.coding_timestamp = iTimeStamp; |
| iVidIn.YCbCr[0] = (uint8*)iVideoIn; |
| iVidIn.YCbCr[1] = (uint8*)(iVideoIn + iVidIn.height * iVidIn.pitch); |
| iVidIn.YCbCr[2] = iVidIn.YCbCr[1] + ((iVidIn.height * iVidIn.pitch) >> 2); |
| iVidIn.disp_order = iDispOrd; |
| |
| AvcStatus = PVAVCEncSetInput(&iAvcHandle, &iVidIn); |
| |
| if (AVCENC_SUCCESS == AvcStatus) |
| { |
| iDispOrd++; |
| iReadyForNextFrame = OMX_FALSE; |
| return AvcStatus; |
| } |
| else if (AVCENC_NEW_IDR == AvcStatus) |
| { |
| iDispOrd++; |
| iIDR = OMX_TRUE; |
| iReadyForNextFrame = OMX_FALSE; |
| return AvcStatus; |
| } |
| else |
| { |
| return AvcStatus; |
| } |
| |
| } |
| |
| |
| /*Encode routine */ |
| AVCEnc_Status AvcEncoder_OMX::AvcEncodeVideo(OMX_U8* aOutBuffer, |
| OMX_U32* aOutputLength, |
| OMX_BOOL* aBufferOverRun, |
| OMX_U8** aOverBufferPointer, |
| OMX_U8* aInBuffer, |
| OMX_U32* aInBufSize, |
| OMX_TICKS aInTimeStamp, |
| OMX_TICKS* aOutTimeStamp, |
| OMX_BOOL* aSyncFlag) |
| { |
| AVCEnc_Status AvcStatus; |
| AVCEnc_Status ReturnValue; |
| AVCEnc_Status SetInputStatus = AVCENC_SUCCESS; |
| AVCFrameIO recon; |
| uint Size; |
| int NALType = 0; |
| |
| if (OMX_FALSE == iSpsPpsHeaderFlag) |
| { |
| Size = *aOutputLength; |
| AvcStatus = PVAVCEncodeNAL(&iAvcHandle, aOutBuffer, &Size, &NALType); |
| |
| //No more SPS and PPS NAL's to come, mark the header flag as true |
| if (AVCENC_WRONG_STATE == AvcStatus) |
| { |
| *aOutputLength = 0; |
| iSpsPpsHeaderFlag = OMX_TRUE; |
| return AVCENC_WRONG_STATE; |
| } |
| |
| switch (NALType) |
| { |
| case AVC_NALTYPE_SPS: |
| case AVC_NALTYPE_PPS: |
| { |
| *aOutputLength = Size; |
| //Mark the SPS and PPS buffers with the first input bufer timestamp |
| *aOutTimeStamp = aInTimeStamp; |
| return AVCENC_SUCCESS; |
| } |
| |
| default: |
| { |
| //No more SPS and PPS NAL's to come, mark the header flag as true |
| *aOutputLength = 0; |
| iSpsPpsHeaderFlag = OMX_TRUE; |
| return AVCENC_FAIL; |
| } |
| } |
| } |
| |
| |
| if (OMX_TRUE == iReadyForNextFrame) |
| { |
| SetInputStatus = AvcEncodeSendInput(aInBuffer, aInBufSize, aInTimeStamp); |
| } |
| |
| if ((AVCENC_SUCCESS == SetInputStatus) || (AVCENC_NEW_IDR == SetInputStatus)) //success |
| { |
| Size = *aOutputLength; |
| |
| AvcStatus = PVAVCEncodeNAL(&iAvcHandle, (uint8*)aOutBuffer, &Size, &NALType); |
| |
| if (AVCENC_SUCCESS == AvcStatus) |
| { |
| *aSyncFlag = iIDR; |
| //Calling the overrun buffer api to see whether overrun buffer has been used or not |
| *aOverBufferPointer = PVAVCEncGetOverrunBuffer(&iAvcHandle); |
| |
| if (NULL != *aOverBufferPointer) |
| { |
| //Output data has been filled in the over run buffer, mark the flag as true |
| *aBufferOverRun = OMX_TRUE; |
| } |
| |
| ReturnValue = AVCENC_SUCCESS; |
| } |
| else if (AVCENC_PICTURE_READY == AvcStatus) |
| { |
| //Calling the overrun buffer api to see whether overrun buffer has been used or not |
| *aOverBufferPointer = PVAVCEncGetOverrunBuffer(&iAvcHandle); |
| |
| if (NULL != *aOverBufferPointer) |
| { |
| //Output data has been filled in the over run buffer, mark the flag as true |
| *aBufferOverRun = OMX_TRUE; |
| } |
| |
| *aSyncFlag = iIDR; |
| if (iIDR == OMX_TRUE) |
| { |
| iIDR = OMX_FALSE; |
| } |
| |
| ReturnValue = AVCENC_PICTURE_READY; |
| |
| iReadyForNextFrame = OMX_TRUE; //ready to receive another set of input frame |
| |
| AvcStatus = PVAVCEncGetRecon(&iAvcHandle, &recon); |
| if (AVCENC_SUCCESS == AvcStatus) |
| { |
| PVAVCEncReleaseRecon(&iAvcHandle, &recon); |
| } |
| } |
| else if (AVCENC_SKIPPED_PICTURE == AvcStatus) |
| { |
| *aOutputLength = 0; |
| iReadyForNextFrame = OMX_TRUE; //ready to receive another set of input frame |
| return AVCENC_SKIPPED_PICTURE; |
| } |
| else |
| { |
| *aOutputLength = 0; |
| iReadyForNextFrame = OMX_TRUE; //ready to receive another set of input frame |
| return AVCENC_FAIL; |
| } |
| |
| *aOutputLength = Size; |
| *aOutTimeStamp = iTimeStamp64;//timestamp in microsec |
| |
| return ReturnValue; |
| |
| } |
| else if (AVCENC_SKIPPED_PICTURE == SetInputStatus) |
| { |
| *aOutputLength = 0; |
| return AVCENC_SKIPPED_PICTURE; |
| } |
| else |
| { |
| *aOutputLength = 0; |
| return AVCENC_FAIL; |
| } |
| } |
| |
| |
| //Deinitialize the avc encoder here and perform the cleanup and free operations |
| OMX_ERRORTYPE AvcEncoder_OMX::AvcEncDeinit() |
| { |
| if (OMX_TRUE == iInitialized) |
| { |
| PVAVCCleanUpEncoder(&iAvcHandle); |
| iAvcHandle.AVCObject = NULL; |
| |
| iInitialized = OMX_FALSE; |
| iReadyForNextFrame = OMX_TRUE; |
| |
| if (iYUVIn) |
| { |
| oscl_free(iYUVIn); |
| iYUVIn = NULL; |
| } |
| } |
| |
| if (iFrameUsed) |
| { |
| oscl_free(iFrameUsed); |
| iFrameUsed = NULL; |
| } |
| |
| if (ccRGBtoYUV) |
| { |
| OSCL_DELETE(ccRGBtoYUV); |
| ccRGBtoYUV = NULL; |
| } |
| |
| if (iDPB) |
| { |
| oscl_free(iDPB); |
| iDPB = NULL; |
| } |
| |
| if (iFramePtr) |
| { |
| oscl_free(iFramePtr); |
| iFramePtr = NULL; |
| } |
| |
| return OMX_ErrorNone; |
| } |
| |
| |
| int AvcEncoder_OMX::AVC_DPBAlloc(uint frame_size_in_mbs, uint num_buffers) |
| { |
| int ii; |
| uint frame_size = (frame_size_in_mbs << 8) + (frame_size_in_mbs << 7); |
| |
| if (iDPB) oscl_free(iDPB); // free previous one first |
| |
| iDPB = (uint8*) oscl_malloc(sizeof(uint8) * frame_size * num_buffers); |
| if (iDPB == NULL) |
| { |
| return 0; |
| } |
| |
| iNumFrames = num_buffers; |
| |
| if (iFrameUsed) oscl_free(iFrameUsed); // free previous one |
| |
| iFrameUsed = (bool*) oscl_malloc(sizeof(bool) * num_buffers); |
| if (iFrameUsed == NULL) |
| { |
| return 0; |
| } |
| |
| if (iFramePtr) oscl_free(iFramePtr); // free previous one |
| iFramePtr = (uint8**) oscl_malloc(sizeof(uint8*) * num_buffers); |
| if (iFramePtr == NULL) |
| { |
| return 0; |
| } |
| |
| iFramePtr[0] = iDPB; |
| iFrameUsed[0] = false; |
| |
| for (ii = 1; ii < (int)num_buffers; ii++) |
| { |
| iFrameUsed[ii] = false; |
| iFramePtr[ii] = iFramePtr[ii-1] + frame_size; |
| } |
| |
| return 1; |
| } |
| |
| |
| //Request for an I frame while encoding is in process |
| OMX_ERRORTYPE AvcEncoder_OMX::AvcRequestIFrame() |
| { |
| if (AVCENC_SUCCESS != PVAVCEncIDRRequest(&iAvcHandle)) |
| { |
| return OMX_ErrorUndefined; |
| } |
| |
| return OMX_ErrorNone; |
| |
| } |
| |
| //Routine to update bitrate dynamically when encoding is in progress |
| OMX_BOOL AvcEncoder_OMX::AvcUpdateBitRate(OMX_U32 aEncodedBitRate) |
| { |
| int32 BitRate[2] = {0, 0}; |
| BitRate[0] = aEncodedBitRate; |
| |
| if (AVCENC_SUCCESS != PVAVCEncUpdateBitRate(&iAvcHandle, BitRate[0])) |
| { |
| return OMX_FALSE; |
| } |
| |
| return OMX_TRUE; |
| } |
| |
| //Routine to update frame rate dynamically when encoding is in progress |
| OMX_BOOL AvcEncoder_OMX::AvcUpdateFrameRate(OMX_U32 aEncodeFramerate) |
| { |
| OMX_U32 EncFrameRate[2] = {0, 0}; |
| EncFrameRate[0] = aEncodeFramerate >> 16; |
| |
| if (AVCENC_SUCCESS != PVAVCEncUpdateFrameRate(&iAvcHandle, (uint32)(1000*EncFrameRate[0]), 1000)) |
| { |
| return OMX_FALSE; |
| } |
| |
| return OMX_TRUE; |
| |
| } |
| |
| |
| |
| /* ///////////////////////////////////////////////////////////////////////// */ |
| void AvcEncoder_OMX::AVC_FrameUnbind(int indx) |
| { |
| /*if (indx < iNumFrames) |
| { |
| iFrameUsed[indx] = false; |
| }*/ |
| |
| OSCL_UNUSED_ARG(indx); |
| return ; |
| } |
| |
| /* ///////////////////////////////////////////////////////////////////////// */ |
| int AvcEncoder_OMX::AVC_FrameBind(int indx, uint8** yuv) |
| { |
| /*if ((iFrameUsed[indx]==true) || (indx >= iNumFrames)) |
| { |
| return 0; // already in used |
| } |
| iFrameUsed[indx] = true;*/ |
| |
| *yuv = iFramePtr[indx]; |
| return 1; |
| } |
| |
| |
| |
| /* ///////////////////////////////////////////////////////////////////////// */ |
| /* Copy from YUV input to YUV frame inside AvcEnc lib */ |
| /* When input is not YUV, the color conv will write it directly to iVideoInOut. */ |
| /* ///////////////////////////////////////////////////////////////////////// */ |
| |
| void AvcEncoder_OMX::CopyToYUVIn(uint8 *YUV, int width, int height, int width_16, int height_16) |
| { |
| uint8 *y, *u, *v, *yChan, *uChan, *vChan; |
| int y_ind, ilimit, jlimit, i, j, ioffset; |
| int size = width * height; |
| int size16 = width_16 * height_16; |
| |
| /* do padding at the bottom first */ |
| /* do padding if input RGB size(height) is different from the output YUV size(height_16) */ |
| if (height < height_16 || width < width_16) /* if padding */ |
| { |
| int offset = (height < height_16) ? height : height_16; |
| |
| offset = (offset * width_16); |
| |
| if (width < width_16) |
| { |
| offset -= (width_16 - width); |
| } |
| |
| yChan = (uint8*)(iYUVIn + offset); |
| oscl_memset(yChan, 16, size16 - offset); /* pad with zeros */ |
| |
| uChan = (uint8*)(iYUVIn + size16 + (offset >> 2)); |
| oscl_memset(uChan, 128, (size16 - offset) >> 2); |
| |
| vChan = (uint8*)(iYUVIn + size16 + (size16 >> 2) + (offset >> 2)); |
| oscl_memset(vChan, 128, (size16 - offset) >> 2); |
| } |
| |
| /* then do padding on the top */ |
| yChan = (uint8*)iYUVIn; /* Normal order */ |
| uChan = (uint8*)(iYUVIn + size16); |
| vChan = (uint8*)(uChan + (size16 >> 2)); |
| |
| u = (uint8*)(&(YUV[size])); |
| v = (uint8*)(&(YUV[size*5/4])); |
| |
| /* To center the output */ |
| if (height_16 > height) /* output taller than input */ |
| { |
| if (width_16 >= width) /* output wider than or equal input */ |
| { |
| i = ((height_16 - height) >> 1) * width_16 + (((width_16 - width) >> 3) << 2); |
| /* make sure that (width_16-width)>>1 is divisible by 4 */ |
| j = ((height_16 - height) >> 2) * (width_16 >> 1) + (((width_16 - width) >> 4) << 2); |
| /* make sure that (width_16-width)>>2 is divisible by 4 */ |
| } |
| else /* output narrower than input */ |
| { |
| i = ((height_16 - height) >> 1) * width_16; |
| j = ((height_16 - height) >> 2) * (width_16 >> 1); |
| YUV += ((width - width_16) >> 1); |
| u += ((width - width_16) >> 2); |
| v += ((width - width_16) >> 2); |
| } |
| oscl_memset((uint8 *)yChan, 16, i); |
| yChan += i; |
| oscl_memset((uint8 *)uChan, 128, j); |
| uChan += j; |
| oscl_memset((uint8 *)vChan, 128, j); |
| vChan += j; |
| } |
| else /* output shorter or equal input */ |
| { |
| if (width_16 >= width) /* output wider or equal input */ |
| { |
| i = (((width_16 - width) >> 3) << 2); |
| /* make sure that (width_16-width)>>1 is divisible by 4 */ |
| j = (((width_16 - width) >> 4) << 2); |
| /* make sure that (width_16-width)>>2 is divisible by 4 */ |
| YUV += (((height - height_16) >> 1) * width); |
| u += (((height - height_16) >> 1) * width) >> 2; |
| v += (((height - height_16) >> 1) * width) >> 2; |
| } |
| else /* output narrower than input */ |
| { |
| i = 0; |
| j = 0; |
| YUV += (((height - height_16) >> 1) * width + ((width - width_16) >> 1)); |
| u += (((height - height_16) >> 1) * width + ((width - width_16) >> 1)) >> 2; |
| v += (((height - height_16) >> 1) * width + ((width - width_16) >> 1)) >> 2; |
| } |
| oscl_memset((uint8 *)yChan, 16, i); |
| yChan += i; |
| oscl_memset((uint8 *)uChan, 128, j); |
| uChan += j; |
| oscl_memset((uint8 *)vChan, 128, j); |
| vChan += j; |
| } |
| |
| /* Copy with cropping or zero-padding */ |
| if (height < height_16) |
| jlimit = height; |
| else |
| jlimit = height_16; |
| |
| if (width < width_16) |
| { |
| ilimit = width; |
| ioffset = width_16 - width; |
| } |
| else |
| { |
| ilimit = width_16; |
| ioffset = 0; |
| } |
| |
| /* Copy Y */ |
| /* Set up pointer for fast looping */ |
| y = (uint8*)YUV; |
| |
| if (width == width_16 && height == height_16) /* no need to pad */ |
| { |
| oscl_memcpy(yChan, y, size); |
| } |
| else |
| { |
| for (y_ind = 0; y_ind < (jlimit - 1) ; y_ind++) |
| { |
| oscl_memcpy(yChan, y, ilimit); |
| oscl_memset(yChan + ilimit, 16, ioffset); /* pad with zero */ |
| yChan += width_16; |
| y += width; |
| } |
| oscl_memcpy(yChan, y, ilimit); /* last line no padding */ |
| } |
| /* Copy U and V */ |
| /* Set up pointers for fast looping */ |
| if (width == width_16 && height == height_16) /* no need to pad */ |
| { |
| oscl_memcpy(uChan, u, size >> 2); |
| oscl_memcpy(vChan, v, size >> 2); |
| } |
| else |
| { |
| for (y_ind = 0; y_ind < (jlimit >> 1) - 1; y_ind++) |
| { |
| oscl_memcpy(uChan, u, ilimit >> 1); |
| oscl_memcpy(vChan, v, ilimit >> 1); |
| oscl_memset(uChan + (ilimit >> 1), 128, ioffset >> 1); |
| oscl_memset(vChan + (ilimit >> 1), 128, ioffset >> 1); |
| uChan += (width_16 >> 1); |
| u += (width >> 1); |
| vChan += (width_16 >> 1); |
| v += (width >> 1); |
| } |
| oscl_memcpy(uChan, u, ilimit >> 1); /* last line no padding */ |
| oscl_memcpy(vChan, v, ilimit >> 1); |
| } |
| |
| return ; |
| } |
| |
| OMX_BOOL AvcEncoder_OMX::GetSpsPpsHeaderFlag() |
| { |
| return iSpsPpsHeaderFlag; |
| } |
| |
| |