五、视频编码

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;
                }
            }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值