h264编码基础
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了h264编码基础相关的知识,希望对你有一定的参考价值。
h264编码
h264编码10年屹立,优秀的东西总是一直在发光,相对于RGB和YUV,将数据成几百倍的压缩。
初始化包含进来的数据格式,出去的数据格式,一般我们的数据如果为BGR格式,一个像素是有三个字节组成的,那么一副图像比如720P是有多大呢,12807203 = 2764800 字节 ,一秒钟20帧数 ,那就是 2764800*20 = 55,296,000 字节,也就是一秒钟52M字节,一分钟产生的图像数据为 3120M字节,也就是3G 左右,第一步为转码为YUV420P,这个格式为RGB的一半,经过h264 编码压缩,比如压缩为每秒钟比特率为3Mbit,一分钟也就是180Mbit,字节为22M多,也就是3G 和 22M byte的对比。压缩比到了 141:1。
除了直接用libx264 编码,也可以用ffmpeg,实际上ffmpeg封装了libx264 libx265,甚至像intel Gpu qsv 和 cuda 硬件编码。这里使用软编码作为示例。
ffmpeg的作者之一Bellard 贝拉是一位极其著名优秀的软件工程师,qemu 的作者,同时也出品了quickjs,有兴趣可以看看他写的东西。
1、编码初始化
int Encodeh264::Encoder_Init(AVCodecID codec_id,
int in_w,
int in_h,
int fps, AVPixelFormat src_pix_fmt,
int dst_w,
int dst_h, AVPixelFormat dst_pix_fmt
)
{
//<---------------找到编码器-------------------->
_pCodec = avcodec_find_encoder(codec_id);
if (!_pCodec)
{
printf("Codec not found\\n");
return -1;
}
//<---------------申请编码器上下文-------------------->
_pCodecCtx = avcodec_alloc_context3(_pCodec);
if (!_pCodecCtx)
{
printf("Could not allocate video codec context\\n");
return -1;
}
//编码指定
_pCodecCtx->bit_rate = 200*1000; //这个需要传进来
_pCodecCtx->width = in_w;
_pCodecCtx->height = in_h;
_pCodecCtx->time_base.num = 1;
_pCodecCtx->time_base.den = fps;
_pCodecCtx->framerate.num = fps;
_pCodecCtx->framerate.den = 1;
_pCodecCtx->gop_size = fps;
_pCodecCtx->max_b_frames = 0; //b帧指定为0
_pCodecCtx->pix_fmt = dst_pix_fmt;
//线程指定
_pCodecCtx->thread_count = 2;
av_opt_set(_pCodecCtx->priv_data, "preset", "ultrafast", 0);
av_opt_set(_pCodecCtx->priv_data, "tune", "zerolatency", 0);
av_opt_set(_pCodecCtx->priv_data, "preset", "ultrafast", 0);
_pCodecCtx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
//<--------------打开编码器-------------------->
if (avcodec_open2(_pCodecCtx, _pCodec, NULL) < 0)
{
printf("Could not open codec\\n");
return -1;
}
//<---------------申请src数据封装格式-------------------->
_AVFrame = av_frame_alloc();
if (!_AVFrame)
{
printf("_AVFrame Could mot allocate video frame\\n");
return -1;
}
_AVFrame->format = src_pix_fmt;
_AVFrame->width = in_w;
_AVFrame->height = in_h;
int ret = av_frame_get_buffer(_AVFrame, 0);
if (ret < 0)
{
printf(" _AVFrame Could not allocate the video frame data\\n");
return -1;
}
/* make sure the frame data is writable */
ret = av_frame_make_writable(_AVFrame);
if (ret < 0)
{
printf("_AVFrame set frame writable fail");
return -1;
}
_AVFrame->pts = 0;
//<---------------申请YUV420P数据封装格式-------------------->
_YUVFrame = av_frame_alloc();
if (!_YUVFrame)
{
printf("_YUVFrame Could mot allocate video frame\\n");
return -1;
}
_YUVFrame->format = dst_pix_fmt;
_YUVFrame->width = dst_w;
_YUVFrame->height = dst_h;
ret = av_frame_get_buffer(_YUVFrame, 0);
if (ret < 0)
{
printf("_YUVFrame Could not allocate the video frame data\\n");
return -1;
}
/* make sure the frame data is writable */
ret = av_frame_make_writable(_YUVFrame);
if (ret < 0)
{
printf("_YUVFrame set frame writable fail");
return -1;
}
_YUVFrame->pts = 0;
//<---------------申请编码后数据包-------------------->
_img_converT_ctx = sws_getContext(in_w, in_h, src_pix_fmt, dst_w, dst_h, dst_pix_fmt, SWS_BICUBIC, NULL, NULL, NULL);
return 0;
}
SWS_BICUBIC 这个参数是可以修改的,大变小和小变大可以选择不同的参数,ffmpeg这个开源库做得非常好,算法异常优秀,这个图像转换可以把能把比较模糊和扭曲的图像变得更符合常规,看起来更为自然。
av_opt_set(_pCodecCtx->priv_data, "preset", "ultrafast", 0);
av_opt_set(_pCodecCtx->priv_data, "tune", "zerolatency", 0);
av_opt_set(_pCodecCtx->priv_data, "preset", "ultrafast", 0);
这三句话是用来实时编码用的,可以修改,具体看ffmpeg的帮助。如果存文件,可以将编码从ultrafast 改成 slow等在码率不变的情况下更加清晰。图像的编码首先是把BGR24这种图像转成YUV420P,然后将YUV420P压缩成h26x
编码
AVPacket *Encodeh264::Encode_H264(uint8_t *data)
{
sws_scale(_img_converT_ctx, &data, _AVFrame->linesize, 0, _AVFrame->height, _YUVFrame->data, _YUVFrame->linesize);
int ret = avcodec_send_frame(_pCodecCtx, _YUVFrame);
if (ret < 0)
{
printf("Error sending a frame for encoding :%d \\n", ret);
return NULL;
}
else
{
AVPacket *pkt = av_packet_alloc();
ret = avcodec_receive_packet(_pCodecCtx, pkt);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
av_packet_free(&pkt);
return NULL;
}
else if (ret < 0)
{
av_packet_free(&pkt);
printf("Error during encoding\\n");
return NULL;
}
else
{
//编码完成
_AVFrame->pts++;
_YUVFrame->pts++; /*= v_framenum++;*/
return pkt;
}
}
}
调用
Encodeh264 v_264_obj;
if (v_264_obj.Encoder_Init(AV_CODEC_ID_H264,
CANVAS_WIDTH,
CANVAS_HEIGHT, fps,
AV_PIX_FMT_BGR24,
CANVAS_WIDTH,
CANVAS_HEIGHT, AV_PIX_FMT_YUV420P) != 0)
{
std::cout << "Error ini h264 encoder" << std::endl;
return -1;
}
AVPacket *pkt = v_264_obj.Encode_H264(v_srcImg.data);
不要忘了释放pkt。
以上是关于h264编码基础的主要内容,如果未能解决你的问题,请参考以下文章