shithub: openh264

ref: 063709c92e8bafe467d384f564afbdc6b66eb39a
dir: /codec/encoder/core/src/slice_multi_threading.cpp/

View raw version
/*!
 * \copy
 *     Copyright (c)  2010-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	slice_multi_threading.h
 *
 * \brief	pSlice based multiple threading
 *
 * \date	04/16/2010 Created
 *
 *************************************************************************************
 */

#if defined(MT_ENABLED)

#include <assert.h>
#ifdef __GNUC__
#include <semaphore.h>
#ifndef SEM_NAME_MAX
// length of semaphore name should be system constrained at least on mac 10.7
#define  SEM_NAME_MAX 32
#endif//SEM_NAME_MAX
#endif//__GNUC__
#include "slice_multi_threading.h"
#include "mt_defs.h"
#include "nal_encap.h"
#include "utils.h"
#include "encoder.h"
#include "svc_encode_slice.h"
#include "deblocking.h"
#include "svc_enc_golomb.h"
#include "crt_util_safe_x.h"	// for safe crt like calls
#include "rc.h"

#if defined(X86_ASM)
#include "cpu.h"
#endif//X86_ASM

#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
#include "measure_time.h"
#endif//DYNAMIC_SLICE_ASSIGN
namespace WelsSVCEnc {
void UpdateMbListNeighborParallel(	SSliceCtx *pSliceCtx,
									  SMB *pMbList,
									  const int32_t uiSliceIdc	)
{
	const uint8_t *kpMbMap			= pSliceCtx->pOverallMbMap;
	const int32_t kiMbWidth			= pSliceCtx->iMbWidth;
	int32_t iIdx						= pSliceCtx->pFirstMbInSlice[uiSliceIdc];
	const int32_t kiEndMbInSlice	= iIdx + pSliceCtx->pCountMbNumInSlice[uiSliceIdc] - 1;
	
	do {
		SMB *pMb							= &pMbList[iIdx];
		uint32_t uiNeighborAvailFlag	= 0;
		const int32_t kiMbXY				= pMb->iMbXY;
		const int32_t kiMbX				= pMb->iMbX;
		const int32_t kiMbY				= pMb->iMbY;
		BOOL_T     bLeft;
		BOOL_T     bTop;
		BOOL_T     bLeftTop;
		BOOL_T     bRightTop;		
		int32_t   iLeftXY, iTopXY, iLeftTopXY, iRightTopXY;		

		iLeftXY = kiMbXY - 1;
		iTopXY = kiMbXY - kiMbWidth;
		iLeftTopXY = iTopXY - 1;
		iRightTopXY = iTopXY + 1;
		
		bLeft = (kiMbX > 0) && (uiSliceIdc == kpMbMap[iLeftXY]);
		bTop = (kiMbY > 0) && (uiSliceIdc == kpMbMap[iTopXY]);
		bLeftTop = (kiMbX > 0) && (kiMbY > 0) && (uiSliceIdc == kpMbMap[iLeftTopXY]);
		bRightTop = (kiMbX < (kiMbWidth-1)) && (kiMbY > 0) && (uiSliceIdc == kpMbMap[iRightTopXY]);		
		
		if( bLeft ){
			uiNeighborAvailFlag |= LEFT_MB_POS;
		}		
		if( bTop ){
			uiNeighborAvailFlag |= TOP_MB_POS;
		}
		if( bLeftTop ){
			uiNeighborAvailFlag |= TOPLEFT_MB_POS;
		}		
		if( bRightTop ){
			uiNeighborAvailFlag |= TOPRIGHT_MB_POS;
		}		
		pMb->uiNeighborAvail	= (uint8_t)uiNeighborAvailFlag;
		pMb->uiSliceIdc		= uiSliceIdc;

		++ iIdx;
	} while(iIdx <= kiEndMbInSlice);
}

void CalcSliceComplexRatio( void *pRatio, SSliceCtx *pSliceCtx, uint32_t *pSliceConsume )
{
	float *pRatioList			= (float *)pRatio;
	float fAvI[MAX_SLICES_NUM];
	float fSumAv				= .0f;
	uint32_t *pSliceTime		= (uint32_t *)pSliceConsume;
	int32_t *pCountMbInSlice	= (int32_t *)pSliceCtx->pCountMbNumInSlice;	
	const int32_t kiSliceCount	= pSliceCtx->iSliceNumInFrame;
	int32_t iSliceIdx			= 0;

#if defined(X86_ASM)
	WelsEmms();
#endif //X86_ASM
	
	while ( iSliceIdx < kiSliceCount )
	{
		fAvI[iSliceIdx]	= 1.0f * pCountMbInSlice[iSliceIdx] / pSliceTime[iSliceIdx];
#if defined(ENABLE_TRACE_MT)
		WelsLog(NULL, WELS_LOG_DEBUG, "[MT] CalcSliceComplexRatio(), pSliceConsumeTime[%d]= %d us, slice_run= %d\n", iSliceIdx, pSliceTime[iSliceIdx], pCountMbInSlice[iSliceIdx]);
#endif//ENABLE_TRACE_MT
		fSumAv += fAvI[iSliceIdx];
		
		++ iSliceIdx;
	}
	while ( -- iSliceIdx >= 0 )
	{
		pRatioList[iSliceIdx] = fAvI[iSliceIdx] / fSumAv;
	}
}

#if defined(MT_ENABLED) && defined(DYNAMIC_SLICE_ASSIGN) && defined(NOT_ABSOLUTE_BALANCING)
int32_t NeedDynamicAdjust( void *pConsumeTime, const int32_t iSliceNum )
{	
#if !defined(USE_RMSE_SLICE_COMPLEXITY_RATIO_FOR_BALANCING)
	const float fRatioLower	= TOLERANT_BALANCING_RATIO_LOWER( uiSliceNum );
	const float fRatioUpper	= TOLERANT_BALANCING_RATIO_UPPER( uiSliceNum );
#endif//USE_RMSE_SLICE_COMPLEXITY_RATIO_FOR_BALANCING
	uint32_t *pSliceConsume	= (uint32_t *)pConsumeTime;
	uint32_t uiTotalConsume	= 0;
	int32_t iSliceIdx		= 0;
	int32_t iNeedAdj		= false;

#if defined(X86_ASM)
	WelsEmms();
#endif //X86_ASM
	
	while( iSliceIdx < iSliceNum )
	{
		uiTotalConsume += pSliceConsume[iSliceIdx] + pSliceConsume[1+iSliceIdx];
		iSliceIdx += 2;
	}
	if (uiTotalConsume == 0)
	{
#if defined(ENABLE_TRACE_MT)
		WelsLog( NULL, WELS_LOG_DEBUG, "[MT] NeedDynamicAdjust(), herein do no adjust due first picture, iCountSliceNum= %d\n", iSliceNum );
#endif//ENABLE_TRACE_MT
		return false;
	}

	iSliceIdx = 0;
#if defined(USE_RMSE_SLICE_COMPLEXITY_RATIO_FOR_BALANCING)
	float fThr				= EPSN;	// threshold for various cores cases
	float fRmse				= .0f;	// root mean square error of pSlice consume ratios
	const float kfMeanRatio	= 1.0f / iSliceNum;
	do{
		const float fRatio = 1.0f * pSliceConsume[iSliceIdx] / uiTotalConsume;
		const float fDiffRatio = fRatio - kfMeanRatio;
		fRmse += (fDiffRatio * fDiffRatio);		
		++ iSliceIdx;
	} while ( iSliceIdx+1 < iSliceNum );	
	fRmse = sqrtf(fRmse/iSliceNum);
	if ( iSliceNum >= 8 )
	{
		fThr += THRESHOLD_RMSE_CORE8;		
	}
	else if ( iSliceNum >= 4 )
	{
		fThr += THRESHOLD_RMSE_CORE4;
	}
	else if ( iSliceNum >= 2 )
	{
		fThr += THRESHOLD_RMSE_CORE2;
	}
	else
		fThr = 1.0f;
	if ( fRmse > fThr )
		iNeedAdj	= true;
#if defined(ENABLE_TRACE_MT)
	WelsLog(NULL, WELS_LOG_DEBUG, "[MT] NeedDynamicAdjust(), herein adjustment decision is made (iNeedAdj= %d) by: fRmse of pSlice complexity ratios %.6f, the corresponding threshold %.6f, iCountSliceNum %d\n",
		iNeedAdj, fRmse, fThr, iSliceNum);
#endif//ENABLE_TRACE_MT
#else
	do{
		const float kfRatio = 1.0f * pSliceConsume[uiSliceIdx] / uiTotalConsume;
		if ( kfRatio+EPSN < fRatioLower || kfRatio > ratio_upper+EPSN )
		{
#if defined(ENABLE_TRACE_MT)
			WelsLog(NULL, WELS_LOG_DEBUG, "[MT] NeedDynamicAdjust(), herein adjustment decision is made by pSlice consume time not balanced at all, uiSliceIdx= %d, comp_ratio= %.6f, pSliceConsumeTime= %d, total_consume_time= %d, iCountSliceNum= %d\n",
				uiSliceIdx, kfRatio, pSliceConsume[uiSliceIdx], uiTotalConsume, uiSliceNum);
#endif//ENABLE_TRACE_MT
			iNeedAdj = true;
			break;
		}
		++ uiSliceIdx;
	} while ( uiSliceIdx+1 < uiSliceNum );
#endif//USE_RMSE_SLICE_COMPLEXITY_RATIO_FOR_BALANCING

	return iNeedAdj;
}
#endif//..

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
void DynamicAdjustSlicing(	sWelsEncCtx *pCtx,
								SDqLayer *pCurDqLayer,
								void *pComplexRatio,
								int32_t iCurDid )
{	
	SSliceCtx *pSliceCtx	= pCurDqLayer->pSliceEncCtx;
	const int32_t kiCountSliceNum	= pSliceCtx->iSliceNumInFrame;	
	const int32_t kiCountNumMb		= pSliceCtx->iMbNumInFrame;
	int32_t iMinimalMbNum			= pSliceCtx->iMbWidth;	// in theory we need only 1 SMB, here let it as one SMB row required
	int32_t iMaximalMbNum			= 0;	// dynamically assign later
	float *pSliceComplexRatio	= (float *)pComplexRatio;	
	int32_t iMbNumLeft					= kiCountNumMb;
	int32_t iRunLen[MAX_THREADS_NUM]	= {0};
	int32_t iSliceIdx					= 0;

	int32_t iNumMbInEachGom;
	SWelsSvcRc *pWelsSvcRc = &pCtx->pWelsSvcRc[iCurDid];
	if(pCtx->pSvcParam->bEnableRc)
	{
		iNumMbInEachGom = pWelsSvcRc->iNumberMbGom;

		if ( iNumMbInEachGom <= 0 )
		{
			WelsLog(pCtx, WELS_LOG_ERROR, "[MT] DynamicAdjustSlicing(), invalid iNumMbInEachGom= %d from RC, iDid= %d, iCountNumMb= %d\n", iNumMbInEachGom, iCurDid, kiCountNumMb);
			return;
		}

		// do not adjust in case no extra iNumMbInEachGom based left for slicing adjustment,
		// extra MB of non integrated GOM assigned at the last pSlice in default, keep up on early initial result.
		if ( iNumMbInEachGom * kiCountSliceNum >= kiCountNumMb )
		{
			return;
		}		
		iMinimalMbNum	= iNumMbInEachGom;		
	}
	
	if ( kiCountSliceNum < 2 || (kiCountSliceNum & 0x01) )	// we need suppose uiSliceNum is even for multiple threading
		return;

	iMaximalMbNum	= kiCountNumMb - (kiCountSliceNum - 1) * iMinimalMbNum;

#if defined(X86_ASM)
	WelsEmms();
#endif //X86_ASM
	
#if defined(ENABLE_TRACE_MT)
	WelsLog(pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), iDid= %d, iCountNumMb= %d\n", iCurDid, kiCountNumMb);
#endif//ENABLE_TRACE_MT

	iSliceIdx	= 0;
	while (iSliceIdx+1 < kiCountSliceNum) {
		int32_t iNumMbAssigning = (int32_t)(kiCountNumMb * pSliceComplexRatio[iSliceIdx] + EPSN);			

		// GOM boundary aligned
		if(pCtx->pSvcParam->bEnableRc)
		{
			iNumMbAssigning=(int32_t)(1.0f * iNumMbAssigning / iNumMbInEachGom + 0.5f + EPSN) * iNumMbInEachGom;			
		}

		// make sure one GOM at least in each pSlice for safe
		if ( iNumMbAssigning < iMinimalMbNum )
			iNumMbAssigning	= iMinimalMbNum;
		else if ( iNumMbAssigning > iMaximalMbNum )
			iNumMbAssigning	= iMaximalMbNum;

		assert( iNumMbAssigning > 0 );

		iMbNumLeft -= iNumMbAssigning;
		if ( iMbNumLeft <= 0 )	// error due to we can not support slice_skip now yet, do not adjust this time
		{
			assert( 0 );
			return;
		}
		iRunLen[iSliceIdx]	= iNumMbAssigning;
#if defined(ENABLE_TRACE_MT)
		WelsLog(pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), uiSliceIdx= %d, pSliceComplexRatio= %.2f, slice_run_org= %d, slice_run_adj= %d\n", 
			iSliceIdx, pSliceComplexRatio[iSliceIdx], pSliceCtx->pCountMbNumInSlice[iSliceIdx], iNumMbAssigning);
#endif//ENABLE_TRACE_MT
		++ iSliceIdx;
		iMaximalMbNum	= iMbNumLeft - (kiCountSliceNum - iSliceIdx - 1) * iMinimalMbNum;	// get maximal num_mb in left parts
	}
	iRunLen[iSliceIdx] = iMbNumLeft;
#if defined(ENABLE_TRACE_MT)
	WelsLog(pCtx, WELS_LOG_DEBUG, "[MT] DynamicAdjustSlicing(), iSliceIdx= %d, pSliceComplexRatio= %.2f, slice_run_org= %d, slice_run_adj= %d\n", 
		iSliceIdx, pSliceComplexRatio[iSliceIdx], pSliceCtx->pCountMbNumInSlice[iSliceIdx], iMbNumLeft);
#endif//ENABLE_TRACE_MT


