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;
}