ref: 8d5ec6759d49ed8e87e0e5f603e95732f23435b9
parent: de68ec6f45c28f2debf0f76e9ad5f5ca588e71d2
author: Sijia Chen <[email protected]>
date: Mon Jan 26 10:25:09 EST 2015
add support of SimulcastAVC functions
--- a/codec/encoder/core/inc/param_svc.h
+++ b/codec/encoder/core/inc/param_svc.h
@@ -246,7 +246,7 @@
sSpatialLayers->iDLayerQp = SVC_QUALITY_BASE_QP;
- uiProfileIdc = PRO_SCALABLE_BASELINE;
+ uiProfileIdc = (!bSimulcastAVC) ? PRO_SCALABLE_BASELINE : PRO_BASELINE;
++ pDlp;
++ iIdxSpatial;
}
@@ -287,6 +287,7 @@
/* Rate Control */
iRCMode = pCodingParam.iRCMode; // rc mode
+ bSimulcastAVC = pCodingParam.bSimulcastAVC;
iPaddingFlag = pCodingParam.iPaddingFlag;
iTargetBitrate = pCodingParam.iTargetBitrate; // target bitrate
@@ -404,7 +405,7 @@
pSpatialLayer->iDLayerQp = pCodingParam.sSpatialLayers[iIdxSpatial].iDLayerQp;
- uiProfileIdc = PRO_SCALABLE_BASELINE;
+ uiProfileIdc = (!bSimulcastAVC) ? PRO_SCALABLE_BASELINE : PRO_BASELINE;
++ pDlp;
++ pSpatialLayer;
++ iIdxSpatial;
@@ -474,7 +475,7 @@
return ENC_RETURN_INVALIDINPUT;
}
- uiProfileIdc = iEntropyCodingModeFlag ? PRO_SCALABLE_HIGH : PRO_SCALABLE_BASELINE;
+ uiProfileIdc = iEntropyCodingModeFlag ? PRO_SCALABLE_HIGH : ((!bSimulcastAVC) ? PRO_SCALABLE_BASELINE : PRO_BASELINE);
++ pDlp;
++ pSpatialLayer;
++ i;
--- a/codec/encoder/core/src/encoder_ext.cpp
+++ b/codec/encoder/core/src/encoder_ext.cpp
@@ -342,6 +342,13 @@
pCodingParam->eSpsPpsIdStrategy = CONSTANT_ID;
}
+ if (pCodingParam->bSimulcastAVC && (SPS_LISTING & pCodingParam->eSpsPpsIdStrategy)) {
+ WelsLog (pLogCtx, WELS_LOG_INFO,
+ "ParamValidationExt(), eSpsPpsIdStrategy(%d) under bSimulcastAVC(%d) not supported yet, adjusted to INCREASING_ID",
+ pCodingParam->eSpsPpsIdStrategy, pCodingParam->bSimulcastAVC);
+ pCodingParam->eSpsPpsIdStrategy = INCREASING_ID;
+ }
+
for (i = 0; i < pCodingParam->iSpatialLayerNum; ++ i) {
SSpatialLayerConfig* pSpatialLayer = &pCodingParam->sSpatialLayers[i];
const int32_t kiPicWidth = pSpatialLayer->iVideoWidth;
@@ -1185,29 +1192,36 @@
// for dynamically malloc for parameter sets memory instead of maximal items for standard to reduce size, 3/18/2010
// SPS
- if (! (SPS_LISTING & pParam->eSpsPpsIdStrategy)) {
- (*ppCtx)->pSpsArray = (SWelsSPS*)pMa->WelsMalloc (sizeof (SWelsSPS), "pSpsArray");
- WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSpsArray), FreeMemorySvc (ppCtx))
- if (iDlayerCount > 1) {
- (*ppCtx)->pSubsetArray = (SSubsetSps*)pMa->WelsMalloc ((iDlayerCount - 1) * sizeof (SSubsetSps), "pSubsetArray");
+ if (!pParam->bSimulcastAVC) {
+ if (! (SPS_LISTING & pParam->eSpsPpsIdStrategy)) {
+ (*ppCtx)->pSpsArray = (SWelsSPS*)pMa->WelsMalloc (sizeof (SWelsSPS), "pSpsArray");
+ WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSpsArray), FreeMemorySvc (ppCtx))
+ if (iDlayerCount > 1) {
+ (*ppCtx)->pSubsetArray = (SSubsetSps*)pMa->WelsMalloc ((iDlayerCount - 1) * sizeof (SSubsetSps), "pSubsetArray");
+ WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSubsetArray), FreeMemorySvc (ppCtx))
+ }
+ } else {
+ // pParam->eSpsPpsIdStrategy == SPS_LISTING_AND_PPS_INCREASING
+ // new memory
+ (*ppCtx)->pSpsArray = (SWelsSPS*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SWelsSPS), "pSpsArray");
+ WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSpsArray), FreeMemorySvc (ppCtx))
+
+ (*ppCtx)->pSubsetArray = (SSubsetSps*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SSubsetSps), "pSubsetArray");
WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSubsetArray), FreeMemorySvc (ppCtx))
+
+ // copy from existing if the pointer exists
+ if (NULL != pExistingParasetList) {
+ (*ppCtx)->sPSOVector.uiInUseSpsNum = pExistingParasetList->uiInUseSpsNum;
+ (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum = pExistingParasetList->uiInUseSubsetSpsNum;
+ memcpy ((*ppCtx)->pSpsArray, pExistingParasetList->sSps, MAX_SPS_COUNT * sizeof (SWelsSPS));
+ memcpy ((*ppCtx)->pSubsetArray, pExistingParasetList->sSubsetSps, MAX_SPS_COUNT * sizeof (SSubsetSps));
+ }
}
} else {
- // pParam->eSpsPpsIdStrategy == SPS_LISTING_AND_PPS_INCREASING
- // new memory
- (*ppCtx)->pSpsArray = (SWelsSPS*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SWelsSPS), "pSpsArray");
+ //bSimulcastAVC
+ (*ppCtx)->pSpsArray = (SWelsSPS*)pMa->WelsMalloc (iDlayerCount * sizeof (SWelsSPS), "pSpsArray");
WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSpsArray), FreeMemorySvc (ppCtx))
-
- (*ppCtx)->pSubsetArray = (SSubsetSps*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SSubsetSps), "pSubsetArray");
- WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pSubsetArray), FreeMemorySvc (ppCtx))
-
- // copy from existing if the pointer exists
- if (NULL != pExistingParasetList) {
- (*ppCtx)->sPSOVector.uiInUseSpsNum = pExistingParasetList->uiInUseSpsNum;
- (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum = pExistingParasetList->uiInUseSubsetSpsNum;
- memcpy ((*ppCtx)->pSpsArray, pExistingParasetList->sSps, MAX_SPS_COUNT * sizeof (SWelsSPS));
- memcpy ((*ppCtx)->pSubsetArray, pExistingParasetList->sSubsetSps, MAX_SPS_COUNT * sizeof (SSubsetSps));
- }
+ (*ppCtx)->pSubsetArray = NULL;
}
// PPS
if (! (SPS_PPS_LISTING == pParam->eSpsPpsIdStrategy)) {
@@ -1237,7 +1251,7 @@
iDlayerIndex = 0;
while (iDlayerIndex < iDlayerCount) {
SDqIdc* pDqIdc = & (*ppCtx)->pDqIdcMap[iDlayerIndex];
- const bool bUseSubsetSps = (iDlayerIndex > BASE_DEPENDENCY_ID);
+ const bool bUseSubsetSps = (!pParam->bSimulcastAVC) && (iDlayerIndex > BASE_DEPENDENCY_ID);
SSpatialLayerConfig* pDlayerParam = &pParam->sSpatialLayers[iDlayerIndex];
pDqIdc->uiSpatialId = iDlayerIndex;
@@ -1337,7 +1351,7 @@
(*ppCtx)->sPSOVector.bPpsIdMappingIntoSubsetsps[iPpsId] = bUseSubsetSps;
- if (bUseSubsetSps)
+ if ((pParam->bSimulcastAVC) || (bUseSubsetSps))
++ iSpsId;
++ iPpsId;
if (bUseSubsetSps) {
@@ -2789,7 +2803,7 @@
SSlice* pBaseSlice = &pCurDq->sLayerInfo.pSliceInLayer[0];
SSlice* pSlice = NULL;
const uint8_t kiCurDid = pCtx->uiDependencyId;
- const bool kbUseSubsetSpsFlag = (kiCurDid > BASE_DEPENDENCY_ID);
+ const bool kbUseSubsetSpsFlag = (!pParam->bSimulcastAVC) && (kiCurDid > BASE_DEPENDENCY_ID);
SSpatialLayerConfig* fDlp = &pParam->sSpatialLayers[kiCurDid];
SNalUnitHeaderExt* pNalHdExt = &pCurDq->sLayerInfo.sNalHeaderExt;
SNalUnitHeader* pNalHd = &pNalHdExt->sNalHeader;
@@ -3108,6 +3122,42 @@
sParaSetOffsetVariable->uiNextParaSetIdToUseInBs = uiNextIdInBs;
}
+int32_t WelsWriteOneSPS (sWelsEncCtx* pCtx, const int32_t kiSpsIdx, int32_t& iNalSize) {
+ int iNal = pCtx->pOut->iNalIndex;
+ WelsLoadNal (pCtx->pOut, NAL_UNIT_SPS, NRI_PRI_HIGHEST);
+
+ WelsWriteSpsNal (&pCtx->pSpsArray[kiSpsIdx], &pCtx->pOut->sBsWrite,
+ & (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_AVCSPS].iParaSetIdDelta[0]));
+ WelsUnloadNal (pCtx->pOut);
+
+ int32_t iReturn = WelsEncodeNal (&pCtx->pOut->sNalList[iNal], NULL,
+ pCtx->iFrameBsSize - pCtx->iPosBsBuffer,//available buffer to be written, so need to substract the used length
+ pCtx->pFrameBs + pCtx->iPosBsBuffer,
+ &iNalSize);
+ WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+ 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;
+ /* generate picture parameter set */
+ WelsLoadNal (pCtx->pOut, NAL_UNIT_PPS, NRI_PRI_HIGHEST);
+ WelsWritePpsSyntax (&pCtx->pPPSArray[kiPpsIdx], &pCtx->pOut->sBsWrite,
+ ((SPS_PPS_LISTING != pCtx->pSvcParam->eSpsPpsIdStrategy)) ? (& (pCtx->sPSOVector)) : NULL);
+ WelsUnloadNal (pCtx->pOut);
+
+ int32_t iReturn = WelsEncodeNal (&pCtx->pOut->sNalList[iNal], NULL,
+ pCtx->iFrameBsSize - pCtx->iPosBsBuffer,
+ pCtx->pFrameBs + pCtx->iPosBsBuffer,
+ &iNalSize);
+ WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+ pCtx->iPosBsBuffer += iNalSize;
+ return ENC_RETURN_SUCCESS;
+}
+
/*!
* \brief write all parameter sets introduced in SVC extension
* \return writing results, success or error
@@ -3128,8 +3178,7 @@
/* write all SPS */
iIdx = 0;
while (iIdx < pCtx->iSpsNum) {
- iNal = pCtx->pOut->iNalIndex;
-
+ // TODO (Sijia) wrap different operation of eSpsPpsIdStrategy to classes to hide the details
if (INCREASING_ID == pCtx->pSvcParam->eSpsPpsIdStrategy) {
#if _DEBUG
pCtx->sPSOVector.eSpsPpsIdStrategy = INCREASING_ID;
@@ -3146,19 +3195,9 @@
/* generate sequence parameters set */
iId = (SPS_LISTING & pCtx->pSvcParam->eSpsPpsIdStrategy) ? iIdx : 0;
- WelsLoadNal (pCtx->pOut, NAL_UNIT_SPS, NRI_PRI_HIGHEST);
- WelsWriteSpsNal (&pCtx->pSpsArray[iId], &pCtx->pOut->sBsWrite,
- & (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_AVCSPS].iParaSetIdDelta[0]));
- WelsUnloadNal (pCtx->pOut);
+ WelsWriteOneSPS (pCtx, iId, iNalLength);
- iReturn = WelsEncodeNal (&pCtx->pOut->sNalList[iNal], NULL,
- pCtx->iFrameBsSize - pCtx->iPosBsBuffer,//available buffer to be written, so need to substract the used length
- pCtx->pFrameBs + pCtx->iPosBsBuffer,
- &iNalLength);
- WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
pNalLen[iCountNal] = iNalLength;
-
- pCtx->iPosBsBuffer += iNalLength;
iSize += iNalLength;
++ iIdx;
@@ -3235,20 +3274,9 @@
MAX_PPS_COUNT);
}
- iNal = pCtx->pOut->iNalIndex;
- /* generate picture parameter set */
- WelsLoadNal (pCtx->pOut, NAL_UNIT_PPS, NRI_PRI_HIGHEST);
- WelsWritePpsSyntax (&pCtx->pPPSArray[iIdx], &pCtx->pOut->sBsWrite,
- ((SPS_PPS_LISTING != pCtx->pSvcParam->eSpsPpsIdStrategy)) ? (& (pCtx->sPSOVector)) : NULL);
- WelsUnloadNal (pCtx->pOut);
+ WelsWriteOnePPS (pCtx, iIdx, iNalLength);
- iReturn = WelsEncodeNal (&pCtx->pOut->sNalList[iNal], NULL,
- pCtx->iFrameBsSize - pCtx->iPosBsBuffer,
- pCtx->pFrameBs + pCtx->iPosBsBuffer,
- &iNalLength);
- WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
pNalLen[iCountNal] = iNalLength;
- pCtx->iPosBsBuffer += iNalLength;
iSize += iNalLength;
++ iIdx;
@@ -3408,6 +3436,107 @@
return iSubSeqId;
}
+int32_t WriteSsvcParaset (sWelsEncCtx* pCtx, const int32_t kiSpatialNum,
+ SLayerBSInfo*& pLayerBsInfo, int32_t& iLayerNum, int32_t& iFrameSize) {
+ int32_t iNonVclSize = 0, iCountNal = 0, iReturn;
+ iReturn = WelsWriteParameterSets (pCtx, &pLayerBsInfo->pNalLengthInByte[0], &iCountNal, &iNonVclSize);
+ WELS_VERIFY_RETURN_IFNEQ (iReturn, ENC_RETURN_SUCCESS)
+
+ pLayerBsInfo->uiSpatialId = 0;
+ 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
+ ++ iLayerNum;
+ iFrameSize += iNonVclSize;
+ return iReturn;
+}
+
+int32_t WriteSavcParaset (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;
+ assert (kiSpatialNum == pCtx->iSpsNum);
+
+ 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 = 1;
+ //finish writing one NAL
+
+
+ pLayerBsInfo->uiSpatialId = iIdx;
+ 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
+
+ //TODO: under new strategy, will PPS be correctly updated?
+
+ 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 = 1;
+ //finish writing one NAL
+
+
+ pLayerBsInfo->uiSpatialId = iIdx;
+ 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 1;
+ }
+
+ iFrameSize += iNonVclSize;
+ return iReturn;
+}
+
+
/*!
* \brief core svc encoding process
*
@@ -3504,25 +3633,12 @@
if (eFrameType == videoFrameTypeIDR) {
++ pCtx->uiIdrPicId;
- //if ( pSvcParam->bEnableSSEI )
-
- // write parameter sets bitstream here
- int32_t iNonVclSize = 0;
- pCtx->iEncoderError = WelsWriteParameterSets (pCtx, &pLayerBsInfo->pNalLengthInByte[0], &iCountNal, &iNonVclSize);
+ // 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)));
WELS_VERIFY_RETURN_IFNEQ (pCtx->iEncoderError, ENC_RETURN_SUCCESS)
-
- pLayerBsInfo->uiSpatialId = 0;
- pLayerBsInfo->uiTemporalId = 0;
- pLayerBsInfo->uiQualityId = 0;
- pLayerBsInfo->uiLayerType = NON_VIDEO_CODING_LAYER;
- pLayerBsInfo->iNalCount = iCountNal;
-
- ++ pLayerBsInfo;
- pLayerBsInfo->pBsBuf = pCtx->pFrameBs + pCtx->iPosBsBuffer;
- pLayerBsInfo->pNalLengthInByte = (pLayerBsInfo - 1)->pNalLengthInByte + iCountNal;
- ++ iLayerNum;
-
- iFrameSize += iNonVclSize;
}
pCtx->pCurDqLayer = pCtx->ppDqLayerList[pSpatialIndexMap->iDid];
@@ -3577,7 +3693,7 @@
}
iNalIdxInLayer = 0;
- bAvcBased = (iCurDid == BASE_DEPENDENCY_ID);
+ bAvcBased = ((pSvcParam->bSimulcastAVC) || (iCurDid == BASE_DEPENDENCY_ID));
pCtx->bNeedPrefixNalFlag = (bAvcBased &&
(pSvcParam->bPrefixNalAddingCtrl ||
(pSvcParam->iSpatialLayerNum > 1)));
@@ -4076,6 +4192,13 @@
pCtx->bRecFlag = true;
#endif//ENABLE_FRAME_DUMP
+
+ // to check number of layers / nals / slices dependencies
+ if (iLayerNum > MAX_LAYER_NUM_OF_FRAME) {
+ WelsLog (& pCtx->sLogCtx, WELS_LOG_ERROR, "WelsEncoderEncodeExt(), iLayerNum(%d) > MAX_LAYER_NUM_OF_FRAME(%d)!",
+ iLayerNum, MAX_LAYER_NUM_OF_FRAME);
+ return 1;
+ }
++ pCtx->iCodingIndex;
pCtx->eLastNalPriority = eNalRefIdc;
--- a/test/api/encode_decode_api_test.cpp
+++ b/test/api/encode_decode_api_test.cpp
@@ -3020,3 +3020,225 @@
#endif
}
+
+TEST_F (EncodeDecodeTestAPI, SimulcastSVC) {
+#define LAYER_NUM (4)
+ int iSpatialLayerNum = WelsClip3 ((rand() % LAYER_NUM), 2, 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;
+ encoder_->GetDefaultParams (¶m_);
+ prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, ¶m_);
+
+ int rv = encoder_->InitializeExt (¶m_);
+ ASSERT_TRUE (rv == cmResultSuccess);
+
+ unsigned char* pBsBuf[LAYER_NUM];
+ int aLen[LAYER_NUM] = {0};
+ ISVCDecoder* decoder[LAYER_NUM];
+
+#ifdef DEBUG_FILE_SAVE2
+ FILE* fEnc[LAYER_NUM] = { NULL };
+ fEnc[0] = fopen ("enc0.264", "wb");
+ fEnc[1] = fopen ("enc1.264", "wb");
+ fEnc[2] = fopen ("enc2.264", "wb");
+ fEnc[3] = fopen ("enc3.264", "wb");
+#endif
+
+ // init decoders
+ int iIdx = 0;
+ for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+ pBsBuf[iIdx] = static_cast<unsigned char*> (malloc (iWidth * iHeight * 3 * sizeof (unsigned char) / 2));
+ aLen[iIdx] = 0;
+
+ long rv = WelsCreateDecoder (&decoder[iIdx]);
+ ASSERT_EQ (0, rv);
+ ASSERT_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);
+ }
+
+ for (int iFrame = 0; iFrame < iEncFrameNum; iFrame++) {
+ int iResult;
+ int iLayerLen = 0;
+ unsigned char* pData[3] = { NULL };
+
+ InitialEncDec (param_.iPicWidth, param_.iPicHeight);
+ EncodeOneFrame (0);
+
+ iLayerLen = 0;
+ 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];
+ }
+
+ if (layerInfo.uiLayerType == NON_VIDEO_CODING_LAYER) {
+ // under SimulcastSVC, need to copy non-VCL to all layers
+ for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+ memcpy ((pBsBuf[iIdx] + aLen[iIdx]), layerInfo.pBsBuf, iLayerLen * sizeof (unsigned char));
+ aLen[iIdx] += iLayerLen;
+ }
+ } else {
+ iIdx = layerInfo.uiSpatialId;
+ EXPECT_TRUE (iIdx < 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_SAVE2
+ 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;
+ }
+ }
+
+ // free all
+ for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+ if (pBsBuf[iIdx]) {
+ free (pBsBuf[iIdx]);
+ }
+
+ if (decoder[iIdx] != NULL) {
+ decoder[iIdx]->Uninitialize();
+ WelsDestroyDecoder (decoder[iIdx]);
+ }
+
+#ifdef DEBUG_FILE_SAVE2
+ fclose (fEnc[iIdx]);
+#endif
+ }
+
+}
+
+TEST_F (EncodeDecodeTestAPI, SimulcastAVC) {
+//#define DEBUG_FILE_SAVE3
+#define LAYER_NUM (4)
+ int iSpatialLayerNum = WelsClip3 ((rand() % LAYER_NUM), 2, 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;
+ encoder_->GetDefaultParams (¶m_);
+ prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, ¶m_);
+
+ //set flag of bSimulcastAVC
+ param_.bSimulcastAVC = true;
+
+ int rv = encoder_->InitializeExt (¶m_);
+ ASSERT_TRUE (rv == cmResultSuccess);
+
+ unsigned char* pBsBuf[LAYER_NUM];
+ int aLen[LAYER_NUM] = {0};
+ ISVCDecoder* decoder[LAYER_NUM];
+
+#ifdef DEBUG_FILE_SAVE3
+ FILE* fEnc[LAYER_NUM];
+ fEnc[0] = fopen ("enc0.264", "wb");
+ fEnc[1] = fopen ("enc1.264", "wb");
+ fEnc[2] = fopen ("enc2.264", "wb");
+ fEnc[3] = fopen ("enc3.264", "wb");
+#endif
+
+ 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));
+ aLen[iIdx] = 0;
+
+ long rv = WelsCreateDecoder (&decoder[iIdx]);
+ ASSERT_EQ (0, rv);
+ ASSERT_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);
+ }
+
+ iEncFrameNum = 1;
+ for (int iFrame = 0; iFrame < iEncFrameNum; iFrame++) {
+ int iResult;
+ int iLayerLen = 0;
+ unsigned char* pData[3] = { NULL };
+
+ InitialEncDec (param_.iPicWidth, param_.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);
+ 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_SAVE3
+ 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;
+ }
+
+ }
+
+ for (iIdx = 0; iIdx < iSpatialLayerNum; iIdx++) {
+ free (pBsBuf[iIdx]);
+
+ if (decoder[iIdx] != NULL) {
+ decoder[iIdx]->Uninitialize();
+ WelsDestroyDecoder (decoder[iIdx]);
+ }
+#ifdef DEBUG_FILE_SAVE3
+ fclose (fEnc[iIdx]);
+#endif
+ }
+}
+