	if ( DynamicAdjustSlicePEncCtxAll( pSliceCtx, iRunLen ) == 0 )
	{
		const int32_t kiThreadNum	= pCtx->pSvcParam->iCountThreadsNum;
		int32_t iThreadIdx			= 0;
		do {
#ifdef WIN32
			WelsEventSignal( &pCtx->pSliceThreading->pUpdateMbListEvent[iThreadIdx] );
#else
			WelsEventSignal( pCtx->pSliceThreading->pUpdateMbListEvent[iThreadIdx] );
#endif//WIN32
			++ iThreadIdx;
		} while(iThreadIdx < kiThreadNum);

		WelsMultipleEventsWaitAllBlocking( kiThreadNum, &pCtx->pSliceThreading->pFinUpdateMbListEvent[0] );
	}
}
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)

#ifdef PACKING_ONE_SLICE_PER_LAYER
void ResetEnvMt( sWelsEncCtx *pCtx)
{
	const int16_t kiSliceCount = pCtx->iMaxSliceCount;
	int32_t iIdx = 0;

	while ( iIdx < kiSliceCount )
	{
		SWelsSliceBs *pSliceBs	= &pCtx->pSliceBs[iIdx];
		pSliceBs->uiBsPos		= 0;	
		++ iIdx;
	}
}
#endif//PACKING_ONE_SLICE_PER_LAYER

int32_t RequestMtResource( sWelsEncCtx **ppCtx, SWelsSvcCodingParam *pCodingParam, const int32_t iCountBsLen, const int32_t iTargetSpatialBsSize )
{	
	CMemoryAlign *pMa			= NULL;
	SWelsSvcCodingParam *pPara= NULL;
	SSliceThreading *pSmt		= NULL;
	SWelsSliceBs *pSliceB		= NULL;
	uint8_t *pBsBase			= NULL;
	int32_t iNumSpatialLayers	= 0;
	int32_t iThreadNum			= 0;	
	int32_t iIdx					= 0;
	int32_t iSliceBsBufferSize= 0;
	int16_t iMaxSliceNum		= 1;
	
	if ( NULL == ppCtx || NULL == pCodingParam || NULL == *ppCtx || iCountBsLen <= 0 )
		return 1;
	
	pMa	= (*ppCtx)->pMemAlign;
	pPara= pCodingParam;
	iNumSpatialLayers	= pPara->iNumDependencyLayer;
	iThreadNum	= pPara->iCountThreadsNum;
	iMaxSliceNum = (*ppCtx)->iMaxSliceCount;

	pSmt	= (SSliceThreading *)pMa->WelsMalloc(sizeof(SSliceThreading), "SSliceThreading");
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt), FreeMemorySvc(ppCtx) )
	(*ppCtx)->pSliceThreading	= pSmt;
	pSmt->pThreadPEncCtx	= (SSliceThreadPrivateData *)pMa->WelsMalloc( sizeof(SSliceThreadPrivateData) * iThreadNum, "pThreadPEncCtx" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pThreadPEncCtx), FreeMemorySvc(ppCtx) )
	pSmt->pThreadHandles	= (WELS_THREAD_HANDLE *)pMa->WelsMalloc( sizeof(WELS_THREAD_HANDLE) * iThreadNum, "pThreadHandles" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pThreadHandles), FreeMemorySvc(ppCtx) )

#ifdef WIN32
	pSmt->pSliceCodedEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pSliceCodedEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pSliceCodedEvent), FreeMemorySvc(ppCtx) )
	pSmt->pReadySliceCodingEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pReadySliceCodingEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pReadySliceCodingEvent), FreeMemorySvc(ppCtx) )
	pSmt->pFinSliceCodingEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pFinSliceCodingEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pFinSliceCodingEvent), FreeMemorySvc(ppCtx) )
#endif//WIN32

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
#if defined(__GNUC__)
	pSmt->pUpdateMbListThrdHandles	= (WELS_THREAD_HANDLE *)pMa->WelsMalloc( sizeof(WELS_THREAD_HANDLE) * iThreadNum, "pUpdateMbListThrdHandles" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pUpdateMbListThrdHandles), FreeMemorySvc(ppCtx) )	
#endif//__GNUC__
#ifdef WIN32
	pSmt->pUpdateMbListEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pUpdateMbListEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pUpdateMbListEvent), FreeMemorySvc(ppCtx) )
	pSmt->pFinUpdateMbListEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pFinUpdateMbListEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pFinUpdateMbListEvent), FreeMemorySvc(ppCtx) )
#endif//WIN32
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)

#ifdef WIN32
	pSmt->pExitEncodeEvent	= (WELS_EVENT *)pMa->WelsMalloc( sizeof(WELS_EVENT) * iThreadNum, "pExitEncodeEvent" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pExitEncodeEvent), FreeMemorySvc(ppCtx) )
#endif//WIN32

#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
	iIdx = 0;
	while ( iIdx < iNumSpatialLayers )
	{		
		SMulSliceOption *pMso	= &pPara->sDependencyLayers[iIdx].sMso;
		const int32_t kiSliceNum= pMso->sSliceArgument.iSliceNum;
		if (pMso->uiSliceMode == SM_FIXEDSLCNUM_SLICE && pPara->iMultipleThreadIdc > 1 && pPara->iMultipleThreadIdc >= kiSliceNum )
		{
			pSmt->pSliceConsumeTime[iIdx]	= (uint32_t *)pMa->WelsMallocz( kiSliceNum * sizeof(uint32_t), "pSliceConsumeTime[]" );
			WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pSliceConsumeTime[iIdx]), FreeMemorySvc(ppCtx) )
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
			pSmt->pSliceComplexRatio[iIdx]	= (float *)pMa->WelsMalloc( kiSliceNum * sizeof(float), "pSliceComplexRatio[]" );
			WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pSliceComplexRatio[iIdx]), FreeMemorySvc(ppCtx) )
