diff --git a/codec/api/svc/codec_app_def.h b/codec/api/svc/codec_app_def.h index 0169696d..9d40febf 100644 --- a/codec/api/svc/codec_app_def.h +++ b/codec/api/svc/codec_app_def.h @@ -385,6 +385,17 @@ typedef enum { HIGH_COMPLEXITY ///< high complexity, lowest speed, high quality } ECOMPLEXITY_MODE; +/** + * @brief Enumulate for the stategy of SPS/PPS strategy + */ +typedef enum { + CONSTANT_ID = 0, ///< constant id in SPS/PPS + INCREASING_ID = 0x01, ///< SPS/PPS id increases at each IDR + SPS_LISTING = 0x02, ///< using SPS in the existing list if possible + SPS_LISTING_AND_PPS_INCREASING = 0x03, + SPS_PPS_LISTING = 0x06, +} EParameterSetStrategy; + // TODO: Refine the parameters definition. /** * @brief SVC Encoding Parameters @@ -421,7 +432,7 @@ typedef struct TagEncParamExt { ECOMPLEXITY_MODE iComplexityMode; unsigned int uiIntraPeriod; ///< period of Intra frame int iNumRefFrame; ///< number of reference frame used - bool bEnableSpsPpsIdAddition; ///< false:not adjust ID in SPS/PPS; true: adjust ID in SPS/PPS + int iSpsPpsIdStrategy; ///< different stategy in adjust ID in SPS/PPS: 0- constant ID, 1-additional ID, 6-mapping and additional bool bPrefixNalAddingCtrl; ///< false:not use Prefix NAL; true: use Prefix NAL bool bEnableSSEI; ///< false:not use SSEI; true: use SSEI -- TODO: planning to remove the interface of SSEI bool bSimulcastAVC; ///< (when encoding more than 1 spatial layer) false: use SVC syntax for higher layers; true: use Simulcast AVC -- coming soon diff --git a/codec/console/enc/src/welsenc.cpp b/codec/console/enc/src/welsenc.cpp index ab5e5c5d..5f20fd20 100644 --- a/codec/console/enc/src/welsenc.cpp +++ b/codec/console/enc/src/welsenc.cpp @@ -232,8 +232,8 @@ int ParseConfig (CReadConfig& cRdCfg, SSourcePicture* pSrcPic, SEncParamExt& pSv pSvcParam.uiIntraPeriod = atoi (strTag[1].c_str()); } else if (strTag[0].compare ("MaxNalSize") == 0) { pSvcParam.uiMaxNalSize = atoi (strTag[1].c_str()); - } else if (strTag[0].compare ("EnableSpsPpsIDAddition") == 0) { - pSvcParam.bEnableSpsPpsIdAddition = atoi (strTag[1].c_str()) ? true : false; + } else if (strTag[0].compare ("SpsPpsIDStrategy") == 0) { + pSvcParam.iSpsPpsIdStrategy = atoi (strTag[1].c_str()); } else if (strTag[0].compare ("EnableScalableSEI") == 0) { pSvcParam.bEnableSSEI = atoi (strTag[1].c_str()) ? true : false; } else if (strTag[0].compare ("EnableFrameCropping") == 0) { @@ -421,7 +421,7 @@ int ParseCommandLine (int argc, char** argv, SSourcePicture* pSrcPic, SEncParamE pSvcParam.uiMaxNalSize = atoi (argv[n++]); else if (!strcmp (pCommand, "-spsid") && (n < argc)) - pSvcParam.bEnableSpsPpsIdAddition = atoi (argv[n++]) ? true : false; + pSvcParam.iSpsPpsIdStrategy = atoi (argv[n++]); else if (!strcmp (pCommand, "-cabac") && (n < argc)) pSvcParam.iEntropyCodingModeFlag = atoi (argv[n++]); @@ -591,7 +591,7 @@ int FillSpecificParameters (SEncParamExt& sParam) { sParam.bEnableLongTermReference = 0; // long term reference control sParam.iLtrMarkPeriod = 30; sParam.uiIntraPeriod = 320; // period of Intra frame - sParam.bEnableSpsPpsIdAddition = 1; + sParam.iSpsPpsIdStrategy = INCREASING_ID; sParam.bPrefixNalAddingCtrl = 0; sParam.iComplexityMode = MEDIUM_COMPLEXITY; int iIndexLayer = 0; diff --git a/codec/encoder/core/inc/au_set.h b/codec/encoder/core/inc/au_set.h index f7e457d5..6ee6843c 100644 --- a/codec/encoder/core/inc/au_set.h +++ b/codec/encoder/core/inc/au_set.h @@ -141,8 +141,26 @@ int32_t WelsInitPps (SWelsPPS* pPps, const bool kbDeblockingFilterPresentFlag, const bool kbUsingSubsetSps, const bool kbEntropyCodingModeFlag); + int32_t WelsCheckRefFrameLimitationNumRefFirst (SLogContext* pLogCtx, SWelsSvcCodingParam* pParam); int32_t WelsCheckRefFrameLimitationLevelIdcFirst (SLogContext* pLogCtx, SWelsSvcCodingParam* pParam); -int32_t WelsAdjustLevel( SSpatialLayerConfig* pSpatialLayer); + +int32_t WelsAdjustLevel (SSpatialLayerConfig* pSpatialLayer); + +/*! + * \brief check if the current parameter can found a presenting sps + * \param pParam the current encoding paramter in SWelsSvcCodingParam + * \param kbUseSubsetSps bool + * \param iDlayerIndex int, the index of current D layer + * \param iDlayerCount int, the number of total D layer + * \param pSpsArray array of all the stored SPSs + * \param pSubsetArray array of all the stored Subset-SPSs + * \return 0 - successful + * -1 - cannot find existing SPS for current encoder parameter + */ +int32_t FindExistingSps (SWelsSvcCodingParam* pParam, const bool kbUseSubsetSps, const int32_t iDlayerIndex, + const int32_t iDlayerCount, const int32_t iSpsNumInUse, + SWelsSPS* pSpsArray, + SSubsetSps* pSubsetArray); } #endif//WELS_ACCESS_UNIT_PARSER_H__ diff --git a/codec/encoder/core/inc/encoder.h b/codec/encoder/core/inc/encoder.h index 2179e996..b8cf5807 100644 --- a/codec/encoder/core/inc/encoder.h +++ b/codec/encoder/core/inc/encoder.h @@ -48,7 +48,7 @@ namespace WelsEnc { * \param pEncCtx sWelsEncCtx* * \return successful - 0; otherwise none 0 for failed */ -int32_t RequestMemorySvc (sWelsEncCtx** ppCtx); +int32_t RequestMemorySvc (sWelsEncCtx** ppCtx, SExistingParasetList* pExistingParasetList); /*! * \brief free memory in SVC core encoder diff --git a/codec/encoder/core/inc/encoder_context.h b/codec/encoder/core/inc/encoder_context.h index 11da441a..c1f6f7b0 100644 --- a/codec/encoder/core/inc/encoder_context.h +++ b/codec/encoder/core/inc/encoder_context.h @@ -191,6 +191,7 @@ typedef struct TagWelsEncCtx { SSubsetSps* pSubsetArray; // MAX_SPS_COUNT by standard compatible SSubsetSps* pSubsetSps; int32_t iSpsNum; // number of pSps used + int32_t iSubsetSpsNum; // number of pSps used int32_t iPpsNum; // number of pPps used // Output @@ -216,6 +217,7 @@ typedef struct TagWelsEncCtx { pDqIdcMap; // overall DQ map of full scalability in specific frame (All full D/T/Q layers involved) // pDqIdcMap[dq_index] for each SDqIdc pData SParaSetOffset sPSOVector; + SParaSetOffset* pPSOVector; CMemoryAlign* pMemAlign; #if defined(STAT_OUTPUT) diff --git a/codec/encoder/core/inc/extern.h b/codec/encoder/core/inc/extern.h index e6c31d0c..bab8e818 100644 --- a/codec/encoder/core/inc/extern.h +++ b/codec/encoder/core/inc/extern.h @@ -75,7 +75,8 @@ void GomValidCheck (const int32_t kiMbWidth, const int32_t kiMbHeight, int32_t* * \param para SWelsSvcCodingParam* * \return successful - 0; otherwise none 0 for failed */ -int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pPara, SLogContext* pLogCtx); +int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pPara, SLogContext* pLogCtx, + SExistingParasetList* pExistingParasetList); /*! * \brief uninitialize Wels encoder core library diff --git a/codec/encoder/core/inc/param_svc.h b/codec/encoder/core/inc/param_svc.h index 999d34ec..c163120e 100644 --- a/codec/encoder/core/inc/param_svc.h +++ b/codec/encoder/core/inc/param_svc.h @@ -162,7 +162,7 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt { param.bEnableAdaptiveQuant = true; // adaptive quantization control param.bEnableFrameSkip = true; // frame skipping param.bEnableLongTermReference = false; // long term reference control - param.bEnableSpsPpsIdAddition = true; // pSps pPps id addition control + param.iSpsPpsIdStrategy = INCREASING_ID; // pSps pPps id addition control param.bPrefixNalAddingCtrl = false; // prefix NAL adding control param.iSpatialLayerNum = 1; // number of dependency(Spatial/CGS) layers used to be encoded param.iTemporalLayerNum = 1; // number of temporal layer specified @@ -350,8 +350,8 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt { bPrefixNalAddingCtrl = pCodingParam.bPrefixNalAddingCtrl; - bEnableSpsPpsIdAddition = - pCodingParam.bEnableSpsPpsIdAddition;//For SVC meeting application, to avoid mosaic issue caused by cross-IDR reference. + iSpsPpsIdStrategy = + pCodingParam.iSpsPpsIdStrategy;//For SVC meeting application, to avoid mosaic issue caused by cross-IDR reference. //SHOULD enable this feature. SSpatialLayerInternal* pDlp = &sDependencyLayers[0]; @@ -477,6 +477,18 @@ typedef struct TagWelsSvcCodingParam: SEncParamExt { } SWelsSvcCodingParam; + +typedef struct TagExistingParasetList { + SWelsSPS sSps[MAX_SPS_COUNT]; + SSubsetSps sSubsetSps[MAX_SPS_COUNT]; + SWelsPPS sPps[MAX_PPS_COUNT]; + + uint32_t uiInUseSpsNum; + uint32_t uiInUseSubsetSpsNum; + uint32_t uiInUsePpsNum; +} SExistingParasetList; + + static inline int32_t FreeCodingParam (SWelsSvcCodingParam** pParam, CMemoryAlign* pMa) { if (pParam == NULL || *pParam == NULL || pMa == NULL) return 1; diff --git a/codec/encoder/core/inc/wels_common_basis.h b/codec/encoder/core/inc/wels_common_basis.h index a56847dd..523d3542 100644 --- a/codec/encoder/core/inc/wels_common_basis.h +++ b/codec/encoder/core/inc/wels_common_basis.h @@ -83,9 +83,15 @@ typedef struct TagParaSetOffset { bool bPpsIdMappingIntoSubsetsps[MAX_DQ_LAYER_NUM/*+1*/]; // need not extra +1 due no MGS and FMO case so far + int32_t iPpsIdList[MAX_DQ_LAYER_NUM][MAX_PPS_COUNT]; //index0: max pps types; index1: for differnt IDRs, if only index0=1, index1 can reach MAX_PPS_COUNT + #if _DEBUG - bool bEnableSpsPpsIdAddition; + int32_t iSpsPpsIdStrategy; #endif + + uint32_t uiInUseSpsNum; + uint32_t uiInUseSubsetSpsNum; + uint32_t uiInUsePpsNum; } SParaSetOffset; diff --git a/codec/encoder/core/inc/wels_const.h b/codec/encoder/core/inc/wels_const.h index e481b9ed..ccf6b06b 100644 --- a/codec/encoder/core/inc/wels_const.h +++ b/codec/encoder/core/inc/wels_const.h @@ -171,6 +171,7 @@ #define UNAVAILABLE_DQ_ID ((uint8_t)(-1)) #define LAYER_NUM_EXCHANGEABLE 2 +#define INVALID_ID (-1) #define NAL_HEADER_ADD_0X30BYTES 50 diff --git a/codec/encoder/core/src/au_set.cpp b/codec/encoder/core/src/au_set.cpp index 96111c45..af24bcc0 100644 --- a/codec/encoder/core/src/au_set.cpp +++ b/codec/encoder/core/src/au_set.cpp @@ -336,27 +336,27 @@ int32_t WelsWriteSubsetSpsSyntax (SSubsetSps* pSubsetSps, SBitStringAux* pBitStr * \note Call it in case EWelsNalUnitType is PPS. ************************************************************************************* */ -int32_t WelsWritePpsSyntax (SWelsPPS* pPps, SBitStringAux* pBitStringAux, SParaSetOffset* sPSOVector) { +int32_t WelsWritePpsSyntax (SWelsPPS* pPps, SBitStringAux* pBitStringAux, SParaSetOffset* pPSOVector) { SBitStringAux* pLocalBitStringAux = pBitStringAux; - bool bUsedSubset = sPSOVector->bPpsIdMappingIntoSubsetsps[pPps->iPpsId]; - int32_t iParameterSetType = (bUsedSubset ? PARA_SET_TYPE_SUBSETSPS : PARA_SET_TYPE_AVCSPS); + const int32_t kiParameterSetType = (pPSOVector != NULL) ? (pPSOVector->bPpsIdMappingIntoSubsetsps[pPps->iPpsId] ? + PARA_SET_TYPE_SUBSETSPS : PARA_SET_TYPE_AVCSPS) : 0; - BsWriteUE (pLocalBitStringAux, pPps->iPpsId + - sPSOVector->sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[pPps->iPpsId]); - BsWriteUE (pLocalBitStringAux, pPps->iSpsId + - sPSOVector->sParaSetOffsetVariable[iParameterSetType].iParaSetIdDelta[pPps->iSpsId]); + BsWriteUE (pLocalBitStringAux, pPps->iPpsId + + ((pPSOVector != NULL) ? (pPSOVector->sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[pPps->iPpsId]) : 0)); + BsWriteUE (pLocalBitStringAux, pPps->iSpsId + + ((pPSOVector != NULL) ? (pPSOVector->sParaSetOffsetVariable[kiParameterSetType].iParaSetIdDelta[pPps->iSpsId]) : 0)); #if _DEBUG //SParaSetOffset use, 110421 - if (sPSOVector->bEnableSpsPpsIdAddition) { + if ((pPSOVector != NULL) && (INCREASING_ID & pPSOVector->iSpsPpsIdStrategy)) { const int32_t kiTmpSpsIdInBs = pPps->iSpsId + - sPSOVector->sParaSetOffsetVariable[iParameterSetType].iParaSetIdDelta[pPps->iSpsId]; + pPSOVector->sParaSetOffsetVariable[kiParameterSetType].iParaSetIdDelta[pPps->iSpsId]; const int32_t tmp_pps_id_in_bs = pPps->iPpsId + - sPSOVector->sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[pPps->iPpsId]; + pPSOVector->sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[pPps->iPpsId]; assert (MAX_SPS_COUNT > kiTmpSpsIdInBs); assert (MAX_PPS_COUNT > tmp_pps_id_in_bs); - assert (sPSOVector->sParaSetOffsetVariable[iParameterSetType].bUsedParaSetIdInBs[kiTmpSpsIdInBs]); + assert (pPSOVector->sParaSetOffsetVariable[kiParameterSetType].bUsedParaSetIdInBs[kiTmpSpsIdInBs]); } #endif diff --git a/codec/encoder/core/src/encoder_ext.cpp b/codec/encoder/core/src/encoder_ext.cpp index 83363725..31719d7a 100644 --- a/codec/encoder/core/src/encoder_ext.cpp +++ b/codec/encoder/core/src/encoder_ext.cpp @@ -323,6 +323,12 @@ int32_t ParamValidationExt (SLogContext* pLogCtx, SWelsSvcCodingParam* pCodingPa pCodingParam->bDeblockingParallelFlag = true; } + if (pCodingParam->iSpatialLayerNum > 1 && (SPS_LISTING & pCodingParam->iSpsPpsIdStrategy)) { + WelsLog (pLogCtx, WELS_LOG_INFO, + "ParamValidationExt(), iSpsPpsIdStrategy adjusted to CONSTANT_ID"); + pCodingParam->iSpsPpsIdStrategy = CONSTANT_ID; + } + for (i = 0; i < pCodingParam->iSpatialLayerNum; ++ i) { SSpatialLayerConfig* pSpatialLayer = &pCodingParam->sSpatialLayers[i]; const int32_t kiPicWidth = pSpatialLayer->iVideoWidth; @@ -665,8 +671,12 @@ static inline int32_t AcquireLayersNals (sWelsEncCtx** ppCtx, SWelsSvcCodingPara ++ iDIndex; } while (iDIndex < iNumDependencyLayers); + // count parasets iCountNumNals += 1 + iNumDependencyLayers + (iCountNumLayers << 1) + - iCountNumLayers; // plus iCountNumLayers for reserved application + iCountNumLayers // plus iCountNumLayers for reserved application + + ((SPS_LISTING & pParam->iSpsPpsIdStrategy) ? MAX_SPS_COUNT : 0) //for Sps + + (((SPS_LISTING & pParam->iSpsPpsIdStrategy) && (iNumDependencyLayers > 1)) ? MAX_SPS_COUNT : 0) //for SubsetSps + + ((SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy) ? MAX_PPS_COUNT : 0); // to check number of layers / nals / slices dependencies, 12/8/2010 if (iCountNumLayers > MAX_LAYER_NUM_OF_FRAME) { @@ -847,9 +857,9 @@ void FreeMbCache (SMbCache* pMbCache, CMemoryAlign* pMa) { } } -int32_t WelsGenerateNewSps (sWelsEncCtx* pCtx, const bool kbUseSubsetSps, const int32_t iDlayerIndex, - const int32_t iDlayerCount, const int32_t kiSpsId, - SWelsSPS*& pSps, SSubsetSps*& pSubsetSps) { +static int32_t WelsGenerateNewSps (sWelsEncCtx* pCtx, const bool kbUseSubsetSps, const int32_t iDlayerIndex, + const int32_t iDlayerCount, const int32_t kiSpsId, + SWelsSPS*& pSps, SSubsetSps*& pSubsetSps) { int32_t iRet = 0; if (!kbUseSubsetSps) { @@ -866,9 +876,6 @@ int32_t WelsGenerateNewSps (sWelsEncCtx* pCtx, const bool kbUseSubsetSps, const iRet = WelsInitSps (pSps, pDlayerParam, &pParam->sDependencyLayers[iDlayerIndex], pParam->uiIntraPeriod, pParam->iMaxNumRefFrame, kiSpsId, pParam->bEnableFrameCroppingFlag, pParam->iRCMode != RC_OFF_MODE, iDlayerCount); - - - } else { iRet = WelsInitSubsetSps (pSubsetSps, pDlayerParam, &pParam->sDependencyLayers[iDlayerIndex], pParam->uiIntraPeriod, pParam->iMaxNumRefFrame, @@ -877,12 +884,131 @@ int32_t WelsGenerateNewSps (sWelsEncCtx* pCtx, const bool kbUseSubsetSps, const return iRet; } +static bool CheckMatchedSps (SWelsSPS* const pSps1, SWelsSPS* const pSps2) { + + if ((pSps1->iMbWidth != pSps2->iMbWidth) + || (pSps1->iMbHeight != pSps2->iMbHeight)) { + return false; + } + + if ((pSps1->uiLog2MaxFrameNum != pSps2->uiLog2MaxFrameNum) + || (pSps1->iLog2MaxPocLsb != pSps2->iLog2MaxPocLsb)) { + return false; + } + + if (pSps1->iNumRefFrames != pSps2->iNumRefFrames) { + return false; + } + + if ((pSps1->bFrameCroppingFlag != pSps2->bFrameCroppingFlag) + || (pSps1->sFrameCrop.iCropLeft != pSps2->sFrameCrop.iCropLeft) + || (pSps1->sFrameCrop.iCropRight != pSps2->sFrameCrop.iCropRight) + || (pSps1->sFrameCrop.iCropTop != pSps2->sFrameCrop.iCropTop) + || (pSps1->sFrameCrop.iCropBottom != pSps2->sFrameCrop.iCropBottom) + ) { + return false; + } + + if ((pSps1->uiProfileIdc != pSps2->uiProfileIdc) + || (pSps1->bConstraintSet0Flag != pSps2->bConstraintSet0Flag) + || (pSps1->bConstraintSet1Flag != pSps2->bConstraintSet1Flag) + || (pSps1->bConstraintSet2Flag != pSps2->bConstraintSet2Flag) + || (pSps1->bConstraintSet3Flag != pSps2->bConstraintSet3Flag) + || (pSps1->iLevelIdc != pSps2->iLevelIdc)) { + return false; + } + + return true; +} + +static bool CheckMatchedSubsetSps (SSubsetSps* const pSubsetSps1, SSubsetSps* const pSubsetSps2) { + if (!CheckMatchedSps (&pSubsetSps1->pSps, &pSubsetSps2->pSps)) { + return false; + } + + if ((pSubsetSps1->sSpsSvcExt.iExtendedSpatialScalability != pSubsetSps2->sSpsSvcExt.iExtendedSpatialScalability) + || (pSubsetSps1->sSpsSvcExt.bAdaptiveTcoeffLevelPredFlag != pSubsetSps2->sSpsSvcExt.bAdaptiveTcoeffLevelPredFlag) + || (pSubsetSps1->sSpsSvcExt.bSeqTcoeffLevelPredFlag != pSubsetSps2->sSpsSvcExt.bSeqTcoeffLevelPredFlag) + || (pSubsetSps1->sSpsSvcExt.bSliceHeaderRestrictionFlag != pSubsetSps2->sSpsSvcExt.bSliceHeaderRestrictionFlag)) { + return false; + } + + return true; +} + +int32_t FindExistingSps (SWelsSvcCodingParam* pParam, const bool kbUseSubsetSps, const int32_t iDlayerIndex, + const int32_t iDlayerCount, const int32_t iSpsNumInUse, + SWelsSPS* pSpsArray, + SSubsetSps* pSubsetArray) { + SSpatialLayerConfig* pDlayerParam = &pParam->sSpatialLayers[iDlayerIndex]; + + assert (iSpsNumInUse <= MAX_SPS_COUNT); + if (!kbUseSubsetSps) { + SWelsSPS sTmpSps; + WelsInitSps (&sTmpSps, pDlayerParam, &pParam->sDependencyLayers[iDlayerIndex], pParam->uiIntraPeriod, + pParam->iMaxNumRefFrame, + 0, pParam->bEnableFrameCroppingFlag, pParam->iRCMode != RC_OFF_MODE, iDlayerCount); + for (int32_t iId = 0; iId < iSpsNumInUse; iId++) { + if (CheckMatchedSps (&sTmpSps, &pSpsArray[iId])) { + return iId; + } + } + } else { + SSubsetSps sTmpSubsetSps; + WelsInitSubsetSps (&sTmpSubsetSps, pDlayerParam, &pParam->sDependencyLayers[iDlayerIndex], pParam->uiIntraPeriod, + pParam->iMaxNumRefFrame, + 0, pParam->bEnableFrameCroppingFlag, pParam->iRCMode != RC_OFF_MODE); + + for (int32_t iId = 0; iId < iSpsNumInUse; iId++) { + if (CheckMatchedSubsetSps (&sTmpSubsetSps, &pSubsetArray[iId])) { + return iId; + } + } + } + + return INVALID_ID; +} + +int32_t FindExistingPps (SWelsSPS* pSps, SSubsetSps* pSubsetSps, const bool kbUseSubsetSps, const int32_t iSpsId, + const bool kbEntropyCodingFlag, const int32_t iPpsNumInUse, + SWelsPPS* pPpsArray) { +#if !defined(DISABLE_FMO_FEATURE) + // feature not supported yet + return INVALID_ID; +#endif//!DISABLE_FMO_FEATURE + + SWelsPPS sTmpPps; + WelsInitPps (&sTmpPps, + pSps, + pSubsetSps, + 0, + true, + kbUseSubsetSps, + kbEntropyCodingFlag); + + assert (iPpsNumInUse <= MAX_PPS_COUNT); + for (int32_t iId = 0; iId < iPpsNumInUse; iId++) { + if ((sTmpPps.iSpsId == pPpsArray[iId].iSpsId) + && (sTmpPps.bEntropyCodingModeFlag == pPpsArray[iId].bEntropyCodingModeFlag) + && (sTmpPps.iPicInitQp == pPpsArray[iId].iPicInitQp) + && (sTmpPps.iPicInitQs == pPpsArray[iId].iPicInitQs) + && (sTmpPps.uiChromaQpIndexOffset == pPpsArray[iId].uiChromaQpIndexOffset) + && (sTmpPps.bDeblockingFilterControlPresentFlag == pPpsArray[iId].bDeblockingFilterControlPresentFlag) + ) { + return iId; + } + } + + return INVALID_ID; +} + + /*! * \brief initialize ppDqLayerList and slicepEncCtx_list due to count number of layers available * \pParam pCtx sWelsEncCtx* * \return 0 - successful; otherwise failed */ -static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx) { +static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx, SExistingParasetList* pExistingParasetList) { SWelsSvcCodingParam* pParam = NULL; SWelsSPS* pSps = NULL; SSubsetSps* pSubsetSps = NULL; @@ -1045,14 +1171,51 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx) { } // for dynamically malloc for parameter sets memory instead of maximal items for standard to reduce size, 3/18/2010 - (*ppCtx)->pPPSArray = (SWelsPPS*)pMa->WelsMalloc (iDlayerCount * sizeof (SWelsPPS), "pPPSArray"); - WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pPPSArray), FreeMemorySvc (ppCtx)) + // SPS + if (! (SPS_LISTING & pParam->iSpsPpsIdStrategy)) { + (*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->iSpsPpsIdStrategy == 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)->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"); + (*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)); + } + } + // PPS + if (! (SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy)) { + (*ppCtx)->pPPSArray = (SWelsPPS*)pMa->WelsMalloc (iDlayerCount * sizeof (SWelsPPS), "pPPSArray"); + WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pPPSArray), FreeMemorySvc (ppCtx)) + } else { + (*ppCtx)->pPPSArray = (SWelsPPS*)pMa->WelsMalloc (MAX_PPS_COUNT * sizeof (SWelsPPS), "pPPSArray"); + WELS_VERIFY_RETURN_PROC_IF (1, (NULL == (*ppCtx)->pPPSArray), FreeMemorySvc (ppCtx)) + + + // copy from existing if the pointer exists + if (NULL != pExistingParasetList) { + (*ppCtx)->sPSOVector.uiInUsePpsNum = pExistingParasetList->uiInUsePpsNum; + memcpy ((*ppCtx)->pPPSArray, pExistingParasetList->sPps, MAX_PPS_COUNT * sizeof (SWelsPPS)); + } + } + + if (INCREASING_ID & pParam->iSpsPpsIdStrategy) { + (*ppCtx)->pPSOVector = & ((*ppCtx)->sPSOVector); + } else { + (*ppCtx)->pPSOVector = NULL; } (*ppCtx)->pDqIdcMap = (SDqIdc*)pMa->WelsMallocz (iDlayerCount * sizeof (SDqIdc), "pDqIdcMap"); @@ -1066,12 +1229,79 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx) { pDqIdc->uiSpatialId = iDlayerIndex; - WelsGenerateNewSps (*ppCtx, bUseSubsetSps, iDlayerIndex, - iDlayerCount, iSpsId, pSps, pSubsetSps); + if (! (SPS_LISTING & pParam->iSpsPpsIdStrategy)) { + WelsGenerateNewSps (*ppCtx, bUseSubsetSps, iDlayerIndex, + iDlayerCount, iSpsId, pSps, pSubsetSps); + } else { + //SPS_LISTING_AND_PPS_INCREASING == pParam->iSpsPpsIdStrategy + //check if the current param can fit in an existing SPS + const int32_t kiFoundSpsId = FindExistingSps ((*ppCtx)->pSvcParam, bUseSubsetSps, iDlayerIndex, iDlayerCount, + bUseSubsetSps ? ((*ppCtx)->sPSOVector.uiInUseSubsetSpsNum) : ((*ppCtx)->sPSOVector.uiInUseSpsNum), + (*ppCtx)->pSpsArray, + (*ppCtx)->pSubsetArray); - pPps = & (*ppCtx)->pPPSArray[iPpsId]; - // initialize pPps - WelsInitPps (pPps, pSps, pSubsetSps, iPpsId, true, bUseSubsetSps, pParam->iEntropyCodingModeFlag != 0); + + if (INVALID_ID != kiFoundSpsId) { + //if yes, set pSps or pSubsetSps to it + iSpsId = kiFoundSpsId; + if (!bUseSubsetSps) { + pSps = & ((*ppCtx)->pSpsArray[kiFoundSpsId]); + } else { + pSubsetSps = & ((*ppCtx)->pSubsetArray[kiFoundSpsId]); + } + } else { + //if no, generate a new SPS as usual + if ((SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy) && (MAX_PPS_COUNT <= (*ppCtx)->sPSOVector.uiInUsePpsNum)) { + //check if we can generate new SPS or not + WelsLog (& (*ppCtx)->sLogCtx, WELS_LOG_ERROR, + "InitDqLayers(), cannot generate new SPS under the SPS_PPS_LISTING mode!"); + return ENC_RETURN_UNSUPPORTED_PARA; + } + + iSpsId = (!bUseSubsetSps) ? ((*ppCtx)->sPSOVector.uiInUseSpsNum++) : ((*ppCtx)->sPSOVector.uiInUseSubsetSpsNum++); + if (iSpsId >= MAX_SPS_COUNT) { + if (SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy) { + WelsLog (& (*ppCtx)->sLogCtx, WELS_LOG_ERROR, + "InitDqLayers(), cannot generate new SPS under the SPS_PPS_LISTING mode!"); + return ENC_RETURN_UNSUPPORTED_PARA; + } + // reset current list + if (!bUseSubsetSps) { + (*ppCtx)->sPSOVector.uiInUseSpsNum = 1; + memset ((*ppCtx)->pSpsArray, 0, MAX_SPS_COUNT * sizeof (SWelsSPS)); + } else { + (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum = 1; + memset ((*ppCtx)->pSubsetArray, 0, MAX_SPS_COUNT * sizeof (SSubsetSps)); + } + iSpsId = 0; + } + + WelsGenerateNewSps (*ppCtx, bUseSubsetSps, iDlayerIndex, + iDlayerCount, iSpsId, pSps, pSubsetSps); + } + } + + if (! (SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy)) { + pPps = & (*ppCtx)->pPPSArray[iPpsId]; + // initialize pPps + WelsInitPps (pPps, pSps, pSubsetSps, iPpsId, true, bUseSubsetSps, pParam->iEntropyCodingModeFlag != 0); + } else { + const int32_t kiFoundPpsId = FindExistingPps (pSps, pSubsetSps, bUseSubsetSps, iSpsId, + pParam->iEntropyCodingModeFlag != 0, + (*ppCtx)->sPSOVector.uiInUsePpsNum, + (*ppCtx)->pPPSArray); + + + if (INVALID_ID != kiFoundPpsId) { + //if yes, set pPps to it + iPpsId = kiFoundPpsId; + pPps = & ((*ppCtx)->pPPSArray[kiFoundPpsId]); + } else { + iPpsId = ((*ppCtx)->sPSOVector.uiInUsePpsNum++); + pPps = & (*ppCtx)->pPPSArray[iPpsId]; + WelsInitPps (pPps, pSps, pSubsetSps, iPpsId, true, bUseSubsetSps, pParam->iEntropyCodingModeFlag != 0); + } + } // Not using FMO in SVC coding so far, come back if need FMO { @@ -1085,7 +1315,7 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx) { if (iResult) { WelsLog (& (*ppCtx)->sLogCtx, WELS_LOG_WARNING, "InitDqLayers(), InitSlicePEncCtx failed(%d)!", iResult); FreeMemorySvc (ppCtx); - return 1; + return iResult; } (*ppCtx)->ppDqLayerList[iDlayerIndex]->pSliceEncCtx = & (*ppCtx)->pSliceCtxList[iDlayerIndex]; } @@ -1097,12 +1327,23 @@ static inline int32_t InitDqLayers (sWelsEncCtx** ppCtx) { if (bUseSubsetSps) ++ iSpsId; ++ iPpsId; - ++ (*ppCtx)->iSpsNum; + if (bUseSubsetSps) { + ++ (*ppCtx)->iSubsetSpsNum; + } else { + ++ (*ppCtx)->iSpsNum; + } ++ (*ppCtx)->iPpsNum; ++ iDlayerIndex; } - return 0; + if (SPS_LISTING & pParam->iSpsPpsIdStrategy) { + (*ppCtx)->iSpsNum = (*ppCtx)->sPSOVector.uiInUseSpsNum; + (*ppCtx)->iSubsetSpsNum = (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum; + } + if (SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy) { + (*ppCtx)->iPpsNum = (*ppCtx)->sPSOVector.uiInUsePpsNum; + } + return ENC_RETURN_SUCCESS; } int32_t AllocStrideTables (sWelsEncCtx** ppCtx, const int32_t kiNumSpatialLayers) { @@ -1411,7 +1652,7 @@ void GetMvMvdRange (SWelsSvcCodingParam* pParam, int32_t& iMvRange, int32_t& iMv iMvdRange = WELS_MIN (iMvdRange, iFixMvdRange); } -int32_t RequestMemorySvc (sWelsEncCtx** ppCtx) { +int32_t RequestMemorySvc (sWelsEncCtx** ppCtx, SExistingParasetList* pExistingParasetList) { SWelsSvcCodingParam* pParam = (*ppCtx)->pSvcParam; CMemoryAlign* pMa = (*ppCtx)->pMemAlign; SSpatialLayerConfig* pFinalSpatial = NULL; @@ -1616,7 +1857,7 @@ int32_t RequestMemorySvc (sWelsEncCtx** ppCtx) { //End of pVaa memory allocation - iResult = InitDqLayers (ppCtx); + iResult = InitDqLayers (ppCtx, pExistingParasetList); if (iResult) { WelsLog (& (*ppCtx)->sLogCtx, WELS_LOG_WARNING, "RequestMemorySvc(), InitDqLayers failed(%d)!", iResult); FreeMemorySvc (ppCtx); @@ -2130,7 +2371,8 @@ int32_t GetMultipleThreadIdc (SLogContext* pLogCtx, SWelsSvcCodingParam* pCoding * \pParam pParam SWelsSvcCodingParam* * \return successful - 0; otherwise none 0 for failed */ -int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pCodingParam, SLogContext* pLogCtx) { +int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pCodingParam, SLogContext* pLogCtx, + SExistingParasetList* pExistingParasetList) { sWelsEncCtx* pCtx = NULL; int32_t iRet = 0; int16_t iSliceNum = 1; // number of slices used @@ -2189,7 +2431,7 @@ int32_t WelsInitEncoderExt (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pCodingPar pCtx->iActiveThreadsNum = pCodingParam->iCountThreadsNum; pCtx->iMaxSliceCount = iSliceNum; - iRet = RequestMemorySvc (&pCtx); + iRet = RequestMemorySvc (&pCtx, pExistingParasetList); if (iRet != 0) { WelsLog (pLogCtx, WELS_LOG_ERROR, "WelsInitEncoderExt(), RequestMemorySvc failed return %d.", iRet); FreeMemorySvc (&pCtx); @@ -2553,18 +2795,26 @@ void WelsInitCurrentLayer (sWelsEncCtx* pCtx, iSliceCount = GetCurrentSliceNum (pCurDq->pSliceEncCtx); assert (iSliceCount > 0); - pBaseSlice->sSliceHeaderExt.sSliceHeader.iPpsId = pDqIdc->iPpsId; + int32_t iCurPpsId = pDqIdc->iPpsId; + int32_t iCurSpsId = pDqIdc->iSpsId; + + if (SPS_PPS_LISTING == pParam->iSpsPpsIdStrategy) { + iCurPpsId = pCtx->sPSOVector.iPpsIdList[pDqIdc->iPpsId][WELS_ABS (pCtx->uiIdrPicId - 1) % MAX_PPS_COUNT]; + } + + pBaseSlice->sSliceHeaderExt.sSliceHeader.iPpsId = iCurPpsId; pCurDq->sLayerInfo.pPpsP = - pBaseSlice->sSliceHeaderExt.sSliceHeader.pPps = &pCtx->pPPSArray[pBaseSlice->sSliceHeaderExt.sSliceHeader.iPpsId]; - pBaseSlice->sSliceHeaderExt.sSliceHeader.iSpsId = pDqIdc->iSpsId; + pBaseSlice->sSliceHeaderExt.sSliceHeader.pPps = &pCtx->pPPSArray[iCurPpsId]; + + pBaseSlice->sSliceHeaderExt.sSliceHeader.iSpsId = iCurSpsId; if (kbUseSubsetSpsFlag) { - pCurDq->sLayerInfo.pSubsetSpsP = &pCtx->pSubsetArray[pDqIdc->iSpsId]; + pCurDq->sLayerInfo.pSubsetSpsP = &pCtx->pSubsetArray[iCurSpsId]; pCurDq->sLayerInfo.pSpsP = pBaseSlice->sSliceHeaderExt.sSliceHeader.pSps = &pCurDq->sLayerInfo.pSubsetSpsP->pSps; } else { pCurDq->sLayerInfo.pSubsetSpsP = NULL; pCurDq->sLayerInfo.pSpsP = - pBaseSlice->sSliceHeaderExt.sSliceHeader.pSps = &pCtx->pSpsArray[pBaseSlice->sSliceHeaderExt.sSliceHeader.iSpsId]; + pBaseSlice->sSliceHeaderExt.sSliceHeader.pSps = &pCtx->pSpsArray[iCurSpsId]; } pSlice = pBaseSlice; @@ -2865,46 +3115,68 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN /* write all SPS */ iIdx = 0; while (iIdx < pCtx->iSpsNum) { - SDqIdc* pDqIdc = &pCtx->pDqIdcMap[iIdx]; - const int32_t kiDid = pDqIdc->uiSpatialId; - const bool kbUsingSubsetSps = (kiDid > BASE_DEPENDENCY_ID); - iNal = pCtx->pOut->iNalIndex; - if (pCtx->pSvcParam->bEnableSpsPpsIdAddition) { + if (INCREASING_ID == pCtx->pSvcParam->iSpsPpsIdStrategy) { #if _DEBUG - pCtx->sPSOVector.bEnableSpsPpsIdAddition = 1; - assert (kiDid < MAX_DEPENDENCY_LAYER); + pCtx->sPSOVector.iSpsPpsIdStrategy = INCREASING_ID; assert (iIdx < MAX_DQ_LAYER_NUM); #endif - ParasetIdAdditionIdAdjust (& (pCtx->sPSOVector.sParaSetOffsetVariable[kbUsingSubsetSps ? PARA_SET_TYPE_SUBSETSPS : - PARA_SET_TYPE_AVCSPS]), - (kbUsingSubsetSps) ? (pCtx->pSubsetArray[iIdx - 1].pSps.uiSpsId) : (pCtx->pSpsArray[0].uiSpsId), + ParasetIdAdditionIdAdjust (& (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_AVCSPS]), + pCtx->pSpsArray[0].uiSpsId, MAX_SPS_COUNT); - } else { + } else if (CONSTANT_ID == pCtx->pSvcParam->iSpsPpsIdStrategy) { memset (& (pCtx->sPSOVector), 0, sizeof (pCtx->sPSOVector)); } - if (kbUsingSubsetSps) { - iId = iIdx - 1; + /* generate sequence parameters set */ + iId = (SPS_LISTING & pCtx->pSvcParam->iSpsPpsIdStrategy) ? iIdx : 0; - /* generate Subset SPS */ - WelsLoadNal (pCtx->pOut, NAL_UNIT_SUBSET_SPS, NRI_PRI_HIGHEST); + 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); - WelsWriteSubsetSpsSyntax (&pCtx->pSubsetArray[iId], &pCtx->pOut->sBsWrite, - & (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_SUBSETSPS].iParaSetIdDelta[0])); - WelsUnloadNal (pCtx->pOut); - } else { - iId = 0; + 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; - /* generate sequence parameters set */ - WelsLoadNal (pCtx->pOut, NAL_UNIT_SPS, NRI_PRI_HIGHEST); - WelsWriteSpsNal (&pCtx->pSpsArray[0], &pCtx->pOut->sBsWrite, - & (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_AVCSPS].iParaSetIdDelta[0])); - WelsUnloadNal (pCtx->pOut); + pCtx->iPosBsBuffer += iNalLength; + iSize += iNalLength; + + ++ iIdx; + ++ iCountNal; + } + + /* write all Subset SPS */ + iIdx = 0; + while (iIdx < pCtx->iSubsetSpsNum) { + iNal = pCtx->pOut->iNalIndex; + + if (INCREASING_ID == pCtx->pSvcParam->iSpsPpsIdStrategy) { +#if _DEBUG + pCtx->sPSOVector.iSpsPpsIdStrategy = INCREASING_ID; + assert (iIdx < MAX_DQ_LAYER_NUM); +#endif + + ParasetIdAdditionIdAdjust (& (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_SUBSETSPS]), + pCtx->pSubsetArray[iIdx].pSps.uiSpsId, + MAX_SPS_COUNT); } + iId = iIdx; + + /* generate Subset SPS */ + WelsLoadNal (pCtx->pOut, NAL_UNIT_SUBSET_SPS, NRI_PRI_HIGHEST); + + WelsWriteSubsetSpsSyntax (&pCtx->pSubsetArray[iId], &pCtx->pOut->sBsWrite, + & (pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_SUBSETSPS].iParaSetIdDelta[0])); + WelsUnloadNal (pCtx->pOut); + 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, @@ -2921,8 +3193,30 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN /* write all PPS */ iIdx = 0; + if ((SPS_PPS_LISTING == pCtx->pSvcParam->iSpsPpsIdStrategy) && (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; + } + while (iIdx < pCtx->iPpsNum) { - if (pCtx->pSvcParam->bEnableSpsPpsIdAddition) { + if ((INCREASING_ID & pCtx->pSvcParam->iSpsPpsIdStrategy)) { //para_set_type = 2: PPS, use MAX_PPS_COUNT ParasetIdAdditionIdAdjust (&pCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_PPS], pCtx->pPPSArray[iIdx].iPpsId, MAX_PPS_COUNT); @@ -2931,7 +3225,8 @@ int32_t WelsWriteParameterSets (sWelsEncCtx* pCtx, int32_t* pNalLen, int32_t* pN iNal = pCtx->pOut->iNalIndex; /* generate picture parameter set */ WelsLoadNal (pCtx->pOut, NAL_UNIT_PPS, NRI_PRI_HIGHEST); - WelsWritePpsSyntax (&pCtx->pPPSArray[iIdx], &pCtx->pOut->sBsWrite, & (pCtx->sPSOVector)); + WelsWritePpsSyntax (&pCtx->pPPSArray[iIdx], &pCtx->pOut->sBsWrite, + ((SPS_PPS_LISTING != pCtx->pSvcParam->iSpsPpsIdStrategy)) ? (& (pCtx->sPSOVector)) : NULL); WelsUnloadNal (pCtx->pOut); iReturn = WelsEncodeNal (&pCtx->pOut->sNalList[iNal], NULL, @@ -3237,7 +3532,6 @@ int32_t WelsEncoderEncodeExt (sWelsEncCtx* pCtx, SFrameBSInfo* pFbi, const SSour iDidList[iSpatialIdx] = iCurDid; // Encoding this picture might mulitiple sQualityStat layers potentially be encoded as followed - switch (pParam->sSliceCfg.uiSliceMode) { case SM_FIXEDSLCNUM_SLICE: case SM_AUTO_SLICE: { @@ -3829,7 +4123,8 @@ int32_t WelsEncoderParamAdjust (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pNewPa (pOldParam->iLTRRefNum != pNewParam->iLTRRefNum) || (pOldParam->iMultipleThreadIdc != pNewParam->iMultipleThreadIdc) || (pOldParam->bEnableBackgroundDetection != pNewParam->bEnableBackgroundDetection) || - (pOldParam->bEnableAdaptiveQuant != pNewParam->bEnableAdaptiveQuant); + (pOldParam->bEnableAdaptiveQuant != pNewParam->bEnableAdaptiveQuant) || + (pOldParam->iSpsPpsIdStrategy != pNewParam->iSpsPpsIdStrategy); if (pNewParam->iMaxNumRefFrame > pOldParam->iMaxNumRefFrame) { bNeedReset = true; } @@ -3876,35 +4171,75 @@ int32_t WelsEncoderParamAdjust (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pNewPa } if (bNeedReset) { - SParaSetOffsetVariable sTmpPsoVariable[PARA_SET_TYPE]; - uint16_t uiTmpIdrPicId;//this is for LTR! SLogContext sLogCtx = (*ppCtx)->sLogCtx; - for (int32_t k = 0; k < PARA_SET_TYPE; k++) - memset (((*ppCtx)->sPSOVector.sParaSetOffsetVariable[k].bUsedParaSetIdInBs), 0, MAX_PPS_COUNT * sizeof (bool)); - memcpy (sTmpPsoVariable, (*ppCtx)->sPSOVector.sParaSetOffsetVariable, - (PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage - uiTmpIdrPicId = (*ppCtx)->uiIdrPicId; + + int32_t iOldSpsPpsIdStrategy = pOldParam->iSpsPpsIdStrategy; + SParaSetOffsetVariable sTmpPsoVariable[PARA_SET_TYPE]; + int32_t iTmpPpsIdList[MAX_DQ_LAYER_NUM * MAX_PPS_COUNT]; + uint16_t uiTmpIdrPicId = (*ppCtx)->uiIdrPicId;//this is for LTR! SEncoderStatistics sTempEncoderStatistics = (*ppCtx)->sEncoderStatistics; + SExistingParasetList sExistingParasetList; + SExistingParasetList* pExistingParasetList = NULL; + + if ((CONSTANT_ID != iOldSpsPpsIdStrategy) && (CONSTANT_ID != pNewParam->iSpsPpsIdStrategy)) { + for (int32_t k = 0; k < PARA_SET_TYPE; k++) { + memset (((*ppCtx)->sPSOVector.sParaSetOffsetVariable[k].bUsedParaSetIdInBs), 0, MAX_PPS_COUNT * sizeof (bool)); + } + memcpy (sTmpPsoVariable, (*ppCtx)->sPSOVector.sParaSetOffsetVariable, + (PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage + + if ((SPS_LISTING & iOldSpsPpsIdStrategy) + && (SPS_LISTING & pNewParam->iSpsPpsIdStrategy)) { + pExistingParasetList = &sExistingParasetList; + sExistingParasetList.uiInUseSpsNum = (*ppCtx)->sPSOVector.uiInUseSpsNum; + sExistingParasetList.uiInUseSubsetSpsNum = (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum; + memcpy (sExistingParasetList.sSps, (*ppCtx)->pSpsArray, MAX_SPS_COUNT * sizeof (SWelsSPS)); + memcpy (sExistingParasetList.sSubsetSps, (*ppCtx)->pSubsetArray, MAX_SPS_COUNT * sizeof (SSubsetSps)); + } + + if ((SPS_PPS_LISTING == iOldSpsPpsIdStrategy) + && (SPS_PPS_LISTING == pNewParam->iSpsPpsIdStrategy)) { + pExistingParasetList = &sExistingParasetList; + sExistingParasetList.uiInUseSpsNum = (*ppCtx)->sPSOVector.uiInUseSpsNum; + sExistingParasetList.uiInUseSubsetSpsNum = (*ppCtx)->sPSOVector.uiInUseSubsetSpsNum; + sExistingParasetList.uiInUsePpsNum = (*ppCtx)->sPSOVector.uiInUsePpsNum; + memcpy (sExistingParasetList.sSps, (*ppCtx)->pSpsArray, MAX_SPS_COUNT * sizeof (SWelsSPS)); + memcpy (sExistingParasetList.sSubsetSps, (*ppCtx)->pSubsetArray, MAX_SPS_COUNT * sizeof (SSubsetSps)); + memcpy (sExistingParasetList.sPps, (*ppCtx)->pPps, MAX_PPS_COUNT * sizeof (SWelsPPS)); + + memcpy (iTmpPpsIdList, ((*ppCtx)->sPSOVector.iPpsIdList), MAX_DQ_LAYER_NUM * MAX_PPS_COUNT * sizeof (int32_t)); + } + } + + WelsUninitEncoderExt (ppCtx); /* Update new parameters */ - if (WelsInitEncoderExt (ppCtx, pNewParam, &sLogCtx)) + if (WelsInitEncoderExt (ppCtx, pNewParam, &sLogCtx, pExistingParasetList)) return 1; // reset the scaled spatial picture size (*ppCtx)->pVpp->WelsPreprocessReset (*ppCtx); //if WelsInitEncoderExt succeed - //load back the needed structure - //for FLEXIBLE_PARASET_ID - memcpy ((*ppCtx)->sPSOVector.sParaSetOffsetVariable, sTmpPsoVariable, - (PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage //for LTR (*ppCtx)->uiIdrPicId = uiTmpIdrPicId; + //for sEncoderStatistics (*ppCtx)->sEncoderStatistics = sTempEncoderStatistics; + + //load back the needed structure for iSpsPpsIdStrategy + if ((CONSTANT_ID != iOldSpsPpsIdStrategy) && (CONSTANT_ID != pNewParam->iSpsPpsIdStrategy)) { + memcpy ((*ppCtx)->sPSOVector.sParaSetOffsetVariable, sTmpPsoVariable, + (PARA_SET_TYPE)*sizeof (SParaSetOffsetVariable)); // confirmed_safe_unsafe_usage + } + + if ((SPS_PPS_LISTING == iOldSpsPpsIdStrategy) + && (SPS_PPS_LISTING == pNewParam->iSpsPpsIdStrategy)) { + memcpy (((*ppCtx)->sPSOVector.iPpsIdList), iTmpPpsIdList, MAX_DQ_LAYER_NUM * MAX_PPS_COUNT * sizeof (int32_t)); + } } else { /* maybe adjustment introduced in bitrate or little settings adjustment and so on.. */ pNewParam->iNumRefFrame = WELS_CLIP3 (pNewParam->iNumRefFrame, MIN_REF_PIC_COUNT, @@ -3919,7 +4254,7 @@ int32_t WelsEncoderParamAdjust (sWelsEncCtx** ppCtx, SWelsSvcCodingParam* pNewPa pOldParam->fMaxFrameRate = pNewParam->fMaxFrameRate; // maximal frame rate [Hz / fps] pOldParam->iComplexityMode = pNewParam->iComplexityMode; // color space of input sequence pOldParam->uiIntraPeriod = pNewParam->uiIntraPeriod; // intra period (multiple of GOP size as desired) - pOldParam->bEnableSpsPpsIdAddition = pNewParam->bEnableSpsPpsIdAddition; + pOldParam->iSpsPpsIdStrategy = pNewParam->iSpsPpsIdStrategy; pOldParam->bPrefixNalAddingCtrl = pNewParam->bPrefixNalAddingCtrl; pOldParam->iNumRefFrame = pNewParam->iNumRefFrame; // number of reference frame used pOldParam->uiGopSize = pNewParam->uiGopSize; diff --git a/codec/encoder/core/src/svc_encode_slice.cpp b/codec/encoder/core/src/svc_encode_slice.cpp index 610dc3e0..523756ca 100644 --- a/codec/encoder/core/src/svc_encode_slice.cpp +++ b/codec/encoder/core/src/svc_encode_slice.cpp @@ -237,7 +237,8 @@ void WriteRefPicMarking (SBitStringAux* pBs, SSliceHeader* pSliceHeader, SNalUni } } -void WelsSliceHeaderWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCurLayer, SSlice* pSlice, int32_t* pPpsIdDelta) { +void WelsSliceHeaderWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCurLayer, SSlice* pSlice, + int32_t* pPpsIdDelta) { SWelsSPS* pSps = pCurLayer->sLayerInfo.pSpsP; SWelsPPS* pPps = pCurLayer->sLayerInfo.pPpsP; SSliceHeader* pSliceHeader = &pSlice->sSliceHeaderExt.sSliceHeader; @@ -246,7 +247,7 @@ void WelsSliceHeaderWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCur BsWriteUE (pBs, pSliceHeader->iFirstMbInSlice); BsWriteUE (pBs, pSliceHeader->eSliceType); /* same type things */ - BsWriteUE (pBs, pSliceHeader->pPps->iPpsId + pPpsIdDelta[pSliceHeader->pPps->iPpsId]); + BsWriteUE (pBs, pSliceHeader->pPps->iPpsId + ((pPpsIdDelta != NULL) ? pPpsIdDelta[pSliceHeader->pPps->iPpsId] : 0)); BsWriteBits (pBs, pSps->uiLog2MaxFrameNum, pSliceHeader->iFrameNum); @@ -301,7 +302,8 @@ void WelsSliceHeaderWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCur } } -void WelsSliceHeaderExtWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCurLayer, SSlice* pSlice, int32_t* pPpsIdDelta) { +void WelsSliceHeaderExtWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* pCurLayer, SSlice* pSlice, + int32_t* pPpsIdDelta) { SWelsSPS* pSps = pCurLayer->sLayerInfo.pSpsP; SWelsPPS* pPps = pCurLayer->sLayerInfo.pPpsP; SSubsetSps* pSubSps = pCurLayer->sLayerInfo.pSubsetSpsP; @@ -312,7 +314,7 @@ void WelsSliceHeaderExtWrite (sWelsEncCtx* pCtx, SBitStringAux* pBs, SDqLayer* p BsWriteUE (pBs, pSliceHeader->iFirstMbInSlice); BsWriteUE (pBs, pSliceHeader->eSliceType); /* same type things */ - BsWriteUE (pBs, pSliceHeader->pPps->iPpsId + pPpsIdDelta[pSliceHeader->pPps->iPpsId]); + BsWriteUE (pBs, pSliceHeader->pPps->iPpsId + ((pPpsIdDelta != NULL) ? pPpsIdDelta[pSliceHeader->pPps->iPpsId] : 0)); BsWriteBits (pBs, pSps->uiLog2MaxFrameNum, pSliceHeader->iFrameNum); @@ -727,11 +729,12 @@ int32_t WelsCodeOneSlice (sWelsEncCtx* pEncCtx, const int32_t kiSliceIdx, const WelsSliceHeaderExtInit (pEncCtx, pCurLayer, pCurSlice); - g_pWelsWriteSliceHeader[pCurSlice->bSliceHeaderExtFlag] (pEncCtx, pBs, pCurLayer, pCurSlice, - & (pEncCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[0])); + ((SPS_PPS_LISTING != pEncCtx->pSvcParam->iSpsPpsIdStrategy) ? (& + (pEncCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[0])) : NULL)); + #if _DEBUG - if (pEncCtx->sPSOVector.bEnableSpsPpsIdAddition) { + if (INCREASING_ID & pEncCtx->sPSOVector.iSpsPpsIdStrategy) { const int32_t kiEncoderPpsId = pCurSlice->sSliceHeaderExt.sSliceHeader.pPps->iPpsId; const int32_t kiTmpPpsIdInBs = kiEncoderPpsId + pEncCtx->sPSOVector.sParaSetOffsetVariable[PARA_SET_TYPE_PPS].iParaSetIdDelta[ kiEncoderPpsId ]; diff --git a/codec/encoder/plus/src/welsEncoderExt.cpp b/codec/encoder/plus/src/welsEncoderExt.cpp index b230ab23..a0a5a445 100644 --- a/codec/encoder/plus/src/welsEncoderExt.cpp +++ b/codec/encoder/plus/src/welsEncoderExt.cpp @@ -332,7 +332,7 @@ int CWelsH264SVCEncoder::InitializeInternal (SWelsSvcCodingParam* pCfg) { m_iMaxPicHeight = pCfg->iPicHeight; TraceParamInfo (pCfg); - if (WelsInitEncoderExt (&m_pEncContext, pCfg, &m_pWelsTrace->m_sLogCtx)) { + if (WelsInitEncoderExt (&m_pEncContext, pCfg, &m_pWelsTrace->m_sLogCtx, NULL)) { WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, "CWelsH264SVCEncoder::Initialize(), WelsInitEncoderExt failed."); WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_DEBUG, "Problematic Input Base Param: iUsageType=%d, Resolution=%dx%d, FR=%f, TLayerNum=%d, DLayerNum=%d", @@ -477,6 +477,7 @@ int CWelsH264SVCEncoder::ForceIntraFrame (bool bIDR) { } WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "CWelsH264SVCEncoder::ForceIntraFrame(), bIDR= %d", bIDR); + ForceCodingIDR (m_pEncContext); m_pEncContext->sEncoderStatistics.uiIDRReqNum++; @@ -486,8 +487,8 @@ int CWelsH264SVCEncoder::ForceIntraFrame (bool bIDR) { void CWelsH264SVCEncoder::TraceParamInfo (SEncParamExt* pParam) { WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, "iUsageType = %d,iPicWidth= %d;iPicHeight= %d;iTargetBitrate= %d;iMaxBitrate= %d;iRCMode= %d;iPaddingFlag= %d;iTemporalLayerNum= %d;iSpatialLayerNum= %d;fFrameRate= %.6ff;uiIntraPeriod= %d;\ - bEnableSpsPpsIdAddition = %d;bPrefixNalAddingCtrl = %d;bEnableDenoise= %d;bEnableBackgroundDetection= %d;bEnableAdaptiveQuant= %d;bEnableFrameSkip= %d;bEnableLongTermReference= %d;iLtrMarkPeriod= %d;\ - iComplexityMode = %d;iNumRefFrame = %d;iEntropyCodingModeFlag = %d;uiMaxNalSize = %d;iLTRRefNum = %d;iMultipleThreadIdc = %d;iLoopFilterDisableIdc = %d", + iSpsPpsIdStrategy = %d;bPrefixNalAddingCtrl = %d;bEnableDenoise= %d;bEnableBackgroundDetection= %d;bEnableAdaptiveQuant= %d;bEnableFrameSkip= %d;bEnableLongTermReference= %d;iLtrMarkPeriod= %d;\ + iComplexityMode = %d;iNumRefFrame = %d;iEntropyCodingModeFlag = %d;uiMaxNalSize = %d;iLTRRefNum = %d;iMultipleThreadIdc = %d;iLoopFilterDisableIdc = %d (offset(alpha/beta): %d,%d)", pParam->iUsageType, pParam->iPicWidth, pParam->iPicHeight, @@ -499,7 +500,7 @@ void CWelsH264SVCEncoder::TraceParamInfo (SEncParamExt* pParam) { pParam->iSpatialLayerNum, pParam->fMaxFrameRate, pParam->uiIntraPeriod, - pParam->bEnableSpsPpsIdAddition, + pParam->iSpsPpsIdStrategy, pParam->bPrefixNalAddingCtrl, pParam->bEnableDenoise, pParam->bEnableBackgroundDetection, @@ -513,7 +514,9 @@ void CWelsH264SVCEncoder::TraceParamInfo (SEncParamExt* pParam) { pParam->uiMaxNalSize, pParam->iLTRRefNum, pParam->iMultipleThreadIdc, - pParam->iLoopFilterDisableIdc + pParam->iLoopFilterDisableIdc, + pParam->iLoopFilterAlphaC0Offset, + pParam->iLoopFilterBetaOffset ); int32_t i = 0; int32_t iSpatialLayers = (pParam->iSpatialLayerNum < MAX_SPATIAL_LAYER_NUM) ? (pParam->iSpatialLayerNum) : @@ -706,16 +709,26 @@ int CWelsH264SVCEncoder::SetOption (ENCODER_OPTION eOptionId, void* pOption) { #endif//OUTPUT_BIT_STREAM if (sEncodingParam.iSpatialLayerNum < 1 || sEncodingParam.iSpatialLayerNum > MAX_SPATIAL_LAYER_NUM) { // verify number of spatial layer + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, + "CWelsH264SVCEncoder::SetOption():ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, iSpatialLayerNum(%d) failed!", + sEncodingParam.iSpatialLayerNum); return cmInitParaError; } if (sConfig.ParamTranscode (sEncodingParam)) { + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, + "CWelsH264SVCEncoder::SetOption():ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, ParamTranscode failed!"); return cmInitParaError; } if (sConfig.iSpatialLayerNum < 1) { + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, + "CWelsH264SVCEncoder::SetOption():ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, iSpatialLayerNum(%d) failed!", + sConfig.iSpatialLayerNum); return cmInitParaError; } if (sConfig.DetermineTemporalSettings()) { + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, + "CWelsH264SVCEncoder::SetOption():ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, DetermineTemporalSettings failed!"); return cmInitParaError; } @@ -898,11 +911,17 @@ int CWelsH264SVCEncoder::SetOption (ENCODER_OPTION eOptionId, void* pOption) { } break; case ENCODER_OPTION_ENABLE_SPS_PPS_ID_ADDITION: { - bool iValue = * ((bool*)pOption); - - m_pEncContext->pSvcParam->bEnableSpsPpsIdAddition = iValue; - WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, " CWelsH264SVCEncoder::SetOption enable SPS/PPS ID = %d ", - m_pEncContext->pSvcParam->bEnableSpsPpsIdAddition); + int32_t iValue = * ((int32_t*)pOption); + if (((iValue > INCREASING_ID) || (m_pEncContext->pSvcParam->iSpsPpsIdStrategy > INCREASING_ID)) + && m_pEncContext->pSvcParam->iSpsPpsIdStrategy != iValue) { + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_ERROR, + " CWelsH264SVCEncoder::SetOption iSpsPpsIdStrategy changing in the middle of call is NOT allowed for iSpsPpsIdStrategy>INCREASING_ID: existing setting is %d and the new one is %d", + m_pEncContext->pSvcParam->iSpsPpsIdStrategy, iValue); + return cmInitParaError; + } + m_pEncContext->pSvcParam->iSpsPpsIdStrategy = iValue; + WelsLog (&m_pWelsTrace->m_sLogCtx, WELS_LOG_INFO, " CWelsH264SVCEncoder::SetOption iSpsPpsIdStrategy = %d ", + m_pEncContext->pSvcParam->iSpsPpsIdStrategy); } break; case ENCODER_OPTION_CURRENT_PATH: { diff --git a/test/api/encode_decode_api_test.cpp b/test/api/encode_decode_api_test.cpp index 378621ba..684e1928 100644 --- a/test/api/encode_decode_api_test.cpp +++ b/test/api/encode_decode_api_test.cpp @@ -88,26 +88,25 @@ class EncodeDecodeTestBase : public ::testing::TestWithParamiUsageType = CAMERA_VIDEO_REAL_TIME; + pParam->iPicWidth = width; + pParam->iPicHeight = height; + pParam->fMaxFrameRate = framerate; + pParam->iRCMode = RC_OFF_MODE; //rc off + pParam->iMultipleThreadIdc = 1; //single thread + pParam->iSpatialLayerNum = iLayers; + pParam->iNumRefFrame = AUTO_REF_PIC_COUNT; for (int i = 0; i < iLayers; i++) { - param_.sSpatialLayers[i].iVideoWidth = width >> (iLayers - i - 1); - param_.sSpatialLayers[i].iVideoHeight = height >> (iLayers - i - 1); - param_.sSpatialLayers[i].fFrameRate = framerate; - param_.sSpatialLayers[i].sSliceCfg.uiSliceMode = SM_FIXEDSLCNUM_SLICE; - param_.sSpatialLayers[i].sSliceCfg.sSliceArgument.uiSliceNum = iSlices; + pParam->sSpatialLayers[i].iVideoWidth = width >> (iLayers - i - 1); + pParam->sSpatialLayers[i].iVideoHeight = height >> (iLayers - i - 1); + pParam->sSpatialLayers[i].fFrameRate = framerate; + pParam->sSpatialLayers[i].sSliceCfg.uiSliceMode = SM_FIXEDSLCNUM_SLICE; + pParam->sSpatialLayers[i].sSliceCfg.sSliceArgument.uiSliceNum = iSlices; } - - } + virtual void prepareEncDecParam (const EncodeDecodeFileParamBase EncDecFileParam) { //for encoder //I420: 1(Y) + 1/4(U) + 1/4(V) @@ -147,6 +146,15 @@ class EncodeDecodeTestBase : public ::testing::TestWithParam> 1) + 1) << 1, 2, MAX_WIDTH)); + } + + virtual int GetRandHeight() { + return (WELS_CLIP3 ((((rand() % MAX_HEIGHT) >> 1) + 1) << 1, 2, MAX_HEIGHT)); + } + protected: SEncParamExt param_; BufferedData buf_; @@ -171,8 +179,8 @@ class EncodeDecodeTestAPI : public EncodeDecodeTestBase { EncodeDecodeTestBase::TearDown(); } - void prepareParam (int iLayers, int iSlices, int width, int height, float framerate) { - EncodeDecodeTestBase::prepareParam (iLayers, iSlices, width, height, framerate); + void prepareParam (int iLayers, int iSlices, int width, int height, float framerate, SEncParamExt* pParam) { + EncodeDecodeTestBase::prepareParam (iLayers, iSlices, width, height, framerate, pParam); } void InitialEncDec (int iWidth, int iHeight); @@ -192,6 +200,24 @@ class EncodeDecodeTestAPI : public EncodeDecodeTestBase { else if (1 == iCheckTypeIndex) ASSERT_TRUE (rv == cmResultSuccess || rv == cmUnkonwReason); } + + void EncDecOneFrame (const int iWidth, const int iHeight, const int iFrame, FILE* pfEnc) { + int iLen = 0, rv; + InitialEncDec (iWidth, iHeight); + EncodeOneFrame (iFrame); + + //extract target layer data + encToDecData (info, iLen); + //call decoder + unsigned char* pData[3] = { NULL }; + memset (&dstBufInfo_, 0, sizeof (SBufferInfo)); + rv = decoder_->DecodeFrame2 (info.sLayerInfo[0].pBsBuf, iLen, pData, &dstBufInfo_); + EXPECT_TRUE (rv == cmResultSuccess) << " rv = " << rv << " iFrameIdx = " << iFrame; + + if (NULL != pfEnc) { + fwrite (info.sLayerInfo[0].pBsBuf, iLen, 1, pfEnc); + } + } }; void EncodeDecodeTestAPI::InitialEncDec (int iWidth, int iHeight) { @@ -234,7 +260,7 @@ void EncodeDecodeTestAPI::RandomParamExtCombination() { param_.iNumRefFrame = AUTO_REF_PIC_COUNT; param_.iMultipleThreadIdc = rand(); - param_.bEnableSpsPpsIdAddition = (rand() % 2 == 0) ? false : true; + param_.iSpsPpsIdStrategy = rand() % 3; param_.bPrefixNalAddingCtrl = (rand() % 2 == 0) ? false : true; param_.bEnableSSEI = (rand() % 2 == 0) ? false : true; param_.iPaddingFlag = rand() % 2; @@ -462,7 +488,7 @@ static const EncodeDecodeFileParamBase kFileParamArray[] = { TEST_P (EncodeDecodeTestAPI, DecoderVclNal) { EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -498,7 +524,7 @@ TEST_P (EncodeDecodeTestAPI, DecoderVclNal) { TEST_P (EncodeDecodeTestAPI, GetOptionFramenum) { EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -535,7 +561,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionFramenum) { TEST_P (EncodeDecodeTestAPI, GetOptionIDR) { EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -554,7 +580,8 @@ TEST_P (EncodeDecodeTestAPI, GetOptionIDR) { int32_t iSpsPpsIdAddition = 0; int iIdx = 0; while (iIdx <= p.numframes) { - iSpsPpsIdAddition = rand() % 2; + iSpsPpsIdAddition = rand() % + 2; //the current strategy supports more than 2 modes, but the switch between the modes>2 is not allowed iIDRPeriod = (rand() % 150) + 1; encoder_->SetOption (ENCODER_OPTION_IDR_INTERVAL, &iIDRPeriod); encoder_->SetOption (ENCODER_OPTION_ENABLE_SPS_PPS_ID_ADDITION, &iSpsPpsIdAddition); @@ -776,7 +803,7 @@ int SimulateNALLoss (const unsigned char* pSrc, int& iSrcLen, std::vectorUninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -809,7 +836,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionLTR_ALLLTR) { SLTRMarkingFeedback m_LTR_Marking_Feedback; SLTRRecoverRequest m_LTR_Recover_Request; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -868,7 +895,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionLTR_Engine) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -918,7 +945,7 @@ TEST_P (EncodeDecodeTestAPI, SetOptionECFlag_ERROR_CON_DISABLE) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.bEnableLongTermReference = true; param_.iLTRRefNum = 1; encoder_->Uninitialize(); @@ -985,7 +1012,7 @@ TEST_P (EncodeDecodeTestAPI, SetOptionECFlag_ERROR_CON_SLICE_COPY) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -1036,7 +1063,7 @@ TEST_P (EncodeDecodeTestAPI, SetOptionECFlag_ERROR_CON_SLICE_COPY) { TEST_P (EncodeDecodeTestAPI, InOutTimeStamp) { EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -1086,7 +1113,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionTid_AVC_NOPREFIX) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.bPrefixNalAddingCtrl = false; param_.iTemporalLayerNum = (rand() % 4) + 1; encoder_->Uninitialize(); @@ -1155,7 +1182,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionTid_AVC_WITH_PREFIX_NOLOSS) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.bPrefixNalAddingCtrl = true; param_.iTemporalLayerNum = (rand() % 4) + 1; param_.iSpatialLayerNum = 1; @@ -1210,7 +1237,7 @@ TEST_P (EncodeDecodeTestAPI, GetOptionTid_SVC_L1_NOLOSS) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (2, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (2, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iTemporalLayerNum = (rand() % 4) + 1; param_.iSpatialLayerNum = 2; encoder_->Uninitialize(); @@ -1266,7 +1293,7 @@ TEST_P (EncodeDecodeTestAPI, SetOption_Trace) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; int rv = encoder_->InitializeExt (¶m_); @@ -1330,7 +1357,7 @@ TEST_P (EncodeDecodeTestAPI, SetOption_Trace_NULL) { SLTRRecoverRequest m_LTR_Recover_Request; m_LTR_Recover_Request.uiIDRPicId = 0; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -1393,7 +1420,7 @@ TEST_P (EncodeDecodeTestAPI, SetOptionECIDC_GeneralSliceChange) { uint32_t uiEcIdc; uint32_t uiGet; EncodeDecodeFileParamBase p = GetParam(); - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -1473,7 +1500,7 @@ TEST_F (EncodeDecodeTestAPI, SetOptionECIDC_SpecificFrameChange) { uint32_t uiEcIdc; uint32_t uiGet; EncodeDecodeFileParamBase p = kFileParamArray[0]; - prepareParam (1, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (1, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -1612,7 +1639,7 @@ TEST_F (EncodeDecodeTestAPI, SetOptionECIDC_SpecificSliceChange_IDRLoss) { uint32_t uiEcIdc = 2; //default set as SLICE_COPY uint32_t uiGet; EncodeDecodeFileParamBase p = kFileParamArray[0]; - prepareParam (1, 2, p.width, p.height, p.frameRate); + prepareParam (1, 2, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -1745,7 +1772,7 @@ TEST_F (EncodeDecodeTestAPI, SetOptionECIDC_SpecificSliceChange_IDRNoLoss) { uint32_t uiEcIdc; uint32_t uiGet; EncodeDecodeFileParamBase p = kFileParamArray[0]; - prepareParam (1, 2, p.width, p.height, p.frameRate); + prepareParam (1, 2, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = 1; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -1914,7 +1941,7 @@ TEST_F (EncodeDecodeTestAPI, Engine_SVC_Switch_I) { EncodeDecodeFileParamBase p = kSVCSwitch[0]; p.width = p.width << 2; p.height = p.height << 2; - prepareParam (4, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (4, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iTemporalLayerNum = (rand() % 4) + 1; param_.iSpatialLayerNum = 4; encoder_->Uninitialize(); @@ -1979,7 +2006,7 @@ TEST_F (EncodeDecodeTestAPI, Engine_SVC_Switch_P) { int iLastDid = 0; p.width = p.width << 2; p.height = p.height << 2; - prepareParam (4, p.slicenum, p.width, p.height, p.frameRate); + prepareParam (4, p.slicenum, p.width, p.height, p.frameRate, ¶m_); param_.iTemporalLayerNum = (rand() % 4) + 1; param_.iSpatialLayerNum = 4; encoder_->Uninitialize(); @@ -2065,7 +2092,7 @@ TEST_F (EncodeDecodeTestAPI, SetOptionEncParamExt) { int iEncFrameNum = WelsClip3 ((rand() % ENCODE_FRAME_NUM) + 1, 1, ENCODE_FRAME_NUM); int iSliceNum = 1; encoder_->GetDefaultParams (¶m_); - prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, ¶m_); int rv = encoder_->InitializeExt (¶m_); ASSERT_TRUE (rv == cmResultSuccess); @@ -2128,8 +2155,8 @@ class DecodeCrashTestAPI : public EncodeDecodeTestBase { ASSERT_TRUE (ucBuf_ == NULL); } - void prepareParam (int iLayerNum, int iSliceNum, int width, int height, float framerate) { - EncodeDecodeTestBase::prepareParam (iLayerNum, iSliceNum, width, height, framerate); + void prepareParam (int iLayerNum, int iSliceNum, int width, int height, float framerate, SEncParamExt* pParam) { + EncodeDecodeTestBase::prepareParam (iLayerNum, iSliceNum, width, height, framerate, pParam); } void prepareEncDecParam (const EncodeDecodeFileParamBase EncDecFileParam); @@ -2203,11 +2230,11 @@ TEST_F (DecodeCrashTestAPI, DecoderCrashTest) { int iSeed = rand() % 3; //3 indicates the length of kParamArray[] used in the following EncodeDecodeParamBase p = kParamArray[iSeed]; //Initialize Encoder - prepareParam (1, 1, p.width, p.height, p.frameRate); + prepareParam (1, 1, p.width, p.height, p.frameRate, ¶m_); param_.iRCMode = RC_BITRATE_MODE; param_.iTargetBitrate = p.iTarBitrate; param_.uiIntraPeriod = 0; - param_.bEnableSpsPpsIdAddition = true; + param_.iSpsPpsIdStrategy = INCREASING_ID; param_.bEnableBackgroundDetection = true; param_.bEnableSceneChangeDetect = true; param_.bPrefixNalAddingCtrl = true; @@ -2398,7 +2425,7 @@ TEST_F (DecodeParseAPI, ParseOnly_General) { p.height = iHeight_; p.frameRate = kiFrameRate; p.numframes = kiFrameNum; - prepareParam (kiTotalLayer, kiSliceNum, p.width, p.height, p.frameRate); + prepareParam (kiTotalLayer, kiSliceNum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = kiTotalLayer; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -2475,7 +2502,7 @@ TEST_F (DecodeParseAPI, ParseOnly_SpecSliceLoss) { p.height = iHeight_; p.frameRate = kiFrameRate; p.numframes = 5; - prepareParam (iLayerNum, iSliceNum, p.width, p.height, p.frameRate); + prepareParam (iLayerNum, iSliceNum, p.width, p.height, p.frameRate, ¶m_); param_.iSpatialLayerNum = iLayerNum; encoder_->Uninitialize(); int rv = encoder_->InitializeExt (¶m_); @@ -2525,3 +2552,434 @@ TEST_F (DecodeParseAPI, ParseOnly_SpecSliceLoss) { } //while } +//#define DEBUG_FILE_SAVE2 +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_LISTING_AND_PPS_INCREASING1) { + + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + SEncParamExt sParam2; + SEncParamExt sParam3; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + //prepare param2 + memcpy (&sParam2, &sParam1, sizeof (SEncParamExt)); + while (sParam2.iPicWidth == sParam1.iPicWidth) { + sParam2.iPicWidth = GetRandWidth(); + } + prepareParam (iSpatialLayerNum, iSliceNum, sParam2.iPicWidth, sParam2.iPicHeight, fFrameRate, &sParam2); + sParam2.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + //prepare param3 + memcpy (&sParam3, &sParam1, sizeof (SEncParamExt)); + while (sParam3.iPicHeight == sParam1.iPicHeight) { + sParam3.iPicHeight = GetRandHeight(); + } + prepareParam (iSpatialLayerNum, iSliceNum, sParam3.iPicWidth, sParam3.iPicHeight, fFrameRate, &sParam3); + sParam3.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE2 + fEnc = fopen ("enc2.264", "wb"); +#endif + + // Test part#1 + // step#1: pParam1 + //int TraceLevel = WELS_LOG_INFO; + //encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &TraceLevel); + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // new IDR + rv = encoder_->ForceIntraFrame (true); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // step#2: pParam2 + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption: rv = " << rv << " at " << sParam2.iPicWidth << "x" << + sParam2.iPicHeight; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + // new IDR + rv = encoder_->ForceIntraFrame (true); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + // step#3: back to pParam1 + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // step#4: back to pParam2 + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv << sParam2.iPicWidth << sParam2.iPicHeight; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + +#ifdef DEBUG_FILE_SAVE2 + fclose (fEnc); +#endif + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + + // Test part#2 + // step#1: pParam1 + rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt Failed: rv = " << rv; + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam3); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam3: rv = " << rv; + +#ifdef DEBUG_FILE_SAVE2 + fEnc = fopen ("enc3.264", "wb"); +#endif + iEncFrameNum = 0; + EncDecOneFrame (sParam3.iPicWidth, sParam3.iPicHeight, iEncFrameNum++, fEnc); + + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + +#ifdef DEBUG_FILE_SAVE2 + fclose (fEnc); +#endif + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; +} + +#define DEBUG_FILE_SAVE5 +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_LISTING_AND_PPS_INCREASING2) { + //usage 3: 2 Params with different num_ref, encode IDR0, P1, IDR2; + //the bs will show two SPS and different PPS + + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + SEncParamExt sParam2; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + sParam1.iTemporalLayerNum = 1; + //prepare param2 + memcpy (&sParam2, &sParam1, sizeof (SEncParamExt)); + prepareParam (iSpatialLayerNum, iSliceNum, sParam2.iPicWidth, sParam2.iPicHeight, fFrameRate, &sParam2); + sParam2.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + sParam2.iTemporalLayerNum = 3; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE5 + fEnc = fopen ("encID2.264", "wb"); +#endif + + // step#1: pParam1 + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + + // step#2: pParam2 + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + + // step#3: set back to pParam1, with a smaller num_ref, it still uses the previous SPS + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam1: rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // new IDR, PPS increases + rv = encoder_->ForceIntraFrame (true); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + +#ifdef DEBUG_FILE_SAVE5 + fclose (fEnc); +#endif +} + +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_LISTING_AND_PPS_INCREASING3) { + + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + SEncParamExt sParam2; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE2 + fEnc = fopen ("enc4.264", "wb"); +#endif + + // step#1: pParam1 + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt Failed: rv = " << rv; + + int max_count = 65; // make it more then twice as MAX_SPS_COUNT + std::vector vWidthTable; + vWidthTable.push_back (sParam1.iPicWidth); + + std::vector::iterator vWidthTableIt; + for (int times = 0; times < max_count; times++) { + //prepare param2 + memcpy (&sParam2, &sParam1, sizeof (SEncParamExt)); + do { + sParam2.iPicWidth = GetRandWidth(); + vWidthTableIt = std::find (vWidthTable.begin(), vWidthTable.end(), sParam2.iPicWidth); + } while (vWidthTableIt == vWidthTable.end()); + vWidthTable.push_back (sParam2.iPicWidth); + prepareParam (iSpatialLayerNum, iSliceNum, sParam2.iPicWidth, sParam2.iPicHeight, fFrameRate, &sParam2); + sParam2.iSpsPpsIdStrategy = SPS_LISTING_AND_PPS_INCREASING; + + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv << ", sParam2.iPicWidth=" << + sParam2.iPicWidth; + } // end of setting loop + + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + +#ifdef DEBUG_FILE_SAVE2 + fclose (fEnc); +#endif + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; +} + +//#define DEBUG_FILE_SAVE6 +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_PPS_LISTING1) { + //usage 1: 1 resolution Params, encode IDR0, P1, IDR2; + //the bs will show same SPS and different PPS + // PPS: pic_parameter_set_id 1 ( 0) + // PPS: seq_parameter_set_id 1 ( 0) + // PPS: pic_parameter_set_id 010 ( 1) + // PPS: seq_parameter_set_id 1 ( 0) + // SH: slice_type 011 ( 2) + // SH: pic_parameter_set_id 1 ( 0) + // SH: slice_type 1 ( 0) + // SH: pic_parameter_set_id 1 ( 0) + // SH: slice_type 011 ( 2) + // SH: pic_parameter_set_id 010 ( 1) + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_PPS_LISTING; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE6 + fEnc = fopen ("encLIST1.264", "wb"); +#endif + + // step#1: pParam1 + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // new IDR + rv = encoder_->ForceIntraFrame (true); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + +#ifdef DEBUG_FILE_SAVE6 + fclose (fEnc); +#endif +} + +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_PPS_LISTING2) { + //usage 2: 2 resolution Params, encode IDR0, IDR1, IDR2; + //the bs will show two SPS and different PPS + // === SPS LIST === + //SPS: seq_parameter_set_id 1 ( 0) -- PARAM1 + //SPS: seq_parameter_set_id 010 ( 1) -- PARAM2 + // === PPS LIST === + //PPS: pic_parameter_set_id 1 ( 0) + //PPS: seq_parameter_set_id 1 ( 0) + //PPS: pic_parameter_set_id 010 ( 1) + //PPS: seq_parameter_set_id 010 ( 1) + //PPS: pic_parameter_set_id 011 ( 2) -- PPS2 - SPS0 + //PPS: seq_parameter_set_id 1 ( 0) + //PPS: pic_parameter_set_id 00100 ( 3) -- PPS3 - SPS1 + //PPS: seq_parameter_set_id 010 ( 1) + //PPS: pic_parameter_set_id 00101 ( 4) -- PPS4 - SPS0 + //PPS: seq_parameter_set_id 1 ( 0) + // === VCL LAYER === + //SH: slice_type 011 ( 2) -- PARAM2 + //SH: pic_parameter_set_id 010 ( 1) -- PPS1 - SPS1 - PARAM2 + //SH: slice_type 011 ( 2) -- PARAM1 + //SH: pic_parameter_set_id 011 ( 2) -- PPS2 - SPS0 - PARAM1 + //SH: slice_type 011 ( 2) -- PARAM1 + //SH: pic_parameter_set_id 00101 ( 4) -- PPS4 - SPS0 - PARAM1 + + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + SEncParamExt sParam2; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_PPS_LISTING; + //prepare param2 + memcpy (&sParam2, &sParam1, sizeof (SEncParamExt)); + while (sParam2.iPicWidth == sParam1.iPicWidth) { + sParam2.iPicWidth = GetRandWidth(); + } + prepareParam (iSpatialLayerNum, iSliceNum, sParam2.iPicWidth, sParam2.iPicHeight, fFrameRate, &sParam2); + sParam2.iSpsPpsIdStrategy = SPS_PPS_LISTING; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE5 + fEnc = fopen ("encLIST2.264", "wb"); +#endif + + // step#1: pParam1 + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + + // step#2: pParam2 + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + // step#3: back to pParam1, SHOULD NOT encounter ERROR + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + // new IDR + rv = encoder_->ForceIntraFrame (true); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + EncDecOneFrame (sParam1.iPicWidth, sParam1.iPicHeight, iEncFrameNum++, fEnc); + + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + +#ifdef DEBUG_FILE_SAVE5 + fclose (fEnc); +#endif +} + +TEST_F (EncodeDecodeTestAPI, ParameterSetStrategy_SPS_PPS_LISTING3) { + + int iWidth = GetRandWidth(); + int iHeight = GetRandHeight(); + float fFrameRate = rand() + 0.5f; + int iEncFrameNum = 0; + int iSpatialLayerNum = 1; + int iSliceNum = 1; + + // prepare params + SEncParamExt sParam1; + SEncParamExt sParam2; + SEncParamExt sParam3; + encoder_->GetDefaultParams (&sParam1); + prepareParam (iSpatialLayerNum, iSliceNum, iWidth, iHeight, fFrameRate, &sParam1); + sParam1.iSpsPpsIdStrategy = SPS_PPS_LISTING; + //prepare param2 + memcpy (&sParam2, &sParam1, sizeof (SEncParamExt)); + while (sParam2.iPicWidth == sParam1.iPicWidth) { + sParam2.iPicWidth = GetRandWidth(); + } + prepareParam (iSpatialLayerNum, iSliceNum, sParam2.iPicWidth, sParam2.iPicHeight, fFrameRate, &sParam2); + sParam2.iSpsPpsIdStrategy = SPS_PPS_LISTING; + //prepare param3 + memcpy (&sParam3, &sParam1, sizeof (SEncParamExt)); + while (sParam3.iPicWidth == sParam1.iPicWidth || sParam3.iPicWidth == sParam2.iPicWidth) { + sParam3.iPicWidth = GetRandWidth(); + } + prepareParam (iSpatialLayerNum, iSliceNum, sParam3.iPicWidth, sParam3.iPicHeight, fFrameRate, &sParam3); + sParam3.iSpsPpsIdStrategy = SPS_PPS_LISTING; + + //prepare output if needed + FILE* fEnc = NULL; +#ifdef DEBUG_FILE_SAVE5 + fEnc = fopen ("enc4.264", "wb"); +#endif + + // step#1: ordinary encoding + int rv = encoder_->InitializeExt (&sParam1); + ASSERT_TRUE (rv == cmResultSuccess) << "InitializeExt: rv = " << rv << " at " << sParam1.iPicWidth << "x" << + sParam1.iPicHeight; + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam2); + ASSERT_TRUE (rv == cmResultSuccess) << "SetOption Failed sParam2: rv = " << rv; + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + // step#2: set strategy for success + int32_t iNewStra = SPS_PPS_LISTING; + rv = encoder_->SetOption (ENCODER_OPTION_ENABLE_SPS_PPS_ID_ADDITION, &iNewStra); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv << " iNewStra=" << iNewStra; + + // step#3: setting new strategy, SHOULD encounter ERROR + unsigned int TraceLevel = WELS_LOG_QUIET; + rv = encoder_->SetOption (ENCODER_OPTION_TRACE_LEVEL, &TraceLevel); + ASSERT_TRUE (rv == cmResultSuccess); + iNewStra = CONSTANT_ID; + rv = encoder_->SetOption (ENCODER_OPTION_ENABLE_SPS_PPS_ID_ADDITION, &iNewStra); + ASSERT_TRUE (rv != cmResultSuccess); + + EncDecOneFrame (sParam2.iPicWidth, sParam2.iPicHeight, iEncFrameNum++, fEnc); + + // step#4: pParam3, SHOULD encounter ERROR + rv = encoder_->SetOption (ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &sParam3); + ASSERT_TRUE (rv != cmResultSuccess) << "SetOption: rv = " << rv << " at " << sParam3.iPicWidth << "x" << + sParam3.iPicHeight; + + rv = encoder_->Uninitialize(); + ASSERT_TRUE (rv == cmResultSuccess) << "rv = " << rv; + +#ifdef DEBUG_FILE_SAVE5 + fclose (fEnc); +#endif +} + diff --git a/test/encoder/EncUT_ParameterSetStrategy.cpp b/test/encoder/EncUT_ParameterSetStrategy.cpp new file mode 100644 index 00000000..3aa6aac5 --- /dev/null +++ b/test/encoder/EncUT_ParameterSetStrategy.cpp @@ -0,0 +1,122 @@ +#include +#include "gtest/gtest.h" + +#include "au_set.h" +#include "param_svc.h" +#include "parameter_sets.h" +#include "wels_const.h" + +using namespace WelsEnc; + +class ParameterSetStrategyTest : public ::testing::Test { + public: + virtual void SetUp() { + pMa = NULL; + m_pSpsArray = NULL; + m_pSubsetArray = NULL; + + pMa = new CMemoryAlign (0); + m_pSpsArray = (SWelsSPS*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SWelsSPS), "m_pSpsArray"); + ASSERT_TRUE (NULL != m_pSpsArray); + m_pSubsetArray = (SSubsetSps*)pMa->WelsMalloc (MAX_SPS_COUNT * sizeof (SSubsetSps), "m_pSubsetArray"); + ASSERT_TRUE (NULL != m_pSubsetArray); + + m_pSpsArrayPointer = &m_pSpsArray[0]; + m_pSubsetArrayPointer = &m_pSubsetArray[0]; + + } + virtual void TearDown() { + pMa->WelsFree (m_pSpsArray, "m_pSpsArray"); + pMa->WelsFree (m_pSubsetArray, "m_pSubsetArray"); + delete pMa; + } + void GenerateParam (SWelsSvcCodingParam* pParam); + public: + CMemoryAlign* pMa; + SWelsSPS* m_pSpsArray; + SSubsetSps* m_pSubsetArray; + + SWelsSPS* m_pSpsArrayPointer; + SSubsetSps* m_pSubsetArrayPointer; + +}; + +void ParameterSetStrategyTest::GenerateParam (SWelsSvcCodingParam* pParam) { + SEncParamBase sEncParamBase; + //TODO: consider randomize it + sEncParamBase.iUsageType = CAMERA_VIDEO_REAL_TIME; + sEncParamBase.iPicWidth = 1280; + sEncParamBase.iPicHeight = 720; + sEncParamBase.iTargetBitrate = 1000000; + sEncParamBase.iRCMode = RC_BITRATE_MODE; + sEncParamBase.fMaxFrameRate = 30.0f; + pParam->ParamBaseTranscode (sEncParamBase); +} + +TEST_F (ParameterSetStrategyTest, FindExistingSps) { + int iDlayerIndex = 0; + int iDlayerCount = 0; + bool bUseSubsetSps = false; + int iFoundId = -1; + int iRet = 0; + SSpatialLayerConfig* pDlayerParam; + + //init parameter + SWelsSvcCodingParam sParam1; + GenerateParam (&sParam1); + + //prepare first SPS + int iCurSpsId = 0; + int iCurSpsInUse = 1; + m_pSpsArrayPointer = &m_pSpsArray[iCurSpsId]; + + pDlayerParam = & (sParam1.sSpatialLayers[iDlayerIndex]); + iRet = WelsInitSps (m_pSpsArrayPointer, pDlayerParam, &sParam1.sDependencyLayers[iDlayerIndex], sParam1.uiIntraPeriod, + sParam1.iMaxNumRefFrame, + iCurSpsId, sParam1.bEnableFrameCroppingFlag, sParam1.iRCMode != RC_OFF_MODE, iDlayerCount); + + // try finding #0 + iFoundId = FindExistingSps (&sParam1, bUseSubsetSps, iDlayerIndex, iDlayerCount, iCurSpsInUse, + m_pSpsArray, m_pSubsetArray); + EXPECT_EQ (iFoundId, iCurSpsId); + + // try not finding + SWelsSvcCodingParam sParam2 = sParam1; + sParam2.iMaxNumRefFrame ++; + iFoundId = FindExistingSps (&sParam2, bUseSubsetSps, iDlayerIndex, iDlayerCount, iCurSpsInUse, + m_pSpsArray, m_pSubsetArray); + EXPECT_EQ (iFoundId, INVALID_ID); + + // add new sps + iCurSpsId = 1; + m_pSpsArrayPointer = &m_pSpsArray[iCurSpsId]; + pDlayerParam = & (sParam2.sSpatialLayers[iDlayerIndex]); + iRet = WelsInitSps (m_pSpsArrayPointer, pDlayerParam, &sParam2.sDependencyLayers[iDlayerIndex], sParam2.uiIntraPeriod, + sParam2.iMaxNumRefFrame, + iCurSpsId, sParam2.bEnableFrameCroppingFlag, sParam2.iRCMode != RC_OFF_MODE, iDlayerCount); + iCurSpsInUse = 2; + + // try finding #1 + iFoundId = FindExistingSps (&sParam2, bUseSubsetSps, iDlayerIndex, iDlayerCount, iCurSpsInUse, + m_pSpsArray, m_pSubsetArray); + EXPECT_EQ (iFoundId, iCurSpsId); + + // try finding #0 + iFoundId = FindExistingSps (&sParam1, bUseSubsetSps, iDlayerIndex, iDlayerCount, iCurSpsInUse, + m_pSpsArray, m_pSubsetArray); + EXPECT_EQ (iFoundId, 0); + + // try not finding + if (sParam2.sDependencyLayers[0].iActualWidth > 1) { + + sParam2.sDependencyLayers[0].iActualWidth--; + } else { + sParam2.sDependencyLayers[0].iActualWidth++; + } + + iFoundId = FindExistingSps (&sParam2, bUseSubsetSps, iDlayerIndex, iDlayerCount, iCurSpsInUse, + m_pSpsArray, m_pSubsetArray); + EXPECT_EQ (iFoundId, INVALID_ID); +} + + diff --git a/test/encoder/targets.mk b/test/encoder/targets.mk index 6f78b1d1..4f8c9cb4 100644 --- a/test/encoder/targets.mk +++ b/test/encoder/targets.mk @@ -11,6 +11,7 @@ ENCODER_UNITTEST_CPP_SRCS=\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_MemoryAlloc.cpp\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_MemoryZero.cpp\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_MotionEstimate.cpp\ + $(ENCODER_UNITTEST_SRCDIR)/EncUT_ParameterSetStrategy.cpp\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_Reconstruct.cpp\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_Sample.cpp\ $(ENCODER_UNITTEST_SRCDIR)/EncUT_SVC_me.cpp\