1. 程式人生 > >ffmpeg 將yuv轉換成H264

ffmpeg 將yuv轉換成H264

#include "stdafx.h"
#ifdef _WIN32
//Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/opt.h"
#include "libavutil/parseutils.h"
#include "libavutil/avutil.h"
};
#else
//Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/parseutils.h>
#include <libavutil/avutil.h>
#ifdef __cplusplus
};
#endif
#endif


void encoder()
{
	const char *pOutFile = "ds.h264";
	FILE*pFile = fopen("ds_480x272.yuv", "rb");
	if (!pFile){
		return;
	}
	// 註冊 ffmpeg 中的所有的封裝、解封裝 和 協議等
	av_register_all();

	//  用作之後寫入視訊幀並編碼成 h264,貫穿整個工程當中
	AVFormatContext *pFormatContext = avformat_alloc_context();

	// 通過這個函式可以獲取輸出檔案的編碼格式, 那麼這裡我們的 fmt 為 h264 格式(AVOutputFormat *)
	AVOutputFormat *pOutputFormat = av_guess_format(NULL, pOutFile, NULL);
	pFormatContext->oformat = pOutputFormat;

	//將輸出檔案中的資料讀入到程式的 buffer 當中,方便之後的資料寫入,也可以說快取資料寫入
	avio_open(&pFormatContext->pb, pOutFile, AVIO_FLAG_READ_WRITE);

	// 通過媒體檔案控制者獲取輸出檔案的流媒體資料,這裡 AVCodec * 寫 0 , 預設會為我們計算出合適的編碼格式
	AVStream *pStream = avformat_new_stream(pFormatContext, 0);
	if (!pStream){
		return;
	}
	// 設定 25 幀每秒 ,也就是 fps 為 25。(其實不設定也可以)
	pStream->time_base.num = 1;
	pStream->time_base.den = 25;


	// 使用者儲存編碼所需的引數格式等等
	AVCodecContext*pCodecContext = NULL;

	// 從媒體流中獲取到編碼結構體,他們是一一對應的關係,一個 AVStream 對應一個  AVCodecContext
	pCodecContext = pStream->codec;

	// 設定編碼器的 id,每一個編碼器都對應著自己的 id,例如 h264 的編碼 id 就是 AV_CODEC_ID_H264
	pCodecContext->codec_id = pOutputFormat->video_codec;

	// 設定編碼型別為 視訊編碼
	pCodecContext->codec_type = AVMEDIA_TYPE_VIDEO;

	// 設定畫素格式為 yuv 格式
	pCodecContext->pix_fmt = PIX_FMT_YUVJ420P;

	// 設定視訊的寬高
	pCodecContext->width = 480;
	pCodecContext->height = 272;

	// 設定 25 幀每秒 ,也就是 fps 為 25
	pCodecContext->time_base.num = 1;
	pCodecContext->time_base.den = 25;

	// 設定位元率,每秒傳輸多少位元數 bit,位元率越高,傳送速度越快,也可以稱作位元速率,
	// 視訊中的位元是指由模擬訊號轉換為數字訊號後,單位時間內的二進位制資料量。
	pCodecContext->bit_rate = 400000;

	// 設定影象組層的大小。
	// 影象組層是在 MPEG 編碼器中存在的概念,影象組包 若干幅影象, 組頭包 起始碼、GOP 標誌等,如視訊磁帶記錄器時間、控制碼、B 幀處理碼等;
	pCodecContext->gop_size = 250;

	pCodecContext->qmin = 10;
	pCodecContext->qmax = 51;
	pCodecContext->qcompress = 1;

	// 設定 B 幀最大的數量,B幀為視訊圖片空間的前後預測幀, B 幀相對於 I、P 幀來說,壓縮率比較大,也就是說相同位元速率的情況下,
	// 越多 B 幀的視訊,越清晰,現在很多打視訊網站的高清視訊,就是採用多編碼 B 幀去提高清晰度,
	// 但同時對於編解碼的複雜度比較高,比較消耗效能與時間
	pCodecContext->max_b_frames = 3;

	AVDictionary *param = 0;
	if(pCodecContext->codec_id == AV_CODEC_ID_H264) {// 可選設定
		// 通過--preset的引數調節編碼速度和質量的平衡。
		av_dict_set(¶m, "preset", "slow", 0);

		// 通過--tune的引數值指定片子的型別,是和視覺優化的引數,或有特別的情況。
		// zerolatency: 零延遲,用在需要非常低的延遲的情況下,比如電視電話會議的編碼
		av_dict_set(¶m, "tune", "zerolatency", 0);
	}

	// 通過 codec_id 找到對應的編碼器
	AVCodec *pCodec = avcodec_find_encoder(pCodecContext->codec_id);
	if (!pCodec){
		return;
	}
	
	// 開啟編碼器,並設定引數 param
	avcodec_open2(pCodecContext, pCodec, ¶m);

	AVFrame *pFrame = av_frame_alloc();

	// 通過畫素格式(這裡為 YUV)獲取圖片的真實大小,例如將 480 * 720 轉換成 int 型別
	int nPicSize = avpicture_get_size(pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height);

	// 將 picture_size 轉換成位元組資料,byte
	uint8_t *buf = (uint8_t*)av_malloc(nPicSize);

	// 設定原始資料 AVFrame 的每一個frame 的圖片大小,AVFrame 這裡儲存著 YUV 非壓縮資料
	avpicture_fill((AVPicture*)pFrame, buf, pCodecContext->pix_fmt, pCodecContext->width, pCodecContext->height);

	// 編寫 h264 封裝格式的檔案頭部,基本上每種編碼都有著自己的格式的頭部,想看具體實現的同學可以看看 h264 的具體實現
	avformat_write_header(pFormatContext, NULL);

	//建立編碼後的資料 AVPacket 結構體來儲存 AVFrame 編碼後生成的資料
	AVPacket pkt;
	av_new_packet(&pkt, nPicSize);

	int i = 0;
	int y_size = pCodecContext->width * pCodecContext->height;
	while (true){
		if (fread(buf, 1, y_size*3/2, pFile) <= 0){
			printf("Could not read input file.");
			break;
		}
		else if(feof(pFile)){
			break;
		}
		pFrame->data[0] = buf;  // 亮度Y
		pFrame->data[1] = buf+ y_size;  // U 
		pFrame->data[2] = buf+ y_size*5/4; // V

		pFrame->pts = i++;
		//Encode
		int got_picture = 0;
		int ret = avcodec_encode_video2(pCodecContext, &pkt, pFrame, &got_picture);
		if(ret < 0){
			printf("Encode Error.\n");
			break;
		}
		if (got_picture == 1){// 編碼成功後寫入 AVPacket 到 輸入輸出資料操作著 pFormatCtx 中,當然,記得釋放記憶體
			pkt.stream_index = pStream->index;
			ret = av_write_frame(pFormatContext, &pkt);
			av_free_packet(&pkt);
		}
	}

	av_free_packet(&pkt);
	// 寫入資料流尾部到輸出檔案當中,並釋放檔案的私有資料
	av_write_trailer(pFormatContext);

	if (pStream){
		avcodec_close(pStream->codec);// 關閉編碼器
	}
	av_free(pFrame);
	av_free(buf);
	avio_close(pFormatContext->pb);
	avformat_free_context(pFormatContext);// 關閉輸入資料的快取

	fclose(pFile);
	return ;
}

int _tmain(int argc, _TCHAR* argv[])
{
	encoder();
	return 0;
}
http://download.csdn.net/detail/sz76211822/9698824