Bootstrap

SDL相关函数的使用及注意事项(正确关闭SDL音频设备)

本文主要的目的在于讲解SDL2库常用的函数库以及讲述在项目中遇到的使用SDL,将记录SDL的初始化以及如何使用SDL开启渲染音频和关闭。


一、SDL播放音频使用步骤及函数说明

(1)SDL初始化:

int SDLCALL SDL_Init(Uint32 flags)

说明:该函数是用于激活需要激活的子系统的初始化函数,其中该SDL的子系统有很多例如音频和视频等子系统,传入的参数具有如下,常用的有 SDL_INIT_AUDIOSDL_INIT_VIDEO

#define SDL_INIT_TIMER          0x00000001
#define SDL_INIT_AUDIO          0x00000010
#define SDL_INIT_VIDEO          0x00000020 
#define SDL_INIT_JOYSTICK       0x00000200 
#define SDL_INIT_HAPTIC         0x00001000
#define SDL_INIT_GAMECONTROLLER 0x00002000 
#define SDL_INIT_EVENTS         0x00004000
#define SDL_INIT_NOPARACHUTE    0x00100000 
#define SDL_INIT_EVERYTHING ( \
                SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \
                SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \
            )

(2)打开设备函数:

int SDLCALL SDL_OpenAudio(SDL_AudioSpec * desired,SDL_AudioSpec * obtained);

说明:这个函数是用于打开设备使用的,以及通过SDL_AudioSpec * desired传入所需要的参数,如下为该结构体的定义,包括了音频的格式(SDL_AudioFormat format)、音频的通道数(Uint8 channels)以及比较重要的回调函数指针(SDL_AudioCallback callback)等:

typedef struct SDL_AudioSpec
{
    int freq;                   /**< DSP frequency -- samples per second */
    SDL_AudioFormat format;     /**< Audio data format */
    Uint8 channels;             /**< Number of channels: 1 mono, 2 stereo */
    Uint8 silence;              /**< Audio buffer silence value (calculated) */
    Uint16 samples;             /**< Audio buffer size in samples (power of 2) */
    Uint16 padding;             /**< Necessary for some compile environments */
    Uint32 size;                /**< Audio buffer size in bytes (calculated) */
    SDL_AudioCallback callback;
    void *userdata;
} SDL_AudioSpec;

(3)启停音频设备函数:

void SDLCALL SDL_PauseAudio(int pause_on);

说明:在使用 SDL_OpenAudio()函数后,应该需要使用SDL_PauseAudio(0)函数且传入参数0来使SDL调用回调函数来播放音频;若传入的是大于0的值此时将暂停音频播放设备,期间将传入静音数据。

(4)对音频数据进行混音函数

void SDLCALL SDL_MixAudio(Uint8 * dst, const Uint8 * src,Uint32 len, int volume);

说明:这是一个混音函数,可以将两段音频进行混音播放。

三、关闭函数特别说明:

因为当使用了SDL_OpenAudio()函数后,回调函数就会以线程的方式启动的,同时如果当需要关闭SDL音频设备时 ,仅仅使用SDL_CloseAudio()时不够的,下次如果再次使用SDL_OpenAudio()会报错,此时还需要进行一个操作就是需要停止掉回调线程,本文使用了一个参数int is_stop_sdl_audio_player = false;来控制线程的启停。

如下为回调函数对该参数的判断,和退出情况,当is_stop_sdl_audio_player为true时结束线程,完成关闭SDL全部操作。

…..
if (is_stop_sdl_audio_player == true)
{
	//is_stop_sdl_audio_player = false;//清零
	xhlog("exit fillaudio\n");
	return ;
}

…..

四、代码展示

(1)SDL初始化函数

