AudioQueue 回调和 inUserData 指针生命周期

AudioQueue callback and inUserData pointer lifetime

我一直在 Objective-C 开发一个使用 AudioQueueNewOutput 的应用程序。我有使用其他声音库的经验,所以回调机制很熟悉。但是,我遇到的一个问题是 inUserData 指针。当您调用 AudioQueueStop 时,即使即时设置为真,回调也可以再次 运行。这使得在调用 AudioQueueStop.

的同时释放用户指针中的任何内容是不合适的

这就是kAudioQueueProperty_IsRunning的目的吗?该侦听器的回调是否保证不会再次调用队列回调并且用户指针可以安全释放?

如果那不是正确的机制,那么我很难想出什么是正确的。回调可以尝试同步它认为在 AudioQueueStop 被调用后还剩下多少缓冲区,但这看起来很脆弱。我想避免像在这么多秒后释放内存或泄漏内存这样的黑客攻击。

根据 Apple 的说法,任何类型的内存管理都不应该在音频上下文回调中完成。在启动音频单元或队列之前,应在回调之外预先分配或保留所有与回调相关的内存。因此,保留状态在任何音频回调中应该没有什么意义。

为了最大限度地谨慎起见,我不会在应用程序终止(如果曾经调用过)之前解除分配音频缓冲区。无论如何,与应用程序的总内存占用量相比,音频缓冲区通常非常小。我没有泄漏,而是将它们留在池中直到再次需要(如果有的话)。

如果您真的想解除分配,因为您知道采样率和缓冲区的大小,您可以估计清空所有实时缓冲区需要多长时间。将其翻四倍(考虑到潜在的 OS 双缓冲)并且估计应该是相当安全的。

事实证明,您可以使用 属性 个侦听器来安全地执行此操作。特别是 kAudioQueueProperty_IsRunning。您的对象可以保留其自身的引用,然后在队列停止 运行.

nil

例如,

@interface Foo : NSObject
@end

@implementation Foo {
  AudioQueueRef queue;
  Foo *inUse;
}

static void listener_callback(void *user_data, AudioQueueRef queue,
                              AudioQueuePropertyID prop) {
  Foo *p = (__bridge id)user_data;
  UInt32 res;
  UInt32 resSize = sizeof(res);
  AudioQueueGetProperty(queue, kAudioQueueProperty_IsRunning, &res, &resSize);
  if (resSize == sizeof(UInt32) && res == 0) {
    p->inUse = nil;
  }
}

- (id)init {
  // ... set up self and audio queue
  // create some kind of playback callback
  inUse = self;
  AudioQueueAddPropertyListener(queue, kAudioQueueProperty_IsRunning,
                                listener_callback,
                                (__bridge void *_Nullable)self);
  AudioQueueStart(queue, NULL);
  return self;
}
@end

属性 侦听器回调仅在队列启动和停止时调用。如果 AudioQueueGetProperty 对此 属性 产生 0,则不会再次调用回调,您可以安全地解除分配。