SDL视频显示

本文详细介绍了如何使用SDL库创建一个简单的视频播放器。从视频显示流程、SDL库的介绍到VC环境下SDL的开发环境搭建,再到关键函数和数据结构的解析,一步步展示了如何处理YUV视频数据并在窗口中显示。此外,还提供了两个示例程序,分别演示了基本的视频播放和增加窗口移动、大小调整及暂停功能。通过这些,读者可以深入理解SDL在视频播放中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下内容为整理的学习笔记,出自雷神:基于 FFmpeg + SDL 的视频播放器的制作

SDL视频显示

1、视频显示的流程

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

2、SDL简介

作用

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

特点

  • 跨平台
  • 开源

PS:尽管SDL已经简化了视音频底层交互工作,对于新手来说学习还是有一些难度的

结构
SDL结构如下所示。可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互。

3、VC下SDL开发环境的搭建

(1)新建VC项目

新建->项目->控制台应用->创建

(2)拷贝SDL开发文件

1、头文件(*.h)拷贝至项目文件夹的include子文件夹下
2、导入库文件(*.lib)拷贝至项目文件夹的lib子文件夹下
3、动态库文件(*.dll)拷贝至项目文件夹下

(3)配置开发文件

step1: 打开属性面板
解决方案资源管理器->右键单击项目->属性

step2: 头文件配置
配置属性->C/C+±>常规->附加包含目录,输入“include”(刚才拷贝头文件的目录)

step3: 导入库配置
配置属性->链接器->常规->附加库目录,输入“lib" (刚才拷贝库文件的目录)

配置属性->链接器->输入->附加依赖项,输入“SDL2.lib; SDL2main.lib”(导入库的文件名)

动态库不用配置

(4)测试

step1: 创建源代码文件
在工程中创建一个包含main()函数的C/C++文件(如果已经有了可以跳过这一步)。
step2: 包含头文件

  • 如果是C语言中使用SDL,则直接使用下面代码
#include <iostream>
#include"SDL2/SDL.h"
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;
}
  • 如果是C++语言中使用SDL,则使用下面代码
#include <iostream>
extern "C"{
   #include"SDL2/SDL.h"
}
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已经配置完成。

出现的问题:

原因:SDL_main.h这个文件中给main 取了别名,将主函数main当做SDL_main识别了

解决:将int main 改成 int main(int argc, char *argv[])

4、SDL视频显示的函数

SDL视频显示的流程图如下所示:

5、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系统

6、SDL视频显示的数据结构

SDL视频显示的数据结构如下所示:

6.1 SDL数据结构简介

1、SDL_Window: 代表了一个“窗口”
2、SDL_Renderer:代表了一个“渲染器”
3、SDL_Texture:代表了一个“纹理”
4、SDL_Rect:一个简单的矩形结构

7、进阶-SDL中事件和多线程

7.1 SDL多线程

函数:

SDL_CreateThread():创建一个线程

数据结构

SDL_Thread:线程的句柄

7.2 SDL事件

函数

SDL_WaitEvent()等待一个事件
SDL_PushEvent()发送一个事件

数据结构

SDL_Event:代表一个事件

8、示例程序1:

/**
 * 最简单的SDL2播放视频的例子(SDL2播放RGB/YUV)
 * Simplest Video Play SDL2 (SDL2 play RGB/YUV) 
 *
 * 雷霄骅 Lei Xiaohua
 * leixiaohua1020@126.com
 * 中国传媒大学/数字电视技术
 * Communication University of China / Digital TV Technology
 * http://blog.youkuaiyun.com/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 <stdio.h>
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C"
{
#include "sdl/SDL.h"
 FILE __iob_func[3] = { *stdin,*stdout,*stderr };
};

const int bpp=12;

int screen_w=640,screen_h=360;
const int pixel_w=640,pixel_h=360;

unsigned char buffer[pixel_w*pixel_h*bpp/8];

int main(int argc, char* argv[])
{
	if(SDL_Init(SDL_INIT_VIDEO)) { //1、初始化SDL系统 
		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); //2、创建窗口SDL_Window
	if(!screen) {  
		printf("SDL: could not create window - exiting:%s\n",SDL_GetError());  
		return -1;
	}
	SDL_Renderer* sdlRenderer = SDL_CreateRenderer(screen, -1, 0);  //3、创建渲染器SDL_Renderer

	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);//4、创建纹理SDL_Texture

	FILE *fp=NULL;
	fp=fopen("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);  //5、设置纹理的数据

			sdlRect.x = 0;  
			sdlRect.y = 0;  
			sdlRect.w = screen_w;  
			sdlRect.h = screen_h;  
			
			SDL_RenderClear( sdlRenderer );   
			SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, &sdlRect);  //6、将纹理的数据拷贝给渲染器
			SDL_RenderPresent( sdlRenderer );  //7、显示
			//Delay 40ms
			SDL_Delay(20); //工具函数,用于延时。
			
	}
	SDL_Quit(); //退出SDL系统
	return 0;
}

问题1:

解决:在源文件里加入下面代码:

#pragma comment(lib, "legacy_stdio_definitions.lib")

问题2:

解决:在源文件里加入下面代码:

FILE __iob_func[3] = { *stdin,*stdout,*stderr };

练习

修改源代码。对于测试文件,实现以下几种显示
基本练习

1、二倍速度

SDL_Delay(20);

2、二倍宽高

int screen_w=640,screen_h=360;

3、窗口大小固定为500x500

int screen_w=500,screen_h=500;

4、视频周围包围10像素的“黑框”

sdlRect.x = 10;  
sdlRect.y = 10;  
sdlRect.w = screen_w-20;  
sdlRect.h = screen_h-20; 

5、换一段测试YUV素材进行播放

const int pixel_w=320,pixel_h=180;
fp=fopen("test_yuv420p_320x180.yuv","rb+");

9、示例程序2:

需求:

1、窗口可以移动
2、窗口可以调整大小
3、按下空格键(SPACE)后暂停,再次按下空格后继续播放
4、显示黑白图像
#include <stdio.h>
extern "C"
{
#include "sdl/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;
	fp=fopen("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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值