需要的解碼的視頻數據在一段內存中。例如,通過其他系統送來的視頻數據。同樣,有的時候編碼后的視頻數據也未必要保存成一個文件。例如,要求將編碼后的視頻數據送給其他的系統進行下一步的處理。以上兩種情況就要求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", NULL, NULL);
關鍵要在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);
- ...//解碼
- }
將數據輸出到內存
也可以將處理后的數據輸出到內存。
回調函數如下示例,可以將輸出到內存的數據寫入到文件中。
- //寫文件的回調函數
- 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;
- ...
- }
關鍵點
關鍵點就兩個:
1. 初始化自定義的AVIOContext,指定自定義的回調函數。示例代碼如下:
- //AVIOContext中的緩存
- unsigned char *aviobuffer=(unsigned char*)av_malloc(32768);
- AVIOContext *avio=avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);
- pFormatCtx->pb=avio;
- if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){
- printf("Couldn't open inputstream.(無法打開輸入流)\n");
- return -1;
- }
上述代碼中,自定義了回調函數read_buffer()。在使用avformat_open_input()打開媒體數據的時候,就可以不指定文件的URL了,即其
第2個參數為NULL
(因為數據不是靠文件讀取,而是由read_buffer()提供)
2. 自己寫回調函數。示例代碼如下:
- //Callback
- int read_buffer(void *opaque, uint8_t *buf, int buf_size){
- if(!feof(fp_open)){
- int true_size=fread(buf,1,buf_size,fp_open);
- return true_size;
- }else{
- return -1;
- }
- }
當系統需要數據的時候,
會自動調用
該回調函數以獲取數據。
這個例子為了簡單,直接使用fread()讀取數據至內存。回調函數需要
格外注意
它的參數和返回值。
avio_alloc_context第3個參數
Set to 1 if the buffer should be writable, 0 otherwise. 為0時表示ffmpeg要從回調函數里
讀取
數據。為1表示ffmpeg會將數據輸出到回調函數中。
參考: