shithub: openh264

Download patch

ref: 776954f0c08ecc28bc99eb125b2c7d0f654f420a
parent: b523d58656a06e835d53cda36cd295d0b3fff33d
parent: 4863c6602aae600f6985f3069401341bacfbc6c2
author: huili2 <huili2@cisco.com>
date: Mon Nov 4 18:20:52 EST 2019

Merge pull request #3191 from xiaotianshi2/thread_commit_4_upated

commit-4 of multi-thread decoding support.

--- a/codec/api/svc/codec_app_def.h
+++ b/codec/api/svc/codec_app_def.h
@@ -167,8 +167,8 @@
   DECODER_OPTION_LEVEL,                 ///< get current AU level info,only is used in GetOption
   DECODER_OPTION_STATISTICS_LOG_INTERVAL,///< set log output interval
   DECODER_OPTION_IS_REF_PIC,             ///< feedback current frame is ref pic or not
-  DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER  ///< number of frames remaining in decoder buffer when pictures are required to re-ordered into display-order.
-
+  DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER,  ///< number of frames remaining in decoder buffer when pictures are required to re-ordered into display-order.
+  DECODER_OPTION_NUM_OF_THREADS,         ///< number of decoding threads. The maximum thread count is equal or less than lesser of (cpu core counts and 16).
 } DECODER_OPTION;
 
 /**
--- a/codec/console/dec/src/h264dec.cpp
+++ b/codec/console/dec/src/h264dec.cpp
@@ -52,7 +52,6 @@
 #include "measure_time.h"
 #include "d3d9_utils.h"
 
-
 using namespace std;
 
 #if defined (WINDOWS_PHONE)
@@ -69,6 +68,106 @@
 #endif
 //using namespace WelsDec;
 
+int32_t readPicture (uint8_t* pBuf, const int32_t& iFileSize, const int32_t& bufPos, uint8_t*& pSpsBuf,
+                     int32_t& sps_byte_count) {
+  int32_t bytes_available = iFileSize - bufPos;
+  if (bytes_available < 4) {
+    return bytes_available;
+  }
+  uint8_t* ptr = pBuf + bufPos;
+  int32_t read_bytes = 0;
+  int32_t sps_count = 0;
+  int32_t pps_count = 0;
+  int32_t non_idr_pict_count = 0;
+  int32_t idr_pict_count = 0;
+  pSpsBuf = NULL;
+  sps_byte_count = 0;
+  while (read_bytes < bytes_available - 4) {
+    bool has4ByteStartCode = ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 1;
+    bool has3ByteStartCode = false;
+    if (!has4ByteStartCode) {
+      has3ByteStartCode = ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 1;
+    }
+    if (has4ByteStartCode || has3ByteStartCode) {
+      uint8_t nal_unit_type = has4ByteStartCode ? (ptr[4] & 0x1F) : (ptr[3] & 0x1F);
+      if (nal_unit_type == 1) {
+        if (++non_idr_pict_count == 1 && idr_pict_count == 1) {
+          return read_bytes;
+        }
+        if (non_idr_pict_count == 2) {
+          return read_bytes;
+        }
+      } else if (nal_unit_type == 5) {
+        if (++idr_pict_count == 1 && non_idr_pict_count == 1) {
+          return read_bytes;
+        }
+        if (idr_pict_count == 2) {
+          return read_bytes;
+        }
+      } else if (nal_unit_type == 7) {
+        pSpsBuf = ptr + (has4ByteStartCode ? 4 : 3);
+        if ((++sps_count == 1) && (non_idr_pict_count == 1 || idr_pict_count == 1)) {
+          return read_bytes;
+        }
+      } else if (nal_unit_type == 8) {
+        if (++pps_count == 1 && sps_count == 1) {
+          sps_byte_count = int32_t (ptr - pSpsBuf);
+        }
+      }
+      if (read_bytes >= bytes_available - 4) {
+        return bytes_available;
+      }
+      read_bytes += 4;
+      ptr += 4;
+    } else {
+      ++ptr;
+      ++read_bytes;
+    }
+  }
+  return bytes_available;
+}
+
+void FlushFrames (ISVCDecoder* pDecoder, int64_t& iTotal, FILE* pYuvFile, FILE* pOptionFile, int32_t& iFrameCount,
+                  unsigned long long& uiTimeStamp, int32_t& iWidth, int32_t& iHeight, int32_t& iLastWidth, int32_t iLastHeight) {
+  uint8_t* pData[3] = { NULL };
+  uint8_t* pDst[3] = { NULL };
+  SBufferInfo sDstBufInfo;
+  int32_t num_of_frames_in_buffer = 0;
+  CUtils cOutputModule;
+  pDecoder->GetOption (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER, &num_of_frames_in_buffer);
+  for (int32_t i = 0; i < num_of_frames_in_buffer; ++i) {
+    int64_t iStart = WelsTime();
+    pData[0] = NULL;
+    pData[1] = NULL;
+    pData[2] = NULL;
+    memset (&sDstBufInfo, 0, sizeof (SBufferInfo));
+    sDstBufInfo.uiInBsTimeStamp = uiTimeStamp;
+    sDstBufInfo.iBufferStatus = 1;
+    pDecoder->FlushFrame (pData, &sDstBufInfo);
+    if (sDstBufInfo.iBufferStatus == 1) {
+      pDst[0] = pData[0];
+      pDst[1] = pData[1];
+      pDst[2] = pData[2];
+    }
+    int64_t iEnd = WelsTime();
+    iTotal += iEnd - iStart;
+    if (sDstBufInfo.iBufferStatus == 1) {
+      cOutputModule.Process ((void**)pDst, &sDstBufInfo, pYuvFile);
+      iWidth = sDstBufInfo.UsrData.sSystemBuffer.iWidth;
+      iHeight = sDstBufInfo.UsrData.sSystemBuffer.iHeight;
+      if (pOptionFile != NULL) {
+        if (iWidth != iLastWidth && iHeight != iLastHeight) {
+          fwrite (&iFrameCount, sizeof (iFrameCount), 1, pOptionFile);
+          fwrite (&iWidth, sizeof (iWidth), 1, pOptionFile);
+          fwrite (&iHeight, sizeof (iHeight), 1, pOptionFile);
+          iLastWidth = iWidth;
+          iLastHeight = iHeight;
+        }
+      }
+      ++iFrameCount;
+    }
+  }
+}
 void H264DecodeInstance (ISVCDecoder* pDecoder, const char* kpH264FileName, const char* kpOuputFileName,
                          int32_t& iWidth, int32_t& iHeight, const char* pOptionFileName, const char* pLengthFileName,
                          int32_t iErrorConMethod,
@@ -95,15 +194,18 @@
 
   int32_t iBufPos = 0;
   int32_t iFileSize;
-  int32_t i = 0;
   int32_t iLastWidth = 0, iLastHeight = 0;
   int32_t iFrameCount = 0;
   int32_t iEndOfStreamFlag = 0;
-  int32_t num_of_frames_in_buffer = 0;
   pDecoder->SetOption (DECODER_OPTION_ERROR_CON_IDC, &iErrorConMethod);
   CUtils cOutputModule;
   double dElapsed = 0;
+  uint8_t uLastSpsBuf[32];
+  int32_t iLastSpsByteCount = 0;
 
+  int32_t iThreadCount = 1;
+  pDecoder->GetOption (DECODER_OPTION_NUM_OF_THREADS, &iThreadCount);
+
   if (kpH264FileName) {
     pH264File = fopen (kpH264FileName, "rb");
     if (pH264File == NULL) {
@@ -181,13 +283,32 @@
         goto label_exit;
       iSliceSize = static_cast<int32_t> (pInfo[2]);
     } else {
-      for (i = 0; i < iFileSize; i++) {
-        if ((pBuf[iBufPos + i] == 0 && pBuf[iBufPos + i + 1] == 0 && pBuf[iBufPos + i + 2] == 0 && pBuf[iBufPos + i + 3] == 1
-             && i > 0) || (pBuf[iBufPos + i] == 0 && pBuf[iBufPos + i + 1] == 0 && pBuf[iBufPos + i + 2] == 1 && i > 0)) {
-          break;
+      if (iThreadCount > 1) {
+        uint8_t* uSpsPtr = NULL;
+        int32_t iSpsByteCount = 0;
+        iSliceSize = readPicture (pBuf, iFileSize, iBufPos, uSpsPtr, iSpsByteCount);
+        if (iLastSpsByteCount > 0 && iSpsByteCount > 0) {
+          if (iSpsByteCount != iLastSpsByteCount || memcmp (uSpsPtr, uLastSpsBuf, iLastSpsByteCount) != 0) {
+            //whenever new sequence is different from preceding sequence. All pending frames must be flushed out before the new sequence can start to decode.
+            FlushFrames (pDecoder, iTotal, pYuvFile, pOptionFile, iFrameCount, uiTimeStamp, iWidth, iHeight, iLastWidth,
+                         iLastHeight);
+          }
         }
+        if (iSpsByteCount > 0 && uSpsPtr != NULL) {
+          if (iSpsByteCount > 32) iSpsByteCount = 32;
+          iLastSpsByteCount = iSpsByteCount;
+          memcpy (uLastSpsBuf, uSpsPtr, iSpsByteCount);
+        }
+      } else {
+        int i = 0;
+        for (i = 0; i < iFileSize; i++) {
+          if ((pBuf[iBufPos + i] == 0 && pBuf[iBufPos + i + 1] == 0 && pBuf[iBufPos + i + 2] == 0 && pBuf[iBufPos + i + 3] == 1
+               && i > 0) || (pBuf[iBufPos + i] == 0 && pBuf[iBufPos + i + 1] == 0 && pBuf[iBufPos + i + 2] == 1 && i > 0)) {
+            break;
+          }
+        }
+        iSliceSize = i;
       }
-      iSliceSize = i;
     }
     if (iSliceSize < 4) { //too small size, no effective data, ignore
       iBufPos += iSliceSize;
@@ -283,41 +404,8 @@
     iBufPos += iSliceSize;
     ++ iSliceIndex;
   }
-
-  pDecoder->GetOption (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER, &num_of_frames_in_buffer);
-  for (int32_t i = 0; i < num_of_frames_in_buffer; ++i) {
-    iStart = WelsTime();
-    pData[0] = NULL;
-    pData[1] = NULL;
-    pData[2] = NULL;
-    memset (&sDstBufInfo, 0, sizeof (SBufferInfo));
-    sDstBufInfo.uiInBsTimeStamp = uiTimeStamp;
-    sDstBufInfo.iBufferStatus = 1;
-    pDecoder->FlushFrame (pData, &sDstBufInfo);
-    if (sDstBufInfo.iBufferStatus == 1) {
-      pDst[0] = pData[0];
-      pDst[1] = pData[1];
-      pDst[2] = pData[2];
-    }
-    iEnd = WelsTime();
-    iTotal += iEnd - iStart;
-    if (sDstBufInfo.iBufferStatus == 1) {
-      cOutputModule.Process ((void**)pDst, &sDstBufInfo, pYuvFile);
-      iWidth = sDstBufInfo.UsrData.sSystemBuffer.iWidth;
-      iHeight = sDstBufInfo.UsrData.sSystemBuffer.iHeight;
-
-      if (pOptionFile != NULL) {
-        if (iWidth != iLastWidth && iHeight != iLastHeight) {
-          fwrite (&iFrameCount, sizeof (iFrameCount), 1, pOptionFile);
-          fwrite (&iWidth, sizeof (iWidth), 1, pOptionFile);
-          fwrite (&iHeight, sizeof (iHeight), 1, pOptionFile);
-          iLastWidth = iWidth;
-          iLastHeight = iHeight;
-        }
-      }
-      ++iFrameCount;
-    }
-  }
+  FlushFrames (pDecoder, iTotal, pYuvFile, pOptionFile, iFrameCount, uiTimeStamp, iWidth, iHeight, iLastWidth,
+               iLastHeight);
   dElapsed = iTotal / 1e6;
   fprintf (stderr, "-------------------------------------------------------\n");
   fprintf (stderr, "iWidth:\t\t%d\nheight:\t\t%d\nFrames:\t\t%d\ndecode time:\t%f sec\nFPS:\t\t%f fps\n",
@@ -488,6 +576,9 @@
   if (iLevelSetting >= 0) {
     pDecoder->SetOption (DECODER_OPTION_TRACE_LEVEL, &iLevelSetting);
   }
+
+  int32_t iThreadCount = 1;
+  pDecoder->SetOption (DECODER_OPTION_NUM_OF_THREADS, &iThreadCount);
 
   if (pDecoder->Initialize (&sDecParam)) {
     printf ("Decoder initialization failed.\n");
--- a/codec/decoder/plus/inc/welsDecoderExt.h
+++ b/codec/decoder/plus/inc/welsDecoderExt.h
@@ -109,22 +109,51 @@
   virtual long EXTAPI SetOption (DECODER_OPTION eOptID, void* pOption);
   virtual long EXTAPI GetOption (DECODER_OPTION eOptID, void* pOption);
 
+ public:
+  DECODING_STATE DecodeFrame2WithCtx (PWelsDecoderContext pCtx, const unsigned char* kpSrc, const int kiSrcLen,
+                                      unsigned char** ppDst, SBufferInfo* pDstInfo);
+  DECODING_STATE ParseAccessUnit (SWelsDecoderThreadCTX& sThreadCtx);
+
  private:
-  PWelsDecoderContext     m_pDecContext;
   welsCodecTrace*         m_pWelsTrace;
+  uint32_t                m_uiDecodeTimeStamp;
+  bool                    m_bIsBaseline;
+  int32_t                 m_iCpuCount;
+  int32_t                 m_iThreadCount;
+  PPicBuff                m_pPicBuff;
+  bool                    m_bParamSetsLostFlag;
+  bool                    m_bFreezeOutput;
+  int32_t                 m_DecCtxActiveCount;
+  PWelsDecoderThreadCTX   m_pDecThrCtx;
+  PWelsDecoderThreadCTX   m_pLastDecThrCtx;
+  WELS_MUTEX              m_csDecoder;
+  SWelsDecEvent           m_sBufferingEvent;
+  SWelsDecEvent           m_sReleaseBufferEvent;
+  SWelsDecSemphore        m_sIsBusy;
   SPictInfo               m_sPictInfoList[16];
   SPictReoderingStatus    m_sReoderingStatus;
+  PWelsDecoderThreadCTX   m_pDecThrCtxActive[WELS_DEC_MAX_NUM_CPU];
   SVlcTable               m_sVlcTable;
   SWelsLastDecPicInfo     m_sLastDecPicInfo;
   SDecoderStatistics      m_sDecoderStatistics;// For real time debugging
 
+ private:
   int32_t InitDecoder (const SDecodingParam* pParam);
   void UninitDecoder (void);
-  int32_t ResetDecoder();
+  int32_t InitDecoderCtx (PWelsDecoderContext& pCtx, const SDecodingParam* pParam);
+  void UninitDecoderCtx (PWelsDecoderContext& pCtx);
+  int32_t ResetDecoder (PWelsDecoderContext& pCtx);
+  int32_t ThreadResetDecoder (PWelsDecoderContext& pCtx);
 
   void OutputStatisticsLog (SDecoderStatistics& sDecoderStatistics);
-  DECODING_STATE ReorderPicturesInDisplay (unsigned char** ppDst, SBufferInfo* pDstInfo);
+  DECODING_STATE ReorderPicturesInDisplay (PWelsDecoderContext pCtx, unsigned char** ppDst, SBufferInfo* pDstInfo);
+  int ThreadDecodeFrameInternal (const unsigned char* kpSrc, const int kiSrcLen, unsigned char** ppDst,
+                                 SBufferInfo* pDstInfo);
+  void BufferingReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst, SBufferInfo* pDstInfo);
+  void ReleaseBufferedReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst, SBufferInfo* pDstInfo);
 
+  void OpenDecoderThreads();
+  void CloseDecoderThreads();
 #ifdef OUTPUT_BIT_STREAM
   WelsFileHandle* m_pFBS;
   WelsFileHandle* m_pFBSSize;
--- a/codec/decoder/plus/src/welsDecoderExt.cpp
+++ b/codec/decoder/plus/src/welsDecoderExt.cpp
@@ -51,6 +51,7 @@
 //#include "macros.h"
 #include "decoder.h"
 #include "decoder_core.h"
+#include "manage_dec_ref.h"
 #include "error_concealment.h"
 
 #include "measure_time.h"
@@ -67,12 +68,11 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <sys/types.h>
+#include <malloc.h>
 #else
 #include <sys/time.h>
 #endif
 
-#define _PICTURE_REORDERING_ 1
-
 namespace WelsDec {
 
 //////////////////////////////////////////////////////////////////////
@@ -88,9 +88,58 @@
 *
 *   return: none
 ***************************************************************************/
