2011-03-14 62 views
10

我在我的应用程序中设置了AudioTrack,它被设置为Stream模式。我想写我通过无线连接收到的音频。该AudioTrack声明如下:AudioTrack:通过WiFi播放声音

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; 

然而,当我写数据与写()的AudioTrack,它将发挥波涛汹涌的...请致电

byte[] audio = packet.getData(); 
mPlayer.write(audio, 0, audio.length); 

只要通过网络连接接收到数据包,有人有一个想法,为什么它听起来波涛汹涌?也许这与WiFi连接本身有关系?我不这么认为,因为当我通过UDP将数据从Android手机发送到另一个来源时,声音听起来不太可怕。声音听起来很完整,而且没有波涛汹涌......所以有人会有这样的想法吗?

+0

你有没有试过玩PLAYER_CAPACITY?我相信你应该使用'getMinBufferSize()'来获得正确的值,如下所示:http://developer.android.com/reference/android/media/AudioTrack.html – gary 2011-03-14 12:41:54

+0

我试过了......但是我从网络获得的数据通常比从getMinBufferSize获得此设备的4096(当前)少。将其设置为缓冲区大小 ThaMe90 2011-03-14 12:46:55

+0

我再看看这个,在我的情况下,波涛起伏是由于大量的数据包丢失/重传。在某些资源有限的系统(如Android)中,程序必须足够快地处理每个单独的UDP数据包,以防止数据包丢失。 – yorkw 2013-04-16 02:43:44

回答

5

我已经部分地通过将mPlayer.write(audio, 0, audio.length);放入它自己的Thread来解决它。这确实消除了一些波动(由于写入是一个阻塞的呼叫),但是在一秒或2秒之后它仍然听起来不稳定。它仍然有2-3秒的显着延迟。

new Thread(){ 
    public void run(){ 
     byte[] audio = packet.getData(); 
     mPlayer.write(audio, 0, audio.length); 
    } 
}.start(); 

只是有点匿名Thread,现在确实写...

任何人有关于如何解决这个问题的想法?


编辑:

一些进一步的检查和调试后,我注意到,这是obtainBuffer的问题。 我看过java code of the AudioTrackC++ 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。 :<但无论如何,有人可以解释这个C++代码是如何工作的吗?我已经与它的一些经验,但并没有像这个一样复杂...


编辑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不断获取数据不断...我真的需要得到这个固定的...

+0

__builtin_expect是一个优化提示。逻辑归结为“如果错误,然后请记录,调用audiotrack上的start(),并设置超时标志。” 'result'在前一行中设置,'result = cblk-> cv.waitRelative(cblk-> lock,seconds(1));',这似乎是一个条件变量的等待。所以看起来线程安全方案有问题。 – AShelly 2011-03-17 23:04:12

+0

此代码中的线程安全错误的进一步证据来自以后在同一函数中的此行:'LOGW_IF(timeout,“*** SERIOUS WARNING *** obtainBuffer()超时但不需要锁定。我们恢复了,但这不应该发生(用户=%08x,服务器=%08x)“,你,s);' – AShelly 2011-03-17 23:05:19

+0

是的,但我从来没有得到那个警告......只有”正规“一... – ThaMe90 2011-03-18 07:25:07

7

你知道你每秒接收多少字节,数据包之间的平均时间比较,以及数据包之间的最大时间?如果没有,你可以添加代码来计算它吗?

您需要平均8000个采样/秒* 2字节/采样= 16,000字节每秒为了保持流填充。

传入数据包之间的间隔超过2048字节/(16000字节/秒)= 128毫秒将导致您的数据流运行干燥并且音频会断断续续。

阻止它的一种方法是增加的缓冲区大小(PLAYER_CAPACITY)。更大的缓冲区将更能够处理传入数据包大小和速率的变化。额外稳定性的代价是在等待缓冲区初始填充时开始播放的较大延迟。

+0

粗略估计,我每秒获得大约67,200字节。关于(大约)70个包,价值960字节。我粗略地说,因为这是通过有线连接。当我通过无线网络时,我想我会收到大约50个(也许更多,也许更少一些)包,每秒给我48,000字节。我想说这足以保持它的满足。 : – ThaMe90 2011-03-15 07:31:43

+0

经过一些调试并查看几个logcats后,我发现它是一个“getsBuffer超时”的bug ...我仍然试图修复它... – ThaMe90 2011-03-15 08:47:40

+0

你有没有解决过这个问题?请发布更新。我也有类似的问题....谢谢! – aProgrammer 2012-03-09 06:17:17