Android Audio Throttle分析

最近在跟踪一个接usb声卡播放ring类型的卡顿问题,跟踪的过程中看到Throttle机制,下面说一下我看到的Throttle及对Throttle的理解。

 

首先,要弄清一个概念:underrun(underflow),其实还有一个叫overrun(overflow)的,两者统称为xrun,关于xrun可以分为三个层次来说,就是硬件层、驱动层和frameworks层。这三个层次都分别会产生xrun,关于xrun,后面专门来记录一下,本文主要说Throttle,这里只说一下underrun和overrun分别是什么?

 

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。就是说生产者生产和速度比消费者的速度要慢,也通俗点说是写入ringbuffer比读的要慢,导致读不到数据。

 

Overrun(overflow),与Underrun是相反的状态,就是读比写的速度要慢。更详细的描述,将用不同的篇章来记录。

 

Throttle一般是在播放线程在使用到的,也是Android为了预防underrun出现而加入的一种机制,出现的文件主要是Threads.cpp的ThreadLoop中,主要的作用是节流,控制整个数据流的速度,通过代码分析:

// Check if we want to throttle the processing to no more than 2x normal rate

mThreadThrottle = property_get_bool("af.thread.throttle", true /* default_value */);

// 通过一个属性来控制开关,默认是开的,所以以下mThreadThrottle = true

mThreadThrottleTimeMs = 0;

mThreadThrottleEndMs = 0;

mHalfBufferMs = mNormalFrameCount * 1000 / (2 * mSampleRate);

// 第一行 google给出的注释,节流控制其数据流的速度不超过Normal Rate的2倍

// 实际上,是可以去调的

 

if (mThreadThrottle

&& mMixerStatus == MIXER_TRACKS_READY // we are mixing (active tracks)

&& ret > 0) { // we wrote something

// Limit MixerThread data processing to no more than twice the

// expected processing rate.

//

// This helps prevent underruns with NuPlayer and other applications

// which may set up buffers that are close to the minimum size, or use

// deep buffers, and rely on a double-buffering sleep strategy to fill.

//

// The throttle smooths out sudden large data drains from the device,

// e.g. when it comes out of standby, which often causes problems with

// (1) mixer threads without a fast mixer (which has its own warm-up)

// (2) minimum buffer sized tracks (even if the track is full,

// the app won't fill fast enough to handle the sudden draw).

//

// Total time spent in last processing cycle equals time spent in

// 1. threadLoop_write, as well as time spent in

// 2. threadLoop_mix (significant for heavy mixing, especially

// on low tier processors)

// it's OK if deltaMs is an overestimate.

const int32_t deltaMs =

(lastWriteFinished - previousLastWriteFinished) / 1000000;

// deltaMs 是两次调用threadLoop_write的时间间隔,可以认为是本次写花的时间

// 如果deltaMs很小,说明本次写非常快,threadLoop_write是会调用hal层的write,然后调用pcm_write

// 把数据写到driver的ringbuffer中,这是一个阻塞式的调用,如果数据没写完,threadLoop_write是不会返回的

// 但如果数据很快写完,几乎没有阻塞就返回了,那deltaMs就会很小

//那什么情况下会写得很快呢?

// 就是在driver层的ringbuffer有很多的空间时,至少有一个framecount*framesize大小的buffer空间,以至写数据时候,

// 完全有足够的空间写,不需要阻塞

//那什么时候会出现这种情况,有很多的buffer空间呢?

// 在刚开始播放的时候,或者从pause到resume的时候,就是从standby状态到非standby状态的时候,因为此时

// driver的buffer都清空了,所以再次进入播放时,buffer是空的

 

const int32_t throttleMs = mHalfBufferMs - deltaMs;

// 一般在正常的话,deltaMs会大约等于一个framecount播放的时间,即framecount / samplerate

// 所以throttleMs 一般是小于0的,当写的很快就返回的时候,throttleMs 才会出现大于0

 

if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {

// 此mHalfBufferMs >= throttleMs条件什么时候满足呢?

// 由于deltaMs肯定是大于0的,所以一般mHalfBufferMs 大于throttleMs 是成立的

// 如果deltaMs非常大,大到溢出,那deltaMs会是个负数,那此条件不成立,不成立也不会进入节流控制

// 暂时还未想到其他情况下此条件不成立的情况,如果有哪位知道,麻烦告知,谢谢

usleep(throttleMs * 1000);

// 其实节流,就是调用了一个延时函数而已

// 那为什么当deltaMs很小时,需要做节流控制?

// 在开始播放时,drvier的buffer是空的,那threadLoop_write写数据到driver时,有够的空间,所以立刻返回,

// 然后进入下一个loop,一开始解码可能达到如此高的速度,所以就会出现解码器写的速度比读的速度要慢,

// 就是上面说的underrun,所以说Throttle可以预防underrun。

// 大家可以根据需要,调整延时的时间,不一定非要是throttleMs

 

// notify of throttle start on verbose log

ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,

"mixer(%p) throttle begin:"

" ret(%zd) deltaMs(%d) requires sleep %d ms",

this, ret, deltaMs, throttleMs);

mThreadThrottleTimeMs += throttleMs;

// 时间统计,累加

// 需要注意的是:连续多次进入这里时,mThreadThrottleEndMs 与 mThreadThrottleTimeMs的差

// 不止是throttleMs,而是多个不同的throttleMs之和

// Throttle must be attributed to the previous mixer loop's write time

// to allow back-to-back throttling.

lastWriteFinished += throttleMs * 1000000;

// 更新lastWriteFinished,把延时的时间也加上,才是真正的本次写完成的时间

} else {

uint32_t diff = mThreadThrottleTimeMs - mThreadThrottleEndMs;

// 统计本次Throttle一共延时的多长时间

if (diff > 0) {

// notify of throttle end on debug log

// but prevent spamming for bluetooth

ALOGD_IF(!audio_is_a2dp_out_device(outDevice()),

"mixer(%p) throttle end: throttle time(%u)", this, diff);

mThreadThrottleEndMs = mThreadThrottleTimeMs;

}

}

}

上面的分析到此,相信大家对Throttle有所理解,节流控制的目的就是控制系统的音频数据流可以平稳的运行。

如有不对之处,请不吝指出,一起学习。谢谢。

 

 

发布了5 篇原创文章 · 获赞 0 · 访问量 2233
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览