动态配置帧率和码率
由于在编码的码流常会使用网络进行传输,但由于网络容易产生波动,有可能会导致丢帧、编码质量较差等情况,为解决由于网络波动影响的码流传输问题,可以使用动态编码,即当网络环境较差时,使用较低的码率进行码流传输。
这一小节我们讲解如何在编码过程中调整编码的分辨率、帧率、码率等。
1.核心代码
本节程序主要展示在网络中遇到网络波动时,改善卡顿花屏的问题,解决方法就是降低帧率/码率/分辨率等方式进行编码。
1.1 获取新旧参数
//(1)check if need recreate vencLib!
// old config
unsigned int nOldBitRate = GetBitRateFromVENC_CHN_ATTR_S(&pContext->mVencChnAttr);
int nOldDstFrameRate = pContext->mstFrameRate.DstFrmRate;
SIZE_S stOldDstEncodeSize = {0};
ret = GetEncodeDstSizeFromVENC_CHN_ATTR_S(&pContext->mVencChnAttr, &stOldDstEncodeSize);
if(ret != SUCCESS)
{
aloge("fatal error! get encode dst size fail! check code!");
}
int nOldKeyInterval = pContext->mVencChnAttr.VeAttr.MaxKeyInterval;
VENC_RC_MODE_E nOldRcMode = pContext->mVencChnAttr.RcAttr.mRcMode;
PAYLOAD_TYPE_E nOldEncodeType = pContext->mVencChnAttr.VeAttr.Type;
// new config
VENC_RECREATE_DYNAMIC_CONFIG_S *pNewDynamicConfig = &pContext->mDynamicConfig;
unsigned int nNewBitRate = pNewDynamicConfig->mVideoBitRate;
int nNewDstFrameRate = pNewDynamicConfig->mVideoFrameRate;
SIZE_S stNewDstEncodeSize = {pNewDynamicConfig->dstWidth, pNewDynamicConfig->dstHeight};
int nNewKeyInterval = pNewDynamicConfig->mKeyFrameInterval;
VENC_RC_MODE_E nNewRcMode = pNewDynamicConfig->mRcMode;
PAYLOAD_TYPE_E nNewEncodeType = pNewDynamicConfig->mVideoEncoderFmt;
1.2 停止VI和VE通道
//a) destroy vipp
ret = AW_MPI_VI_DisableVirChn(pContext->mViDev, pContext->mViChn);
if (ret != SUCCESS)
{
aloge("fatal error! vipp[%d] disable virChn[%d] fail[0x%x]!", pContext->mViDev, pContext->mViChn, ret);
}
ret = AW_MPI_VENC_StopRecvPic(pContext->mVeChn);
if (ret != SUCCESS)
{
aloge("fatal error! vencChn[%d] stop fail[0x%x]!", pContext->mVeChn, ret);
}
1.3 重置编码通道
//must return all inputFrames before unbind.
ret = AW_MPI_VENC_ResetChn(pContext->mVeChn);
if (ret != SUCCESS)
{
aloge("fatal error! vencChn[%d] ret fail[0x%x]!", pContext->mVeChn, ret);
}
1.4 解绑VI和VE通道
//unbind virViChn and vencChn
MPP_CHN_S stVirViChn = {MOD_ID_VIU, pContext->mViDev, pContext->mViChn};
MPP_CHN_S stVencChn = {MOD_ID_VENC, 0, pContext->mVeChn};
ret = AW_MPI_SYS_UnBind(&stVirViChn, &stVencChn);
if (ret != SUCCESS)
{
aloge("fatal error! virViChn[%d-%d] && vencChn[%d] unbind fail[0x%x]!", pContext->mViDev, pContext->mViChn, pContext->mVeChn, ret);
}
1.5 销毁VI通道
ret = AW_MPI_VI_DestroyVirChn(pContext->mViDev, pContext->mViChn);
if (ret != SUCCESS)
{
aloge("fatal error! vipp[%d] destroy virViChn[%d] fail[0x%x]!", pContext->mViDev, pContext->mViChn, ret);
}
ret = AW_MPI_VI_DisableVipp(pContext->mViDev);
if (ret != SUCCESS)
{
aloge("fatal error! disableVipp[%d] fail!", pContext->mViDev);
}
#if ISP_RUN
AW_MPI_ISP_Stop(pContext->mIspDev);
#endif
ret = AW_MPI_VI_DestroyVipp(pContext->mViDev);
if (ret != SUCCESS)
{
aloge("fatal error! AW_MPI_VI DestroyVipp[%d] failed", pContext->mViDev);
}
1.6重新创建VI通道
//b) create vipp
//update viAttr's resolution
pContext->mViAttr.format.width = stNewDstEncodeSize.Width;
pContext->mViAttr.format.height = stNewDstEncodeSize.Height;
ret = AW_MPI_VI_CreateVipp(pContext->mViDev);
if (ret != SUCCESS)
{
aloge("fatal error! AW_MPI_VI CreateVipp[%d] failed", pContext->mViDev);
}
MPPCallbackInfo cbInfo;
cbInfo.cookie = (void*)pContext;
cbInfo.callback = (MPPCallbackFuncType)&MPPCallbackWrapper;
ret = AW_MPI_VI_RegisterCallback(pContext->mViDev, &cbInfo);
if (ret != SUCCESS)
{
aloge("fatal error! AW_MPI_VI RegisterCallback[%d] failed", pContext->mViDev);
}
ret = AW_MPI_VI_SetVippAttr(pContext->mViDev, &pContext->mViAttr);
if (ret != SUCCESS)
{
aloge("fatal error! AW_MPI_VI SetVippAttr[%d] failed", pContext->mViDev);
}
ret = AW_MPI_VI_GetVippAttr(pContext->mViDev, &pContext->mViAttr);
if (ret != SUCCESS)
{
aloge("fatal error! AW_MPI_VI GetVippAttr[%d] failed", pContext->mViDev);
}
#if ISP_RUN
AW_MPI_ISP_Run(pContext->mIspDev);
#endif
ret = AW_MPI_VI_EnableVipp(pContext->mViDev);
if (ret != SUCCESS)
{
aloge("fatal error! enableVipp[%d] fail!", pContext->mViDev);
}
ret = AW_MPI_VI_CreateVirChn(pContext->mViDev, pContext->mViChn, NULL);
if (ret != SUCCESS)
{
aloge("fatal error! vipp[%d] createVirChn[%d] fail!", pContext->mViDev, pContext->mViChn);
}
1.7 绑定VI和VE通道
//bind vi to venc.
ret = AW_MPI_SYS_Bind(&stVirViChn, &stVencChn);
if (ret != SUCCESS)
{
aloge("fatal error! bind viChn[%d-%d] to vencChn[%d] fail!", pContext->mViDev, pContext->mViChn, pContext->mVeChn);
}
1.8 开启 VI通道
//start virViChn.
ret = AW_MPI_VI_EnableVirChn(pContext->mViDev, pContext->mViChn);
if (ret != SUCCESS)
{
aloge("fatal error! virViChn[%d-%d] enable error!", pContext->mViDev, pContext->mViChn);
}
1.9 停止和销毁编码器
AW_MPI_VENC_StopRecvPic(pContext->mVeChn);
//must return all outFrames before destroy encLib.
AW_MPI_VENC_DestroyEncoder(pContext->mVeChn);
1.10 重新设置编码器属性
VENC_CHN_ATTR_S *pVencChnAttr = &pContext->mVencChnAttr;
VENC_RC_PARAM_S *pVencRcParam = &pContext->mVencRcParam;
unsigned int nThreshSize = stNewDstEncodeSize.Width*stNewDstEncodeSize.Height*3/2/5;
unsigned int nBufSize = nNewBitRate/8*2 + nThreshSize;
pVencChnAttr->VeAttr.Type = nNewEncodeType;
pVencChnAttr->VeAttr.MaxKeyInterval = nNewKeyInterval;
pVencChnAttr->VeAttr.SrcPicWidth = pContext->mViAttr.format.width;
pVencChnAttr->VeAttr.SrcPicHeight = pContext->mViAttr.format.height;
1.11 设置H264属性
if (PT_H264 == pVencChnAttr->VeAttr.Type)
{
pVencChnAttr->VeAttr.AttrH264e.BufSize = AWALIGN(nBufSize, 1024);
pVencChnAttr->VeAttr.AttrH264e.mThreshSize = nThreshSize;
pVencChnAttr->VeAttr.AttrH264e.bByFrame = TRUE;
pVencChnAttr->VeAttr.AttrH264e.Profile = 2; //1//0:base 1:main 2:high
pVencChnAttr->VeAttr.AttrH264e.mLevel = H264_LEVEL_51;
pVencChnAttr->VeAttr.AttrH264e.PicWidth = stNewDstEncodeSize.Width;
pVencChnAttr->VeAttr.AttrH264e.PicHeight = stNewDstEncodeSize.Height;
pVencChnAttr->VeAttr.AttrH264e.mbPIntraEnable = TRUE;
pVencChnAttr->RcAttr.mRcMode = nNewRcMode;
switch (pVencChnAttr->RcAttr.mRcMode)
{
case VENC_RC_MODE_H264VBR:
{
pVencRcParam->ParamH264Vbr.mMinQp = 10;
pVencRcParam->ParamH264Vbr.mMaxQp = 45;
pVencChnAttr->RcAttr.mAttrH264Vbr.mMaxBitRate = nNewBitRate;
pVencRcParam->ParamH264Vbr.mMaxPqp = 50;
pVencRcParam->ParamH264Vbr.mMinPqp = 10;
pVencRcParam->ParamH264Vbr.mQpInit = 37;
pVencRcParam->ParamH264Vbr.mbEnMbQpLimit = 0;
pVencRcParam->ParamH264Vbr.mMovingTh = 20;
pVencRcParam->ParamH264Vbr.mQuality = 17;
pVencRcParam->ParamH264Vbr.mIFrmBitsCoef = 10;
pVencRcParam->ParamH264Vbr.mPFrmBitsCoef = 10;
break;
}
case VENC_RC_MODE_H264CBR:
{
pVencChnAttr->RcAttr.mAttrH264Cbr.mBitRate = nNewBitRate;
pVencRcParam->ParamH264Cbr.mMaxQp = 35;
pVencRcParam->ParamH264Cbr.mMinQp = 20;
pVencRcParam->ParamH264Cbr.mMaxPqp = 45;
pVencRcParam->ParamH264Cbr.mMinPqp = 22;
pVencRcParam->ParamH264Cbr.mQpInit = 30;
pVencRcParam->ParamH264Cbr.mbEnMbQpLimit = 0;
break;
}
default:
{
aloge("fatal error! wrong rcMode[%d], check code!", pVencChnAttr->RcAttr.mRcMode);
break;
}
}
}
1.12 将属性设置给编码通道
pContext->mstFrameRate.DstFrmRate = nNewDstFrameRate;
AW_MPI_VENC_SetChnAttr(pContext->mVeChn, pVencChnAttr);
AW_MPI_VENC_SetRcParam(pContext->mVeChn, pVencRcParam);
AW_MPI_VENC_SetFrameRate(pContext->mVeChn, &pContext->mstFrameRate);
ret = AW_MPI_VENC_StartRecvPic(pContext->mVeChn);
if (ret != SUCCESS)
{
aloge("fatal error! venc[%d] start fail!", pContext->mVeChn);
}
2.总结
在这个示例中,演示了在编码一段时间后重新设置编码参数,其代码流程为:
- 停止VI和VE通道;
- 重新设置VI和VE通道;
- 重新启动VI和VE通道。