前面铺垫了这么多,现在终于进入核心的主题了,那就是使用SDL播放视频,本节我们将使用SDL播放YUV视频,也就是做一个YUV播放器。
下面说明一下使用SDL播放YUV视频的基本流程,主要分为两大部分:初始化SDL、循环显示画面。
1. 初始化SDL
1). 初始化SDL (SDL_Init)
2). 创建窗口(SDL_CreateWindow)
3). 基于窗口创建渲染器(SDL_CreateRenderer)
4). 创建纹理(SDL_CreateTexture)
2. 循环显示画面
1). 设置纹理的数据(SDL_UpdateTexture)
2). 纹理复制给渲染目标(SDL_RenderCopy)
3). 显示(SDL_RenderPresent)
具体涉及到每个API的含义,这里就不过多介绍了,在之前的内容中,我们已经都做了说明,下面我们直接开始实战。
实战
// SDL.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include "pch.h" #include <iostream> extern "C" { #include "SDL.h" } //Bit per Pixel const int bpp = 12; int screen_w = 500, screen_h = 500; // 根据不同的YUV视频,来设置不同的 宽 * 高 数据 const int pixel_w = 352, pixel_h = 288; 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; } int main(int argc, char* argv[]) { if (SDL_Init(SDL_INIT_VIDEO)) { printf("Could not initialize SDL - %s\n", SDL_GetError()); return -1; } SDL_Window *window; // 创建SDL窗口 window = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); // 判断是否创建窗口成功 if (!window) { printf("SDL: could not create window - exiting:%s\n", SDL_GetError()); return -1; } // 创建SDL渲染器 SDL_Renderer* sdlRenderer = SDL_CreateRenderer(window, -1, 0); // 声明像素格式 Uint32 pixformat = 0; // IYUV: Y + U + V (3 planes) // YV12: Y + V + U (3 planes) // I420也叫IYUV, 也叫YUV420 pixformat = SDL_PIXELFORMAT_IYUV; // 按照YUV视频的宽高创建SDL纹理对象 SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h); FILE *fp = NULL; fp = fopen("222.yuv", "rb+"); if (fp == NULL) { printf("cannot open this file\n"); return -1; } SDL_Rect sdlRect; 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); //FIX: If window is resize sdlRect.x = 0; sdlRect.y = 0; sdlRect.w = screen_w; sdlRect.h = screen_h; SDL_RenderClear(sdlRenderer); // 将更新后的纹理拷贝到渲染器 SDL_RenderCopy(sdlRenderer, sdlTexture, NULL, &sdlRect); // 渲染器显示画面 SDL_RenderPresent(sdlRenderer); //Delay 40ms -- Dealy时常根据帧率进行调整 SDL_Delay(40); } else if (event.type == SDL_WINDOWEVENT) { //If Resize SDL_GetWindowSize(window, &screen_w, &screen_h); } else if (event.type == SDL_QUIT) { break; } } return 0; }
运行效果: