1.SDL使用的基本步骤
- SDL Init/sDL _Quit()
- SDL_CreateWindow()/SDL_DestoryWindow()
- SDL CreateRender()
SDL_Windows *windows = NULL;
SDL_Init(SDL_INIT_VIDEO);
window = SDL_CreateWindow("SDL2 Windows",
200,
200, 640,
480,
SDL_WINDOW_SHOWN);
if(!window) {
printf("Couldn't create window\n");
goto __EXIT;
}
SDL_DestroyWindow(window);
__EXIT:
SDL_Quit();
2.SDL窗口渲染
SDL渲染窗口
- SDL _CreateRender/SDL_DestoryRenderer
- SDL RenderClear
- SDL RenderPresent
render = SDL_CreateRenderer(window, -1, 0);
if(!render) {
SDL_Log("Failed to create renderer\n");
goto __DWINDOW;
}
SDL_SetRenderDrawColor(render, 255, 0, 0, 255);
SDL_RenderClear(render);
SDL_RenderPresent(render);
SDL_Delay(5000);
__DWINDOW:
SDL_DestroyWindow(window);
3.SDL事件
SDL事件基本原理
- SDL将所有事件都存放在一个队列中
- 所有对事件的操作,其实就是对队列的操作
SDL事件种类
- SDL WindowEvent:窗口事件
- SDL_KeyboardEvent:键盘事件
- SDL MouseMotionEvent:鼠标事件
- 自定义事件
do{
SDL_Event event;
SDL_WaitEvent(&event);
switch(event.type) {
case SDL_QUIT:
quit = 0;
break;
default:
SDL_Log("event type is %d\n", event.type);
}
}while(quit);
4.纹理渲染
SDL纹理相关 API
- SDL CreateTexture()
format : YUV, RGBaccess :Texture类型,Target,Stream - SDL_DestroyTexture()
SDL渲染相关API
- SDL SetRenderTarget()
- SDL _RenderClear()
- SDL_RenderCopy()
- SDL RenderPresent()
到此终于可以写出完整版代码了:
#include <stdio.h>
#include <SDL.h>
int main(int argc, char const *argv[])
{
SDL_Windows *windows = NULL;
SDL_Renderer *renderer = NULL;
int quit = 1;
SDL_Texture *texture = NULL;
SDL_Rect rect;
rect.w = 30;
rect.h = 30;
window = SDL_CreateWindow("SDL2 Windows",
200,
200, 640,
480,
SDL_WINDOW_SHOWN);
if(!window) {
printf("Couldn't create window\n");
goto __EXIT;
}
render = SDL_CreateRenderer(window, -1, 0);
if(!render) {
SDL_Log("Failed to create renderer\n");
goto __DWINDOW;
}
// SDL_SetRenderDrawColor(render, 255, 0, 0, 255);
// SDL_RenderClear(render);
// SDL_RenderPresent(render);
texture = SDL_CreateTexture(render, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET,640,480);
if(!texture){
SDL_Log("Failed to Create Texture!\n");
goto _RENDER;
}
do{
SDL_Event event;
// SDL_WaitEvent(&event);
SDL_PollEvent(&event);
switch(event.type) {
case SDL_QUIT:
quit = 0;
break;
default:
SDL_Log("event type is %d\n", event.type);
}
rect.x = rand() % 640;
rect.y = rand() % 480;
SDL_SetRenderTarget(render, texture);
SDL_SetRenderDrawColor(render, 0, 0, 0, 0);
SDL_RenderClear(render);
SDL_RenderDrawRect(render, &rect);
SDL_SetRenderDrawColor(render, 255, 0, 0, 0);
SDL_RenderFillRect(render, &rect);
SDL_SetRenderTarget(render, NULL);
SDL_RenderCopy(render, texture, NULL, NULL);
SDL_RenderPresent(render);
}while(quit);
SDL_DestroyTexture(texture);
_RENDER:
SDL_DestroyRenderer(render);
__DWINDOW:
SDL_DestroyWindow(window);
__EXIT:
SDL_Quit();
return 0;
}
5.YUV视频播放器
创建线程
- SDL_CreateThread
fn:线程执行函数
name:线程名
data:执行函数参数
SDL更新纹理
- SDL_UpdateTexutre()
- SDL_UpdateYUVTexture()
核心代码:
do {
//Wait
SDL_WaitEvent(&event);
if(event.type==REFRESH_EVENT){
//not enought data to render
if((video_pos + yuv_frame_len) > video_end){
//have remain data, but there isn't space
remain_len = video_end - video_pos;
if(remain_len && !blank_space_len) {
//copy data to header of buffer
memcpy(video_buf, video_pos, remain_len);
blank_space_len = BLOCK_SIZE - remain_len;
video_pos = video_buf;
video_end = video_buf + remain_len;
}
//at the end of buffer, so rotate to header of buffer
if(video_end == (video_buf + BLOCK_SIZE)){
video_pos = video_buf;
video_end = video_buf;
blank_space_len = BLOCK_SIZE;
}
//read data from yuv file to buffer
if((video_buff_len = fread(video_end, 1, blank_space_len, video_fd)) <= 0){
fprintf(stderr, "eof, exit thread!");
thread_exit = 1;
continue;// to wait event for exiting
}
//reset video_end
video_end += video_buff_len;
blank_space_len -= video_buff_len;
printf("not enought data: pos:%p, video_end:%p, blank_space_len:%d\n", video_pos, video_end, blank_space_len);
}
SDL_UpdateTexture( texture, NULL, video_pos, video_width);
//FIX: If window is resize
rect.x = 0;
rect.y = 0;
rect.w = w_width;
rect.h = w_height;
SDL_RenderClear( renderer );
SDL_RenderCopy( renderer, texture, NULL, &rect);
SDL_RenderPresent( renderer );
printf("not enought data: pos:%p, video_end:%p, blank_space_len:%d\n", video_pos, video_end, blank_space_len);
video_pos += yuv_frame_len;
}else if(event.type==SDL_WINDOWEVENT){
//If Resize
SDL_GetWindowSize(win, &w_width, &w_height);
}else if(event.type==SDL_QUIT){
thread_exit=1;
}else if(event.type==QUIT_EVENT){
break;
}
}while ( 1 );
6.PCM音频播放器
播放音频的基本原则
- 声卡向你要数据而不是你主动推给声卡
- 数据的多少由音频参数决定的
SDL音频API
- SDL_OpenAudio/SDL_CloseAudio
- SDL PauseAudio
- SDL MixAudio
//SDL initialize
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)){
fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
return ret;
}
//open pcm file
audio_fd = fopen(path, "rb");
if(!audio_fd){
fprintf(stderr, "Failed to open pcm file!\n");
goto __FAIL;
}
//alloc memory for audio
audio_buf = (Uint8*)malloc(BLOCK_SIZE);
if(!audio_buf){
goto __FAIL;
}
//SDL_AudioSpec
spec.freq = 44100;;
spec.format = AUDIO_S16SYS;
spec.channels = 2;
spec.silence = 0;
spec.samples = 1024;
spec.callback = read_audio_data;;
spec.userdata = NULL;
//open audio devcie
if(SDL_OpenAudio(&spec, NULL)){
fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError());
goto __FAIL;
}
//play audio
SDL_PauseAudio(0);
do{
//read data from pcm file
buffer_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
fprintf(stderr, "block size is %zu\n", buffer_len);
audio_pos = audio_buf;
//the main thread wait for a moment
while(audio_pos < (audio_buf + buffer_len)) {
SDL_Delay(1);
}
}while(buffer_len !=0);
//close audio device
SDL_CloseAudio();
ret = 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;
printf("len=%d\n", len);
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
buffer_len -= len;
}