欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

FFmpeg 视频编码

程序员文章站 2022-07-14 18:40:40
...

FFmpeg 视频编码

一、什么是视频编码?

视频编码的主要作用是将视频像素数据(RGB,YUV等)压缩成为视频码流,从而降低视频的数据量。如果视频不经过压缩编码的话,体积通常是非常大的,一部电影可能就要上百G的空间。视频编码是视音频技术中最重要的技术之一。视频码流的数据量占了视音频总数据量的绝大部分。高效率的视频编码在同等的码率下,可以获得更高的视频质量。

二、FFmpeg开发中的视频编码流程(rgb–>mp4)

这图绝了
FFmpeg 视频编码
1.创建解码器

//1、创建编码器
AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *c = avcodec_alloc_context3(codec);
//打开编码器、关联编码器上下文
int ret = avcodec_open2(c, codec, NULL);

在这一步中我们需要给解码器上下文数据赋值:
c->bit_rate = 4000000; //压缩比特率
c->width = width;
c->height = height;
c->time_base = { 1, fps };
c->framerate = { fps,1 };
c->gop_size = 50; //每50帧有一个关键帧,画面组大小
c->max_b_frames = 0; //b帧数量
c->pix_fmt = AV_PIX_FMT_YUV420P;
c->codec_id = AV_CODEC_ID_H264; //编码器id
c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; //全局的编码信息
c->thread_count = 8; //线程数

2.创建输出上下文空间

//2、输出上下文
AVFormatContext *oc = NULL;
//为输出格式开辟上下文空间
avformat_alloc_output_context2(&oc, 0, 0, outfile);
  • 开辟的AVFormatContext空间需要在7中释放!

3.向输出上下文空间空间中添加视频流信息

//3、添加视频流信息
AVStream *st = avformat_new_stream(oc, NULL);
//将解码器上下文中的流信息拷贝入AVStream
avcodec_parameters_from_context(st->codecpar, c);
av_dump_format(oc, 0, outfile, 1);  //打印

我们需要传入的流信息:
st->id = 0;
st->codecpar->codec_tag = 0;

4.创建将rgb数据转换为yuv数据的空间

//4、rgb -》yuv 装换
SwsContext* ctx = NULL;
ctx = sws_getCachedContext(ctx,        //更新
	width, height, AV_PIX_FMT_BGRA,    //输入
	width, height, AV_PIX_FMT_YUV420P, //输出
	SWS_BICUBIC, //转换算法
	NULL, NULL, NULL
	);  //多次调用时如果相同则不会重复创建
//输入的空间
unsigned char* rgb = new unsigned char[width*height * 4];
//输出的空间
AVFrame* yuv = av_frame_alloc();
ret = av_frame_get_buffer(yuv, 32);
  • 视频图像重采样空间ctx、yuv和rgb的储存空间均需要释放!

5.将头信息写入输出文件(.mp4)

//5、write MP4 head输入头信息
ret = avio_open(&oc->pb, outfile, AVIO_FLAG_WRITE);
ret = avformat_write_header(oc, NULL);

5.逐帧将rgb数据转换为yuv数据,并进行编码

for(;;)
{
	int len = fread(rgb, 1, width*height * 4, fp);
	if (len <= 0) break;
	uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
	indata[0] = rgb;
	int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
	inlinesize[0] = width * 4; //一行字节数大小
	int h = sws_scale(ctx, indata, inlinesize, 0, height,
	yuv->data, yuv->linesize
	); //返回输出height

上述部分可能略微晦涩,请参见FFmpeg视频图像重采样(rgb转yuv).

	//将yuv数据发送进编码器上下文进行编码
	ret = avcodec_send_frame(c, yuv);
	AVPacket pkt;
	av_init_packet(&pkt);
	//将编码器上下文中已成功编码的数据发送至AVPacket
	ret = avcodec_receive_packet(c, &pkt);
	av_write_frame(oc, &pkt);
	//减引用计数
	av_packet_unref(&pkt);
	//av_interleaved_write_frame(oc, &pkt); //按dts排序
}
  • 开辟的AVPacket空间需要在7中释放!

6.写入视频索引

//写入视频索引
av_write_trailer(oc);

7.删除或关闭上述操作中开辟的空间

//关闭视频输出io
avio_close(oc->pb);
//清理封装输出上下文
avformat_free_context(oc);
//关闭编码器
avcodec_close(c);
//清理编码器上下文
avcodec_free_context(&c);
//清理视频重采样上下文
sws_freeContext(ctx);
//释放rgb空间
delete[] rgb;

注:理解FFmpeg中的结构体和函数并不容易,可以参见博主其他文章。
链接: FFmpeg 音视频解封装.
链接: FFmpeg 音视频解码.
链接: FFmpeg 内存模型.
链接: FFmpeg 结构体关系分析.

相关标签: 音视频技术