extern int audio_sample_rate;//rtmp中获取得到的音频采样频率,默认是44.1khz
int SDL_INIT_PLAYER(void)
{
	//Init
	int ret = 0;
	if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER| SDL_INIT_VIDEO )) 
	{
		//printf("Could not initialize SDL - %s\n", SDL_GetError());
		xhlog("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}
	//SDL_AudioSpec
	SDL_AudioSpec wanted_spec;
	//wanted_spec.freq = 44100;
	wanted_spec.freq = audio_sample_rate;
	wanted_spec.format = AUDIO_S16SYS;
	wanted_spec.channels = 2;
	wanted_spec.silence = 0;
	wanted_spec.samples = 1024;//1024;
	wanted_spec.callback = fill_audio;
	if (SDL_OpenAudio(&wanted_spec, NULL)<0) {

		//printf("can't open audio.\n");
		xhlog("can't open audio.\n");
		return -1;
	}
	SDL_PauseAudio(0);
	is_stop_sdl_audio_player = false;
	return 0;
}

(2)填充数据播放

/*函数名称:SDL_START_PLAYER
*函数功能:使用SDL播放PCM,使用线程来播放
*传入参数:unsigned char *data_pcm:pcm的数据指针;
*			int len_data_pcm:pcm数据的长度
*函数返回值:成功返回0,失败返回-1;
*函数作者:吴豪乐
*日期:2020年7月29日
*/
#if 0
int SDL_START_PLAYER(unsigned char *data_pcm, int len_data_pcm)
#endif
DWORD WINAPI SDL_START_PLAYER(LPVOID lpParam)
{
	audio_chunk = (Uint8 *)lpParam;
	if (audio_chunk == NULL)
	{
		xhlog("audio audio_chunk is NULL\n");
		return -1;
	}
	//SDL_LockAudio();

	audio_pos = audio_chunk;
	int pcm_buffer_size = 4096;
	
	//Audio buffer length
	audio_len = pcm_buffer_size;
	while (audio_len > 0)//Wait until finish
	{
		SDL_Delay(1);
	}
	return 0;
}

(3)循环读取数据线程函数

/* Audio Callback
 * The audio function callback takes the following parameters: 
 * stream: A pointer to the audio buffer to be filled 
 * len: The length (in bytes) of the audio buffer 
 * 
*/ 
int is_stop_sdl_audio_player = false;
void  fill_audio(void *udata,Uint8 *stream,int len){ 

	int count_played = 0;//已经播放了数据大小
	if (is_stop_sdl_audio_player == true)
	{
		//is_stop_sdl_audio_player = false;//清零
		xhlog("exit fillaudio\n");
		return ;
	}
	if (len == 4096 && audio_len == 0)
	{

		//xhlog("temp:%d\n", temp);
		//if(temp == 4096)
			//audio_len = len;
		SDL_memset(stream, 0, temp);
		//return;//这一句测试一下是否导致噪点,检测后发现这个return注释掉会噪点少很多
	}
	pcm_ptr_save = pcm_ptr;
	pcm_ptr = audio_pos;

	while (len > 0 && audio_len > 0)
	{
		SDL_memset(stream, 0, audio_len);
		if (audio_len == 0)
		{
			continue;
		}
		temp = (len > audio_len ? audio_len : len);

		//SDL_MIX_MAXVOLUME
		SDL_MixAudio(stream, audio_pos, temp, SDL_MIX_MAXVOLUME);
		audio_pos += temp;
		audio_len -= temp;
		stream += temp;
		len -= temp;
	}
} 

(4)停止播放函数

/*函数名称:SDL_STOP_PLAYER
*函数功能:停止SDL播放PCM,关闭线程来播放
*传入参数:无
*函数返回值:成功返回0,失败返回-1;
*函数作者:吴豪乐
*日期:2020年11月27日
*/
void SDL_STOP_PLAYER(void)
{
	printf("stop audioplayer\n");
	is_stop_sdl_audio_player = true;
	SDL_CloseAudio();
}

每写一篇文章都不容易,尊重别人的知识产权才是对自己和技术的尊重。为了避免发生知识产权被侵权的情况,我决定做出以下声明:

1.博客中标注原创的文章,版权归原作者 吴豪乐工作室 所有;

2.未经原作者允许不得转载本文内容,否则将视为侵权;

3.转载或者引用本文内容请注明来源及原作者;

4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。

;