解决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 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

 

你可能感兴趣的:(underrun,overrun,Xrun)