shithub: openh264

ref: cf92e8d6208af51fb55ed61554b135585ed33c1b
dir: /codec/encoder/core/src/wels_preprocess.cpp/

View raw version
/*!
 * \copy
 *     Copyright (c)  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.
 *
 */

#if defined(WIN32)
#include <windows.h>
#elif defined(MACOS)
#include "bundleloader.h"
#elif defined(__GNUC__)
#include <dlfcn.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "wels_preprocess.h"
#include "memory_align.h"
#include "encoder.h"
#include "extern.h"
#include "picture_handle.h"
#include "encoder_context.h"
#include "utils.h"

#ifdef NO_DYNAMIC_VP
EResult WELSAPI CreateVpInterface  (void **ppCtx, int iVersion);
EResult WELSAPI DestroyVpInterface  (void **ppCtx, int iVersion);
#endif

namespace WelsSVCEnc {

#define WelsSafeDelete(p) if(p){ delete (p); (p) = NULL; }


//***** entry API declaration ************************************************************************//
typedef EResult (WELSAPI *pfnCreateVpInterface)  (void **, int );
typedef EResult (WELSAPI *pfnDestroyVpInterface) (void * , int );

int32_t WelsInitScaledPic( SWelsSvcCodingParam *pParam,  Scaled_Picture  *pScaledPic, CMemoryAlign *pMemoryAlign );
bool_t  JudgeNeedOfScaling( SWelsSvcCodingParam *pParam, Scaled_Picture * pScaledPic );
void    FreeScaledPic( Scaled_Picture  *pScaledPic, CMemoryAlign *pMemoryAlign );

//******* table definition ***********************************************************************//
const uint8_t g_kuiRefTemporalIdx[MAX_TEMPORAL_LEVEL][MAX_GOP_SIZE] =
{
	{  0, }, // 0
	{  0,  0, }, // 1
	{  0,  0,  0,  1, }, // 2
	{  0,  0,  0,  2,  0,  1,  1,  2, }, // 3
	{  0,  0,  0,  3,  0,  2,  2,  3,  0,  1,  1,  3,  1,  2,  2,  3 }  // 4
};

const int32_t g_kiPixMapSizeInBits = sizeof(uint8_t) * 8;


inline  void   WelsUpdateSpatialIdxMap(sWelsEncCtx * pEncCtx, int32_t iPos, SPicture * pPic, int32_t iDidx)
{
    pEncCtx->sSpatialIndexMap[iPos].pSrc = pPic;
	pEncCtx->sSpatialIndexMap[iPos].iDid = iDidx;
}


//***************************************************************************************************//
CWelsLib::CWelsLib(void *pEncCtx)
{
	m_pInterface[0] = m_pInterface[1] = NULL;

#ifndef NO_DYNAMIC_VP
#if defined(WIN32)
	const str_t WelsVPLib[] = "welsvp.dll";
	HMODULE shModule = LoadLibrary(WelsVPLib);
	if(!shModule)
		WelsLog( pEncCtx, WELS_LOG_ERROR, "welsvp load lib dynamic failed module=%x\n", shModule );

#elif defined(MACOS)
	const str_t WelsVPLib[] = "welsvp.bundle";
	str_t pCurPath[256];
	GetCurrentModulePath(pCurPath, 256);
	strlcat(pCurPath, WelsVPLib, 256);	
	CFBundleRef shModule = LoadBundle(pCurPath);
	if(!shModule)
		WelsLog( pEncCtx, WELS_LOG_ERROR, "welsvp load lib dynamic failed module=%x\n", shModule );

#elif defined(__GNUC__)
	const str_t WelsVPLib[] = "./libwelsvp.so";
	void* shModule = NULL;
	shModule = dlopen(WelsVPLib, RTLD_LAZY);
	if (shModule == NULL)
		printf("dlopen %s iRet=%x, err=%s\n", WelsVPLib, shModule, dlerror());
#endif

	m_pVpLib = (void *)shModule;
#endif
}

CWelsLib::~CWelsLib()
{
	if (m_pVpLib)
	{
#if defined(WIN32)
		HMODULE shModule = (HMODULE)m_pVpLib;
		FreeLibrary(shModule);

#elif defined(MACOS)
		CFBundleRef shModule = (CFBundleRef)m_pVpLib;
		FreeBundle(shModule);

#elif defined(__GNUC__)
		void* shModule = m_pVpLib;
		dlclose(shModule);
#endif
		m_pVpLib = NULL;
	}
}

void* CWelsLib::QueryFunction(const str_t *pName)
{
	void *pFunc = NULL;

	if (m_pVpLib)
	{
#if defined(WIN32)
		HMODULE shModule = (HMODULE)m_pVpLib;
		pFunc = (void *)GetProcAddress(shModule, pName);

#elif defined(MACOS)
		CFBundleRef shModule = (CFBundleRef)m_pVpLib;
		pFunc = (void *)GetProcessAddress(shModule, pName);

#elif defined(__GNUC__)
		void* shModule = m_pVpLib;
		pFunc = (void *)dlsym(shModule, pName);
		if (pFunc == NULL)
			printf("dlsym %s iRet=%p, err=%s\n", shModule, pFunc, dlerror());
#endif
	}
	return pFunc;
}

int32_t CWelsLib::CreateIface(void **ppEncCtx)
{
#ifndef NO_DYNAMIC_VP
	if (m_pVpLib)
	{

#endif
		pfnCreateVpInterface  pCreateVpInterface  = NULL;
		pfnDestroyVpInterface pDestroyVpInterface = NULL;

#ifndef NO_DYNAMIC_VP
		pCreateVpInterface  = (pfnCreateVpInterface)  QueryFunction("CreateVpInterface");
		pDestroyVpInterface = (pfnDestroyVpInterface) QueryFunction("DestroyVpInterface");
#else
		pCreateVpInterface  = CreateVpInterface;
		// TODO([email protected]): This cast corrects a signature difference... This is a potential real problem
		pDestroyVpInterface = (pfnDestroyVpInterface)DestroyVpInterface;
#endif

		m_pInterface[0] = (void *)pCreateVpInterface;
		m_pInterface[1] = (void *)pDestroyVpInterface;

		if (m_pInterface[0] && m_pInterface[1])
			pCreateVpInterface(ppEncCtx, WELSVP_INTERFACE_VERION);
#ifndef NO_DYNAMIC_VP
	}
	else
	{
	}	
#endif

	return ppEncCtx ? 0 : 1;
}

int32_t CWelsLib::DestroyIface(void *pEncCtx)
{
	if (pEncCtx)
	{
		pfnDestroyVpInterface pDestroyVpInterface = (pfnDestroyVpInterface) m_pInterface[1];
		if (pDestroyVpInterface)
		{
			pDestroyVpInterface(pEncCtx, WELSVP_INTERFACE_VERION);
		}
		else
		{
		}
	}

	return 0;
}

/***************************************************************************
*	
*	implement of the interface
*	
***************************************************************************/

CWelsPreProcess::CWelsPreProcess(void *pEncCtx)
{
	m_pInterfaceVp = NULL;
	m_pEncLib = NULL;
	m_bInitDone = false;
	m_bOfficialBranch  = FALSE;
	m_pEncCtx = pEncCtx;
	memset(&m_sScaledPicture, 0, sizeof(m_sScaledPicture));	
}

CWelsPreProcess::~CWelsPreProcess()
{
	FreeScaledPic(&m_sScaledPicture, static_cast<sWelsEncCtx *>(m_pEncCtx)->pMemAlign);
	WelsPreprocessDestroy();
}

int32_t CWelsPreProcess::WelsPreprocessCreate()
{
	if (m_pEncLib == NULL && m_pInterfaceVp == NULL)
	{
		m_pEncLib  = new CWelsLib(m_pEncCtx);
		if (!m_pEncLib)
			goto exit;

		m_pEncLib->CreateIface((void **)&m_pInterfaceVp);
		if (!m_pInterfaceVp)
			goto exit;
	}
	else 
		goto exit;

	return 0;

exit:
	WelsPreprocessDestroy();
	return 1;
}

int32_t CWelsPreProcess::WelsPreprocessDestroy()
{
	if (m_pEncLib)
	{	
		m_pEncLib->DestroyIface((void *)m_pInterfaceVp);
		m_pInterfaceVp = NULL;
		WelsSafeDelete(m_pEncLib);
	}

	return 0;
}

int32_t CWelsPreProcess::WelsPreprocessReset ( void *pCtx )
{
	sWelsEncCtx *pEncCtx = (sWelsEncCtx *)pCtx;
	int32_t iRet = -1;

	if (pEncCtx)
	{
		FreeScaledPic(&m_sScaledPicture, pEncCtx->pMemAlign);
        iRet = InitLastSpatialPictures(pEncCtx);
		iRet = WelsInitScaledPic(pEncCtx->pSvcParam, &m_sScaledPicture, pEncCtx->pMemAlign);
	}

	return iRet;
}

int32_t CWelsPreProcess::WelsPreprocessStep1( void *pCtx, const SSourcePicture **kppSrcPicList, const int32_t kiConfiguredLayerNum )
{
	sWelsEncCtx *pEncCtx = (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pSvcParam = pEncCtx->pSvcParam;
	int32_t	iNumDependencyLayer = (int32_t)pSvcParam->iNumDependencyLayer;
	int32_t iSpatialNum = 0;

	if (!m_bInitDone)
	{
		if (WelsPreprocessCreate() != 0)
			return -1;
		if (WelsPreprocessReset(pEncCtx) != 0)
			return -1;	

		m_bOfficialBranch  = (iNumDependencyLayer != kiConfiguredLayerNum);
		if ( !m_bOfficialBranch && (iNumDependencyLayer == 1) ) 
		{
			// check the input source uiSize to decide if need switch to official branch 
			// NOTICE: the layernum=1 case is confused in official/non-official cases!
			SSourcePicture **pic_queue = (SSourcePicture **)kppSrcPicList;
			for (int32_t i=0; i<iNumDependencyLayer; i++)
			{			
				if ( pSvcParam->sDependencyLayers[i].iFrameWidth != pic_queue[i]->iPicWidth ||
					pSvcParam->sDependencyLayers[i].iFrameHeight != pic_queue[i]->iPicHeight )
				{
					m_bOfficialBranch = TRUE;
					break;
				}		
			}		
		}
		m_bInitDone = TRUE;
	}

	if (m_pInterfaceVp == NULL)
		return -1;

	if ( kiConfiguredLayerNum <= 0 )
		return -1;	

    pEncCtx->pVaa->bSceneChangeFlag = pEncCtx->pVaa->bIdrPeriodFlag = false;
	if( pSvcParam->uiIntraPeriod )
		pEncCtx->pVaa->bIdrPeriodFlag = ( 1 + pEncCtx->iFrameIndex >= (int32_t)pSvcParam->uiIntraPeriod ) ? true : false;		

	if ( m_bOfficialBranch )	// Perform Down Sampling potentially due to application
	{
		assert( kiConfiguredLayerNum == 1 );
		iSpatialNum	= SingleLayerPreprocess( pEncCtx, kppSrcPicList[0], &m_sScaledPicture );
	}
	else // for console each spatial pictures are available there
	{
		iSpatialNum	= kiConfiguredLayerNum;
		MultiLayerPreprocess( pEncCtx, kppSrcPicList, iSpatialNum );	
	}

	return iSpatialNum;
}

int32_t CWelsPreProcess::WelsPreprocessStep3( void *pCtx, const int32_t kiDidx )
{
	sWelsEncCtx *pEncCtx = (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pSvcParam = pEncCtx->pSvcParam;	
	bool_t bNeededMbAq = (pSvcParam->bEnableAdaptiveQuant && (pEncCtx->eSliceType == P_SLICE));
	bool_t bCalculateBGD = (pEncCtx->eSliceType == P_SLICE && pSvcParam->bEnableBackgroundDetection);
		
	int32_t iCurTemporalIdx  = pEncCtx->uiSpatialLayersInTemporal[kiDidx] - 1;

	int32_t iRefTemporalIdx = (int32_t)g_kuiRefTemporalIdx[pSvcParam->iDecompStages][pEncCtx->iCodingIndex & (pSvcParam->uiGopSize-1)];
	if ( pEncCtx->uiTemporalId == 0 && pEncCtx->pLtr[pEncCtx->uiDependencyId].bReceivedT0LostFlag )	
		iRefTemporalIdx = pEncCtx->uiSpatialLayersInTemporal[kiDidx] + pEncCtx->pVaa->uiValidLongTermPicIdx;

	SPicture *pCurPic = pEncCtx->pSpatialPic[kiDidx][iCurTemporalIdx];
	SPicture *pRefPic = pEncCtx->pSpatialPic[kiDidx][iRefTemporalIdx];	
	{			
		SPicture *pLastPic= m_pLastSpatialPicture[kiDidx][0];
		bool_t bCalculateSQDiff = ((pLastPic->pData[0] == pRefPic->pData[0]) && bNeededMbAq);
		bool_t bCalculateVar = (pSvcParam->iRCMode == RC_MODE1 && pEncCtx->eSliceType == I_SLICE);

		VaaCalculation( pEncCtx->pVaa, pCurPic, pRefPic, bCalculateSQDiff, bCalculateVar, bCalculateBGD);
	}

	if (pSvcParam->bEnableBackgroundDetection)
	{
		BackgroundDetection(pEncCtx->pVaa, pCurPic, pRefPic, bCalculateBGD && pRefPic->iPictureType != I_SLICE);
	}

	if ( bNeededMbAq )
	{
		SPicture *pCurPic = m_pLastSpatialPicture[kiDidx][1];
		SPicture *pRefPic = m_pLastSpatialPicture[kiDidx][0];

		AdaptiveQuantCalculation( pEncCtx->pVaa, pCurPic, pRefPic );           
	}	

	if ( pSvcParam->bEnableRc )
	{
		AnalyzePictureComplexity( pEncCtx, pCurPic, pRefPic, kiDidx, bCalculateBGD );	
	}

	WelsExchangeSpatialPictures( &m_pLastSpatialPicture[kiDidx][1], &m_pLastSpatialPicture[kiDidx][0] );

	return 0;
}


/*
*	SingleLayerPreprocess: down sampling if applicable
*  @return:	exact number of spatial layers need to encoder indeed
*/
int32_t CWelsPreProcess::SingleLayerPreprocess( void *pCtx, const SSourcePicture *kpSrc, Scaled_Picture * pScaledPicture )
{
	sWelsEncCtx *pEncCtx = (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pSvcParam    = pEncCtx->pSvcParam;	
	int8_t	iDependencyId			= pSvcParam->iNumDependencyLayer - 1;
	int32_t iPicturePos	                    = pEncCtx->uiSpatialLayersInTemporal[iDependencyId] - 1;

	SPicture *pSrcPic					= NULL;	// large
	SPicture *pDstPic					= NULL;	// small
	SDLayerParam *pDlayerParam					= NULL;
	int32_t iSpatialNum					= 0;
	int32_t iSrcWidth					= 0;
	int32_t iSrcHeight					= 0;
	int32_t iTargetWidth					= 0;
	int32_t iTargetHeight					= 0;		
	int32_t iTemporalId = 0;
	int32_t iActualSpatialLayerNum      = 0;

	pDlayerParam = &pSvcParam->sDependencyLayers[iDependencyId];
	iTargetWidth	  = pDlayerParam->iFrameWidth;
	iTargetHeight  = pDlayerParam->iFrameHeight;	
	iTemporalId    = pDlayerParam->uiCodingIdx2TemporalId[pEncCtx->iCodingIndex & (pSvcParam->uiGopSize-1)];	
	iSrcWidth   = pSvcParam->SUsedPicRect.iWidth;
	iSrcHeight  = pSvcParam->SUsedPicRect.iHeight;
	
	pSrcPic = pScaledPicture->pScaledInputPicture ? pScaledPicture->pScaledInputPicture : pEncCtx->pSpatialPic[iDependencyId][iPicturePos];

	WelsMoveMemoryWrapper( pSvcParam, pSrcPic, kpSrc, iSrcWidth, iSrcHeight );

	if( pSvcParam->bEnableDenoise )
		BilateralDenoising(pSrcPic, iSrcWidth, iSrcHeight);

	// different scaling in between input picture and dst highest spatial picture. 
	int32_t iShrinkWidth  = iSrcWidth;
	int32_t iShrinkHeight = iSrcHeight;
	pDstPic = pSrcPic;
	if ( pScaledPicture->pScaledInputPicture )
	{	
		// for highest downsampling				
		pDstPic		= pEncCtx->pSpatialPic[iDependencyId][iPicturePos];			
		iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId];
		iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId];			
	}
	DownsamplePadding(pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight);	

	if(pSvcParam->bEnableSceneChangeDetect && !pEncCtx->pVaa->bIdrPeriodFlag && !(pEncCtx->iCodingIndex & (pSvcParam->uiGopSize-1))){
		SPicture *pRefPic = pEncCtx->pLtr[iDependencyId].bReceivedT0LostFlag ? 
			pEncCtx->pSpatialPic[iDependencyId][pEncCtx->uiSpatialLayersInTemporal[iDependencyId] + pEncCtx->pVaa->uiValidLongTermPicIdx] : m_pLastSpatialPicture[iDependencyId][0];

		pEncCtx->pVaa->bSceneChangeFlag = DetectSceneChange(pDstPic, pRefPic);		
	}

	for( int32_t i=0;i<pSvcParam->iNumDependencyLayer;i++ ){
		if( pSvcParam->sDependencyLayers[i].uiCodingIdx2TemporalId[pEncCtx->iCodingIndex & (pSvcParam->uiGopSize-1)]
			!= INVALID_TEMPORAL_ID ){
			++ iActualSpatialLayerNum;
		}
	}

	if ( iTemporalId != INVALID_TEMPORAL_ID )
	{
		WelsUpdateSpatialIdxMap(pEncCtx, iActualSpatialLayerNum - 1, pDstPic, iDependencyId);	
		++ iSpatialNum;
		-- iActualSpatialLayerNum;
	}	

	m_pLastSpatialPicture[iDependencyId][1]	= pEncCtx->pSpatialPic[iDependencyId][iPicturePos];	
	-- iDependencyId;

	// generate other spacial layer
	// pSrc is 
	//	-- padded input pic, if downsample should be applied to generate highest layer, [if] block above
	//	-- highest layer, if no downsampling, [else] block above
	if ( pSvcParam->iNumDependencyLayer > 1 )
	{
		while (iDependencyId >= 0) 
		{
			pDlayerParam			= &pSvcParam->sDependencyLayers[iDependencyId];
			iTargetWidth	= pDlayerParam->iFrameWidth;
			iTargetHeight	= pDlayerParam->iFrameHeight;					
			iTemporalId = pDlayerParam->uiCodingIdx2TemporalId[pEncCtx->iCodingIndex & (pSvcParam->uiGopSize-1)];
			iPicturePos		= pEncCtx->uiSpatialLayersInTemporal[iDependencyId] - 1;

			// NOT work for CGS, FIXME
			// spatial layer is able to encode indeed
			if ( (iTemporalId != INVALID_TEMPORAL_ID) )
			{ 
				// down sampling performed
				if( NULL == pSrcPic )
					return -1;

				pDstPic	= pEncCtx->pSpatialPic[iDependencyId][iPicturePos];	// small
				iShrinkWidth = pScaledPicture->iScaledWidth[iDependencyId];
				iShrinkHeight = pScaledPicture->iScaledHeight[iDependencyId];
				DownsamplePadding(pSrcPic, pDstPic, iSrcWidth, iSrcHeight, iShrinkWidth, iShrinkHeight, iTargetWidth, iTargetHeight);

				WelsUpdateSpatialIdxMap(pEncCtx, iActualSpatialLayerNum - 1, pDstPic, iDependencyId);				

				-- iActualSpatialLayerNum;
				++ iSpatialNum;				

				m_pLastSpatialPicture[iDependencyId][1]	= pEncCtx->pSpatialPic[iDependencyId][iPicturePos];	
			}
			-- iDependencyId;
		}		
	}

	return iSpatialNum;
}

int32_t CWelsPreProcess::MultiLayerPreprocess( void *pCtx, const SSourcePicture **kppSrcPicList, const int32_t kiSpatialNum )
{
	sWelsEncCtx *pEncCtx = (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pSvcParam	= pEncCtx->pSvcParam;		
	const SSourcePicture *pSrc			= NULL;
	SPicture *pDstPic						= NULL;
	const int32_t iSpatialLayersCfgCount = pSvcParam->iNumDependencyLayer;	// count number of spatial layers to be encoded in cfg
	int32_t i							= 0;
	int32_t j							= -1;

	do {
		pSrc	= kppSrcPicList[i];

		// do not clear j, just let it continue to save complexity
		do {
			++ j;
			if ( pSvcParam->sDependencyLayers[j].iFrameWidth == pSrc->iPicWidth &&
				pSvcParam->sDependencyLayers[j].iFrameHeight== pSrc->iPicHeight )
			{
				break;
			}			
		} while( j < iSpatialLayersCfgCount );

		assert( j < iSpatialLayersCfgCount );
		pDstPic = pEncCtx->pSpatialPic[j][pEncCtx->uiSpatialLayersInTemporal[j]-1];
		
		WelsUpdateSpatialIdxMap(pEncCtx, i, pDstPic, j);		

		WelsMoveMemoryWrapper( pSvcParam, pDstPic, pSrc, pSrc->iPicWidth, pSrc->iPicHeight );

		if(pSvcParam->bEnableDenoise)
			BilateralDenoising(pDstPic, pSrc->iPicWidth, pSrc->iPicHeight);

		m_pLastSpatialPicture[j][1]	= pDstPic;
		++ i;
	} while( i < kiSpatialNum );

	if( pSvcParam->bEnableSceneChangeDetect && (kiSpatialNum == pSvcParam->iNumDependencyLayer) && !pEncCtx->pVaa->bIdrPeriodFlag )
	{
		SPicture *pRef = pEncCtx->pLtr[0].bReceivedT0LostFlag ? 
			pEncCtx->pSpatialPic[0][pEncCtx->uiSpatialLayersInTemporal[0] + pEncCtx->pVaa->uiValidLongTermPicIdx] : m_pLastSpatialPicture[0][0];

		pEncCtx->pVaa->bSceneChangeFlag = DetectSceneChange(pDstPic, pRef);
	}

	return 0;
}

/*!
 * \brief	Whether input picture need be scaled?	
 */
bool_t JudgeNeedOfScaling( SWelsSvcCodingParam *pParam, Scaled_Picture * pScaledPicture )
{
	const int32_t kiInputPicWidth	= pParam->SUsedPicRect.iWidth;
	const int32_t kiInputPicHeight = pParam->SUsedPicRect.iHeight;
	const int32_t kiDstPicWidth		= pParam->sDependencyLayers[pParam->iNumDependencyLayer-1].iActualWidth;
	const int32_t kiDstPicHeight	= pParam->sDependencyLayers[pParam->iNumDependencyLayer-1].iActualHeight;
	bool_t bNeedDownsampling = true;

	int32_t iSpatialIdx = pParam->iNumDependencyLayer-1;

	if ( kiDstPicWidth >= kiInputPicWidth && kiDstPicHeight >= kiInputPicHeight )
	{
		iSpatialIdx --;  // highest D layer do not need downsampling
		bNeedDownsampling = false;
	}

	for(; iSpatialIdx >= 0; iSpatialIdx --)
	{
		SDLayerParam *pCurLayer = &pParam->sDependencyLayers[iSpatialIdx];
		int32_t iCurDstWidth			= pCurLayer->iActualWidth; 
		int32_t iCurDstHeight			= pCurLayer->iActualHeight;
		int32_t iInputWidthXDstHeight	= kiInputPicWidth * iCurDstHeight;
		int32_t iInputHeightXDstWidth	= kiInputPicHeight * iCurDstWidth;

		if (iInputWidthXDstHeight > iInputHeightXDstWidth)
		{
			pScaledPicture->iScaledWidth[iSpatialIdx] = iCurDstWidth;
			pScaledPicture->iScaledHeight[iSpatialIdx] = iInputHeightXDstWidth / kiInputPicWidth;
		}else {
			pScaledPicture->iScaledWidth[iSpatialIdx] = iInputWidthXDstHeight / kiInputPicHeight;
			pScaledPicture->iScaledHeight[iSpatialIdx] = iCurDstHeight;
		}
	}

	return bNeedDownsampling;
}

int32_t  WelsInitScaledPic( SWelsSvcCodingParam *pParam,  Scaled_Picture  *pScaledPicture, CMemoryAlign *pMemoryAlign )
{
	bool_t bInputPicNeedScaling = JudgeNeedOfScaling( pParam, pScaledPicture );
    if( bInputPicNeedScaling )
    {
        pScaledPicture->pScaledInputPicture = AllocPicture(pMemoryAlign, pParam->SUsedPicRect.iWidth, pParam->SUsedPicRect.iHeight, false);
        if( pScaledPicture->pScaledInputPicture == NULL )           
            return -1;
    }
    return 0;
}

void  FreeScaledPic(Scaled_Picture  *pScaledPicture, CMemoryAlign *pMemoryAlign)
{
	if ( pScaledPicture->pScaledInputPicture )
	{
		FreePicture( pMemoryAlign, &pScaledPicture->pScaledInputPicture );	
		pScaledPicture->pScaledInputPicture = NULL;
	}			
}

int32_t CWelsPreProcess::InitLastSpatialPictures( void *pCtx )
{
	sWelsEncCtx *pEncCtx         = (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pParam	= pEncCtx->pSvcParam;
	const int32_t kiDlayerCount			= pParam->iNumDependencyLayer;
	int32_t iDlayerIndex					= 0;

	for (; iDlayerIndex<kiDlayerCount; iDlayerIndex++)
	{
		const int32_t kiLayerInTemporal = pEncCtx->uiSpatialLayersInTemporal[iDlayerIndex];
		m_pLastSpatialPicture[iDlayerIndex][0]	= pEncCtx->pSpatialPic[iDlayerIndex][kiLayerInTemporal - 2];
		m_pLastSpatialPicture[iDlayerIndex][1]	= NULL;
	}
	for (; iDlayerIndex<MAX_DEPENDENCY_LAYER; iDlayerIndex++)
	{
		m_pLastSpatialPicture[iDlayerIndex][0]	= m_pLastSpatialPicture[iDlayerIndex][1] = NULL;
	}

	return 0;
}
//*********************************************************************************************************/

int32_t CWelsPreProcess::ColorspaceConvert(SWelsSvcCodingParam * pSvcParam, SPicture *pDstPic, const SSourcePicture *kpSrc, const int32_t kiWidth, const int32_t kiHeight )
{
	return 1;
	//not support yet
}

void CWelsPreProcess::BilateralDenoising ( SPicture *pSrc, const int32_t kiWidth, const int32_t kiHeight )
{
	int32_t iMethodIdx = METHOD_DENOISE;
	SPixMap sSrcPixMap = {0};

	sSrcPixMap.pPixel[0] = pSrc->pData[0];
	sSrcPixMap.pPixel[1] = pSrc->pData[1];
	sSrcPixMap.pPixel[2] = pSrc->pData[2];
	sSrcPixMap.iSizeInBits = g_kiPixMapSizeInBits;
	sSrcPixMap.sRect.iRectWidth = kiWidth;
	sSrcPixMap.sRect.iRectHeight = kiHeight;
	sSrcPixMap.iStride[0] = pSrc->iLineSize[0];
	sSrcPixMap.iStride[1] = pSrc->iLineSize[1];
	sSrcPixMap.iStride[2] = pSrc->iLineSize[2];
	sSrcPixMap.eFormat = VIDEO_FORMAT_I420;

	m_pInterfaceVp->Process(iMethodIdx, &sSrcPixMap, NULL);
}

bool_t CWelsPreProcess::DetectSceneChange( SPicture *pCurPicture, SPicture *pRefPicture )
{
	bool_t bSceneChangeFlag = false;
	int32_t iMethodIdx = METHOD_SCENE_CHANGE_DETECTION;
	SSceneChangeResult sSceneChangeDetectResult = {0};
	SPixMap sSrcPixMap = {0};
	SPixMap sRefPixMap = {0};

	sSrcPixMap.pPixel[0] = pCurPicture->pData[0];
	sSrcPixMap.iSizeInBits = g_kiPixMapSizeInBits;
	sSrcPixMap.iStride[0] = pCurPicture->iLineSize[0];
	sSrcPixMap.sRect.iRectWidth = pCurPicture->iWidthInPixel;
	sSrcPixMap.sRect.iRectHeight = pCurPicture->iHeightInPixel;
	sSrcPixMap.eFormat = VIDEO_FORMAT_I420;


	sRefPixMap.pPixel[0] = pRefPicture->pData[0]; 
	sRefPixMap.iSizeInBits = g_kiPixMapSizeInBits;
	sRefPixMap.iStride[0] = pRefPicture->iLineSize[0];
	sRefPixMap.sRect.iRectWidth = pRefPicture->iWidthInPixel;
	sRefPixMap.sRect.iRectHeight = pRefPicture->iHeightInPixel;
	sRefPixMap.eFormat = VIDEO_FORMAT_I420;

	int32_t iRet = m_pInterfaceVp->Process(iMethodIdx, &sSrcPixMap, &sRefPixMap);
	if (iRet == 0)
	{
		m_pInterfaceVp->Get(iMethodIdx, (void*)&sSceneChangeDetectResult);
		bSceneChangeFlag = sSceneChangeDetectResult.bSceneChangeFlag ? true : false;
	}

	return bSceneChangeFlag;
}

int32_t CWelsPreProcess::DownsamplePadding( SPicture *pSrc, SPicture *pDstPic,  int32_t iSrcWidth, int32_t iSrcHeight,
											int32_t iShrinkWidth, int32_t iShrinkHeight, int32_t iTargetWidth, int32_t iTargetHeight )
{
	int32_t iRet = 0;
	SPixMap sSrcPixMap = {0};
	SPixMap sDstPicMap = {0};

	sSrcPixMap.pPixel[0]   = pSrc->pData[0];
	sSrcPixMap.pPixel[1]   = pSrc->pData[1];
	sSrcPixMap.pPixel[2]   = pSrc->pData[2];
	sSrcPixMap.iSizeInBits = g_kiPixMapSizeInBits;
	sSrcPixMap.sRect.iRectWidth  = iSrcWidth;
	sSrcPixMap.sRect.iRectHeight = iSrcHeight;
	sSrcPixMap.iStride[0]  = pSrc->iLineSize[0];
	sSrcPixMap.iStride[1]  = pSrc->iLineSize[1];
	sSrcPixMap.iStride[2]  = pSrc->iLineSize[2];
	sSrcPixMap.eFormat     = VIDEO_FORMAT_I420;	

	if (iSrcWidth != iShrinkWidth || iSrcHeight != iShrinkHeight)
	{
		int32_t iMethodIdx = METHOD_DOWNSAMPLE;
		sDstPicMap.pPixel[0]   = pDstPic->pData[0];
		sDstPicMap.pPixel[1]   = pDstPic->pData[1];
		sDstPicMap.pPixel[2]   = pDstPic->pData[2];
		sDstPicMap.iSizeInBits = g_kiPixMapSizeInBits;
		sDstPicMap.sRect.iRectWidth  = iShrinkWidth;
		sDstPicMap.sRect.iRectHeight = iShrinkHeight;
		sDstPicMap.iStride[0]  = pDstPic->iLineSize[0];
		sDstPicMap.iStride[1]  = pDstPic->iLineSize[1];
		sDstPicMap.iStride[2]  = pDstPic->iLineSize[2];
		sDstPicMap.eFormat     = VIDEO_FORMAT_I420;

		iRet = m_pInterfaceVp->Process(iMethodIdx, &sSrcPixMap, &sDstPicMap);
	}	
	else
	{
        memcpy(&sDstPicMap, &sSrcPixMap, sizeof(sDstPicMap));	// confirmed_safe_unsafe_usage
	}

	// get rid of odd line
	iShrinkWidth -= (iShrinkWidth & 1);
	iShrinkHeight -= (iShrinkHeight & 1);
	Padding( (uint8_t *)sDstPicMap.pPixel[0], (uint8_t *)sDstPicMap.pPixel[1], (uint8_t *)sDstPicMap.pPixel[2], 
		sDstPicMap.iStride[0], sDstPicMap.iStride[1],	iShrinkWidth, iTargetWidth, iShrinkHeight, iTargetHeight);

	return iRet;
}

//*********************************************************************************************************/
void CWelsPreProcess::VaaCalculation(SVAAFrameInfo *pVaaInfo, SPicture *pCurPicture, SPicture *pRefPicture,
                                     bool_t bCalculateSQDiff, bool_t bCalculateVar, bool_t bCalculateBGD)
{
	pVaaInfo->sVaaCalcInfo.pCurY = pCurPicture->pData[0];
	pVaaInfo->sVaaCalcInfo.pRefY = pRefPicture->pData[0];
	{
		int32_t iMethodIdx = METHOD_VAA_STATISTICS;
		SPixMap sCurPixMap = {0};
		SPixMap sRefPixMap = {0};
		SVAACalcParam calc_param = {0};

		sCurPixMap.pPixel[0] = pCurPicture->pData[0];
		sCurPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sCurPixMap.sRect.iRectWidth = pCurPicture->iWidthInPixel;
		sCurPixMap.sRect.iRectHeight = pCurPicture->iHeightInPixel;
		sCurPixMap.iStride[0] = pCurPicture->iLineSize[0];
		sCurPixMap.eFormat = VIDEO_FORMAT_I420;

		sRefPixMap.pPixel[0] = pRefPicture->pData[0];
		sRefPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sRefPixMap.sRect.iRectWidth = pRefPicture->iWidthInPixel;
		sRefPixMap.sRect.iRectHeight = pRefPicture->iHeightInPixel;
		sRefPixMap.iStride[0] = pRefPicture->iLineSize[0];
		sRefPixMap.eFormat = VIDEO_FORMAT_I420;

		calc_param.iCalcVar	= bCalculateVar;
		calc_param.iCalcBgd	= bCalculateBGD;
		calc_param.iCalcSsd	= bCalculateSQDiff;
		calc_param.pCalcResult = &pVaaInfo->sVaaCalcInfo;

		m_pInterfaceVp->Set(iMethodIdx, &calc_param);
		m_pInterfaceVp->Process(iMethodIdx, &sCurPixMap, &sRefPixMap);
	} 
}

void CWelsPreProcess::BackgroundDetection( SVAAFrameInfo *pVaaInfo, SPicture *pCurPicture, SPicture *pRefPicture, bool_t bDetectFlag )
{
	if (bDetectFlag)
	{
		pVaaInfo->iPicWidth     = pCurPicture->iWidthInPixel;
		pVaaInfo->iPicHeight    = pCurPicture->iHeightInPixel;

		pVaaInfo->iPicStride	= pCurPicture->iLineSize[0];
		pVaaInfo->iPicStrideUV	= pCurPicture->iLineSize[1];
		pVaaInfo->pCurY			= pCurPicture->pData[0];
		pVaaInfo->pRefY			= pRefPicture->pData[0];	
		pVaaInfo->pCurU			= pCurPicture->pData[1];
		pVaaInfo->pRefU			= pRefPicture->pData[1];	
		pVaaInfo->pCurV			= pCurPicture->pData[2];
		pVaaInfo->pRefV			= pRefPicture->pData[2];	

		int32_t iMethodIdx = METHOD_BACKGROUND_DETECTION;
		SPixMap sSrcPixMap = {0};
		SPixMap sRefPixMap = {0};
		SBGDInterface BGDParam = {0};

		sSrcPixMap.pPixel[0] = pCurPicture->pData[0];
		sSrcPixMap.pPixel[1] = pCurPicture->pData[1];
		sSrcPixMap.pPixel[2] = pCurPicture->pData[2];
		sSrcPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sSrcPixMap.iStride[0] = pCurPicture->iLineSize[0];
		sSrcPixMap.iStride[1] = pCurPicture->iLineSize[1];
		sSrcPixMap.iStride[2] = pCurPicture->iLineSize[2];
		sSrcPixMap.sRect.iRectWidth = pCurPicture->iWidthInPixel;
		sSrcPixMap.sRect.iRectHeight = pCurPicture->iHeightInPixel;
		sSrcPixMap.eFormat = VIDEO_FORMAT_I420;

		sRefPixMap.pPixel[0] = pRefPicture->pData[0];
		sRefPixMap.pPixel[1] = pRefPicture->pData[1];
		sRefPixMap.pPixel[2] = pRefPicture->pData[2];
		sRefPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sRefPixMap.iStride[0] = pRefPicture->iLineSize[0];
		sRefPixMap.iStride[1] = pRefPicture->iLineSize[1];
		sRefPixMap.iStride[2] = pRefPicture->iLineSize[2];
		sRefPixMap.sRect.iRectWidth = pRefPicture->iWidthInPixel;
		sRefPixMap.sRect.iRectHeight = pRefPicture->iHeightInPixel;
		sRefPixMap.eFormat = VIDEO_FORMAT_I420;

		BGDParam.pBackgroundMbFlag = pVaaInfo->pVaaBackgroundMbFlag;
		BGDParam.pCalcRes = &(pVaaInfo->sVaaCalcInfo);
		m_pInterfaceVp->Set(iMethodIdx, (void*)&BGDParam);
		m_pInterfaceVp->Process(iMethodIdx, &sSrcPixMap, &sRefPixMap);
	} 
	else
	{
		int32_t	iPicWidthInMb	= (pCurPicture->iWidthInPixel + 15) >> 4;
		int32_t	iPicHeightInMb= (pCurPicture->iHeightInPixel+ 15) >> 4;
		memset(pVaaInfo->pVaaBackgroundMbFlag, 0, iPicWidthInMb * iPicHeightInMb);
	}
}

void CWelsPreProcess::AdaptiveQuantCalculation( SVAAFrameInfo *pVaaInfo, SPicture *pCurPicture, SPicture *pRefPicture )
{
	pVaaInfo->sAdaptiveQuantParam.pCalcResult = &(pVaaInfo->sVaaCalcInfo); 
	pVaaInfo->sAdaptiveQuantParam.dAverMotionTextureIndexToDeltaQp = 0;

	{
		int32_t iMethodIdx = METHOD_ADAPTIVE_QUANT;
		SPixMap pSrc = {0};
		SPixMap pRef = {0};
		int32_t iRet = 0;

		pSrc.pPixel[0] = pCurPicture->pData[0];
		pSrc.iSizeInBits = g_kiPixMapSizeInBits;
		pSrc.iStride[0] = pCurPicture->iLineSize[0];
		pSrc.sRect.iRectWidth = pCurPicture->iWidthInPixel;
		pSrc.sRect.iRectHeight = pCurPicture->iHeightInPixel;
		pSrc.eFormat = VIDEO_FORMAT_I420;

		pRef.pPixel[0] = pRefPicture->pData[0]; 
		pRef.iSizeInBits = g_kiPixMapSizeInBits;
		pRef.iStride[0] = pRefPicture->iLineSize[0];
		pRef.sRect.iRectWidth = pRefPicture->iWidthInPixel;
		pRef.sRect.iRectHeight = pRefPicture->iHeightInPixel;
		pRef.eFormat = VIDEO_FORMAT_I420;

		iRet = m_pInterfaceVp->Set(iMethodIdx, (void*)&(pVaaInfo->sAdaptiveQuantParam));
		iRet = m_pInterfaceVp->Process(iMethodIdx, &pSrc, &pRef);
		if (iRet == 0)
			m_pInterfaceVp->Get(iMethodIdx, (void*)&(pVaaInfo->sAdaptiveQuantParam));
	}
}

void CWelsPreProcess::SetRefMbType( void *pCtx, uint32_t **pRefMbTypeArray, int32_t iRefPicType )
{
  sWelsEncCtx *pEncCtx	    = (sWelsEncCtx *)pCtx;
  const uint8_t uiTid		    = pEncCtx->uiTemporalId;	
  const uint8_t uiDid          = pEncCtx->uiDependencyId;
  SRefList *pRefPicLlist				= pEncCtx->ppRefPicListExt[uiDid];	
  SLTRState* pLtr				= &pEncCtx->pLtr[uiDid];
  uint8_t i							= 0;

  if (pEncCtx->pSvcParam->bEnableLongTermReference && pLtr->bReceivedT0LostFlag && uiTid == 0)
  {
    for ( i = 0;i <pRefPicLlist->uiLongRefCount;i++ )	
    {
      SPicture *pRef = pRefPicLlist->pLongRefList[i];
      if ( pRef != NULL && pRef->uiRecieveConfirmed == 1/*RECIEVE_SUCCESS*/)	
      {
        *pRefMbTypeArray = pRef->uiRefMbType;
        break;
      }
    }
  }
  else
  {
    for ( i = 0; i < pRefPicLlist->uiShortRefCount; i++ )
    {
      SPicture *pRef = pRefPicLlist->pShortRefList[i];
      if ( pRef != NULL && pRef->bUsedAsRef && pRef->iFramePoc >= 0 && pRef->uiTemporalId <= uiTid)
      {		
        *pRefMbTypeArray = pRef->uiRefMbType;
        break;	
      }
    }
  }
}


void CWelsPreProcess::AnalyzePictureComplexity( void *pCtx, SPicture *pCurPicture, SPicture *pRefPicture, const int32_t kiDependencyId, const bool_t bCalculateBGD )
{
	sWelsEncCtx *pEncCtx	= (sWelsEncCtx *)pCtx;
	SWelsSvcCodingParam *pSvcParam= pEncCtx->pSvcParam;
	SVAAFrameInfo *pVaaInfo			= pEncCtx->pVaa;

	SComplexityAnalysisParam *sComplexityAnalysisParam = &(pVaaInfo->sComplexityAnalysisParam);
	SWelsSvcRc *SWelsSvcRc = &pEncCtx->pWelsSvcRc[kiDependencyId];
	int32_t iComplexityAnalysisMode = 0;

	if( pSvcParam->iRCMode == RC_MODE0 && pEncCtx->eSliceType == P_SLICE )
	{
		iComplexityAnalysisMode = FRAME_SAD;
	}
	else if ( pSvcParam->iRCMode == RC_MODE1 && pEncCtx->eSliceType == P_SLICE )
	{
		iComplexityAnalysisMode = GOM_SAD;
	}
	else if ( pSvcParam->iRCMode == RC_MODE1 && pEncCtx->eSliceType == I_SLICE )
	{
		iComplexityAnalysisMode = GOM_VAR;
	}
	else
	{
		return;
	}

	sComplexityAnalysisParam->iComplexityAnalysisMode = iComplexityAnalysisMode;
	sComplexityAnalysisParam->pCalcResult = &(pVaaInfo->sVaaCalcInfo); 
	sComplexityAnalysisParam->pBackgroundMbFlag = pVaaInfo->pVaaBackgroundMbFlag;
    SetRefMbType(pEncCtx, &(sComplexityAnalysisParam->uiRefMbType), pRefPicture->iPictureType);
	sComplexityAnalysisParam->iCalcBgd = bCalculateBGD; 
	sComplexityAnalysisParam->iFrameComplexity = 0;

	memset(SWelsSvcRc->pGomForegroundBlockNum, 0, SWelsSvcRc->iGomSize*sizeof(int32_t));
	if ( iComplexityAnalysisMode != FRAME_SAD )
		memset( SWelsSvcRc->pCurrentFrameGomSad, 0, SWelsSvcRc->iGomSize*sizeof(int32_t) );

	sComplexityAnalysisParam->pGomComplexity = SWelsSvcRc->pCurrentFrameGomSad;
	sComplexityAnalysisParam->pGomForegroundBlockNum = SWelsSvcRc->pGomForegroundBlockNum;
	sComplexityAnalysisParam->iMbNumInGom = SWelsSvcRc->iNumberMbGom;

	{
		int32_t iMethodIdx = METHOD_COMPLEXITY_ANALYSIS;
		SPixMap sSrcPixMap = {0};
		SPixMap sRefPixMap = {0};
		int32_t iRet = 0;

		sSrcPixMap.pPixel[0] = pCurPicture->pData[0];
		sSrcPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sSrcPixMap.iStride[0] = pCurPicture->iLineSize[0];
		sSrcPixMap.sRect.iRectWidth = pCurPicture->iWidthInPixel;
		sSrcPixMap.sRect.iRectHeight = pCurPicture->iHeightInPixel;
		sSrcPixMap.eFormat = VIDEO_FORMAT_I420;

		sRefPixMap.pPixel[0] = pRefPicture->pData[0]; 
		sRefPixMap.iSizeInBits = g_kiPixMapSizeInBits;
		sRefPixMap.iStride[0] = pRefPicture->iLineSize[0];
		sRefPixMap.sRect.iRectWidth = pRefPicture->iWidthInPixel;
		sRefPixMap.sRect.iRectHeight = pRefPicture->iHeightInPixel;
		sRefPixMap.eFormat = VIDEO_FORMAT_I420;

		iRet = m_pInterfaceVp->Set(iMethodIdx, (void*)sComplexityAnalysisParam);
		iRet = m_pInterfaceVp->Process(iMethodIdx, &sSrcPixMap, &sRefPixMap);
		if (iRet == 0)
			m_pInterfaceVp->Get(iMethodIdx, (void*)sComplexityAnalysisParam);
	}
}

void  CWelsPreProcess::Padding(uint8_t * pSrcY, uint8_t * pSrcU, uint8_t * pSrcV, int32_t iStrideY, int32_t iStrideUV,
			  int32_t iActualWidth, int32_t iPaddingWidth, int32_t iActualHeight, int32_t iPaddingHeight)
{
	int32_t i;

	if( iPaddingHeight > iActualHeight ){
		for( i=iActualHeight;i<iPaddingHeight;i++ ){
			memset(pSrcY + i*iStrideY, 0, iActualWidth);			

			if( !(i&1) ){
				memset(pSrcU + i/2*iStrideUV, 0x80, iActualWidth/2);
				memset(pSrcV + i/2*iStrideUV, 0x80, iActualWidth/2);	
			}
		}		
	}

	if( iPaddingWidth > iActualWidth ){
		for( i=0;i<iPaddingHeight;i++ ){
			memset(pSrcY + i*iStrideY + iActualWidth, 0, iPaddingWidth - iActualWidth);
			if( !(i&1) ){
				memset(pSrcU + i/2*iStrideUV + iActualWidth/2, 0x80, (iPaddingWidth - iActualWidth)/2);
				memset(pSrcV + i/2*iStrideUV + iActualWidth/2, 0x80, (iPaddingWidth - iActualWidth)/2);
			}
		}        
	}
}


//TODO: may opti later
//TODO: not use this func?
void * WelsMemcpy( void *dst, const void *kpSrc, uint32_t uiSize)
{
	return ::memcpy(dst, kpSrc, uiSize);
}
void * WelsMemset( void * p, int32_t val, uint32_t uiSize)
{
	return ::memset(p, val, uiSize);
}

//i420_to_i420_c
void  WelsMoveMemory_c(uint8_t * pDstY, uint8_t * pDstU, uint8_t * pDstV,  int32_t iDstStrideY, int32_t iDstStrideUV,  
                               uint8_t * pSrcY, uint8_t * pSrcU, uint8_t * pSrcV, int32_t iSrcStrideY, int32_t iSrcStrideUV, int32_t iWidth, int32_t iHeight )
{
	int32_t   iWidth2 = iWidth >> 1;
	int32_t   iHeight2 = iHeight >> 1;
	int32_t   j;

	for( j=iHeight;j;j-- )
	{
		WelsMemcpy(pDstY, pSrcY, iWidth);
		pDstY += iDstStrideY;
		pSrcY += iSrcStrideY;
	}

	for( j=iHeight2;j;j-- )
	{
		WelsMemcpy(pDstU, pSrcU, iWidth2);
		WelsMemcpy(pDstV, pSrcV, iWidth2);
		pDstU += iDstStrideUV;
		pDstV += iDstStrideUV;
		pSrcU += iSrcStrideUV;
		pSrcV += iSrcStrideUV;
	}
}
//vp's padding
void  VPpadding(uint8_t * pSrcPtr, int32_t iCurWidth, int32_t iTargetWidth, int32_t iCurHeight, int32_t iTargetHeight, 
				int32_t iStride, uint8_t uiStuffValue)
{
	uint8_t *pTmp;	
	if( iTargetWidth > iCurWidth )
	{
		pTmp = pSrcPtr + iCurWidth;
		for( int32_t i = 0; i < iCurHeight; i++ )
		{
			WelsMemset(pTmp, uiStuffValue, iTargetWidth - iCurWidth);
			pTmp += iStride;
		}        
	}
	
	if( iTargetHeight > iCurHeight )
	{
		pTmp = pSrcPtr + iCurHeight * iStride;
		for( int32_t i = iCurHeight; i < iTargetHeight;i++ )
		{
			WelsMemset(pTmp, uiStuffValue, iTargetWidth);
			pTmp += iStride;
		}		
	}
}


void  CWelsPreProcess::WelsMoveMemoryWrapper(SWelsSvcCodingParam * pSvcParam, SPicture *pDstPic, const SSourcePicture *kpSrc, 
                                             const int32_t kiTargetWidth, const int32_t kiTargetHeight )
{
    if (VIDEO_FORMAT_I420!=(kpSrc->iColorFormat & (~VIDEO_FORMAT_VFlip)))
        return;

    int32_t  iSrcWidth       = kpSrc->iPicWidth;
    int32_t  iSrcHeight      = kpSrc->iPicHeight;

    if ( iSrcHeight > kiTargetHeight ) 	iSrcHeight = kiTargetHeight;
    if ( iSrcWidth > kiTargetWidth )		iSrcWidth  = kiTargetWidth;

    // copy from fr26 to fix the odd uiSize failed issue 
    if( iSrcWidth & 0x1 )		-- iSrcWidth;
    if( iSrcHeight & 0x1 )		-- iSrcHeight;	

    const int32_t kiSrcTopOffsetY = pSvcParam->SUsedPicRect.iTop;
    const int32_t kiSrcTopOffsetUV = (kiSrcTopOffsetY>>1);
    const int32_t kiSrcLeftOffsetY = pSvcParam->SUsedPicRect.iLeft;
    const int32_t kiSrcLeftOffsetUV = (kiSrcLeftOffsetY>>1);
    int32_t  iSrcOffset[3]       = {0,0,0};
    iSrcOffset[0] = kpSrc->iStride[0]*kiSrcTopOffsetY + kiSrcLeftOffsetY;
    iSrcOffset[1] = kpSrc->iStride[1]*kiSrcTopOffsetUV + kiSrcLeftOffsetUV ;
    iSrcOffset[2] = kpSrc->iStride[2]*kiSrcTopOffsetUV + kiSrcLeftOffsetUV;

    uint8_t * pSrcY = kpSrc->pData[0] + iSrcOffset[0];
    uint8_t * pSrcU = kpSrc->pData[1] + iSrcOffset[1];
    uint8_t * pSrcV = kpSrc->pData[2] + iSrcOffset[2];
    const int32_t kiSrcStrideY = kpSrc->iStride[0];
    const int32_t kiSrcStrideUV= kpSrc->iStride[1];
    
    uint8_t * pDstY = pDstPic->pData[0];
    uint8_t * pDstU = pDstPic->pData[1];
    uint8_t * pDstV = pDstPic->pData[2];
    const int32_t kiDstStrideY = pDstPic->iLineSize[0];
    const int32_t kiDstStrideUV = pDstPic->iLineSize[1];

#define MAX_WIDTH      (4096)
#define MAX_HEIGHT     (2304)//MAX_FS_LEVEL51 (36864); MAX_FS_LEVEL51*256/4096 = 2304
    if (pSrcY)
    {
        if (iSrcWidth <= 0 || iSrcWidth > MAX_WIDTH || iSrcHeight <= 0 || iSrcHeight > MAX_HEIGHT)
            return;
        if (kiSrcTopOffsetY >= iSrcHeight || kiSrcLeftOffsetY>= iSrcWidth || iSrcWidth > kiSrcStrideY )
            return;
    }
    if (pDstY)
    {
        if (kiTargetWidth <= 0 || kiTargetWidth > MAX_WIDTH || kiTargetHeight<= 0 || kiTargetHeight> MAX_HEIGHT)
            return;
        if (kiTargetWidth > kiDstStrideY)
            return;
    }

    if (pSrcY == NULL || pSrcU == NULL || pSrcV == NULL || pDstY == NULL || pDstU == NULL || pDstV == NULL
        || (iSrcWidth & 1) || (iSrcHeight & 1) )
    {}
    else
    { 
        //i420_to_i420_c
        WelsMoveMemory_c( pDstY,  pDstU,  pDstV,  kiDstStrideY, kiDstStrideUV,  
            pSrcY,  pSrcU,  pSrcV, kiSrcStrideY, kiSrcStrideUV, iSrcWidth, iSrcHeight );

        //in VP Process
        if ( kiTargetWidth > iSrcWidth || kiTargetHeight > iSrcHeight )
        {
            const int32_t kiTargetWidthC  = (kiTargetWidth>>1);
            const int32_t kiTargetHeightC = (kiTargetHeight>>1);
            const int32_t kiSrcWidthC        = (iSrcWidth>>1);
            const int32_t kiSrcHeightC       = (iSrcHeight>>1);

            // padding pDstPic I420
            VPpadding((uint8_t *)pDstY, iSrcWidth, kiTargetWidth, iSrcHeight, kiTargetHeight, kiDstStrideY, 0);
            VPpadding((uint8_t *)pDstU, kiSrcWidthC, kiTargetWidthC, kiSrcHeightC, kiTargetHeightC, kiDstStrideUV, 0x80);
            VPpadding((uint8_t *)pDstV, kiSrcWidthC, kiTargetWidthC, kiSrcHeightC, kiTargetHeightC, kiDstStrideUV, 0x80);
        }
    }

}

//*********************************************************************************************************/
} // namespace WelsSVCEnc