H264编码介绍和参数设置

发布时间:2025-12-09 19:23:54 浏览次数:5

视频h264编码流程

宏块划分 Macro Block

帧内预测 I帧

帧间预测和GOP P帧和B帧 

DCT离散余弦变换 

量化 

熵编码 

preset 预设编码器 

预设编码器 

比特率控制 

恒定比特率(CBR) 

恒定速率因子(CRF) 

H264码流中NALU sps pps IDR帧的分析 

NALU的类型

SPS 

 

PPS

代码 

114_preset_encoder.cpp

#include <iostream>#include <fstream>using namespace std;extern "C" // 指定函数是 C 语言函数,函数目标名不包含重载标识,C++ 中调用 C 函数需要使用 extern "C"{// 引用 ffmpeg 头文件#include "libavcodec/avcodec.h"#include "libavutil/opt.h"}// 预处理指令导入库#pragma comment(lib, "avcodec.lib")#pragma comment(lib, "avutil.lib")int main(int argc, char* argv[]){// 1 找到编码器 AV_CODEC_ID_HEVC(H265)AVCodec* codec = nullptr;AVFrame* frame = nullptr;AVPacket* packet = nullptr;AVCodecID codec_id = AV_CODEC_ID_H264;ofstream ofs;string file_name = "400_300_25_preset";int r = 0;if (argc > 1){string suffix = argv[1];cout << "suffix = " << suffix << endl;if ((suffix == "h265") || (suffix == "hevc")){codec_id = AV_CODEC_ID_HEVC;}}if (codec_id == AV_CODEC_ID_H264){file_name += ".h264";}else if (codec_id == AV_CODEC_ID_HEVC){file_name += ".h265";}ofs.open(file_name, ios::out | ios::binary);if (!ofs){cout << "open " << file_name << " failed!" << endl;return -1;}codec = avcodec_find_encoder(codec_id);if (codec == nullptr){cout << "avcodec_find_encoder failed!" << endl;return -1;}// 2 编码上下文AVCodecContext* context = avcodec_alloc_context3(codec);if (context == nullptr){cout << "avcodec_alloc_context3 failed!" << endl;return -1;}// 3 设定上下文参数context->width = 400; //视频宽高context->height = 300;context->pix_fmt = AV_PIX_FMT_YUV420P; // 源数据像素格式,与编码算法相关context->time_base = { 1, 25 }; // 帧时间戳的时间单位 pts*time_base = 播放时间(秒) 分数 1/25context->thread_count = 16; // 编码线程数,可以通过调用系统接口获取cpu核心数量// context->max_b_frames = 0; // B帧设为0 降低延时,增大空间//r = av_opt_set(context->priv_data, "preset", "ultrafast", 0); // 最快速度if (r != 0){char buf[1024] = { 0 };av_strerror(r, buf, sizeof(buf) - 1);cout << "av_opt_set preset failed! " << buf << endl;avcodec_free_context(&context);return -1;}//r = av_opt_set(context->priv_data, "tune", "zerolatency", 0); // 零延时 h265不支持b_frame////// ABR 平均比特率//int br = 500000; //400kb//c->bit_rate = br;/////CQP 恒定质量 H.264中的QP范围从0到51 // x264默认 23 效果较好18// x265默认28 效果较好25// av_opt_set_int(context->priv_data, "qp", 18, 0);//// 恒定比特率(CBR) 由于MP4不支持NAL填充,因此输出文件必须为(MPEG-2 TS)/*context->rc_min_rate = br;context->rc_max_rate = br;context->rc_buffer_size = br;context->bit_rate = br;av_opt_set(context->priv_data, "nal-hrd", "cbr", 0);*/if (r != 0){char buf[1024] = { 0 };av_strerror(r, buf, sizeof(buf) - 1);cout << "av_opt_set tune failed! " << buf << endl;avcodec_free_context(&context);return -1;}context->gop_size = 6;// 4 打开编码上下文r = avcodec_open2(context, codec, nullptr);if (r != 0){char buf[1024] = { 0 };av_strerror(r, buf, sizeof(buf) - 1);cout << "avcodec_open2 failed! " << buf << endl;avcodec_free_context(&context);return -1;}// 创建好AVFrame空间 未压缩数据frame = av_frame_alloc();frame->width = context->width;frame->height = context->height;frame->format = context->pix_fmt;r = av_frame_get_buffer(frame, 0);if (r != 0){char buf[1024] = { 0 };av_strerror(r, buf, sizeof(buf) - 1);cout << "av_frame_get_buffer failed! " << buf << endl;avcodec_free_context(&context);return -1;}packet = av_packet_alloc();/* 十秒视频 250帧 */for (int i = 0; i < 250; i++){// 生成AVFrame 数据 每帧数据不同// Yfor (int h = 0; h < frame->height; h++){for (int w = 0; w < frame->width; w++){frame->data[0][frame->linesize[0] * h + w] = h + w + i * 3;}}// UVfor (int h = 0; h < frame->height / 2; h++){for (int w = 0; w < frame->width / 2; w++){frame->data[1][frame->linesize[1] * h + w] = 128 + h + i * 2;frame->data[2][frame->linesize[2] * h + w] = 64 + w + i * 5;}}frame->pts = i; // 显示的时间r = avcodec_send_frame(context, frame);if (r != 0){break;}while (r >= 0) // 返回多帧{// 接收压缩帧,一般前几次调用返回空(缓冲,立刻返回,编码未完成)// 编码是在独立的线程中// 每次调用会重新分配packet中的空间r = avcodec_receive_packet(context, packet);if ((r == AVERROR(EAGAIN)) || (r == AVERROR_EOF)){break;}else if (r < 0){char buf[1024] = { 0 };av_strerror(r, buf, sizeof(buf) - 1);cout << "avcodec_receive_packet failed! " << buf << endl;avcodec_free_context(&context);break;}// cout << packet->size << " " << flush;ofs.write((char*)packet->data, packet->size);/// 分析NALU/// 一个AVPacket中包含多个NALU 以0001间隔,多个是以001间隔/ 0001[NAL_HEAD][NAL_HEAD] //1个字节 orbidden_bit(1bit),nal_reference_bit(2bits)(优先级),/// nal_unit_type(5bits)/// 1:非IDR图像中不采用数据划分的片段/// 5:IDR图像的片段/// 6:补充增强信息(SEI)/// 7:序列参数集 / SPS/// 8:图像参数集 / PPSint nal_unit_type = 0;char nal_head = *(packet->data + 4); // +4 去掉开头的0001nal_unit_type = nal_head & 0x1f;cout << nal_unit_type << " " << flush;for (int i = 4; i < packet->size - 3; i++) // 一个data中由多条nalu{if ((packet->data[i] == 0) && (packet->data[i + 1] == 0) && (packet->data[i + 2] == 1)){nal_unit_type = packet->data[i + 3] & 0x1f;cout << "(" << nal_unit_type << ")" << " " << flush;}}av_packet_unref(packet);}}ofs.close();av_packet_free(&packet);av_frame_free(&frame);avcodec_free_context(&context); // 释放编码器上下文return 0;}

我们来查看每个NALU中的类型。

运行结果如下所示:

 

需要做网站?需要网络推广?欢迎咨询客户经理 13272073477