2011-03-14 94 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