shithub: openh264

Download patch

ref: e29ceca39267a98ca93088392bd2a7e44470e6f1
parent: 1641ce9ee54462edf37dec86122f04265eec4e0c
parent: 15723e1204a8d5f725a88ad1a17668ecbabb9b91
author: ruil2 <ruil2@cisco.com>
date: Fri Feb 13 11:31:18 EST 2015

Merge pull request #1813 from sijchen/savc61

[Encoder] complete logic of simulcastavc with sps_pps_listing

--- a/codec/encoder/core/inc/encoder_context.h
+++ b/codec/encoder/core/inc/encoder_context.h
@@ -246,8 +246,8 @@
 
   uint32_t GetNeededSpsNum() {
     if (0 == sPSOVector.uiNeededSpsNum) {
-      sPSOVector.uiNeededSpsNum = ((SPS_LISTING & pSvcParam->eSpsPpsIdStrategy) ? (MAX_SPS_COUNT) :
-                                   ((pSvcParam->bSimulcastAVC) ? (pSvcParam->iSpatialLayerNum) : (1)));
+      sPSOVector.uiNeededSpsNum = ((SPS_LISTING & pSvcParam->eSpsPpsIdStrategy) ? (MAX_SPS_COUNT) : (1));
+      sPSOVector.uiNeededSpsNum *= ((pSvcParam->bSimulcastAVC) ? (pSvcParam->iSpatialLayerNum) : (1));
     }
     return sPSOVector.uiNeededSpsNum;
   }
@@ -264,6 +264,7 @@
     if (0 == sPSOVector.uiNeededPpsNum) {
       sPSOVector.uiNeededPpsNum = ((pSvcParam->eSpsPpsIdStrategy & SPS_PPS_LISTING) ? (MAX_PPS_COUNT) :
                                    (1 + pSvcParam->iSpatialLayerNum));
+      sPSOVector.uiNeededPpsNum *= ((pSvcParam->bSimulcastAVC) ? (pSvcParam->iSpatialLayerNum) : (1));
     }
     return sPSOVector.uiNeededPpsNum;
   }
--- a/codec/encoder/core/src/encoder_ext.cpp
+++ b/codec/encoder/core/src/encoder_ext.cpp
@@ -321,7 +321,6 @@
     return ENC_RETURN_UNSUPPORTED_PARA;
   }
 
-
   //about iMultipleThreadIdc, bDeblockingParallelFlag, iLoopFilterDisableIdc, & uiSliceMode
   // (1) Single Thread
   //	if (THREAD==1)//single thread
@@ -335,9 +334,10 @@
   }
 
   // eSpsPpsIdStrategy checkings
-  if (pCodingParam->iSpatialLayerNum > 1 && (SPS_LISTING & pCodingParam->eSpsPpsIdStrategy)) {
+  if (pCodingParam->iSpatialLayerNum > 1 && (!pCodingParam->bSimulcastAVC)
+      && (SPS_LISTING & pCodingParam->eSpsPpsIdStrategy)) {
     WelsLog (pLogCtx, WELS_LOG_WARNING,
-             "ParamValidationExt(), eSpsPpsIdStrategy setting (%d) with multiple SpatialLayers (%d) not supported! eSpsPpsIdStrategy adjusted to CONSTANT_ID",
+             "ParamValidationExt(), eSpsPpsIdStrategy setting (%d) with multiple svc SpatialLayers (%d) not supported! eSpsPpsIdStrategy adjusted to CONSTANT_ID",
              pCodingParam->eSpsPpsIdStrategy, pCodingParam->iSpatialLayerNum);
     pCodingParam->eSpsPpsIdStrategy = CONSTANT_ID;
   }
@@ -540,7 +540,8 @@
       }
 
       if (pCodingParam->uiMaxNalSize < (NAL_HEADER_ADD_0X30BYTES + MAX_MACROBLOCK_SIZE_IN_BYTE)) {
-        WelsLog (pLogCtx, WELS_LOG_ERROR, "ParamValidationExt(), invalid uiMaxNalSize (%d) settings! should be larger than (NAL_HEADER_ADD_0X30BYTES + MAX_MACROBLOCK_SIZE_IN_BYTE)(%d)",
+        WelsLog (pLogCtx, WELS_LOG_ERROR,
+                 "ParamValidationExt(), invalid uiMaxNalSize (%d) settings! should be larger than (NAL_HEADER_ADD_0X30BYTES + MAX_MACROBLOCK_SIZE_IN_BYTE)(%d)",
                  pCodingParam->uiMaxNalSize, (NAL_HEADER_ADD_0X30BYTES + MAX_MACROBLOCK_SIZE_IN_BYTE));
         return ENC_RETURN_UNSUPPORTED_PARA;
       }
@@ -3140,6 +3141,7 @@
   pCtx->iPosBsBuffer	+= iNalSize;
   return ENC_RETURN_SUCCESS;
 }
