本博客是摘自雷霄骅大神的课程《基于 FFmpeg + SDL 的视频播放器的制作》课程 里的内容,非常适合音视频小白入门,在这里感谢雷神的指导!
视频显示的流程
• 视频显示的流程,就是将像素数据“画”在屏幕上的过程。
• 例如显示YUV,就是将YUV“画”在系统的窗口中。
作用
• SDL(Simple DirectMedia Layer)库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度。
• 本课程中只涉及到SDL库的一小部分——视频显示部分。
特点
• 跨平台
• 开源
结构
• SDL结构如下所示。可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互。
新建控制台工程
• 打开VC++
• 文件->新建->项目->控制台应用程序
拷贝SDL开发文件
• 头文件(.h)拷贝至项目文件夹的include子文件夹下
• 导入库文件(.lib)拷贝至项目文件夹的lib子文件夹下
• 动态库文件(*.dll)拷贝至项目文件夹下
配置开发文件
• 打开属性面板
解决方案资源管理器->右键单击项目->属性
• 头文件配置
配置属性->C/C+±>常规->附加包含目录,输入“include”(刚才拷贝文件的目录)
• 导入库配置
配置属性->链接器->常规->附加库目录,输入“lib” (刚才拷贝文件
的目录)
配置属性->链接器->输入->附加依赖项,输入“SDL2.lib; SDL2main.lib”(导入库的文件名)
▫ 动态库不用配置
测试
• 创建源代码文件
在工程中创建一个包含main()函数的C/C++文件(如果已经有了可以跳过这一步),后续步骤在该文件中编写源代码。
• 包含头文件
如果是C语言中使用SDL,则直接使用下面代码
#include "SDL2/SDL.h"
如果是C++语言中使用SDL,则使用下面代码
extern "C"
{
#include "SDL2/SDL.h"
}
• main()中调用一个SDL的接口函数
例如下面代码初始化了SDL
int main(int argc, char* argv[]){
if(SDL_Init(SDL_INIT_VIDEO)) {
printf( "Could not initialize SDL - %s\n", SDL_GetError());
} else{
printf("Success init SDL");
}
return 0;
}
如果运行无误,则代表SDL已经配置完成。
/**
* 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
* Simplest Video Play SDL2 (SDL2 play RGB/YUV)
*
* 雷霄骅 Lei Xiaohua
* [email protected]
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
* API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
* API。
*
* This software plays RGB/YUV raw video data using SDL2.
* SDL is a wrapper of low-level API (Direct3D, OpenGL).
* Use SDL is much easier than directly call these low-level API.
*/
#include
extern "C"
{
#include "SDL.h"
};
const int bpp = 12;
//窗口的宽高
int screen_w = 640, screen_h = 360;
//像素的宽高
const int pixel_w = 640, pixel_h = 360;
//保存yuv数据
unsigned char buffer[pixel_w * pixel_h * bpp / 8];
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* screen;
//SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!screen) {
printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
return -1;
}
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
Uint32 pixformat = 0;
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
pixformat = SDL_PIXELFORMAT_IYUV;
SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
FILE* fp = NULL;
fopen_s(&fp,"sintel_640_360.yuv", "rb+");
if (fp == NULL) {
printf("cannot open this file\n");
return -1;
}
SDL_Rect sdlRect;
while (1) {
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);
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
SDL_Delay(40);
}
SDL_Quit();
return 0;
}
SDL视频显示函数简介
• SDL_Init():初始化SDL系统
• SDL_CreateWindow():创建窗口SDL_Window
• SDL_CreateRenderer():创建渲染器SDL_Renderer
• SDL_CreateTexture():创建纹理SDL_Texture
• SDL_UpdateTexture():设置纹理的数据
• SDL_RenderCopy():将纹理的数据拷贝给渲染器
• SDL_RenderPresent():显示
• SDL_Delay():工具函数,用于延时。
• SDL_Quit():退出SDL系统
SDL视频显示的数据结构如下所示
SDL数据结构简介
• SDL_Window
代表了一个“窗口”
• SDL_Renderer
代表了一个“渲染器”
• SDL_Texture
代表了一个“纹理”
• SDL_Rect
一个简单的矩形结构
/**
* 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
* Simplest Video Play SDL2 (SDL2 play RGB/YUV)
*
* 雷霄骅 Lei Xiaohua
* [email protected]
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 本程序使用SDL2播放RGB/YUV视频像素数据。SDL实际上是对底层绘图
* API(Direct3D,OpenGL)的封装,使用起来明显简单于直接调用底层
* API。
*
* This software plays RGB/YUV raw video data using SDL2.
* SDL is a wrapper of low-level API (Direct3D, OpenGL).
* Use SDL is much easier than directly call these low-level API.
*/
#include
extern "C"
{
#include "SDL.h"
};
const int bpp = 12;
int screen_w = 500, screen_h = 500;
const int pixel_w = 320, pixel_h = 180;
unsigned char buffer[pixel_w * pixel_h * bpp / 8];
//Refresh Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
//Break
#define BREAK_EVENT (SDL_USEREVENT + 2)
int thread_exit = 0;
int refresh_video(void* opaque) {
thread_exit = 0;
while (thread_exit == 0) {
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
thread_exit = 0;
//Break
SDL_Event event;
event.type = BREAK_EVENT;
SDL_PushEvent(&event);
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* screen;
//SDL 2.0 Support for multiple windows
screen = SDL_CreateWindow("Simplest Video Play SDL2", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
if (!screen) {
printf("SDL: could not create window - exiting:%s\n", SDL_GetError());
return -1;
}
SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
Uint32 pixformat = 0;
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
pixformat = SDL_PIXELFORMAT_IYUV;
SDL_Texture* sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat, SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);
FILE* fp = NULL;
fopen_s(&fp,"test_yuv420p_320x180.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);
}
else if (event.type == SDL_WINDOWEVENT) {
//If Resize
SDL_GetWindowSize(screen, &screen_w, &screen_h);
}
else if (event.type == SDL_QUIT) {//如果退出
thread_exit = 1;//让线程安全退出
}
else if (event.type == BREAK_EVENT) {
break;
}
}
SDL_Quit();
return 0;
}
SDL多线程
• 函数
SDL_CreateThread():创建一个线程
• 数据结构
SDL_Thread:线程的句柄
SDL事件
• 函数
SDL_WaitEvent()等待一个事件
SDL_PushEvent()发送一个事件
• 数据结构
SDL_Event:代表一个事件