【问题概要】
上一次我介绍了一种 Android 系统下发生音频 underrun 问题的解决方法(参见《记一次Android系统下解决音频UnderRun问题的过程》),这之后平静了一段时间,测试组同事也没有再报告相关的噪声问题。
但就在前 2 天,测试组同事告诉我说她们又听见噪声了,并且这次的使用场景比上次复杂了许多——由于从 Android 6.0 开始已经支持应用多开以及多窗口的功能,所以她们先在后台运行了一个程序(比如 阴阳师、全民飞机大战 这样的游戏),再在前台播放视频,于是噪声大量出现了。上次问题的情况比较简单,出现噪声时 framesReady 的值与 framesDesired 的值始终相差 2。所以我通过在 Android 原有处理 underrun 问题的方法的延时基础上增加 3 毫秒,解决了问题。但这次的问题,我们从 Log 中可以看到差值分布范围广,使用固定的延时时间已经无法消除噪声。
在 Log 中搜索包含“underrun”关键字的内容可以看到如下记录:
-
03-07 18:44:04.290 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(407) < framesDesired(516)
-
03-07 18:44:04.470 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(31) < framesDesired(516)
-
03-07 18:44:04.570 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(47) < framesDesired(516)
-
03-07 18:44:04.730 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(79) < framesDesired(516)
-
03-07 18:44:04.950 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(119) < framesDesired(516)
-
03-07 18:44:05.007 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(127) < framesDesired(516)
-
03-07 18:44:05.139 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(151) < framesDesired(516)
-
03-07 18:44:05.231 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(167) < framesDesired(516)
-
03-07 18:44:05.323 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(183) < framesDesired(516)
-
03-07 18:44:05.415 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(199) < framesDesired(516)
-
03-07 18:44:05.507 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(215) < framesDesired(516)
-
03-07 18:44:05.563 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(223) < framesDesired(516)
-
03-07 18:44:05.614 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(231) < framesDesired(516)
-
03-07 18:44:05.669 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(239) < framesDesired(516)
-
03-07 18:44:05.783 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(263) < framesDesired(516)
-
03-07 18:44:05.840 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(271) < framesDesired(516)
-
03-07 18:44:05.886 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(279) < framesDesired(516)
-
03-07 18:44:05.937 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(287) < framesDesired(516)
-
03-07 18:44:05.996 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(295) < framesDesired(516)
-
03-07 18:44:06.130 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(319) < framesDesired(516)
-
03-07 18:44:06.187 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(327) < framesDesired(516)
【解决问题】
既然 framesReady与 framesDesired 的差值不再固定,那么我们也应该根据差值大小来调节延时时间来解决问题。只要我们检测到 framesReady 小于 framesDesired,我们就进行 1 毫秒延时,然后再获取延时后的 framesReady 值与 framesDesired 值进行比较,如果 framesReady 仍然小于 framesDesired,那么则继续延时 1 毫秒。如此循环,直到 framesReady 值大于等于 framesDesired 或者超时退出。这次的改动是在上次修复的代码基础上进行的(参见《记一次Android系统下解决音频UnderRun问题的过程》)。话不多说,直接把修复问题的 Patch 贴上来吧,如下:
-
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
-
index 3c941bc..614a5c2 100644
-
--- a/services/audioflinger/Threads.cpp
-
+++ b/services/audioflinger/Threads.cpp
-
@@ -1454,6 +1454,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
-
type_t type,
-
bool systemReady)
-
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type, systemReady),
-
+ mLackFrames(0),
-
+ mNeededFrames(0),
-
mNormalFrameCount(0), mSinkBuffer(NULL),
-
mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
-
mMixerBuffer(NULL),
-
@@ -2729,6 +2731,7 @@ void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId)
-
bool AudioFlinger::PlaybackThread::threadLoop()
-
{
-
Vector< sp<Track> > tracksToRemove;
-
+ size_t maxReadyFrames = 0;
-
-
mStandbyTimeNs = systemTime();
-
-
@@ -3012,10 +3015,32 @@ bool AudioFlinger::PlaybackThread::threadLoop()
-
const int32_t deltaMs = delta / 1000000;
-
const int32_t throttleMs = mHalfBufferMs - deltaMs;
-
if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
-
- //usleep(throttleMs * 1000); // We can prolong this sleep time to prepare much data for threadLoop_write()
-
- usleep((throttleMs + 3) * 1000); /* Delay more 3ms to prepare much data to
-
- * fix tencent video player underrun bug 20161216
-
- */
-
+ //usleep(throttleMs * 1000); // This is the original method to solve underrun problem by Android
-
+
-
+ /* In short, the underrun problem occurs due to framesReady less than
-
+ * desiredFrames. So we can simply wait the upper level prepares enough
-
+ * data for writing when we detected framesReady < desiredFrames.
-
+ * Use do...while loop to wait and judge if we have enough data.
-
+ * Fix all underrun problem - 20170401
-
+ */
-
+ ALOGV("Qidi - real mLackFrames = %d", mLackFrames);
-
+ if(mLackFrames > 0) { // 只要检测到framesReady小于framesDesired则执行下方的延时等待
-
+ size_t count = mActiveTracks.size();
-
+ size_t tmpReadyFrames = 0;
-
+ unsigned char waitTimeout = 0;
-
+ #define MAX_TIME 100
-
+ do {
-
+ usleep(1000); // 延时1毫米,然后判断准备好的数据是否足够,以及是否超时
-
+ for (size_t i = 0; i < count; i++) {
-
+ sp<Track> t = mActiveTracks[i].promote();
-
+ Track* const track = t.get();
-
+ tmpReadyFrames = track->framesReady();
-
+ maxReadyFrames = tmpReadyFrames > maxReadyFrames ? tmpReadyFrames : maxReadyFrames;
-
+ ALOGV("Qidi - maxReadyFrames=%d, mNeededFrames=%d", maxReadyFrames, mNeededFrames);
-
+ }
-
+ }while((maxReadyFrames < mNeededFrames) && (waitTimeout++ < MAX_TIME)); // 如果数据不足且没有超时,则继续等待
-
+ }
-
+ mLackFrames = 0;
-
+ maxReadyFrames = 0;
-
+
-
// notify of throttle start on verbose log
-
ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,
-
"mixer(%p) throttle begin:"
-
@@ -3915,7 +3940,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
-
// add frames already consumed but not yet released by the resampler
-
// because mAudioTrackServerProxy->framesReady() will include these frames
-
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
-
-
-
+ mNeededFrames = desiredFrames;
-
+
-
uint32_t minFrames = 1;
-
if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
-
(mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
-
@@ -4147,6 +4173,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
-
if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) { // underrun occurs
-
ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
-
track, framesReady, desiredFrames);
-
+ mLackFrames = desiredFrames - framesReady;
-
track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
-
}
-
// clear effect chain input buffer if an active track underruns to avoid sending
-
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
-
index 46ac300..14d88c6 100644
-
--- a/services/audioflinger/Threads.h
-
+++ b/services/audioflinger/Threads.h
-
@@ -480,6 +480,12 @@ public:
-
//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
-
// for offloaded tracks
-
static const int8_t kMaxTrackRetriesOffload = 20;
-
+ size_t mLackFrames;
-
+ // mLackFrames is used for adjusting underrun delay time dynamically.
-
+ // such delay is performed in MixerThread::prepareTracks_l() when
-
+ // underrun occurrs.
-
+ size_t mNeededFrames;
-
+ // mNeededFrames is equal to desiredFrames in MixerThread::prepareTracks_l()
-
-
PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
-
audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
修改完成后,编译系统并烧写镜像文件到设备上。经测试,该方法对所有之前出现过的 underrun 问题均有效。
阅读(10342) | 评论(0) | 转发(0) |