使用 NSData 解决生产者-消费者问题(用于音频流)

发布于 2024-10-11 05:21:40 字数 992 浏览 3 评论 0原文

我正在使用 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 数据时播放 PCM 数据的正确方法,例如一次复制 8192 个字节。我可能必须使用另一个线程来执行此操作(我现在正在使用 NSOperation),但不清楚如何同时写入和读取缓冲区,最好使用一些更高级别的 Objective-C 方法。

I am using AVAssetReader to copy PCM data from an iPod track to a buffer, which is then played with a RemoteIO audio unit. I am trying to create a separate thread for loading sound data, so that I can access and play data from the buffer while it is being loaded into.

I currently have a large NSMutableData object that eventually holds the entire song's data. Currently, I load audio data in a separate thread using NSOperation like so:

  1. AVAssetReaderOutput copies, at most, 8192 bytes at a time to a CMBlockBuffer
  2. Copy these bytes to a NSData object
  3. Append this NSData object to a larger NSMutableData object (which eventually holds the entire song)
  4. When finished, play the song by accessing each packet in the NSMutableData object

I'm trying to be able to play the song WHILE copying these bytes. I am unsure what a good way to write to and read from a file from the same time is.

A short idea I had:

  1. Create and fill 3 NSData objects, each 8192 bytes in length, as buffers.
  2. Start playing. When I have finished playing the first buffer, load new data into the first buffer.
  3. When I have finished playing the second buffer, load new data into the second. Same for the third
  4. Start playing from the first buffer again, fill the third. And so on.

Or, create one NSData object that holds 3 * 8192 PCM units, and somehow write to and read from it at the same time with two different threads.

I have my code working on two different threads right now. I append data to the array until I press play, at which point it stops (probably because the thread is blocked, but I don't know right now) and plays until it reaches the end of whatever I loaded and causes an EXC_BAD_ACCESS exception.

In short, I want to find the right way to play PCM data while it is being copied, say, 8192 bytes at a time. I will probably have to do so with another thread (I am using NSOperation right now), but am unclear on how to write to and read from a buffer at the same time, preferably using some higher level Objective-C methods.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

葮薆情 2024-10-18 05:21:40

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

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

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

希望有帮助!

I'm doing this exact thing. You will definitely need to play your audio on a different thread (I am doing this with RemoteIO). You will also need to use a circular buffer. You probably want to look up this data structure if you aren't familiar with it as you will be using it a lot for this type of operation. My general setup is as follows:

  • LoadTrackThread starts up and starts loading data from AVAssetReader and storing it in a file as PCM.
  • LoadPCMThread starts up once enough data is loaded into my PCM file and essentially loads that file into local memory for my RemoteIO thread on demand. It does this by feeding this data into a circular buffer whenever my RemoteIO thread gets even remotely close to running out of samples.
  • RemoteIO playback callback thread consumes the circular buffer frames and feeds them to the RemoteIO interface. It also informs LoadPCMThread to wake up when it needs to start loading more samples.

This should be about all you need as far as threads. You will need to have some sort of mutex or semaphore between the two threads to ensure you aren't trying to read your file while you are writing into it at the same time (this is bad form and will cause you to crash). I just have both my threads set a boolean and sleep for a while until it is unset. There is probably a more sophisticated way of doing this but it works for my purposes.

Hope that helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文