1. 程式人生 > >解決Android5.1系統下音訊Xrun(underrun或overrun)問題

解決Android5.1系統下音訊Xrun(underrun或overrun)問題

基於RK3288解決Android5.1系統下音訊Xrun(underrun或overrun)問題

2018年11月23日 16:35:06 21cnhui 閱讀數:38 標籤: Android  audiopcm_configxruntinyalsaperiod_size 更多

個人分類: Linux核心與驅動Android系統架構

版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/u010872301/article/details/84397167

音訊檔案在播放時出現聲音斷斷續續,一卡一卡的或類似“爆破”(Pop-Click)雜音的現象,稱之為 Xrun(可以是 underrun,也可以是 overrun)。

 

分析:

alsa driver使用了環形緩衝區對dma buffer進行管理,如下圖。

播放時,應用程式把音訊資料來源源不斷地寫入dma buffer中,然後相應platform的dma操作則不停地從該buffer中取出資料,經dai送往codec中,當寫入的資料慢,播放的資料快時聲音會出現斷斷續續,一卡一卡的現象。

錄音時,codec源源不斷地把A/D轉換好的音訊資料經過dai送入dma buffer中,而應用程式則不斷地從該buffer中讀走音訊資料,當寫入的資料快,播放的資料慢時,當資料量較多buffer有可能被沖掉,聲音會出現類似“爆破”(Pop-Click)雜音的現象。

[RK3288][Android5.1] 除錯如下:

1、播放的資料快,寫入的資料慢時,觸發DMA中斷,將音訊資料寫入dma buffer中,出現一卡一卡的現象!檢查codec和machine的取樣頻率,設定ASoc框架的Codec驅動和Machine驅動的輸出freq = 12288000。

 

//設定codec dai的主時鐘,取樣率

static int xvf3500_set_dai_sysclk(struct snd_soc_dai *codec_dai,int clk_id, unsigned int freq, int dir)

{

...

freq = 12288000;

switch (freq) {

  1. ...
  2. case 12288000:
  3. case 16934400:
  4. case 24576000:
  5. case 33868800:
  6. xvf3500->sysclk_constraints = &constraints_12288;
  7. xvf3500->sysclk = freq;
  8. return 0;
  9. ...
  10. }
  11. return -EINVAL;
  12. }
 

//在machine設定codec dai取樣率,保持codec驅動取樣率一致

static int rk29_xvf3500_init(struct snd_soc_pcm_runtime *rtd)

{

...

 

ret = snd_soc_dai_set_sysclk(codec_dai, 0,12288000, SND_SOC_CLOCK_IN);

if (ret < 0) {

printk(KERN_ERR "Failed to set xvf3500 SYSCLK: %d\n", ret);

return ret;

}

...

return 0;

}

2、當寫入的資料快,播放的資料慢時,當資料量較多buffer有可能被沖掉,聲音會出現類似“爆破”或“呲呲”雜音的現象。通過調整硬體抽象層的period_size和period_count,來改變dma的傳輸資料量。

 
  1. struct pcm_config {
  2. unsigned int channels;
  3. unsigned int rate;
  4. unsigned int period_size;
  5. unsigned int period_count;
  6. enum pcm_format format;
  7. unsigned int start_threshold;
  8. unsigned int stop_threshold;
  9. unsigned int silence_threshold;
  10. int avail_min;
  11. };

合理的pcm_config可以做到更好的低時延和功耗。解釋一下結構中的各個引數,每個引數的單位都是frame(1幀 = 通道*取樣位深):

  • period_size. 每次傳輸的資料長度。值越小,時延越小,cpu佔用就越高。
  • period_count. 緩之衝區period的個數。緩衝區越大,發生XRUN的機會就越少。
  • format. 定義資料格式,如取樣位深,大小端。
  • start_threshold. 緩衝區的資料超過該值時,硬體開始啟動資料傳輸。如果太大, 從開始播放到聲音出來時延太長,甚至可導致太短促的聲音根本播不出來;如果太小, 又可能容易導致XRUN.
  • stop_threshold. 緩衝區空閒區大於該值時,硬體停止傳輸。預設情況下,這個數 為整個緩衝區的大小,即整個緩衝區空了,就停止傳輸。但偶爾的原因導致緩衝區空, 如CPU忙,增大該值,繼續播放緩衝區的歷史資料,而不關閉再啟動硬體傳輸(一般此 時有明顯的聲音卡頓),可以達到更好的體驗。
  • silence_threshold. 這個值本來是配合stop_threshold使用,往緩衝區填充靜音 資料,這樣就不會重播歷史資料了。但如果沒有設定silence_size,
  • avail_min. 緩衝區空閒區大於該值時,pcm_mmap_write()才往緩衝寫資料。這個 值越大,往緩衝區寫入資料的次數就越少,面臨XRUN的機會就越大。Android samsung tuna 裝置在screen_off時增大該值以減小功耗,在screen_on時減小該 值以減小XRUN的機會。

在不同的場景下,合理的引數就是在效能、時延、功耗等之間達到較好的平衡。

 

//修改HAL層的period_size 和 period_count路徑...hardware/rockchip/audio/tinyalsa_hal/audio_hw.h

//播放

struct pcm_config pcm_config = {

.channels = 2,

.rate = 48000,

.period_size = 2048,

.period_count = 2,

.format = PCM_FORMAT_S16_LE,

};

//錄音

struct pcm_config pcm_config_in = {

.channels = 2,

.rate = 48000,

.period_size = 512,

.period_count = 2,

.format = PCM_FORMAT_S16_LE,

};

總結:

這種方法通過增加或減少音訊資料的 period_count或period_size來進行補償。但這樣也會使音訊播放/錄音的資料準備時間變長,增加音訊操作的延遲。

 

pcm播放的時候,介面snd_pcm_writei 返回 -EPIPE,為underrun

    錄製音訊的時候, 介面snd_pcm_readi 返回 -EPIPE, 為overrun

    使用ALSA架構的驅動程式,在實際開發使用過程中,比較常見的錯誤有-EPIPE,也就是-32?為什麼會出現呢?肯定是系統內部不和諧了!EPIPE的錯誤在播放時出現就是因為驅動buffer沒有資料可以丟給codec所致,通俗一點就是上層給下面喂資料的速度慢了,下面餓暈了,所以抱怨你上層慢啊,給你一個-EPIPE錯誤出來,自己去找原因。

    在錄音的時候,出現EPIPE也是有原因的,ALSA的驅動也有一塊專門用來儲存錄音資料的buffer,上層從該buffer搬運資料再儲存起來就能得到我們需要的錄音檔案。一旦驅動的buffer滿了,就會出現EPIPE的錯誤,因為你上層讀錄音buffer資料的速度慢了,這就不能抱怨下層不給面子了。

    通過分析出現原因後,我們得找找對策,說起來容易,做的可能因為系統的原因並不是想的那麼容易。

    在播放的時候,如果會出現這種-EPIPE的錯誤,請調整下發資料的資料,加快一點點!

    錄音的時候出現這種錯誤的時候,請讀得更快一點!可以提高任務的優先順序來處理,也可以把驅動buffer擴大一下,給系統更多一點的緩衝時間!
--------------------- 
作者:SevenFighting 
來源:CSDN 
原文:https://blog.csdn.net/bingyu880101/article/details/83106497 
版權宣告:本文為博主原創文章,轉載請附上博文連結!