openh264动态调整码率
正⽂
openh264 提供动态码率调整功能,可以通过ISVCEncoder::setOption()实现, 如下所⽰,更改了编码视频空域层的码率;
SEncParamExt encParamExt;
encoder->GetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &encParamExt);
encParamExt.sSpatialLayers[0].iSpatialBitrate =  100 * 1000;
encParamExt.sSpatialLayers[0].iMaxSpatialBitrate =  100 * 1000;
encoder->SetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &encParamExt);
直接设置编码器的bitrate没有效果,需要改变空域层bitrate,实测发现变更的码率设置的太低会导致⼀些帧被过滤掉,完整代码如下(bufferReader没有贴上来),#include <iostream>
#include <wels/codec_api.h>
#include <wels/codec_app_def.h>
#include <wels/codec_def.h>
#include <wels/codec_ver.h>
#include <assert.h>
#include <string.h>
#include "bufferReader.h"
#include <stdio.h>
struct Args{
int picHeight= 288;
int picWidth = 352;
const char* srcFile =  "../res/soccor.yuv";
const char* dstFile =  "../res/output.264";
};
void InitParam(SEncParamBase& param, const int picWidth, const int picHeight){
memset(¶m, 0, sizeof(SEncParamBase));
param.iUsageType = CAMERA_VIDEO_REAL_TIME;
param.fMaxFrameRate = 50;
param.iPicWidth = picWidth;
param.iPicHeight = picHeight;
param.iTargetBitrate = 900 * 1000;
param.iRCMode = RC_BITRATE_MODE;
}
void  SetupEncoder(ISVCEncoder*& encoder, const SEncParamBase& param){
int rv = WelsCreateSVCEncoder(&encoder);
assert(0 == rv);
assert(encoder != NULL);
assert(encoder->Initialize(¶m) == 0);
//changing logging granularity
int g_LevelSetting = 1;
encoder->SetOption(ENCODER_OPTION_TRACE_LEVEL, &g_LevelSetting);
int videoFormat = videoFormatI420;
encoder->SetOption(ENCODER_OPTION_DATAFORMAT, &videoFormat);
//changing encoding param
/
/encoder->SetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_BASE, ¶m);
}
void SetupPic(unsigned char* data, SFrameBSInfo& info, SSourcePicture& pic, const int& picHeight, const int& picWidth){
memset(&info, 0, sizeof(SFrameBSInfo));
memset(&pic, 0, sizeof(SSourcePicture));
pic.iPicWidth = picWidth;
pic.iPicHeight = picHeight;
pic.iColorFormat = videoFormatI420;
pic.iStride[0] = pic.iPicWidth;
pic.iStride[1] = pic.iStride[2] = pic.iPicWidth >> 1;
pic.pData[0] = data;
pic.pData[1] = pic.pData[0] + picWidth * picHeight;
pic.pData[2] = pic.pData[1] + (picWidth * picHeight >> 2);
}
void Encode(ISVCEncoder* encoder, const Args& args){
const char* file_name = args.srcFile;
sshsu::BufferReader buf(args.picWidth, args.picHeight, file_name);
FILE* outFd = fopen(args.dstFile, "w+");
assert(outFd!= nullptr);
SFrameBSInfo info;
SSourcePicture pic;
SetupPic(buf.Data(), info, pic, args.picHeight, args.picWidth);
SEncParamExt encParamExt;
encoder->GetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &encParamExt);
int frame_num = 0;
int total_bytes = 0;
while(buf.ReadFrame()){
if(frame_num == 100){
std::cout<<"change bitrate to 100kb/s"<<std::endl;
encParamExt.sSpatialLayers[0].iSpatialBitrate =  100 * 1000;
encParamExt.sSpatialLayers[0].iMaxSpatialBitrate =  100 * 1000;
encoder->SetOption(ENCODER_OPTION_SVC_ENCODE_PARAM_EXT, &encParamExt);
}
int rv = encoder->EncodeFrame(&pic, &info);
assert(rv == cmResultSuccess);
if (info.eFrameType != videoFrameTypeSkip)
{
int iLayerNum = info.iLayerNum;
for(int i = 0;i<iLayerNum;++i){
SLayerBSInfo &layer = info.sLayerInfo[i];
int iLayerSize = 0;
int iLNalIdx = layer.iNalCount - 1;
do
{
iLayerSize += layer.pNalLengthInByte[iLNalIdx];
fopen和open区别
} while (--iLNalIdx >= 0);
fwrite(layer.pBsBuf, 1, iLayerSize, outFd);
++frame_num;
total_bytes += iLayerSize;
std::cout << "save frame: " << frame_num << ", size:" << iLayerSize << std::endl;
}
}
else{
std::cout<<"videoFrameTypeskip for rate control"<<std::endl;
}
}
std::cout << "save frame to file: \"" <<args.dstFile << "\", total bytes: " << total_bytes <<std::endl;
fflush(outFd);
fclose(outFd);
}
int main(int, char**) {
Args args;
SEncParamBase encoderParam;
InitParam(encoderParam, args.picWidth, args.picHeight);
ISVCEncoder* encoder;
SetupEncoder(encoder, encoderParam);
Encode(encoder, args);
encoder->Uninitialize();
WelsDestroySVCEncoder(encoder);
return 0;
}
通过elecard streameye去看调整前后调整后的图像,分明可以看到⼤⼩不⼀样, 以下是将码率设置成500kb/s然后调整到250kb/s的结果,量化步长变⼤了,编码过程中有⼀些丢帧,可能将很多intra-block改成了inter-block