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博客

相关推荐
小贺儿开发10 小时前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
承渊政道3 天前
用群晖部署OmniBox+pansou:把分散的影视资源全聚合到一个界面里
服务器·windows·网络协议·https·ip·视频·持续部署
巴德鸟3 天前
DaVinci 常用技巧 关键帧 自动字幕 追踪 音频 冻结帧 快捷键 多轨道字幕 扩充边缘
android·编辑器·音视频·视频·davinci·davin
Never_every996 天前
8 个高清 4K 视频素材网址!无水印可商用
大数据·前端·音视频·视频
承渊政道7 天前
群晖配Plex搭建私人影音中心,用起来到底怎么样?
服务器·windows·网络协议·https·ip·视频·持续部署
大学生小郑7 天前
如何定义图像质量,如何评价图像质量
图像处理·学习·音视频·视频
珊瑚怪人10 天前
分享一个Edge浏览器播放H265 RTSP流的问题,涉及到ZLMediaKit、WebRTC
音视频·视频·js·zlmediakit·视频流处理
_Emma_15 天前
【Linux media】Linux Media Driver Framework
linux·服务器·视频
蚁库17 天前
【工具推荐】AixDownloader - 简洁高效的网页视频下载工具
视频
高心星22 天前
鸿蒙6.0应用开发——图片合成视频
视频·图片处理·图片合成视频·鸿蒙6.0·harmonyos6.0