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的原始碼,發現其竟然是可以從記憶體中讀取資料的,程式碼很簡單,如下所示:
- 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"
關鍵要在avformat_open_input()之前初始化一個AVIOContext,而且將原本的AVFormatContext的指標pb(AVIOContext型別)指向這個自行初始化AVIOContext。當自行指定了AVIOContext之後,avformat_open_input()裡面的URL引數就不起作用了。示例程式碼開闢了一塊空間iobuffer作為AVIOContext的快取。
fill_iobuffer則是將資料讀取至iobuffer的回撥函式。fill_iobuffer()形式(引數,返回值)是固定的,是一個回撥函式,如下所示(只是個例子,具體怎麼讀取資料可以自行設計)。示例中回撥函式將檔案中的內容通過fread()讀入記憶體。
- //讀取資料的回撥函式-------------------------
- //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;
- }
- }
整體結構大致如下:
- 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也可以將處理後的資料輸出到記憶體。
回撥函式如下示例,可以將輸出到記憶體的資料寫入到檔案中。
- //寫檔案的回撥函式
- 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;
- }
- }
主函式如下所示。
- 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;
- ...
- }