SDL2 播放视频数据(YUV420P)

1.简介

这里以常用的视频原始数据YUV420P为例,展示视频的播放。

SDL播放视频的流程如下:

  • 初始化SDL:SDL_Init();
  • 创建窗口:SDL_CreateWindow();
  • 创建渲染器:SDL_CreateRenderer();
  • 创建纹理:SDL_CreateTexture();
  • 设置纹理数据:SDL_UpdateTexture();
  • 纹理复制给渲染目标:使用SDL_RenderCopy()将纹理数据复制给渲染目标。在使用SDL_RenderCopy()之前,可以使用SDL_RenderClear()先使用清空渲染目标。
  • 显示界面:SDL_RenderPresent();

2.使用的数据结构以及方法介绍

SDL显示视频涉及到下列结构体:

  • SDL_Window:代表了窗口
  • SDL_Renderer:代表了渲染器
  • SDL_Texture:代表了纹理
  • SDL_Rect:一个矩形框,用于确定纹理显示的位置。

YUV/RGB像素数据首先加载至SDL_Texture,然后通过SDL_Render渲染至SDL_Window。其中SDL_Rect可以指定显示的位置。

SDL_CreateWindow:创建窗口

cpp 复制代码
SDL_Window *SDL_CreateWindow(const char *title,int x, int y, int w,
                                                      int h, Uint32 flags);

flags有以下的类型:

cpp 复制代码
typedef enum
{
    SDL_WINDOW_FULLSCREEN = 0x00000001,         /**< fullscreen window */
    SDL_WINDOW_OPENGL = 0x00000002,             /**< window usable with OpenGL context */
    SDL_WINDOW_SHOWN = 0x00000004,              /**< window is visible */
    SDL_WINDOW_HIDDEN = 0x00000008,             /**< window is not visible */
    SDL_WINDOW_BORDERLESS = 0x00000010,         /**< no window decoration */
    SDL_WINDOW_RESIZABLE = 0x00000020,          /**< window can be resized */
    SDL_WINDOW_MINIMIZED = 0x00000040,          /**< window is minimized */
    SDL_WINDOW_MAXIMIZED = 0x00000080,          /**< window is maximized */
    SDL_WINDOW_MOUSE_GRABBED = 0x00000100,      /**< window has grabbed mouse input */
    SDL_WINDOW_INPUT_FOCUS = 0x00000200,        /**< window has input focus */
    SDL_WINDOW_MOUSE_FOCUS = 0x00000400,        /**< window has mouse focus */
    SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ),
    SDL_WINDOW_FOREIGN = 0x00000800,            /**< window not created by SDL */
    SDL_WINDOW_ALLOW_HIGHDPI = 0x00002000,      /**< window should be created in high-DPI mode if supported.
                                                     On macOS NSHighResolutionCapable must be set true in the
                                                     application's Info.plist for this to have any effect. */
    SDL_WINDOW_MOUSE_CAPTURE    = 0x00004000,   /**< window has mouse captured (unrelated to MOUSE_GRABBED) */
    SDL_WINDOW_ALWAYS_ON_TOP    = 0x00008000,   /**< window should always be above others */
    SDL_WINDOW_SKIP_TASKBAR     = 0x00010000,   /**< window should not be added to the taskbar */
    SDL_WINDOW_UTILITY          = 0x00020000,   /**< window should be treated as a utility window */
    SDL_WINDOW_TOOLTIP          = 0x00040000,   /**< window should be treated as a tooltip */
    SDL_WINDOW_POPUP_MENU       = 0x00080000,   /**< window should be treated as a popup menu */
    SDL_WINDOW_KEYBOARD_GRABBED = 0x00100000,   /**< window has grabbed keyboard input */
    SDL_WINDOW_VULKAN           = 0x10000000,   /**< window usable for Vulkan surface */
    SDL_WINDOW_METAL            = 0x20000000,   /**< window usable for Metal view */

    SDL_WINDOW_INPUT_GRABBED = SDL_WINDOW_MOUSE_GRABBED /**< equivalent to SDL_WINDOW_MOUSE_GRABBED for compatibility */
} SDL_WindowFlags;

SDL_CreateRenderer:创建渲染器

cpp 复制代码
SDL_Renderer *SDL_CreateRenderer(SDL_Window * window,int index, Uint32 flags);

flags有以下的类型:

cpp 复制代码
typedef enum
{
    SDL_RENDERER_SOFTWARE = 0x00000001,         /**< The renderer is a software fallback */
    SDL_RENDERER_ACCELERATED = 0x00000002,      /**< The renderer uses hardware
                                                     acceleration */
    SDL_RENDERER_PRESENTVSYNC = 0x00000004,     /**< Present is synchronized
                                                     with the refresh rate */
    SDL_RENDERER_TARGETTEXTURE = 0x00000008     /**< The renderer supports
                                                     rendering to texture */
} SDL_RendererFlags;

SDL_CreateTexture:创建纹理

