shithub: openh264

Download patch

ref: 17139510a354b3f5aec036b8d583d188cb04aa10
parent: 2ca99122d4e561fd3890fdbe28063c2f318b4fd9
author: huili2 <huili2@cisco.com>
date: Fri Feb 10 12:45:38 EST 2017

fix parseonly length overflow

--- a/codec/api/svc/codec_app_def.h
+++ b/codec/api/svc/codec_app_def.h
@@ -708,7 +708,7 @@
 */
 typedef struct TagParserBsInfo {
   int iNalNum;                                 ///< total NAL number in current AU
-  int iNalLenInByte [MAX_NAL_UNITS_IN_LAYER];  ///< each nal length
+  int *pNalLenInByte;  ///< each nal length
   unsigned char* pDstBuff;                     ///< outputted dst buffer for parsed bitstream
   int iSpsWidthInPixel;                        ///< required SPS width info
   int iSpsHeightInPixel;                       ///< required SPS height info
--- a/codec/decoder/core/inc/decoder_context.h
+++ b/codec/decoder/core/inc/decoder_context.h
@@ -378,7 +378,7 @@
   bool bFramePending;
   bool bFrameFinish;
   int32_t iNalNum;
-  int32_t iNalLenInByte[MAX_NAL_UNITS_IN_LAYER];
+  int32_t iMaxNalNum; //permitted max NAL num stored in parser
   SSpsBsInfo sSpsBsInfo [MAX_SPS_COUNT];
   SSpsBsInfo sSubsetSpsBsInfo [MAX_PPS_COUNT];
   SPpsBsInfo sPpsBsInfo [MAX_PPS_COUNT];
--- a/codec/decoder/core/inc/decoder_core.h
+++ b/codec/decoder/core/inc/decoder_core.h
@@ -66,6 +66,16 @@
 int32_t ExpandBsBuffer (PWelsDecoderContext pCtx, const int32_t kiSrcLen);
 
 /*
+ * ExpandBsLenBuffer
+ * Expand current BS length buffer to double size or maximum, due to max slice number exceeding
+ * Parameter:
+ * kiCurrLen: current value of total nal number (including non-VCL nal)
+ * return:
+ *  0 - success; otherwise returned error_no defined in error_no.h.
+ */
+int32_t ExpandBsLenBuffer (PWelsDecoderContext pCtx, const int32_t kiCurrLen);
+
+/*
  * CheckBsBuffer
  * Check if current buffer size is enough
  */
--- a/codec/decoder/core/src/decoder_core.cpp
+++ b/codec/decoder/core/src/decoder_core.cpp
@@ -81,7 +81,7 @@
       int32_t iNalLen = 0;
       int32_t iNum = 0;
       while (iNum < pParser->iNalNum) {
-        iTotalNalLen += pParser->iNalLenInByte[iNum++];
+        iTotalNalLen += pParser->pNalLenInByte[iNum++];
       }
       uint8_t* pDstBuf = pParser->pDstBuff + iTotalNalLen;
       int32_t iIdx = pCurAu->uiStartPos;
@@ -96,6 +96,12 @@
 
       if (pCurAu->pNalUnitsList [iIdx]->sNalHeaderExt.bIdrFlag) { //IDR
         if (pCtx->bFrameFinish) { //add required sps/pps
+          if (pParser->iNalNum > pCtx->iMaxNalNum - 2) { //2 reserved for sps+pps
+            WelsLog (& (pCtx->sLogCtx), WELS_LOG_INFO,
+                     "DecodeFrameConstruction(): current NAL num (%d) plus sps & pps exceeds permitted num (%d). Will expand",
+                     pParser->iNalNum, pCtx->iMaxNalNum);
+            WELS_VERIFY_RETURN_IF (ERR_INFO_OUT_OF_MEMORY, ExpandBsLenBuffer (pCtx, pParser->iNalNum + 2))
+          }
           bool bSubSps = (NAL_UNIT_CODED_SLICE_EXT == pCurAu->pNalUnitsList [iIdx]->sNalHeaderExt.sNalUnitHeader.eNalUnitType);
           SSpsBsInfo* pSpsBs = NULL;
           SPpsBsInfo* pPpsBs = NULL;
@@ -114,21 +120,26 @@
             return ERR_INFO_OUT_OF_MEMORY;
           }
           memcpy (pDstBuf, pSpsBs->pSpsBsBuf, pSpsBs->uiSpsBsLen);
-          pParser->iNalLenInByte [pParser->iNalNum ++] = pSpsBs->uiSpsBsLen;
-          pCtx->iNalLenInByte[pCtx->iNalNum ++] = pSpsBs->uiSpsBsLen;
+          pParser->pNalLenInByte [pParser->iNalNum ++] = pSpsBs->uiSpsBsLen;
           pDstBuf += pSpsBs->uiSpsBsLen;
           memcpy (pDstBuf, pPpsBs->pPpsBsBuf, pPpsBs->uiPpsBsLen);
-          pParser->iNalLenInByte [pParser->iNalNum ++] = pPpsBs->uiPpsBsLen;
+          pParser->pNalLenInByte [pParser->iNalNum ++] = pPpsBs->uiPpsBsLen;
           pDstBuf += pPpsBs->uiPpsBsLen;
           pCtx->bFrameFinish = false;
         }
       }
       //then VCL data re-write
