播放音频基本流程
打开音频设备 ---> 设置音频参数 ---> 声卡回调读取音频数据 ---> 播放音频 ---> 关闭音频设备
播放音频基本原则
声卡通过回调函数主动获取音频数据,而非主动推给声卡 音频数据大小由音频参数(采样率、声道数等)决定
SDL音频API
- SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
// 打开音频设备
int SDL_OpenAudio(SDL_AudioSpec * desired, SDL_AudioSpec * obtained)
{
SDL_AudioDeviceID id = 0;
/* Start up the audio driver, if necessary. This is legacy behaviour! */
// 判断是否初始化SDL_INIT_AUDIO
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
return -1;
}
}
/* SDL_OpenAudio() is legacy and can only act on Device ID #1. */
if (open_devices[0] != NULL) {
return SDL_SetError("Audio device is already opened");
}
if (obtained) {
id = open_audio_device(NULL, 0, desired, obtained,
SDL_AUDIO_ALLOW_ANY_CHANGE, 1);
} else {
SDL_AudioSpec _obtained;
SDL_zero(_obtained);
id = open_audio_device(NULL, 0, desired, &_obtained, 0, 1);
/* On successful open, copy calculated values into 'desired'. */
if (id > 0) {
desired->size = _obtained.size;
desired->silence = _obtained.silence;
}
}
SDL_assert((id == 0) || (id == 1));
return (id == 0) ? -1 : 0;
}
- SDL_CloseAudio(void)
//关闭音频设备
void SDL_CloseAudio(void)
{
SDL_CloseAudioDevice(1);
}
void SDL_CloseAudioDevice(SDL_AudioDeviceID devid)
{
close_audio_device(get_audio_device(devid));
}
- SDL_PauseAudio(int pause_on)
// 设置音频播放状态
void SDL_PauseAudio(int pause_on)
{
SDL_PauseAudioDevice(1, pause_on);
}
void SDL_PauseAudioDevice(SDL_AudioDeviceID devid, int pause_on)
{
SDL_AudioDevice *device = get_audio_device(devid);
if (device) {
current_audio.impl.LockDevice(device);
SDL_AtomicSet(&device->paused, pause_on ? 1 : 0);
current_audio.impl.UnlockDevice(device);
}
}
- SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
// 混合音频数据到声卡
void
SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
{
/* Mix the user-level audio format */
SDL_AudioDevice *device = get_audio_device(1);
if (device != NULL) {
SDL_MixAudioFormat(dst, src, device->callbackspec.format, len, volume);
}
}
SDL播放音频DEMO
#include <SDL2/SDL.h>
#define BLOCK_SIZE 4096000
static size_t buf_len = 0;
static Uint8 *audio_buf = NULL;
static Uint8 *audio_pos = NULL;
void read_audio_data(void *udata, Uint8 *stream, int len){
if(buf_len == 0){
return;
}
//清空声卡缓存区
SDL_memset(stream, 0, len);
//设置copy数据大小
len = (len < buf_len) ? len : buf_len;
//copy音频数据
SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
audio_pos += len;
buf_len -= len;
}
int main(int argc, char* argv[]){
int ret = -1;
char *path = "./audio_player.pcm";
FILE *audio_fd = NULL;
if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)){
SDL_Log("Failed to Audio init, ERROR:'%s' \n",SDL_GetError());
return ret;
}
audio_fd = fopen(path, "r");
if (!audio_fd){
SDL_Log("Failed to open pcm file:'%s', ERROR:'%s' \n",path, SDL_GetError());
goto _FAIL;
}
audio_buf = (Uint8*)malloc(BLOCK_SIZE);
if(!audio_buf){
SDL_Log("Failed to alloc memory, ERROR:'%s'! \n", SDL_GetError());
goto _FAIL;
}
SDL_AudioSpec spec;
spec.freq = 44100;
spec.channels = 2;
spec.format = AUDIO_S16SYS;
spec.silence = 0;
//声卡获取音频数据回调函数名称
spec.callback = read_audio_data;
//声卡获取音频数据回调函数参数
spec.userdata = NULL;
//打开音频设备
if(SDL_OpenAudio(&spec, NULL)) {
SDL_Log("Failed to open audio device, ERROR:'%s' \n", SDL_GetError());
goto _FAIL;
}
//设置音频开始暂停:0:开始,1:暂停
SDL_PauseAudio(0);
do{
buf_len = fread(audio_buf, 1, BLOCK_SIZE, audio_fd);
audio_pos = audio_buf;
while (audio_pos < (audio_buf + buf_len)){
SDL_Delay(1);
}
} while (buf_len != 0);
//关闭音频设备
SDL_CloseAudio();
return 0;
_FAIL:
if(audio_buf){
free(audio_buf);
}
if(audio_fd){
fclose(audio_fd);
}
SDL_Quit();
return ret;
}
备注:FFMpeg提取PCM格式音频数据:
ffmpeg -i yhz.1080p.mkv -ar 48000 -ac 2 -f s16le audio_player.pcm
yhz.1080p.mkv: 替换成您的音频文件名称