SDL视频显示

本博客是摘自雷霄骅大神的课程《基于 FFmpeg + SDL 的视频播放器的制作》课程 里的内容,非常适合音视频小白入门,在这里感谢雷神的指导!

目录

  • 视频显示知识
  • SDL简介
  • VC下SDL开发环境的搭建
  • 示例程序运行
  • SDL视频显示的函数
  • SDL视频显示的数据结构
  • 进阶-示例程序运行
  • 进阶-SDL中多线程和事件

视频显示知识

视频显示的流程
• 视频显示的流程,就是将像素数据“画”在屏幕上的过程。
• 例如显示YUV,就是将YUV“画”在系统的窗口中。

SDL简介

作用
• SDL(Simple DirectMedia Layer)库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度。
• 本课程中只涉及到SDL库的一小部分——视频显示部分。

特点
• 跨平台
• 开源

结构
• SDL结构如下所示。可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互。
SDL视频显示_第1张图片

VC下SDL开发环境的搭建

新建控制台工程
• 打开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视频显示_第2张图片

SDL视频显示的函数

SDL视频显示的流程图如下所示
SDL视频显示_第3张图片

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视频显示_第4张图片
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视频显示_第5张图片

进阶-SDL中多线程和事件

SDL多线程
• 函数
SDL_CreateThread():创建一个线程

• 数据结构
SDL_Thread:线程的句柄

SDL事件
• 函数
SDL_WaitEvent()等待一个事件
SDL_PushEvent()发送一个事件

• 数据结构
SDL_Event:代表一个事件

你可能感兴趣的:(音视频学习,音视频,SDL,视频显示)