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問題的過程》
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