#endif//TRY_SLICING_BALANCE
		}
		else
		{
			pSmt->pSliceConsumeTime[iIdx]	= NULL;
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
			pSmt->pSliceComplexRatio[iIdx]	= NULL;
#endif//TRY_SLICING_BALANCE
		}		
		++ iIdx;
	}
	// NULL for pSliceConsumeTime[iIdx]: iIdx from iNumSpatialLayers to MAX_DEPENDENCY_LAYERS	
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
	
#ifdef MT_DEBUG
	// file handle for MT debug
	pSmt->pFSliceDiff = NULL;

	if ( pSmt->pFSliceDiff )
	{
		fclose( pSmt->pFSliceDiff );
		pSmt->pFSliceDiff = NULL;
	}
#ifdef WIN32
	pSmt->pFSliceDiff	= fopen(".\\slice_time.txt", "wt+" );
#else
	pSmt->pFSliceDiff	= fopen("/tmp/slice_time.txt", "wt+" );
#endif//WIN32
#endif//MT_DEBUG
	
#if defined(ENABLE_TRACE_MT)
	WelsLog((*ppCtx), WELS_LOG_INFO, "encpEncCtx= 0x%p\n", (void *)(*ppCtx));
#endif//ENABLE_TRACE_MT

	iIdx = 0;
	while ( iIdx < iThreadNum )
	{
#ifdef __GNUC__	// for posix threading
		str_t name[SEM_NAME_MAX] = {0};
		int32_t used_len = 0;
		WELS_THREAD_ERROR_CODE err = 0;
#endif//__GNUC__
		pSmt->pThreadPEncCtx[iIdx].pWelsPEncCtx	= (void *)(*ppCtx);
		pSmt->pThreadPEncCtx[iIdx].iSliceIndex	= iIdx;
		pSmt->pThreadPEncCtx[iIdx].iThreadIndex	= iIdx;
		pSmt->pThreadHandles[iIdx]				= 0;

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
#ifdef WIN32
		WelsEventInit( &pSmt->pUpdateMbListEvent[iIdx] );
		WelsEventInit( &pSmt->pFinUpdateMbListEvent[iIdx] );		
#else
		// length of semaphore name should be system constrained at least on mac 10.7
		SNPRINTF( name, SEM_NAME_MAX, "ud%d%p", iIdx, (void *)(*ppCtx) );
		err = WelsEventOpen( &pSmt->pUpdateMbListEvent[iIdx], name );
#if defined(ENABLE_TRACE_MT)
		WelsLog((*ppCtx), WELS_LOG_INFO, "[MT] Open pUpdateMbListEvent%d named(%s) ret%d err%d\n", iIdx, name, err, errno);
#endif
		used_len = SNPRINTF( name, SEM_NAME_MAX, "fu%d%p", iIdx, (void *)(*ppCtx) );
		name[used_len] = '\0';
		err = WelsEventOpen( &pSmt->pFinUpdateMbListEvent[iIdx], name );
#if defined(ENABLE_TRACE_MT)
		WelsLog((*ppCtx), WELS_LOG_INFO, "[MT] Open pFinUpdateMbListEvent%d named(%s) ret%d err%d\n", iIdx, name, err, errno);
#endif
#endif//WIN32
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
		
#ifdef WIN32
		WelsEventInit( &pSmt->pSliceCodedEvent[iIdx] );
		WelsEventInit( &pSmt->pReadySliceCodingEvent[iIdx] );
		WelsEventInit( &pSmt->pFinSliceCodingEvent[iIdx] );
		WelsEventInit( &pSmt->pExitEncodeEvent[iIdx] );
#else
		used_len = SNPRINTF( name, SEM_NAME_MAX, "sc%d%p", iIdx, (void *)(*ppCtx) );
		name[used_len] = '\0';
		err = WelsEventOpen( &pSmt->pSliceCodedEvent[iIdx], name );
#if defined(ENABLE_TRACE_MT)
		WelsLog((*ppCtx), WELS_LOG_INFO, "[MT] Open pSliceCodedEvent%d named(%s) ret%d err%d\n", iIdx, name, err, errno);
#endif
		used_len = SNPRINTF( name, SEM_NAME_MAX, "rc%d%p", iIdx, (void *)(*ppCtx) );
		name[used_len] = '\0';
		err = WelsEventOpen( &pSmt->pReadySliceCodingEvent[iIdx], name );		
#if defined(ENABLE_TRACE_MT)
		WelsLog((*ppCtx), WELS_LOG_INFO, "[MT] Open pReadySliceCodingEvent%d = 0x%p named(%s) ret%d err%d\n", iIdx, (void *)pSmt->pReadySliceCodingEvent[iIdx]), (void *)(*ppCtx), err, errno);
#endif
#endif//WIN32

		++ iIdx;
	}

#ifdef PACKING_ONE_SLICE_PER_LAYER
	pSmt->pCountBsSizeInPartition	= (uint32_t *)pMa->WelsMalloc( sizeof(uint32_t) * iThreadNum, "pCountBsSizeInPartition" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSmt->pCountBsSizeInPartition), FreeMemorySvc(ppCtx) )
#endif//PACKING_ONE_SLICE_PER_LAYER	

	WelsMutexInit( &pSmt->mutexSliceNumUpdate );
	
	(*ppCtx)->pSliceBs	= (SWelsSliceBs *)pMa->WelsMalloc( sizeof(SWelsSliceBs) * iMaxSliceNum, "pSliceBs" );
	WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == (*ppCtx)->pSliceBs), FreeMemorySvc(ppCtx) )
	
	pBsBase		= (*ppCtx)->pFrameBs + iCountBsLen;
	pSliceB	= (*ppCtx)->pSliceBs;
	iSliceBsBufferSize	= iTargetSpatialBsSize;
	iIdx = 0;
	while ( iIdx < iMaxSliceNum )
	{
		pSliceB->pBsBuffer	= (uint8_t *)pMa->WelsMalloc( iSliceBsBufferSize, "pSliceB->pBsBuffer" );

		WELS_VERIFY_RETURN_PROC_IF( 1, (NULL == pSliceB->pBsBuffer), FreeMemorySvc(ppCtx) )
		pSliceB->uiSize	= iSliceBsBufferSize;
		
		if ( iIdx > 0 )
		{
			pSliceB->pBs		= pBsBase;
			pSliceB->uiBsPos	= 0;
			pBsBase				+= iSliceBsBufferSize;
		}
		else
		{
			pSliceB->pBs		= NULL;
			pSliceB->uiBsPos	= 0;
		}
		++ pSliceB;
		++ iIdx;
	}

#if defined(ENABLE_TRACE_MT)
	WelsLog((*ppCtx), WELS_LOG_INFO, "RequestMtResource(), iThreadNum=%d, iCountSliceNum= %d\n", pPara->iCountThreadsNum, iMaxSliceNum);
#endif
	
	return 0;
}

