在 iOS 中通过网络连接逐渐播放 WAV 文件

Posted

技术标签:

【中文标题】在 iOS 中通过网络连接逐渐播放 WAV 文件【英文标题】:Playing back a WAV file streamed gradually over a network connection in iOS 【发布时间】:2014-07-01 03:51:47 【问题描述】:

我正在使用行为如下的第三方 API:

我必须连接到它的 URL 并发出我的请求,这涉及到 POST 请求数据; 然后远程服务器一次“块”发回相应的 WAV 数据(我在 NSURLConnectionDataDelegate 的 didReceiveData 回调中收到)。

为了论证,“块”是指数据的任意“下一部分”,不保证它对应于任何有意义的音频划分(例如,它可能未与特定的音频帧倍数对齐,每个块中的字节数只是一些任意数字,每个块可以不同,等等)。

现在——如果我错了,请纠正我,我不能简单地使用 AVAudioPlayer,因为我需要 POST 到我的 URL,所以我需要通过 NSURLConnection“手动”拉回数据。

那么...鉴于上述情况,那么当音频从电线上传下来时,对我来说最轻松的播放音频的方法是什么? (我很欣赏我可以连接所有字节数组,然后在最后将整个内容传递给 AVAudioPlayer - 只是这会延迟播放的开始,因为我必须等待所有数据。)

【问题讨论】:

我对视频流的现代/提议标准的理解是提供一个 XML 文件,有点像 RSS 提要,包含最近的 5 或 10 个“块”。客户端轮询 XML URL 并在新块出现时下载它们。 我同意我正在处理的第三方 API 是笨拙和落后的。但不幸的是,这是我必须处理的! 【参考方案1】:

我将对解决方案进行鸟瞰。我认为这将极大地帮助您找到具体的编码解决方案。

ios 提供了大量的音频 API,其中有几个可用于播放音频。您选择其中哪一个取决于您的特定要求。正如您已经写的那样,AVAudioPlayer 类不适合您的情况,因为有了这个,您需要在开始播放音频的那一刻知道所有音频数据。显然,流媒体并非如此,因此我们必须寻找替代方案。

Audio Queue Services 在易用性和多功能性之间取得了很好的平衡,我向您推荐。另一种选择是音频单元,但它们是低级 C API,因此使用起来不太直观,并且有很多陷阱。所以坚持使用音频队列。

音频队列允许您定义在需要更多音频数据进行播放时从 API 调用的回调函数 - 类似于网络代码的回调,当有可用数据时调用。

现在的难点是如何连接两个回调,一个提供数据,一个请求数据。为此,您必须使用缓冲区。更具体地说,一个队列(不要将此队列与音频队列的东西混淆。音频队列服务是 API 的名称。另一方面,queue I'm talking about next 是一个容器对象)。为了清楚起见,我将其称为一个缓冲区队列。

要将数据填充到缓冲区队列中,您将使用网络回调函数,该函数从网络向您提供数据。音频回调函数会从缓冲区队列中取出数据,当需要更多数据时,音频队列服务会调用该函数。

你必须找到一个支持并发访问的缓冲区队列实现(也就是线程安全的),因为它将从两个不同的线程访问,音频线程和网络线程。 或者找到一个已经线程安全的缓冲区队列实现,您可以自己处理线程安全,例如通过在某个dispatch queue (3rd kind of queue here; yes, Apple and IT love them) 上执行所有处理缓冲区队列的代码。

现在,如果有的话会发生什么

调用了音频回调并且您的缓冲区队列为空,或者

调用了网络回调并且你的缓冲队列已经满了?

在这两种情况下,各自的回调函数都无法正常进行。如果没有可用的音频数据,则音频回调函数无法提供音频数据,如果缓冲区队列已满,则网络回调函数无法存储传入的数据。

在这些情况下,我会首先尝试阻止进一步的执行,直到有更多数据可用或相应的空间可用于存储数据。在网络方面,这很可能会奏效。在音频方面,这可能会导致问题。如果它在音频方面引起问题,您有一个简单的解决方案:如果您没有数据,只需提供静音作为数据。这意味着您需要向音频队列服务提供零帧,它将作为静音播放以填补空白,直到网络提供更多数据。 这是所有流媒体播放器在音频突然停止时使用的概念,它会在某种旋转图标旁边告诉您“缓冲”,表示您必须等待,没人知道要等待多长时间。

【讨论】:

请随时向我询问有关其中一点的详细说明。我还可以添加代码来概述概念的一部分。 谢谢 - 原则上,这一切听起来都是可行的 - 尽管它表明应该是一项简单的任务由于某种原因变得虚假复杂......!

以上是关于在 iOS 中通过网络连接逐渐播放 WAV 文件的主要内容,如果未能解决你的问题,请参考以下文章

在 Linux 中通过 PHP 使用 SoX

在 iOS 中通过蓝牙播放时从内置麦克风录制

如何使用 python 连接两个 wav 文件?

在python中通过调制解调器发送wav声音

如何在 ipad 上使用 javascript 在网络浏览器中播放 wav 文件

如何在 iOS 中添加带有 PCM 数据/缓冲区的可播放(如 wav、wmv)标头?