FFmpeg+SDL---SDL视频显示

四章SDL视频显示

在阅读这章节之前建议先读:FFmpeg+SDL-----学习大纲

目录

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

视频显示知识

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

SDL简介

  • 作用
    • SDL(Simple DirectMedia Layer)库的作用说白了就是封装了复杂的视音频底层交互工作,简化了视音频处理的难度。
    • 本课程中只涉及到SDL库的一小部分——视频显示部分。(这个库主要是用来做游戏开发的)
  • 特点
    • 跨平台,程序可以在多平台使用
    • 开源
  • 结构
    • SDL结构如下所示。可以看出它实际上还是调用了DirectX等底层的API完成了和硬件的交互。根据不同的系统调用对应的API。
      在这里插入图片描述

VC下SDL开发环境的搭建

  • 新建控制台工程
    • 打开VC++
    • 文件->新建->项目->Win32控制台应用程序
  • 拷贝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已经配置完成

示例程序运行

SDL视频显示的函数

1、SDL视频显示的流程图如下所示
在这里插入图片描述
2、SDL视频显示函数简介
▫ SDL_Init():初始化SDL系统
▫ SDL_CreateWindow():创建窗口SD L_Window
▫ SDL_CreateRenderer():创建渲染器SDL_Renderer
▫ SDL_CreateTexture():创建纹理SDL_Texture
▫ SDL_UpdateTexture():设置纹理的数据
▫ SDL_RenderCopy():将纹理的数据拷贝给渲染器
▫ SDL_RenderPresent():显示
▫ SDL_Delay():工具函数,用于延时。
▫ SDL_Quit():退出SDL系统

SDL视频显示的数据结构

1、SDL视频显示的数据结构如下所示
在这里插入图片描述
2、SDL数据结构简介
▫ SDL_Window:代表了一个“窗口”,一个窗口可以同时播放多个视频
▫ SDL_Renderer:代表了一个“渲染器”
▫ SDL_Texture:代表了一个“纹理”,一个textuure其实是对应一个YUV
▫ SDL_Rect:一个简单的矩形结构,确认是画在什么位置的,可以是2*2的形式,不只显示一个

源代码1:

#include <stdio.h>

extern "C"
{
#include "sdl/SDL.h"
};

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)) {  
		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("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中事件和多线程

1、SDL多线程
▫ 函数
 SDL_CreateThread():创建一个线程
▫ 数据结构
 SDL_Thread:线程的句柄
2、SDL事件
▫ 函数
 SDL_WaitEvent()等待一个事件
 SDL_PushEvent()发送一个事件
▫ 数据结构
 SDL_Event:代表一个事件

进阶源码

新增一个线程,可以手动拉伸屏幕的宽度,视频的大小也会跟着动态调整。

#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;			//修改全局变量,下一次循环接收到的事件是BREAK_EVENT,然后退出整个循环过程
		}else if(event.type==BREAK_EVENT){
			break;
		}
	}
	SDL_Quit();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_37921201/article/details/89367499
SDL