SDL音视频渲染实战

SDL简介

  • 官网:https://www.libsdl.org/
  • 文档:http://wiki.libsdl.org/Introduction
  • SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。目前SDL多用于开发游戏、模拟器、媒体播放器等多媒体应用领域。

1.Windows环境搭建

2.Linux环境搭建

  • 下载地址:https://github.com/libsdl-org/SDL/releases/latest

  1. 下载SDL源码库,SDL2-devel-2.30.3-mingw.tar.gz

  2. 解压,然后依次执行命令

    ./configure
    make
    sudo make install .
    
  3. 如果出现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;
    }
    
  • 26
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值