1

我正在使用 AVAssetReader 将 PCM 数据从 iPod 轨道复制到缓冲区,然后使用 RemoteIO 音频单元播放。我正在尝试创建一个单独的线程来加载声音数据,这样我就可以在加载缓冲区时访问和播放缓冲区中的数据。

我目前有一个大型 NSMutableData 对象,它最终保存了整首歌曲的数据。目前,我使用 NSOperation 在单独的线程中加载音频数据,如下所示:

  1. AVAssetReaderOutput 一次最多复制 8192 个字节到 CMBlockBuffer
  2. 将这些字节复制到 NSData 对象
  3. 将此 NSData 对象附加到更大的 NSMutableData 对象(最终保存整首歌曲)
  4. 完成后,通过访问 NSMutableData 对象中的每个数据包来播放歌曲

我试图在复制这些字节的同时播放歌曲。我不确定同时写入和读取文件的好方法是什么。

我有一个简短的想法:

  1. 创建并填充 3 个 NSData 对象,每个对象的长度为 8192 字节,作为缓冲区。
  2. 开始播放。当我播放完第一个缓冲区后,将新数据加载到第一个缓冲区中。
  3. 当我播放完第二个缓冲区后,将新数据加载到第二个缓冲区中。第三个也是一样
  4. 再次从第一个缓冲区开始播放,填充第三个。等等。

或者,创建一个包含 3 * 8192 个 PCM 单元的 NSData 对象,并以某种方式使用两个不同的线程同时对其进行写入和读取。

我的代码现在在两个不同的线程上工作。我将数据附加到数组,直到我按下播放,此时它停止(可能是因为线程被阻塞,但我现在不知道)并播放直到它到达我加载的任何内容的末尾并导致 EXC_BAD_ACCESS 异常。

简而言之,我想找到正确的方式在 PCM 数据被复制时播放,比如一次复制 8192 个字节。我可能不得不使用另一个线程(我现在正在使用 NSOperation)这样做,但不清楚如何同时写入和读取缓冲区,最好使用一些更高级别的 Objective-C 方法。

4

1 回答 1

1

我正在做这件事。您肯定需要在不同的线程上播放音频(我正在使用 RemoteIO 执行此操作)。您还需要使用循环缓冲区。如果您不熟悉此数据结构,您可能需要查找它,因为您将在此类操作中大量使用它。我的一般设置如下:

  • LoadTrackThread 启动并开始从 AVAssetReader 加载数据并将其作为 PCM 存储在文件中。
  • 一旦有足够的数据加载到我的 PCM 文件中,LoadPCMThread 就会启动,并且本质上将该文件加载到本地内存中以供我的 RemoteIO 线程按需使用。每当我的 RemoteIO 线程甚至远程接近用完样本时,它都会将此数据输入循环缓冲区来实现这一点。
  • RemoteIO 播放回调线程消耗循环缓冲区帧并将它们提供给 RemoteIO 接口。它还通知 LoadPCMThread 在需要开始加载更多样本时唤醒。

就线程而言,这应该是您所需要的一切。您将需要在两个线程之间使用某种互斥锁或信号量,以确保在同时写入文件时不会尝试读取文件(这是错误的形式,会导致您崩溃)。我只是让我的两个线程都设置一个布尔值并休眠一段时间,直到它被取消设置。可能有一种更复杂的方法可以做到这一点,但它适用于我的目的。

希望有帮助!

于 2011-06-07T04:20:51.560 回答