add support of SimulcastAVC functions

This commit is contained in:
Sijia Chen 2015-01-26 15:25:09 +08:00
parent de68ec6f45
commit 8d5ec6759d
3 changed files with 415 additions and 69 deletions

View File

@ -246,7 +246,7 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
sSpatialLayers->iDLayerQp = SVC_QUALITY_BASE_QP;
uiProfileIdc = PRO_SCALABLE_BASELINE;
uiProfileIdc = (!bSimulcastAVC) ? PRO_SCALABLE_BASELINE : PRO_BASELINE;
++ pDlp;
++ iIdxSpatial;
}
@ -287,6 +287,7 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
/* Rate Control */
iRCMode = pCodingParam.iRCMode; // rc mode
bSimulcastAVC = pCodingParam.bSimulcastAVC;
iPaddingFlag = pCodingParam.iPaddingFlag;
iTargetBitrate = pCodingParam.iTargetBitrate; // target bitrate
@ -404,7 +405,7 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
pSpatialLayer->iDLayerQp = pCodingParam.sSpatialLayers[iIdxSpatial].iDLayerQp;
uiProfileIdc = PRO_SCALABLE_BASELINE;
uiProfileIdc = (!bSimulcastAVC) ? PRO_SCALABLE_BASELINE : PRO_BASELINE;
++ pDlp;
++ pSpatialLayer;
++ iIdxSpatial;
@ -474,7 +475,7 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt {
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;

View File

@ -342,6 +342,13 @@ int32_t ParamValidationExt (SLogContext* pLogCtx, SWelsSvcCodingParam* pCodingPa
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 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx, SExistingParasetList* p
// 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 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx, SExistingParasetList* p
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 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx, SExistingParasetList* p
(*ppCtx)->sPSOVector.bPpsIdMappingIntoSubsetsps[iPpsId] = bUseSubsetSps;
if (bUseSubsetSps)
if ((pParam->bSimulcastAVC) || (bUseSubsetSps))
++ iSpsId;
++ iPpsId;
if (bUseSubsetSps) {
@ -2789,7 +2803,7 @@ void WelsInitCurrentLayer (sWelsEncCtx* pCtx,
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 @@ void ParasetIdAdditionIdAdjust (SParaSetOffsetVariable* sParaSetOffsetVariable,
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 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN
/* 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 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN
/* 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 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN
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 @@ int32_t GetSubSequenceId (sWelsEncCtx* pCtx, EVideoFrameType eFrameType) {
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 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
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 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
}
iNalIdxInLayer = 0;
bAvcBased = (iCurDid == BASE_DEPENDENCY_ID);
bAvcBased = ((pSvcParam->bSimulcastAVC) || (iCurDid == BASE_DEPENDENCY_ID));
pCtx->bNeedPrefixNalFlag = (bAvcBased &&
(pSvcParam->bPrefixNalAddingCtrl ||
(pSvcParam->iSpatialLayerNum > 1)));
@ -4077,6 +4193,13 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour
#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;
pFbi->iLayerNum = iLayerNum;

View File

@ -3020,3 +3020,225 @@ TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_PPS_LISTING3) {
#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 (&param_);
prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &param_);
int rv = encoder_->InitializeExt (&param_);
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 (&param_);
prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &param_);
//set flag of bSimulcastAVC
param_.bSimulcastAVC = true;
int rv = encoder_->InitializeExt (&param_);
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
}
}