SDL简介
- 官网:https://www.libsdl.org/
- 文档:http://wiki.libsdl.org/Introduction
- SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。
1.Windows环境搭建
-
下载地址:https://github.com/libsdl-org/SDL/releases/latest
先直接下载
dll
和lib
使用
2.Linux环境搭建
-
下载地址:https://github.com/libsdl-org/SDL/releases/latest
-
下载SDL源码库,
SDL2-devel-2.30.3-mingw.tar.gz
-
解压,然后依次执行命令
./configure make sudo make install .
-
如果出现
Could not initialize SDL - No available video device(Did you set the DISPLAY variable?)
错误,说明系统中没有安装x11的库文件,因此编译出来的SDL库实际上不能用。下载安装sudo apt-get install libx11-dev sudo apt-get install xorg-dev
SDL子系统
SDL将功能分成下列数个子系统(subsystem):
- SDL_INIT_TIMER:定时器
- SDL_INIT_AUDIO:音频
- SDL_INIT_VIDEO:视频
- SDL_INIT_JOYSTICK:摇杆
- SDL_INIT_HAPTIC:触摸屏
- SDL_INIT_GAMECONTROLLER:游戏控制器
- SDL_INIT_EVENTS:事件
- SDL_INIT_EVERYTHING:包含上述所有选项
Window显示: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系统
Window显示:SDL数据结构简介
-
SDL_Window 代表了一个“窗口”
-
SDL_Renderer 代表了一个“渲染器”
-
SDL_Texture 代表了一个“纹理”
-
SDL_Rect 一个简单的矩形结构
-
存储RGB和存储纹理的区别:
比如一个从左到右由红色渐变到蓝色的矩形,用存储RGB的话就需要把矩形中每个点的具体颜色值存储下来;而纹理只是一些描述信息,比如记录了矩形的大小、起始颜色、终止颜色等信息,显卡可以通过这些信息推算出矩形块的详细信息。所以相对于存储RGB而已,存储纹理占用的内存要少的多。
SDL事件
- 函数
- SDL_WaitEvent():等待一个事件
- SDL_PushEvent():发送一个事件
- SDL_PumpEvents():将硬件设备产生的事件放入事件队列,用于读取事件,在调用该函数之前,必须调用SDL_PumpEvents搜集键盘等事件
- SDL_PeepEvents():从事件队列提取一个事件
- 数据结构
- SDL_Event:代表一个事件
SDL线程
SDL多线程
- SDL线程创建:SDL_CreateThread
- SDL线程等待:SDL_WaitThead
- SDL互斥锁:SDL_CreateMutex/SDL_DestroyMutex
- SDL锁定互斥:SDL_LockMutex/SDL_UnlockMutex
- SDL条件变量(信号量):SDL_CreateCond/SDL_DestoryCond
- SDL条件变量(信号量)等待/通知:SDL_CondWait/SDL_CondSingal
YUV显示:SDL视频显示的流程
SDL播放音频PCM-打开音频设备
-
打开音频设备
int SDLCALL SDL_OpenAudio(SDL_AudioSpec *desired, SDL_AudioSpec *obtained); // desired:期望的参数。 // obtained:实际音频设备的参数,一般情况下设置为NULL即可。
-
SDL_AudioSpec
typedef struct SDL_AudioSpec { int freq; // 音频采样率 SDL_AudioFormat format; // 音频数据格式 Uint8 channels; // 声道数: 1 单声道, 2 立体声 Uint8 silence; // 设置静音的值,因为声音采样是有符号的,所以0当然就是这个值 Uint16 samples; // 音频缓冲区中的采样个数,要求必须是2的n次 Uint16 padding; // 考虑到兼容性的一个参数 Uint32 size; // 音频缓冲区的大小,以字节为单位 SDL_AudioCallback callback; // 填充音频缓冲区的回调函数 void *userdata; // 用户自定义的数据 } SDL_AudioSpec;
SDL播放音频PCM-SDL_AudioCallback
-
SDL_AudioCallback
// userdata:SDL_AudioSpec结构中的用户自定义数据,一般情况下可以不用。 // stream:该指针指向需要填充的音频缓冲区。 // len:音频缓冲区的大小(以字节为单位)1024*2*2。 void (SDLCALL *SDL_AudioCallback) (void *userdata, Uint8 *stream, int len);
-
播放音频数据
// 当pause_on设置为0的时候即可开始播放音频数据。设置为1的时候,将会播放静音的值。 void SDLCALL SDL_PauseAudio(int pause_on)
SDL播放音频PCM-代码
-
SDL2-PCM示例代码:
#include <iostream> extern "C" { #include "SDL.h" } #define _CRT_SECURE_NO_WARNINGS 1 // 禁用在VS中关于不安全函数的警告 #define PCM_BUFFER_SIZE (1024 * 2 * 2 * 2) // 每次读取2帧数据,以1024个采样点为一帧 2通道 16bit采样点为例 static Uint8* s_audio_buf = NULL; // 音频PCM数据缓存 static Uint8* s_audio_pos = NULL; // 目前读取的位置 static Uint8* s_audio_end = NULL; // 缓存结束的位置 void releaseResources(FILE* audio_fd, Uint8* s_audio_buf) { // 释放音频PCM数据缓存 if (s_audio_buf) free(s_audio_buf); // 关闭音频文件流 if (audio_fd) fclose(audio_fd); } // 音频设备回调函数 void fill_audio_pcm(void* udata, Uint8* stream, int len) { SDL_memset(stream, 0, len); if (s_audio_pos >= s_audio_end) // 数据读取完毕 { return; } // 数据够了就读预设长度,数据不够就只读部分(不够的时候剩多少就读取多少) int remain_buffer_len = (int)(s_audio_end - s_audio_pos); len = (len < remain_buffer_len) ? len : remain_buffer_len; // 拷贝数据到 stream 并调整音量 SDL_MixAudio(stream, s_audio_pos, len, SDL_MIX_MAXVOLUME / 8); printf("len = %d\n", len); s_audio_pos += len; // 移动缓存指针 } // 提取PCM文件 // ffmpeg -i input.mp4 -codec:a pcm_s16le -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm // 测试PCM文件 // ffplay -ar 44100 -ac 2 -f s16le 44100_16bit_2ch.pcm #undef main int main() { int ret = -1; FILE* audio_fd = NULL; SDL_AudioSpec spec; const char* path = "D:\\FFmpeg_Study\\44100_16bit_2ch.pcm"; size_t read_buffer_len = 0; // 每次缓存的长度 if (SDL_Init(SDL_INIT_AUDIO)) // SDL初始化 { fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError()); return ret; } audio_fd = fopen(path, "rb"); // 打开PCM文件 if (!audio_fd) { fprintf(stderr, "Failed to open pcm file!\n"); releaseResources(audio_fd, s_audio_buf); } s_audio_buf = (uint8_t*)malloc(PCM_BUFFER_SIZE); // 音频参数设置 SDL_AudioSpec spec.freq = 44100; // 采样频率 spec.format = AUDIO_S16SYS; // 采样点格式 spec.channels = 2; // 2通道 spec.silence = 0; // 静音值,0为自动处理 spec.samples = 1024; // 每次读取的采样数量,多久产生一次回调和samples spec.callback = fill_audio_pcm; // 回调函数 spec.userdata = NULL; // 打开音频设备 if (SDL_OpenAudio(&spec, NULL)) { fprintf(stderr, "Failed to open audio device, %s\n", SDL_GetError()); releaseResources(audio_fd, s_audio_buf); } // 播放音频 SDL_PauseAudio(0); int data_count = 0; while (1) { // 从文件中读取PCM数据 read_buffer_len = fread(s_audio_buf, 1, PCM_BUFFER_SIZE, audio_fd); if (read_buffer_len == 0) { break; } data_count += (int)read_buffer_len; // 统计读取的数据总字节数 printf("Now playing %10d bytes data.\n", data_count); s_audio_end = s_audio_buf + read_buffer_len; // 更新buffer的结束位置 s_audio_pos = s_audio_buf; // 更新buffer的起始位置 while (s_audio_pos < s_audio_end) // 主线程等待片刻 { SDL_Delay(2); // 等待PCM数据消耗 spec.samples: 1024 / 44100 = 0.0232... 故不大于2 } } printf("Play PCM finish\n"); SDL_CloseAudio(); // 关闭音频设备 return 0; }