我正在使用 ioUnit 的渲染回调将音频数据存储到循环缓冲区中:
OSStatus ioUnitRenderCallback(
void *inRefCon,
AudioUnitRenderActionFlags *ioActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList *ioData)
{
OSStatus err = noErr;
AMNAudioController *This = (__bridge AMNAudioController*)inRefCon;
err = AudioUnitRender(This.encoderMixerNode->unit,
ioActionFlags,
inTimeStamp,
inBusNumber,
inNumberFrames,
ioData);
// Copy the audio to the encoder buffer
TPCircularBufferCopyAudioBufferList(&(This->encoderBuffer), ioData, inTimeStamp, kTPCircularBufferCopyAll, NULL);
return err;
}
然后我想从循环缓冲区中读取字节,将它们提供给 libLame,然后提供给 libShout。 我已经尝试启动一个线程并使用 NSCondition 使其等待数据可用,但这会导致由于在 Core Audio 回调上使用锁而导致的各种问题。
推荐的方法是什么?
提前致谢。
关于我如何实现 Adam 的回答的更多细节
我最终采纳了 Adam 的建议并像这样实现了它。
制作人
我在 Core Audio Render 回调中使用 TPCircularBufferProduceBytes 将字节添加到循环缓冲区。在我的例子中,我有非交错的音频数据,所以我最终使用了两个循环缓冲区。
消费者
- 我使用 pthread_create 产生了一个新线程
- 在新线程中创建一个新的 CFTimer 并将其添加到当前 CFRunLoop(0.005 秒的间隔似乎效果很好)
- 我告诉当前的 CFRunLoop 运行
- 在我的计时器回调中,我对音频进行编码并将其发送到服务器(如果没有缓冲数据则快速返回)
- 我还有一个 5MB 的缓冲区大小,这似乎工作正常(2MB 使我超限)。这似乎有点高:/
最佳答案
使用重复计时器(NSTimer 或 CADisplayLink)轮询无锁循环缓冲区或 FIFO。如果缓冲区中没有足够的数据,则跳过工作,然后返回(到运行循环)。这是有效的,因为你知道高精度的采样率,以及你喜欢或需要一次处理多少数据,所以可以将轮询率设置得稍微快一点,为了安全起见,但仍然非常接近与使用条件锁的效率相同。
不建议在实时音频线程回调中使用信号量或锁(或任何其他具有不可预测延迟的东西)。
关于ios - 与核心音频线程同步,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21978305/