FFMPEG實現H264的解碼(從原始碼角度)
阿新 • • 發佈:2019-01-28
農曆2014年底了,將前段時間工作中研究的FFMPEG解碼H264流程在此做一下整理,也算作年終技術總結了!
H264解碼原理:
H264分為NAL(網路抽象層)和VCL(視訊編碼層)
解碼器的總框架:
解碼器的流程為:將NAL資料位流輸入到H264的解碼器中,熵解碼模組解碼後輸出量化係數X;係數經過反量化和反變換得到殘差資料R;解碼器使用從碼流中解碼的頭資訊生成預測塊Pred,然後Pred與殘差R求和得到塊資料dF;每個塊dF通過去除塊效應的濾波得到解碼影象的重建塊F。
FFMPEG原始碼解析:
FFMPEG中對於codec的註冊和初始化在另一篇博文中有介紹,在這裡不在涉及了:av_register_all 。
FFMPEG處理h264的模組主要是在h264.c中。
解碼的主函式為:
主函式中的avcodec_init(),avcodec_register_all()等函式都是直接拿去的FFMPGE的原始碼實現的,這個工程對於理解FFMPEG的解碼流程有很大的作用。int main(){ FILE * inp_file; FILE * out_file; int i; int nalLen; /*NAL 長度*/ unsigned char* Buf; /*H.264碼流*/ int got_picture; /*是否解碼一幀影象*/ int consumed_bytes; /*解碼器消耗的碼流長度*/ int cnt=0; AVCodec *codec; /* 編解碼CODEC*/ AVCodecContext *c; /* 編解碼CODEC context*/ AVFrame *picture; /* 解碼後的影象*/ /*輸出和輸出的檔案*/ inp_file = fopen("test.h264", "rb"); out_file = fopen("test.yuv", "wb"); nalLen = 0; /*分配記憶體,並初始化為0*/ Buf = (unsigned char*)calloc ( 500*1024, sizeof(char)); /*CODEC的初始化,初始化一些常量表*/ avcodec_init(); /*註冊CODEC*/ avcodec_register_all(); /*查詢 H264 CODEC*/ codec = avcodec_find_decoder(CODEC_ID_H264); if (!codec) return 0; /*初始化CODEC的預設引數*/ c = avcodec_alloc_context(); if(!c) return 0; /*1. 開啟CODEC,這裡初始化H.264解碼器,呼叫decode_init本地函式*/ if (avcodec_open(c, codec) < 0) return 0; /*為AVFrame申請空間,並清零*/ picture = avcodec_alloc_frame(); if(!picture) return 0; /*迴圈解碼*/ while(!feof(inp_file)) { /*從碼流中獲得一個NAL包*/ nalLen = getNextNal(inp_file, Buf); /*2. NAL解碼,呼叫decode_frame本地函式*/ consumed_bytes= avcodec_decode_video(c, picture, &got_picture, Buf, nalLen); cnt++; /*輸出當前的解碼資訊*/ printf("No:=%4d, length=%4d\n",cnt,consumed_bytes); /*返回<0 表示解碼資料頭,返回>0,表示解碼一幀影象*/ if(consumed_bytes > 0) { /*從二維空間中提取解碼後的影象*/ for(i=0; i<c->height; i++) fwrite(picture->data[0] + i * picture->linesize[0], 1, c->width, out_file); for(i=0; i<c->height/2; i++) fwrite(picture->data[1] + i * picture->linesize[1], 1, c->width/2, out_file); for(i=0; i<c->height/2; i++) fwrite(picture->data[2] + i * picture->linesize[2], 1, c->width/2, out_file); } } /*關閉檔案*/ if(inp_file) fclose(inp_file); if(out_file) fclose(out_file); /*3. 關閉CODEC,釋放資源,呼叫decode_end本地函式*/ if(c) { avcodec_close(c); av_free(c); c = NULL; } /*釋放AVFrame空間*/ if(picture) { av_free(picture); picture = NULL; } /*釋放記憶體*/ if(Buf) { free(Buf); Buf = NULL; } return 0; }
本工程是參考的網上的很多資料來改寫的,因為開源,我們才會變的更優秀,希望所有的開發者都積極分享自己的音視訊開發經驗。向開源專案致敬。
原始碼下載: