SDL基础使用07(YUV数据显示)

SDL显示YUV数据和文件

使用SDL播放随机yuv数据 

#include <stdio.h>
#include <Windows.h>

extern "C"
{
#include <SDL.h>
}

#pragma comment(lib, "SDL2.lib")

/* 填充随机颜色 */
static void FillYuvImage(BYTE* pYuv, int nWidth, int nHeight, int nIndex)
{
	int x, y, i;
	i = nIndex;

	BYTE* pY = pYuv;								// 数组开始位置
	BYTE* pU = pYuv + nWidth * nHeight;				// 数组开始位置 + Y数据后的位置
	BYTE* pV = pYuv + nWidth * nHeight * 5 / 4;		// 数组开始位置 + YU数据后的位置

	/* Y */
	for (y = 0; y < nHeight; y++)
	{
		for (x = 0; x < nWidth; x++)
		{
			pY[y * nWidth + x] = x + y + i * 2;
		}
	}

	/* Cb and Cr */
	for (y = 0; y < nHeight / 2; y++)
	{
		for (x = 0; x < nWidth / 2; x++)
		{
			pU[y * (nWidth / 2) + x] = 64 + y + i * 2;
			pV[y * (nWidth / 2) + x] = 64 + x + i * 5;
		}
	}
}

#undef main
int main()
{
	// 初始化SDL
	if (SDL_Init(SDL_INIT_VIDEO))
	{
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}

	// 提升图像质量,否则默认缩放质量会有毛剌(设置反锯齿)
	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1");

	// 创建窗口(获取控制台窗口),基于窗口创建渲染器
	SDL_Window*   window = SDL_CreateWindowFrom(::GetConsoleWindow());
	SDL_Renderer* render = SDL_CreateRenderer(window, -1, 0);

	// 窗口宽高
	const int W = 1920;
	const int H = 1080;

	// 创建渲染器
	SDL_Texture*  texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, W, H);

	/// yuv 420P : planer   W * H * 1.5 (y: 1 uv: 0.5)
	static BYTE Yuv[W * H * 2];
	BYTE* pY = Yuv;
	BYTE* pU = Yuv + W * H;
	BYTE* pV = Yuv + W * H * 5 / 4;

	int index = 0;
	while (true)
	{
		FillYuvImage(Yuv, W, H, index++);				// 填充随机颜色到YUV数组中

		// 更新纹理数据
		int e = SDL_UpdateYUVTexture(texture, NULL,
			pY, W,
			pU, W / 2,
			pV, W / 2);

		/// 刷新渲染器:三部曲: clear, copy, present
		SDL_RenderClear(render);
		SDL_RenderCopy(render, texture, NULL, NULL);
		SDL_RenderPresent(render);
		Sleep(40);
	}
	// 释放资源,退出
	SDL_DestroyTexture(texture);
	SDL_DestroyRenderer(render);
	SDL_Quit();

	return 0;
}

 SDL播放YUV文件

#define  _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdlib.h>
#include <string.h>

extern "C"
{
#include <SDL.h>
}
using namespace std;

const int width = 640;	
const int height = 480;

static SDL_Window *window = nullptr;
SDL_Renderer *ren = nullptr;

