对Audio Xrun的一点了解

来源:互联网 发布:java常用设计模式 编辑:程序博客网 时间:2024/04/29 15:27
在《Android Audio Throttle》中描述过:
Underrun(underflow),buffer underrun or buffer underflow is a state occurring when a buffer fed with data at a lower speed than the data is being read from it。就是说生产者生产和速度比消费者的速度要慢,也通俗点说是写入ring buffer比读的要慢,导致读不到数据。
Overrun(overflow),与Underrun是相反的状态,就是读比写的速度要慢。

关于xrun可以分为三个层次来说,就是硬件层、驱动层和frameworks层。这三个层次都分别会产生xrun。
用一个简单的图来表示Android音频播放的数据流:
从这个流程来看,有三个点是可能存在xrun的,分别是RingBuffer A、RingBuffer B和FIFO。

1、硬件层面的xrun
FIFO是什么? 先进先出的buffer,也就是一块内存块,专门划分给音频用的,是有固定的寄存器地址,在驱动里配置好后,音频的数据就会从RingBuffer B从被搬到FIFO中,
只要数据到了FIFO就自动的会输出到Codec。

有什么原因可以导致硬件xrun呢? 这里说的硬件xrun,就是播放时FIFO的数据不足,当然FIFO出现数据不足,很大可能是RingBuffer B中没有数据,如果RingBuffer B没有数据,那就是驱动层的xrun了,我们这里假设RingBuffer B的数据是足够的。需要注意的是:RingBuffer B是属于driver层的buffer,在上图是分开的,其实是一起的,图没画好。
从上面可看到,RingBuffer B 与 FIFO是通过DMA来搬数据的,DMA搬数据时需要使用总线,如果此时,总线被占用,那DMA就申请不到总线资源,那FIFO就来不及填充,这会导致硬件层的xrun。
当然,硬件层的xrun是极小因为总线被占用导致的,很大可能是RingBuffer B没数据导致的。

2、驱动层的xrun
从上图可见,AudioFlinger调用hal层,调用pcm_write给驱动的RingBuffer B写数据,这是一个阻塞的操作,直到写数据完成才返回给AudioFlilnger,当然除非是驱动报错。

那驱动层的xrun,也有一部分是因为上层的RingBuffer A的数据不够,导致AudioFlinger来不及给驱动写数据。RingBuffer A数据不足,是上层xrun。这里也是假设RingBuffer A是有足够数据的。
还有什么原因导致RingBuffer B数据不足呢? 比如资源不足,buffersize配置太小,驱动代码问题,都可能会导致xrun,也可能是上层框架导致的,这里说的肯定不是因为RingBuffer A的数据不够,而是AudioFlinger写数据时,因为锁或者其他原因阻塞了。
要解决驱动层的xrun,也可以从这几方面去考虑和分析。驱动层如果出现xrun,一般会返回EPIPE错误码。

3、框架层的xrun
RingBuffer A的数据不够,这个很好发现,在AudioFlinger会有一堆打印给你看,比如AudioFlinger: track(0xea5dda80) underrun,  framesReady(1024) < framesDesired(1026) 
原因也是多种的:解码性能不好,解码写数据的策略,计算buffer或者延时不正确,如果是网络音频或者视频,或者是网络带宽,也可能是系统性能不足。原因非常多,要具体分析问题。

近期也因为一个解码计算延时出错的原因导致xrun,在计算时,会读取service端还有多长时间的数据,应该使用AudioFlinger的采样率,而解码器使用了音频源数据的采样率,这也是原生的问题。

原创粉丝点击