1. 程式人生 > >ffmpeg 從記憶體中讀取資料(或將資料輸出到記憶體)

ffmpeg 從記憶體中讀取資料(或將資料輸出到記憶體)

原文見雷大神部落格:http://blog.csdn.net/leixiaohua1020/article/details/12980423

更新記錄(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的原始碼,發現其竟然是可以從記憶體中讀取資料的,程式碼很簡單,如下所示:

  1. AVFormatContext *ic = NULL;  
  2. ic = avformat_alloc_context();  
  1. unsigned char * iobuffer=(unsigned char *)av_malloc(32768);  
  2. AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,NULL,fill_iobuffer,NULL,NULL);  
  3. ic->pb=avio;  
  4. 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()讀入記憶體。

  1. //讀取資料的回撥函式-------------------------
  2. //AVIOContext使用的回撥函式!
  3. //注意:返回值是讀取的位元組數
  4. //手動初始化AVIOContext只需要兩個東西:內容來源的buffer,和讀取這個Buffer到FFmpeg中的函式
  5. //回撥函式,功能就是:把buf_size位元組資料送入buf即可
  6. //第一個引數(void *opaque)一般情況下可以不用
  7. int fill_iobuffer(void * opaque,uint8_t *buf, int bufsize){  
  8.     if(!feof(fp_open)){  
  9.         int true_size=fread(buf,1,buf_size,fp_open);  
  10.         return true_size;  
  11.     }else{  
  12.         return -1;  
  13.     }  
  14. }  


整體結構大致如下:
  1. FILE *fp_open;  
  2. int fill_iobuffer(void *opaque, uint8_t *buf, int buf_size){  
  3. ...  
  4. }  
  5. int main(){  
  6.     ...  
  7.     fp_open=fopen("test.h264","rb+");  
  8.     AVFormatContext *ic = NULL;  
  9.     ic = avformat_alloc_context();  
  10.     unsigned char * iobuffer=(unsigned char *)av_malloc(32768);  
  11.     AVIOContext *avio =avio_alloc_context(iobuffer, 32768,0,NULL,fill_iobuffer,NULL,NULL);  
  12.     ic->pb=avio;  
  13.     err = avformat_open_input(&ic, "nothing", NULL, NULL);  
  14.     ...//解碼
  15. }  


將資料輸出到記憶體

和從記憶體中讀取資料類似,ffmpeg也可以將處理後的資料輸出到記憶體。

回撥函式如下示例,可以將輸出到記憶體的資料寫入到檔案中。

  1. //寫檔案的回撥函式
  2. int write_buffer(void *opaque, uint8_t *buf, int buf_size){  
  3.     if(!feof(fp_write)){  
  4.         int true_size=fwrite(buf,1,buf_size,fp_write);  
  5.         return true_size;  
  6.     }else{  
  7.         return -1;  
  8.     }  
  9. }  

主函式如下所示。

  1. FILE *fp_write;  
  2. int write_buffer(void *opaque, uint8_t *buf, int buf_size){  
  3. ...  
  4. }  
  5. main(){  
  6.     ...  
  7.     fp_write=fopen("src01.h264","wb+"); //輸出檔案
  8.     ...  
  9.     AVFormatContext* ofmt_ctx=NULL;  
  10.     avformat_alloc_output_context2(&ofmt_ctx, NULL, "h264", NULL);  
  11.     unsigned char* outbuffer=(unsigned char*)av_malloc(32768);  
  12.     AVIOContext *avio_out =avio_alloc_context(outbuffer, 32768,0,NULL,NULL,write_buffer,NULL);    
  13.     ofmt_ctx->pb=avio_out;   
  14.     ofmt_ctx->flags=AVFMT_FLAG_CUSTOM_IO;  
  15.     ...  
  16. }