FFmpeg RGB轉YUV

前言

下面這個例子是使用 FFmpeg 將 RGB 格式像素數據轉換成 YUV 格式像素數據,在本地生成的 YUV 文本使用雷霄驊大神改寫的 yuvplayer 成功播放。

我測試的 rgb 文件像素格式是 RGB24 的,如果你的是其它像素格式,請自行替換 "像素格式ID" 和申請的內存空間大小等等。如果有些同學沒有 rgb 測試文件,可以留言我發給你。

<br />

效果

設置好分辨率,將本地生成的 YUV 文本直接拖到 yuvplayer 窗口中,即可正常播放,如下圖所示:

<br />

完整代碼

#include <iostream>

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

#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avcodec.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"swscale.lib")

using namespace std;

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

	// 源圖像參數(這裏參數要設置正確,否則會轉換異常)
	int width = 640;
	int height = 360;

	//1 打開RGB和YUV文件
	FILE *fpin = fopen(infile, "rb");
	if (!fpin)
	{
		cout << infile << "open infile failed!" << endl;
		getchar();
		return -1;
	}

	FILE* fpout = fopen(outfile, "wb");
	if (!fpout)
	{
		cout << infile << "open outfile failed!" << endl;
		getchar();
		return -1;
	}
	// 創建RGB緩衝區同時分配內存
	unsigned char *rgbBuf = new unsigned char[width * height * 3];

	// 註冊所有和編解碼器有關的組件
	av_register_all();

	//2 創建視頻重採樣上下文:指定源和目標圖像分辨率、格式
	SwsContext *swsCtx = NULL;
	swsCtx = sws_getCachedContext(swsCtx,
		width, height, AV_PIX_FMT_RGB24,
		width, height, AV_PIX_FMT_YUV420P,
		SWS_BICUBIC,
		NULL, NULL, NULL
		);

	//3 創建YUV視頻幀並配置
	AVFrame *yuvFrame = av_frame_alloc();
	yuvFrame->format = AV_PIX_FMT_YUV420P;
	yuvFrame->width = width;
	yuvFrame->height = height;
	av_frame_get_buffer(yuvFrame, 3 * 8);

	// 循環寫視頻文件
	for (;;)
	{
		//4 每次讀取一幀RGB數據到rgbBuf,讀取完畢則退出
		int len = fread(rgbBuf, width*height * 3, 1, fpin);
		if (len <= 0)
		{
			break;
		}
        
		//5 創建RGB視頻幀並綁定RGB緩衝區(avpicture_fill是給rgbFrame初始化一些字段,並且會自動填充data和linesize)
		AVFrame *rgbFrame = av_frame_alloc();
		avpicture_fill((AVPicture *)rgbFrame, rgbBuf, AV_PIX_FMT_BGR24, width, height);

		//6 像素格式轉換,轉換後的YUV數據存放在yuvFrame
		int outSliceH = sws_scale(swsCtx, rgbFrame->data, rgbFrame->linesize, 0, height,
			yuvFrame->data, yuvFrame->linesize);
		if (outSliceH <= 0)
			break;

		//7 將YUV各分量數據寫入文件
		fwrite(yuvFrame->data[0], width * height, 1, fpout);
		fwrite(yuvFrame->data[1], width * height / 4, 1, fpout);
		fwrite(yuvFrame->data[2], width * height / 4, 1, fpout);

		cout << ".";
	}

	cout << "\n======================end=========================" << endl;

	// 關閉RGB和YUV文件
	fclose(fpin);
	fclose(fpout);
    
	// 釋放RGB緩衝區
	delete rgbBuf;    

	//清理視頻重採樣上下文
	sws_freeContext(swsCtx);

	getchar();

	return 0;
}

<br />

代碼分析

因爲是要做的 RGB 轉 YUV,這個過程中最重要的函數是 sws_scale,其函數原型如下:

int sws_scale(struct SwsContext *c,
              const uint8_t *const srcSlice[],
              const int srcStride[],
              int srcSliceY, int srcSliceH,
              uint8_t *const dst[], const int dstStride[]);

所以我們先要創建並配置好 sws_scale 需要的參數,下面我們分別講解一下:

(1)參數 c,用於指定源和目標圖像分辨率、格式,在第 2 步創建;

(2)參數 srcSlice[] 和 srcStride[],即源圖像的數據指針和行字節數,可以通過rgbFrame->datargbFrame->linesize獲得,而 rgbFrame 在第 5 步創建 ;

(3)參數 dst[] 和 dstStride[],即目標圖像的數據指針和行字節數,可以通過yuvFrame->datayuvFrame->linesize獲得,而 yuvFrame 在第 3 步創建 ;

<br />

總而言之,就是先打開 RGB 和 YUV 文本,配置好 sws_scale 需要的參數,在循環中每次讀取一幀 RGB 視頻幀數據保存在 rgbFrame 結構體中,然後經過 swscale 函數轉換,將得到的一幀 YUV 視頻幀數據保存在 pFrameYUV 中,最後將 yuvFrame 中的數據寫入 YUV 文件。

<span style="color:green">注意:源圖像參數要跟你的本地 RGB 文件一致,否則會轉換異常。</span>

<br />

參考:

雷霄驊 - FFMPEG 實現 YUV,RGB各種圖像原始數據之間的轉換(swscale)

<br />

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章