1. 视频编码原理
视频编码原理,详见音视频开发基础
2. AVPacket
AVPacket结构体,详见AVpacket结构体
3. x264 / x265编码参数设置
x264、x265编码器码率控制:ABR、CQB、CBR、CBF、VBV
详见码率控制
#include <iostream>
#include <fstream>
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavutil/avutil.h>
#include <libavutil/opt.h>
}
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avutil.lib")
using namespace std;
int main()
{
ofstream ofs;
ofs.open("400_300_25_yuv420p_preset.h264", ios::binary);
int width = 400;
int height = 300;
auto pix_fmt = AV_PIX_FMT_YUV420P;
/********************打开编码器*********************/
char buf[1024] = { 0 };
//查找编码器
//avcodec_find_encoder_by_name("libx264");
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
if (!codec)
{
cerr << "find encoder Faild!" << endl;
return -1;
}
//创建上下文
AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
if (!codec_ctx)
{
cerr << "alloc_context3 Faild!" << endl;
return -1;
}
codec_ctx->width = width;
codec_ctx->height = height;
codec_ctx->pix_fmt = pix_fmt;
codec_ctx->time_base = { 1, 25 };
codec_ctx->thread_count = 32;
/*编码器参数*/
int ret = 1;
//codec_ctx->max_b_frames = 10; //设置b帧数量
//int ret = av_opt_set(codec_ctx->priv_data, "preset", "ultrafast", 0); //设置快速编码
//int ret = av_opt_set(codec_ctx->priv_data, "tune", "zerolatency", 0); //设置效果零延时
帧率的控制
ABR平均帧率
//codec_ctx->bit_rate = 400000;
CQB恒定质量
//ret = av_opt_set_int(codec_ctx->priv_data, "qp", 1, 0);
CBR恒定比特率:MP$不支持,输出文件必须为MPEG2 ts
//ret = av_opt_set(codec_ctx->priv_data, "nal-hrd", "cbr", 0);
//codec_ctx->rc_min_rate = 400000;
//codec_ctx->rc_max_rate = 400000;
//codec_ctx->rc_buffer_size = 400000 * 2;
//codec_ctx->bit_rate = 400000;
//CFR恒定速率因子与VBV约束编码配合使用
ret = av_opt_set_int(codec_ctx->priv_data, "cfr", 23, 0);
//VBF
codec_ctx->rc_max_rate = 400000;
codec_ctx->rc_buffer_size = 400000 * 2;
if (ret != 0)
{
cerr << "Failed to set opt" << endl;
}
//打开编码器
ret = avcodec_open2(codec_ctx, codec, nullptr);
if (ret)
{
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to open codec" << buf << endl;
return -1;
}
/********************创建数据并进行编解码*********************/
//创建数据
AVFrame* frame = av_frame_alloc();
frame->width = width;
frame->height = height;
frame->format = pix_fmt;
ret = av_frame_get_buffer(frame, 0);
if (ret)
{
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to open codec" << buf << endl;
return -1;
}
AVPacket* packet = av_packet_alloc();
for (int t = 0; t < 250; t++)
{
for (int i = 0; i < frame->height; i++)
{
for (int j = 0; j < frame->width; j++)
{
frame->data[0][frame->width * i + j] = i + j + t * 3; //Y分量
if ((i < (frame->height / 2)) && (j < (frame->width / 2)))
{
frame->data[1][frame->width / 2 * i + j] = 128 + i + t * 2; //u分量
frame->data[2][frame->width / 2 * i + j] = 64 + j + t * 5; //v分量
}
}
}
frame->pts = t;
/********************进行编解码*********************/
//发送数据
ret = avcodec_send_frame(codec_ctx, frame);
if (ret)
{
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to send_frame" << buf << endl;
return -1;
}
//接收数据
while (true)
{
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret)
{
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
{
break;
}
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to receive_packet" << buf << endl;
return -1;
}
cout << packet->size << " " << flush;
//写入到文件
ofs.write((char *)packet->data, packet->size);
av_packet_unref(packet);
}
}
//去除缓冲区数据
ret = avcodec_send_frame(codec_ctx, NULL);
if (ret)
{
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to send_frame" << buf << endl;
return -1;
}
//接收数据
while (true)
{
ret = avcodec_receive_packet(codec_ctx, packet);
if (ret)
{
if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
{
break;
}
av_strerror(ret, buf, sizeof(buf) - 1);
cerr << "Faild to receive_packet" << buf << endl;
return -1;
}
cout << packet->size << " " << flush;
//写入到文件
ofs.write((char *)packet->data, packet->size);
av_packet_unref(packet);
}
}
ofs.close();
avcodec_close(codec_ctx);
avcodec_free_context(&codec_ctx);
av_frame_free(&frame);
av_packet_free(&packet);
return 0;
}
4. H264裸流分析
H264裸流分析,详见H264 Header、H264协议格式
一个packet中可能包含多帧数据。通过startcode根据nalu header中的sps来判断当前帧的类型
/// 分析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
int nal_unit_type = 0;
unsigned char nal_head = *(pkt->data + 4); //+4 去掉开头的0001
nal_unit_type = nal_head & 0x1f;//取后五位 0001 1111
cout << nal_unit_type << " " << flush;
for (int i = 4; i < pkt->size - 4; i++) //一个data中由多条nalu
{
if (pkt->data[i] == 0 &&
pkt->data[i + 1] == 0 &&
pkt->data[i + 2] == 1) { ///001
nal_unit_type = pkt->data[i + 3] & 0x1f;
cout << "(" << nal_unit_type << ")" << flush;
}
}