+      if (pParser->iNalNum + iEndIdx - iIdx + 1 > pCtx->iMaxNalNum) { //calculate total NAL num
+        WelsLog (& (pCtx->sLogCtx), WELS_LOG_INFO,
+                 "DecodeFrameConstruction(): current NAL num (%d) exceeds permitted num (%d). Will expand",
+                 pParser->iNalNum + iEndIdx - iIdx + 1, pCtx->iMaxNalNum);
+        WELS_VERIFY_RETURN_IF (ERR_INFO_OUT_OF_MEMORY, ExpandBsLenBuffer (pCtx, pParser->iNalNum + iEndIdx - iIdx + 1))
+      }
       while (iIdx <= iEndIdx) {
         pCurNal = pCurAu->pNalUnitsList [iIdx ++];
         iNalLen = pCurNal->sNalData.sVclNal.iNalLength;
         pNalBs = pCurNal->sNalData.sVclNal.pNalPos;
-        pParser->iNalLenInByte [pParser->iNalNum ++] = iNalLen;
+        pParser->pNalLenInByte [pParser->iNalNum ++] = iNalLen;
         if (pDstBuf - pParser->pDstBuff + iNalLen >= MAX_ACCESS_UNIT_CAPACITY) {
           WelsLog (& (pCtx->sLogCtx), WELS_LOG_ERROR,
                    "DecodeFrameConstruction(): composed output size (%ld) exceeds (%d). Failed to parse. current data pos %d out of %d:, previously accumulated num: %d, total num: %d, previously accumulated len: %d, current len: %d, current buf pos: %p, header buf pos: %p \n",
@@ -504,6 +515,13 @@
     }
     pCtx->sSavedData.pStartPos = pCtx->sSavedData.pCurPos = pCtx->sSavedData.pHead;
     pCtx->sSavedData.pEnd = pCtx->sSavedData.pHead + pCtx->iMaxBsBufferSizeInByte;
+
+    pCtx->iMaxNalNum = MAX_NAL_UNITS_IN_LAYER + 2; //2 reserved for SPS+PPS
+    pCtx->pParserBsInfo->pNalLenInByte = static_cast<int*> (pMa->WelsMallocz (pCtx->iMaxNalNum * sizeof (int),
+                                         "pCtx->pParserBsInfo->pNalLenInByte"));
+    if (pCtx->pParserBsInfo->pNalLenInByte == NULL) {
+      return ERR_INFO_OUT_OF_MEMORY;
+    }
   }
   return ERR_NONE;
 }
@@ -562,6 +580,37 @@
   return ERR_NONE;
 }
 