void ReleaseMtResource( sWelsEncCtx **ppCtx )
{
	SWelsSliceBs *pSliceB			= NULL;
	SWelsSvcCodingParam *pCodingParam	= NULL;
	SSliceThreading *pSmt			= NULL;
	CMemoryAlign *pMa				= NULL;	
	int32_t iIdx						= 0;
	int32_t iThreadNum				= 0;
	int16_t uiSliceNum				= 0;

	if ( NULL == ppCtx || NULL == *ppCtx )
		return;

	pMa			= (*ppCtx)->pMemAlign;
	pCodingParam		= (*ppCtx)->pSvcParam;
	uiSliceNum	= (*ppCtx)->iMaxSliceCount;
	iThreadNum	= (*ppCtx)->pSvcParam->iCountThreadsNum;
	pSmt		= (*ppCtx)->pSliceThreading;

	if ( NULL == pSmt )
		return;

	while ( iIdx < iThreadNum) {
#ifdef WIN32
		if ( pSmt->pThreadHandles != NULL && pSmt->pThreadHandles[iIdx] != NULL )
			WelsThreadDestroy( &pSmt->pThreadHandles[iIdx] );

		if ( pSmt->pSliceCodedEvent != NULL )
			WelsEventDestroy( &pSmt->pSliceCodedEvent[iIdx] );
		if ( pSmt->pReadySliceCodingEvent != NULL )
			WelsEventDestroy( &pSmt->pReadySliceCodingEvent[iIdx] );
		if ( pSmt->pFinSliceCodingEvent != NULL )
			WelsEventDestroy( &pSmt->pFinSliceCodingEvent[iIdx] );
		if ( pSmt->pExitEncodeEvent != NULL )
			WelsEventDestroy( &pSmt->pExitEncodeEvent[iIdx] );
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
		if ( pSmt->pUpdateMbListEvent != NULL )
			WelsEventDestroy( &pSmt->pUpdateMbListEvent[iIdx] );
		if ( pSmt->pFinUpdateMbListEvent != NULL )
			WelsEventDestroy( &pSmt->pFinUpdateMbListEvent[iIdx] );
#endif//DYNAMIC_SLICE_ASSIGN && TRY_SLICING_BALANCE
#else	
		str_t ename[SEM_NAME_MAX] = {0};
		int32_t used_len = 0;
		// length of semaphore name should be system constrained at least on mac 10.7
		SNPRINTF( ename, SEM_NAME_MAX, "sc%d%p", iIdx, (void *)(*ppCtx) );
		WelsEventClose( pSmt->pSliceCodedEvent[iIdx], ename );
		used_len = SNPRINTF( ename, SEM_NAME_MAX, "rc%d%p", iIdx, (void *)(*ppCtx) );
		ename[used_len] = '\0';
		WelsEventClose( pSmt->pReadySliceCodingEvent[iIdx], ename );
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
		used_len = SNPRINTF( ename, SEM_NAME_MAX, "ud%d%p", iIdx, (void *)(*ppCtx) );
		ename[used_len] = '\0';
		WelsEventClose( pSmt->pUpdateMbListEvent[iIdx], ename );
		used_len = SNPRINTF( ename, SEM_NAME_MAX, "fu%d%p", iIdx, (void *)(*ppCtx) );
		ename[used_len] = '\0';
		WelsEventClose( pSmt->pFinUpdateMbListEvent[iIdx], ename );
#endif//DYNAMIC_SLICE_ASSIGN && TRY_SLICING_BALANCE
#endif//WIN32		

		++ iIdx;
	}

#ifdef WIN32
	if ( pSmt->pExitEncodeEvent != NULL )
	{
		pMa->WelsFree( pSmt->pExitEncodeEvent, "pExitEncodeEvent" );
		pSmt->pExitEncodeEvent = NULL;
	}
	if ( pSmt->pSliceCodedEvent != NULL )
	{
		pMa->WelsFree( pSmt->pSliceCodedEvent, "pSliceCodedEvent" );
		pSmt->pSliceCodedEvent = NULL;
	}
	if ( pSmt->pReadySliceCodingEvent != NULL )
	{
		pMa->WelsFree( pSmt->pReadySliceCodingEvent, "pReadySliceCodingEvent" );
		pSmt->pReadySliceCodingEvent = NULL;
	}
	if ( pSmt->pFinSliceCodingEvent != NULL )
	{
		pMa->WelsFree( pSmt->pFinSliceCodingEvent, "pFinSliceCodingEvent" );
		pSmt->pFinSliceCodingEvent = NULL;
	}
#endif//WIN32

#ifdef PACKING_ONE_SLICE_PER_LAYER
	if ( NULL != pSmt->pCountBsSizeInPartition )
	{
		pMa->WelsFree( pSmt->pCountBsSizeInPartition, "pCountBsSizeInPartition" );
		pSmt->pCountBsSizeInPartition = NULL;
	}
#endif//PACKING_ONE_SLICE_PER_LAYER
	WelsMutexDestroy( &pSmt->mutexSliceNumUpdate );

	if ( pSmt->pThreadPEncCtx != NULL )
	{
		pMa->WelsFree( pSmt->pThreadPEncCtx, "pThreadPEncCtx" );
		pSmt->pThreadPEncCtx = NULL;
	}
	if ( pSmt->pThreadHandles != NULL )
	{
		pMa->WelsFree( pSmt->pThreadHandles, "pThreadHandles" );
		pSmt->pThreadHandles = NULL;
	}
	
	pSliceB = (*ppCtx)->pSliceBs;
	iIdx = 0;
	while ( pSliceB != NULL && iIdx < uiSliceNum )
	{		
		if ( pSliceB->pBsBuffer )
		{
			pMa->WelsFree( pSliceB->pBsBuffer, "pSliceB->pBsBuffer" );
			pSliceB->pBsBuffer = NULL;
			pSliceB->uiSize = 0;
		}		
		++ iIdx;
		++ pSliceB;
	}
	if ( (*ppCtx)->pSliceBs != NULL )
	{
		pMa->WelsFree( (*ppCtx)->pSliceBs, "pSliceBs" );
		(*ppCtx)->pSliceBs = NULL;
	}
#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
	if ( pSmt->pSliceConsumeTime != NULL )
	{
		iIdx = 0;
		while (iIdx < pCodingParam->iNumDependencyLayer)
		{
			if ( pSmt->pSliceConsumeTime[iIdx] )
			{
				pMa->WelsFree( pSmt->pSliceConsumeTime[iIdx], "pSliceConsumeTime[]" );
				pSmt->pSliceConsumeTime[iIdx] = NULL;
			}
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
			if ( pSmt->pSliceComplexRatio[iIdx] != NULL )
			{
				pMa->WelsFree( pSmt->pSliceComplexRatio[iIdx], "pSliceComplexRatio[]" );
				pSmt->pSliceComplexRatio[iIdx] = NULL;
			}
#endif//TRY_SLICING_BALANCE
			++ iIdx;
		}		
	}
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)	

#ifdef WIN32
	if ( pSmt->pUpdateMbListEvent != NULL )
	{
		pMa->WelsFree( pSmt->pUpdateMbListEvent, "pUpdateMbListEvent" );
		pSmt->pUpdateMbListEvent = NULL;
	}
	if ( pSmt->pFinUpdateMbListEvent != NULL )
	{
		pMa->WelsFree( pSmt->pFinUpdateMbListEvent, "pFinUpdateMbListEvent" );
		pSmt->pFinUpdateMbListEvent = NULL;
	}
#else
	if ( pSmt->pUpdateMbListThrdHandles )
	{
		pMa->WelsFree( pSmt->pUpdateMbListThrdHandles, "pUpdateMbListThrdHandles" );
		pSmt->pUpdateMbListThrdHandles = NULL;
	}
#endif//WIN32

#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)

#ifdef MT_DEBUG
	// file handle for debug
	if ( pSmt->pFSliceDiff )
	{
		fclose( pSmt->pFSliceDiff );
		pSmt->pFSliceDiff = NULL;
	}
#endif//MT_DEBUG
	pMa->WelsFree((*ppCtx)->pSliceThreading, "SSliceThreading");
	(*ppCtx)->pSliceThreading = NULL;
}

int32_t AppendSliceToFrameBs( sWelsEncCtx *pCtx, SLayerBSInfo *pLbi, const int32_t iSliceCount )
{	
	SWelsSvcCodingParam *pCodingParam	= pCtx->pSvcParam;
	SDLayerParam *pDlp				= &pCodingParam->sDependencyLayers[pCtx->uiDependencyId];
	SWelsSliceBs *pSliceBs			= NULL;
	const BOOL_T kbIsDynamicSlicingMode	= (pDlp->sMso.uiSliceMode == SM_DYN_SLICE);
	int32_t iLayerSize					= 0;
	int32_t iNalIdxBase				= pLbi->iNalCount;
	int32_t iSliceIdx					= 0;	

	if ( !kbIsDynamicSlicingMode )
	{
		pSliceBs	= &pCtx->pSliceBs[0];
		iLayerSize	= pSliceBs->uiBsPos;	// assign with base pSlice first			
		iSliceIdx	= 1;				// pSlice 0 bs has been written to pFrameBs yet by now, so uiSliceIdx base should be 1
		while (iSliceIdx < iSliceCount)
		{
			++ pSliceBs;
			if ( pSliceBs != NULL && pSliceBs->uiBsPos > 0 )
			{
				int32_t iNalIdx = 0;
				const int32_t iCountNal	= pSliceBs->iNalIndex;

#if MT_DEBUG_BS_WR
				assert(pSliceBs->bSliceCodedFlag);
#endif//MT_DEBUG_BS_WR

				memmove(pCtx->pFrameBs + pCtx->iPosBsBuffer, pSliceBs->pBs, pSliceBs->uiBsPos);	// confirmed_safe_unsafe_usage
				pCtx->iPosBsBuffer += pSliceBs->uiBsPos;

				iLayerSize += pSliceBs->uiBsPos;

				while (iNalIdx < iCountNal)
				{
					pLbi->iNalLengthInByte[iNalIdxBase+iNalIdx]	= pSliceBs->iNalLen[iNalIdx];
					++ iNalIdx;
				}
				pLbi->iNalCount	+= iCountNal;
				iNalIdxBase	+= iCountNal;
			}		
			++ iSliceIdx;		
		}
	}
	else	// for SM_DYN_SLICE
	{
		const int32_t kiPartitionCnt	= iSliceCount;
		int32_t iPartitionIdx		= 0;
		
		// due partition_0 has been written to pFrameBsBuffer
		// so iLayerSize need add it
		while ( iPartitionIdx < kiPartitionCnt )
		{
			const int32_t kiCountSlicesCoded = pCtx->pCurDqLayer->pNumSliceCodedOfPartition[iPartitionIdx];
			int32_t iIdx = 0;

			iSliceIdx	= iPartitionIdx;
			while(iIdx < kiCountSlicesCoded)
			{
				pSliceBs	= &pCtx->pSliceBs[iSliceIdx];
				if ( pSliceBs != NULL && pSliceBs->uiBsPos > 0 )
				{
					if ( iPartitionIdx > 0 )
					{
						int32_t iNalIdx = 0;
						const int32_t iCountNal	= pSliceBs->iNalIndex;

						memmove(pCtx->pFrameBs + pCtx->iPosBsBuffer, pSliceBs->pBs, pSliceBs->uiBsPos);	// confirmed_safe_unsafe_usage
						pCtx->iPosBsBuffer += pSliceBs->uiBsPos;

						iLayerSize += pSliceBs->uiBsPos;

						while (iNalIdx < iCountNal)
						{
							pLbi->iNalLengthInByte[iNalIdxBase+iNalIdx]	= pSliceBs->iNalLen[iNalIdx];
							++ iNalIdx;
						}
						pLbi->iNalCount	+= iCountNal;
						iNalIdxBase	+= iCountNal;
					}
					else
					{
						iLayerSize	+= pSliceBs->uiBsPos;
					}
				}

				iSliceIdx += kiPartitionCnt;
				++ iIdx;
			}			
			++ iPartitionIdx;
		}
	}

	return iLayerSize;
}

