shithub: openh264

Download patch

ref: b0c6ea9385f16075f49c99f39ec2a122ae3cdfbb
parent: cda6a1fa7605956fae72757bfd71b96070d2fff3
parent: 3350cf75a57271249d305474a5771aabe409c55a
author: sijchen <sijchen@cisco.com>
date: Tue Nov 3 04:05:43 EST 2015

Merge pull request #2206 from sijchen/thp42

[Encoder] adjust encoder tasks, add ut and enable new thread pool under some cases

--- a/codec/common/inc/WelsCircleQueue.h
+++ b/codec/common/inc/WelsCircleQueue.h
@@ -112,6 +112,18 @@
     }
     return NULL;
   }
+
+  TNodeType* GetIndexNode (const int32_t iIdx) {
+    if (size() > 0) {
+      if ((iIdx + m_iCurrentListStart) < m_iMaxNodeCount) {
+        return m_pCurrentQueue[m_iCurrentListStart + iIdx];
+      } else {
+        return m_pCurrentQueue[m_iCurrentListStart + iIdx - m_iMaxNodeCount];
+      }
+    }
+    return NULL;
+  }
+
  private:
   int32_t InternalPushBack (TNodeType* pNode) {
     m_pCurrentQueue[m_iCurrentListEnd] = pNode;
--- a/codec/common/src/WelsThreadLib.cpp
+++ b/codec/common/src/WelsThreadLib.cpp
@@ -313,6 +313,7 @@
     *p_event = NULL;
     return WELS_THREAD_ERROR_GENERAL;
   } else {
+    //printf("event_open:%x, %s\n", p_event, event_name);
     return WELS_THREAD_ERROR_OK;
   }
 #else
@@ -329,6 +330,7 @@
 #endif
 }
 WELS_THREAD_ERROR_CODE    WelsEventClose (WELS_EVENT* event, const char* event_name) {
+  //printf("event_close:%x, %s\n", event, event_name);
 #ifdef __APPLE__
   WELS_THREAD_ERROR_CODE err = sem_close (*event); // match with sem_open
   if (event_name)
--- a/codec/encoder/core/inc/encoder_context.h
+++ b/codec/encoder/core/inc/encoder_context.h
@@ -57,6 +57,7 @@
 
 #include "mt_defs.h" // for multiple threadin,
 #include "WelsThreadLib.h"
+#include "wels_task_management.h"
 
 namespace WelsEnc {
 
@@ -134,7 +135,7 @@
   SWelsFuncPtrList* pFuncList;
 
   SSliceThreading*  pSliceThreading;
-  //IWelsTaskManage*  pTaskManage; //was planning to put it under CWelsH264SVCEncoder but it may be updated (lock/no lock) when param is changed
+  IWelsTaskManage*  pTaskManage; //was planning to put it under CWelsH264SVCEncoder but it may be updated (lock/no lock) when param is changed
 
 // SSlice context
   SSliceCtx*        pSliceCtxList;// slice context table for each dependency quality layer
--- a/codec/encoder/core/inc/wels_task_encoder.h
+++ b/codec/encoder/core/inc/wels_task_encoder.h
@@ -61,6 +61,7 @@
 
   virtual WelsErrorType Execute();
   virtual WelsErrorType InitTask();
+  virtual WelsErrorType ExecuteTask();
   virtual void FinishTask();
 
   virtual uint32_t        GetTaskType() const {
--- a/codec/encoder/core/inc/wels_task_management.h
+++ b/codec/encoder/core/inc/wels_task_management.h
@@ -56,7 +56,7 @@
   virtual WelsErrorType   Init (sWelsEncCtx*   pEncCtx) = 0;
   virtual void            Uninit() = 0;
 
-  virtual void            InitFrame() {}
+  virtual void            InitFrame (const int32_t kiCurDid) {}
   virtual WelsErrorType   ExecuteTasks() = 0;
 
   static IWelsTaskManage* CreateTaskManage (sWelsEncCtx* pCtx, bool bNeedLock);
@@ -75,7 +75,7 @@
   virtual WelsErrorType   Init (sWelsEncCtx*   pEncCtx);
   void    Uninit();
 
-  virtual void            InitFrame();
+  virtual void            InitFrame (const int32_t kiCurDid);
   virtual WelsErrorType   ExecuteTasks();
 
   //IWelsThreadPoolSink
@@ -88,6 +88,7 @@
 
  protected:
   sWelsEncCtx*    m_pEncCtx;
+  WelsCommon::CWelsThreadPool*   m_pThreadPool;
 
   TASKLIST_TYPE*   m_cTaskList;
   int32_t         m_iTaskNum;
@@ -94,7 +95,6 @@
 
   //SLICE_PAIR_LIST *m_cSliceList;
 
-  WelsCommon::CWelsThreadPool*   m_pThreadPool;
   int32_t         m_iThreadNum;
 
   int32_t          m_iWaitTaskNum;
@@ -104,6 +104,7 @@
 
  private:
   DISALLOW_COPY_AND_ASSIGN (CWelsTaskManageBase);
+  void  OnTaskMinusOne();
 };
 
 class  CWelsTaskManageOne : public CWelsTaskManageBase {
--- a/codec/encoder/core/src/encoder_ext.cpp
+++ b/codec/encoder/core/src/encoder_ext.cpp
@@ -2930,6 +2930,10 @@
   } else {
     pCurDq->bBaseLayerAvailableFlag = false;
   }
+
+  if (pCtx->pTaskManage) {
+    pCtx->pTaskManage->InitFrame(kiCurDid);
+  }
 }
 
 static inline void SetFastCodingFunc (SWelsFuncPtrList* pFuncList) {
@@ -4016,7 +4020,7 @@
                    iSliceCount);
           return ENC_RETURN_UNEXPECTED;
         }