+DECLARE_PROCTHREAD (pThrProcInit, p) {
+  SWelsDecThreadInfo* sThreadInfo = (SWelsDecThreadInfo*)p;
+#if defined(WIN32)
+  _alloca (WELS_DEC_MAX_THREAD_STACK_SIZE * (sThreadInfo->uiThrNum + 1));
+#endif
+  return sThreadInfo->pThrProcMain (p);
+}
+
+static DECODING_STATE  ConstructAccessUnit (CWelsDecoder* pWelsDecoder, PWelsDecoderThreadCTX pThrCtx) {
+  int iRet = dsErrorFree;
+  //WelsMutexLock (&pWelsDecoder->m_csDecoder);
+  if (pThrCtx->pCtx->pLastThreadCtx != NULL) {
+    PWelsDecoderThreadCTX pLastThreadCtx = (PWelsDecoderThreadCTX) (pThrCtx->pCtx->pLastThreadCtx);
+    WAIT_EVENT (&pLastThreadCtx->sSliceDecodeStart, WELS_DEC_THREAD_WAIT_INFINITE);
+    RESET_EVENT (&pLastThreadCtx->sSliceDecodeStart);
+  }
+  pThrCtx->pDec = NULL;
+  RESET_EVENT (&pThrCtx->sSliceDecodeFinsh);
+  iRet |= pWelsDecoder->DecodeFrame2WithCtx (pThrCtx->pCtx, NULL, 0, pThrCtx->ppDst, &pThrCtx->sDstInfo);
+
+  //WelsMutexUnlock (&pWelsDecoder->m_csDecoder);
+  return (DECODING_STATE)iRet;
+}
+
+DECLARE_PROCTHREAD (pThrProcFrame, p) {
+  SWelsDecoderThreadCTX* pThrCtx = (SWelsDecoderThreadCTX*)p;
+  while (1) {
+    RELEASE_SEMAPHORE (pThrCtx->sThreadInfo.sIsBusy);
+    RELEASE_SEMAPHORE (&pThrCtx->sThreadInfo.sIsIdle);
+    WAIT_SEMAPHORE (&pThrCtx->sThreadInfo.sIsActivated, WELS_DEC_THREAD_WAIT_INFINITE);
+    if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_RUN) {
+      CWelsDecoder* pWelsDecoder = (CWelsDecoder*)pThrCtx->threadCtxOwner;
+      ConstructAccessUnit (pWelsDecoder, pThrCtx);
+    } else if (pThrCtx->sThreadInfo.uiCommand == WELS_DEC_THREAD_COMMAND_ABORT) {
+      break;
+    }
+  }
+  return 0;
+}
+
 CWelsDecoder::CWelsDecoder (void)
-  : m_pDecContext (NULL),
-    m_pWelsTrace (NULL) {
+  : m_pWelsTrace (NULL),
+    m_uiDecodeTimeStamp (0),
+    m_bIsBaseline (false),
+    m_iCpuCount (1),
+    m_iThreadCount (1),
+    m_pPicBuff (NULL),
+    m_bParamSetsLostFlag (false),
+    m_bFreezeOutput (false),
+    m_DecCtxActiveCount (0),
+    m_pDecThrCtx (NULL),
+    m_pLastDecThrCtx (NULL) {
 #ifdef OUTPUT_BIT_STREAM
   char chFileName[1024] = { 0 };  //for .264
   int iBufUsed = 0;
@@ -114,6 +163,15 @@
 
   ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true);
 
+  m_iCpuCount = GetCPUCount();
+  if (m_iCpuCount > WELS_DEC_MAX_NUM_CPU) {
+    m_iCpuCount = WELS_DEC_MAX_NUM_CPU;
+  }
+  m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iThreadCount];
+  memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iThreadCount);
+  for (int32_t i = 0; i < WELS_DEC_MAX_NUM_CPU; ++i) {
+    m_pDecThrCtxActive[i] = NULL;
+  }
 #ifdef OUTPUT_BIT_STREAM
   SWelsTime sCurTime;
 
@@ -169,7 +227,7 @@
   if (m_pWelsTrace != NULL) {
     WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::~CWelsDecoder()");
   }
-
+  CloseDecoderThreads();
   UninitDecoder();
 
 #ifdef OUTPUT_BIT_STREAM
@@ -187,6 +245,10 @@
     delete m_pWelsTrace;
     m_pWelsTrace = NULL;
   }
+  if (m_pDecThrCtx != NULL) {
+    delete[] m_pDecThrCtx;
+    m_pDecThrCtx = NULL;
+  }
 }
 
 long CWelsDecoder::Initialize (const SDecodingParam* pParam) {
@@ -215,26 +277,87 @@
 }
 
 void CWelsDecoder::UninitDecoder (void) {
-  if (NULL == m_pDecContext)
-    return;
+  for (int32_t i = 0; i < m_iThreadCount; ++i) {
+    if (m_pDecThrCtx[i].pCtx != NULL) {
+      if (i > 0) {
+        WelsResetRefPicWithoutUnRef (m_pDecThrCtx[i].pCtx);
+      }
+      UninitDecoderCtx (m_pDecThrCtx[i].pCtx);
+    }
+  }
+}
 
