@ltlovezh
        
        2021-01-11T08:54:32.000000Z
        字数 1853
        阅读 1410
    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。
// 创建Bufferuint8_t *avio_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);// 创建AVIOContext,read_packet函数负责读buffer,write_packet负责写buffer,seek_user负责seekAVIOContext 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 header0, 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 header0, 0, 0, 8, // free box 共8字节66, 72, 65, 65,mdat box header0, 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 header0, 0, 10, 2d, // moov box size6d, 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)、文件头和文件尾三部分向外回调。