-
+        if (SM_AUTO_SLICE == pParam->sSliceCfg.uiSliceMode) {
         if (pSvcParam->iCountThreadsNum >= iSliceCount) {       //THREAD_FULLY_FIRE_MODE
 #if defined(MT_DEBUG)
           int64_t t_bs_append = 0;
@@ -4104,6 +4108,19 @@
           // all slices are finished coding here
           // append exclusive slice 0 bs to pFrameBs
           iLayerSize = AppendSliceToFrameBs (pCtx, pLayerBsInfo, iSliceCount);
+        }
+
+        } else {
+                  pLayerBsInfo->pBsBuf = pCtx->pFrameBs + pCtx->iPosBsBuffer;
+                pLayerBsInfo->uiLayerType   = VIDEO_CODING_LAYER;
+                pLayerBsInfo->uiSpatialId   = pCtx->uiDependencyId;
+                pLayerBsInfo->uiTemporalId  = pCtx->uiTemporalId;
+                pLayerBsInfo->uiQualityId   = 0;
+                pLayerBsInfo->iNalCount     = 0;
+                pCtx->pSliceBs[0].pBs = pLayerBsInfo->pBsBuf;
+
+                pCtx->pTaskManage->ExecuteTasks();
+                iLayerSize = AppendSliceToFrameBs (pCtx, pLayerBsInfo, iSliceCount);
         }
       }
       // THREAD_FULLY_FIRE_MODE && SM_DYN_SLICE
--- a/codec/encoder/core/src/slice_multi_threading.cpp
+++ b/codec/encoder/core/src/slice_multi_threading.cpp
@@ -333,6 +333,7 @@
   int32_t iIdx                  = 0;
   int16_t iMaxSliceNum          = 1;
   int32_t iReturn = ENC_RETURN_SUCCESS;
+  bool bWillUseTaskManage = false;
 
   if (NULL == ppCtx || NULL == pCodingParam || NULL == *ppCtx || iCountBsLen <= 0)
     return 1;
@@ -372,6 +373,10 @@
       pSmt->pSliceConsumeTime[iIdx]     = NULL;
       pSmt->pSliceComplexRatio[iIdx]    = NULL;
     }
+
+    if ( pMso->uiSliceMode == SM_FIXEDSLCNUM_SLICE || pMso->uiSliceMode == SM_RASTER_SLICE || pMso->uiSliceMode == SM_ROWMB_SLICE) {
+      bWillUseTaskManage = true;
+    }
     ++ iIdx;
   }
   // NULL for pSliceConsumeTime[iIdx]: iIdx from iNumSpatialLayers to MAX_DEPENDENCY_LAYERS
@@ -430,9 +435,11 @@
     pSmt->pThreadBsBuffer[iIdx] = NULL;
   }
 
+  //previous conflict
   WelsSnprintf (name, SEM_NAME_MAX, "scm%s", pSmt->eventNamespace);
   err = WelsEventOpen (&pSmt->pSliceCodedMasterEvent, name);
   MT_TRACE_LOG (*ppCtx, WELS_LOG_INFO, "[MT] Open pSliceCodedMasterEvent named(%s) ret%d err%d", name, err, errno);
