2017-05-01 38 views
1

在非阻塞模式下使用snd_pcm_writei()時,一切都完美,但最終音頻會變得波濤洶涌。這聽起來像環形緩衝區指針不同步(即有時我可以告訴音頻不按順序播放)。這個問題需要多長時間才能開始取決於硬件。在真實硬件上的Gentoo盒子上,它很少發生,但是在QEMU上運行的buildroot系統上,它在大約5分鐘後發生。在這兩種情況下,排除pcm流都可以解決問題。我已經證實我正在通過將它們寫入文件並使用aplay播放它們來正確地寫入示例。Glitchy audio output,no underruns

目前我正在設置avail_min到期間大小(1024幀),並在寫入期間大小的塊之前調用snd_pcm_wait()。但是我嘗試了許多不同的變體(不同的塊大小,檢查自己並使用pthread_cond_timedwait()而不是snd_pcm_wait()等)。但唯一能正常工作的是使用阻塞模式,但我無法做到這一點。

你可以在這裏看到當前的源代碼:https://bitbucket.org/frodzdev/mediabox/src/5a6471316c7ae481b329e7e0d4af1bb68a32e71d/src/audio.c?at=staging&fileviewer=file-view-default(因爲我嘗試了各種各樣的東西,所以需要一點清理)。執行實際IO的代碼始於第375行。

編輯: 我想我有一個解決方案,但我不明白爲什麼它似乎工作。看來,如果我使用非阻塞模式並不重要,問題是我等待確保緩衝區上有空間(通過snd_pcm_wait(),pthread_cond_timedwait()或usleep())。

似乎工作的版本在這裏:https://bitbucket.org/frodzdev/mediabox/src/c3eb290087d9bbe0d5f37653a33a1ba88ef0628b/src/audio.c?fileviewer=file-view-default。在調用snd_pcm_writei()之前,我仍然在等待中切換到阻塞模式,但沒有任何區別。然後,我在調用avbox_audiostream_gettime()的snd_pcm_status()之前添加了對snd_pcm_avail()的調用。此函數由另一個線程不斷調用以獲取流時鐘,並且僅使用snd_pcm_status()獲取時間戳。現在看起來很有效(至少發生這種情況的可能性很小),但我不明白爲什麼。我知道snd_pcm_avail()會將指針與內核同步,但我不明白它何時需要調用,以及snd_pcm_state()等和snd_pcm_status()之間的區別。 snd_pcm_status()是否也同步任何東西?這似乎不是因爲有時snd_pcm_status_get_state()會在snd_pcm_avail()返回-EPIPE時返回RUNNING。 ALSA文檔非常模糊。也許理解這些東西會幫助我理解我的問題?

現在,當我說它似乎正在工作,我的意思是我不能在真實的硬件上重現它。儘管如此,它仍然發生在QEMU上。但考慮到在下一次提交時,我切換到阻塞模式而沒有等待(我過去使用過,在真正的硬件上從來沒有遇到過問題),它仍然發生在QEMU中,而且這是一個常見問題QEMU我開始認爲我可能已經解決了我的問題,現在這只是一個QEMU問題。有沒有什麼方法可以確定問題是否在我的端更容易在仿真器上觸發,或者它只是一個模擬器問題?

編輯:我知道我應該在等待之前填充緩衝區,但在這一點上,我的擔心不是防止欠載,而是要確保我的代碼在發生時能夠處理它們。除了緩衝區在幾次迭代之後被填滿之外。在寫每個數據包之前,我通過輸出avail,buffer_size等證實了這一點,我得到的數字並不完美,他們每8個週期就會顯示1或2個週期的錯誤。此外(這是主要問題)我沒有檢測到任何欠載,音頻變得波濤洶涌,但所有的寫入都成功了。事實上,如果問題開始發生,並且我通過超載CPU觸發欠載運行,它將在pcm復位時自行更正。

回答

1

line 505:您使用時間作爲malloc的參數。

line 568:你不是在播放音頻嗎?在這種情況下,只有在您編寫框架之後,您才應該等待。讓我們來想...

音頻設備在終止處理句點時產生一箇中斷。

 
| period A | period B | 
      ^  ^
      irq   irq 

在啓動pcm之前,音頻設備不會產生任何中斷。注意here你還在等待,你還沒有開始pcm。您只有在致電snd_pcm_writei()時才能啓動它。

當您等待音頻數據時,只有在當前時間段已經完全處理時纔會醒來 - 在您第一次等待時,第一個時間段甚至沒有寫入 - 所以在舒適的情況下,您應該寫入整個緩衝區,等待第一個中斷,然後寫入剛剛處理的時間段,然後繼續。

 
Initially, buffer is empty: 
    |   |   | 
write(): 
    |############|############| 
wait(): 
    .............. 
When we wake up: 
    |   |############| 
write(): 
    |############|############| 

我發現問題是您在播放音頻前正在寫音頻,有時候它可能會在緩衝區中延遲到達。

+0

良好的緩衝區大小,但是這只是在我的情況(48000KHz)過度分配,它是臨時代碼。我也理解snd_pcm_wait()應該立即返回,如果有效> = avail_min所以我真的沒有等到緩衝區填滿,對吧?除了原版,我只是在等待EAGAIN的時候才知道,它還在發生。現在你可能對我的最終結論是正確的,我能做些什麼呢?首先填充緩衝區不是解決方案,因爲音頻可能來自網絡流,因此它可能仍會發生。 – fernan

+0

我注意到,有時在調用snd_pcm_writei()之前,snd_pcm_delay()返回(buffer_size - avail)+ 1024,但下一次寫入仍然成功。所以我想我不是underruning環緩衝區但硬件緩衝區是?如果是這種情況,我該怎麼辦?我應該睡一段時間(delay - (buffer_size-avail))> 0讓環緩衝區溢出,以便我可以檢測到它? – fernan

+0

此外,毛刺聽起來不僅僅是一個延遲,它聽起來像音頻按順序播放,好像緩衝區指針在某種程度上被破壞一樣。我甚至通過寫入文件證實我正在寫正確的順序。 – fernan