cpp 复制代码
SDL_Texture *SDL_CreateTexture(SDL_Renderer * renderer,Uint32 format
                                            ,int access, int w,int h);

access有以下的类型:

cpp 复制代码
typedef enum
{
    SDL_TEXTUREACCESS_STATIC,    /**< Changes rarely, not lockable */
    SDL_TEXTUREACCESS_STREAMING, /**< Changes frequently, lockable */
    SDL_TEXTUREACCESS_TARGET     /**< Texture can be used as a render target */
} SDL_TextureAccess;

3.示例

cpp 复制代码
#include <stdio.h>
#include <SDL.h>

#define WINDOW_W 800
#define WINDOW_H 600

const int bpp = 12;
const int pixel_w = 1920, pixel_h = 1080;
unsigned char buffer[pixel_w*pixel_h*bpp / 8];

//Refresh Event
#define REFRESH_EVENT  (SDL_USEREVENT + 1)

int thread_exit = 0;
int refresh_video(void *opaque) 
{
	while (thread_exit == 0) 
	{
		SDL_Event event;
		event.type = REFRESH_EVENT;
		SDL_PushEvent(&event);
		SDL_Delay(40);
	}
	return 0;
}

#undef main
int main(int argc,char* argv[])
{
	/*SDL初始化*/
	SDL_Init(SDL_INIT_VIDEO);
	/*创建窗口*/
	SDL_Window *window = SDL_CreateWindow("SDL SHOW VIDEO", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOW_W, WINDOW_H, SDL_WINDOW_SHOWN);
	/*创建渲染器*/
	SDL_Renderer *render = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
	/*设置渲染器颜色*/
	SDL_SetRenderDrawColor(render, 255, 255, 255, 255);
	/*清空渲染器*/
	SDL_RenderClear(render);
	//YUV420P
	Uint32 pixformat = SDL_PIXELFORMAT_IYUV;
	//创建纹理
	SDL_Texture* sdlTexture = SDL_CreateTexture(render, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
	//添加边框
	int border = 0;
	SDL_Rect sdlRect;
	sdlRect.x = border;
	sdlRect.y = border;
	sdlRect.w = WINDOW_W - border * 2;
	sdlRect.h = WINDOW_H - border * 2;

	//加载文件
	FILE *fp = NULL;
	fp = fopen("./test_yuv420p_1920x1080.yuv", "rb+");
	if (fp == NULL) 
	{
		printf("cannot open this file\n");
		return -1;
	}
	//刷新线程
	SDL_Thread *refresh_thread = SDL_CreateThread(refresh_video, NULL, NULL);
	SDL_Event event;
	while (1)
	{
		//Wait
		SDL_WaitEvent(&event);
		if (event.type == REFRESH_EVENT) 
		{
			if (fread(buffer, 1, pixel_w*pixel_h*bpp / 8, fp) != pixel_w * pixel_h*bpp / 8) 
			{
				// Loop
				fseek(fp, 0, SEEK_SET);
				fread(buffer, 1, pixel_w*pixel_h*bpp / 8, fp);
			}

			SDL_UpdateTexture(sdlTexture, NULL, buffer, pixel_w);
			SDL_RenderClear(render);
			SDL_RenderCopy(render, sdlTexture, NULL, &sdlRect);
			SDL_RenderPresent(render);
			//Delay 40ms
			SDL_Delay(40);
		}
		else if (event.type == SDL_QUIT)
		{
			break;
		}
	}

	SDL_DestroyTexture(sdlTexture);/*释放纹理*/
	SDL_DestroyWindow(window);
	SDL_DestroyRenderer(render);

	return 0;
}

4.更多参考

[总结]FFMPEG视音频编解码零基础学习方法_零基础ffmpeg 雷霄骅-CSDN博客

相关推荐
江上清风山间明月14 小时前
Android 14 screenrecord录制视频失败的原因分析
android·视频·大小·失败·录制·screenrecord·0kb
Likeadust2 天前
国标GB28181设备管理软件EasyGBS国标GB28181视频平台:RTMP和GB28181两种视频上云协议的区别
音视频·视频·安防监控·视频监控
初生牛犊被人煮2 天前
summernote富文本批量上传音频,视频等附件
音频·视频·富文本·summernote·批量上传
安全二次方security²4 天前
2024 RISC-V 中国峰会 演讲幻灯片和视频回放 均已公开
视频·risc-v·中国峰会·risc-v安全·ppt·2024
Garfield20057 天前
从H264视频中获取宽、高、帧率、比特率等属性信息
h.265·视频·h.264·解码
矢量赛奇8 天前
比ChatGPT更酷的AI工具
人工智能·ai·ai写作·视频
橘子味的茶二12 天前
SDL事件相关
sdl
橘子味的茶二13 天前
SDL线程
sdl
橘子味的茶二13 天前
SDL渲染器和纹理
sdl
橘子味的茶二14 天前
SDL打开YUV视频
ffmpeg·音视频·sdl