1. 程式人生 > >ffmpeg編碼之RGB通過H264編碼並封裝成mp4

ffmpeg編碼之RGB通過H264編碼並封裝成mp4

原始rgb檔案

h264編碼成功,並封裝成MP4檔案

執行截圖

日誌資訊

實現方式

extern "C"
{
   #include <libavformat/avformat.h>
   #include <libswscale/swscale.h>
}

#include <iostream>
using namespace std;
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")

int main()
{
	char infile[] = "out.rgb";
	char outfile[] = "rgb.mp4";

	//註冊封裝,解封裝,格式
	av_register_all();
	//註冊解碼器
	avcodec_register_all();

	FILE *fp = fopen(infile,"rb");
	if (!fp)
	{
		cout << infile << " open failed!" << endl;
		getchar();
		return -1;
	}
	int width = 848;
	int height = 480;
	int fps = 25;

	//1 建立編碼器 
	AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_H264);
	if (!codec)
	{
		cout << " avcodec_find_encoder AV_CODEC_ID_H264 failed!" << endl;
		getchar();
		return -1;
	}
	//編碼器上下文
	AVCodecContext *c = avcodec_alloc_context3(codec);
	if (!c)
	{
		cout << " avcodec_alloc_context3  failed!" << endl;
		getchar();
		return -1;
	}
	//設定視訊編碼相關引數
	//位元率
	c->bit_rate = 400000000;

	c->width = width;
	c->height = height;
	//把1秒鐘分成fps個單位 
	c->time_base = { 1,fps };
	c->framerate = { fps,1 };

	//畫面組大小,就是多少幀出現一個關鍵幀
	//GOP 介紹  見   https://blog.csdn.net/xiangjai/article/details/44238005
	c->gop_size = 50;

	c->max_b_frames = 0;
	c->pix_fmt = AV_PIX_FMT_YUV420P;
	c->codec_id = AV_CODEC_ID_H264;
	c->thread_count = 8;

	//全域性的編碼資訊
	c->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;

	//開啟編碼器
	int ret = avcodec_open2(c,codec,NULL);
	if (ret < 0)
	{
		cout << " avcodec_open2  failed!" << endl;
		getchar();
		return -1;
	}
	cout << "avcodec_open2 success!" << endl;

	//2 create out context
	AVFormatContext *oc = NULL;
	avformat_alloc_output_context2(&oc, 0, 0, outfile);

	//3 add video stream
	AVStream *st = avformat_new_stream(oc,NULL);
	st->id = 0;
	st->codecpar->codec_tag = 0;
	avcodec_parameters_from_context(st->codecpar,c);

	cout << "===============================================" << endl;
	av_dump_format(oc, 0, outfile, 1);
	cout << "===============================================" << endl;

	//4 rgb to yuv
	//改變視訊尺寸
	SwsContext *ctx= NULL;
	ctx = sws_getCachedContext(ctx,
		width,height,AV_PIX_FMT_BGRA,
		width,height,AV_PIX_FMT_YUV420P,SWS_BICUBIC,
		NULL,NULL,NULL);
	//輸入空間
	unsigned char *rgb = new unsigned char[width*height*4];


	//輸出空間
	AVFrame *yuv = av_frame_alloc();
	yuv->format = AV_PIX_FMT_YUV420P;
	yuv->width = width;
	yuv->height = height;
	//分配空間
	ret = av_frame_get_buffer(yuv,32);

	if (ret < 0)
	{
		cout << " av_frame_get_buffer  failed!" << endl;
		getchar();
		return -1;
	}

	//5 write mp4 head
	ret = avio_open(&oc->pb,outfile,AVIO_FLAG_WRITE);
	if (ret < 0)
	{
		cout << " avio_open  failed!" << endl;
		getchar();
		return -1;
	}
	ret = avformat_write_header(oc, NULL);
	if (ret < 0)
	{
		cout << " avformat_write_header  failed!" << endl;
		getchar();
		return -1;
	}
	int p = 0;
	for (;;)
	{
		//size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
		//ptr -- 這是指向帶有最小尺寸 size*nmemb 位元組的記憶體塊的指標。
		//size -- 這是要讀取的每個元素的大小,以位元組為單位。
		//nmemb -- 這是元素的個數,每個元素的大小為 size 位元組。
		//stream -- 這是指向 FILE 物件的指標,該 FILE 物件指定了一個輸入流。
		int len = fread(rgb,1,width*height*4,fp);
		if (len<=0)
		{
			break;
		}
		uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
		indata[0] = rgb;
		int inlinesize[AV_NUM_DATA_POINTERS] = { 0 };
		inlinesize[0] = width * 4;

		int h = sws_scale(ctx,indata,inlinesize,0,height,yuv->data,yuv->linesize);
		if (h<=0)
		{
			break;
		}
		//6 encode frame
		yuv->pts = p;
		p = p + 3600;
		//傳送到編碼器
		ret = avcodec_send_frame(c,yuv);
		if (ret != 0)
		{
			continue;
		}
		AVPacket pkt;
		av_init_packet(&pkt);
		//接收編碼結果
		ret = avcodec_receive_packet(c,&pkt);
		if (ret != 0)
			continue;
		//將編碼後的幀寫入檔案
		av_interleaved_write_frame(oc,&pkt);
		cout << "<" << pkt.size << ">";
	}

	//寫檔案尾
	av_write_trailer(oc);

	//關閉視訊輸出IO
	avio_close(oc->pb);

	//清理封裝輸出上下文
	avformat_free_context(oc);

	//關閉編碼器
	avcodec_close(c);

	//清理編碼器上下文
	avcodec_free_context(&c);

	//清理視訊重取樣上下文
	sws_freeContext(ctx);
	cout << "======================end=========================" << endl;
	delete rgb;
	getchar();
	return 0;
}