7-9_动态配置帧率和码率

动态配置帧率和码率

由于在编码的码流常会使用网络进行传输,但由于网络容易产生波动,有可能会导致丢帧、编码质量较差等情况,为解决由于网络波动影响的码流传输问题,可以使用动态编码,即当网络环境较差时,使用较低的码率进行码流传输。

这一小节我们讲解如何在编码过程中调整编码的分辨率、帧率、码率等。

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.总结

在这个示例中,演示了在编码一段时间后重新设置编码参数,其代码流程为:

  1. 停止VI和VE通道;
  2. 重新设置VI和VE通道;
  3. 重新启动VI和VE通道。