一. 创建上下文
1.1 概述
解封装是直接调用avformat_open_input()
函数就生成了一个上下文,但是封装却需要创建一个上下文。因为有这样一个区别,在解封装过程中,上下文中有很多信息是由FFmpeg
的接口填入的,但是如果是封装的话,很多信息需要我们自己填入(毕竟FFmpeg
不知道你最终想要生成的视频的具体参数是什么)。FFmpeg
提供了一个函数avformat_alloc_output_context2()
用于创建此上下文,当上下文创建完毕之后,我们需要自行设定上下文参数。
1.2 需要用到的接口说明
// 创建封装的上下文
int avformat_alloc_output_context2(AVFormatContext **ctx, // 生成的封装的上下文(可以看到它是一个二级指针,本身就是用做赋值用的)
ff_const59 AVOutputFormat *oformat, // 输出的格式指定,一般传NULL, 因为输出的格式可以直接通过文件名后缀进行指定
const char *format_name, // 输出的格式名称,一般传NULL,也是可以通过文件名后缀进行指定
const char *filename); // 最终输出的文件名,比如abc.mp4, 那FFmpeg就会以mp4的封装格式进行创建
二. 添加音频视频流
2.1 概述
在上下文中插入音频或视频流信息。在AVFormatContext
的streams[]
数组中插入音频或视频流信息。
2.2 需要用到的接口说明
// 添加流信息
AVStream *avformat_new_stream(AVFormatContext *s, // 上面创建的封装的上下文
const AVCodec *c); // 指定编码音频或视频时所用的编码器对象
注意:new stream是有次序的,可以看到上述接口并没有需要传入索引号,那第一个new的stream,它的索引号就是0
,第二个new的stream,它的索引号就是1
,尽量使用通用的方式,比如0
代表video
, 1
代表audio
,这样可以使一些播放器能够兼容(有些播放器没有去区分流的信息是音频还是视频, 默认它们就以索引号为0的当做视频,索引号为1的当做音频)。
另外,这个新new的AVStream
的空间不需要管理,因为它是关联在AVFormatContext
对象当中,当AVFormatContext
在做清理的时候,会把它也给清理掉。
三. 打开输出IO
3.1 概述
不管你是想在本地生成一个视频文件,还是想通过rtmp
进行推流,都需要指定一个具体的输出对象。如果是推流的话,则是通过网络接口往外发,如果是生成文件的话,则需要打开输入输出的IO。
3.2 需要用到的接口说明
// 打开输出的IO
int avio_open(AVIOContext **s, // 封装器的IO上下文,它是AVFormatContext的成员 pb
const char *url, // 打开的地址,就是输出的文件路径,前面也传了一个filename,不过那个filename主要是用于做格式的判断
int flags); // 涉及IO操作的FLAG,如果是写文件的话,可以传 AVIO_FLAG_WRITE
// 实际调用
avio_open(&c->pb, url, AVIO_FLAG_WRITE);
// 关闭封装器的IO上下文
int avio_closep(AVIOContext **s); // 实测, AVFormatContext在清理的时候