-  WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::UninitDecoder(), openh264 codec version = %s.",
-           VERSION_NUMBER);
+void CWelsDecoder::OpenDecoderThreads() {
+  if (m_iThreadCount > 1) {
+    m_uiDecodeTimeStamp = 0;
+    CREATE_SEMAPHORE (&m_sIsBusy, m_iThreadCount, m_iThreadCount, NULL);
+    WelsMutexInit (&m_csDecoder);
+    CREATE_EVENT (&m_sBufferingEvent, 1, 0, NULL);
+    SET_EVENT (&m_sBufferingEvent);
+    CREATE_EVENT (&m_sReleaseBufferEvent, 1, 0, NULL);
+    SET_EVENT (&m_sReleaseBufferEvent);
+    for (int32_t i = 0; i < m_iThreadCount; ++i) {
+      m_pDecThrCtx[i].sThreadInfo.uiThrMaxNum = m_iThreadCount;
+      m_pDecThrCtx[i].sThreadInfo.uiThrNum = i;
+      m_pDecThrCtx[i].sThreadInfo.uiThrStackSize = WELS_DEC_MAX_THREAD_STACK_SIZE;
+      m_pDecThrCtx[i].sThreadInfo.pThrProcMain = pThrProcFrame;
+      m_pDecThrCtx[i].sThreadInfo.sIsBusy = &m_sIsBusy;
+      m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN;
+      m_pDecThrCtx[i].threadCtxOwner = this;
+      m_pDecThrCtx[i].kpSrc = NULL;
+      m_pDecThrCtx[i].kiSrcLen = 0;
+      m_pDecThrCtx[i].ppDst = NULL;
+      m_pDecThrCtx[i].pDec = NULL;
+      CREATE_EVENT (&m_pDecThrCtx[i].sImageReady, 1, 0, NULL);
+      CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart, 1, 0, NULL);
+      CREATE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinsh, 1, 0, NULL);
+      CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, 0, 1, NULL);
+      CREATE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated, 0, 1, NULL);
+      CREATE_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle, pThrProcInit, (void*) (& (m_pDecThrCtx[i])));
+    }
+  }
+}
+void CWelsDecoder::CloseDecoderThreads() {
+  if (m_iThreadCount > 1) {
+    for (int32_t i = 0; i < m_iThreadCount; i++) { //waiting the completion begun slices
+      WAIT_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE);
+      m_pDecThrCtx[i].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_ABORT;
+      RELEASE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated);
+      WAIT_THREAD (&m_pDecThrCtx[i].sThreadInfo.sThrHandle);
+      CLOSE_EVENT (&m_pDecThrCtx[i].sImageReady);
+      CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeStart);
+      CLOSE_EVENT (&m_pDecThrCtx[i].sSliceDecodeFinsh);
+      CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsIdle);
+      CLOSE_SEMAPHORE (&m_pDecThrCtx[i].sThreadInfo.sIsActivated);
+    }
+    WelsMutexDestroy (&m_csDecoder);
+    CLOSE_EVENT (&m_sBufferingEvent);
+    CLOSE_EVENT (&m_sReleaseBufferEvent);
+    CLOSE_SEMAPHORE (&m_sIsBusy);
+  }
+}
 
-  WelsEndDecoder (m_pDecContext);
+void CWelsDecoder::UninitDecoderCtx (PWelsDecoderContext& pCtx) {
+  if (pCtx != NULL) {
 
-  if (m_pDecContext->pMemAlign != NULL) {
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
-             "CWelsDecoder::UninitDecoder(), verify memory usage (%d bytes) after free..",
-             m_pDecContext->pMemAlign->WelsGetMemoryUsage());
-    delete m_pDecContext->pMemAlign;
-    m_pDecContext->pMemAlign = NULL;
-  }
+    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsDecoder::UninitDecoderCtx(), openh264 codec version = %s.",
+             VERSION_NUMBER);
 
-  if (NULL != m_pDecContext) {
-    WelsFree (m_pDecContext, "m_pDecContext");
+    WelsEndDecoder (pCtx);
 
-    m_pDecContext = NULL;
+    if (pCtx->pMemAlign != NULL) {
+      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
+               "CWelsDecoder::UninitDecoder(), verify memory usage (%d bytes) after free..",
+               pCtx->pMemAlign->WelsGetMemoryUsage());
+      delete pCtx->pMemAlign;
+      pCtx->pMemAlign = NULL;
+    }
+
+    if (NULL != pCtx) {
+      WelsFree (pCtx, "m_pDecContext");
+
+      pCtx = NULL;
+    }
   }
 }
 
@@ -244,59 +367,100 @@
   WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
            "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d",
            VERSION_NUMBER, (int32_t)pParam->bParseOnly);
-
+  if (m_iThreadCount > 1 && pParam->bParseOnly) {
+    m_iThreadCount = 1;
+  }
+  OpenDecoderThreads();
   //reset decoder context
   memset (&m_sDecoderStatistics, 0, sizeof (SDecoderStatistics));
   memset (&m_sLastDecPicInfo, 0, sizeof (SWelsLastDecPicInfo));
   memset (&m_sVlcTable, 0, sizeof (SVlcTable));
+  UninitDecoder();
   WelsDecoderLastDecPicInfoDefaults (m_sLastDecPicInfo);
-  if (m_pDecContext) //free
-    UninitDecoder();
-  m_pDecContext = (PWelsDecoderContext)WelsMallocz (sizeof (SWelsDecoderContext), "m_pDecContext");
-  if (NULL == m_pDecContext)
+  for (int32_t i = 0; i < m_iThreadCount; ++i) {
+    InitDecoderCtx (m_pDecThrCtx[i].pCtx, pParam);
+    if (m_iThreadCount > 1) {
+      m_pDecThrCtx[i].pCtx->pThreadCtx = &m_pDecThrCtx[i];
+    }
+  }
+  m_bParamSetsLostFlag = false;
+  m_bFreezeOutput = false;
+  return cmResultSuccess;
+}
+
+// the return value of this function is not suitable, it need report failure info to upper layer.
+int32_t CWelsDecoder::InitDecoderCtx (PWelsDecoderContext& pCtx, const SDecodingParam* pParam) {
+
+  WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
+           "CWelsDecoder::init_decoder(), openh264 codec version = %s, ParseOnly = %d",
+           VERSION_NUMBER, (int32_t)pParam->bParseOnly);
+
+  //reset decoder context
+  UninitDecoderCtx (pCtx);
+  pCtx = (PWelsDecoderContext)WelsMallocz (sizeof (SWelsDecoderContext), "m_pDecContext");
+  if (NULL == pCtx)
     return cmMallocMemeError;
   int32_t iCacheLineSize = 16;   // on chip cache line size in byte
-  m_pDecContext->pMemAlign = new CMemoryAlign (iCacheLineSize);
-  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == m_pDecContext->pMemAlign), UninitDecoder())
+  pCtx->pMemAlign = new CMemoryAlign (iCacheLineSize);
+  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pMemAlign), UninitDecoderCtx (pCtx))
 
   //fill in default value into context
-  m_pDecContext->pLastDecPicInfo = &m_sLastDecPicInfo;
-  m_pDecContext->pDecoderStatistics = &m_sDecoderStatistics;
-  m_pDecContext->pVlcTable = &m_sVlcTable;
-  m_pDecContext->pPictInfoList = m_sPictInfoList;
-  m_pDecContext->pPictReoderingStatus = &m_sReoderingStatus;
-  WelsDecoderDefaults (m_pDecContext, &m_pWelsTrace->m_sLogCtx);
-  WelsDecoderSpsPpsDefaults (m_pDecContext->sSpsPpsCtx);
-
+  pCtx->pLastDecPicInfo = &m_sLastDecPicInfo;
+  pCtx->pDecoderStatistics = &m_sDecoderStatistics;
+  pCtx->pVlcTable = &m_sVlcTable;
+  pCtx->pPictInfoList = m_sPictInfoList;
+  pCtx->pPictReoderingStatus = &m_sReoderingStatus;
+  pCtx->pCsDecoder = &m_csDecoder;
+  WelsDecoderDefaults (pCtx, &m_pWelsTrace->m_sLogCtx);
+  WelsDecoderSpsPpsDefaults (pCtx->sSpsPpsCtx);
   //check param and update decoder context
-  m_pDecContext->pParam = (SDecodingParam*)m_pDecContext->pMemAlign->WelsMallocz (sizeof (SDecodingParam),
-                          "SDecodingParam");
-  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == m_pDecContext->pParam), UninitDecoder());
-  int32_t iRet = DecoderConfigParam (m_pDecContext, pParam);
+  pCtx->pParam = (SDecodingParam*)pCtx->pMemAlign->WelsMallocz (sizeof (SDecodingParam),
+                 "SDecodingParam");
+  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, (NULL == pCtx->pParam), UninitDecoderCtx (pCtx));
+  int32_t iRet = DecoderConfigParam (pCtx, pParam);
   WELS_VERIFY_RETURN_IFNEQ (iRet, cmResultSuccess);
 
   //init decoder
-  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, WelsInitDecoder (m_pDecContext, &m_pWelsTrace->m_sLogCtx),
-                              UninitDecoder())
-
+  WELS_VERIFY_RETURN_PROC_IF (cmMallocMemeError, WelsInitDecoder (pCtx, &m_pWelsTrace->m_sLogCtx),
+                              UninitDecoderCtx (pCtx))
+  pCtx->pPicBuff = NULL;
   return cmResultSuccess;
 }
 
-int32_t CWelsDecoder::ResetDecoder() {
+int32_t CWelsDecoder::ResetDecoder (PWelsDecoderContext& pCtx) {
   // TBC: need to be modified when context and trace point are null
-  if (m_pDecContext != NULL && m_pWelsTrace != NULL) {
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d",
-             m_pDecContext->iErrorCode);
-    SDecodingParam sPrevParam;
-    memcpy (&sPrevParam, m_pDecContext->pParam, sizeof (SDecodingParam));
+  if (m_iThreadCount > 1) {
+    ThreadResetDecoder (pCtx);
+  } else {
+    if (pCtx != NULL && m_pWelsTrace != NULL) {
+      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d",
+               pCtx->iErrorCode);
+      SDecodingParam sPrevParam;
+      memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam));
 
-    WELS_VERIFY_RETURN_PROC_IF (cmInitParaError, InitDecoder (&sPrevParam), UninitDecoder());
+      WELS_VERIFY_RETURN_PROC_IF (cmInitParaError, InitDecoderCtx (pCtx, &sPrevParam),
+                                  UninitDecoderCtx (pCtx));
+    } else if (m_pWelsTrace != NULL) {
+      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null");
+    }
+    ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, false);
+  }
+  return ERR_INFO_UNINIT;
+}
+
+int32_t CWelsDecoder::ThreadResetDecoder (PWelsDecoderContext& pCtx) {
+  // TBC: need to be modified when context and trace point are null
+  SDecodingParam sPrevParam;
+  if (pCtx != NULL && m_pWelsTrace != NULL) {
+    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "ResetDecoder(), context error code is %d", pCtx->iErrorCode);
+    memcpy (&sPrevParam, pCtx->pParam, sizeof (SDecodingParam));
+    ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, true);
+    CloseDecoderThreads();
+    UninitDecoder();
+    InitDecoder (&sPrevParam);
   } else if (m_pWelsTrace != NULL) {
     WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "ResetDecoder() failed as decoder context null");
   }
-#ifdef _PICTURE_REORDERING_
-  ResetReorderingPictureBuffers (&m_sReoderingStatus, m_sPictInfoList, false);
-#endif
   return ERR_INFO_UNINIT;
 }
 
