首先开始的时候我们借用一张雷神的图帮助大家理解一下我们今天的操作究竟属于那一步。
从上图可以看出我们要做的,就是将像素层的 YUV 格式,编码出编码层的 h264数据。
首先熟悉一下今天我们要用到的 ffmpeg 中的函数和结构体
- AVFormatContext: 数据文件操作者,主要是用于存储音视频封装格式中包含的信息, 在工程当中占着具足轻重的地位,因为很多函数都要用到它作为参数。同时,它也是我们进行解封装的功能结构体。
- AVOutputFormat: 输出的格式,包括音频封装格式、视频装格式、字幕封装格式,所有封装格式都在
AVCodecID
这个枚举类型上面了 - AVStream: 一个装载着视频/音频流信息的结构体,包括音视频流的长度,元数据信息,其中 index 属性用于标识视频/音频流。
- AVCodecContext: 这个结构体十分庞大,但它的主要是用于编码使用的,结构体中的的
AVCodec *codec
就是编码所采用的编码器器, 当然,这个结构体中要存入视频的基本参数,例如宽高等,存入音频的基本参数,声道,采样率等。 - AVCodec:编码器,设置编码类型,像素格式,视频宽高,fps(每秒帧数), 用于编解码音视频编码层使用。
- AVIOContext:用于管理输入输出结构体。例如解码的情况下,将一个视频文件中的数据先从硬盘中读入到结构体中的 buffer 中,然后送给解码器用于解码,后面我们会用到。
- AVFrame: 结构体一般用于存储原始数据(即非压缩数据,例如对视频来说是YUV,RGB,对音频来说是PCM),此外还包含了一些相关的信息。比如说,解码的时候存储了宏块类型表,QP表,运动矢量表等数据。编码的时候也存储了相关的数据。因此在使用FFMPEG进行码流分析的时候,AVFrame是一个很重要的结构体。
好了,上面就是我们这次解封装用到的结构体的大概解析,那么我们就上代码,好好分析一番。
1、先取个霸气点的函数名,通过输入一个 yuv 文件路径,然后将文件数据进行编码,输出 H264文件。
2、打开输入的 yuv 文件, 并设置我们 h264 文件的输出路径
3、获取 yuv 视频中的信息
// 注册 ffmpeg 中的所有的封装、解封装 和 协议等,当然,你也可用以下两个函数代替
// * @see av_register_input_format()
// * @see av_register_output_format()
av_register_all();
// 用作之后写入视频帧并编码成 h264,贯穿整个工程当中
AVFormatContext* pFormatCtx;
pFormatCtx = avformat_alloc_context();
// 通过这个函数可以获取输出文件的编码格式, 那么这里我们的 fmt 为 h264 格式(AVOutputFormat *)
fmt = av_guess_format(NULL, out_file, NULL);
pFormatCtx->oformat = fmt;
4、将输出文件中的数据读入到程序的 buffer 当中,方便之后的数据写入,也可以说缓存数据写入
// 打开文件的缓冲区输入输出,flags 标识为 AVIO_FLAG_READ_WRITE ,可读写
if (avio_open(&pFormatCtx->pb,out_file, AVIO_FLAG_READ_WRITE) < 0){
printf("Failed to open output file! \n");
return;
}
5、创建流媒体数据,规范流媒体的编码格式,设置视频流的 fps
AVStream* video_st;
// 通过媒体文件控制者获取输出文件的流媒体数据,这里 AVCodec * 写 0 , 默认会为我们计算出合适的编码格式