+
 int32_t WelsWriteOnePPS (sWelsEncCtx* pCtx, const int32_t kiPpsIdx, int32_t& iNalSize) {
   //TODO
   int32_t iNal	= pCtx->pOut->iNalIndex;
@@ -3159,6 +3161,29 @@
   return ENC_RETURN_SUCCESS;
 }
 
+void UpdatePpsList (sWelsEncCtx* pCtx) {
+  assert (pCtx->iPpsNum <= MAX_DQ_LAYER_NUM);
+
+  //Generate PPS LIST
+  int32_t iPpsId = 0, iUsePpsNum = pCtx->iPpsNum;
+
+  for (int32_t iIdrRound = 0; iIdrRound < MAX_PPS_COUNT; iIdrRound++) {
+    for (iPpsId = 0; iPpsId < pCtx->iPpsNum; iPpsId++) {
+      pCtx->sPSOVector.iPpsIdList[iPpsId][iIdrRound] = ((iIdrRound * iUsePpsNum + iPpsId) % MAX_PPS_COUNT);
+    }
+  }
+
+  for (iPpsId = iUsePpsNum; iPpsId < MAX_PPS_COUNT; iPpsId++) {
+    memcpy (& (pCtx->pPPSArray[iPpsId]), & (pCtx->pPPSArray[iPpsId % iUsePpsNum]), sizeof (SWelsPPS));
+    pCtx->pPPSArray[iPpsId].iPpsId = iPpsId;
+    pCtx->iPpsNum++;
+  }
+
+  assert (pCtx->iPpsNum == MAX_PPS_COUNT);
+  pCtx->sPSOVector.uiInUsePpsNum = pCtx->iPpsNum;
+
+}
+
 /*!
  * \brief	write all parameter sets introduced in SVC extension
  * \return	writing results, success or error
@@ -3245,29 +3270,11 @@
   }
 
   /* write all PPS */
-  iIdx = 0;
   if ((SPS_PPS_LISTING == pCtx->pSvcParam->eSpsPpsIdStrategy) && (pCtx->iPpsNum < MAX_PPS_COUNT)) {
-    assert (pCtx->iPpsNum <= MAX_DQ_LAYER_NUM);
-
-    //Generate PPS LIST
-    int32_t iPpsId = 0, iUsePpsNum = pCtx->iPpsNum;
-
-    for (int32_t iIdrRound = 0; iIdrRound < MAX_PPS_COUNT; iIdrRound++) {
-      for (iPpsId = 0; iPpsId < pCtx->iPpsNum; iPpsId++) {
-        pCtx->sPSOVector.iPpsIdList[iPpsId][iIdrRound] = ((iIdrRound * iUsePpsNum + iPpsId) % MAX_PPS_COUNT);
-      }
-    }
-
-    for (iPpsId = iUsePpsNum; iPpsId < MAX_PPS_COUNT; iPpsId++) {
-      memcpy (& (pCtx->pPPSArray[iPpsId]), & (pCtx->pPPSArray[iPpsId % iUsePpsNum]), sizeof (SWelsPPS));
-      pCtx->pPPSArray[iPpsId].iPpsId = iPpsId;
-      pCtx->iPpsNum++;
-    }
-
-    assert (pCtx->iPpsNum == MAX_PPS_COUNT);
-    pCtx->sPSOVector.uiInUsePpsNum = pCtx->iPpsNum;
+    UpdatePpsList (pCtx);
   }
 
