@ltlovezh
2021-01-11T16:54:32.000000Z
字数 1853
阅读 1151
ffmpeg
AVFormatContext->pb
是一个AVIOContext结构体,负责IO操作。
一般情况下,我们通过avio_open函数创建并初始化AVFormatContext->pb;通过avio_closep函数关闭AVFormatContext->pb。
// 创建AVIOContext,用于读写url标识的文件
int avio_open(AVIOContext **s, const char *url, int flags);
// 关闭AVIOContext资源
int avio_closep(AVIOContext **s);
Mux流程:
所谓边合成边上传,就是在写文件的同时,通过回调向外抛出一份Buffer数据,业务侧可以基于Buffer数据同步做文件上传。
核心逻辑就是创建自定义AVIOContext,接管文件IO。
// 创建Buffer
uint8_t *avio_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
// 创建AVIOContext,read_packet函数负责读buffer,write_packet负责写buffer,seek_user负责seek
AVIOContext io = avio_alloc_context(avio_buffer, avio_ctx_buffer_size, 1, opaque, &read_packet, &write_packet, &seek_user);
io->seekable = AVIO_SEEKABLE_NORMAL;
AVFormatContext->pb = io;
Mux时,AVIOContext->write_packet
收到Buffer数据,可以一边写文件,一边向外回调Buffer,业务侧进行同步上传。
下面的48字节,一开始就会输出,其中ftyp和free box是固定的40字节,最后8字节表示mdat box header,在所有AVPacket输出完之后(只有所有AVPacket都输出完了,才知道mdat box size),会更新40~43字节的mdat box size。
ftyp box header
0, 0, 0, 20, // ftyp box 共32字节
66, 74, 79, 70,
69, 73, 6f, 6d,
0, 0, 2, 0,
69, 73, 6f, 6d,
69, 73, 6f, 32,
61, 76, 63, 31,
6d, 70, 34, 31,
free box header
0, 0, 0, 8, // free box 共8字节
66, 72, 65, 65,
mdat box header
0, d, a1, af,
6d, 64, 61, 74,
最后生成的Mp4文件,正好可以与前48字节对应起来:
输出完所有AVPacket之后,调用av_write_trailer输出尾部数据,例如:Mp4的moov box。
调用av_write_trailer之后,AVIOContext->write_packet首先重写mdat box size(40~43字节),然后输出moov数据。
moov box header
0, 0, 10, 2d, // moov box size
6d, 6f, 6f, 76,
// 后续为moov具体内容
0, 0, 0, 6c,
6d, 76, 68, 64
............
最终生成的Mp4文件,正好与尾部数据一致:
AVIOContext->write_packet
函数首先输出48字节,包含ftyp box、free box、mdat box header,然后输出所有的AVPacket,最后调用av_write_trailer
函数后,AVIOContext->write_packet
函数先调整40~43字节的mdat size,然后输出moov box。
在边合成边上传场景中,可以先缓存48字节的文件头和MOOV文件尾,等到所有AVPacket向外回调之后,再单独回调文件头Buffer和文件尾Buffer。
因为会存在修改已上传数据(40~43)的情况,并且上传SDK并不支持这种操作,所以才分为文件主内容(AVPacket)、文件头和文件尾三部分向外回调。