+int32_t ExpandBsLenBuffer (PWelsDecoderContext pCtx, const int kiCurrLen) {
+  SParserBsInfo* pParser = pCtx->pParserBsInfo;
+  if (!pParser->pNalLenInByte)
+    return ERR_INFO_INVALID_ACCESS;
+
+  int iNewLen = kiCurrLen;
+  if (kiCurrLen >= MAX_MB_SIZE + 2) { //exceeds the max MB number of level 5.2
+    WelsLog (& (pCtx->sLogCtx), WELS_LOG_WARNING, "Current nal num (%d) exceededs %d.", kiCurrLen, MAX_MB_SIZE);
+    pCtx->iErrorCode |= dsOutOfMemory;
+    return ERR_INFO_OUT_OF_MEMORY;
+  } else {
+    iNewLen = kiCurrLen << 1;
+    iNewLen = WELS_MIN (iNewLen, MAX_MB_SIZE + 2);
+  }
+
+  CMemoryAlign* pMa = pCtx->pMemAlign;
+  int* pNewLenBuffer = static_cast<int*> (pMa->WelsMallocz (iNewLen * sizeof (int),
+                                          "pCtx->pParserBsInfo->pNalLenInByte"));
+  if (pNewLenBuffer == NULL) {
+    pCtx->iErrorCode |= dsOutOfMemory;
+    return ERR_INFO_OUT_OF_MEMORY;
+  }
+
+  //copy existing data from old length buffer to new
+  memcpy (pNewLenBuffer, pParser->pNalLenInByte, pCtx->iMaxNalNum * sizeof (int));
+  pMa->WelsFree (pParser->pNalLenInByte, "pCtx->pParserBsInfo->pNalLenInByte");
+  pParser->pNalLenInByte = pNewLenBuffer;
+  pCtx->iMaxNalNum = iNewLen;
+  return ERR_NONE;
+}
+
 int32_t CheckBsBuffer (PWelsDecoderContext pCtx, const int32_t kiSrcLen) {
   if (kiSrcLen > MAX_ACCESS_UNIT_CAPACITY) { //exceeds max allowed data
     WelsLog (& (pCtx->sLogCtx), WELS_LOG_WARNING, "Max AU size exceeded. Allowed size = %d, current size = %d",
@@ -633,6 +682,11 @@
     pCtx->sSavedData.pStartPos            = NULL;
     pCtx->sSavedData.pCurPos              = NULL;
     if (pCtx->pParserBsInfo) {
+      if (pCtx->pParserBsInfo->pNalLenInByte) {
+        pMa->WelsFree (pCtx->pParserBsInfo->pNalLenInByte, "pCtx->pParserBsInfo->pNalLenInByte");
+        pCtx->pParserBsInfo->pNalLenInByte = NULL;
+        pCtx->iMaxNalNum = 0;
+      }
       if (pCtx->pParserBsInfo->pDstBuff) {
         pMa->WelsFree (pCtx->pParserBsInfo->pDstBuff, "pCtx->pParserBsInfo->pDstBuff");
         pCtx->pParserBsInfo->pDstBuff = NULL;
--- a/codec/decoder/plus/src/welsDecoderExt.cpp
+++ b/codec/decoder/plus/src/welsDecoderExt.cpp
@@ -668,7 +668,7 @@
   m_pDecContext->pParam->eEcActiveIdc = ERROR_CON_DISABLE; //add protection to disable EC here.
   if (!m_pDecContext->bFramePending) { //frame complete
     m_pDecContext->pParserBsInfo->iNalNum = 0;
-    memset (m_pDecContext->pParserBsInfo->iNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER);
+    memset (m_pDecContext->pParserBsInfo->pNalLenInByte, 0, MAX_NAL_UNITS_IN_LAYER);
   }
   pDstInfo->iNalNum = 0;
   pDstInfo->iSpsWidthInPixel = pDstInfo->iSpsHeightInPixel = 0;
binary files /dev/null b/res/jm_1080p_allslice.264 differ
--- a/test/api/decode_api_test.cpp
+++ b/test/api/decode_api_test.cpp
@@ -801,8 +801,13 @@
     int rv = decoder_->Initialize (&decParam);
     ASSERT_EQ (0, rv);
     memset (&BsInfo_, 0, sizeof (SParserBsInfo));
-    fYuv_ = fopen ("./res/CiscoVT2people_160x96_6fps.yuv", "rb");
-    ASSERT_TRUE (fYuv_ != NULL);
+    const char* sFileName = "res/CiscoVT2people_160x96_6fps.yuv";
+#if defined(ANDROID_NDK)
+    std::string filename = std::string ("/sdcard/") + sFileName;
+    ASSERT_TRUE ((fYuv_ = fopen (filename.c_str(), "rb")) != NULL);
+#else
+    ASSERT_TRUE ((fYuv_ = fopen (sFileName, "rb")) != NULL);
+#endif
     iWidth_ = kiWidth;
     iHeight_ = kiHeight;
   }
@@ -810,6 +815,7 @@
     EncodeDecodeTestBase::TearDown();
     if (fYuv_ != NULL) {
       fclose (fYuv_);
+      fYuv_ = NULL;
     }
   }
 
@@ -820,7 +826,15 @@
     BsInfo_.pDstBuff = pTmpPtr;
   }
 
-  void EncodeOneFrame (int iIdx) {
+  void MockInputData (uint8_t* pData, int32_t iSize) {
+    int32_t iCurr = 0;
+    while (iCurr < iSize) {
+      * (pData + iCurr) = (* (pData + iCurr) + (rand() % 20) + 256) & 0x00ff;
+      iCurr++;
+    }
+  }
+
+  void EncodeOneFrame (bool bMock) {
     int iFrameSize = iWidth_ * iHeight_ * 3 / 2;
     int iSize = (int) fread (buf_.data(), sizeof (char), iFrameSize, fYuv_);
     if (feof (fYuv_) || iSize != iFrameSize) {
@@ -828,6 +842,9 @@
       iSize = (int) fread (buf_.data(), sizeof (char), iFrameSize, fYuv_);
       ASSERT_TRUE (iSize == iFrameSize);
     }
+    if (bMock) {
+      MockInputData (buf_.data(), iWidth_ * iHeight_);
+    }
     int rv = encoder_->EncodeFrame (&EncPic, &info);
     ASSERT_TRUE (rv == cmResultSuccess || rv == cmUnknownReason);
   }
@@ -845,7 +862,7 @@
   SHA1Context ctx_;
 };
 
-//#define DEBUG_FILE_SAVE
+//#define DEBUG_FILE_SAVE_PARSEONLY_GENERAL
 TEST_F (DecodeParseAPI, ParseOnly_General) {
   EncodeDecodeFileParamBase p;
   p.width = iWidth_;
@@ -861,7 +878,7 @@
   encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &iTraceLevel);
   decoder_->SetOption (DECODER_OPTION_TRACE_LEVEL, &iTraceLevel);
   uint32_t uiTargetLayerId = rand() % kiTotalLayer; //run only once
-#ifdef DEBUG_FILE_SAVE
+#ifdef DEBUG_FILE_SAVE_PARSEONLY_GENERAL
   FILE* fDec = fopen ("output.264", "wb");
   FILE* fEnc = fopen ("enc.264", "wb");
   FILE* fExtract = fopen ("extract.264", "wb");
@@ -874,14 +891,14 @@
 
     while (iFrame < p.numframes) {
       //encode
-      EncodeOneFrame (iFrame);
+      EncodeOneFrame (0);
       //extract target layer data
       encToDecData (info, iLen);
-#ifdef DEBUG_FILE_SAVE
+#ifdef DEBUG_FILE_SAVE_PARSEONLY_GENERAL
       fwrite (info.sLayerInfo[0].pBsBuf, iLen, 1, fEnc);
 #endif
       ExtractDidNal (&info, iLen, &m_SLostSim, uiTargetLayerId);
-#ifdef DEBUG_FILE_SAVE
+#ifdef DEBUG_FILE_SAVE_PARSEONLY_GENERAL
       fwrite (info.sLayerInfo[0].pBsBuf, iLen, 1, fExtract);
 #endif
       //parseonly
@@ -896,10 +913,10 @@
       iLen = 0;
       int i = 0;
       while (i < BsInfo_.iNalNum) {
-        iLen += BsInfo_.iNalLenInByte[i];
+        iLen += BsInfo_.pNalLenInByte[i];
         i++;
       }
-#ifdef DEBUG_FILE_SAVE
+#ifdef DEBUG_FILE_SAVE_PARSEONLY_GENERAL
       fwrite (BsInfo_.pDstBuff, iLen, 1, fDec);
 #endif
       SHA1Input (&ctx_, BsInfo_.pDstBuff, iLen);
@@ -909,10 +926,10 @@
     unsigned char digest[SHA_DIGEST_LENGTH];
     SHA1Result (&ctx_, digest);
     if (!HasFatalFailure()) {
-      CompareHashAnyOf (digest, pHashStr[uiTargetLayerId], sizeof *pHashStr / sizeof **pHashStr);
+      CompareHashAnyOf (digest, pHashStr[uiTargetLayerId], sizeof * pHashStr / sizeof** pHashStr);
     }
   } //while
-#ifdef DEBUG_FILE_SAVE
+#ifdef DEBUG_FILE_SAVE_PARSEONLY_GENERAL
   fclose (fEnc);
   fclose (fExtract);
   fclose (fDec);
@@ -947,7 +964,7 @@
 
   while (iFrame < p.numframes) {
     //encode
-    EncodeOneFrame (iFrame);
+    EncodeOneFrame (0);
     //parseonly
     if (iFrame == iMissedPicNum) { //make current frame partly missing
       //Frame: P, first slice loss
@@ -979,5 +996,197 @@
   } //while
 }
 
+//Test parseonly crash cases
+class DecodeParseCrashAPI : public DecodeParseAPI {
+ public:
+  DecodeParseCrashAPI() {
+  }
+  void SetUp() {
+    DecodeParseAPI::SetUp();
+    iWidth_ = 1280;
+    iHeight_ = 720;
+
+    ucBuf_ = NULL;
+    ucBuf_ = new unsigned char[1000000];
+    ASSERT_TRUE (ucBuf_ != NULL);
+
+  }
+  void TearDown() {
+    DecodeParseAPI::TearDown();
+    if (NULL != ucBuf_) {
+      delete[] ucBuf_;
+      ucBuf_ = NULL;
+    }
+    ASSERT_TRUE (ucBuf_ == NULL);
+  }
+
+ protected:
+  unsigned char* ucBuf_;
+};
+
+//#define DEBUG_FILE_SAVE_PARSE_CRA1
+TEST_F (DecodeParseCrashAPI, ParseOnlyCrash_General) {
+  if (fYuv_)
+    fclose (fYuv_);
+  const char* sFileName = "res/Cisco_Absolute_Power_1280x720_30fps.yuv";
+#if defined(ANDROID_NDK)
+  std::string filename = std::string ("/sdcard/") + sFileName;
+  ASSERT_TRUE ((fYuv_ = fopen (filename.c_str(), "rb")) != NULL);
+#else
+  ASSERT_TRUE ((fYuv_ = fopen (sFileName, "rb")) != NULL);
+#endif
+  uint32_t uiGet;
+  encoder_->Uninitialize();
+  //do tests until crash
+  unsigned int uiLoopRound = 0;
+  unsigned char* pucBuf = ucBuf_;
+  int iDecAuSize;
+#ifdef DEBUG_FILE_SAVE_PARSE_CRA1
+  //open file to save tested BS
+  FILE* fDataFile = fopen ("test_parseonly_crash.264", "wb");
+  FILE* fLenFile = fopen ("test_parseonly_crash_len.log", "w");
+  int iFileSize = 0;
+#endif
+
+  do {
+#ifdef DEBUG_FILE_SAVE_PARSE_CRA1
+    int iTotalFrameNum = (rand() % 1200) + 1;
+#else
+    int iTotalFrameNum = (rand() % 100) + 1;
+#endif
+    EncodeDecodeParamBase p = kParamArray[8]; //720p by default
+
+    //Initialize Encoder
+    prepareParam (1, 4, p.width, p.height, p.frameRate, &param_);
+    param_.iRCMode = RC_TIMESTAMP_MODE;
+    param_.iTargetBitrate = p.iTarBitrate;
+    param_.uiIntraPeriod = 0;
+    param_.eSpsPpsIdStrategy = CONSTANT_ID;
+    param_.bEnableBackgroundDetection = true;
+    param_.bEnableSceneChangeDetect = (rand() % 3) ? true : false;
+    param_.bPrefixNalAddingCtrl = 0;// (rand() % 2) ? true : false;
+    param_.iEntropyCodingModeFlag = 0;
+    param_.bEnableFrameSkip = true;
+    param_.iMultipleThreadIdc = 0;
+    param_.sSpatialLayers[0].iSpatialBitrate = p.iTarBitrate;
+    param_.sSpatialLayers[0].iMaxSpatialBitrate = p.iTarBitrate << 1;
+    param_.sSpatialLayers[0].sSliceArgument.uiSliceMode =
+      SM_FIXEDSLCNUM_SLICE; // (rand() % 2) ? SM_SIZELIMITED_SLICE : SM_SINGLE_SLICE;
+    if (param_.sSpatialLayers[0].sSliceArgument.uiSliceMode == SM_SIZELIMITED_SLICE) {
+      param_.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = 1400;
+      param_.uiMaxNalSize = 1400;
+    } else {
+      param_.sSpatialLayers[0].sSliceArgument.uiSliceSizeConstraint = 0;
+      param_.uiMaxNalSize = 0;
+    }
+
+    int rv = encoder_->InitializeExt (&param_);
+    ASSERT_TRUE (rv == cmResultSuccess);
+    decoder_->GetOption (DECODER_OPTION_ERROR_CON_IDC, &uiGet);
+    EXPECT_EQ (uiGet, (uint32_t)ERROR_CON_DISABLE); //default value should be ERROR_CON_SLICE_COPY
+    int32_t iTraceLevel = WELS_LOG_QUIET;
+    encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &iTraceLevel);
+    decoder_->SetOption (DECODER_OPTION_TRACE_LEVEL, &iTraceLevel);
+
+    //Start for enc/dec
+    int iIdx = 0;
+    unsigned char* pData[3] = { NULL };
+
+    EncodeDecodeFileParamBase pInput; //to conform with old functions
+    pInput.width = p.width;
+    pInput.height = p.height;
+    pInput.frameRate = p.frameRate;
+    prepareEncDecParam (pInput);
+    while (iIdx++ < iTotalFrameNum) { // loop in frame
+      EncodeOneFrame (1);
+#ifdef DEBUG_FILE_SAVE_PARSE_CRA1
+      //reset file if file size large
+      if ((info.eFrameType == videoFrameTypeIDR) && (iFileSize >= (1 << 25))) {
+        fclose (fDataFile);
+        fclose (fLenFile);
+        fDataFile = fopen ("test_parseonly_crash.264", "wb");
+        fLenFile = fopen ("test_parseonly_crash_len.log", "w");
+        iFileSize = 0;
+        decoder_->Uninitialize();
+
+        SDecodingParam decParam;
+        memset (&decParam, 0, sizeof (SDecodingParam));
+        decParam.uiTargetDqLayer = UCHAR_MAX;
+        decParam.eEcActiveIdc = ERROR_CON_DISABLE;
+        decParam.bParseOnly = true;
+        decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
+
+        rv = decoder_->Initialize (&decParam);
+        ASSERT_EQ (0, rv);
+      }
+#endif
+      if (info.eFrameType == videoFrameTypeSkip)
+        continue;
+      //deal with packets
+      unsigned char* pBsBuf;
+      iDecAuSize = 0;
+      pucBuf = ucBuf_; //init buf start pos for decoder usage
+      for (int iLayerNum = 0; iLayerNum < info.iLayerNum; iLayerNum++) {
+        SLayerBSInfo* pLayerBsInfo = &info.sLayerInfo[iLayerNum];
+        pBsBuf = info.sLayerInfo[iLayerNum].pBsBuf;
+        int iTotalNalCnt = pLayerBsInfo->iNalCount;
+        for (int iNalCnt = 0; iNalCnt < iTotalNalCnt; iNalCnt++) {  //loop in NAL
+          int iPacketSize = pLayerBsInfo->pNalLengthInByte[iNalCnt];
+          //packet loss
+          int iLossRateRange = (uiLoopRound % 20) + 1; //1-100
+          int iLossRate = (rand() % iLossRateRange);
+          bool bPacketLost = (rand() % 101) > (100 -
+                                               iLossRate);   // [0, (100-iLossRate)] indicates NO LOSS, (100-iLossRate, 100] indicates LOSS
+          if (!bPacketLost) { //no loss
+            memcpy (pucBuf, pBsBuf, iPacketSize);
+            pucBuf += iPacketSize;
+            iDecAuSize += iPacketSize;
+          }
+          //update bs info
+          pBsBuf += iPacketSize;
+        } //nal
+      } //layer
+
+#ifdef DEBUG_FILE_SAVE_PARSE_CRA1
+      //save to file
+      if (iDecAuSize != 0) {
+        fwrite (ucBuf_, 1, iDecAuSize, fDataFile);
+        fflush (fDataFile);
+        iFileSize += iDecAuSize;
+      }
+
+      //save to len file
+      unsigned long ulTmp[4];
+      ulTmp[0] = ulTmp[1] = ulTmp[2] = iIdx;
+      ulTmp[3] = iDecAuSize;
+      fwrite (ulTmp, sizeof (unsigned long), 4, fLenFile); // index, timeStamp, data size
+      fflush (fLenFile);
+#endif
+
+      //decode
+      pData[0] = pData[1] = pData[2] = 0;
+      memset (&BsInfo_, 0, sizeof (SParserBsInfo));
+
+      rv = decoder_->DecodeParser (ucBuf_, iDecAuSize, &BsInfo_);
+      rv = decoder_->DecodeParser (NULL, 0, &BsInfo_); //reconstruction
+      //guarantee decoder EC status
+      decoder_->GetOption (DECODER_OPTION_ERROR_CON_IDC, &uiGet);
+      EXPECT_EQ (uiGet, (uint32_t)ERROR_CON_DISABLE);
+    } //frame
+    uiLoopRound++;
+    if (uiLoopRound >= (1 << 30))
+      uiLoopRound = 0;
+#ifdef DEBUG_FILE_SAVE_PARSE_CRA1
+    if (uiLoopRound % 10 == 0)
+      printf ("run %d times.\n", uiLoopRound);
+  } while (1);
+  fclose (fDataFile);
+  fclose (fLenFile);
+#else
+  }
+  while (0);
+#endif
+
+}
 
 
--- a/test/decoder/DecUT_ParseSyntax.cpp
+++ b/test/decoder/DecUT_ParseSyntax.cpp
@@ -58,7 +58,7 @@
   return dsErrorFree;
 }
 
