1. 程式人生 > >Android系統下解決音訊underrun噪聲問題的一種更優方法

Android系統下解決音訊underrun噪聲問題的一種更優方法

【問題概要】

    上一次我介紹了一種 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 minReadyFrames = 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;
+                                 minReadyFrames = mNeededFrames;
+				  #define MAX_TIME 5
+				  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();
+						minReadyFrames =  tmpReadyFrames > minReadyFrames ? minReadyFrames : tmpReadyFrames;
+						ALOGV("Qidi - minReadyFrames=%d, mNeededFrames=%d", minReadyFrames, mNeededFrames);
+	        			}
+	        		  }while((minReadyFrames < mNeededFrames) && (waitTimeout++ < MAX_TIME));    // 如果資料不足且沒有超時,則繼續等待
+			     }
+			     mLackFrames  = 0;
+			     minReadyFrames = 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 問題均有效。

相關推薦

Android系統解決音訊underrun噪聲問題的方法

【問題概要】     上一次我介紹了一種 Android 系統下發生音訊 underrun 問題的解決方法(參見《記一次Android系統下解決音訊UnderRun問題的過程》),這之後平靜了一段時間,測試組同事也沒有再報告相關的噪聲問題。     但就在前 2 天,測試組

Android系統解決音訊UnderRun問題的過程

記一次Android系統下解決音訊UnderRun問題的過程 2017年01月04日 18:09:32 Qidi_Huang 閱讀數:4540 標籤: AndroidAudiounderrunxrun解決辦法 更多 個人分類: 嵌入

Android】App應用前後臺切換的監聽方法

Android本身並沒有提供監聽App的前後臺切換操作的方法。最近看到一種簡單巧妙的方法來監聽前後臺,這裡分享記錄一下。 一、Activity生命週期 我們知道在Android中,兩個Activity,分別為A和B。假設此時A在前臺,當A啟動B時,他們倆之間的生命週期關係如下,可

linux系統解決getch()輸入數值不回顯示

continue pan not while image png bsp log main 在linux系統下開發C 程序卻會遇到系統不支持conio.h頭文件,無法使用getch()不回顯函數。下面就演示如何構建函數實現數值輸入不回顯。 1 #includ

Ubuntu16.04系統 解決“無法獲得鎖 /var/lib/dpkg/lock -open (11:資源暫時不可用)、無法鎖定管理目錄(/var/lib/dpkg/),是否有其他進程正占用它?”的方法

方法 div 終端 例如 解決辦法 all -o 強制 安裝 在Ubuntu16.04下安裝軟件,例如:sudo apt-get install lrzsz時提示:   無法獲得鎖 /var/lib/dpkg/lock - open (11: 資源暫時不可用)  無法鎖定

Android系統用js自定義gesture事件(仿ios實現移動端事件一致)

initial path acc mtab uil 查看 sans fault default 一、手勢事件 下面二維碼是一個實例dome,可掃碼直接查看: 在ios系統中,系統自帶了gesture事件,兩個手指操作的時候,就會產生一下三種手勢

Android系統C語言hello world

在android上執行c編寫的helloword   一般情況下Android系統應用程式都是java編寫APK,如果要重用C程式碼,也是通過JNI,呼叫C庫。   也許有人會和我有一樣的想法既然android是基於linux的核心的,那應該也可以直接執行C編寫的二進位制呢?很顯然是可以的。  

Ubuntu系統解決Qt5使用SSL的“qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method”錯誤

參考連結:https://www.cnblogs.com/btian/p/6130560.html 首先下載openssl: git clone https://github.com/openssl/openssl.git 然後選擇1.1.0穩定版,並且編譯: git check

iTOP-iMX6開發板Android系統LVDS和HDMI雙屏異顯方法

迅為iMX6 開發板 android 系統下 LVDS 和 HDMI 雙屏異顯的使用過程。 使用“Mfgtools-Rel-1.1.0_180403_MX6Q_UPDATER”版本的燒寫工具,把系統燒寫進開發板。系統啟動之後,把“.mp4”格式的視訊檔案放到“/sdcard”目錄下,如下圖所示。

android studio解決 java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/

android studio下解決 java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/ 最近把以前寫的訪問資料庫的小專案拿出來重溫,編譯後發現連不上資料庫了,提示錯誤: jav

win7系統解決VC6.0 與office2007、2010不相容的問題

  以前在windows7下面安裝VC6.0時,新建一個工程,開啟類檢視,在source file右擊,選擇Add file to project,有些情況下,VC6.0沒任何反應,嚴重時,VC6.0直接就崩潰了,原本以為與win7

在windows系統的MySQL,(

  1、若是MySQL安裝之後沒有配置預設路徑,則在cmd下輸入mysql會顯示:沒有合適的路徑 輸入:mysql -u root -p   2、在設定的服務選項下查詢MySQL點選停止此服務後,則MySQL不能正常連線   3、基本使用:

android系統使用gdbserver除錯C++

         使用gdbserver可以對不同平臺的裝置原始碼進行除錯,記錄下在android系統下除錯C++程式的方法 在要除錯的目標裝置啟動gdbserver: gdbserver :9555 --attach PID(或者在本機 adb shell gdbserver :9555 --atta

Android系統安裝openVPN流程

直接來思路: 第一步: 選擇一款android手機,安裝openVPN.apk 第二步: 將帶有 xxx.ovpn的檔案匯入到Android手機中; 第三步: 開啟 安卓介面,點選安卓openVP

Unity3D在android系統除錯

一、工具準備 1.JDK——由於android是基於Java平臺開發的,jdk是必須要安裝的。下載地址:http://www.java.net/download/jdk6/6u10/promoted/b32/binaries/jdk-6u10-rc2-bin-b32-win

Linux系統設定交換檔案(swapfie)提升效能。

如果當初安裝Linux,沒有分配交換分割槽,或者十分不願意分配一個小分割槽專門作為交換分割槽,但任務一多,記憶體又不夠(特別是還在linux下開虛擬機器),又不願意花錢升級記憶體),這時最好的解決方案就是建立一個磁碟檔案來作為交換空間(若磁碟是SSD,那效果也是非常明顯的)

android系統chmod -R命令無效

組員反饋, chmod 777 -R dirname命令 在a裝置,正常工作 在b裝置,無效 問題排查: 在a裝置正常,說明命令格式沒問題 在b裝置無效,怪環境咯。 這裡跟環境相關的只有chmod命令 那麼就試試busybox的命令吧,咦,問題解決。

IDEA/Android Studio報Ambiguous method call的解決方法

最近在用IDEA開發android一直報如題的錯誤,此時是可以正常編譯執行,但紅色的錯誤提示總是令人非常不爽。 錯誤都指向了java基類Object.java中的方法存在ambiguous method call,此問題出現在設定了Sourcepath的情況下,當不設定時不

Android系統設定settings應用學習()--允許未知來源應用安裝

 settings,是Android系統應用--設定的原始碼,包名稱為:com.android.settings  安全設定程式碼:SecuritySettings.java /* * Copyright (C) 2007 The Android Open Source

樹莓派Android系統串列埠GPS模組驅動

由於方案需要Android系統,安裝了konstakang大神編譯的LineageOS14.1 for RPI3,最新版解決了Wifi連線的問題,點32個贊! TB買了個串列埠GPS模組,相容NMEA協議,但是如何整合到安卓系統呢?一通搜尋後找到了解決方法: 1、到http