ref: 3dccfabce38447862af35ec2d40179c3d96d7adb
dir: /codec/encoder/core/src/encoder.cpp/
/*! * \copy * Copyright (c) 2009-2013, Cisco Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * * \file encoder.c * * \brief core encoder * * \date 5/14/2009 Created * ************************************************************************************* */ #include "encoder.h" #include "cpu_core.h" #include "decode_mb_aux.h" #include "get_intra_predictor.h" #include "deblocking.h" #include "ref_list_mgr_svc.h" #include "mc.h" #include "sample.h" #include "svc_enc_golomb.h" #include "svc_base_layer_md.h" #include "svc_mode_decision.h" #include "set_mb_syn_cavlc.h" #include "crt_util_safe_x.h" // Safe CRT routines like utils for cross_platforms #include "slice_multi_threading.h" // global function pointers definition namespace WelsEnc { /* Motion compensation */ /*! * \brief initialize source picture body * \param pSrc SSourcePicture* * \param csp internal csp format * \param iWidth widht of picture in pixels * \param iHeight iHeight of picture in pixels * \return successful - 0; otherwise none 0 for failed */ int32_t InitPic (const void* kpSrc, const int32_t kiColorspace, const int32_t kiWidth, const int32_t kiHeight) { SSourcePicture* pSrcPic = (SSourcePicture*)kpSrc; if (NULL == pSrcPic || kiWidth == 0 || kiHeight == 0) return 1; pSrcPic->iColorFormat = kiColorspace; pSrcPic->iPicWidth = kiWidth; pSrcPic->iPicHeight = kiHeight; //currently encoder only supports videoFormatI420. if ((kiColorspace & (~videoFormatVFlip)) != videoFormatI420) return 2; switch (kiColorspace & (~videoFormatVFlip)) { case videoFormatI420: case videoFormatYV12: pSrcPic->pData[0] = NULL; pSrcPic->pData[1] = NULL; pSrcPic->pData[2] = NULL; pSrcPic->pData[3] = NULL; pSrcPic->iStride[0] = kiWidth; pSrcPic->iStride[2] = pSrcPic->iStride[1] = kiWidth >> 1; pSrcPic->iStride[3] = 0; break; case videoFormatYUY2: case videoFormatYVYU: case videoFormatUYVY: pSrcPic->pData[0] = NULL; pSrcPic->pData[1] = NULL; pSrcPic->pData[2] = NULL; pSrcPic->pData[3] = NULL; pSrcPic->iStride[0] = CALC_BI_STRIDE (kiWidth, 16); pSrcPic->iStride[3] = pSrcPic->iStride[2] = pSrcPic->iStride[1] = 0; break; case videoFormatRGB: case videoFormatBGR: pSrcPic->pData[0] = NULL; pSrcPic->pData[1] = NULL; pSrcPic->pData[2] = NULL; pSrcPic->pData[3] = NULL; pSrcPic->iStride[0] = CALC_BI_STRIDE (kiWidth, 24); pSrcPic->iStride[3] = pSrcPic->iStride[2] = pSrcPic->iStride[1] = 0; if (kiColorspace & videoFormatVFlip) pSrcPic->iColorFormat = kiColorspace & (~videoFormatVFlip); else pSrcPic->iColorFormat = kiColorspace | videoFormatVFlip; break; case videoFormatBGRA: case videoFormatRGBA: case videoFormatARGB: case videoFormatABGR: pSrcPic->pData[0] = NULL; pSrcPic->pData[1] = NULL; pSrcPic->pData[2] = NULL; pSrcPic->pData[3] = NULL; pSrcPic->iStride[0] = kiWidth << 2; pSrcPic->iStride[3] = pSrcPic->iStride[2] = pSrcPic->iStride[1] = 0; if (kiColorspace & videoFormatVFlip) pSrcPic->iColorFormat = kiColorspace & (~videoFormatVFlip); else pSrcPic->iColorFormat = kiColorspace | videoFormatVFlip; break; default: return 2; // any else? } return 0; } void WelsInitBGDFunc (SWelsFuncPtrList* pFuncList, const bool kbEnableBackgroundDetection) { if (kbEnableBackgroundDetection) { pFuncList->pfInterMdBackgroundDecision = WelsMdInterJudgeBGDPskip; pFuncList->pfInterMdBackgroundInfoUpdate = WelsMdInterUpdateBGDInfo; } else { pFuncList->pfInterMdBackgroundDecision = WelsMdInterJudgeBGDPskipFalse; pFuncList->pfInterMdBackgroundInfoUpdate = WelsMdInterUpdateBGDInfoNULL; } } /*! * \brief initialize function pointers that potentially used in Wels encoding * \param pEncCtx sWelsEncCtx* * \return successful - 0; otherwise none 0 for failed */ int32_t InitFunctionPointers (sWelsEncCtx* pEncCtx, SWelsSvcCodingParam* pParam, uint32_t uiCpuFlag) { int32_t iReturn = ENC_RETURN_SUCCESS; SWelsFuncPtrList* pFuncList = pEncCtx->pFuncList; bool bScreenContent = (SCREEN_CONTENT_REAL_TIME == pParam->iUsageType); /* Functionality utilization of CPU instructions dependency */ pFuncList->pfSetMemZeroSize8 = WelsSetMemZero_c; // confirmed_safe_unsafe_usage pFuncList->pfSetMemZeroSize64Aligned16 = WelsSetMemZero_c; // confirmed_safe_unsafe_usage pFuncList->pfSetMemZeroSize64 = WelsSetMemZero_c; // confirmed_safe_unsafe_usage #if defined(X86_ASM) if (uiCpuFlag & WELS_CPU_MMXEXT) { pFuncList->pfSetMemZeroSize8 = WelsSetMemZeroSize8_mmx; // confirmed_safe_unsafe_usage pFuncList->pfSetMemZeroSize64Aligned16 = WelsSetMemZeroSize64_mmx; // confirmed_safe_unsafe_usage pFuncList->pfSetMemZeroSize64 = WelsSetMemZeroSize64_mmx; // confirmed_safe_unsafe_usage } if (uiCpuFlag & WELS_CPU_SSE2) { pFuncList->pfSetMemZeroSize64Aligned16 = WelsSetMemZeroAligned64_sse2; // confirmed_safe_unsafe_usage } #endif//X86_ASM #if defined(HAVE_NEON) if (uiCpuFlag & WELS_CPU_NEON) { pFuncList->pfSetMemZeroSize8 = WelsSetMemZero_neon; pFuncList->pfSetMemZeroSize64Aligned16 = WelsSetMemZero_neon; pFuncList->pfSetMemZeroSize64 = WelsSetMemZero_neon; } #endif #if defined(HAVE_NEON_AARCH64) if (uiCpuFlag & WELS_CPU_NEON) { pFuncList->pfSetMemZeroSize8 = WelsSetMemZero_AArch64_neon; pFuncList->pfSetMemZeroSize64Aligned16 = WelsSetMemZero_AArch64_neon; pFuncList->pfSetMemZeroSize64 = WelsSetMemZero_AArch64_neon; } #endif InitExpandPictureFunc (& (pFuncList->sExpandPicFunc), uiCpuFlag); /* Intra_Prediction_fn*/ WelsInitIntraPredFuncs (pFuncList, uiCpuFlag); /* ME func */ WelsInitMeFunc (pFuncList, uiCpuFlag, bScreenContent); /* sad, satd, average */ WelsInitSampleSadFunc (pFuncList, uiCpuFlag); // WelsInitBGDFunc (pFuncList, pParam->bEnableBackgroundDetection); WelsInitSCDPskipFunc (pFuncList, bScreenContent && (pParam->bEnableSceneChangeDetect)); // for pfGetVarianceFromIntraVaa function ptr adaptive by CPU features, 6/7/2010 InitIntraAnalysisVaaInfo (pFuncList, uiCpuFlag); /* Motion compensation */ /*init pixel average function*/ /*get one column or row pixel when refinement*/ InitMcFunc (&pFuncList->sMcFuncs, uiCpuFlag); InitCoeffFunc (pFuncList,uiCpuFlag,pParam->iEntropyCodingModeFlag); WelsInitEncodingFuncs (pFuncList, uiCpuFlag); WelsInitReconstructionFuncs (pFuncList, uiCpuFlag); DeblockingInit (&pFuncList->pfDeblocking, uiCpuFlag); WelsBlockFuncInit (&pFuncList->pfSetNZCZero, uiCpuFlag); InitFillNeighborCacheInterFunc (pFuncList, pParam->bEnableBackgroundDetection); InitRefListMgrFunc (pFuncList, pParam->bEnableLongTermReference, bScreenContent); return iReturn; } void UpdateFrameNum(sWelsEncCtx* pEncCtx) { bool bNeedFrameNumIncreasing = false; for (int32_t i=0; i<MAX_DEPENDENCY_LAYER; i++) { if (NRI_PRI_LOWEST != pEncCtx->eLastNalPriority[i]) { bNeedFrameNumIncreasing = true; } } if (bNeedFrameNumIncreasing) { if (pEncCtx->iFrameNum < (1 << pEncCtx->pSps->uiLog2MaxFrameNum) - 1) ++ pEncCtx->iFrameNum; else pEncCtx->iFrameNum = 0; // if iFrameNum overflow } for (int32_t i=0; i<MAX_DEPENDENCY_LAYER; i++) { pEncCtx->eLastNalPriority[i] = NRI_PRI_LOWEST; } } void LoadBackFrameNum(sWelsEncCtx* pEncCtx) { bool bNeedFrameNumIncreasing = false; for (int32_t i=0; i<MAX_DEPENDENCY_LAYER; i++) { if (NRI_PRI_LOWEST != pEncCtx->eLastNalPriority[i]) { bNeedFrameNumIncreasing = true; } } if (bNeedFrameNumIncreasing) { if (pEncCtx->iFrameNum != 0) { pEncCtx->iFrameNum --; } else { pEncCtx->iFrameNum = (1 << pEncCtx->pSps->uiLog2MaxFrameNum) - 1; } } } /*! * \brief initialize frame coding */ void InitFrameCoding (sWelsEncCtx* pEncCtx, const EVideoFrameType keFrameType) { // for bitstream writing pEncCtx->iPosBsBuffer = 0; // reset bs pBuffer position pEncCtx->pOut->iNalIndex = 0; // reset NAL index pEncCtx->pOut->iLayerBsIndex = 0; // reset index of Layer Bs InitBits (&pEncCtx->pOut->sBsWrite, pEncCtx->pOut->pBsBuffer, pEncCtx->pOut->uiSize); if (keFrameType == videoFrameTypeP) { ++pEncCtx->iFrameIndex; if (pEncCtx->iPOC < (1 << pEncCtx->pSps->iLog2MaxPocLsb) - 2) // if iPOC type is no 0, this need be modification pEncCtx->iPOC += 2; // for POC type 0 else pEncCtx->iPOC = 0; UpdateFrameNum(pEncCtx); pEncCtx->eNalType = NAL_UNIT_CODED_SLICE; pEncCtx->eSliceType = P_SLICE; pEncCtx->eNalPriority = NRI_PRI_HIGH; } else if (keFrameType == videoFrameTypeIDR) { pEncCtx->iFrameNum = 0; pEncCtx->iPOC = 0; pEncCtx->bEncCurFrmAsIdrFlag = false; pEncCtx->iFrameIndex = 0; pEncCtx->eNalType = NAL_UNIT_CODED_SLICE_IDR; pEncCtx->eSliceType = I_SLICE; pEncCtx->eNalPriority = NRI_PRI_HIGHEST; pEncCtx->iCodingIndex = 0; // reset_ref_list // rc_init_gop } else if (keFrameType == videoFrameTypeI) { if (pEncCtx->iPOC < (1 << pEncCtx->pSps->iLog2MaxPocLsb) - 2) // if iPOC type is no 0, this need be modification pEncCtx->iPOC += 2; // for POC type 0 else pEncCtx->iPOC = 0; UpdateFrameNum(pEncCtx); pEncCtx->eNalType = NAL_UNIT_CODED_SLICE; pEncCtx->eSliceType = I_SLICE; pEncCtx->eNalPriority = NRI_PRI_HIGHEST; // rc_init_gop } else { // B pictures are not supported now, any else? assert (0); } #if defined(STAT_OUTPUT) memset (&pEncCtx->sPerInfo, 0, sizeof (SStatSliceInfo)); #endif//FRAME_INFO_OUTPUT } EVideoFrameType DecideFrameType (sWelsEncCtx* pEncCtx, const int8_t kiSpatialNum) { SWelsSvcCodingParam* pSvcParam = pEncCtx->pSvcParam; EVideoFrameType iFrameType = videoFrameTypeInvalid; bool bSceneChangeFlag = false; if (pSvcParam->iUsageType == SCREEN_CONTENT_REAL_TIME) { if ((!pSvcParam->bEnableSceneChangeDetect) || pEncCtx->pVaa->bIdrPeriodFlag || (kiSpatialNum < pSvcParam->iSpatialLayerNum)) { bSceneChangeFlag = false; } else { bSceneChangeFlag = pEncCtx->pVaa->bSceneChangeFlag; } if (pEncCtx->pVaa->bIdrPeriodFlag || pEncCtx->bEncCurFrmAsIdrFlag || (!pSvcParam->bEnableLongTermReference && bSceneChangeFlag)) { iFrameType = videoFrameTypeIDR; } else if (pSvcParam->bEnableLongTermReference && (bSceneChangeFlag || pEncCtx->pVaa->eSceneChangeIdc == LARGE_CHANGED_SCENE)) { int iActualLtrcount = 0; SPicture** pLongTermRefList = pEncCtx->ppRefPicListExt[0]->pLongRefList; for (int i = 0; i < pSvcParam->iLTRRefNum; ++i) { if (NULL != pLongTermRefList[i] && pLongTermRefList[i]->bUsedAsRef && pLongTermRefList[i]->bIsLongRef && pLongTermRefList[i]->bIsSceneLTR) { ++iActualLtrcount; } } if (iActualLtrcount == pSvcParam->iLTRRefNum && bSceneChangeFlag) { iFrameType = videoFrameTypeIDR; } else { iFrameType = videoFrameTypeP; pEncCtx->bCurFrameMarkedAsSceneLtr = true; } } else { iFrameType = videoFrameTypeP; } if (videoFrameTypeP == iFrameType && pEncCtx->iSkipFrameFlag > 0) { -- pEncCtx->iSkipFrameFlag; iFrameType = videoFrameTypeSkip; } else if (videoFrameTypeIDR == iFrameType) { pEncCtx->iCodingIndex = 0; pEncCtx->bCurFrameMarkedAsSceneLtr = true; } } else { // perform scene change detection if ((!pSvcParam->bEnableSceneChangeDetect) || pEncCtx->pVaa->bIdrPeriodFlag || (kiSpatialNum < pSvcParam->iSpatialLayerNum) || (pEncCtx->iFrameIndex < (VGOP_SIZE << 1))) { // avoid too frequent I frame coding, rc control bSceneChangeFlag = false; } else { bSceneChangeFlag = pEncCtx->pVaa->bSceneChangeFlag; } //scene_changed_flag: RC enable && iSpatialNum == pSvcParam->iSpatialLayerNum //bIdrPeriodFlag: RC disable || iSpatialNum != pSvcParam->iSpatialLayerNum //pEncCtx->bEncCurFrmAsIdrFlag: 1. first frame should be IDR; 2. idr pause; 3. idr request iFrameType = (pEncCtx->pVaa->bIdrPeriodFlag || bSceneChangeFlag || pEncCtx->bEncCurFrmAsIdrFlag) ? videoFrameTypeIDR : videoFrameTypeP; if (videoFrameTypeP == iFrameType && pEncCtx->iSkipFrameFlag > 0) { // for frame skip, 1/5/2010 -- pEncCtx->iSkipFrameFlag; iFrameType = videoFrameTypeSkip; } else if (videoFrameTypeIDR == iFrameType) { pEncCtx->iCodingIndex = 0; } } return iFrameType; } /*! * \brief Dump reconstruction for dependency layer */ extern "C" void DumpDependencyRec (SPicture* pCurPicture, const char* kpFileName, const int8_t kiDid, bool bAppend, SDqLayer* pDqLayer) { WelsFileHandle* pDumpRecFile = NULL; int32_t iWrittenSize = 0; const char* openMode = bAppend ? "ab" : "wb"; SWelsSPS* pSpsTmp = (kiDid > BASE_DEPENDENCY_ID) ? & (pDqLayer->sLayerInfo.pSubsetSpsP->pSps) : pDqLayer->sLayerInfo.pSpsP; bool bFrameCroppingFlag = pSpsTmp->bFrameCroppingFlag; SCropOffset* pFrameCrop = &pSpsTmp->sFrameCrop; if (NULL == pCurPicture || NULL == kpFileName || kiDid >= MAX_DEPENDENCY_LAYER) return; if (strlen (kpFileName) > 0) // confirmed_safe_unsafe_usage pDumpRecFile = WelsFopen (kpFileName, openMode); else { char sDependencyRecFileName[16] = {0}; WelsSnprintf (sDependencyRecFileName, 16, "rec%d.yuv", kiDid); // confirmed_safe_unsafe_usage pDumpRecFile = WelsFopen (sDependencyRecFileName, openMode); } if (NULL != pDumpRecFile && bAppend) WelsFseek (pDumpRecFile, 0, SEEK_END); if (NULL != pDumpRecFile) { int32_t i = 0; int32_t j = 0; const int32_t kiStrideY = pCurPicture->iLineSize[0]; const int32_t kiLumaWidth = bFrameCroppingFlag ? (pCurPicture->iWidthInPixel - ((pFrameCrop->iCropLeft + pFrameCrop->iCropRight) << 1)) : pCurPicture->iWidthInPixel; const int32_t kiLumaHeight = bFrameCroppingFlag ? (pCurPicture->iHeightInPixel - ((pFrameCrop->iCropTop + pFrameCrop->iCropBottom) << 1)) : pCurPicture->iHeightInPixel; const int32_t kiChromaWidth = kiLumaWidth >> 1; const int32_t kiChromaHeight = kiLumaHeight >> 1; uint8_t* pSrc = NULL; pSrc = bFrameCroppingFlag ? (pCurPicture->pData[0] + kiStrideY * (pFrameCrop->iCropTop << 1) + (pFrameCrop->iCropLeft << 1)) : pCurPicture->pData[0]; for (j = 0; j < kiLumaHeight; ++ j) { iWrittenSize = WelsFwrite (pSrc + j * kiStrideY, 1, kiLumaWidth, pDumpRecFile); assert (iWrittenSize == kiLumaWidth); if (iWrittenSize < kiLumaWidth) { assert (0); // make no sense for us if writing failed WelsFclose (pDumpRecFile); return; } } for (i = 1; i < I420_PLANES; ++ i) { const int32_t kiStrideUV = pCurPicture->iLineSize[i]; pSrc = bFrameCroppingFlag ? (pCurPicture->pData[i] + kiStrideUV * pFrameCrop->iCropTop + pFrameCrop->iCropLeft) : pCurPicture->pData[i]; for (j = 0; j < kiChromaHeight; ++ j) { iWrittenSize = WelsFwrite (pSrc + j * kiStrideUV, 1, kiChromaWidth, pDumpRecFile); assert (iWrittenSize == kiChromaWidth); if (iWrittenSize < kiChromaWidth) { assert (0); // make no sense for us if writing failed WelsFclose (pDumpRecFile); return; } } } WelsFclose (pDumpRecFile); pDumpRecFile = NULL; } } /*! * \brief Dump the reconstruction pictures */ void DumpRecFrame (SPicture* pCurPicture, const char* kpFileName, const int8_t kiDid, bool bAppend, SDqLayer* pDqLayer) { WelsFileHandle* pDumpRecFile = NULL; SWelsSPS* pSpsTmp = (kiDid > BASE_DEPENDENCY_ID) ? & (pDqLayer->sLayerInfo.pSubsetSpsP->pSps) : pDqLayer->sLayerInfo.pSpsP; bool bFrameCroppingFlag = pSpsTmp->bFrameCroppingFlag; SCropOffset* pFrameCrop = &pSpsTmp->sFrameCrop; int32_t iWrittenSize = 0; const char* openMode = bAppend ? "ab" : "wb"; if (NULL == pCurPicture || NULL == kpFileName) return; if (strlen (kpFileName) > 0) { // confirmed_safe_unsafe_usage pDumpRecFile = WelsFopen (kpFileName, openMode); } else { pDumpRecFile = WelsFopen ("rec.yuv", openMode); } if (NULL != pDumpRecFile && bAppend) WelsFseek (pDumpRecFile, 0, SEEK_END); if (NULL != pDumpRecFile) { int32_t i = 0; int32_t j = 0; const int32_t kiStrideY = pCurPicture->iLineSize[0]; const int32_t kiLumaWidth = bFrameCroppingFlag ? (pCurPicture->iWidthInPixel - ((pFrameCrop->iCropLeft + pFrameCrop->iCropRight) << 1)) : pCurPicture->iWidthInPixel; const int32_t kiLumaHeight = bFrameCroppingFlag ? (pCurPicture->iHeightInPixel - ((pFrameCrop->iCropTop + pFrameCrop->iCropBottom) << 1)) : pCurPicture->iHeightInPixel; const int32_t kiChromaWidth = kiLumaWidth >> 1; const int32_t kiChromaHeight = kiLumaHeight >> 1; uint8_t* pSrc = NULL; pSrc = bFrameCroppingFlag ? (pCurPicture->pData[0] + kiStrideY * (pFrameCrop->iCropTop << 1) + (pFrameCrop->iCropLeft << 1)) : pCurPicture->pData[0]; for (j = 0; j < kiLumaHeight; ++ j) { iWrittenSize = WelsFwrite (pSrc + j * kiStrideY, 1, kiLumaWidth, pDumpRecFile); assert (iWrittenSize == kiLumaWidth); if (iWrittenSize < kiLumaWidth) { assert (0); // make no sense for us if writing failed WelsFclose (pDumpRecFile); return; } } for (i = 1; i < I420_PLANES; ++ i) { const int32_t kiStrideUV = pCurPicture->iLineSize[i]; pSrc = bFrameCroppingFlag ? (pCurPicture->pData[i] + kiStrideUV * pFrameCrop->iCropTop + pFrameCrop->iCropLeft) : pCurPicture->pData[i]; for (j = 0; j < kiChromaHeight; ++ j) { iWrittenSize = WelsFwrite (pSrc + j * kiStrideUV, 1, kiChromaWidth, pDumpRecFile); assert (iWrittenSize == kiChromaWidth); if (iWrittenSize < kiChromaWidth) { assert (0); // make no sense for us if writing failed WelsFclose (pDumpRecFile); return; } } } WelsFclose (pDumpRecFile); pDumpRecFile = NULL; } } /***********************************************************************************/ void WelsSetMemZero_c (void* pDst, int32_t iSize) { // confirmed_safe_unsafe_usage memset (pDst, 0, iSize); } }