int32_t WriteSliceToFrameBs( sWelsEncCtx *pCtx, SLayerBSInfo *pLbi, uint8_t *pFrameBsBuffer, const int32_t iSliceIdx )
{
	SWelsSliceBs *pSliceBs			= &pCtx->pSliceBs[iSliceIdx];
	SNalUnitHeaderExt *pNalHdrExt= &pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt;	
	uint8_t *pDst					= pFrameBsBuffer;
	int32_t pNalLen[2];
	int32_t iSliceSize				= 0;	
	const int32_t kiNalCnt			= pSliceBs->iNalIndex;	
	int32_t iNalIdx					= 0;
#if !defined(PACKING_ONE_SLICE_PER_LAYER)
	const int32_t iFirstSlice		= (iSliceIdx == 0);
	int32_t iNalBase				= iFirstSlice ? 0 : pLbi->iNalCount;
#else
	int32_t iNalBase				= 0;
#endif//!PACKING_ONE_SLICE_PER_LAYER
	
	while ( iNalIdx < kiNalCnt ) {
		iSliceSize += WelsEncodeNalExt( &pSliceBs->sNalList[iNalIdx], pNalHdrExt, pDst, &pNalLen[iNalIdx] );
		pDst += pNalLen[iNalIdx];
		pLbi->iNalLengthInByte[iNalBase+iNalIdx]	= pNalLen[iNalIdx];
		
		++ iNalIdx;
	}	
	
#if !defined(PACKING_ONE_SLICE_PER_LAYER)
	pSliceBs->uiBsPos	= iSliceSize;
	if ( iFirstSlice )
	{
		// pBsBuffer has been updated at coding_slice_0_in_encoder_mother_thread()
		pLbi->uiLayerType		= VIDEO_CODING_LAYER;
		pLbi->uiSpatialId		= pNalHdrExt->uiDependencyId;
		pLbi->uiTemporalId	= pNalHdrExt->uiTemporalId;
		pLbi->uiQualityId		= 0;
		pLbi->uiPriorityId	= 0;	
		pLbi->iNalCount		= kiNalCnt;
	}
	else
	{
		pLbi->iNalCount		+= kiNalCnt;
	}
#else
	pLbi->uiLayerType		= VIDEO_CODING_LAYER;
	pLbi->uiSpatialId		= pNalHdrExt->uiDependencyId;
	pLbi->uiTemporalId	= pNalHdrExt->uiTemporalId;
	pLbi->uiQualityId		= 0;
	pLbi->uiPriorityId	= 0;	
	pLbi->iNalCount		= kiNalCnt;
#endif//PACKING_ONE_SLICE_PER_LAYER
	
	return iSliceSize;
}

int32_t WriteSliceBs( sWelsEncCtx *pCtx, uint8_t *pSliceBsBuf, const int32_t iSliceIdx )
{
	SWelsSliceBs *pSliceBs			= &pCtx->pSliceBs[iSliceIdx];
	SNalUnitHeaderExt *pNalHdrExt= &pCtx->pCurDqLayer->sLayerInfo.sNalHeaderExt;	
	uint8_t *pDst					= pSliceBsBuf;
	int32_t *pNalLen				= &pSliceBs->iNalLen[0];
	int32_t iSliceSize				= 0;
	const int32_t kiNalCnt			= pSliceBs->iNalIndex;	
	int32_t iNalIdx					= 0;	

	assert( kiNalCnt <= 2 );
	if ( kiNalCnt > 2 )
		return 0;
	
	while ( iNalIdx < kiNalCnt ) {
		iSliceSize += WelsEncodeNalExt( &pSliceBs->sNalList[iNalIdx], pNalHdrExt, pDst, &pNalLen[iNalIdx] );
		pDst += pNalLen[iNalIdx];		
		
		++ iNalIdx;
	}
	pSliceBs->uiBsPos	= iSliceSize;
	
	return iSliceSize;
}

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
#if defined(__GNUC__)
WELS_THREAD_ROUTINE_TYPE UpdateMbListThreadProc( void *arg )
{
	SSliceThreadPrivateData *pPrivateData	= (SSliceThreadPrivateData *)arg;
	sWelsEncCtx *pEncPEncCtx			= NULL;
	SDqLayer *pCurDq							= NULL;
	int32_t iSliceIdx							= -1;
	int32_t iEventIdx							= -1;
	WELS_THREAD_ERROR_CODE iWaitRet				= WELS_THREAD_ERROR_GENERIAL;
	uint32_t uiThrdRet							= 0;
	
	if ( NULL == pPrivateData )
		WELS_THREAD_ROUTINE_RETURN(1);

	pEncPEncCtx	= (sWelsEncCtx *)pPrivateData->pWelsPEncCtx;	
	iSliceIdx		= pPrivateData->iSliceIndex;
	iEventIdx		= pPrivateData->iThreadIndex;

	do {
#if defined(ENABLE_TRACE_MT)
		WelsLog(pEncPEncCtx, WELS_LOG_INFO, "[MT] UpdateMbListThreadProc(), try to wait (pUpdateMbListEvent[%d])!\n", iEventIdx);
#endif
		iWaitRet = WelsEventWait( pEncPEncCtx->pSliceThreading->pUpdateMbListEvent[iEventIdx] );
		if ( WELS_THREAD_ERROR_WAIT_OBJECT_0 == iWaitRet )
		{
			pCurDq			= pEncPEncCtx->pCurDqLayer;
			UpdateMbListNeighborParallel( pCurDq->pSliceEncCtx, pCurDq->sMbDataP, iSliceIdx );
			WelsEventSignal( pEncPEncCtx->pSliceThreading->pFinUpdateMbListEvent[iEventIdx] );	// mean finished update pMb list for this pSlice
		}
		else
		{
			WelsLog(pEncPEncCtx, WELS_LOG_WARNING, "[MT] UpdateMbListThreadProc(), waiting pUpdateMbListEvent[%d] failed(%d) and thread%d terminated!\n", iEventIdx, iWaitRet, iEventIdx);
			uiThrdRet = 1;
			break;
		}
	} while(1);

	WELS_THREAD_ROUTINE_RETURN(uiThrdRet);
}
#endif//__GNUC__
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)

// thread process for coding one pSlice
WELS_THREAD_ROUTINE_TYPE CodingSliceThreadProc( void *arg )
{
	SSliceThreadPrivateData *pPrivateData	= (SSliceThreadPrivateData *)arg;
	sWelsEncCtx *pEncPEncCtx			= NULL;
	SDqLayer *pCurDq							= NULL;
	SSlice *pSlice								= NULL;
	SWelsSliceBs *pSliceBs						= NULL;
#ifdef WIN32
	WELS_EVENT pEventsList[3];
	int32_t iEventCount						= 0;
#endif
	WELS_THREAD_ERROR_CODE iWaitRet				= WELS_THREAD_ERROR_GENERIAL;
	uint32_t uiThrdRet							= 0;
	int32_t iSliceSize							= 0;
	int32_t iSliceIdx							= -1;
	int32_t iThreadIdx							= -1;
	int32_t iEventIdx							= -1;
	bool_t bNeedPrefix							= false;
	EWelsNalUnitType eNalType						= NAL_UNIT_UNSPEC_0;
	EWelsNalRefIdc eNalRefIdc						= NRI_PRI_LOWEST;	

	if ( NULL == pPrivateData )
		WELS_THREAD_ROUTINE_RETURN(1);

	WelsSetThreadCancelable();
	
	pEncPEncCtx	= (sWelsEncCtx *)pPrivateData->pWelsPEncCtx;
	
	iThreadIdx		= pPrivateData->iThreadIndex;	
	iEventIdx		= iThreadIdx;
	
#ifdef WIN32
	pEventsList[iEventCount++]	= pEncPEncCtx->pSliceThreading->pReadySliceCodingEvent[iEventIdx];
	pEventsList[iEventCount++]	= pEncPEncCtx->pSliceThreading->pExitEncodeEvent[iEventIdx];
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
	pEventsList[iEventCount++] = pEncPEncCtx->pSliceThreading->pUpdateMbListEvent[iEventIdx];
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)	
#endif//WIN32

	do {
#ifdef WIN32
		iWaitRet = WelsMultipleEventsWaitSingleBlocking(	iEventCount,
															&pEventsList[0],
															(uint32_t)-1	);	// blocking until at least one event is 
#else
#if defined(ENABLE_TRACE_MT)
		WelsLog(pEncPEncCtx, WELS_LOG_INFO, "[MT] CodingSliceThreadProc(), try to call WelsEventWait(pReadySliceCodingEvent[%d]= 0x%p), pEncPEncCtx= 0x%p!\n", iEventIdx, (void *)(pEncPEncCtx->pReadySliceCodingEvent[iEventIdx]), (void *)pEncPEncCtx );
#endif
		iWaitRet = WelsEventWait( pEncPEncCtx->pSliceThreading->pReadySliceCodingEvent[iEventIdx] );
#endif//WIN32		
		if ( WELS_THREAD_ERROR_WAIT_OBJECT_0 == iWaitRet )	// start pSlice coding signal waited
		{
			SLayerBSInfo *pLbi = pPrivateData->pLayerBs;
			const int32_t kiCurDid			= pEncPEncCtx->uiDependencyId;
			const int32_t kiCurTid			= pEncPEncCtx->uiTemporalId;
			SWelsSvcCodingParam *pCodingParam	= pEncPEncCtx->pSvcParam;
			SDLayerParam *pParamD			= &pCodingParam->sDependencyLayers[kiCurDid];

			pCurDq			= pEncPEncCtx->pCurDqLayer;			
			eNalType		= pEncPEncCtx->eNalType;
			eNalRefIdc		= pEncPEncCtx->eNalPriority;
			bNeedPrefix		= pEncPEncCtx->bNeedPrefixNalFlag;			
			
			if ( pParamD->sMso.uiSliceMode != SM_DYN_SLICE )
			{
				int64_t iSliceStart	= 0;
				bool_t bDsaFlag = false;
				iSliceIdx		= pPrivateData->iSliceIndex;
				pSlice			= &pCurDq->sLayerInfo.pSliceInLayer[iSliceIdx];
				pSliceBs		= &pEncPEncCtx->pSliceBs[iSliceIdx];

#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
				bDsaFlag	= (pParamD->sMso.uiSliceMode == SM_FIXEDSLCNUM_SLICE && 
							   pCodingParam->iMultipleThreadIdc > 1 &&
							   pCodingParam->iMultipleThreadIdc >= pParamD->sMso.sSliceArgument.iSliceNum);
				if ( bDsaFlag )
					iSliceStart = WelsTime();
#endif//DYNAMIC_SLICE_ASSIGN || MT_DEBUG

#if !defined(PACKING_ONE_SLICE_PER_LAYER)
				pSliceBs->uiBsPos	= 0;
#endif//!PACKING_ONE_SLICE_PER_LAYER
				pSliceBs->iNalIndex	= 0;
				assert( (void*)(&pSliceBs->sBsWrite) == (void*)pSlice->pSliceBsa );
				InitBits( &pSliceBs->sBsWrite, pSliceBs->pBsBuffer, pSliceBs->uiSize );

#if MT_DEBUG_BS_WR
				pSliceBs->bSliceCodedFlag	= FALSE;
#endif//MT_DEBUG_BS_WR
			
				if ( bNeedPrefix )
				{
					if ( eNalRefIdc != NRI_PRI_LOWEST )
					{
						WelsLoadNalForSlice( pSliceBs, NAL_UNIT_PREFIX, eNalRefIdc );
						WelsWriteSVCPrefixNal( &pSliceBs->sBsWrite, eNalRefIdc, (NAL_UNIT_CODED_SLICE_IDR == eNalType) );
						WelsUnloadNalForSlice( pSliceBs );			
					}
					else // No Prefix NAL Unit RBSP syntax here, but need add NAL Unit Header extension
					{
						WelsLoadNalForSlice( pSliceBs, NAL_UNIT_PREFIX, eNalRefIdc );
						// No need write any syntax of prefix NAL Unit RBSP here
						WelsUnloadNalForSlice( pSliceBs );			
					}
				}
				
				WelsLoadNalForSlice( pSliceBs, eNalType, eNalRefIdc );

				WelsCodeOneSlice( pEncPEncCtx, iSliceIdx, eNalType );			

				WelsUnloadNalForSlice( pSliceBs );

#if !defined(PACKING_ONE_SLICE_PER_LAYER)
				if ( 0 == iSliceIdx )
				{			
					pLbi->pBsBuf	= pEncPEncCtx->pFrameBs + pEncPEncCtx->iPosBsBuffer;
					iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbi, pLbi->pBsBuf, iSliceIdx );
					pEncPEncCtx->iPosBsBuffer += iSliceSize;
				}
				else
					iSliceSize = WriteSliceBs( pEncPEncCtx, pSliceBs->pBs, iSliceIdx );
#else// PACKING_ONE_SLICE_PER_LAYER
				if ( 0 == iSliceIdx )
				{
					pLbi->pBsBuf	= pEncPEncCtx->pFrameBs + pEncPEncCtx->iPosBsBuffer;
					iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbi, pLbi->pBsBuf, iSliceIdx );
					pEncPEncCtx->iPosBsBuffer += iSliceSize;
				}
				else
				{
					pLbi->pBsBuf	= pSliceBs->bs + pSliceBs->uiBsPos;
					iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbi, pLbi->pBsBuf, iSliceIdx );
					pSliceBs->uiBsPos += iSliceSize;
				}			
