AVIOContext和边合成边上传

982 阅读2分钟

基础知识

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流程:

  1. avio_open(打开AVIOContext)
  2. avformat_write_header(写封装格式头信息)
  3. av_interleaved_write_frame(写入AVPacket)
  4. av_write_trailer(写封装格式尾信息)
  5. avio_closep(关闭AVIOContext)

边合成边上传

所谓边合成边上传,就是在写文件的同时,通过回调向外抛出一份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字节对应起来: ftyp

输出完所有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文件,正好与尾部数据一致: moov