+  //previous conflict ends
 
 
   iReturn = SetMultiSliceBuffer (ppCtx, pMa, pSmt, iMaxSliceNum,
@@ -444,6 +451,11 @@
   iReturn = WelsMutexInit (&pSmt->mutexSliceNumUpdate);
   WELS_VERIFY_RETURN_PROC_IF (1, (WELS_THREAD_ERROR_OK != iReturn), FreeMemorySvc (ppCtx))
 
+  if (bWillUseTaskManage) {
+    (*ppCtx)->pTaskManage = IWelsTaskManage::CreateTaskManage(*ppCtx, bDynamicSlice);
+    WELS_VERIFY_RETURN_PROC_IF (iReturn, (NULL == (*ppCtx)->pTaskManage), FreeMemorySvc (ppCtx))
+  }
+
   memset(&pSmt->bThreadBsBufferUsage, 0, MAX_THREADS_NUM * sizeof(bool));
   iReturn = WelsMutexInit (&pSmt->mutexThreadBsBufferUsage);
   WELS_VERIFY_RETURN_PROC_IF (1, (WELS_THREAD_ERROR_OK != iReturn), FreeMemorySvc (ppCtx))
@@ -530,6 +542,12 @@
     pMa->WelsFree ((*ppCtx)->pSliceBs, "pSliceBs");
     (*ppCtx)->pSliceBs = NULL;
   }