#undef main
int main(int argc, char **argv)
{
	SDL_Event        event;
	SDL_bool         done = SDL_FALSE;
	Uint32           pixel_format = SDL_PIXELFORMAT_IYUV;	// yuv420p
	int frame_number = 0;

	// 如果去掉“b”,文本模式读取,遇到“\0”,就会自动退出
	FILE *fp = fopen("5s_yuv420p_640x480.yuv", "rb");		

	// 设置SDL日志优先级(日志分类,优先级)
	SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);

	// 1. 初始化SDL
	if (SDL_Init(SDL_INIT_VIDEO) != 0) 
	{
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
		return 3;
	}

	// 2. 创建窗口
	SDL_Window *win = SDL_CreateWindow("Hello", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_SHOWN);
	if (win == nullptr)
	{
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create window: %s\n", SDL_GetError());
		return 1;
	}

	// 3. 基于窗口创建渲染器
	ren = SDL_CreateRenderer(win, 0, 0); 
	if (ren == nullptr)
	{
		SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't set create renderer: %s\n", SDL_GetError());
		return 1;
	}


	// 4. 基于yuv格式创建纹理
	SDL_Texture *tex = SDL_CreateTexture(ren, pixel_format, SDL_TEXTUREACCESS_STREAMING, width, height);
	int len = height * width;
	Uint8* buf = (Uint8*)malloc(len *  2);				// 1.5倍w * h

	// SDL_BYTESPERPIXEL(pixel_format) 根据像素格式获取每个像素占用的字节数
	int iPitch = width * SDL_BYTESPERPIXEL(pixel_format);
	printf("IPath %d\n", iPitch);		// 640 一行像素数据字节数

	SDL_Rect rec;
	rec.x = 0;
	rec.y = 0;
	rec.w = width;
	rec.h = height;
	int nRets = 0;
	while (!done)
	{
		while (SDL_PollEvent(&event)) {
			switch (event.type) {
			case SDL_KEYDOWN:
				if (event.key.keysym.sym == SDLK_ESCAPE) {
					done = SDL_TRUE;
				}
				break;
			case SDL_QUIT:
				done = SDL_TRUE;
				break;
			}
		}
		/// 注意:每次需要的字节数: width*height*1.5
		/// Y:  width * height Bytes
		/// cb: width * height / 4
		/// cr: width * height / 4
		nRets = fread(buf, 1, height * width * 3 / 2, fp);
		if (nRets < 0) {
			break;
		}

		// 用buf缓冲区数据 更新 纹理
		SDL_UpdateTexture(tex, NULL, buf, iPitch);

		/*	刷新渲染器:三部曲:clear, copy, present */
		SDL_RenderClear(ren);
		SDL_RenderCopy(ren, tex, NULL, &rec);
		SDL_RenderPresent(ren);
		frame_number++;

		/// 音视频同步实现原理: 休眠
		SDL_Delay(40);			// 1000 / 40 = 25fps(帧率:每秒25帧)

	}

	/// 退出:清理资源
	SDL_Delay(2000);
	SDL_DestroyTexture(tex);
	SDL_DestroyRenderer(ren);
	SDL_DestroyWindow(win);
	SDL_Quit();
	fclose(fp);
	return 0;
}

 SDL播放YUV文件(带缩放)

#include <stdio.h>
#include <string.h>
#include <math.h>

extern "C"
{
#include <SDL.h>
}

// 自定义事件
#define REFRESH_EVENT   (SDL_USEREVENT + 1)     // 请求画面刷新事件
#define QUIT_EVENT      (SDL_USEREVENT + 2)     // 退出事件

// 定义YUV像素分辨率	    5s_yuv420p_640x480.yuv
#define YUV_WIDTH   640
#define YUV_HEIGHT  480

// 定义YUV格式
#define YUV_FORMAT  SDL_PIXELFORMAT_IYUV        // 实际为YUV420P

int s_thread_exit = 0;              // 线程退出标志 1 退出