+  iIdx = 0;
   while (iIdx < pCtx->iPpsNum) {
     if ((INCREASING_ID & pCtx->pSvcParam->eSpsPpsIdStrategy)) {
       //para_set_type = 2: PPS, use MAX_PPS_COUNT
@@ -3437,6 +3444,7 @@
   return iSubSeqId;
 }
 
+// writing parasets for (simulcast) svc
 int32_t WriteSsvcParaset (sWelsEncCtx* pCtx, const int32_t kiSpatialNum,
                           SLayerBSInfo*& pLayerBsInfo, int32_t& iLayerNum, int32_t& iFrameSize) {
   int32_t iNonVclSize = 0, iCountNal = 0, iReturn;
@@ -3459,6 +3467,7 @@
   return iReturn;
 }
 
+// writing parasets for simulcast avc
 int32_t WriteSavcParaset (sWelsEncCtx* pCtx, const int32_t kiSpatialNum,
                           SLayerBSInfo*& pLayerBsInfo, int32_t& iLayerNum, int32_t& iFrameSize) {
   int32_t iNonVclSize = 0, iCountNal = 0, iReturn;
@@ -3479,7 +3488,6 @@
     iCountNal = 1;
     //finish writing one NAL
 
-
     pLayerBsInfo->uiSpatialId		= iIdx;
     pLayerBsInfo->uiTemporalId	= 0;
     pLayerBsInfo->uiQualityId		= 0;
@@ -3511,7 +3519,6 @@
     iCountNal = 1;
     //finish writing one NAL
 
-
     pLayerBsInfo->uiSpatialId		= iIdx;
     pLayerBsInfo->uiTemporalId	= 0;
     pLayerBsInfo->uiQualityId		= 0;
@@ -3538,7 +3545,89 @@
   return iReturn;
 }
 
+//cover the logic of simulcast avc + sps_pps_listing
+int32_t WriteSavcParaset_Listing (sWelsEncCtx* pCtx, const int32_t kiSpatialNum,
+                                  SLayerBSInfo*& pLayerBsInfo, int32_t& iLayerNum, int32_t& iFrameSize) {
+  int32_t iNonVclSize = 0, iCountNal = 0, iReturn;
 
+  // write SPS
+  iNonVclSize = 0;
+
+  for (int32_t iSpatialId = 0; iSpatialId < kiSpatialNum; iSpatialId++) {
+    iCountNal = 0;
+    for (int32_t iIdx = 0; iIdx < pCtx->iSpsNum; iIdx++) {
+      //writing one NAL
+      int32_t iNalSize = 0;
+      iReturn = WelsWriteOneSPS (pCtx, iIdx, iNalSize);
+      WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+      pLayerBsInfo->pNalLengthInByte[iCountNal] = iNalSize;
+      iNonVclSize += iNalSize;
+      iCountNal ++;
+      //finish writing one NAL
+    }
+
+    pLayerBsInfo->uiSpatialId		= iSpatialId;
+    pLayerBsInfo->uiTemporalId	= 0;
+    pLayerBsInfo->uiQualityId		= 0;
+    pLayerBsInfo->uiLayerType		= NON_VIDEO_CODING_LAYER;
+    pLayerBsInfo->iNalCount		= iCountNal;
+
+    //point to next pLayerBsInfo
+    ++ pLayerBsInfo;
+    pLayerBsInfo->pBsBuf			= pCtx->pFrameBs + pCtx->iPosBsBuffer;
+    pLayerBsInfo->pNalLengthInByte = (pLayerBsInfo - 1)->pNalLengthInByte + iCountNal;
+    //update for external countings
+    iCountNal = 0;
+    ++ iLayerNum;
+  }
+
+  // write PPS
+  if ((SPS_PPS_LISTING == pCtx->pSvcParam->eSpsPpsIdStrategy) && (pCtx->iPpsNum < MAX_PPS_COUNT)) {
+    UpdatePpsList (pCtx);
+  }
+
+  //TODO: under new strategy, will PPS be correctly updated?
+  for (int32_t iSpatialId = 0; iSpatialId < kiSpatialNum; iSpatialId++) {
+    iCountNal = 0;
+    for (int32_t iIdx = 0; iIdx < pCtx->iPpsNum; iIdx++) {
+      //writing one NAL
+      int32_t iNalSize = 0;
+      iReturn = WelsWriteOnePPS (pCtx, iIdx, iNalSize);
+      WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+      pLayerBsInfo->pNalLengthInByte[iCountNal] = iNalSize;
+      iNonVclSize += iNalSize;
+      iCountNal ++;
+      //finish writing one NAL
+    }
+
+    pLayerBsInfo->uiSpatialId		= iSpatialId;
+    pLayerBsInfo->uiTemporalId	= 0;
+    pLayerBsInfo->uiQualityId		= 0;
+    pLayerBsInfo->uiLayerType		= NON_VIDEO_CODING_LAYER;
+    pLayerBsInfo->iNalCount		= iCountNal;
+
+    //point to next pLayerBsInfo
+    ++ pLayerBsInfo;
+    pLayerBsInfo->pBsBuf			= pCtx->pFrameBs + pCtx->iPosBsBuffer;
+    pLayerBsInfo->pNalLengthInByte = (pLayerBsInfo - 1)->pNalLengthInByte + iCountNal;
+    //update for external countings
+    iCountNal = 0;
+    ++ iLayerNum;
+  }
+
+  // to check number of layers / nals / slices dependencies
+  if (iLayerNum > MAX_LAYER_NUM_OF_FRAME) {
+    WelsLog (& pCtx->sLogCtx, WELS_LOG_ERROR, "WriteSavcParaset(), iLayerNum(%d) > MAX_LAYER_NUM_OF_FRAME(%d)!",
+             iLayerNum, MAX_LAYER_NUM_OF_FRAME);
+    return ENC_RETURN_UNEXPECTED;
+  }
+
+  iFrameSize += iNonVclSize;
+  return iReturn;
+}
+
 /*!
  * \brief	core svc encoding process
  *
@@ -3637,9 +3726,13 @@
     ++ pCtx->uiIdrPicId;
     // write parameter sets bitstream or SEI/SSEI (if any) here
     // TODO: use function pointer instead
-    pCtx->iEncoderError = ((!pSvcParam->bSimulcastAVC)
-                           ? (WriteSsvcParaset (pCtx, iSpatialNum, pLayerBsInfo, iLayerNum, iFrameSize))
-                           : (WriteSavcParaset (pCtx, iSpatialNum, pLayerBsInfo, iLayerNum, iFrameSize)));
+    if (! (SPS_LISTING & pCtx->pSvcParam->eSpsPpsIdStrategy)) {
+      pCtx->iEncoderError = ((!pSvcParam->bSimulcastAVC)
+                             ? (WriteSsvcParaset (pCtx, iSpatialNum, pLayerBsInfo, iLayerNum, iFrameSize))
+                             : (WriteSavcParaset (pCtx, iSpatialNum, pLayerBsInfo, iLayerNum, iFrameSize)));
+    } else {
+      pCtx->iEncoderError = WriteSavcParaset_Listing (pCtx, iSpatialNum, pLayerBsInfo, iLayerNum, iFrameSize);
+    }
     WELS_VERIFY_RETURN_IFNEQ (pCtx->iEncoderError, ENC_RETURN_SUCCESS)
   }
 
@@ -4217,6 +4310,33 @@
 
   pFbi->eFrameType = eFrameType;
   pFbi->iFrameSizeInBytes = iFrameSize;
+
+#ifdef _DEBUG
+  if (pFbi->iLayerNum > MAX_LAYER_NUM_OF_FRAME) {
+    WelsLog (& pCtx->sLogCtx, WELS_LOG_ERROR, "WelsEncoderEncodeExt(), iLayerNum(%d) > MAX_LAYER_NUM_OF_FRAME(%d)!",
+             pFbi->iLayerNum, MAX_LAYER_NUM_OF_FRAME);
+    return ENC_RETURN_UNEXPECTED;
+  }
+
+  int32_t iTotalNal = 0;
+  for (int32_t k = 0; k < pFbi->iLayerNum; k++) {
+    iTotalNal += pFbi->sLayerInfo[k].iNalCount;
+
+    if ((pCtx->iActiveThreadsNum > 1) && (MAX_NAL_UNITS_IN_LAYER < pFbi->sLayerInfo[k].iNalCount)) {
+      WelsLog (& pCtx->sLogCtx, WELS_LOG_ERROR,
+               "WelsEncoderEncodeExt(), iCountNumNals(%d) > MAX_NAL_UNITS_IN_LAYER(%d) under multi-thread(%d) NOT supported!",
+               pFbi->sLayerInfo[k].iNalCount, MAX_NAL_UNITS_IN_LAYER), pCtx->iActiveThreadsNum;
+      return ENC_RETURN_UNEXPECTED;
+    }
+  }
+
+  if (iTotalNal > pCtx->pOut->iCountNals) {
+    WelsLog (& pCtx->sLogCtx, WELS_LOG_ERROR, "WelsEncoderEncodeExt(), iTotalNal(%d) > iCountNals(%d)!",
+             iTotalNal, pCtx->pOut->iCountNals);
+    return ENC_RETURN_UNEXPECTED;
+  }
+#endif
+
   return ENC_RETURN_SUCCESS;
 }
 
--- a/test/api/encode_decode_api_test.cpp
+++ b/test/api/encode_decode_api_test.cpp
@@ -224,6 +224,80 @@
       fwrite (info.sLayerInfo[0].pBsBuf, iLen, 1, pfEnc);
     }
   }
+
+  void TestOneSimulcastAVC (SEncParamExt* pParam, ISVCDecoder** decoder, unsigned char** pBsBuf, int iSpatialLayerNum,
+                            int iEncFrameNum,
+                            int iCallTimes) {
+//#define DEBUG_FILE_SAVE4
+    int aLen[MAX_SPATIAL_LAYER_NUM] = {0, 0, 0, 0};
+#ifdef DEBUG_FILE_SAVE4
+    FILE* fEnc[MAX_SPATIAL_LAYER_NUM];
+    if (iCallTimes == 0) {
+      fEnc[0] = fopen ("enc00.264", "wb");
+      fEnc[1] = fopen ("enc01.264", "wb");
+      fEnc[2] = fopen ("enc02.264", "wb");
+      fEnc[3] = fopen ("enc03.264", "wb");
+    } else {
+      fEnc[0] = fopen ("enc10.264", "wb");
+      fEnc[1] = fopen ("enc11.264", "wb");
+      fEnc[2] = fopen ("enc12.264", "wb");
+      fEnc[3] = fopen ("enc13.264", "wb");
+    }
+#endif
+
+    int rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, pParam);
+    ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed pParam: rv = " << rv;
+
+    int iIdx;
+    //begin testing
+    for (int iFrame = 0; iFrame < iEncFrameNum; iFrame++) {
+      int iResult;
+      int iLayerLen = 0;
+      unsigned char* pData[3] = { NULL };
+
+      InitialEncDec (pParam->iPicWidth, pParam->iPicHeight);
+      EncodeOneFrame (0);
+
+      // init
+      for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+        aLen[iIdx] = 0;
+      }
+      for (int iLayer = 0; iLayer < info.iLayerNum; ++iLayer) {
+        iLayerLen = 0;
+        const SLayerBSInfo& layerInfo = info.sLayerInfo[iLayer];
+        for (int iNal = 0; iNal < layerInfo.iNalCount; ++iNal) {
+          iLayerLen += layerInfo.pNalLengthInByte[iNal];
+        }
+
+        iIdx = layerInfo.uiSpatialId;
+        EXPECT_TRUE (iIdx < iSpatialLayerNum) << "iIdx = " << iIdx << ", iSpatialLayerNum = " << iSpatialLayerNum;
+        memcpy ((pBsBuf[iIdx] + aLen[iIdx]), layerInfo.pBsBuf, iLayerLen * sizeof (unsigned char));
+        aLen[iIdx] += iLayerLen;
+      }
+
+      for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+        pData[0] = pData[1] = pData[2] = 0;
+        memset (&dstBufInfo_, 0, sizeof (SBufferInfo));
+
+#ifdef DEBUG_FILE_SAVE4
+        fwrite (pBsBuf[iIdx], aLen[iIdx], 1, fEnc[iIdx]);
+#endif
+        iResult = decoder[iIdx]->DecodeFrame2 (pBsBuf[iIdx], aLen[iIdx], pData, &dstBufInfo_);
+        EXPECT_TRUE (iResult == cmResultSuccess) << "iResult=" << iResult << ", LayerIdx=" << iIdx;
+
+        iResult = decoder[iIdx]->DecodeFrame2 (NULL, 0, pData, &dstBufInfo_);
+        EXPECT_TRUE (iResult == cmResultSuccess) << "iResult=" << iResult << ", LayerIdx=" << iIdx;
+        EXPECT_EQ (dstBufInfo_.iBufferStatus, 1) << "LayerIdx=" << iIdx;
+      }
+    }
+
+#ifdef DEBUG_FILE_SAVE4
+    fclose (fEnc[0]);
+    fclose (fEnc[1]);
+    fclose (fEnc[2]);
+    fclose (fEnc[3]);
+#endif
+  }
 };
 
 class EncodeDecodeTestAPI : public ::testing::TestWithParam<EncodeDecodeFileParamBase>, public EncodeDecodeTestAPIBase {
@@ -3194,7 +3268,7 @@
     ASSERT_EQ (0, rv);
   }
 
-  iEncFrameNum = 1;
+  iEncFrameNum = 10;
   for (int iFrame = 0; iFrame < iEncFrameNum; iFrame++) {
     int iResult;
     int iLayerLen = 0;
@@ -3237,9 +3311,73 @@
       EXPECT_TRUE (iResult == cmResultSuccess) << "iResult=" << iResult << "LayerIdx=" << iIdx;
       EXPECT_EQ (dstBufInfo_.iBufferStatus, 1) << "LayerIdx=" << iIdx;
     }
+  }
 
+  for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    free (pBsBuf[iIdx]);
+
+    if (decoder[iIdx] != NULL) {
+      decoder[iIdx]->Uninitialize();
+      WelsDestroyDecoder (decoder[iIdx]);
+    }
   }
+}
 
+TEST_F (EncodeDecodeTestAPI, SimulcastAVC_SPS_PPS_LISTING) {
+  int iSpatialLayerNum = WelsClip3 ((rand() % MAX_SPATIAL_LAYER_NUM), 2, MAX_SPATIAL_LAYER_NUM);;
+  int iWidth       = WelsClip3 ((((rand() % MAX_WIDTH) >> 1)  + 1) << 1, 1 << iSpatialLayerNum, MAX_WIDTH);
+  int iHeight      = WelsClip3 ((((rand() % MAX_HEIGHT) >> 1)  + 1) << 1, 1 << iSpatialLayerNum, MAX_HEIGHT);
+  float fFrameRate = rand() + 0.5f;
+  int iEncFrameNum = WelsClip3 ((rand() % ENCODE_FRAME_NUM) + 1, 1, ENCODE_FRAME_NUM);
+  int iSliceNum        = 1;
+
+  // prepare params
+  SEncParamExt   sParam1;
+  SEncParamExt   sParam2;
+  encoder_->GetDefaultParams (&sParam1);
+  prepareParamDefault (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1);
+  //set flag of SPS_PPS_LISTING
+  sParam1.eSpsPpsIdStrategy = SPS_PPS_LISTING;//SPS_LISTING;//
+  //set flag of bSimulcastAVC
+  sParam1.bSimulcastAVC = true;
+  //prepare param2
+  memcpy (&sParam2, &sParam1, sizeof (SEncParamExt));
+  sParam2.sSpatialLayers[0].iVideoWidth = (sParam1.sSpatialLayers[0].iVideoWidth / 2);
+  sParam2.sSpatialLayers[0].iVideoHeight = (sParam1.sSpatialLayers[0].iVideoHeight / 2);
+
+  int rv = encoder_->InitializeExt (&sParam1);
+  ASSERT_TRUE (rv == cmResultSuccess) << "Init Failed sParam1: rv = " << rv;;
+  rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2);
+  ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv;
+
+  unsigned char*  pBsBuf[MAX_SPATIAL_LAYER_NUM];
+  ISVCDecoder* decoder[MAX_SPATIAL_LAYER_NUM];
+
+  int iIdx = 0;
+
+  //create decoder
+  for (int iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+    pBsBuf[iIdx] = static_cast<unsigned char*> (malloc (iWidth * iHeight * 3 * sizeof (unsigned char) / 2));
+    EXPECT_TRUE (pBsBuf[iIdx] != NULL);
+
+    long rv = WelsCreateDecoder (&decoder[iIdx]);
+    ASSERT_EQ (0, rv);
+    EXPECT_TRUE (decoder[iIdx] != NULL);
+
+    SDecodingParam decParam;
+    memset (&decParam, 0, sizeof (SDecodingParam));
+    decParam.eOutputColorFormat  = videoFormatI420;
+    decParam.uiTargetDqLayer = UCHAR_MAX;
+    decParam.eEcActiveIdc = ERROR_CON_SLICE_COPY;
+    decParam.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT;
+
+    rv = decoder[iIdx]->Initialize (&decParam);
+    ASSERT_EQ (0, rv);
+  }
+
+  TestOneSimulcastAVC (&sParam1, decoder, pBsBuf, iSpatialLayerNum, iEncFrameNum, 0);
+  TestOneSimulcastAVC (&sParam2, decoder, pBsBuf, iSpatialLayerNum, iEncFrameNum, 1);
+
   for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
     free (pBsBuf[iIdx]);
 
@@ -3247,9 +3385,7 @@
       decoder[iIdx]->Uninitialize();
       WelsDestroyDecoder (decoder[iIdx]);
     }
-#ifdef DEBUG_FILE_SAVE3
-    fclose (fEnc[iIdx]);
-#endif
+
   }
 }
 
@@ -3398,5 +3534,4 @@
     fclose (pFile);
   }
 }
-