#endif//!PACKING_ONE_SLICE_PER_LAYER
			
				if ( pCurDq->bDeblockingParallelFlag && pSlice->sSliceHeaderExt.sSliceHeader.uiDisableDeblockingFilterIdc != 1
#if !defined(ENABLE_FRAME_DUMP)
					&& ( eNalRefIdc != NRI_PRI_LOWEST ) && 
					( pParamD->iHighestTemporalId == 0 || kiCurTid < pParamD->iHighestTemporalId )
#endif// !ENABLE_FRAME_DUMP
					)
				{
					DeblockingFilterSliceAvcbase( pCurDq, pEncPEncCtx->pFuncList, iSliceIdx );
				}			

#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)
				if ( bDsaFlag )
				{
					pEncPEncCtx->pSliceThreading->pSliceConsumeTime[pEncPEncCtx->uiDependencyId][iSliceIdx] = (uint32_t)(WelsTime() - iSliceStart);
#if defined(ENABLE_TRACE_MT)
					WelsLog(pEncPEncCtx, WELS_LOG_INFO, "[MT] CodingSliceThreadProc(), coding_idx %d, uiSliceIdx %d, pSliceConsumeTime %d, iSliceSize %d, pFirstMbInSlice %d, count_num_mb_in_slice %d\n",
						pEncPEncCtx->iCodingIndex, iSliceIdx, pEncPEncCtx->pSliceThreading->pSliceConsumeTime[pEncPEncCtx->uiDependencyId][iSliceIdx], iSliceSize, pCurDq->pSliceEncCtx->pFirstMbInSlice[iSliceIdx], pCurDq->pSliceEncCtx->pCountMbNumInSlice[iSliceIdx]);
#endif//ENABLE_TRACE_MT
				}
#endif//DYNAMIC_SLICE_ASSIGN || MT_DEBUG

#if defined(SLICE_INFO_OUTPUT)
				fprintf(	stderr,
							"@pSlice=%-6d sliceType:%c idc:%d size:%-6d\n",
							iSliceIdx,
							(pEncPEncCtx->eSliceType == P_SLICE ? 'P' : 'I'),
							eNalRefIdc,
							iSliceSize
						);
#endif//SLICE_INFO_OUTPUT				

#if MT_DEBUG_BS_WR
				pSliceBs->bSliceCodedFlag	= TRUE;
#endif//MT_DEBUG_BS_WR

#ifdef WIN32
				WelsEventSignal( &pEncPEncCtx->pSliceThreading->pSliceCodedEvent[iEventIdx] );	// mean finished coding current pSlice						
#else
				WelsEventSignal( pEncPEncCtx->pSliceThreading->pSliceCodedEvent[iEventIdx] );	// mean finished coding current pSlice				
#endif//WIN32				
			}
			else	// for SM_DYN_SLICE parallelization
			{
#ifdef PACKING_ONE_SLICE_PER_LAYER
				SLayerBSInfo *pLbiPacking			= NULL;
#endif//PACKING_ONE_SLICE_PER_LAYER
				SSliceCtx *pSliceCtx			= pCurDq->pSliceEncCtx;
				const int32_t kiPartitionId			= iThreadIdx;
				const int32_t kiSliceIdxStep		= pEncPEncCtx->iActiveThreadsNum;
				const int32_t kiFirstMbInPartition	= pPrivateData->iStartMbIndex;	// inclusive
				const int32_t kiEndMbInPartition	= pPrivateData->iEndMbIndex;		// exclusive
				int32_t iAnyMbLeftInPartition	= kiEndMbInPartition - kiFirstMbInPartition;				
				
				iSliceIdx		= pPrivateData->iSliceIndex;

				pSliceCtx->pFirstMbInSlice[iSliceIdx]				= kiFirstMbInPartition;					
				pCurDq->pNumSliceCodedOfPartition[kiPartitionId]		= 1;	// one pSlice per partition intialized, dynamic slicing inside
				pCurDq->pLastMbIdxOfPartition[kiPartitionId]			= kiEndMbInPartition-1;

				pCurDq->pLastCodedMbIdxOfPartition[kiPartitionId]		= 0;

				while( iAnyMbLeftInPartition > 0 )
				{
					if ( iSliceIdx >= pSliceCtx->iMaxSliceNumConstraint )
					{
						// TODO: need exception handler for not large enough of MAX_SLICES_NUM related memory usage
						// No idea about its solution due MAX_SLICES_NUM is fixed lenght in relevent pData structure
						uiThrdRet	= 1;
						break;
					}

					pSlice			= &pCurDq->sLayerInfo.pSliceInLayer[iSliceIdx];
					pSliceBs		= &pEncPEncCtx->pSliceBs[iSliceIdx];
			
#if !defined(PACKING_ONE_SLICE_PER_LAYER)
					pSliceBs->uiBsPos	= 0;
#endif//!PACKING_ONE_SLICE_PER_LAYER
					pSliceBs->iNalIndex	= 0;
					InitBits( &pSliceBs->sBsWrite, pSliceBs->pBsBuffer, pSliceBs->uiSize );
			
					if ( bNeedPrefix )
					{
						if ( eNalRefIdc != NRI_PRI_LOWEST )
						{
							WelsLoadNalForSlice( pSliceBs, NAL_UNIT_PREFIX, eNalRefIdc );
							WelsWriteSVCPrefixNal( &pSliceBs->sBsWrite, eNalRefIdc, (NAL_UNIT_CODED_SLICE_IDR == eNalType) );
							WelsUnloadNalForSlice( pSliceBs );			
						}
						else // No Prefix NAL Unit RBSP syntax here, but need add NAL Unit Header extension
						{
							WelsLoadNalForSlice( pSliceBs, NAL_UNIT_PREFIX, eNalRefIdc );
							// No need write any syntax of prefix NAL Unit RBSP here
							WelsUnloadNalForSlice( pSliceBs );			
						}
					}
				
					WelsLoadNalForSlice( pSliceBs, eNalType, eNalRefIdc );

					WelsCodeOneSlice( pEncPEncCtx, iSliceIdx, eNalType );			

					WelsUnloadNalForSlice( pSliceBs );

#if !defined(PACKING_ONE_SLICE_PER_LAYER)
					if ( 0 == kiPartitionId )
					{	
						if ( 0 == iSliceIdx )
							pLbi->pBsBuf	= pEncPEncCtx->pFrameBs + pEncPEncCtx->iPosBsBuffer;
						iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbi, pEncPEncCtx->pFrameBs + pEncPEncCtx->iPosBsBuffer, iSliceIdx );
						pEncPEncCtx->iPosBsBuffer += iSliceSize;
					}
					else
						iSliceSize = WriteSliceBs( pEncPEncCtx, pSliceBs->pBs, iSliceIdx );
#else// PACKING_ONE_SLICE_PER_LAYER
					pLbiPacking	= pLbi + (iSliceIdx - kiPartitionId);

					if ( 0 == kiPartitionId )
					{
						pLbiPacking->pBsBuf	= pEncPEncCtx->pFrameBs + pEncPEncCtx->iPosBsBuffer;
						iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbiPacking, pLbiPacking->pBsBuf, iSliceIdx );
						pEncPEncCtx->iPosBsBuffer += iSliceSize;
					}
					else
					{
						pLbiPacking->pBsBuf	= pSliceBs->bs + pSliceBs->uiBsPos;
						iSliceSize = WriteSliceToFrameBs( pEncPEncCtx, pLbiPacking, pLbiPacking->pBsBuf, iSliceIdx );
						pSliceBs->uiBsPos += iSliceSize;
					}
					pEncPEncCtx->pSliceThreading->pCountBsSizeInPartition[kiPartitionId] += iSliceSize;
