SDL:升级 OpenAudio 至 OpenAudioDevice 后不能播放音频
Posted Time_Limit
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SDL:升级 OpenAudio 至 OpenAudioDevice 后不能播放音频相关的知识,希望对你有一定的参考价值。
OpenAudio 与 OpenAudioDevice 简介
使用 SDL 库可以快捷实现 PCM 音频文件的播放功能。以播放 44100Hz,双声道,S16 格式的音频数据为例:
SDL_Init(SDL_INIT_TIMER|SDL_INIT_AUDIO); // 初始化
SDL_Audiospec audio_spec;
audio_spec.format = AUDIO_S16SYS; // 设置格式
audio_spec.freq = 44100; // 设置频率
audio_spec.channels = 2; // 设置声道数
audio_spec.callback = fill_audio_data; // 设置回调函数
audio_spec.samples = 1024; // 设置缓冲区采样数量
audio_spec.userdata = nullptr;
SDL_OpenAudio(&audio_spec); // 打开音频设备
SDL_PauseAudio(0); // 开始播放
// 其他逻辑:如等待播放结束,清理数据,退出等。
其中回调函数 fill_audio_data
实现如下:
size_t cursor = 0; // 已播放的字节数
std::vector<uint8_t> pcm_data; // 设需播放的pcm数据都存放在 pcm_data 中。
bool end_flag = false;
void fill_audio_data(void *userdata, Uint8 *stream, int len)
SDL_memset(stream, 0, len); // 先清理缓冲区,避免影响后续数据
// 检查剩余数据是否可填满缓冲区,若不能则只填充一部分。
if (len > pcm_data.size() - cursor)
len = pcm_data.size() - cursor;
if (len == 0)
// 无数据可播了,那就不填数据了。
// 顺便更新下结束标志,以便主线程处理。
end_flag = true;
return;
// 填充数据
SDL_MixAudio(stream, &pcm_data[cursor], len, SDL_MIX_MAXVOLUME);
// 更新已播放字节数
cursor += len;
以上代码就是播放PCM功能的主要部分啦。但 SDL_OpenAudio
只能打开一个音频设备,对于同时需要多个音频设备的场景,需用 SDL_OpenAudioDevice
。
SDL_OpenAudioDevice
的函数签名如下:
SDL_AudioDeviceID
SDL_OpenAudioDevice(const char *device, int iscapture,
const SDL_AudioSpec * desired,
SDL_AudioSpec * obtained,
int allowed_changes)
一般使用方式如下:
SDL_AudioSpec audio_spec;
audio_spec.format = AUDIO_S16SYS; // 设置格式
audio_spec.freq = 44100; // 设置频率
audio_spec.channels = 2; // 设置声道数
audio_spec.callback = fill_audio_data; // 设置回调函数
audio_spec.samples = 1024; // 设置缓冲区采样数量
audio_spec.userdata = nullptr;
// SDL_OpenAudio(&audio_spec); // 打开音频设备
auto device_id = SDL_OpenAudioDevice(nullptr, 0, &audio_spec, nullptr, 0);
// SDL_PauseAudio(0); // 开始播放
SDL_PauseAudioDevice(device_id, 0); // 控制 device_id 管理的设备开始播放
问题描述
升级之后发现不能播放了。现象是:
- 有返回值的函数的返回值都正常。如:
SDL_OpenAudioDevice
的返回值device_id
为2
。SDL_Init
的返回值为 0。
- 回调函数
fill_audio_data
有调用。 - 没有声音。。。
解决方案
度娘给的方案
度娘给了几个解决方案,尝试玩之后还是没的声音。。
1. SDL_Init
不要加 SDL_INIT_VIDEO
SDL_Init
不要加 SDL_INIT_VIDEO
。即按如下方式调用:
SDL_Init(SDL_INIT_TIMER|SDL_INIT_AUDIO)
2. OpenAudioDevice
的 allowed_changes
参数设为 SDL_AUDIO_ALLOW_ANY_CHANGE
我的发现
浏览了 SDL_OpenAudio
和 SDL_OpenAudioDevice
的源码,没发现端倪 ,其实也没看懂多少 。不过发现了一个函数 SDL_SetError
,在很多错误返回的地方都会调用该函数记录原因。
于是,我抱着试一试的心态,在回调函数fill_audio_data
中加了一行日志:
void fill_audio_data(void *userdata, Uint8 *stream, int len)
std::cerr << SDL_GetError() << std::endl;
...
然后就得到了内容为 Invalid audio device ID
的错误日志。通过这行日志在源码中找到了 get_audio_device
函数:
static SDL_AudioDevice *
get_audio_device(SDL_AudioDeviceID id)
id--;
if ((id >= SDL_arraysize(open_devices)) || (open_devices[id] == NULL))
SDL_SetError("Invalid audio device ID");
return NULL;
return open_devices[id];
然后使用 lldb 调试,打印调用栈。发现是在 SDL_audio.c:1787 行调进来的,id = 0。这就不太对啦,理论上 id = 2 (和 SDL_OpenAudioDevice 的返回值)才对。
继续阅读 SDL_audio.c:1787 的代码。发现里面写死了 id = 1,基本猜到 SDL_MixAudio
是和 SDL_OpenAudio
配套使用得了。
1783 void
1784 SDL_MixAudio(Uint8 * dst, const Uint8 * src, Uint32 len, int volume)
1785
1786 /* Mix the user-level audio format */
1787 SDL_AudioDevice *device = get_audio_device(1);
1788 if (device != NULL)
1789 SDL_MixAudioFormat(dst, src, device->callbackspec.format, len, volume);
1790
1791
尝试改造回调函数,用 SDL_MixAudioFormat
替换 SDL_MixAudio
。
size_t cursor = 0; // 已播放的字节数
std::vector<uint8_t> pcm_data; // 设需播放的pcm数据都存放在 pcm_data 中。
bool end_flag = false;
void fill_audio_data(void *userdata, Uint8 *stream, int len)
SDL_memset(stream, 0, len); // 先清理缓冲区,避免影响后续数据
// 检查剩余数据是否可填满缓冲区,若不能则只填充一部分。
if (len > pcm_data.size() - cursor)
len = pcm_data.size() - cursor;
if (len == 0)
// 无数据可播了,那就不填数据了。
// 顺便更新下结束标志,以便主线程处理。
end_flag = true;
return;
// 填充数据
SDL_MixAudioFormat(stream,
&pcm_data[cursor], audio_spec.format, len,
SDL_MIX_MAXVOLUME);
// 更新已播放字节数
cursor += len;
如上,将 SDL_MixAudio
替换为 SDL_MixAudioFormat
后就能正常播放声音了。
iPhone SDK SDL_openAudio 支持多任务
【中文标题】iPhone SDK SDL_openAudio 支持多任务【英文标题】:iPhone SDK SDL_openAudio with Multitasking Support 【发布时间】:2011-01-14 16:30:46 【问题描述】:我正在使用 ffmpeg 播放来自在线实时 RTPS 流的音频(因为 Apple 不支持 rtsp 实时流)。
现在我将在后台播放我的 Stream。我在后台启动了一个线程并注册了音乐以获得后台支持。
当应用程序进入后台时,NSThread 会暂停,然后从后台返回后恢复。
如果我开始在使用 Apple 官方框架的应用程序中播放音乐(MP3-Stream),那么当应用程序进入后台时,两个流都会播放。
我能做些什么来解决这个问题?
【问题讨论】:
听起来像是音频会话问题 - 您是否使用过会话属性?见developer.apple.com/library/ios/#documentation/Audio/Conceptual/… 谢谢,这就是解决方案! @Till 将您的评论作为答案。你至少有 +15 等待 ;) 【参考方案1】:好吧,根据大众的需求;),答案来了;
这听起来很像音频会话问题。您应该尝试调整一下会话属性:AudioSessionProgrammingGuide
【讨论】:
以上是关于SDL:升级 OpenAudio 至 OpenAudioDevice 后不能播放音频的主要内容,如果未能解决你的问题,请参考以下文章
iPhone SDK SDL_openAudio 支持多任务
ubuntu 下SDL 声音无法播放,我的电脑能正常听哥的。 Mix_OpenAudio: No available audio device
Failed to open the file:SDL_OpenAudio:DirectSoundCreate:NO audio device found 视频报错这是啥情况