1 简介
SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。
2 显示流程
(1)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系统
(2)SDL多线程
SDL_CreateThread():创建一个线程
(3)SDL事件
SDL_WaitEvent()等待一个事件
SDL_PushEvent()发送一个事件
3 渲染
(1)原理
SDL数据结构简介
SDL_Window:代表了一个“窗口”
SDL_Renderer:代表了一个“渲染器”
SDL_Texture:代表了一个“纹理”
SDL_Rect:一个简单的矩形结构
对Render操作时实际上分为两个阶段:
- 渲染阶段。在该阶段,用户可以画各种图形渲染到SDL_Surface或SDL_Texture 中;
- 显示阶段。以SDL_Texture为数据,将 SDL_Surfce或SDL_Texture中的数据输出到显示器上。
SDL_SetRenderTarget(renderer, texture) // 渲染到纹理
SDL_SetRenderTarget(renderer, NULL) // 渲染到屏幕
(2)测试
在窗体中,绘制随机出现的方块
#include <iostream>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "sdl2/SDL.h"
};
int screen_w = 800, screen_h = 600;
int main(int argc, char* argv[]) {
int quit = 1;
SDL_Event event;
SDL_Texture* texture;
SDL_Rect rect;
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* renderer = SDL_CreateRenderer(screen, -1, 0);
if (!renderer) {
goto _DWINDOW;
}
SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
SDL_RenderClear(renderer);
SDL_RenderPresent(renderer); //显示
texture = SDL_CreateTexture(renderer,
SDL_PIXELFORMAT_RGBA8888,
SDL_TEXTUREACCESS_TARGET,
screen_w,
screen_h
);
rect.w = 50;
rect.h = 50;
do {
SDL_PollEvent(&event);
switch (event.type) {
case SDL_QUIT:
quit = 0;
break;
default:
break;
}
rect.x = rand() % 600;
rect.y = rand() % 450;
SDL_SetRenderTarget(renderer, texture);
SDL_SetRenderDrawColor(renderer, 0x00, 0x00, 0x00, 0x00);
SDL_RenderClear(renderer);
SDL_RenderDrawRect(renderer, &rect);
SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0x00, 0x00);
SDL_RenderFillRect(renderer, &rect);
SDL_SetRenderTarget(renderer, NULL);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
} while (quit);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
_DWINDOW:
SDL_DestroyWindow(screen);
_EXIT:
SDL_Quit();
return 0;
}
4 实例
4.1 yuv播放器
主要yuv视频格式要提前知道,并具体设置
#include <iostream>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "sdl2/SDL.h"
};
//Bit per Pixel
const int bpp = 12;
int screen_w = 600, screen_h = 600;
const int pixel_w = 640, pixel_h = 360;
unsigned char buffer[pixel_w * pixel_h * bpp / 8];
// Event
#define REFRESH_EVENT (SDL_USEREVENT + 1)
#define QUIT_EVENT (SDL_USEREVENT + 2)
int thread_exit = 0;
int refresh_video(void* opaque) {
while (thread_exit == 0) {
SDL_Event event;
event.type = REFRESH_EVENT;
SDL_PushEvent(&event);
SDL_Delay(40);
}
thread_exit = 0;
//push quit event
SDL_Event event;
event.type = QUIT_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("1.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 == QUIT_EVENT) {
break;
}
}
return 0;
}
4.2 pcm播放器
注意:声卡通过回调函数要数据,而不是用户主动推数据
#define BLOCK_SIZE 4096000
static Uint8 *audio_buf = NULL;
static Uint8 *audio_pos = NULL;
static size_t buffer_len = 0;
//callback function for audio devcie
void read_audio_data(void *udata, Uint8 *stream, int len){
if (buffer_len == 0){
return;
}
SDL_memset(stream, 0, len);
len = (len < buffer_len) ? len : buffer_len;
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
buffer_len -= len;
}
int main(int argc, char* argv[])
{
if (SDL_Init(SDL_INIT_AUDIO)) {
printf("Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
char *path = "1.pcm";
FILE *audio_fd = fopen(path, "rb+");
if (!audio_fd){
SDL_Log("Failed to open");
goto __FAIL;
}
audio_buf = (Uint8*)malloc(BLOCK_SIZE);
if (!audio_buf){
SDL_Log("Failed to malloc");
goto __FAIL;
}
SDL_AudioSpec spec;
spec.freq = 44100;
spec.channels = 2;
spec.format = AUDIO_S16SYS;
spec.silence = 0;
spec.samples = 2048;
spec.callback = read_audio_data;
spec.userdata = NULL;
if (SDL_OpenAudio(&spec, NULL)){
SDL_Log("Failed to malloc");
goto __FAIL;
}
SDL_PauseAudio(0);
while (1){
buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
if (buffer_len == 0) {
break;
}
audio_pos = audio_buf;
while (audio_pos < (audio_buf + buffer_len)) {
SDL_Delay(1);
}
}
SDL_CloseAudio();
__FAIL:
if (audio_buf){
free(audio_buf);
}
if (audio_fd){
fclose(audio_fd);
}
SDL_Quit();
return 0;
}