// 线程函数
int refresh_video_timer(void *data)
{
	(void)data;
	while (!s_thread_exit)
	{
		// 每隔40ms发送一次刷新事件
		SDL_Event event;
		event.type = REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	s_thread_exit = 0;

	// 发送退出事件
	SDL_Event event;
	event.type = QUIT_EVENT;
	SDL_PushEvent(&event);

	return 0;
}

#undef main
int main()
{
	// 1. 初始化 SDL
	if (SDL_Init(SDL_INIT_VIDEO))
	{
		fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}

	uint32_t pixformat = YUV_FORMAT;            // YUV420P,即是SDL_PIXELFORMAT_IYUV

	// YUV的分辨率640x480
	int video_width = YUV_WIDTH;
	int video_height = YUV_HEIGHT;
	// 显示窗口分辨率640x480
	int win_width = YUV_WIDTH;
	int win_height = YUV_WIDTH;

	FILE *video_fd = NULL;
	const char *yuv_path = "5s_yuv420p_640x480.yuv";

	// 我们测试的文件是YUV420P格式   640x480
	uint32_t y_frame_len = video_width * video_height;          
	uint32_t u_frame_len = video_width * video_height / 4;
	uint32_t v_frame_len = video_width * video_height / 4;
	// 一帧YUV数据的长度
	uint32_t yuv_frame_len = y_frame_len + u_frame_len + v_frame_len;

	// 2. 创建窗口
	SDL_Window* window = SDL_CreateWindow("Simplest YUV Player",
		SDL_WINDOWPOS_UNDEFINED,
		SDL_WINDOWPOS_UNDEFINED,
		video_width, video_height,       // 320 240
		SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	if (!window)
	{
		fprintf(stderr, "SDL: could not create window, err:%s\n", SDL_GetError());
		goto _FAIL;
	}

	// 2. 基于窗口创建渲染器
	SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
	// 3. 基于渲染器创建纹理
	SDL_Texture* texture = SDL_CreateTexture(renderer,
		pixformat,                      // 像素格式 yuv420p
		SDL_TEXTUREACCESS_STREAMING,    // 改变是否频繁
		video_width,
		video_height);

	// 分配一帧数据长度的空间
	uint8_t *video_buf = (uint8_t*)malloc(yuv_frame_len);
	if (!video_buf)
	{
		fprintf(stderr, "Failed to alloce yuv frame space!\n");
		goto _FAIL;
	}

	// 4. 打开YUV文件(二进制方式打开)
	video_fd = fopen(yuv_path, "rb");
	if (!video_fd)
	{
		fprintf(stderr, "Failed to open yuv file\n");
		goto _FAIL;
	}
	else
	{
		printf("open file success!\n");
	}

	// 创建刷新线程
	SDL_Thread* timer_thread = SDL_CreateThread(refresh_video_timer, NULL, NULL);

	SDL_Event event;
	SDL_Rect rect;
	size_t video_buff_len;						// 获取读取数据后buff的数据长度
	while (1)
	{
		// 收取SDL系统里面的事件
		SDL_WaitEvent(&event);
		if (event.type == REFRESH_EVENT)         // 自定义画面刷新事件
		{
			video_buff_len = fread(video_buf, 1, yuv_frame_len, video_fd);
			if (video_buff_len <= 0)
			{
				fprintf(stderr, "Failed to read data from yuv file!\n");
				goto _FAIL;
			}
			// 设置纹理的数据 
			SDL_UpdateTexture(texture, NULL, video_buf, video_width);  // 参数四,一行像素数据字节数

			// 显示区域,可以通过修改w和h进行缩放
			rect.x = 0;
			rect.y = 0;
			float w_ratio = win_width  * 1.0 / video_width;           // 宽高缩放比例(倍数) 2 * 640 / 640 = 2
			float h_ratio = win_height * 1.0 / video_height;
			// 1280x960
			rect.w = video_width * w_ratio;
			rect.h = video_height * h_ratio;
			// 保持原视频的宽高比例(显示到窗口中间)
			// rect.x = fabs(video_width * w_ratio - video_width) / 2.0;
			// rect.y = fabs(video_height * h_ratio - video_height) / 2.0;
			// rect.w = video_width;
			// rect.h = video_height;

			// 渲染三部曲
			SDL_RenderClear(renderer);
			SDL_RenderCopy(renderer, texture, NULL, &rect);
			SDL_RenderPresent(renderer);
		}
		else if (event.type == SDL_WINDOWEVENT)	
		{
			// 如果窗口大小改变(会触发该事件获取到窗口的高度和宽度)
			SDL_GetWindowSize(window, &win_width, &win_height);
			printf("SDL_WINDOWEVENT win_width:%d, win_height:%d\n", win_width, win_height);
		}
		else if (event.type == SDL_QUIT)     // 退出事件(点击关闭窗口)
		{
			s_thread_exit = 1;
		}
		else if (event.type == QUIT_EVENT)   // 自定义的退出事件
		{
			break;
		}
	}

_FAIL:
	s_thread_exit = 1;      // 保证线程能够退出
	// 释放资源, 退出
	if (timer_thread)
		SDL_WaitThread(timer_thread, NULL); // 等待线程退出
	if (video_buf)
		free(video_buf);
	if (video_fd)
		fclose(video_fd);
	if (texture)
		SDL_DestroyTexture(texture);
	if (renderer)
		SDL_DestroyRenderer(renderer);
	if (window)
		SDL_DestroyWindow(window);

	SDL_Quit();

	return 0;
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SDL2.0 支持渲染 YUV4:2:0 格式的视频,需要使用 SDL_Texture 和 SDL_RenderCopy 函数来实现。 以下是一个简单的示例代码: ```c #include <SDL2/SDL.h> #include <stdio.h> const int SCREEN_WIDTH = 640; const int SCREEN_HEIGHT = 480; int main(int argc, char* argv[]) { SDL_Window* window = NULL; SDL_Renderer* renderer = NULL; SDL_Texture* texture = NULL; if (SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL could not initialize! SDL_Error: %s\n", SDL_GetError()); return 1; } window = SDL_CreateWindow("YUV4:2:0 Video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN); if (window == NULL) { printf("Window could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } renderer = SDL_CreateRenderer(window, -1, 0); if (renderer == NULL) { printf("Renderer could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_YV12, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); if (texture == NULL) { printf("Texture could not be created! SDL_Error: %s\n", SDL_GetError()); return 1; } // YUV data Uint8* yPlane = NULL; Uint8* uPlane = NULL; Uint8* vPlane = NULL; int yPitch = 0; int uPitch = 0; int vPitch = 0; int w = 0; int h = 0; // read YUV file FILE* fp = fopen("video.yuv", "rb"); if (fp == NULL) { printf("Could not open YUV file!\n"); return 1; } // allocate memory for YUV data int bufferSize = SCREEN_WIDTH * SCREEN_HEIGHT * 3 / 2; Uint8* buffer = (Uint8*)malloc(bufferSize); // main loop SDL_Event event; bool quit = false; while (!quit) { while (SDL_PollEvent(&event)) { if (event.type == SDL_QUIT) { quit = true; } } // read YUV data if (fread(buffer, 1, bufferSize, fp) != bufferSize) { // end of file rewind(fp); } // set YUV data yPlane = buffer; uPlane = yPlane + SCREEN_WIDTH * SCREEN_HEIGHT; vPlane = uPlane + SCREEN_WIDTH * SCREEN_HEIGHT / 4; yPitch = SCREEN_WIDTH; uPitch = SCREEN_WIDTH / 2; vPitch = SCREEN_WIDTH / 2; w = SCREEN_WIDTH; h = SCREEN_HEIGHT; // update texture SDL_UpdateYUVTexture(texture, NULL, yPlane, yPitch, uPlane, uPitch, vPlane, vPitch); // clear screen SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF); SDL_RenderClear(renderer); // render texture SDL_RenderCopy(renderer, texture, NULL, NULL); // update screen SDL_RenderPresent(renderer); } // free memory free(buffer); // cleanup SDL_DestroyTexture(texture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); return 0; } ``` 在代码中,我们首先创建一个 SDL 窗口和渲染器。然后,使用 SDL_CreateTexture 函数创建一个 YV12 格式的纹理。接下来,读取 YUV 文件,并将数据设置到纹理中。在主循环中,使用 SDL_RenderCopy 函数将纹理渲染到屏幕上。最后,记得在程序结束前释放所有内存和资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

石小浪♪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值