此博文相关知识点从雷神的博客以及视频学习,截图也是用了他的课件,
雷神博客地址:http://blog.csdn.net/leixiaohua1020/
日期:2016.10.1
作者:isshe
github:github.com/isshe
邮箱:i.sshe@outlook.com
SDL基础知识
SDL结构图
SDL函数调用的一般流程
- 最最主要操作的函数是SDL_texture();
- 工作过程大致是:FFMpeg「Decode」解码一帧,交给SDL_texture(), 然后再复制给渲染器,渲染器再显示出来。以此循环。
- 相关函数:待补充(不定期更新)
SDL的一些主要的数据结构
- SDL2支持多窗口显示,主要是依靠SDL_rect().
- 相关数据结构:待补充(不定期更新)
示例代码1:
- 代码
#include <stdio.h>#include "SDL2/SDL.h"#define SCREEN_W 640 //窗口的宽
#define SCREEN_H 360 //窗口的高
#define PIXEL_W 320 //视频像素的宽,要和视频文件相同才能显示正常
#define PIXEL_H 180 //像素的高
#define BPP 12 //像素深度:指存储每个像素所用的位数(bit)
#define BUF_LEN ((PIXEL_W) * (PIXEL_H) * (BPP) / 8) //存一帧的需要空间const int bpp = BPP;
int screen_w = SCREEN_W;
int screen_h = SCREEN_H;
const int pixel_w = PIXEL_W;
const int pixel_h = PIXEL_H;unsigned char buffer[BUF_LEN+1];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_w * screen_hscreen = 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;int i = 5;while(i >= 0){//一次读1byte,总共读一帧if (fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp) != pixel_w*pixel_h*bpp/8){// Loopfseek(fp, 0, SEEK_SET);
// fread(buffer, 1, pixel_w*pixel_h*bpp/8, fp);i--;continue;}//更新纹理数据SDL_UpdateTexture( sdlTexture, NULL, buffer, pixel_w);//(x,y)是窗口左上边开始的点。//w,h是整个像素窗口宽和高(注意不是整个窗口)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,一般视频都是这个,25帧/s.SDL_Delay(40);}SDL_Quit();return 0;
}
- 编译:
gcc1_SDL_create_window.c -o 1_SDL_create_window.out -O2 -Wall -g -lSDL2 -lSDL2main
结果截图:
* 注意,这个程序如果是在windows下面,窗口是不能移动的,鼠标放上去也是忙的状态。在下一个程序中修改程序,使它能移动,以及自动适应窗口大小。
示例程序2:
- 代码:
#include <stdio.h>
#include <errno.h>
#include <string.h>#include <SDL2/SDL.h>#define SCREEN_W 640 //窗口的宽
#define SCREEN_H 360 //窗口的高
#define PIXEL_W 320 //视频像素的宽,要和视频文件相同才能显示正常
#define PIXEL_H 180 //像素的高
#define BPP 12 //像素深度:指存储每个像素所用的位数(bit)
#define BUF_LEN ((PIXEL_W) * (PIXEL_H) * (BPP) / 8) //存一帧的需要空间
#define FILENAME "test_yuv420p_320x180.yuv"
#define MY_DEFINE_REFRESH_EVENT (SDL_USEREVENT + 1)
#define MY_DEFINE_BREAK_EVENT (SDL_USEREVENT + 2)int thread_exit = 0;
static int refresh_func(void *arg)
{SDL_Event event;thread_exit = 0;while(0 == thread_exit){event.type = MY_DEFINE_REFRESH_EVENT;SDL_PushEvent(&event); //发送一个事件,使主线程继续运行SDL_Delay(40);}//子线程退出后发送事件给主线程,使主线程也退出thread_exit = 0;event.type = MY_DEFINE_BREAK_EVENT;SDL_PushEvent(&event);return 0;
}const int bpp = BPP;int main(int argc, char *argv[])
{int screen_w = SCREEN_W;int screen_h = SCREEN_H;const int pixel_w = PIXEL_W;const int pixel_h = PIXEL_H;unsigned char buffer[BUF_LEN + 1]; //注意类型char filename[256] = FILENAME;SDL_Window *screen = NULL; //窗口数据结构SDL_Renderer *sdlRenderer = NULL; //渲染器数据结构Uint32 pixformat = 0;SDL_Texture *sdlTexture = NULL; //主要操作的FILE *fp = NULL;SDL_Rect sdlRect;
// SDL_Thread *refresh_thread = NULL; //线程数据结构SDL_Event event; //事件数据结构//注意:可以把文件传进来了,但是如果不使用ffmmpeg的函数还不知道怎么改像素值,以使视频正常显示!!!!!if (argc > 2){printf("Usage: ./*.out videofile.yuv\n");return 0;}else if (argc == 2){memcpy(filename, argv[1], strlen(argv[1]) + 1);
// filename[strlen(argv[1])] = '\0';}printf("video file name: %s\n", filename);if (SDL_Init(SDL_INIT_VIDEO)){printf("Couldn't initialize SDL - %s\n", SDL_GetError());return (-1);}screen = SDL_CreateWindow("isshe Video Player SDL2",SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,screen_w, screen_h,SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);if (!screen){printf("SDL:Couldn't not create window error: %s\n", SDL_GetError());return (-1);}//创建渲染器,-1,0不懂什么意思,再看这个函数的定义sdlRenderer = SDL_CreateRenderer(screen, -1, 0);//在pixels.h中,大概是指定输入数据格式?不懂!pixformat = SDL_PIXELFORMAT_IYUV;sdlTexture = SDL_CreateTexture(sdlRenderer, pixformat,SDL_TEXTUREACCESS_STREAMING, pixel_w, pixel_h);//打开文件fp = fopen(filename, "r");if (NULL == fp){printf("Open file error:%s\n", strerror(errno));return (-1);}//新建线程
// refresh_thread =SDL_CreateThread(refresh_func, NULL, NULL);while(1){//等待一个事件SDL_WaitEvent(&event); //事件的信息存到结构中了//处理事件, 尝试使用自定义的事件if (event.type == MY_DEFINE_REFRESH_EVENT){//读一帧if (fread(buffer, 1, BUF_LEN, fp) != BUF_LEN) //出错或结尾{//重定位会文件头部fseek(fp, 0, SEEK_SET);continue; //}//更新纹理,但是不懂最后一个参数,是一次更新一行吗?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);}else if (event.type == SDL_WINDOWEVENT){//获取像素窗口的大小,窗口拉伸的时候用这个则会自动调整SDL_GetWindowSize(screen, &screen_w, &screen_h);}else if (event.type == SDL_QUIT){thread_exit = 1;}else if (event.type == MY_DEFINE_BREAK_EVENT) //线程结束,主线程也结束{break;}}fclose(fp);SDL_Quit();return 0;
}
- 程序中主线程阻塞等待事件,子线程发送事件后主线程继续运行。
程序中自定义了两个事件类型,用以说明事件类型可自定义。
编译:
gcc 2_SDL_pthread_event.c -o 2_SDL_pthread_event.out -O2 -Wall -g -lSDL2 -lSDL2main
- 运行结果:
- 可以随意拉伸窗口。
- 可以关闭。
参考资料:
- 雷神的视频
- SDL2.0 源码
资料下载:http://download.csdn.net/detail/i_scream_/9644380