1. 程式人生 > >FFMPEG實現H264的解碼(從原始碼角度)

FFMPEG實現H264的解碼(從原始碼角度)

農曆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;
}

本工程是參考的網上的很多資料來改寫的,因為開源,我們才會變的更優秀,希望所有的開發者都積極分享自己的音視訊開發經驗。向開源專案致敬。

原始碼下載: