• 博客访问: 55527
  • 博文数量: 35
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 400
  • 用 户 组: 普通用户
  • 注册时间: 2016-09-01 19:08
  • 认证徽章:
个人简介

Android/Linux/音频/驱动

文章分类
文章存档

2017年(15)

2016年(20)

我的朋友
微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

订阅
热词专题

分类: Android平台

【问题概要】

  上一次我介绍了一种 Android 系统下发生音频 underrun 问题的解决方法(参见《记一次Android系统下解决音频UnderRun问题的过程》),这之后平静了一段时间,测试组同事也没有再报告相关的噪声问题。

  但就在前 2 天,测试组同事告诉我说她们又听见噪声了,并且这次的使用场景比上次复杂了许多——由于从 Android 6.0 开始已经支持应用多开以及多窗口的功能,所以她们先在后台运行了一个程序(比如 阴阳师、全民飞机大战 这样的游戏),再在前台播放视频,于是噪声大量出现了。上次问题的情况比较简单,出现噪声时 framesReady 的值与 framesDesired 的值始终相差 2。所以我通过在 Android 原有处理 underrun 问题的方法的延时基础上增加 3 毫秒,解决了问题。但这次的问题,我们从 Log 中可以看到差值分布范围广,使用固定的延时时间已经无法消除噪声

  在 Log 中搜索包含“underrun”关键字的内容可以看到如下记录:

  1. 03-07 18:44:04.290 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(407) < framesDesired(516)
  2. 03-07 18:44:04.470 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(31) < framesDesired(516)
  3. 03-07 18:44:04.570 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(47) < framesDesired(516)
  4. 03-07 18:44:04.730 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(79) < framesDesired(516)
  5. 03-07 18:44:04.950 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(119) < framesDesired(516)
  6. 03-07 18:44:05.007 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(127) < framesDesired(516)
  7. 03-07 18:44:05.139 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(151) < framesDesired(516)
  8. 03-07 18:44:05.231 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(167) < framesDesired(516)
  9. 03-07 18:44:05.323 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(183) < framesDesired(516)
  10. 03-07 18:44:05.415 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(199) < framesDesired(516)
  11. 03-07 18:44:05.507 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(215) < framesDesired(516)
  12. 03-07 18:44:05.563 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(223) < framesDesired(516)
  13. 03-07 18:44:05.614 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(231) < framesDesired(516)
  14. 03-07 18:44:05.669 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(239) < framesDesired(516)
  15. 03-07 18:44:05.783 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(263) < framesDesired(516)
  16. 03-07 18:44:05.840 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(271) < framesDesired(516)
  17. 03-07 18:44:05.886 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(279) < framesDesired(516)
  18. 03-07 18:44:05.937 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(287) < framesDesired(516)
  19. 03-07 18:44:05.996 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(295) < framesDesired(516)
  20. 03-07 18:44:06.130 2828 3521 V AudioFlinger: track(0xf5ea3a80) underrun, framesReady(319) < framesDesired(516)
  21. 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 贴上来吧,如下:

  1. diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
  2. index 3c941bc..614a5c2 100644
  3. --- a/services/audioflinger/Threads.cpp
  4. +++ b/services/audioflinger/Threads.cpp
  5. @@ -1454,6 +1454,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
  6.                                               type_t type,
  7.                                               bool systemReady)
  8.      : ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type, systemReady),
  9. +        mLackFrames(0),
  10. +        mNeededFrames(0),
  11.          mNormalFrameCount(0), mSinkBuffer(NULL),
  12.          mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
  13.          mMixerBuffer(NULL),
  14. @@ -2729,6 +2731,7 @@ void AudioFlinger::PlaybackThread::detachAuxEffect_l(int effectId)
  15.  bool AudioFlinger::PlaybackThread::threadLoop()
  16.  {
  17.      Vector< sp<Track> > tracksToRemove;
  18. +    size_t maxReadyFrames = 0;
  19.  
  20.      mStandbyTimeNs = systemTime();
  21.  
  22. @@ -3012,10 +3015,32 @@ bool AudioFlinger::PlaybackThread::threadLoop()
  23.                          const int32_t deltaMs = delta / 1000000;
  24.                          const int32_t throttleMs = mHalfBufferMs - deltaMs;
  25.                          if ((signed)mHalfBufferMs >= throttleMs && throttleMs > 0) {
  26. -                                //usleep(throttleMs * 1000); // We can prolong this sleep time to prepare much data for threadLoop_write()
  27. -                                usleep((throttleMs + 3) * 1000); /* Delay more 3ms to prepare much data to
  28. -                                                                  * fix tencent video player underrun bug 20161216
  29. -                                                                  */
  30. +                                //usleep(throttleMs * 1000); // This is the original method to solve underrun problem by Android
  31. +
  32. +                                /* In short, the underrun problem occurs due to framesReady less than
  33. +                                 * desiredFrames. So we can simply wait the upper level prepares enough
  34. +                                 * data for writing when we detected framesReady < desiredFrames.
  35. +                                 * Use do...while loop to wait and judge if we have enough data.
  36. +                                 * Fix all underrun problem - 20170401
  37. +                                 */
  38. +                                ALOGV("Qidi - real mLackFrames = %d", mLackFrames);
  39. +                                if(mLackFrames > 0) { // 只要检测到framesReady小于framesDesired则执行下方的延时等待
  40. +                                   size_t count = mActiveTracks.size();
  41. +                                   size_t tmpReadyFrames = 0;
  42. +                                   unsigned char waitTimeout = 0;
  43. +                                   #define MAX_TIME 100
  44. +                                   do {
  45. +                                       usleep(1000); // 延时1毫米,然后判断准备好的数据是否足够,以及是否超时
  46. +                                       for (size_t i = 0; i < count; i++) {
  47. +                                           sp<Track> t = mActiveTracks[i].promote();
  48. +                                           Track* const track = t.get();
  49. +                                           tmpReadyFrames = track->framesReady();
  50. +                                           maxReadyFrames = tmpReadyFrames > maxReadyFrames ? tmpReadyFrames : maxReadyFrames;
  51. +                                           ALOGV("Qidi - maxReadyFrames=%d, mNeededFrames=%d", maxReadyFrames, mNeededFrames);
  52. +                                       }
  53. +                                }while((maxReadyFrames < mNeededFrames) && (waitTimeout++ < MAX_TIME)); // 如果数据不足且没有超时,则继续等待
  54. +                            }
  55. +                            mLackFrames = 0;
  56. +                            maxReadyFrames = 0;
  57. +            
  58.                              // notify of throttle start on verbose log
  59.                              ALOGV_IF(mThreadThrottleEndMs == mThreadThrottleTimeMs,
  60.                                      "mixer(%p) throttle begin:"
  61. @@ -3915,7 +3940,8 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
  62.          // add frames already consumed but not yet released by the resampler
  63.          // because mAudioTrackServerProxy->framesReady() will include these frames
  64.          desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
  65. -
  66. +        mNeededFrames = desiredFrames;
  67. +        
  68.          uint32_t minFrames = 1;
  69.          if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
  70.                  (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
  71. @@ -4147,6 +4173,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
  72.              if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) { // underrun occurs
  73.                  ALOGV("track(%p) underrun, framesReady(%zu) < framesDesired(%zd)",
  74.                          track, framesReady, desiredFrames);
  75. +                mLackFrames = desiredFrames - framesReady;
  76.                  track->mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
  77.              }
  78.              // clear effect chain input buffer if an active track underruns to avoid sending
  79. diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
  80. index 46ac300..14d88c6 100644
  81. --- a/services/audioflinger/Threads.h
  82. +++ b/services/audioflinger/Threads.h
  83. @@ -480,6 +480,12 @@ public:
  84.  //FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
  85.      // for offloaded tracks
  86.      static const int8_t kMaxTrackRetriesOffload = 20;
  87. +    size_t mLackFrames;
  88. +     // mLackFrames is used for adjusting underrun delay time dynamically.
  89. +     // such delay is performed in MixerThread::prepareTracks_l() when
  90. +     // underrun occurrs.
  91. +    size_t    mNeededFrames;
  92. +     // mNeededFrames is equal to desiredFrames in MixerThread::prepareTracks_l()
  93.  
  94.      PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
  95.                     audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
 修改完成后,编译系统并烧写镜像文件到设备上。经测试,该方法对所有之前出现过的 underrun 问题均有效。



阅读(80) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册