#endif//!PACKING_ONE_SLICE_PER_LAYER
			
					if ( pCurDq->bDeblockingParallelFlag && pSlice->sSliceHeaderExt.sSliceHeader.uiDisableDeblockingFilterIdc != 1
#if !defined(ENABLE_FRAME_DUMP)
						&& ( eNalRefIdc != NRI_PRI_LOWEST ) && 
						( pParamD->iHighestTemporalId == 0 || kiCurTid < pParamD->iHighestTemporalId )
#endif// !ENABLE_FRAME_DUMP
						)
					{
						DeblockingFilterSliceAvcbase( pCurDq, pEncPEncCtx->pFuncList, iSliceIdx );
					}
					
#if defined(SLICE_INFO_OUTPUT)
					fprintf(	stderr,
								"@pSlice=%-6d sliceType:%c idc:%d size:%-6d\n",
								iSliceIdx,
								(pEncPEncCtx->eSliceType == P_SLICE ? 'P' : 'I'),
								eNalRefIdc,
								iSliceSize
							);
#endif//SLICE_INFO_OUTPUT					

#if defined(ENABLE_TRACE_MT)
					WelsLog(pEncPEncCtx, WELS_LOG_INFO, "[MT] CodingSliceThreadProc(), coding_idx %d, iPartitionId %d, uiSliceIdx %d, iSliceSize %d, count_mb_slice %d, iEndMbInPartition %d, pCurDq->pLastCodedMbIdxOfPartition[%d] %d\n",
						pEncPEncCtx->iCodingIndex, kiPartitionId, iSliceIdx, iSliceSize, pCurDq->pSliceEncCtx->pCountMbNumInSlice[iSliceIdx], kiEndMbInPartition, kiPartitionId, pCurDq->pLastCodedMbIdxOfPartition[kiPartitionId]);
#endif//ENABLE_TRACE_MT
					
					iAnyMbLeftInPartition = kiEndMbInPartition - (1 + pCurDq->pLastCodedMbIdxOfPartition[kiPartitionId]);
					iSliceIdx += kiSliceIdxStep;
				}

				if ( uiThrdRet )	// any exception??
					break;

#ifdef WIN32
				WelsEventSignal( &pEncPEncCtx->pSliceThreading->pSliceCodedEvent[iEventIdx] );	// mean finished coding current pSlice		
#else
				WelsEventSignal( pEncPEncCtx->pSliceThreading->pSliceCodedEvent[iEventIdx] );	// mean finished coding current pSlice		
#endif//WIN32
			}
		}
#ifdef WIN32
		else if ( WELS_THREAD_ERROR_WAIT_OBJECT_0+1 == iWaitRet )	// exit thread signal
		{
			uiThrdRet	= 0;
			break;
		}
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
		else if ( WELS_THREAD_ERROR_WAIT_OBJECT_0+2 == iWaitRet )	// update pMb list singal
		{
			iSliceIdx		= iEventIdx;	// pPrivateData->iSliceIndex; old threads can not be terminated, pPrivateData is not correct for applicable
			pCurDq			= pEncPEncCtx->pCurDqLayer;
			UpdateMbListNeighborParallel( pCurDq->pSliceEncCtx, pCurDq->sMbDataP, iSliceIdx );
			WelsEventSignal( &pEncPEncCtx->pSliceThreading->pFinUpdateMbListEvent[iEventIdx] );	// mean finished update pMb list for this pSlice			
		}
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
#endif//WIN32		
		else // WELS_THREAD_ERROR_WAIT_TIMEOUT, or WELS_THREAD_ERROR_WAIT_FAILED
		{
			WelsLog(pEncPEncCtx, WELS_LOG_WARNING, "[MT] CodingSliceThreadProc(), waiting pReadySliceCodingEvent[%d] failed(%d) and thread%d terminated!\n", iEventIdx, iWaitRet, iThreadIdx);
			uiThrdRet	= 1;
			break;
		}		
	} while( 1 );

#ifdef WIN32
	WelsEventSignal( &pEncPEncCtx->pSliceThreading->pFinSliceCodingEvent[iEventIdx] );	// notify to mother encoding threading
#endif//WIN32

	WELS_THREAD_ROUTINE_RETURN(uiThrdRet);
}

int32_t CreateSliceThreads( sWelsEncCtx *pCtx )
{
	const int32_t kiThreadCount = pCtx->pSvcParam->iCountThreadsNum;
	int32_t iIdx = 0;
#if defined(WIN32) && defined(BIND_CPU_CORES_TO_THREADS)
	DWORD  dwProcessAffinity;
	DWORD  dwSystemAffinity;
	GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinity, &dwSystemAffinity);
#endif//WIN32 && BIND_CPU_CORES_TO_THREADS
	
	while ( iIdx < kiThreadCount ) {
		WelsThreadCreate( &pCtx->pSliceThreading->pThreadHandles[iIdx], CodingSliceThreadProc, &pCtx->pSliceThreading->pThreadPEncCtx[iIdx], 0);
#if defined(WIN32) && defined(BIND_CPU_CORES_TO_THREADS)
		if ( dwProcessAffinity > 1 && pCtx->pSliceThreading->pThreadHandles[iIdx] != NULL )	// multiple cores and thread created successfully
		{	
			DWORD  dw = 0;
			DWORD  dwAffinityMask = 1 << iIdx;
			if (dwAffinityMask & dwProcessAffinity) // check if cpu is available
			{
				dw = SetThreadAffinityMask( pCtx->pSliceThreading->pThreadHandles[iIdx], dwAffinityMask ); //1 << iIdx
				if ( dw == 0)
				{
					str_t str[64] = {0};
					SNPRINTF(str, 64, "SetThreadAffinityMask iIdx:%d", iIdx);
				}
			}			
		}
#endif//WIN32 && BIND_CPU_CORES_TO_THREADS
		// We need extra threads for update_mb_list_proc on __GNUC__ like OS (mac/linux) 
		// due to WelsMultipleEventsWaitSingleBlocking implememtation can not work well 
		// in case waiting pUpdateMbListEvent and pReadySliceCodingEvent events at the same time
#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
#if defined(__GNUC__)
		WelsThreadCreate( &pCtx->pSliceThreading->pUpdateMbListThrdHandles[iIdx], UpdateMbListThreadProc, &pCtx->pSliceThreading->pThreadPEncCtx[iIdx], 0);
#endif//__GNUC__
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)
		++ iIdx;
	}
#if defined(ENABLE_TRACE_MT)
	WelsLog(pCtx, WELS_LOG_INFO, "CreateSliceThreads() exit..\n");
#endif
	return 0;
}

#ifdef PACKING_ONE_SLICE_PER_LAYER
void ResetCountBsSizeInPartitions( uint32_t *pCountBsSizeList, const int32_t iPartitionCnt )
{
	if ( pCountBsSizeList != NULL && iPartitionCnt > 0 )
	{
		memset(pCountBsSizeList, 0, sizeof(pCountBsSizeList[0]) * iPartitionCnt );
	}
}
#endif//PACKING_ONE_SLICE_PER_LAYER

#ifdef WIN32
int32_t FiredSliceThreads( SSliceThreadPrivateData *pPriData, WELS_EVENT *pEventsList, SLayerBSInfo *pLbi, const uint32_t uiNumThreads, SSliceCtx *pSliceCtx, const BOOL_T bIsDynamicSlicingMode )
#else
int32_t FiredSliceThreads( SSliceThreadPrivateData *pPriData, WELS_EVENT **pEventsList, SLayerBSInfo *pLbi, const uint32_t uiNumThreads, SSliceCtx *pSliceCtx, const BOOL_T bIsDynamicSlicingMode )
#endif//WIN32
{
	int32_t iEndMbIdx	= 0;
	int32_t iIdx		= 0;
	const int32_t kiEventCnt = uiNumThreads;
	
	if ( pPriData == NULL || pLbi == NULL || kiEventCnt <= 0 || pEventsList == NULL )
	{
		WelsLog( NULL, WELS_LOG_ERROR, "FiredSliceThreads(), fail due pPriData == %p || pLbi == %p || iEventCnt(%d) <= 0 || pEventsList == %p!!\n", (void *)pPriData, (void *)pLbi, uiNumThreads,  (void *)pEventsList);	
		return 1;
	}

#if defined(PACKING_ONE_SLICE_PER_LAYER)
	////////////////////////////////////////
	if ( bIsDynamicSlicingMode )
	{
		iEndMbIdx	= pSliceCtx->iMbNumInFrame;
		for (iIdx = kiEventCnt-1; iIdx >= 0; --iIdx)
		{
			const int32_t kiFirstMbIdx		= pSliceCtx->pFirstMbInSlice[iIdx];
			pPriData[iIdx].iStartMbIndex	= kiFirstMbIdx;			
			pPriData[iIdx].iEndMbIndex		= iEndMbIdx;
			iEndMbIdx						= kiFirstMbIdx;
		}
	}

	iIdx = 0;
	while (iIdx < kiEventCnt) {
		pPriData[iIdx].pLayerBs = pLbi;
		pPriData[iIdx].iSliceIndex	= iIdx;
#ifdef WIN32
		if ( pEventsList[iIdx] )
			WelsEventSignal( &pEventsList[iIdx] );
#else
		WelsEventSignal( pEventsList[iIdx] );
#endif//WIN32
		++ pLbi;
		++ iIdx;
	}	
	////////////////////////////////////////
#else
	////////////////////////////////////////
	if ( bIsDynamicSlicingMode )
	{
		iEndMbIdx	= pSliceCtx->iMbNumInFrame;
		for (iIdx = kiEventCnt-1; iIdx >= 0; --iIdx)
		{
			const int32_t iFirstMbIdx		= pSliceCtx->pFirstMbInSlice[iIdx];
			pPriData[iIdx].iStartMbIndex	= iFirstMbIdx;			
			pPriData[iIdx].iEndMbIndex		= iEndMbIdx;
			iEndMbIdx						= iFirstMbIdx;
		}
	}

	iIdx = 0;
	while (iIdx < kiEventCnt) {
		pPriData[iIdx].pLayerBs = pLbi;
		pPriData[iIdx].iSliceIndex	= iIdx;
#ifdef WIN32
		if ( pEventsList[iIdx] )
			WelsEventSignal( &pEventsList[iIdx] );
#else
		WelsEventSignal( pEventsList[iIdx] );
#endif//WIN32
		++ iIdx;
	}		
	////////////////////////////////////////
#endif//PACKING_ONE_SLICE_PER_LAYER

	return 0;
}