@@ -305,71 +469,95 @@
  */
 long CWelsDecoder::SetOption (DECODER_OPTION eOptID, void* pOption) {
   int iVal = 0;
+  if (eOptID == DECODER_OPTION_NUM_OF_THREADS) {
+    if (pOption != NULL) {
+      int32_t threadCount = * ((int32_t*)pOption);
+      if (threadCount <= 0) {
+        threadCount = 1;
+      } else if (threadCount > m_iCpuCount) {
+        threadCount = m_iCpuCount;
+      }
+      if (threadCount > 3) {
+        threadCount = 3;
+      }
+      if (threadCount != m_iThreadCount) {
+        m_iThreadCount = threadCount;
+        if (m_pDecThrCtx != NULL) {
+          delete [] m_pDecThrCtx;
+          m_pDecThrCtx = new SWelsDecoderThreadCTX[m_iThreadCount];
+          memset (m_pDecThrCtx, 0, sizeof (SWelsDecoderThreadCTX)*m_iThreadCount);
+        }
+      }
+    }
+    return cmResultSuccess;
+  }
+  for (int32_t i = 0; i < m_iThreadCount; ++i) {
+    PWelsDecoderContext pDecContext = m_pDecThrCtx[i].pCtx;
+    if (pDecContext == NULL && eOptID != DECODER_OPTION_TRACE_LEVEL &&
+        eOptID != DECODER_OPTION_TRACE_CALLBACK && eOptID != DECODER_OPTION_TRACE_CALLBACK_CONTEXT)
+      return dsInitialOptExpected;
+    if (eOptID == DECODER_OPTION_END_OF_STREAM) { // Indicate bit-stream of the final frame to be decoded
+      if (pOption == NULL)
+        return cmInitParaError;
 
-  if (m_pDecContext == NULL && eOptID != DECODER_OPTION_TRACE_LEVEL &&
-      eOptID != DECODER_OPTION_TRACE_CALLBACK && eOptID != DECODER_OPTION_TRACE_CALLBACK_CONTEXT)
-    return dsInitialOptExpected;
-  if (eOptID == DECODER_OPTION_END_OF_STREAM) { // Indicate bit-stream of the final frame to be decoded
-    if (pOption == NULL)
-      return cmInitParaError;
+      iVal = * ((int*)pOption); // boolean value for whether enabled End Of Stream flag
 
-    iVal = * ((int*)pOption); // boolean value for whether enabled End Of Stream flag
+      pDecContext->bEndOfStreamFlag = iVal ? true : false;
 
-    m_pDecContext->bEndOfStreamFlag = iVal ? true : false;
+      return cmResultSuccess;
+    } else if (eOptID == DECODER_OPTION_ERROR_CON_IDC) { // Indicate error concealment status
+      if (pOption == NULL)
+        return cmInitParaError;
 
-    return cmResultSuccess;
-  } else if (eOptID == DECODER_OPTION_ERROR_CON_IDC) { // Indicate error concealment status
-    if (pOption == NULL)
-      return cmInitParaError;
+      iVal = * ((int*)pOption); // int value for error concealment idc
+      iVal = WELS_CLIP3 (iVal, (int32_t)ERROR_CON_DISABLE, (int32_t)ERROR_CON_SLICE_MV_COPY_CROSS_IDR_FREEZE_RES_CHANGE);
+      if ((pDecContext->pParam->bParseOnly) && (iVal != (int32_t)ERROR_CON_DISABLE)) {
+        WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
+                 "CWelsDecoder::SetOption for ERROR_CON_IDC = %d not allowd for parse only!.", iVal);
+        return cmInitParaError;
+      }
 
-    iVal = * ((int*)pOption); // int value for error concealment idc
-    iVal = WELS_CLIP3 (iVal, (int32_t)ERROR_CON_DISABLE, (int32_t)ERROR_CON_SLICE_MV_COPY_CROSS_IDR_FREEZE_RES_CHANGE);
-    if ((m_pDecContext->pParam->bParseOnly) && (iVal != (int32_t)ERROR_CON_DISABLE)) {
+      pDecContext->pParam->eEcActiveIdc = (ERROR_CON_IDC)iVal;
+      InitErrorCon (pDecContext);
       WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
-               "CWelsDecoder::SetOption for ERROR_CON_IDC = %d not allowd for parse only!.", iVal);
-      return cmInitParaError;
-    }
+               "CWelsDecoder::SetOption for ERROR_CON_IDC = %d.", iVal);
 
-    m_pDecContext->pParam->eEcActiveIdc = (ERROR_CON_IDC)iVal;
-    InitErrorCon (m_pDecContext);
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
-             "CWelsDecoder::SetOption for ERROR_CON_IDC = %d.", iVal);
-
-    return cmResultSuccess;
-  } else if (eOptID == DECODER_OPTION_TRACE_LEVEL) {
-    if (m_pWelsTrace) {
-      uint32_t level = * ((uint32_t*)pOption);
-      m_pWelsTrace->SetTraceLevel (level);
-    }
-    return cmResultSuccess;
-  } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK) {
-    if (m_pWelsTrace) {
-      WelsTraceCallback callback = * ((WelsTraceCallback*)pOption);
-      m_pWelsTrace->SetTraceCallback (callback);
-      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
-               "CWelsDecoder::SetOption():DECODER_OPTION_TRACE_CALLBACK callback = %p.",
-               callback);
-    }
-    return cmResultSuccess;
-  } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK_CONTEXT) {
-    if (m_pWelsTrace) {
-      void* ctx = * ((void**)pOption);
-      m_pWelsTrace->SetTraceCallbackContext (ctx);
-    }
-    return cmResultSuccess;
-  } else if (eOptID == DECODER_OPTION_GET_STATISTICS) {
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING,
-             "CWelsDecoder::SetOption():DECODER_OPTION_GET_STATISTICS: this option is get-only!");
-    return cmInitParaError;
-  } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) {
-    if (pOption) {
-      m_pDecContext->pDecoderStatistics->iStatisticsLogInterval = (* ((unsigned int*)pOption));
       return cmResultSuccess;
+    } else if (eOptID == DECODER_OPTION_TRACE_LEVEL) {
+      if (m_pWelsTrace) {
+        uint32_t level = * ((uint32_t*)pOption);
+        m_pWelsTrace->SetTraceLevel (level);
+      }
+      return cmResultSuccess;
+    } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK) {
+      if (m_pWelsTrace) {
+        WelsTraceCallback callback = * ((WelsTraceCallback*)pOption);
+        m_pWelsTrace->SetTraceCallback (callback);
+        WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO,
+                 "CWelsDecoder::SetOption():DECODER_OPTION_TRACE_CALLBACK callback = %p.",
+                 callback);
+      }
+      return cmResultSuccess;
+    } else if (eOptID == DECODER_OPTION_TRACE_CALLBACK_CONTEXT) {
+      if (m_pWelsTrace) {
+        void* ctx = * ((void**)pOption);
+        m_pWelsTrace->SetTraceCallbackContext (ctx);
+      }
+      return cmResultSuccess;
+    } else if (eOptID == DECODER_OPTION_GET_STATISTICS) {
+      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING,
+               "CWelsDecoder::SetOption():DECODER_OPTION_GET_STATISTICS: this option is get-only!");
+      return cmInitParaError;
+    } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) {
+      if (pOption) {
+        pDecContext->pDecoderStatistics->iStatisticsLogInterval = (* ((unsigned int*)pOption));
+        return cmResultSuccess;
+      }
+    } else if (eOptID == DECODER_OPTION_GET_SAR_INFO) {
+      WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING,
+               "CWelsDecoder::SetOption():DECODER_OPTION_GET_SAR_INFO: this option is get-only!");
+      return cmInitParaError;
     }
-  } else if (eOptID == DECODER_OPTION_GET_SAR_INFO) {
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING,
-             "CWelsDecoder::SetOption():DECODER_OPTION_GET_SAR_INFO: this option is get-only!");
-    return cmInitParaError;
   }
   return cmInitParaError;
 }
