ffmpeg 從內存中讀取數據(或將數據輸出到內存)
更新記錄(2014.7.24):
1.為了使本文更通俗易懂,更新了部分內容,將例子改為從內存中打開。
2.增加了將數據輸出到內存的方法。
從內存中讀取數據
ffmpeg一般情況下支持打開一個本地文件,例如“C:\test.avi”
或者是一個流媒體協議的URL,例如“rtmp://222.31.64.208/vod/test.flv”
其打開文件的函數是avformat_open_input(),直接將文件路徑或者流媒體URL的字符串傳遞給該函數就可以了。
但其是否支持從內存中讀取數據呢?這個問題困擾了我很長時間。當時在做項目的時候,通過Winpcap抓取網絡上的RTP包,打算直接送給ffmpeg進行解碼。一直沒能找到合適的方法。因為抓取的數據包是存在內存中的,所以無法傳遞給avformat_open_input()函數其路徑(根本沒有路徑= =)。當然也可以將抓取的數據報存成文件,然后用ffmpeg打開這個文件,但是這樣的話,程序的就太難控制了。
后來經過分析ffmpeg的源代碼,發現其竟然是可以從內存中讀取數據的,代碼很簡單,如下所示:
[cpp] view plaincopy

- AVFormatContext *ic = NULL;
- ic = avformat_alloc_context();
[cpp] view plaincopy

- unsigned char * iobuffer=(unsigned char *)av_malloc(32768);
- AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,NULL,fill_iobuffer,NULL,NULL);
- ic->pb=avio;
- err = avformat_open_input(&ic, "nothing", NULL, NULL);
關鍵要在avformat_open_input()之前初始化一個AVIOContext,而且將原本的AVFormatContext的指針pb(AVIOContext類型)指向這個自行初始化AVIOContext。當自行指定了AVIOContext之后,avformat_open_input()里面的URL參數就不起作用了。示例代碼開辟了一塊空間iobuffer作為AVIOContext的緩存。
fill_iobuffer則是將數據讀取至iobuffer的回調函數。fill_iobuffer()形式(參數,返回值)是固定的,是一個回調函數,如下所示(只是個例子,具體怎么讀取數據可以自行設計)。示例中回調函數將文件中的內容通過fread()讀入內存。
[cpp] view plaincopy

- //讀取數據的回調函數-------------------------
- //AVIOContext使用的回調函數!
- //注意:返回值是讀取的字節數
- //手動初始化AVIOContext只需要兩個東西:內容來源的buffer,和讀取這個Buffer到FFmpeg中的函數
- //回調函數,功能就是:把buf_size字節數據送入buf即可
- //第一個參數(void *opaque)一般情況下可以不用
- int fill_iobuffer(void * opaque,uint8_t *buf, int bufsize){
- if(!feof(fp_open)){
- int true_size=fread(buf,1,buf_size,fp_open);
- return true_size;
- }else{
- return -1;
- }
- }
整體結構大致如下:
[cpp] view plaincopy

- FILE *fp_open;
- int fill_iobuffer(void *opaque, uint8_t *buf, int buf_size){
- ...
- }
- int main(){
- ...
- fp_open=fopen("test.h264","rb+");
- AVFormatContext *ic = NULL;
- ic = avformat_alloc_context();
- unsigned char * iobuffer=(unsigned char *)av_malloc(32768);
- AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,NULL,fill_iobuffer,NULL,NULL);
- ic->pb=avio;
- err = avformat_open_input(&ic, "nothing", NULL, NULL);
- ...//解碼
- }
將數據輸出到內存
和從內存中讀取數據類似,ffmpeg也可以將處理后的數據輸出到內存。
回調函數如下示例,可以將輸出到內存的數據寫入到文件中。
[cpp] view plaincopy

- //寫文件的回調函數
- int write_buffer(void *opaque, uint8_t *buf, int buf_size){
- if(!feof(fp_write)){
- int true_size=fwrite(buf,1,buf_size,fp_write);
- return true_size;
- }else{
- return -1;
- }
- }
主函數如下所示。
[cpp] view plaincopy

- FILE *fp_write;
- int write_buffer(void *opaque, uint8_t *buf, int buf_size){
- ...
- }
- main(){
- ...
- fp_write=fopen("src01.h264","wb+"); //輸出文件
- ...
- AVFormatContext* ofmt_ctx=NULL;
- avformat_alloc_output_context2(&ofmt_ctx, NULL, "h264", NULL);
- unsigned char* outbuffer=(unsigned char*)av_malloc(32768);
- AVIOContext *avio_out =avio_alloc_context(outbuffer, 32768,0,NULL,NULL,write_buffer,NULL);
- ofmt_ctx->pb=avio_out;
- ofmt_ctx->flags=AVFMT_FLAG_CUSTOM_IO;
- ...
- }