int32_t DynamicDetectCpuCores()
{
	WelsLogicalProcessInfo  info;
	WelsQueryLogicalProcessInfo(&info);
	return info.ProcessorCount;
}

#if defined(MT_ENABLED) && defined(DYNAMIC_SLICE_ASSIGN)

int32_t AdjustBaseLayer( sWelsEncCtx *pCtx )
{
	SDqLayer *pCurDq	= pCtx->ppDqLayerList[0];		
	int32_t iNeedAdj	= 1;
#ifdef MT_DEBUG
	int64_t iT0 = WelsTime();
#endif//MT_DEBUG
#ifdef TRY_SLICING_BALANCE
	
	pCtx->pCurDqLayer	= pCurDq;
	
#ifdef NOT_ABSOLUTE_BALANCING
	// do not need adjust due to not different at both slices of consumed time
	iNeedAdj	= NeedDynamicAdjust( pCtx->pSliceThreading->pSliceConsumeTime[0], pCurDq->pSliceEncCtx->iSliceNumInFrame );
	if ( iNeedAdj )
#endif//NOT_ABSOLUTE_BALANCING
	DynamicAdjustSlicing(	pCtx,
							pCurDq,
							pCtx->pSliceThreading->pSliceComplexRatio[0],
							0 );
#endif//TRY_SLICING_BALANCE
#ifdef MT_DEBUG
	iT0 = WelsTime() - iT0;
	if ( pCtx->pSliceThreading->pFSliceDiff )
	{
		fprintf( pCtx->pSliceThreading->pFSliceDiff, 
#ifdef WIN32
				"%6I64d us adjust time at base spatial layer, iNeedAdj %d, DynamicAdjustSlicing()\n",
#else
				"%6lld us adjust time at base spatial layer, iNeedAdj %d, DynamicAdjustSlicing()\n",
#endif//WIN32
				iT0, iNeedAdj );
	}
#endif//MT_DEBUG

	return iNeedAdj;
}

int32_t AdjustEnhanceLayer( sWelsEncCtx *pCtx, int32_t iCurDid )
{
#ifdef MT_DEBUG
	int64_t iT1 = WelsTime();
#endif//MT_DEBUG
	int32_t iNeedAdj = 1;	
	// uiSliceMode of referencing spatial should be SM_FIXEDSLCNUM_SLICE
	// if using spatial base layer for complexity estimation
	const BOOL_T kbModelingFromSpatial =	(pCtx->pCurDqLayer->pRefLayer != NULL && iCurDid > 0) 
										&& (pCtx->pSvcParam->sDependencyLayers[iCurDid-1].sMso.uiSliceMode == SM_FIXEDSLCNUM_SLICE && pCtx->pSvcParam->iMultipleThreadIdc >= pCtx->pSvcParam->sDependencyLayers[iCurDid-1].sMso.sSliceArgument.iSliceNum);

	if ( kbModelingFromSpatial )	// using spatial base layer for complexity estimation
	{	
#ifdef TRY_SLICING_BALANCE
#ifdef NOT_ABSOLUTE_BALANCING
		// do not need adjust due to not different at both slices of consumed time
		iNeedAdj = NeedDynamicAdjust( pCtx->pSliceThreading->pSliceConsumeTime[iCurDid-1], pCtx->pCurDqLayer->pSliceEncCtx->iSliceNumInFrame );
		if ( iNeedAdj )
#endif//NOT_ABSOLUTE_BALANCING
		DynamicAdjustSlicing(	pCtx,
								pCtx->pCurDqLayer,
								pCtx->pSliceThreading->pSliceComplexRatio[iCurDid-1],
								iCurDid
							  );
#endif//TRY_SLICING_BALANCE
	}
	else	// use temporal layer for complexity estimation
	{	
#ifdef TRY_SLICING_BALANCE
#ifdef NOT_ABSOLUTE_BALANCING
		// do not need adjust due to not different at both slices of consumed time
		iNeedAdj = NeedDynamicAdjust( pCtx->pSliceThreading->pSliceConsumeTime[iCurDid], pCtx->pCurDqLayer->pSliceEncCtx->iSliceNumInFrame );
		if ( iNeedAdj )
#endif//NOT_ABSOLUTE_BALANCING
		DynamicAdjustSlicing(	pCtx,
								pCtx->pCurDqLayer,
								pCtx->pSliceThreading->pSliceComplexRatio[iCurDid],
								iCurDid
							  );
#endif//TRY_SLICING_BALANCE
	}

#ifdef MT_DEBUG
	iT1 = WelsTime() - iT1;
	if ( pCtx->pSliceThreading->pFSliceDiff )
	{
		fprintf( pCtx->pSliceThreading->pFSliceDiff, 
#ifdef WIN32
				"%6I64d us adjust time at spatial layer %d, iNeedAdj %d, DynamicAdjustSlicing()\n",
#else
				"%6lld us adjust time at spatial layer %d, iNeedAdj %d, DynamicAdjustSlicing()\n",
#endif//WIN32
				iT1, iCurDid, iNeedAdj );
	}
#endif//MT_DEBUG

	return iNeedAdj;
}

#endif//#if defined(MT_ENABLED) && defined(DYNAMIC_SLICE_ASSIGN)

#if defined(MT_ENABLED)

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE) && defined(MT_DEBUG)
void TrackSliceComplexities( sWelsEncCtx *pCtx, const int32_t iCurDid )
{
	const int32_t kiCountSliceNum = pCtx->pCurDqLayer->pSliceEncCtx->iSliceNumInFrame;
	if ( kiCountSliceNum > 0 )
	{
		int32_t iSliceIdx = 0;
		do {
			fprintf( pCtx->pSliceThreading->pFSliceDiff, "%6.3f complexity pRatio at iDid %d pSlice %d\n", pCtx->pSliceThreading->pSliceComplexRatio[iCurDid][iSliceIdx], iCurDid, iSliceIdx );
			++ iSliceIdx;
		} while(iSliceIdx < kiCountSliceNum);
	}
}
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) && defined(TRY_SLICING_BALANCE)

#if defined(DYNAMIC_SLICE_ASSIGN) && defined(MT_DEBUG)
void TrackSliceConsumeTime( sWelsEncCtx *pCtx, int32_t *pDidList, const int32_t iSpatialNum )
{
	SWelsSvcCodingParam *pPara = NULL;
	int32_t iSpatialIdx = 0;

	if ( iSpatialNum > MAX_DEPENDENCY_LAYER )
		return;

	pPara	= pCtx->pSvcParam;
	while ( iSpatialIdx < iSpatialNum )
	{
		const int32_t kiDid		= pDidList[iSpatialIdx];
		SDLayerParam *pDlp		= &pPara->sDependencyLayers[kiDid];
		SMulSliceOption *pMso	= &pDlp->sMso;
		SDqLayer *pCurDq		= pCtx->ppDqLayerList[kiDid];
		SSliceCtx *pSliceCtx= pCurDq->pSliceEncCtx;
		const uint32_t kuiCountSliceNum = pSliceCtx->iSliceNumInFrame;
		if(pCtx->pSliceThreading)
		{
			if ( pCtx->pSliceThreading->pFSliceDiff && pMso->uiSliceMode == SM_FIXEDSLCNUM_SLICE && pPara->iMultipleThreadIdc > 1 && pPara->iMultipleThreadIdc >= kuiCountSliceNum  )
			{
				uint32_t i = 0;
				uint32_t uiMaxT = 0;
				int32_t iMaxI = 0;
				while (i < kuiCountSliceNum) {
					if ( pCtx->pSliceThreading->pSliceConsumeTime[kiDid] != NULL )
						fprintf( pCtx->pSliceThreading->pFSliceDiff, "%6d us consume_time coding_idx %d iDid %d pSlice %d\n",
						pCtx->pSliceThreading->pSliceConsumeTime[kiDid][i], pCtx->iCodingIndex, kiDid, i /*/ 1000*/);
					if (pCtx->pSliceThreading->pSliceConsumeTime[kiDid][i] > uiMaxT)
					{
						uiMaxT = pCtx->pSliceThreading->pSliceConsumeTime[kiDid][i];
						iMaxI = i;
					}
					++ i;
				}			 
			fprintf( pCtx->pSliceThreading->pFSliceDiff, "%6d us consume_time_max coding_idx %d iDid %d pSlice %d\n", uiMaxT, pCtx->iCodingIndex, kiDid, iMaxI /*/ 1000*/);
			}
		}
		++ iSpatialIdx;
	}
}
#endif//#if defined(DYNAMIC_SLICE_ASSIGN) || defined(MT_DEBUG)

#endif//MT_ENABLED
}
#endif//MT_ENABLED