与Core Audio Thread同步

时间:2014-02-24 03:18:30

标签: ios multithreading performance audio core-audio

我正在使用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回调上使用锁定而导致各种问题。

建议的方法是什么?

提前致谢。


关于我如何实施亚当回答的更多细节

我最终接受了亚当的建议并实施了它。

生产者

我在Core Audio Render回调中使用TPCircularBufferProduceBytes将字节添加到循环缓冲区。在我的情况下,我有非交错的音频数据,所以我最终使用了两个循环缓冲区。

消费

  1. 我使用pthread_create
  2. 生成一个新线程
  3. 在新线程中创建一个新的CFTimer并将其添加到当前 CFRunLoop(0.005秒的间隔似乎运作良好)
  4. 我告诉当前的CFRunLoop运行
  5. 在我的计时器回调中,我对音频进行编码并将其发送到服务器(如果没有缓冲数据则快速返回)
  6. 我的缓冲区大小也只有5MB,看起来效果很好(2MB让我超支)。这看起来有点高:/

2 个答案:

答案 0 :(得分:3)

使用重复计时器(NSTimer或CADisplayLink)轮询无锁循环缓冲区或FIFO。如果缓冲区中没有足够的数据,则跳过工作,然后返回(运行循环)。这是因为您知道高精度的采样率,以及您希望或一次需要处理多少数据,因此可以将轮询速率设置得稍快一些,为安全起见,但仍然非常接近与使用条件锁相同的效率。

不建议在实时音频线程回调中使用信号量或锁(或任何其他具有不可预测延迟的内容)。

答案 1 :(得分:2)

你走在正确的轨道上,但你不需要NSCondition。你绝对不想阻止。您正在使用的循环缓冲区实现是无锁的,应该可以解决问题。在音频渲染回调中,通过调用TPCircularBufferProduceBytes将数据放入缓冲区。然后在阅读器上下文(计时器回调是好的,如hotpaw建议的那样),调用TPCircularBufferTail来获取尾指针(读取地址)和要读取的可用字节数,然后调用TPCircularBufferConsume来做实际阅读。现在你已经完成了转移而没有采取任何锁定。只要确保你分配的缓冲区足够大,可以处理由于某种原因你的读取器线程被操作系统阻止的最坏情况,否则你可能会遇到缓冲区溢出情况并丢失数据。