AudioTrack:播放通过 WiFi 传入的声音
Posted
技术标签:
【中文标题】AudioTrack:播放通过 WiFi 传入的声音【英文标题】:AudioTrack: Playing sound coming in over WiFi 【发布时间】:2011-03-14 11:25:44 【问题描述】:我的应用程序中有一个 AudioTrack,它设置为流模式。我想编写通过无线连接接收的音频。 AudioTrack 是这样声明的:
mPlayer = new AudioTrack(STREAM_TYPE,
FREQUENCY,
CHANNEL_CONFIG_OUT,
AUDIO_ENCODING,
PLAYER_CAPACITY,
PLAY_MODE);
参数定义如下:
private static final int FREQUENCY = 8000,
CHANNEL_CONFIG_OUT = AudioFormat.CHANNEL_OUT_MONO,
AUDIO_ENCODING = AudioFormat.ENCODING_PCM_16BIT,
PLAYER_CAPACITY = 2048,
STREAM_TYPE = AudioManager.STREAM_MUSIC,
PLAY_MODE = AudioTrack.MODE_STREAM;
但是,当我使用 write() 将数据写入 AudioTrack 时,它会播放断断续续...调用
byte[] audio = packet.getData();
mPlayer.write(audio, 0, audio.length);
每当通过网络连接接收到数据包时都会生成。有没有人知道为什么它听起来波涛汹涌?也许它与WiFi连接本身有关?我不这么认为,因为当我通过 UDP 将数据从 android 手机发送到另一个源时,声音听起来并不可怕。然后声音听起来很完整,一点也不断断续续……那么有人知道为什么会发生这种情况吗?
【问题讨论】:
你试过玩PLAYER_CAPACITY
吗?我相信您应该使用getMinBufferSize()
来获得正确的值,如下所示:developer.android.com/reference/android/media/AudioTrack.html
我已经尝试过了...但是我从网络获得的数据通常小于我(当前)从 getMinBufferSize 在此设备上获得的 4096。将其设置为 buffersize
我再次查看了这个,在我的情况下,波动是由于大量的数据包丢失/重新传输。在一些资源有限的系统(如 Android)中,程序必须足够快地处理每个单独的 UDP 数据包,以防止数据包丢失。
【参考方案1】:
您知道每秒接收多少字节、比较数据包之间的平均时间以及数据包之间的最长时间吗?如果没有,你可以添加代码来计算它吗?
您需要平均 8000 个样本/秒 * 2 个字节/样本 = 16,000 个字节/秒,以保持流充满。
传入数据包之间的间隔超过 2048 字节/(16000 字节/秒)= 128 毫秒 将导致您的流干涸和音频断断续续。
防止它的一种方法是增加缓冲区大小(PLAYER_CAPACITY)。更大的缓冲区将更能处理传入数据包大小和速率的变化。额外稳定性的代价是在等待缓冲区初始填充时开始播放的延迟更大。
【讨论】:
粗略估计我每秒得到大约 67,200 字节。大约(大约)70 个包,价值 960 字节。我粗略地说是因为这是通过有线连接。当我通过无线时,我想我每秒会收到大约 50 个(也许多一点,也许少一点)包,每秒给我 48,000 字节。我会说这足以让它充满。 :o 经过一些调试并查看了几个 logcat,我注意到这是一个“obtainBuffer 超时”错误......我仍在尝试修复它...... 你解决过这个问题吗?请发布更新。我也有类似的问题....谢谢!!! 我最终使用了 OpenSL ES 音频 API。它包含在 NDK 开发套件中。它更底层,但在我看来它比 Java API 好一点...... @ThaMe90,OpenSL ES 解决波涛汹涌的问题有多好,你能发表一些反馈意见吗?【参考方案2】:我通过将mPlayer.write(audio, 0, audio.length);
放在它自己的Thread
中已经部分解决了这个问题。这确实消除了一些不稳定的情况(因为 write 是一个阻塞调用),但在一两秒后它仍然听起来不稳定。它仍然有 2-3 秒的显着延迟。
new Thread()
public void run()
byte[] audio = packet.getData();
mPlayer.write(audio, 0, audio.length);
.start();
只是一个匿名的Thread
现在正在写作......
有人知道如何解决这个问题吗?
编辑:
经过进一步的检查和调试,我注意到这是 gainBuffer 的问题。 我查看了java code of the AudioTrack 和C++ code of AudioTrack 我注意到它只能出现在 C++ 代码中。
if (__builtin_expect(result!=NO_ERROR, false))
LOGW( "obtainBuffer timed out (is the CPU pegged?) "
"user=%08x, server=%08x", u, s);
mAudioTrack->start(); // FIXME: Wake up audioflinger
timeout = 1;
我注意到这段代码中有一个FIXME
。 :
编辑 2:
我现在尝试了一些不同的方法,不同之处在于我缓冲接收到的数据,然后当缓冲区充满一些数据时,它被写入播放器。但是,播放器持续消耗几个周期,然后obtainBuffer timed out (is the CPU pegged?)
警告启动,并且根本没有数据写入播放器,直到它开始恢复生命......之后,它将不断将数据写入其中,直到缓冲区清空。
另一个细微的区别是我现在将文件流式传输到播放器。也就是说,以块的形式读取它,将这些块写入缓冲区。这模拟了通过 wifi 接收的包裹...
我开始怀疑这是否只是 Android 的操作系统问题,而这不是我可以自己解决的问题……有人对此有任何想法吗?
编辑 3:
我做了更多的测试,但这对我没有任何帮助。这个测试告诉我,我只有在我第一次尝试写入 AudioTrack 时才会出现延迟。这需要 1 到 3 秒才能完成。我通过使用以下代码来做到这一点:
long beforeTime = Utilities.getCurrentTimeMillis(), afterTime = 0;
mPlayer.write(data, 0, data.length);
afterTime = Utilities.getCurrentTimeMillis();
Log.e("WriteToPlayerThread", "Writing a package took " + (afterTime - beforeTime) + " milliseconds");
但是,我得到以下结果: Logcat Image http://img810.imageshack.us/img810/3453/logcatimage.png
这些表明滞后最初发生在开始,之后AudioTrack不断获取数据......我真的需要修复这个......
【讨论】:
__builtin_expect 是一个优化提示。逻辑归结为“如果出错,则写入日志,在音轨上调用 start(),并设置超时标志。”result
被设置在上一行 result = cblk->cv.waitRelative(cblk->lock, seconds(1));
中,这似乎是对条件变量的等待。所以看起来线程安全方案有问题。
此代码中线程安全错误的进一步证据是同一函数后面的这一行:`LOGW_IF(超时,“***严重警告***获得缓冲区()超时但没有'不需要被锁定。我们恢复了,但这不应该发生 (user=%08x, server=%08x)", u, s);`以上是关于AudioTrack:播放通过 WiFi 传入的声音的主要内容,如果未能解决你的问题,请参考以下文章
音视频 — AudioRecorder 和 AudioTrack