基礎(chǔ)知識
AVFormatContext->pb是一個AVIOContext結(jié)構(gòu)體,負(fù)責(zé)IO操作。
一般情況下,我們通過avio_open函數(shù)創(chuàng)建并初始化AVFormatContext->pb;通過avio_closep函數(shù)關(guān)閉AVFormatContext->pb。
1. // 創(chuàng)建AVIOContext,用于讀寫url標(biāo)識的文件
2. int avio_open(AVIOContext **s, const char *url, int flags);
3. // 關(guān)閉AVIOContext資源
4. int avio_closep(AVIOContext **s);
Mux流程:
- avio_open(打開AVIOContext)
- avformat_write_header(寫封裝格式頭信息)
- av_interleaved_write_frame(寫入AVPacket)
- av_write_trailer(寫封裝格式尾信息)
- avio_closep(關(guān)閉AVIOContext)
邊合成邊上傳
所謂邊合成邊上傳,就是在寫文件的同時,通過回調(diào)向外拋出一份Buffer數(shù)據(jù),業(yè)務(wù)側(cè)可以基于Buffer數(shù)據(jù)同步做文件上傳。
核心邏輯就是創(chuàng)建自定義AVIOContext,接管文件IO。
1. // 創(chuàng)建Buffer
2. uint8_t *avio_buffer = (uint8_t*)av_malloc(avio_ctx_buffer_size);
3. // 創(chuàng)建AVIOContext,read_packet函數(shù)負(fù)責(zé)讀buffer,write_packet負(fù)責(zé)寫buffer,seek_user負(fù)責(zé)seek
4. AVIOContext io = avio_alloc_context(avio_buffer, avio_ctx_buffer_size, 1, opaque, &read_packet, &write_packet, &seek_user);
5. io->seekable = AVIO_SEEKABLE_NORMAL;
7. AVFormatContext->pb = io;
Mux時,AVIOContext->write_packet收到Buffer數(shù)據(jù),可以一邊寫文件,一邊向外回調(diào)Buffer,業(yè)務(wù)側(cè)進(jìn)行同步上傳。
下面的48字節(jié),一開始就會輸出,其中ftyp和free box是固定的40字節(jié),最后8字節(jié)表示mdat box header,在所有AVPacket輸出完之后(只有所有AVPacket都輸出完了,才知道m(xù)dat box size),會更新40~43字節(jié)的mdat box size。
1. `ftyp box header`
2. `0, 0, 0, 20, // ftyp box 共32字節(jié)`
3. `66, 74, 79, 70,`
4. `69, 73, 6f, 6d,`
5. `0, 0, 2, 0,`
6. `69, 73, 6f, 6d,`
7. `69, 73, 6f, 32,`
8. `61, 76, 63, 31,`
9. `6d, 70, 34, 31,`
11. `free box header`
12. `0, 0, 0, 8, // free box 共8字節(jié)`
13. `66, 72, 65, 65,`
15. `mdat box header`
16. `0, d, a1, af,`
17. `6d, 64, 61, 74,`
最后生成的Mp4文件,正好可以與前48字節(jié)對應(yīng)起來:

輸出完所有AVPacket之后,調(diào)用av_write_trailer輸出尾部數(shù)據(jù),例如:Mp4的moov box。
調(diào)用av_write_trailer之后,AVIOContext->write_packet首先重寫mdat box size(40~43字節(jié)),然后輸出moov數(shù)據(jù)。
1. `moov box header`
2. `0, 0, 10, 2d, // moov box size`
3. `6d, 6f, 6f, 76,`
5. `// 后續(xù)為moov具體內(nèi)容`
6. `0, 0, 0, 6c,`
7. `6d, 76, 68, 64`
8. `............`
最終生成的Mp4文件,正好與尾部數(shù)據(jù)一致:

總結(jié)
AVIOContext->write_packet函數(shù)首先輸出48字節(jié),包含ftyp box、free box、mdat box header,然后輸出所有的AVPacket,最后調(diào)用av_write_trailer函數(shù)后,AVIOContext->write_packet函數(shù)先調(diào)整40~43字節(jié)的mdat size,然后輸出moov box。
在邊合成邊上傳場景中,可以先緩存48字節(jié)的文件頭和MOOV文件尾,等到所有AVPacket向外回調(diào)之后,再單獨(dú)回調(diào)文件頭Buffer和文件尾Buffer。
因?yàn)闀嬖谛薷囊焉蟼鲾?shù)據(jù)(40~43)的情況,并且上傳SDK并不支持這種操作,所以才分為文件主內(nèi)容(AVPacket)、文件頭和文件尾三部分向外回調(diào)。