openh264
OpenH264 是一个开源的 H.264 视频编码库,由 Cisco Systems, Inc. 开发并提供。它支持 H.264
的主要编码特性,包括但不限于:
- 支持基线、主要、高和高10配置文件
- 支持帧内预测、帧间预测、变换编码、量化、环路滤波等
- 支持多线程编码
- 支持可变编码速度
OpenH264库被广泛用于视频编码和转码,特别是在需要高性能和低延迟的场景中。由于它是开源的,许多开发者和公司将其集成到他们的产品中,以提供视频压缩和传输功能。
码率控制
码率控制是数字视频和音频编码中的一个重要概念,它涉及到如何分配和控制数据的传输速率。在视频编码中,码率控制(Rate Control)通常用于确保视频数据在传输或存储时符合特定的带宽限制或文件大小要求。以下是一些常见的码率控制方法:
恒定码率(CBR, Constant Bitrate):以固定的速率编码视频,不管场景的复杂度如何,码率保持不变。
可变码率(VBR, Variable Bitrate):根据视频内容的复杂度动态调整码率,复杂场景使用更高码率,简单场景使用更低码率。
平均码率(ABR, Average Bitrate):介于CBR和VBR之间,设定一个平均码率值,允许在一定范围内波动。
质量可伸缩性(Quality Scalability):在编码时,生成多个质量层的视频流,用户可以根据网络条件选择不同质量的视频。
帧内码率控制(Intra-frame Rate Control):对每一帧进行码率控制,确保每帧的质量。
帧间码率控制(Inter-frame Rate Control):考虑帧与帧之间的关系,通过调整帧之间的码率来控制整体视频质量。
基于场景的码率控制(Scene-based Rate Control):根据视频场景的变化动态调整码率。
基于内容的码率控制(Content-based Rate Control):分析视频内容,如运动、纹理等,来决定码率分配。
码率控制对于视频压缩和传输非常重要,它可以帮助在保持视频质量的同时,减少所需的存储空间和带宽。不同的编码标准和应用场景可能需要不同的码率控制策略。
openh264 码率控制框架
函数调用关系
码控函数框架
码率控制方式
- RC_QUALITY_MODE:质量模式
- RC_BITRATE_MODE:码率模式
- RC_BUFFERBASED_MODE:缓存状态模式
- RC_TIMESTAMP_MODE:基于时间戳模式
- RC_BITRATE_MODE_POST_SKIP:内置码控方式,算法调整后删除
- RC_OFF_MODE:关闭码控
/**
* @brief Enumerate the type of rate control mode
*/
typedef enum {
RC_QUALITY_MODE = 0, ///< quality mode
RC_BITRATE_MODE = 1, ///< bitrate mode
RC_BUFFERBASED_MODE = 2, ///< no bitrate control,only using buffer status,adjust the video quality
RC_TIMESTAMP_MODE = 3, //rate control based timestamp
RC_BITRATE_MODE_POST_SKIP = 4, ///< this is in-building RC MODE, WILL BE DELETED after algorithm tuning!
RC_OFF_MODE = -1, ///< rate control off mode
} RC_MODES;
码控初始化
- 通过
WelsRcInitFuncPointers
函数完成码控模块的初始化;具体根据不同的码控方式,调用不同的函数实现不同的码控逻辑。
void WelsRcInitFuncPointers (sWelsEncCtx* pEncCtx, RC_MODES iRcMode) {
SWelsRcFunc* pRcf = &pEncCtx->pFuncList->pfRc;
switch (iRcMode) {
case RC_OFF_MODE:
pRcf->pfWelsRcPictureInit = WelsRcPictureInitDisable;
pRcf->pfWelsRcPicDelayJudge = NULL;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateDisable;
pRcf->pfWelsRcMbInit = WelsRcMbInitDisable;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateDisable;
pRcf->pfWelsCheckSkipBasedMaxbr = NULL;
pRcf->pfWelsUpdateBufferWhenSkip = NULL;
pRcf->pfWelsUpdateMaxBrWindowStatus = NULL;
pRcf->pfWelsRcPostFrameSkipping = NULL;
break;
case RC_BUFFERBASED_MODE:
pRcf->pfWelsRcPictureInit = WelRcPictureInitBufferBasedQp;
pRcf->pfWelsRcPicDelayJudge = NULL;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateDisable;
pRcf->pfWelsRcMbInit = WelsRcMbInitDisable;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateDisable;
pRcf->pfWelsCheckSkipBasedMaxbr = NULL;
pRcf->pfWelsUpdateBufferWhenSkip = NULL;
pRcf->pfWelsUpdateMaxBrWindowStatus = NULL;
pRcf->pfWelsRcPostFrameSkipping = NULL;
break;
case RC_BITRATE_MODE:
pRcf->pfWelsRcPictureInit = WelsRcPictureInitGom;
pRcf->pfWelsRcPicDelayJudge = NULL;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateGom;
pRcf->pfWelsRcMbInit = WelsRcMbInitGom;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateGom;
pRcf->pfWelsCheckSkipBasedMaxbr = CheckFrameSkipBasedMaxbr;
pRcf->pfWelsUpdateBufferWhenSkip = UpdateBufferWhenFrameSkipped;
pRcf->pfWelsUpdateMaxBrWindowStatus = UpdateMaxBrCheckWindowStatus;
pRcf->pfWelsRcPostFrameSkipping = WelsRcPostFrameSkipping;
break;
case RC_BITRATE_MODE_POST_SKIP:
pRcf->pfWelsRcPictureInit = WelsRcPictureInitGom;
pRcf->pfWelsRcPicDelayJudge = NULL;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateGom;
pRcf->pfWelsRcMbInit = WelsRcMbInitGom;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateGom;
pRcf->pfWelsCheckSkipBasedMaxbr = CheckFrameSkipBasedMaxbr;
pRcf->pfWelsUpdateBufferWhenSkip = UpdateBufferWhenFrameSkipped;
pRcf->pfWelsUpdateMaxBrWindowStatus = UpdateMaxBrCheckWindowStatus;
pRcf->pfWelsRcPostFrameSkipping = WelsRcPostFrameSkipping;
break;
case RC_TIMESTAMP_MODE:
pRcf->pfWelsRcPictureInit = WelsRcPictureInitGom;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateGomTimeStamp;
pRcf->pfWelsRcMbInit = WelsRcMbInitGom;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateGom;
pRcf->pfWelsRcPicDelayJudge = WelsRcFrameDelayJudgeTimeStamp;
pRcf->pfWelsCheckSkipBasedMaxbr = NULL;
pRcf->pfWelsUpdateBufferWhenSkip = NULL;
pRcf->pfWelsUpdateMaxBrWindowStatus = NULL;
pRcf->pfWelsRcPostFrameSkipping = NULL;
break;
case RC_QUALITY_MODE:
default:
pRcf->pfWelsRcPictureInit = WelsRcPictureInitGom;
pRcf->pfWelsRcPicDelayJudge = NULL;
pRcf->pfWelsRcPictureInfoUpdate = WelsRcPictureInfoUpdateGom;
pRcf->pfWelsRcMbInit = WelsRcMbInitGom;
pRcf->pfWelsRcMbInfoUpdate = WelsRcMbInfoUpdateGom;
pRcf->pfWelsCheckSkipBasedMaxbr = CheckFrameSkipBasedMaxbr;
pRcf->pfWelsUpdateBufferWhenSkip = UpdateBufferWhenFrameSkipped;
pRcf->pfWelsUpdateMaxBrWindowStatus = UpdateMaxBrCheckWindowStatus;
pRcf->pfWelsRcPostFrameSkipping = NULL;
break;
}
}
码控模块销毁
- 通过
WelsRcFreeMemory
函数完成码控模块的销毁逻辑。
void WelsRcFreeMemory (sWelsEncCtx* pEncCtx) {
SWelsSvcRc* pWelsSvcRc = NULL;
int32_t i = 0;
for (i = 0; i < pEncCtx->pSvcParam->iSpatialLayerNum; i++) {
pWelsSvcRc = &pEncCtx->pWelsSvcRc[i];
RcFreeLayerMemory (pWelsSvcRc, pEncCtx->pMemAlign);
}
}
码控模块结构体
SRCTemporal
结构体主要用于时域码率控制
typedef struct TagRCTemporal {
int32_t iMinBitsTl;
int32_t iMaxBitsTl;
int32_t iTlayerWeight;
int32_t iGopBitsDq;
//P frame level R-Q Model
int64_t iLinearCmplx; // *INT_MULTIPLY
int32_t iPFrameNum;
int64_t iFrameCmplxMean;
int32_t iMaxQp;
int32_t iMinQp;
} SRCTemporal;
SWelsSvcRc
结构体主要用于帧级码率控制。
typedef struct TagWelsRc {
int32_t iRcVaryPercentage;
int32_t iRcVaryRatio;
int32_t iInitialQp; //initial qp
int64_t iBitRate; // Note: although the max bit rate is 240000*1200 which can be represented by int32, but there are many multipler of this iBitRate in the calculation of RC, so use int64 to avoid type conversion at all such places
int32_t iPreviousBitrate;
int32_t iPreviousGopSize;
double fFrameRate;
int32_t iBitsPerFrame;
int32_t iMaxBitsPerFrame;
double dPreviousFps;
// bits allocation and status
int32_t iRemainingBits;
int32_t iBitsPerMb;
int32_t iTargetBits;
int32_t iCurrentBitsLevel;//0:normal; 1:limited; 2:exceeded.
int32_t iIdrNum;
int64_t iIntraComplexity; //255*255(MaxMbSAD)*36864(MaxFS) make the highest bit of 32-bit integer 1
int32_t iIntraMbCount;
int64_t iIntraComplxMean;
int8_t iTlOfFrames[VGOP_SIZE];
int32_t iRemainingWeights;
int32_t iFrameDqBits;
bool bGomRC;
double* pGomComplexity;
int32_t* pGomForegroundBlockNum;
int32_t* pCurrentFrameGomSad;
int32_t* pGomCost;
int32_t bEnableGomQp;
int32_t iAverageFrameQp;
int32_t iMinFrameQp;
int32_t iMaxFrameQp;
int32_t iNumberMbFrame;
int32_t iNumberMbGom;
int32_t iGomSize;
int32_t iSkipFrameNum;
int32_t iFrameCodedInVGop;
int32_t iSkipFrameInVGop;
int32_t iGopNumberInVGop;
int32_t iGopIndexInVGop;
int32_t iSkipQpValue;
int32_t iQpRangeUpperInFrame;
int32_t iQpRangeLowerInFrame;
int32_t iMinQp;
int32_t iMaxQp;
//int32_t delta_adaptive_qp;
int32_t iSkipBufferRatio;
int32_t iQStep; // *INT_MULTIPLY
int32_t iFrameDeltaQpUpper;
int32_t iFrameDeltaQpLower;
int32_t iLastCalculatedQScale;
//for skip frame and padding
int32_t iBufferSizeSkip;
int64_t iBufferFullnessSkip;
int64_t iBufferMaxBRFullness[TIME_WINDOW_TOTAL];//0: EVEN_TIME_WINDOW; 1: ODD_TIME_WINDOW
int32_t iPredFrameBit;
bool bNeedShiftWindowCheck[TIME_WINDOW_TOTAL];
int32_t iBufferSizePadding;
int32_t iBufferFullnessPadding;
int32_t iPaddingSize;
int32_t iPaddingBitrateStat;
bool bSkipFlag;
int32_t iContinualSkipFrames;
SRCTemporal* pTemporalOverRc;
//for scc
int64_t iAvgCost2Bits;
int64_t iCost2BitsIntra;
int32_t iBaseQp;
long long uiLastTimeStamp;
//for statistics and online adjustments
int32_t iActualBitRate; // TODO: to complete later
float fLatestFrameRate; // TODO: to complete later
} SWelsSvcRc;
SRCSlicing
主要用于片级slice 的码率控制。
// slice level rc statistic info
typedef struct TagRCSlicing {
int32_t iComplexityIndexSlice;
int32_t iCalculatedQpSlice;
int32_t iStartMbSlice;
int32_t iEndMbSlice;
int32_t iTotalQpSlice;
int32_t iTotalMbSlice;
int32_t iTargetBitsSlice;
int32_t iBsPosSlice;
int32_t iFrameBitsSlice;
int32_t iGomBitsSlice;
int32_t iGomTargetBits;
//int32_t gom_coded_mb;
} SRCSlicing;