-void UninitDecoder (PWelsDecoderContext pCtx) {
+void UninitDecoder (PWelsDecoderContext& pCtx) {
   if (NULL == pCtx)
     return;
 
@@ -132,11 +132,13 @@
   //Uninit members
   void Uninit();
   //Decoder real bitstream
-// void DecoderBs (const char* sFileName);
-  //Parse real bitstream
   void DecodeBs (const char* sFileName);
+  //Parse real bitstream
+  void ParseBs (const char* sFileName);
   //Scalinglist
   void TestScalingList();
+  //specific bitstream test
+  void TestSpecificBs();
   //Do whole tests here
   void DecoderParseSyntaxTestAll();
 
@@ -250,6 +252,66 @@
 
 
 }
+void DecoderParseSyntaxTest::ParseBs (const char* sFileName) {
+
+  uint8_t* pBuf = NULL;
+  int32_t iBufPos = 0;
+  int32_t iFileSize;
+  int32_t i = 0;
+  int32_t iSliceSize;
+  int32_t iSliceIndex = 0;
+  int32_t iEndOfStreamFlag = 0;
+  FILE* pH264File;
+  uint8_t uiStartCode[4] = { 0, 0, 0, 1 };
+  int iRet;
+
+#if defined(ANDROID_NDK)
+  std::string filename = std::string ("/sdcard/") + sFileName;
+  ASSERT_TRUE ((pH264File = fopen (filename.c_str(), "rb")) != NULL);
+#else
+  ASSERT_TRUE ((pH264File = fopen (sFileName, "rb")) != NULL);
+#endif
+  fseek (pH264File, 0L, SEEK_END);
+  iFileSize = (int32_t)ftell (pH264File);
+  fseek (pH264File, 0L, SEEK_SET);
+  pBuf = new uint8_t[iFileSize + 4];
+  ASSERT_EQ (fread (pBuf, 1, iFileSize, pH264File), (unsigned int)iFileSize);
+  memcpy (pBuf + iFileSize, &uiStartCode[0], 4); //confirmed_safe_unsafe_usage
+  while (true) {
+    if (iBufPos >= iFileSize) {
+      iEndOfStreamFlag = true;
+      if (iEndOfStreamFlag)
+        m_pDec->SetOption (DECODER_OPTION_END_OF_STREAM, (void*)&iEndOfStreamFlag);
+      break;
+    }
+    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)) {
+        break;
+      }
+    }
+    iSliceSize = i;
+    memset (&m_sParserBsInfo, 0, sizeof (SParserBsInfo));
+    iRet = m_pDec->DecodeParser (pBuf + iBufPos, iSliceSize, &m_sParserBsInfo);
+    EXPECT_TRUE (iRet == dsErrorFree || iRet == dsFramePending);
+    iRet = m_pDec->DecodeParser (NULL, 0, &m_sParserBsInfo);
+    EXPECT_TRUE (iRet == dsErrorFree || iRet == dsFramePending);
+    iBufPos += iSliceSize;
+    ++iSliceIndex;
+    if (iSliceIndex == 4)
+      break;
+  }
+
+  fclose (pH264File);
+  if (pBuf) {
+    delete[] pBuf;
+    pBuf = NULL;
+  }
+
+
+}
+
+
 void DecoderParseSyntaxTest::TestScalingList() {
   uint8_t iScalingList[6][16] = {
     {17, 17, 16, 16, 17, 16, 15, 15, 16, 15, 15, 15, 16, 15, 15, 15 },
@@ -295,11 +357,22 @@
   Uninit();
 }
 
+void DecoderParseSyntaxTest::TestSpecificBs() {
+  int32_t iRet = ERR_NONE;
+  Uninit();
+  m_sDecParam.bParseOnly = true;
+  m_sDecParam.eEcActiveIdc = ERROR_CON_DISABLE;
+  iRet = m_pDec->Initialize (&m_sDecParam);
+  ASSERT_EQ (iRet, ERR_NONE);
+  ParseBs ("res/jm_1080p_allslice.264");
+  m_pDec->Uninitialize();
+  //Uninit();
+}
 //TEST here for whole tests
 TEST_F (DecoderParseSyntaxTest, DecoderParseSyntaxTestAll) {
 
   TestScalingList();
-
+  TestSpecificBs();
 }