@@ -379,8 +567,12 @@
  */
 long CWelsDecoder::GetOption (DECODER_OPTION eOptID, void* pOption) {
   int iVal = 0;
-
-  if (m_pDecContext == NULL)
+  if (DECODER_OPTION_NUM_OF_THREADS == eOptID) {
+    * ((int*)pOption) = m_iThreadCount;
+    return cmResultSuccess;
+  }
+  PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx;
+  if (pDecContext == NULL)
     return cmInitExpected;
 
   if (pOption == NULL)
@@ -387,63 +579,63 @@
     return cmInitParaError;
 
   if (DECODER_OPTION_END_OF_STREAM == eOptID) {
-    iVal = m_pDecContext->bEndOfStreamFlag;
+    iVal = pDecContext->bEndOfStreamFlag;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   }
 #ifdef LONG_TERM_REF
   else if (DECODER_OPTION_IDR_PIC_ID == eOptID) {
-    iVal = m_pDecContext->uiCurIdrPicId;
+    iVal = pDecContext->uiCurIdrPicId;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_FRAME_NUM == eOptID) {
-    iVal = m_pDecContext->iFrameNum;
+    iVal = pDecContext->iFrameNum;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_LTR_MARKING_FLAG == eOptID) {
-    iVal = m_pDecContext->bCurAuContainLtrMarkSeFlag;
+    iVal = pDecContext->bCurAuContainLtrMarkSeFlag;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_LTR_MARKED_FRAME_NUM == eOptID) {
-    iVal = m_pDecContext->iFrameNumOfAuMarkedLtr;
+    iVal = pDecContext->iFrameNumOfAuMarkedLtr;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   }
 #endif
   else if (DECODER_OPTION_VCL_NAL == eOptID) { //feedback whether or not have VCL NAL in current AU
-    iVal = m_pDecContext->iFeedbackVclNalInAu;
+    iVal = pDecContext->iFeedbackVclNalInAu;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_TEMPORAL_ID == eOptID) { //if have VCL NAL in current AU, then feedback the temporal ID
-    iVal = m_pDecContext->iFeedbackTidInAu;
+    iVal = pDecContext->iFeedbackTidInAu;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_IS_REF_PIC == eOptID) {
-    iVal = m_pDecContext->iFeedbackNalRefIdc;
+    iVal = pDecContext->iFeedbackNalRefIdc;
     if (iVal > 0)
       iVal = 1;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_ERROR_CON_IDC == eOptID) {
-    iVal = (int)m_pDecContext->pParam->eEcActiveIdc;
+    iVal = (int)pDecContext->pParam->eEcActiveIdc;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_GET_STATISTICS == eOptID) { // get decoder statistics info for real time debugging
     SDecoderStatistics* pDecoderStatistics = (static_cast<SDecoderStatistics*> (pOption));
 
-    memcpy (pDecoderStatistics, m_pDecContext->pDecoderStatistics, sizeof (SDecoderStatistics));
+    memcpy (pDecoderStatistics, pDecContext->pDecoderStatistics, sizeof (SDecoderStatistics));
 
-    if (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount != 0) { //not original status
-      pDecoderStatistics->fAverageFrameSpeedInMs = (float) (m_pDecContext->dDecTime) /
-          (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount);
-      pDecoderStatistics->fActualAverageFrameSpeedInMs = (float) (m_pDecContext->dDecTime) /
-          (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount + m_pDecContext->pDecoderStatistics->uiFreezingIDRNum +
-           m_pDecContext->pDecoderStatistics->uiFreezingNonIDRNum);
+    if (pDecContext->pDecoderStatistics->uiDecodedFrameCount != 0) { //not original status
+      pDecoderStatistics->fAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) /
+          (pDecContext->pDecoderStatistics->uiDecodedFrameCount);
+      pDecoderStatistics->fActualAverageFrameSpeedInMs = (float) (pDecContext->dDecTime) /
+          (pDecContext->pDecoderStatistics->uiDecodedFrameCount + pDecContext->pDecoderStatistics->uiFreezingIDRNum +
+           pDecContext->pDecoderStatistics->uiFreezingNonIDRNum);
     }
     return cmResultSuccess;
   } else if (eOptID == DECODER_OPTION_STATISTICS_LOG_INTERVAL) {
     if (pOption) {
-      iVal = m_pDecContext->pDecoderStatistics->iStatisticsLogInterval;
+      iVal = pDecContext->pDecoderStatistics->iStatisticsLogInterval;
       * ((unsigned int*)pOption) = iVal;
       return cmResultSuccess;
     }
@@ -450,34 +642,34 @@
   } else if (DECODER_OPTION_GET_SAR_INFO == eOptID) { //get decoder SAR info in VUI
     PVuiSarInfo pVuiSarInfo = (static_cast<PVuiSarInfo> (pOption));
     memset (pVuiSarInfo, 0, sizeof (SVuiSarInfo));
-    if (!m_pDecContext->pSps) {
+    if (!pDecContext->pSps) {
       return cmInitExpected;
     } else {
-      pVuiSarInfo->uiSarWidth = m_pDecContext->pSps->sVui.uiSarWidth;
-      pVuiSarInfo->uiSarHeight = m_pDecContext->pSps->sVui.uiSarHeight;
-      pVuiSarInfo->bOverscanAppropriateFlag = m_pDecContext->pSps->sVui.bOverscanAppropriateFlag;
+      pVuiSarInfo->uiSarWidth = pDecContext->pSps->sVui.uiSarWidth;
+      pVuiSarInfo->uiSarHeight = pDecContext->pSps->sVui.uiSarHeight;
+      pVuiSarInfo->bOverscanAppropriateFlag = pDecContext->pSps->sVui.bOverscanAppropriateFlag;
       return cmResultSuccess;
     }
   } else if (DECODER_OPTION_PROFILE == eOptID) {
-    if (!m_pDecContext->pSps) {
+    if (!pDecContext->pSps) {
       return cmInitExpected;
     }
-    iVal = (int)m_pDecContext->pSps->uiProfileIdc;
+    iVal = (int)pDecContext->pSps->uiProfileIdc;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_LEVEL == eOptID) {
-    if (!m_pDecContext->pSps) {
+    if (!pDecContext->pSps) {
       return cmInitExpected;
     }
-    iVal = (int)m_pDecContext->pSps->uiLevelIdc;
+    iVal = (int)pDecContext->pSps->uiLevelIdc;
     * ((int*)pOption) = iVal;
     return cmResultSuccess;
   } else if (DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER == eOptID) {
-    if (m_pDecContext->pSps && m_pDecContext->pSps->uiProfileIdc != 66) {
-      * ((int*)pOption) = m_sReoderingStatus.iNumOfPicts > 0 ? m_sReoderingStatus.iNumOfPicts : 0;
-    } else {
-      * ((int*)pOption) = 0;
+    for (int32_t activeThread = 0; activeThread < m_DecCtxActiveCount; ++activeThread) {
+      WAIT_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE);
+      RELEASE_SEMAPHORE (&m_pDecThrCtxActive[activeThread]->sThreadInfo.sIsIdle);
     }
+    * ((int*)pOption) = m_sReoderingStatus.iNumOfPicts;
     return cmResultSuccess;
   }
 
@@ -488,7 +680,17 @@
     const int kiSrcLen,
     unsigned char** ppDst,
     SBufferInfo* pDstInfo) {
-  int iRet;
+  int iRet = dsErrorFree;
+  if (m_iThreadCount > 1) {
+    iRet = ThreadDecodeFrameInternal (kpSrc, kiSrcLen, ppDst, pDstInfo);
+    if (m_sReoderingStatus.iNumOfPicts) {
+      WAIT_EVENT (&m_sBufferingEvent, WELS_DEC_THREAD_WAIT_INFINITE);
+      RESET_EVENT (&m_sReleaseBufferEvent);
+      ReleaseBufferedReadyPicture (NULL, ppDst, pDstInfo);
+      SET_EVENT (&m_sReleaseBufferEvent);
+    }
+    return (DECODING_STATE)iRet;
+  }
   //SBufferInfo sTmpBufferInfo;
   //unsigned char* ppTmpDst[3] = {NULL, NULL, NULL};
   iRet = (int)DecodeFrame2 (kpSrc, kiSrcLen, ppDst, pDstInfo);
@@ -506,11 +708,11 @@
   return (DECODING_STATE)iRet;
 }
 
-DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc,
+DECODING_STATE CWelsDecoder::DecodeFrame2WithCtx (PWelsDecoderContext pDecContext, const unsigned char* kpSrc,
     const int kiSrcLen,
     unsigned char** ppDst,
     SBufferInfo* pDstInfo) {
-  if (m_pDecContext == NULL || m_pDecContext->pParam == NULL) {
+  if (pDecContext == NULL || pDecContext->pParam == NULL) {
     if (m_pWelsTrace != NULL) {
       WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeFrame2 without Initialize.\n");
     }
@@ -517,13 +719,13 @@
     return dsInitialOptExpected;
   }
 
-  if (m_pDecContext->pParam->bParseOnly) {
+  if (pDecContext->pParam->bParseOnly) {
     WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be false for this API calling! \n");
-    m_pDecContext->iErrorCode |= dsInvalidArgument;
+    pDecContext->iErrorCode |= dsInvalidArgument;
     return dsInvalidArgument;
   }
-  if (CheckBsBuffer (m_pDecContext, kiSrcLen)) {
-    if (ResetDecoder())
+  if (CheckBsBuffer (pDecContext, kiSrcLen)) {
+    if (ResetDecoder (pDecContext))
       return dsOutOfMemory;
 
     return dsErrorFree;
@@ -539,147 +741,175 @@
       WelsFflush (m_pFBSSize);
     }
 #endif//OUTPUT_BIT_STREAM
-    m_pDecContext->bEndOfStreamFlag = false;
+    pDecContext->bEndOfStreamFlag = false;
   } else {
     //For application MODE, the error detection should be added for safe.
     //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL.
-    m_pDecContext->bEndOfStreamFlag = true;
-    m_pDecContext->bInstantDecFlag = true;
+    pDecContext->bEndOfStreamFlag = true;
+    pDecContext->bInstantDecFlag = true;
   }
 
   int64_t iStart, iEnd;
   iStart = WelsTime();
 
-  ppDst[0] = ppDst[1] = ppDst[2] = NULL;
-  m_pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding.
-  m_pDecContext->iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL; //initialize
+  if (pDecContext->pThreadCtx == NULL) {
+    ppDst[0] = ppDst[1] = ppDst[2] = NULL;
+  }
+  pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding.
+  pDecContext->iFeedbackVclNalInAu = FEEDBACK_UNKNOWN_NAL; //initialize
   unsigned long long uiInBsTimeStamp = pDstInfo->uiInBsTimeStamp;
-  memset (pDstInfo, 0, sizeof (SBufferInfo));
+  if (pDecContext->pThreadCtx == NULL) {
+    memset (pDstInfo, 0, sizeof (SBufferInfo));
+  }
   pDstInfo->uiInBsTimeStamp = uiInBsTimeStamp;
 #ifdef LONG_TERM_REF
-  m_pDecContext->bReferenceLostAtT0Flag = false; //initialize for LTR
-  m_pDecContext->bCurAuContainLtrMarkSeFlag = false;
-  m_pDecContext->iFrameNumOfAuMarkedLtr = 0;
-  m_pDecContext->iFrameNum = -1; //initialize
+  pDecContext->bReferenceLostAtT0Flag = false; //initialize for LTR
+  pDecContext->bCurAuContainLtrMarkSeFlag = false;
+  pDecContext->iFrameNumOfAuMarkedLtr = 0;
+  pDecContext->iFrameNum = -1; //initialize
 #endif
 
-  m_pDecContext->iFeedbackTidInAu = -1; //initialize
-  m_pDecContext->iFeedbackNalRefIdc = -1; //initialize
+  pDecContext->iFeedbackTidInAu = -1; //initialize
+  pDecContext->iFeedbackNalRefIdc = -1; //initialize
   if (pDstInfo) {
     pDstInfo->uiOutYuvTimeStamp = 0;
-    m_pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp;
+    pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp;
   } else {
-    m_pDecContext->uiTimeStamp = 0;
+    pDecContext->uiTimeStamp = 0;
   }
-  WelsDecodeBs (m_pDecContext, kpSrc, kiSrcLen, ppDst,
+  WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, ppDst,
                 pDstInfo, NULL); //iErrorCode has been modified in this function
-  m_pDecContext->bInstantDecFlag = false; //reset no-delay flag
-  if (m_pDecContext->iErrorCode) {
+  pDecContext->bInstantDecFlag = false; //reset no-delay flag
+  if (pDecContext->iErrorCode) {
     EWelsNalUnitType eNalType =
       NAL_UNIT_UNSPEC_0; //for NBR, IDR frames are expected to decode as followed if error decoding an IDR currently
 
-    eNalType = m_pDecContext->sCurNalHead.eNalUnitType;
-    if (m_pDecContext->iErrorCode & dsOutOfMemory) {
-      if (ResetDecoder()) {
+    eNalType = pDecContext->sCurNalHead.eNalUnitType;
+    if (pDecContext->iErrorCode & dsOutOfMemory) {
+      if (ResetDecoder (pDecContext)) {
         return dsOutOfMemory;
       }
       return dsErrorFree;
     }
-    if (m_pDecContext->iErrorCode & dsRefListNullPtrs) {
-      if (ResetDecoder()) {
+    if (pDecContext->iErrorCode & dsRefListNullPtrs) {
+      if (ResetDecoder (pDecContext)) {
         return dsRefListNullPtrs;
       }
       return dsErrorFree;
     }
-    if ((m_pDecContext->iErrorCode & (dsBitstreamError | dsDataErrorConcealed)) && m_pDecContext->eSliceType == B_SLICE) {
-      if (ResetDecoder()) {
+    if ((pDecContext->iErrorCode & (dsBitstreamError | dsDataErrorConcealed)) && pDecContext->eSliceType == B_SLICE) {
+      if (ResetDecoder (pDecContext)) {
         pDstInfo->iBufferStatus = 0;
-        return (DECODING_STATE)m_pDecContext->iErrorCode;
+        return (DECODING_STATE)pDecContext->iErrorCode;
       }
       return dsErrorFree;
     }
     //for AVC bitstream (excluding AVC with temporal scalability, including TP), as long as error occur, SHOULD notify upper layer key frame loss.
     if ((IS_PARAM_SETS_NALS (eNalType) || NAL_UNIT_CODED_SLICE_IDR == eNalType) ||
-        (VIDEO_BITSTREAM_AVC == m_pDecContext->eVideoType)) {
-      if (m_pDecContext->pParam->eEcActiveIdc == ERROR_CON_DISABLE) {
+        (VIDEO_BITSTREAM_AVC == pDecContext->eVideoType)) {
+      if (pDecContext->pParam->eEcActiveIdc == ERROR_CON_DISABLE) {
 #ifdef LONG_TERM_REF
-        m_pDecContext->bParamSetsLostFlag = true;
+        pDecContext->bParamSetsLostFlag = true;
 #else
-        m_pDecContext->bReferenceLostAtT0Flag = true;
+        pDecContext->bReferenceLostAtT0Flag = true;
 #endif
       }
     }
 
-    if (m_pDecContext->bPrintFrameErrorTraceFlag) {
+    if (pDecContext->bPrintFrameErrorTraceFlag) {
       WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n",
-               m_pDecContext->iErrorCode);
-      m_pDecContext->bPrintFrameErrorTraceFlag = false;
+               pDecContext->iErrorCode);
+      pDecContext->bPrintFrameErrorTraceFlag = false;
     } else {
-      m_pDecContext->iIgnoredErrorInfoPacketCount++;
-      if (m_pDecContext->iIgnoredErrorInfoPacketCount == INT_MAX) {
+      pDecContext->iIgnoredErrorInfoPacketCount++;
+      if (pDecContext->iIgnoredErrorInfoPacketCount == INT_MAX) {
         WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_WARNING, "continuous error reached INT_MAX! Restart as 0.");
-        m_pDecContext->iIgnoredErrorInfoPacketCount = 0;
+        pDecContext->iIgnoredErrorInfoPacketCount = 0;
       }
     }
-    if ((m_pDecContext->pParam->eEcActiveIdc != ERROR_CON_DISABLE) && (pDstInfo->iBufferStatus == 1)) {
+    if ((pDecContext->pParam->eEcActiveIdc != ERROR_CON_DISABLE) && (pDstInfo->iBufferStatus == 1)) {
       //TODO after dec status updated
-      m_pDecContext->iErrorCode |= dsDataErrorConcealed;
+      pDecContext->iErrorCode |= dsDataErrorConcealed;
 
-      m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
-      if (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
-        ResetDecStatNums (m_pDecContext->pDecoderStatistics);
-        m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+      pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+      if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
+        ResetDecStatNums (pDecContext->pDecoderStatistics);
+        pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
       }
-      int32_t iMbConcealedNum = m_pDecContext->iMbEcedNum + m_pDecContext->iMbEcedPropNum;
-      m_pDecContext->pDecoderStatistics->uiAvgEcRatio = m_pDecContext->iMbNum == 0 ?
-          (m_pDecContext->pDecoderStatistics->uiAvgEcRatio * m_pDecContext->pDecoderStatistics->uiEcFrameNum) : ((
-                m_pDecContext->pDecoderStatistics->uiAvgEcRatio * m_pDecContext->pDecoderStatistics->uiEcFrameNum) + ((
-                      iMbConcealedNum * 100) / m_pDecContext->iMbNum));
-      m_pDecContext->pDecoderStatistics->uiAvgEcPropRatio = m_pDecContext->iMbNum == 0 ?
-          (m_pDecContext->pDecoderStatistics->uiAvgEcPropRatio * m_pDecContext->pDecoderStatistics->uiEcFrameNum) : ((
-                m_pDecContext->pDecoderStatistics->uiAvgEcPropRatio * m_pDecContext->pDecoderStatistics->uiEcFrameNum) + ((
-                      m_pDecContext->iMbEcedPropNum * 100) / m_pDecContext->iMbNum));
-      m_pDecContext->pDecoderStatistics->uiEcFrameNum += (iMbConcealedNum == 0 ? 0 : 1);
-      m_pDecContext->pDecoderStatistics->uiAvgEcRatio = m_pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 :
-          m_pDecContext->pDecoderStatistics->uiAvgEcRatio / m_pDecContext->pDecoderStatistics->uiEcFrameNum;
-      m_pDecContext->pDecoderStatistics->uiAvgEcPropRatio = m_pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 :
-          m_pDecContext->pDecoderStatistics->uiAvgEcPropRatio / m_pDecContext->pDecoderStatistics->uiEcFrameNum;
+      int32_t iMbConcealedNum = pDecContext->iMbEcedNum + pDecContext->iMbEcedPropNum;
+      pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->iMbNum == 0 ?
+          (pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : ((
+                pDecContext->pDecoderStatistics->uiAvgEcRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + ((
+                      iMbConcealedNum * 100) / pDecContext->iMbNum));
+      pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->iMbNum == 0 ?
+          (pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) : ((
+                pDecContext->pDecoderStatistics->uiAvgEcPropRatio * pDecContext->pDecoderStatistics->uiEcFrameNum) + ((
+                      pDecContext->iMbEcedPropNum * 100) / pDecContext->iMbNum));
+      pDecContext->pDecoderStatistics->uiEcFrameNum += (iMbConcealedNum == 0 ? 0 : 1);
+      pDecContext->pDecoderStatistics->uiAvgEcRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 :
+          pDecContext->pDecoderStatistics->uiAvgEcRatio / pDecContext->pDecoderStatistics->uiEcFrameNum;
+      pDecContext->pDecoderStatistics->uiAvgEcPropRatio = pDecContext->pDecoderStatistics->uiEcFrameNum == 0 ? 0 :
+          pDecContext->pDecoderStatistics->uiAvgEcPropRatio / pDecContext->pDecoderStatistics->uiEcFrameNum;
     }
     iEnd = WelsTime();
-    m_pDecContext->dDecTime += (iEnd - iStart) / 1e3;
+    pDecContext->dDecTime += (iEnd - iStart) / 1e3;
 
-    OutputStatisticsLog (*m_pDecContext->pDecoderStatistics);
+    OutputStatisticsLog (*pDecContext->pDecoderStatistics);
 
-#ifdef  _PICTURE_REORDERING_
-    ReorderPicturesInDisplay (ppDst, pDstInfo);
-#endif
+    if (pDecContext->pThreadCtx != NULL) {
+      WAIT_EVENT (&m_sReleaseBufferEvent, WELS_DEC_THREAD_WAIT_INFINITE);
+      RESET_EVENT (&m_sBufferingEvent);
+      BufferingReadyPicture (pDecContext, ppDst, pDstInfo);
+      SET_EVENT (&m_sBufferingEvent);
+    } else {
+      ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo);
+    }
 
-    return (DECODING_STATE)m_pDecContext->iErrorCode;
+    return (DECODING_STATE)pDecContext->iErrorCode;
   }
   // else Error free, the current codec works well
 
   if (pDstInfo->iBufferStatus == 1) {
 
-    m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
-    if (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
-      ResetDecStatNums (m_pDecContext->pDecoderStatistics);
-      m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+    pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+    if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
+      ResetDecStatNums (pDecContext->pDecoderStatistics);
+      pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
     }
 
-    OutputStatisticsLog (*m_pDecContext->pDecoderStatistics);
+    OutputStatisticsLog (*pDecContext->pDecoderStatistics);
   }
   iEnd = WelsTime();
-  m_pDecContext->dDecTime += (iEnd - iStart) / 1e3;
+  pDecContext->dDecTime += (iEnd - iStart) / 1e3;
 
-#ifdef  _PICTURE_REORDERING_
-  ReorderPicturesInDisplay (ppDst, pDstInfo);
-#endif
+  if (pDecContext->pThreadCtx != NULL) {
+    WAIT_EVENT (&m_sReleaseBufferEvent, WELS_DEC_THREAD_WAIT_INFINITE);
+    RESET_EVENT (&m_sBufferingEvent);
+    BufferingReadyPicture (pDecContext, ppDst, pDstInfo);
+    SET_EVENT (&m_sBufferingEvent);
+  } else {
+    ReorderPicturesInDisplay (pDecContext, ppDst, pDstInfo);
+  }
   return dsErrorFree;
 }
 
+DECODING_STATE CWelsDecoder::DecodeFrame2 (const unsigned char* kpSrc,
+    const int kiSrcLen,
+    unsigned char** ppDst,
+    SBufferInfo* pDstInfo) {
+  PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx;
+  return DecodeFrame2WithCtx (pDecContext, kpSrc, kiSrcLen, ppDst, pDstInfo);
+}
+
 DECODING_STATE CWelsDecoder::FlushFrame (unsigned char** ppDst,
     SBufferInfo* pDstInfo) {
-  if (m_pDecContext->bEndOfStreamFlag && m_sReoderingStatus.iNumOfPicts > 0) {
+  bool bEndOfStreamFlag = true;
+  for (int32_t j = 0; j < m_iThreadCount; ++j) {
+    if (!m_pDecThrCtx[j].pCtx->bEndOfStreamFlag) {
+      bEndOfStreamFlag = false;
+    }
+  }
+  if (bEndOfStreamFlag && m_sReoderingStatus.iNumOfPicts > 0) {
     m_sReoderingStatus.iMinPOC = IMinInt32;
     for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
       if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32) {
@@ -704,12 +934,15 @@
     ppDst[1] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[1];
     ppDst[2] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[2];
     m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32;
-    if (m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx < m_pDecContext->pPicBuff->iCapacity)
-      m_pDecContext->pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
+    PPicBuff pPicBuff = m_iThreadCount == 1 ? m_pDecThrCtx[0].pCtx->pPicBuff : m_pPicBuff;
+    if (m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx < pPicBuff->iCapacity) {
+      pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
+    }
     m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].bLastGOP = false;
     m_sReoderingStatus.iMinPOC = IMinInt32;
     --m_sReoderingStatus.iNumOfPicts;
   }
+
   return dsErrorFree;
 }
 
@@ -758,24 +991,15 @@
   }
 }
 
-DECODING_STATE CWelsDecoder::ReorderPicturesInDisplay (unsigned char** ppDst, SBufferInfo* pDstInfo) {
-  DECODING_STATE iRet = dsErrorFree;
+void CWelsDecoder::BufferingReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst,
+    SBufferInfo* pDstInfo) {
   if (pDstInfo->iBufferStatus == 0) {
-    return iRet;
+    return;
   }
-  ++m_pDecContext->uiDecodingTimeStamp;
-  if (m_pDecContext->pSps->uiProfileIdc != 66 && m_pDecContext->pSps->uiProfileIdc != 83) {
-    /*if (m_pDecContext->pSliceHeader->iPicOrderCntLsb == 0) {
-      m_sReoderingStatus.iLastWrittenPOC = 0;
-      return dsErrorFree;
-    }
-    if (m_sReoderingStatus.iNumOfPicts == 0 && m_pDecContext->pLastDecPicInfo->pPreviousDecodedPictureInDpb->bNewSeqBegin
-        && m_pDecContext->eSliceType != I_SLICE) {
-      m_sReoderingStatus.iLastWrittenPOC = m_pDecContext->pSliceHeader->iPicOrderCntLsb;
-      return dsErrorFree;
-    }*/
-    if (m_sReoderingStatus.iNumOfPicts && m_pDecContext->pLastDecPicInfo->pPreviousDecodedPictureInDpb
-        && m_pDecContext->pLastDecPicInfo->pPreviousDecodedPictureInDpb->bNewSeqBegin) {
+  m_bIsBaseline = pCtx->pSps->uiProfileIdc == 66 || pCtx->pSps->uiProfileIdc == 83;
+  if (!m_bIsBaseline) {
+    if (m_sReoderingStatus.iNumOfPicts && pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb
+        && pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->bNewSeqBegin) {
       m_sReoderingStatus.iLastGOPRemainPicts = m_sReoderingStatus.iNumOfPicts;
       for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
         if (m_sPictInfoList[i].iPOC > IMinInt32) {
@@ -787,7 +1011,7 @@
         //This can happen when decoder moves to next GOP without being able to decoder first picture PicOrderCntLsb = 0
         bool hasGOPChanged = false;
         for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
-          if (m_sPictInfoList[i].iPOC == m_pDecContext->pSliceHeader->iPicOrderCntLsb) {
+          if (m_sPictInfoList[i].iPOC == pCtx->pSliceHeader->iPicOrderCntLsb) {
             hasGOPChanged = true;
             break;
           }
@@ -802,105 +1026,153 @@
         }
       }
     }
-    for (int32_t i = 0; i < 16; ++i) {
-      if (m_sPictInfoList[i].iPOC == IMinInt32) {
-        memcpy (&m_sPictInfoList[i].sBufferInfo, pDstInfo, sizeof (SBufferInfo));
-        m_sPictInfoList[i].pData[0] = ppDst[0];
-        m_sPictInfoList[i].pData[1] = ppDst[1];
-        m_sPictInfoList[i].pData[2] = ppDst[2];
-        m_sPictInfoList[i].iPOC = m_pDecContext->pSliceHeader->iPicOrderCntLsb;
-        m_sPictInfoList[i].uiDecodingTimeStamp = m_pDecContext->uiDecodingTimeStamp;
-        m_sPictInfoList[i].iPicBuffIdx = m_pDecContext->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx;
-        m_pDecContext->pPicBuff->ppPic[m_sPictInfoList[i].iPicBuffIdx]->bAvailableFlag = false;
-        m_sPictInfoList[i].bLastGOP = false;
-        pDstInfo->iBufferStatus = 0;
-        ++m_sReoderingStatus.iNumOfPicts;
-        if (i > m_sReoderingStatus.iLargestBufferedPicIndex) {
-          m_sReoderingStatus.iLargestBufferedPicIndex = i;
-        }
-        break;
+  }
+  for (int32_t i = 0; i < 16; ++i) {
+    if (m_sPictInfoList[i].iPOC == IMinInt32) {
+      memcpy (&m_sPictInfoList[i].sBufferInfo, pDstInfo, sizeof (SBufferInfo));
+      m_sPictInfoList[i].pData[0] = ppDst[0];
+      m_sPictInfoList[i].pData[1] = ppDst[1];
+      m_sPictInfoList[i].pData[2] = ppDst[2];
+      m_sPictInfoList[i].iPOC = pCtx->pSliceHeader->iPicOrderCntLsb;
+      m_sPictInfoList[i].uiDecodingTimeStamp = pCtx->uiDecodingTimeStamp;
+      m_sPictInfoList[i].iPicBuffIdx = pCtx->pLastDecPicInfo->pPreviousDecodedPictureInDpb->iPicBuffIdx;
+      pCtx->pPicBuff->ppPic[m_sPictInfoList[i].iPicBuffIdx]->bAvailableFlag = false;
+      m_sPictInfoList[i].bLastGOP = false;
+      pDstInfo->iBufferStatus = 0;
+      ++m_sReoderingStatus.iNumOfPicts;
+      if (i > m_sReoderingStatus.iLargestBufferedPicIndex) {
+        m_sReoderingStatus.iLargestBufferedPicIndex = i;
       }
+      break;
     }
-    if (m_sReoderingStatus.iLastGOPRemainPicts > 0) {
-      m_sReoderingStatus.iMinPOC = IMinInt32;
-      for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
-        if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].bLastGOP) {
-          m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
-          m_sReoderingStatus.iPictInfoIndex = i;
-        }
-        if (m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC
-            && m_sPictInfoList[i].bLastGOP) {
-          m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
-          m_sReoderingStatus.iPictInfoIndex = i;
-        }
+  }
+}
+
+void CWelsDecoder::ReleaseBufferedReadyPicture (PWelsDecoderContext pCtx, unsigned char** ppDst,
+    SBufferInfo* pDstInfo) {
+  PPicBuff pPicBuff = pCtx ? pCtx->pPicBuff : m_pPicBuff;
+  if (!m_bIsBaseline && m_sReoderingStatus.iLastGOPRemainPicts > 0) {
+    m_sReoderingStatus.iMinPOC = IMinInt32;
+    for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
+      if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].bLastGOP) {
+        m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
+        m_sReoderingStatus.iPictInfoIndex = i;
       }
-      m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC;
+      if (m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC
+          && m_sPictInfoList[i].bLastGOP) {
+        m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
+        m_sReoderingStatus.iPictInfoIndex = i;
+      }
+    }
+    m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC;
 #if defined (_DEBUG)
 #ifdef _MOTION_VECTOR_DUMP_
-      fprintf (stderr, "Output POC: #%d\n", m_sReoderingStatus.iLastWrittenPOC);
+    fprintf (stderr, "Output POC: #%d\n", m_sReoderingStatus.iLastWrittenPOC);
 #endif
 #endif
+    memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo));
+    ppDst[0] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[0];
+    ppDst[1] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[1];
+    ppDst[2] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[2];
+    m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32;
+    pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
+    m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].bLastGOP = false;
+    m_sReoderingStatus.iMinPOC = IMinInt32;
+    --m_sReoderingStatus.iNumOfPicts;
+    --m_sReoderingStatus.iLastGOPRemainPicts;
+    if (m_sReoderingStatus.iLastGOPRemainPicts == 0) {
+      m_sReoderingStatus.iLastWrittenPOC = IMinInt32;
+    }
+    return;
+  }
+  if (m_sReoderingStatus.iNumOfPicts && m_bIsBaseline) {
+    uint32_t uiDecodingTimeStamp = 0;
+    for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
+      if (m_sPictInfoList[i].iPOC > IMinInt32) {
+        uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp;
+        m_sReoderingStatus.iPictInfoIndex = i;
+        break;
+      }
+    }
+    for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
+      if (m_sReoderingStatus.iPictInfoIndex != i && m_sPictInfoList[i].iPOC > IMinInt32
+          && m_sPictInfoList[i].sBufferInfo.uiInBsTimeStamp < uiDecodingTimeStamp) {
+        uiDecodingTimeStamp = m_sPictInfoList[i].uiDecodingTimeStamp;
+        m_sReoderingStatus.iPictInfoIndex = i;
+      }
+    }
+    if (uiDecodingTimeStamp > 0) {
       memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo));
       ppDst[0] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[0];
       ppDst[1] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[1];
       ppDst[2] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[2];
       m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32;
-      if (m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx < m_pDecContext->pPicBuff->iCapacity)
-        m_pDecContext->pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
-      m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].bLastGOP = false;
-      m_sReoderingStatus.iMinPOC = IMinInt32;
+      pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
       --m_sReoderingStatus.iNumOfPicts;
-      --m_sReoderingStatus.iLastGOPRemainPicts;
-      if (m_sReoderingStatus.iLastGOPRemainPicts == 0) {
-        m_sReoderingStatus.iLastWrittenPOC = IMinInt32;
-      }
-      return iRet;
     }
-    if (m_sReoderingStatus.iNumOfPicts > 0) {
-      m_sReoderingStatus.iMinPOC = IMinInt32;
-      for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
-        if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32) {
-          m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
-          m_sReoderingStatus.iPictInfoIndex = i;
-        }
-        if (m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC) {
-          m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
-          m_sReoderingStatus.iPictInfoIndex = i;
-        }
+    return;
+  }
+  if (m_sReoderingStatus.iNumOfPicts > 0) {
+    m_sReoderingStatus.iMinPOC = IMinInt32;
+    for (int32_t i = 0; i <= m_sReoderingStatus.iLargestBufferedPicIndex; ++i) {
+      if (m_sReoderingStatus.iMinPOC == IMinInt32 && m_sPictInfoList[i].iPOC > IMinInt32) {
+        m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
+        m_sReoderingStatus.iPictInfoIndex = i;
       }
+      if (m_sPictInfoList[i].iPOC > IMinInt32 && m_sPictInfoList[i].iPOC < m_sReoderingStatus.iMinPOC) {
+        m_sReoderingStatus.iMinPOC = m_sPictInfoList[i].iPOC;
+        m_sReoderingStatus.iPictInfoIndex = i;
+      }
     }
-    if (m_sReoderingStatus.iMinPOC > IMinInt32) {
-      if ((m_sReoderingStatus.iLastWrittenPOC > IMinInt32
-           && m_sReoderingStatus.iMinPOC - m_sReoderingStatus.iLastWrittenPOC <= 1)
-          || m_sReoderingStatus.iMinPOC < m_pDecContext->pSliceHeader->iPicOrderCntLsb) {
-        m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC;
+  }
+  if (m_sReoderingStatus.iMinPOC > IMinInt32) {
+    bool isReady = false;
+    if (pCtx != NULL) {
+      isReady = (m_sReoderingStatus.iLastWrittenPOC > IMinInt32
+                 && m_sReoderingStatus.iMinPOC - m_sReoderingStatus.iLastWrittenPOC <= 1)
+                || m_sReoderingStatus.iMinPOC < pCtx->pSliceHeader->iPicOrderCntLsb;
+    } else {
+      isReady = m_sReoderingStatus.iMinPOC == 0 || (m_sReoderingStatus.iLastWrittenPOC >= 0
+                && m_sReoderingStatus.iMinPOC <= m_sReoderingStatus.iLastWrittenPOC + 2) ;
+    }
+    if (isReady) {
+      m_sReoderingStatus.iLastWrittenPOC = m_sReoderingStatus.iMinPOC;
 #if defined (_DEBUG)
 #ifdef _MOTION_VECTOR_DUMP_
-        fprintf (stderr, "Output POC: #%d\n", m_sReoderingStatus.iLastWrittenPOC);
+      fprintf (stderr, "Output POC: #%d\n", m_sReoderingStatus.iLastWrittenPOC);
 #endif
 #endif
-        memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo));
-        ppDst[0] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[0];
-        ppDst[1] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[1];
-        ppDst[2] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[2];
-        m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32;
-        if (m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx < m_pDecContext->pPicBuff->iCapacity)
-          m_pDecContext->pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
-        m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].bLastGOP = false;
-        m_sReoderingStatus.iMinPOC = IMinInt32;
-        --m_sReoderingStatus.iNumOfPicts;
-        return iRet;
-      }
+      memcpy (pDstInfo, &m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].sBufferInfo, sizeof (SBufferInfo));
+      ppDst[0] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[0];
+      ppDst[1] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[1];
+      ppDst[2] = m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].pData[2];
+      m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPOC = IMinInt32;
+      pPicBuff->ppPic[m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].iPicBuffIdx]->bAvailableFlag = true;
+      m_sPictInfoList[m_sReoderingStatus.iPictInfoIndex].bLastGOP = false;
+      m_sReoderingStatus.iMinPOC = IMinInt32;
+      --m_sReoderingStatus.iNumOfPicts;
     }
   }
