【问题标题】:AudioQueue callback and inUserData pointer lifetimeAudioQueue 回调和 inUserData 指针生命周期
【发布时间】:2017-12-25 22:15:39
【问题描述】:

我一直在开发一个使用 AudioQueueNewOutput 的 Objective-C 应用程序。我有其他声音库的经验,所以回调机制很熟悉。但是,我遇到的一个问题是inUserData 指针。当您调用 AudioQueueStop 时,即使立即设置为 true,回调也可以再次运行。这使得在调用 AudioQueueStop 的同时释放用户指针中的任何内容是不合适的。

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

如果这不是正确的机制,那么我很难想到可能是什么。在调用AudioQueueStop 之后,回调可能会尝试同步它认为还剩下多少缓冲区,但这似乎很脆弱。我想避免像这么多秒后解除分配或内存泄漏这样的黑客攻击。

【问题讨论】:

    标签: objective-c callback core-audio


    【解决方案1】:

    根据 Apple 的说法,任何类型的内存管理都不应该在音频上下文回调中完成。在启动音频单元或队列之前,应在回调之外预先分配或保留回调感兴趣的所有内存。因此,在任何音频回调中,保留状态都应该是无关紧要的。

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

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

    【讨论】:

    • 我就是这样做的。它总是看起来很老套,但它是解决问题的实用方法。只需从 dealloc 中释放 dispatch_after 块中的渲染内存即可。
    • 明确地说,我希望释放的不仅仅是缓冲区,尽管我也想释放它们。在我的例子中,给回调的用户指针想要释放,但它不能,因为回调可能仍在使用它。所以那里的内存使用量更大。
    【解决方案2】:

    事实证明,您可以使用属性侦听器安全地执行此操作。特别是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,则不会再次调用回调,您可以安全地解除分配。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-17
      • 1970-01-01
      • 2020-02-13
      • 1970-01-01
      • 1970-01-01
      • 2020-06-03
      相关资源
      最近更新 更多