/*! * \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 #include #include #include #include "encoder.h" #include "extern.h" #include "cpu.h" #include "cpu_core.h" #include "utils.h" #include "encode_mb_aux.h" #include "decode_mb_aux.h" #include "get_intra_predictor.h" #include "svc_encode_mb.h" #include "deblocking.h" #include "expand_pic.h" #include "mc.h" #include "sample.h" #include "svc_encode_slice.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 "codec_def.h" #ifdef MT_ENABLED #include "slice_multi_threading.h" #endif//MT_ENABLED // global function pointers definition namespace WelsSVCEnc { /* 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; 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_t kbEnableBackgroundDetection ) { if ( kbEnableBackgroundDetection ) { pFuncList->pfInterMdBackgroundDecision = (PInterMdBackgroundDecisionFunc)WelsMdInterJudgeBGDPskip; pFuncList->pfInterMdBackgroundInfoUpdate = (PInterMdBackgroundInfoUpdateFunc)WelsMdInterUpdateBGDInfo; } else { pFuncList->pfInterMdBackgroundDecision = (PInterMdBackgroundDecisionFunc)WelsMdInterJudgeBGDPskipFalse; pFuncList->pfInterMdBackgroundInfoUpdate = (PInterMdBackgroundInfoUpdateFunc)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( SWelsFuncPtrList *pFuncList, SWelsSvcCodingParam *pParam, uint32_t uiCpuFlag ) { int32_t iReturn = 0; /* 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 InitExpandPictureFunc( pFuncList, uiCpuFlag ); /* Intra_Prediction_fn*/ WelsInitFillingPredFuncs( uiCpuFlag ); WelsInitIntraPredFuncs( pFuncList, uiCpuFlag ); /* sad, satd, average */ WelsInitSampleSadFunc(pFuncList, uiCpuFlag); // WelsInitBGDFunc(pFuncList, pParam->bEnableBackgroundDetection ); // 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*/ WelsInitMcFuncs(pFuncList, uiCpuFlag); InitCoeffFunc( uiCpuFlag ); WelsInitEncodingFuncs( pFuncList, uiCpuFlag ); WelsInitReconstructionFuncs( pFuncList, uiCpuFlag ); DeblockingInit( &pFuncList->pfDeblocking, uiCpuFlag ); WelsBlockFuncInit( &pFuncList->pfSetNZCZero, uiCpuFlag ); InitFillNeighborCacheInterFunc ( pFuncList, pParam->bEnableBackgroundDetection ); return iReturn; } /*! * \brief initialize frame coding */ void InitFrameCoding( sWelsEncCtx *pEncCtx, const EFrameType keFrameType ) { // for bitstream writing pEncCtx->iPosBsBuffer = 0; // reset bs pBuffer position pEncCtx->pOut->iNalIndex = 0; // reset NAL index InitBits( &pEncCtx->pOut->sBsWrite, pEncCtx->pOut->pBsBuffer, pEncCtx->pOut->uiSize ); if ( keFrameType == WELS_FRAME_TYPE_P ) { if ( pEncCtx->pSvcParam->uiIntraPeriod ) { ++pEncCtx->iFrameIndex; } ++pEncCtx->uiFrameIdxRc; 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; if ( pEncCtx->eLastNalPriority != 0 ) { if ( pEncCtx->iFrameNum < (1 << pEncCtx->pSps->uiLog2MaxFrameNum) - 1 ) ++ pEncCtx->iFrameNum; else pEncCtx->iFrameNum = 0; // if iFrameNum overflow } pEncCtx->eNalType = NAL_UNIT_CODED_SLICE; pEncCtx->eSliceType = P_SLICE; pEncCtx->eNalPriority = NRI_PRI_HIGH; } else if ( keFrameType == WELS_FRAME_TYPE_IDR ) { pEncCtx->iFrameNum = 0; pEncCtx->iPOC = 0; pEncCtx->bEncCurFrmAsIdrFlag = false; if ( pEncCtx->pSvcParam->uiIntraPeriod ) { pEncCtx->iFrameIndex = 0; } pEncCtx->uiFrameIdxRc = 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 == WELS_FRAME_TYPE_I ) { 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; if ( pEncCtx->eLastNalPriority != 0 ) { if ( pEncCtx->iFrameNum < (1 << pEncCtx->pSps->uiLog2MaxFrameNum) - 1 ) ++ pEncCtx->iFrameNum; else pEncCtx->iFrameNum = 0; // if iFrameNum overflow } 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 #if defined(MT_ENABLED) && defined(PACKING_ONE_SLICE_PER_LAYER) if ( pEncCtx->pSvcParam->iMultipleThreadIdc > 1 ) reset_env_mt( pEncCtx ); #endif } EFrameType DecideFrameType( sWelsEncCtx *pEncCtx, const int8_t kiSpatialNum ) { SWelsSvcCodingParam *pSvcParam = pEncCtx->pSvcParam; EFrameType iFrameType = WELS_FRAME_TYPE_AUTO; bool_t bSceneChangeFlag = false; // perform scene change detection if ( (!pSvcParam->bEnableSceneChangeDetect) || pEncCtx->pVaa->bIdrPeriodFlag || (kiSpatialNum < pSvcParam->iNumDependencyLayer) || (pEncCtx->uiFrameIdxRc < (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->iNumDependencyLayer //bIdrPeriodFlag: RC disable || iSpatialNum != pSvcParam->iNumDependencyLayer //pEncCtx->bEncCurFrmAsIdrFlag: 1. first frame should be IDR; 2. idr pause; 3. idr request iFrameType = ( pEncCtx->pVaa->bIdrPeriodFlag || bSceneChangeFlag || pEncCtx->bEncCurFrmAsIdrFlag ) ? WELS_FRAME_TYPE_IDR : WELS_FRAME_TYPE_P; if ( WELS_FRAME_TYPE_P == iFrameType && pEncCtx->iSkipFrameFlag > 0 ) // for frame skip, 1/5/2010 { -- pEncCtx->iSkipFrameFlag; iFrameType = WELS_FRAME_TYPE_SKIP; } else if ( WELS_FRAME_TYPE_IDR == iFrameType ) { pEncCtx->iCodingIndex = 0; } return iFrameType; } /*! * \brief Dump reconstruction for dependency layer */ extern "C" void DumpDependencyRec( SPicture *pCurPicture, const str_t *kpFileName, const int8_t kiDid ) { FILE *pDumpRecFile = NULL; static bool_t bDependencyRecFlag[MAX_DEPENDENCY_LAYER] = {0}; int32_t iWrittenSize = 0; if ( NULL == pCurPicture || NULL == kpFileName || kiDid >= MAX_DEPENDENCY_LAYER ) return; if ( bDependencyRecFlag[kiDid] ) { if ( STRNLEN(kpFileName, MAX_FNAME_LEN) > 0 ) // confirmed_safe_unsafe_usage #if defined(__GNUC__) || (defined(WIN32) && defined(_MSC_VER) && (_MSC_VER<1500)) pDumpRecFile = FOPEN( kpFileName, "ab" ); #elif defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, kpFileName, "ab"); #endif//__GNUC__.. else { str_t sDependencyRecFileName[16] = {0}; #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 SNPRINTF( sDependencyRecFileName, 16, 16, "rec%d.yuv", kiDid ); // confirmed_safe_unsafe_usage FOPEN( &pDumpRecFile, sDependencyRecFileName, "ab" ); #else SNPRINTF( sDependencyRecFileName, 16, "rec%d.yuv", kiDid ); // confirmed_safe_unsafe_usage pDumpRecFile = FOPEN( sDependencyRecFileName, "ab" ); #endif//WIN32.. } if ( NULL != pDumpRecFile) fseek( pDumpRecFile, 0, SEEK_END ); } else { if ( STRNLEN(kpFileName, MAX_FNAME_LEN) > 0 ) // confirmed_safe_unsafe_usage { #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, kpFileName, "wb"); #else pDumpRecFile = FOPEN( kpFileName, "wb" ); #endif//WIN32.. } else { str_t sDependencyRecFileName[16] = {0}; #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 SNPRINTF( sDependencyRecFileName, 16, 16, "rec%d.yuv", kiDid ); // confirmed_safe_unsafe_usage FOPEN(&pDumpRecFile, sDependencyRecFileName, "wb"); #else SNPRINTF( sDependencyRecFileName, 16, "rec%d.yuv", kiDid ); // confirmed_safe_unsafe_usage pDumpRecFile = FOPEN( sDependencyRecFileName, "wb"); #endif//WIN32.. } bDependencyRecFlag[kiDid] = true; } if ( NULL != pDumpRecFile ) { int32_t i = 0; int32_t j = 0; const int32_t kiStrideY = pCurPicture->iLineSize[0]; const int32_t kiLumaWidth = pCurPicture->iWidthInPixel; const int32_t kiLumaHeight = pCurPicture->iHeightInPixel; const int32_t kiChromaWidth = kiLumaWidth >> 1; const int32_t kiChromaHeight = kiLumaHeight >> 1; for( j = 0; j < kiLumaHeight; ++ j) { iWrittenSize = fwrite( &pCurPicture->pData[0][j*kiStrideY], 1, kiLumaWidth, pDumpRecFile ); assert( iWrittenSize == kiLumaWidth ); if ( iWrittenSize < kiLumaWidth ) { assert( 0 ); // make no sense for us if writing failed fclose(pDumpRecFile); return; } } for( i = 1; i < I420_PLANES; ++ i) { const int32_t kiStrideUV = pCurPicture->iLineSize[i]; for ( j = 0; j < kiChromaHeight; ++ j) { iWrittenSize = fwrite( &pCurPicture->pData[i][j*kiStrideUV], 1, kiChromaWidth, pDumpRecFile ); assert(iWrittenSize == kiChromaWidth ); if ( iWrittenSize < kiChromaWidth ) { assert( 0 ); // make no sense for us if writing failed fclose(pDumpRecFile); return; } } } fclose(pDumpRecFile); pDumpRecFile = NULL; } } /*! * \brief Dump the reconstruction pictures */ void DumpRecFrame( SPicture *pCurPicture, const str_t *kpFileName ) { FILE *pDumpRecFile = NULL; static bool_t bRecFlag = false; int32_t iWrittenSize = 0; if ( NULL == pCurPicture || NULL == kpFileName ) return; if ( bRecFlag ) { if ( STRNLEN(kpFileName, MAX_FNAME_LEN) > 0 ) // confirmed_safe_unsafe_usage { #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, kpFileName, "ab"); #else pDumpRecFile = FOPEN( kpFileName, "ab" ); #endif//WIN32 } else { #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, "rec.yuv", "ab"); #else pDumpRecFile = FOPEN( "rec.yuv", "ab" ); #endif//WIN32 } if ( NULL != pDumpRecFile) fseek( pDumpRecFile, 0, SEEK_END ); } else { if ( STRNLEN(kpFileName, MAX_FNAME_LEN) > 0 ) // confirmed_safe_unsafe_usage { #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, kpFileName, "wb"); #else pDumpRecFile = FOPEN( kpFileName, "wb" ); #endif//WIN32 } else { #if defined(WIN32) && defined(_MSC_VER) && (_MSC_VER>=1500) // vs2008 FOPEN(&pDumpRecFile, "rec.yuv", "wb"); #else pDumpRecFile = FOPEN( "rec.yuv", "wb"); #endif//WIN32.. } bRecFlag = true; } if ( NULL != pDumpRecFile ) { int32_t i = 0; int32_t j = 0; const int32_t kiStrideY = pCurPicture->iLineSize[0]; const int32_t kiLumaWidth = pCurPicture->iWidthInPixel; const int32_t kiLumaHeight = pCurPicture->iHeightInPixel; const int32_t kiChromaWidth = kiLumaWidth >> 1; const int32_t kiChromaHeight = kiLumaHeight >> 1; for( j = 0; j < kiLumaHeight; ++ j) { iWrittenSize = fwrite( &pCurPicture->pData[0][j*kiStrideY], 1, kiLumaWidth, pDumpRecFile ); assert( iWrittenSize == kiLumaWidth ); if ( iWrittenSize < kiLumaWidth ) { assert( 0 ); // make no sense for us if writing failed fclose(pDumpRecFile); return; } } for( i = 1; i < I420_PLANES; ++ i) { const int32_t kiStrideUV = pCurPicture->iLineSize[i]; for ( j = 0; j < kiChromaHeight; ++ j) { iWrittenSize = fwrite( &pCurPicture->pData[i][j*kiStrideUV], 1, kiChromaWidth, pDumpRecFile ); assert(iWrittenSize == kiChromaWidth ); if ( iWrittenSize < kiChromaWidth ) { assert( 0 ); // make no sense for us if writing failed fclose(pDumpRecFile); return; } } } fclose(pDumpRecFile); pDumpRecFile = NULL; } } /***********************************************************************************/ void WelsSetMemZero_c(void *pDst, int32_t iSize) // confirmed_safe_unsafe_usage { memset(pDst, 0, iSize); } }