+
+  if ((*ppCtx)->pTaskManage != NULL) {
+    delete (*ppCtx)->pTaskManage;
+    (*ppCtx)->pTaskManage = NULL;
+  }
+
   iIdx = 0;
   while (iIdx < pCodingParam->iSpatialLayerNum) {
     if (pSmt->pSliceConsumeTime[iIdx]) {
@@ -1171,6 +1189,7 @@
 void SetOneSliceBsBufferUnderMultithread (sWelsEncCtx* pCtx, const int32_t kiThreadIdx, const int32_t iSliceIdx) {
   pCtx->pSliceBs[iSliceIdx].pBsBuffer = pCtx->pSliceThreading->pThreadBsBuffer[kiThreadIdx];
   pCtx->pSliceBs[iSliceIdx].uiBsPos = 0;
+  //printf("SetOneSliceBsBufferUnderMultithread, thread %d, slice %d, buffer=%x\n", kiThreadIdx, iSliceIdx, pCtx->pSliceBs[iSliceIdx].pBsBuffer);
 }
 }
 
--- a/codec/encoder/core/src/wels_task_encoder.cpp
+++ b/codec/encoder/core/src/wels_task_encoder.cpp
@@ -63,6 +63,18 @@
 CWelsSliceEncodingTask::~CWelsSliceEncodingTask() {
 }
 
+WelsErrorType CWelsSliceEncodingTask::Execute() {
+  WelsThreadSetName ("OpenH264Enc_CWelsSliceEncodingTask_Execute");
+
+  int32_t iReturn = InitTask();
+  WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+  iReturn = ExecuteTask();
+
+  FinishTask();
+  return ENC_RETURN_SUCCESS;
+}
+
 WelsErrorType CWelsSliceEncodingTask::SetBoundary (int32_t iStartIdx,  int32_t iEndIdx) {
   m_iStartMbIdx = iStartIdx;
   m_iEndMbIdx = iEndIdx;
@@ -88,10 +100,11 @@
   m_iThreadIdx = QueryEmptyThread (m_pCtx->pSliceThreading->bThreadBsBufferUsage);
   WelsMutexUnlock (&m_pCtx->pSliceThreading->mutexThreadBsBufferUsage);
   if (m_iThreadIdx < 0) {
-    printf ("cannot find avaialble thread %d\n", m_iThreadIdx);
+    WelsLog (&m_pCtx->sLogCtx, WELS_LOG_WARNING,
+             "[MT] CWelsSliceEncodingTask InitTask(), Cannot find available thread for m_iSliceIdx = %d", m_iSliceIdx);
     return ENC_RETURN_UNEXPECTED;
   }
-  SetOneSliceBsBufferUnderMultithread (m_pCtx, m_iSliceIdx, m_iThreadIdx);
+  SetOneSliceBsBufferUnderMultithread (m_pCtx, m_iThreadIdx, m_iSliceIdx);
 
   m_pSlice = &m_pCtx->pCurDqLayer->sLayerInfo.pSliceInLayer[m_iSliceIdx];
   m_pSliceBs = &m_pCtx->pSliceBs[m_iSliceIdx];
@@ -101,9 +114,11 @@
 
   assert ((void*) (&m_pSliceBs->sBsWrite) == (void*)m_pSlice->pSliceBsa);
   InitBits (&m_pSliceBs->sBsWrite, m_pSliceBs->pBsBuffer, m_pSliceBs->uiSize);
+  //printf ("CWelsSliceEncodingTask_InitTask slice %d\n", m_iSliceIdx);
 
   return ENC_RETURN_SUCCESS;
 }
+
 void CWelsSliceEncodingTask::FinishTask() {
   WelsMutexLock (&m_pCtx->pSliceThreading->mutexThreadBsBufferUsage);
   m_pCtx->pSliceThreading->bThreadBsBufferUsage[m_iThreadIdx] = false;
@@ -110,9 +125,7 @@
   WelsMutexUnlock (&m_pCtx->pSliceThreading->mutexThreadBsBufferUsage);
 }
 
-WelsErrorType CWelsSliceEncodingTask::Execute() {
-  WelsThreadSetName ("OpenH264Enc_CWelsSliceEncodingTask_Execute");
-
+WelsErrorType CWelsSliceEncodingTask::ExecuteTask() {
 #if MT_DEBUG_BS_WR
   m_pSliceBs->bSliceCodedFlag = false;
 #endif//MT_DEBUG_BS_WR
@@ -144,6 +157,10 @@
                           iLeftBufferSize,
                           m_iSliceIdx, m_iSliceSize);
   if (ENC_RETURN_SUCCESS != iReturn) {
+    WelsLog (&m_pCtx->sLogCtx, WELS_LOG_WARNING,
+             "[MT] CWelsSliceEncodingTask ExecuteTask(), WriteSliceBs not successful: coding_idx %d, um_iSliceIdx %d",
+             m_pCtx->iCodingIndex,
+             m_iSliceIdx);
     return iReturn;
   }
   if (0 == m_iSliceIdx) {
@@ -152,15 +169,11 @@
 
   m_pCtx->pFuncList->pfDeblocking.pfDeblockingFilterSlice (m_pCtx->pCurDqLayer, m_pCtx->pFuncList, m_iSliceIdx);
 
-#if defined(SLICE_INFO_OUTPUT)
-  fprintf (stderr,
-           "@pSlice=%-6d sliceType:%c idc:%d size:%-6d\n",
-           m_iSliceIdx,
-           (pEncPEncCtx->eSliceType == P_SLICE ? 'P' : 'I'),
-           eNalRefIdc,
-           iSliceSize
-          );
-#endif//SLICE_INFO_OUTPUT
+  WelsLog (&m_pCtx->sLogCtx, WELS_LOG_DETAIL,
+           "@pSlice=%-6d sliceType:%c idc:%d size:%-6d",  m_iSliceIdx,
+           (m_pCtx->eSliceType == P_SLICE ? 'P' : 'I'),
+           m_eNalRefIdc,
+           m_iSliceSize);
 
 #if MT_DEBUG_BS_WR
   m_pSliceBs->bSliceCodedFlag = true;
--- a/codec/encoder/core/src/wels_task_management.cpp
+++ b/codec/encoder/core/src/wels_task_management.cpp
@@ -41,6 +41,7 @@
 #include <assert.h>
 
 #include "typedefs.h"
+#include "utils.h"
 #include "WelsLock.h"
 #include "memory_align.h"
 
@@ -74,8 +75,8 @@
 
 CWelsTaskManageBase::CWelsTaskManageBase()
   : m_pEncCtx (NULL),
-    m_iTaskNum (0),
     m_pThreadPool (NULL),
+    m_iTaskNum (0),
     m_iWaitTaskNum (0) {
   m_cTaskList = new TASKLIST_TYPE();
   WelsEventOpen (&m_hTaskEvent);
@@ -120,8 +121,9 @@
 }
 
 void CWelsTaskManageBase::DestroyTasks() {
-  if (m_iTaskNum == 0)
+  if (m_iTaskNum == 0) {
     return;
+  }
 
   if (m_cTaskList->size() != m_iTaskNum) {
     //printf("m_cTaskList %d %d\n", static_cast<int32_t>(m_cTaskList->size()), m_iTaskNum);
@@ -133,41 +135,47 @@
     WELS_DELETE_OP (pTask);
     m_cTaskList->pop_front();
   }
+  //WelsLog (&m_pEncCtx->sLogCtx, WELS_LOG_INFO,
+  //         "[MT] CWelsTaskManageParallel()DestroyTasks, cleaned %d tasks", m_iTaskNum);
+  //printf ("[MT] CWelsTaskManageBase() DestroyTasks, cleaned %d tasks\n", m_iTaskNum);
   m_iTaskNum = 0;
 }
 
-void CWelsTaskManageBase::InitFrame() {
+void  CWelsTaskManageBase::OnTaskMinusOne() {
+  WelsCommon::CWelsAutoLock cAutoLock (m_cWaitTaskNumLock);
+  m_iWaitTaskNum --;
+  if (m_iWaitTaskNum <= 0) {
+    WelsEventSignal (&m_hTaskEvent);
+  }
+  //printf("OnTaskMinusOne m_iWaitTaskNum=%d\n", m_iWaitTaskNum);
 }
 
-WelsErrorType  CWelsTaskManageBase::ExecuteTasks() {
-  m_iWaitTaskNum = static_cast<int32_t> (m_cTaskList->size());
-  while (NULL != m_cTaskList->begin()) {
-    m_pThreadPool->QueueTask (m_cTaskList->begin());
-    m_cTaskList->pop_front();
-  }
-  WelsEventWait (&m_hTaskEvent);
-
+WelsErrorType  CWelsTaskManageBase::OnTaskCancelled (WelsCommon::IWelsTask* pTask) {
+  OnTaskMinusOne();
   return ENC_RETURN_SUCCESS;
 }
 
 WelsErrorType  CWelsTaskManageBase::OnTaskExecuted (WelsCommon::IWelsTask* pTask) {
-  WelsCommon::CWelsAutoLock cAutoLock (m_cWaitTaskNumLock);
-  m_iWaitTaskNum --;
-  //WELS_INFO_TRACE("Waiting Task Num: " << m_iWaitTaskNum);
-  if (m_iWaitTaskNum == 0) {
-    WelsEventSignal (&m_hTaskEvent);
-    //WELS_INFO_TRACE("Tasks over ");
-  }
-
+  OnTaskMinusOne();
   return ENC_RETURN_SUCCESS;
 }
 
-WelsErrorType  CWelsTaskManageBase::OnTaskCancelled (WelsCommon::IWelsTask* pTask) {
-  WelsCommon::CWelsAutoLock cAutoLock (m_cWaitTaskNumLock);
-  m_iWaitTaskNum --;
-  if (m_iWaitTaskNum == 0) {
-    WelsEventSignal (&m_hTaskEvent);
+void CWelsTaskManageBase::InitFrame (const int32_t kiCurDid) {
+  m_iWaitTaskNum = m_pEncCtx->pSvcParam->sSpatialLayers[kiCurDid].sSliceCfg.sSliceArgument.uiSliceNum;
+  //printf("InitFrame m_iWaitTaskNum=%d, slice_mode=%d\n", m_iWaitTaskNum, m_pEncCtx->pSvcParam->sSpatialLayers[kiCurDid].sSliceCfg.uiSliceMode);
+  //TODO: update mbmap;
+}
+
+WelsErrorType  CWelsTaskManageBase::ExecuteTasks() {
+  //printf("ExecuteTasks m_iWaitTaskNum=%d\n", m_iWaitTaskNum);
+  int32_t iCurrentTaskCount = m_iWaitTaskNum; //if directly use m_iWaitTaskNum in the loop make cause sync problem
+  int32_t iIdx = 0;
+   while (iIdx < iCurrentTaskCount) {
+    m_pThreadPool->QueueTask (m_cTaskList->GetIndexNode(iIdx));
+    iIdx ++;
   }
+  WelsEventWait (&m_hTaskEvent);
+
   return ENC_RETURN_SUCCESS;
 }
 
--- a/test/api/encode_decode_api_test.cpp
+++ b/test/api/encode_decode_api_test.cpp
@@ -3727,4 +3727,133 @@
 #endif
 }
 
+TEST_F (EncodeDecodeTestAPI, DiffSlicingInDlayer) {
+  int iSpatialLayerNum = 3;
+  int iWidth       = WelsClip3 ((((rand() % MAX_WIDTH) >> 1)  + 1) << 1, (64 << 2), MAX_WIDTH);
+  int iHeight      = WelsClip3 ((((rand() % MAX_HEIGHT) >> 1)  + 1) << 1, (64 << 2), 2240);//TODO: use MAX_HEIGHT after the limit is removed
+  float fFrameRate = rand() + 0.5f;
+  int iEncFrameNum = WelsClip3 ((rand() % ENCODE_FRAME_NUM) + 1, 1, ENCODE_FRAME_NUM);
+
+  // prepare params
+  SEncParamExt   sParam;
+  encoder_->GetDefaultParams (&sParam);
+  prepareParamDefault (iSpatialLayerNum, 1, iWidth, iHeight, fFrameRate, &sParam);
+  sParam.iMultipleThreadIdc = (rand() % 4) + 1;
+  sParam.bSimulcastAVC = 1;
+  sParam.sSpatialLayers[0].iVideoWidth = (iWidth >> 2);
+  sParam.sSpatialLayers[0].iVideoHeight = (iHeight >> 2);
+  sParam.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_ROWMB_SLICE;
+
+  sParam.sSpatialLayers[1].iVideoWidth = (iWidth >> 1);
+  sParam.sSpatialLayers[1].iVideoHeight = (iHeight >> 1);
+  sParam.sSpatialLayers[1].sSliceCfg.uiSliceMode = SM_RASTER_SLICE;
+  sParam.sSpatialLayers[1].sSliceCfg.sSliceArgument.uiSliceMbNum[0] = 30;
+  sParam.sSpatialLayers[1].sSliceCfg.sSliceArgument.uiSliceMbNum[1] = 32;
+
+  sParam.sSpatialLayers[2].iVideoWidth = iWidth;
+  sParam.sSpatialLayers[2].iVideoHeight = iHeight;
+  sParam.sSpatialLayers[2].sSliceCfg.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
+  sParam.sSpatialLayers[2].sSliceCfg.sSliceArgument.uiSliceNum = (rand() % 30) + 1;
+
+  int rv = encoder_->InitializeExt (&sParam);
+  ASSERT_TRUE (rv == cmResultSuccess) << "Init Failed sParam: rv = " << rv;;
+
+  unsigned char*  pBsBuf[MAX_SPATIAL_LAYER_NUM];
+  ISVCDecoder* decoder[MAX_SPATIAL_LAYER_NUM];
+
+  int iIdx = 0;
+
+  //create decoder
+  for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    pBsBuf[iIdx] = static_cast<unsigned char*> (malloc (iWidth * iHeight * 3 * sizeof (unsigned char) / 2));
+    EXPECT_TRUE (pBsBuf[iIdx] != NULL);
+
+    long rv = WelsCreateDecoder (&decoder[iIdx]);
+    ASSERT_EQ (0, rv);
+    EXPECT_TRUE (decoder[iIdx] != NULL);
+
+    SDecodingParam decParam;
+    memset (&decParam, 0, sizeof (SDecodingParam));
+    decParam.eOutputColorFormat  = videoFormatI420;
+    decParam.uiTargetDqLayer = UCHAR_MAX;
+    decParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
+    decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
+
+    rv = decoder[iIdx]->Initialize (&decParam);
+    ASSERT_EQ (0, rv);
+  }
+
+  TestOneSimulcastAVC (&sParam, decoder, pBsBuf, iSpatialLayerNum, iEncFrameNum, 0);
+
+  for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    free (pBsBuf[iIdx]);
+
+    if (decoder[iIdx] != NULL) {
+      decoder[iIdx]->Uninitialize();
+      WelsDestroyDecoder (decoder[iIdx]);
+    }
+
+  }
+}
+
+TEST_F (EncodeDecodeTestAPI, ThreadNumAndSliceNum) {
+  int iSpatialLayerNum = 1;
+  int iWidth       = WelsClip3 ((((rand() % MAX_WIDTH) >> 1)  + 1) << 1, (64 << 2), MAX_WIDTH);
+  int iHeight      = WelsClip3 ((((rand() % MAX_HEIGHT) >> 1)  + 1) << 1, (64 << 2),
+                                2240);//TODO: use MAX_HEIGHT after the limit is removed
+  float fFrameRate = rand() + 0.5f;
+  int iEncFrameNum = WelsClip3 ((rand() % ENCODE_FRAME_NUM) + 1, 1, ENCODE_FRAME_NUM);
+
+  // prepare params
+  SEncParamExt   sParam;
+  encoder_->GetDefaultParams (&sParam);
+  prepareParamDefault (iSpatialLayerNum, 1, iWidth, iHeight, fFrameRate, &sParam);
+  sParam.iMultipleThreadIdc = (rand() % 3) + 2;
+  sParam.bSimulcastAVC = 1;
+  sParam.sSpatialLayers[0].iVideoWidth = iWidth;
+  sParam.sSpatialLayers[0].iVideoHeight = iHeight;
+  sParam.sSpatialLayers[0].sSliceCfg.uiSliceMode = SM_FIXEDSLCNUM_SLICE;
+  sParam.sSpatialLayers[0].sSliceCfg.sSliceArgument.uiSliceNum = (rand() % 2) ? (sParam.iMultipleThreadIdc + 1) :
+      (sParam.iMultipleThreadIdc - 1);
+
+  int rv = encoder_->InitializeExt (&sParam);
+  ASSERT_TRUE (rv == cmResultSuccess) << "Init Failed sParam: rv = " << rv;;
+
+  unsigned char*  pBsBuf[MAX_SPATIAL_LAYER_NUM];
+  ISVCDecoder* decoder[MAX_SPATIAL_LAYER_NUM];
+
+  int iIdx = 0;
+
+  //create decoder
+  for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    pBsBuf[iIdx] = static_cast<unsigned char*> (malloc (iWidth * iHeight * 3 * sizeof (unsigned char) / 2));
+    EXPECT_TRUE (pBsBuf[iIdx] != NULL);
+
+    long rv = WelsCreateDecoder (&decoder[iIdx]);
+    ASSERT_EQ (0, rv);
+    EXPECT_TRUE (decoder[iIdx] != NULL);
+
+    SDecodingParam decParam;
+    memset (&decParam, 0, sizeof (SDecodingParam));
+    decParam.eOutputColorFormat  = videoFormatI420;
+    decParam.uiTargetDqLayer = UCHAR_MAX;
+    decParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
+    decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
+
+    rv = decoder[iIdx]->Initialize (&decParam);
+    ASSERT_EQ (0, rv);
+  }
+
+  TestOneSimulcastAVC (&sParam, decoder, pBsBuf, iSpatialLayerNum, iEncFrameNum, 0);
+
+  for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    free (pBsBuf[iIdx]);
+
+    if (decoder[iIdx] != NULL) {
+      decoder[iIdx]->Uninitialize();
+      WelsDestroyDecoder (decoder[iIdx]);
+    }
+
+  }
+}
 
--- a/test/build/win32/codec_ut/codec_unittest.vcproj
+++ b/test/build/win32/codec_ut/codec_unittest.vcproj
@@ -866,6 +866,10 @@
 					/>
 				</FileConfiguration>
 			</File>
+			<File
+				RelativePath="..\..\..\common\WelsThreadPoolTest.cpp"
+				>
+			</File>
 		</Filter>
 	</Files>
 	<Globals>
--- a/test/common/CWelsCircleQueue.cpp
+++ b/test/common/CWelsCircleQueue.cpp
@@ -194,3 +194,50 @@
 }
 #endif
 
+
+TEST (CWelsCircleQueue, CWelsCircleQueueReadWithIdx) {
+  CWelsCircleQueue<int32_t> cThreadQueue;
+  const int kiIncreaseNum = (rand() % 1000) + 1;
+  const int kiDecreaseNum = rand() % kiIncreaseNum;
+
+  int32_t* pInput = static_cast<int32_t*> (malloc (kiIncreaseNum * 10 * sizeof (int32_t)));
+  if (!pInput) {
+    return;
+  }
+  for (int32_t i = 0; i < kiIncreaseNum * 10; i++) {
+    pInput[i] = i;
+  }
+
+  for (int j = 0; j < 10; j++) {
+    const int iBias = j * (kiIncreaseNum - kiDecreaseNum);
+    for (int i = 0; i < kiIncreaseNum; i++) {
+      cThreadQueue.push_back (&pInput[i + kiIncreaseNum * j]);
+    }
+    EXPECT_TRUE (kiIncreaseNum + iBias == cThreadQueue.size()) << "after push size=" <<
+        cThreadQueue.size() ;
+
+    EXPECT_TRUE ((kiDecreaseNum * j) == * (cThreadQueue.begin()));
+
+    for (int i = 0; i < kiIncreaseNum; i++) {
+      EXPECT_TRUE ((i + kiIncreaseNum * j) == * (cThreadQueue.GetIndexNode (i + iBias)));
+    }
+    for (int i = 0; i < cThreadQueue.size(); i++) {
+      EXPECT_TRUE ((i + kiDecreaseNum * j) == * (cThreadQueue.GetIndexNode (i)));
+    }
+
+    for (int i = kiDecreaseNum; i > 0; i--) {
+      cThreadQueue.pop_front();
+    }
+    EXPECT_TRUE ((j + 1) * (kiIncreaseNum - kiDecreaseNum) == cThreadQueue.size()) << "after pop size=" <<
+        cThreadQueue.size() ;
+
+    EXPECT_TRUE ((kiDecreaseNum * (j + 1)) == * (cThreadQueue.begin()));
+  }
+
+  //clean-up
+  while (NULL != cThreadQueue.begin()) {
+    cThreadQueue.pop_front();
+  }
+  EXPECT_TRUE (0 == cThreadQueue.size());
+  free (pInput);
+}
--- /dev/null
+++ b/test/common/WelsThreadPoolTest.cpp
@@ -1,0 +1,54 @@
+#include <gtest/gtest.h>
+#include <string.h>
+#include <string>
+#include <list>
+#include <map>
+
+#include "typedefs.h"
+#include "WelsThreadLib.h"
+#include "WelsThreadPool.h"
+#include "WelsTask.h"
+#include "WelsThreadPoolTest.h"
+
+#define  TEST_TASK_NUM  20
+
+class CSimpleTask : public IWelsTask {
+ public:
+  static uint32_t id;
+
+  CSimpleTask() {
+    m_uiID = id ++;
+  }
+
+  virtual ~CSimpleTask() {
+  }
+
+  virtual int32_t Execute() {
+    WelsSleep (300 - m_uiID);
+    //printf ("Task %d executing\n", m_uiID);
+    return cmResultSuccess;
+  }
+
+ private:
+  uint32_t m_uiID;
+};
+
+uint32_t CSimpleTask::id = 0;
+
+
+TEST (CThreadPoolTest, CThreadPoolTest) {
+  CSimpleTask tasks[TEST_TASK_NUM];
+  CThreadPoolTest cThreadPoolTest;
+  CWelsThreadPool  cThreadPool (&cThreadPoolTest);
+
+  int32_t  i;
+
+  for (i = 0; i < TEST_TASK_NUM; i++) {
+    cThreadPool.QueueTask (&tasks[i]);
+  }
+
+  while (cThreadPoolTest.GetTaskCount() < TEST_TASK_NUM) {
+    WelsSleep (1);
+  }
+}
+
--- /dev/null
+++ b/test/common/WelsThreadPoolTest.h
@@ -1,0 +1,39 @@
+#ifndef _WELS_THREAD_POOL_TEST_H_
+#define _WELS_THREAD_POOL_TEST_H_
+
+#include "WelsThreadPool.h"
+
+using namespace WelsCommon;
+
+class CThreadPoolTest : public IWelsThreadPoolSink {
+ public:
+  CThreadPoolTest() {
+    m_iTaskCount = 0;
+  }
+
+  ~CThreadPoolTest() {}
+
+  virtual int32_t OnTaskExecuted (IWelsTask* pTask) {
+    m_iTaskCount ++;
+    //printf("Task execute over count is %d\n", m_iTaskCount);
+    return cmResultSuccess;
+  }
+
+  virtual int32_t OnTaskCancelled (IWelsTask* pTask) {
+    m_iTaskCount ++;
+    //printf("Task execute cancelled count is %d\n", m_iTaskCount);
+    return cmResultSuccess;
+  }
+
+  int32_t  GetTaskCount() {
+    return m_iTaskCount;
+  }
+
+ private:
+  int32_t  m_iTaskCount;
+};
+
+
+
+#endif
+
--- a/test/common/targets.mk
+++ b/test/common/targets.mk
@@ -1,8 +1,9 @@
 COMMON_UNITTEST_SRCDIR=test/common
 COMMON_UNITTEST_CPP_SRCS=\
-	$(COMMON_UNITTEST_SRCDIR)/ExpandPicture.cpp\
 	$(COMMON_UNITTEST_SRCDIR)/CWelsCircleQueue.cpp\
 	$(COMMON_UNITTEST_SRCDIR)/CWelsListTest.cpp\
+	$(COMMON_UNITTEST_SRCDIR)/ExpandPicture.cpp\
+	$(COMMON_UNITTEST_SRCDIR)/WelsThreadPoolTest.cpp\
 
 COMMON_UNITTEST_OBJS += $(COMMON_UNITTEST_CPP_SRCS:.cpp=.$(OBJ))