+}
 
+DECODING_STATE CWelsDecoder::ReorderPicturesInDisplay (PWelsDecoderContext pDecContext, unsigned char** ppDst,
+    SBufferInfo* pDstInfo) {
+  DECODING_STATE iRet = dsErrorFree;
+  if (pDstInfo->iBufferStatus == 1) {
+    ++pDecContext->uiDecodingTimeStamp;
+    m_bIsBaseline = pDecContext->pSps->uiProfileIdc == 66 || pDecContext->pSps->uiProfileIdc == 83;
+    if (!m_bIsBaseline) {
+      BufferingReadyPicture (pDecContext, ppDst, pDstInfo);
+      ReleaseBufferedReadyPicture (pDecContext, ppDst, pDstInfo);
+    }
+  }
   return iRet;
 }
 
-DECODING_STATE CWelsDecoder::DecodeParser (const unsigned char* kpSrc,
-    const int kiSrcLen,
-    SParserBsInfo* pDstInfo) {
-  if (m_pDecContext == NULL || m_pDecContext->pParam == NULL) {
+DECODING_STATE CWelsDecoder::DecodeParser (const unsigned char* kpSrc, const int kiSrcLen, SParserBsInfo* pDstInfo) {
+  PWelsDecoderContext pDecContext = m_pDecThrCtx[0].pCtx;
+
+  if (pDecContext == NULL || pDecContext->pParam == NULL) {
     if (m_pWelsTrace != NULL) {
       WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "Call DecodeParser without Initialize.\n");
     }
@@ -907,14 +1179,14 @@
     return dsInitialOptExpected;
   }
 
-  if (!m_pDecContext->pParam->bParseOnly) {
+  if (!pDecContext->pParam->bParseOnly) {
     WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "bParseOnly should be true for this API calling! \n");
-    m_pDecContext->iErrorCode |= dsInvalidArgument;
+    pDecContext->iErrorCode |= dsInvalidArgument;
     return dsInvalidArgument;
   }
   int64_t iEnd, iStart = WelsTime();
-  if (CheckBsBuffer (m_pDecContext, kiSrcLen)) {
-    if (ResetDecoder())
+  if (CheckBsBuffer (pDecContext, kiSrcLen)) {
+    if (ResetDecoder (pDecContext))
       return dsOutOfMemory;
 
     return dsErrorFree;
@@ -926,58 +1198,57 @@
       WelsFflush (m_pFBS);
     }
 #endif//OUTPUT_BIT_STREAM
-    m_pDecContext->bEndOfStreamFlag = false;
+    pDecContext->bEndOfStreamFlag = false;
   } else {
     //For application MODE, the error detection should be added for safe.
     //But for CONSOLE MODE, when decoding LAST AU, kiSrcLen==0 && kpSrc==NULL.
-    m_pDecContext->bEndOfStreamFlag = true;
-    m_pDecContext->bInstantDecFlag = true;
+    pDecContext->bEndOfStreamFlag = true;
+    pDecContext->bInstantDecFlag = true;
   }
 
-  m_pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding.
-  m_pDecContext->pParam->eEcActiveIdc = ERROR_CON_DISABLE; //add protection to disable EC here.
-  m_pDecContext->iFeedbackNalRefIdc = -1; //initialize
-  if (!m_pDecContext->bFramePending) { //frame complete
-    m_pDecContext->pParserBsInfo->iNalNum = 0;
-    memset (m_pDecContext->pParserBsInfo->pNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER);
+  pDecContext->iErrorCode = dsErrorFree; //initialize at the starting of AU decoding.
+  pDecContext->pParam->eEcActiveIdc = ERROR_CON_DISABLE; //add protection to disable EC here.
+  pDecContext->iFeedbackNalRefIdc = -1; //initialize
+  if (!pDecContext->bFramePending) { //frame complete
+    pDecContext->pParserBsInfo->iNalNum = 0;
+    memset (pDecContext->pParserBsInfo->pNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER);
   }
   pDstInfo->iNalNum = 0;
   pDstInfo->iSpsWidthInPixel = pDstInfo->iSpsHeightInPixel = 0;
   if (pDstInfo) {
-    m_pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp;
+    pDecContext->uiTimeStamp = pDstInfo->uiInBsTimeStamp;
     pDstInfo->uiOutBsTimeStamp = 0;
   } else {
-    m_pDecContext->uiTimeStamp = 0;
+    pDecContext->uiTimeStamp = 0;
   }
-  WelsDecodeBs (m_pDecContext, kpSrc, kiSrcLen, NULL, NULL, pDstInfo);
-  if (m_pDecContext->iErrorCode & dsOutOfMemory) {
-    if (ResetDecoder())
+  WelsDecodeBs (pDecContext, kpSrc, kiSrcLen, NULL, NULL, pDstInfo);
+  if (pDecContext->iErrorCode & dsOutOfMemory) {
+    if (ResetDecoder (pDecContext))
       return dsOutOfMemory;
     return dsErrorFree;
   }
 
-  if (!m_pDecContext->bFramePending && m_pDecContext->pParserBsInfo->iNalNum) {
-    memcpy (pDstInfo, m_pDecContext->pParserBsInfo, sizeof (SParserBsInfo));
+  if (!pDecContext->bFramePending && pDecContext->pParserBsInfo->iNalNum) {
+    memcpy (pDstInfo, pDecContext->pParserBsInfo, sizeof (SParserBsInfo));
 
-    if (m_pDecContext->iErrorCode == ERR_NONE) { //update statistics: decoding frame count
-      m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
-      if (m_pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
-        ResetDecStatNums (m_pDecContext->pDecoderStatistics);
-        m_pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+    if (pDecContext->iErrorCode == ERR_NONE) { //update statistics: decoding frame count
+      pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
+      if (pDecContext->pDecoderStatistics->uiDecodedFrameCount == 0) { //exceed max value of uint32_t
+        ResetDecStatNums (pDecContext->pDecoderStatistics);
+        pDecContext->pDecoderStatistics->uiDecodedFrameCount++;
       }
     }
   }
 
-  m_pDecContext->bInstantDecFlag = false; //reset no-delay flag
+  pDecContext->bInstantDecFlag = false; //reset no-delay flag
 
-  if (m_pDecContext->iErrorCode && m_pDecContext->bPrintFrameErrorTraceFlag) {
-    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", m_pDecContext->iErrorCode);
-    m_pDecContext->bPrintFrameErrorTraceFlag = false;
+  if (pDecContext->iErrorCode && pDecContext->bPrintFrameErrorTraceFlag) {
+    WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "decode failed, failure type:%d \n", pDecContext->iErrorCode);
+    pDecContext->bPrintFrameErrorTraceFlag = false;
   }
   iEnd = WelsTime();
-  m_pDecContext->dDecTime += (iEnd - iStart) / 1e3;
-
-  return (DECODING_STATE) m_pDecContext->iErrorCode;
+  pDecContext->dDecTime += (iEnd - iStart) / 1e3;
+  return (DECODING_STATE)pDecContext->iErrorCode;
 }
 
 DECODING_STATE CWelsDecoder::DecodeFrame (const unsigned char* kpSrc,
@@ -1016,6 +1287,92 @@
     int& iColorFormat) {
   DECODING_STATE state = dsErrorFree;
 
+  return state;
+}
+
+DECODING_STATE CWelsDecoder::ParseAccessUnit (SWelsDecoderThreadCTX& sThreadCtx) {
+  sThreadCtx.pCtx->bHasNewSps = false;
+  sThreadCtx.pCtx->bParamSetsLostFlag = m_bParamSetsLostFlag;
+  sThreadCtx.pCtx->bFreezeOutput = m_bFreezeOutput;
+  sThreadCtx.pCtx->uiDecodingTimeStamp = ++m_uiDecodeTimeStamp;
+  bool bPicBuffChanged = false;
+  if (m_pLastDecThrCtx != NULL && sThreadCtx.pCtx->sSpsPpsCtx.iSeqId < m_pLastDecThrCtx->pCtx->sSpsPpsCtx.iSeqId) {
+    CopySpsPps (m_pLastDecThrCtx->pCtx, sThreadCtx.pCtx);
+    sThreadCtx.pCtx->iPicQueueNumber = m_pLastDecThrCtx->pCtx->iPicQueueNumber;
+    if (sThreadCtx.pCtx->pPicBuff != m_pPicBuff) {
+      bPicBuffChanged = true;
+      sThreadCtx.pCtx->pPicBuff = m_pPicBuff;
+      sThreadCtx.pCtx->bHaveGotMemory = m_pPicBuff != NULL;
+      sThreadCtx.pCtx->iImgWidthInPixel = m_pLastDecThrCtx->pCtx->iImgWidthInPixel;
+      sThreadCtx.pCtx->iImgHeightInPixel = m_pLastDecThrCtx->pCtx->iImgHeightInPixel;
+    }
+  }
+  int32_t iRet = DecodeFrame2WithCtx (sThreadCtx.pCtx, sThreadCtx.kpSrc, sThreadCtx.kiSrcLen, sThreadCtx.ppDst,
+                                      &sThreadCtx.sDstInfo);
+
+  int32_t iErr = InitConstructAccessUnit (sThreadCtx.pCtx, &sThreadCtx.sDstInfo);
+  if (ERR_NONE != iErr) {
+    return (DECODING_STATE) (iRet | iErr);
+  }
+  if (sThreadCtx.pCtx->bNewSeqBegin) {
+    m_pPicBuff = sThreadCtx.pCtx->pPicBuff;
+  } else if (bPicBuffChanged) {
+    InitialDqLayersContext (sThreadCtx.pCtx, sThreadCtx.pCtx->pSps->iMbWidth << 4, sThreadCtx.pCtx->pSps->iMbHeight << 4);
+  }
+  m_bParamSetsLostFlag = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bParamSetsLostFlag;
+  m_bFreezeOutput = sThreadCtx.pCtx->bNewSeqBegin ? false : sThreadCtx.pCtx->bFreezeOutput;
+  return (DECODING_STATE)iErr;
+}
+/*
+* Run decoding picture in separate thread.
+*/
+
+int CWelsDecoder::ThreadDecodeFrameInternal (const unsigned char* kpSrc, const int kiSrcLen, unsigned char** ppDst,
+    SBufferInfo* pDstInfo) {
+  int state = dsErrorFree;
+  int32_t i, j;
+  int32_t signal = 0;
+
+  //serial using of threads
+  if (m_DecCtxActiveCount < m_iThreadCount) {
+    signal = m_DecCtxActiveCount;
+  } else {
+    signal = m_pDecThrCtxActive[0]->sThreadInfo.uiThrNum;
+  }
+
+  WAIT_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE);
+
+  for (i = 0; i < m_DecCtxActiveCount; ++i) {
+    if (m_pDecThrCtxActive[i] == &m_pDecThrCtx[signal]) {
+      m_pDecThrCtxActive[i] = NULL;
+      for (j = i; j < m_DecCtxActiveCount - 1; j++) {
+        m_pDecThrCtxActive[j] = m_pDecThrCtxActive[j + 1];
+        m_pDecThrCtxActive[j + 1] = NULL;
+      }
+      --m_DecCtxActiveCount;
+      break;
+    }
+  }
+
+  m_pDecThrCtxActive[m_DecCtxActiveCount++] = &m_pDecThrCtx[signal];
+  if (m_pLastDecThrCtx != NULL) {
+    m_pDecThrCtx[signal].pCtx->pLastThreadCtx = m_pLastDecThrCtx;
+  }
+  m_pDecThrCtx[signal].kpSrc = const_cast<uint8_t*> (kpSrc);
+  m_pDecThrCtx[signal].kiSrcLen = kiSrcLen;
+  m_pDecThrCtx[signal].ppDst = ppDst;
+  memcpy (&m_pDecThrCtx[signal].sDstInfo, pDstInfo, sizeof (SBufferInfo));
+
+  ParseAccessUnit (m_pDecThrCtx[signal]);
+  m_pLastDecThrCtx = &m_pDecThrCtx[signal];
+  m_pDecThrCtx[signal].sThreadInfo.uiCommand = WELS_DEC_THREAD_COMMAND_RUN;
+  RELEASE_SEMAPHORE (&m_pDecThrCtx[signal].sThreadInfo.sIsActivated);
+
+  // wait early picture
+  if (m_DecCtxActiveCount >= m_iThreadCount) {
+    WAIT_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle, WELS_DEC_THREAD_WAIT_INFINITE);
+    RELEASE_SEMAPHORE (&m_pDecThrCtxActive[0]->sThreadInfo.sIsIdle);
+  }
   return state;
 }