安卓 Audio Thread 分析

一、PlaybackThread::threadLoop_write

1.變量

mFramesWritten
類型: int64_t
作用: 記錄從線程啟動以來已寫入音頻設備的幀數(不包括掛起狀態下的寫入)

mSuspendedFrames
類型: int64_t
作用: 記錄線程在掛起(suspended)狀態下模擬寫入的幀數

mBytesRemaining
作用: 用于實時跟蹤待寫入的剩余字節數。初始化時等于mCurrentWriteLength,表示本輪需寫入的總量;寫入過程中遞減,直至歸零

mCurrentWriteLength
混音模式?:threadLoop_mix()計算混音數據后,設置mCurrentWriteLength為需寫入的字節數。
直通模式?:當需要立即寫入時,mCurrentWriteLength設為mSinkBufferSize(硬件接收緩沖區大小)

2.代碼段

mBytesRemaining !=0
if (mBytesRemaining) {// 如果還有未寫入的字節數據(mBytesRemaining > 0)// FIXME: 重構代碼以減少系統調用次數(當前實現可能效率不高)const int64_t lastIoBeginNs = systemTime(); // 記錄開始寫入操作的時間點(納秒)ret = threadLoop_write(); // 執行實際的音頻數據寫入操作const int64_t lastIoEndNs = systemTime(); // 記錄寫入操作完成的時間點(納秒)if (ret < 0) {// 寫入失敗(ret < 0)mBytesRemaining = 0; // 清零剩余字節數(標記寫入失敗)} else if (ret > 0) {// 成功寫入部分數據(ret > 0)mBytesWritten += ret; // 更新總寫入字節數mBytesRemaining -= ret; // 減少剩余待寫入字節數const int64_t frames = ret / mFrameSize; // 計算實際寫入的音頻幀數mFramesWritten += frames; // 更新總寫入幀數writePeriodNs = lastIoEndNs - mLastIoEndNs; // 計算本次寫入操作的時間間隔(納秒)// 僅對線性PCM格式音頻進行處理(幀大小固定)if (audio_has_proportional_frames(mFormat)) {// 檢查是否處于連續混音周期(混音器狀態就緒且是連續寫入)if (mMixerStatus == MIXER_TRACKS_READY &&loopCount == lastLoopCountWritten + 1) {// 計算抖動(jitter):實際寫入時間與理論時間的偏差const double jitterMs =TimestampVerifier<int64_t, int64_t>::computeJitterMs({frames, writePeriodNs}, // 本次寫入的幀數和時間間隔{0, 0} /* 上次時間戳 */, // 首次計算使用零值mSampleRate); // 音頻采樣率// 計算處理時間(從上次寫入結束到本次寫入開始的時間間隔)const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6;// 加鎖修改線程內部狀態audio_utils::lock_guard _l(mutex());mIoJitterMs.add(jitterMs); // 記錄抖動數據用于統計mProcessTimeMs.add(processMs); // 記錄處理時間用于統計// 如果使用MonoPipe(環形緩沖區)if (mPipeSink.get() != nullptr) {MonoPipe* monoPipe = static_cast<MonoPipe*>(mPipeSink.get());const ssize_t availableToWrite = mPipeSink->availableToWrite(); // 獲取可寫空間const size_t pipeFrames = monoPipe->maxFrames(); // 管道總容量(幀)// 計算管道中已占用的幀數(總容量 - 可寫空間)const size_t remainingFrames = pipeFrames - max(availableToWrite, 0);mMonopipePipeDepthStats.add(remainingFrames); // 記錄管道使用深度}}// 檢測寫入阻塞(耗時過長)const int64_t deltaWriteNs = lastIoEndNs - lastIoBeginNs; // 實際寫入耗時if ((mType == MIXER || mType == SPATIALIZER) && // 僅檢查混音/空間音頻線程deltaWriteNs > maxPeriod) { // 超過最大允許時間mNumDelayedWrites++; // 增加延遲寫入計數// 避免頻繁告警(超過告警間隔才觸發)if ((lastIoEndNs - lastWarning) > kWarningThrottleNs) {ATRACE_NAME("underrun"); // 性能分析標記ALOGW("write blocked for %lld msecs, %d delayed writes, thread %d",(long long)deltaWriteNs / NANOS_PER_MILLISECOND, // 納秒轉毫秒mNumDelayedWrites, // 延遲計數mId); // 線程IDlastWarning = lastIoEndNs; // 更新最后告警時間}}}// 更新時間追蹤變量mLastIoBeginNs = lastIoBeginNs; // 更新最后寫入開始時間mLastIoEndNs = lastIoEndNs; // 更新最后寫入結束時間lastLoopCountWritten = loopCount; // 更新最后成功寫入的循環計數}
}
mBytesRemaining == 0
// 檢查當前沒有剩余字節需要寫入(mBytesRemaining == 0)
if (mBytesRemaining == 0) {// 重置當前寫入長度為0mCurrentWriteLength = 0;// 如果混音器狀態為TRACKS_READY(有活躍音軌且數據就緒)if (mMixerStatus == MIXER_TRACKS_READY) {// 執行混音操作:將各音軌數據混合到混音緩沖區// 同時會設置mCurrentWriteLength為實際寫入長度threadLoop_mix();} // 如果混音器狀態不是DRAIN_TRACK或DRAIN_ALL(非排空狀態)else if ((mMixerStatus != MIXER_DRAIN_TRACK) && (mMixerStatus != MIXER_DRAIN_ALL)) {// 計算線程需要休眠的時間(可能為0)threadLoop_sleepTime();// 如果休眠時間為0(需要立即寫入)if (mSleepTimeUs == 0) {// 設置當前寫入長度為整個接收緩沖區大小mCurrentWriteLength = mSinkBufferSize;// 統計所有活躍音軌的欠載(underrun)情況:for (const auto& track : activeTracks) {// 只處理活躍且未停止/暫停/終止的音軌if (track->fillingStatus() == IAfTrack::FS_ACTIVE&& !track->isStopped()&& !track->isPaused()&& !track->isTerminated()) {// 記錄因線程休眠導致的欠載ALOGV("%s: track(%d) %s underrun due to thread sleep of %zu frames",__func__, track->id(), track->getTrackStateAsString(),mNormalFrameCount);// 在音軌代理中累加欠載幀數track->audioTrackServerProxy()->tallyUnderrunFrames(mNormalFrameCount);}}}}// 以下處理混音后的數據:// ----------------------------------------------------------------// 檢查混音緩沖區是否有效且需要復制到效果緩沖區或接收緩沖區if (mMixerBufferValid && (mEffectBufferValid || !mHasDataCopiedToSinkBuffer)) {// 確定目標緩沖區(效果緩沖區優先,否則直接到接收緩沖區)void *buffer = mEffectBufferValid ? mEffectBuffer : mSinkBuffer;// 確定目標格式(效果緩沖區格式優先,否則使用設備格式)audio_format_t format = mEffectBufferValid ? mEffectBufferFormat : mFormat;// 如果不需要效果處理(直通模式),需額外處理:if (!mEffectBufferValid) {// 單聲道混合處理(如果需要)if (requireMonoBlend()) {mono_blend(mMixerBuffer, mMixerBufferFormat, mChannelCount,mNormalFrameCount, true /*limit*/);}// 聲道平衡處理(如果沒有FastMixer)if (!hasFastMixer()) {mBalance.setBalance(mMasterBalance.load());mBalance.process((float *)mMixerBuffer, mNormalFrameCount);}}// 格式轉換:將混音緩沖區的數據轉換格式后復制到目標緩沖區memcpy_by_audio_format(buffer, format, mMixerBuffer, mMixerBufferFormat,mNormalFrameCount * (mixerChannelCount + mHapticChannelCount));// 特殊處理:如果有觸覺通道且直通輸出if (!mEffectBufferValid && mHapticChannelCount > 0) {// 調整通道布局(分離音頻和觸覺數據)adjust_channels_non_destructive(buffer, mChannelCount, buffer,mChannelCount + mHapticChannelCount,audio_bytes_per_sample(format),audio_bytes_per_frame(mChannelCount, format) * mNormalFrameCount);}}// 更新剩余字節數(準備寫入)mBytesRemaining = mCurrentWriteLength;// 如果設備處于掛起狀態(如藍牙通話)if (isSuspended()) {// 計算休眠時間(模擬寫入完成)mSleepTimeUs = suspendSleepTimeUs();// 計算剩余幀數const size_t framesRemaining = mBytesRemaining / mFrameSize;// 更新統計信息mBytesWritten += mBytesRemaining;mFramesWritten += framesRemaining;mSuspendedFrames += framesRemaining; // 用于調整內核位置mBytesRemaining = 0; // 重置剩余字節}
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/90304.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/90304.shtml
英文地址,請注明出處:http://en.pswp.cn/web/90304.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

JavaWeb_原始項目初識(一)

Students2025項目&#xff08;一&#xff09; 原始ServletJSP架構項目初步搭建 jsp項目已被淘汰&#xff0c;在此學習目的是了解未來學習的新技術的底層原理項目結構&#xff1a;項目結構介紹&#xff1a; 目前階段只完成了初始化的后端搭建&#xff0c;實現從本地數據庫獲取數…

前端_CSS復習

文章目錄CSS復習1. css三種引入方式1.1 行內樣式常用樣式&#xff1a;1.2頁內樣式常見選擇器&#xff1a;1. 標記選擇器2. id選擇器3. 類選擇器&#xff08;最常用&#xff09;4. 星號選擇器&#xff0c;頻率很低5. 復合選擇器6. 偽類選擇器&#xff1a;7. 子元素偽類1.3引入外…

工業互聯網時代,如何通過混合SD-WAN提升煤炭行業智能化網絡安全

1. 背景&#xff1a;煤炭行業智能化轉型的網絡挑戰隨著工業互聯網技術的普及&#xff0c;煤炭行業智能化轉型進入加速期。選煤廠作為煤炭生產的核心環節&#xff0c;需要構建一套既安全又高效的網絡系統&#xff0c;以滿足工業控制系統&#xff08;ICS&#xff09;、智能設備和…

AI浪潮下數據中心的突圍者:臺達DPH Gen3系列UPS如何重構供電架構

2025年6月13日&#xff0c;臺達-中達電通資通訊基礎設施事業部聯合中國數據中心工作組&#xff08;CDCC&#xff09;在江蘇吳江舉辦"數據中心供配電技術革新與AI算力基礎設施未來展望研討會"&#xff0c;同時開展CDCC專家組工廠參觀。盛會匯聚了數據中心行業專家、互…

DiffServ服務模型與DS碼點詳解

1. DiffServ概述 DiffServ(Differentiated Services&#xff0c;差異化服務)是IETF定義的一種QoS(Quality of Service)體系結構&#xff0c;旨在為IP網絡提供可擴展的服務區分能力。與傳統的IntServ(集成服務)模型不同&#xff0c;DiffServ采用簡單、粗粒度的流量分類機制&…

基于 PIC16 系列的多功能電子煙(溫控 + 電壓控制 + 多模式)方案

基于 PIC16 系列的多功能電子煙&#xff08;溫控 電壓控制 多模式&#xff09;方案 一、芯片與最小系統推薦型號&#xff1a;PIC16F18313/18323 8-bit 內核&#xff0c;14/20-pin 小封裝&#xff0c;成本低28 MHz 內部振蕩&#xff0c;帶 10-bit ADC&#xff08;12 通道&…

小模數齒輪的加工方法有哪些?

小模數齒輪(一般指0.3≤Mn≤1)的加工方法有哪些呢&#xff1f;小模數齒輪的加工方法主要分為減材、增材、變形加工三類&#xff1a; 去材料制造 有銑齒、滾齒、插齒、刨齒、剃齒、拉齒、沖齒、研磨、珩齒、磨齒及其拋光、線切割等。 增材制造 有注塑&#xff08;塑料、尼龍&…

若依前后端分離版學習筆記(二)——系統菜單介紹

前言&#xff1a; 這一節是將ruoyi的前端界面過一遍&#xff0c;查看所有系統菜單及頁面功能&#xff0c;為后續代碼學習做準備。&#xff08;注意&#xff1a;文中包含大量截圖&#xff0c;截圖為從本地啟動的3.9.0 vue3的前端界面。&#xff09; 一 系統管理 1 用戶管理 主要…

VRRP技術-設備備份技術

一、VRRP的概念及應用場景1.定義在 VRRP&#xff08;虛擬路由冗余協議&#xff09;中&#xff0c;將多個路由器邏輯上看作一個路由器時所使用的虛擬 IP 地址&#xff0c;需要滿足以下要求&#xff1a;這個虛擬 IP 地址必須與該 VRRP 組內所有物理路由器的接口 IP 地址處于同一網…

VUE2 學習筆記5 動態綁定class、條件渲染、列表過濾與排序

動態綁定class樣式&#xff1a;先設置css&#xff1a;<style>.styleBackgroundColor{background-color: aqua;}.styleContent{width:300px;height: 200px;}.styleBorder{border: 2px black solid;}</style>vue模版中&#xff0c;使用動態類名綁定&#xff0c;一般可…

推客系統全棧開發指南:從架構設計到高并發實戰

一、推客系統概述與市場前景推客系統&#xff08;也稱為"推客營銷系統"或"社交電商系統"&#xff09;是近年來快速崛起的社交化營銷工具&#xff0c;它通過整合社交網絡與電子商務功能&#xff0c;讓每個用戶都能成為產品的推廣者并獲得相應獎勵。市場數據…

RabbitMQ有多少種Exchange?

面試回答模板 “RabbitMQ 在 AMQP 協議中預定義了 四種常用交換機 兩種特殊類型&#xff0c;共 6 種&#xff1a; Direct&#xff1a;routing-key 全等匹配&#xff1b;Fanout &#xff1a;廣播&#xff0c;忽略 key&#xff1b;Topic&#xff1a;按 *.# 通配符匹配&#xff1…

ctfshow pwn43

1. 分析程序首先檢查程序相關保護&#xff0c;發現程序為32位且只開啟了一個NX保護checksec pwn使用IDA進行逆向分析代碼&#xff0c;查看漏洞觸發點&#xff1a;在main函數中&#xff0c;有一個ctfshow函數&#xff0c;這里我們跟進ctfshow()發現存在一個gets()函數&#xff0…

內網IM:BeeWorks私有化部署的安全通訊解決方案

在當今數字化辦公環境中&#xff0c;內網IM已成為企業保障數據安全的核心工具。BeeWorks作為一款支持私有化部署的內網IM解決方案&#xff0c;能夠幫助企業構建完全自主可控的通訊系統。無論是政府機構、金融機構&#xff0c;還是對數據安全要求極高的企業&#xff0c;BeeWorks…

SHA512算法詳解

SHA-512 是 SHA-2&#xff08;Secure Hash Algorithm 2&#xff09;系列密碼散列函數的重要成員&#xff0c;由美國國家安全局&#xff08;NSA&#xff09;設計&#xff0c;2001 年被納入 NIST&#xff08;美國國家標準與技術研究院&#xff09;的 FIPS 180 標準&#xff0c;后…

通過python管理vcenter中的虛擬機

通過python管理vcenter中的虛擬機因業務需要&#xff0c;需在夜間關閉虛擬機&#xff0c;隨通過計劃任務遠程管理開機、關機虛擬機一、通過docker配置python3.9環境 Dockerfile FROM python:3.9 RUN pip3 install pyvmomi7.0.0創建自定義鏡像 docker build -t pyvmomi7:v1 .二…

AWS S3 生命周期管理最佳實踐:IoT Core 日志的智能存儲優化

在現代物聯網應用中,設備日志數據的管理是一個重要挑戰。隨著設備數量的增長,日志數據量呈指數級增長,如何有效管理這些數據的存儲成本成為關鍵問題。本文將分享如何為 AWS IoT Core 日志實施智能生命周期管理策略。 背景與挑戰 IoT 設備產生的日志數據具有以下特點: 數據…

18.TaskExecutor獲取ResourceManagerGateway

TaskExecutor獲取ResourceManagerGatewayTaskExecutor 與 ResourceManager 之間的交互機制較為復雜&#xff0c;核心可以拆分為三個階段&#xff1a; 首次發現與注冊連接建立心跳維持 本文聚焦連接建立階段&#xff0c;詳細分析底層 RPC 連接的實現原理。回顧&#xff1a;start…

kafka查看消息的具體內容 kafka-dump-log.sh

目錄kafka 消息查看1. 直接查看日志文件內容步驟&#xff1a;2. 使用 Kafka 工具查看日志主要參數說明常用命令&#xff1a;輸出說明&#xff1a;3. 注意事項kafka 消息日志文件詳解我們有時候遇到這樣的需求&#xff0c;需要查看下kafka消息的內容。 kafka 消息查看 查看 Ka…

Spring Cloud OpenFeign 常用注解_筆記

Spring Cloud OpenFeign 提供了一種聲明式、模板化的HTTP客戶端&#xff0c;可以通過簡單的接口描述遠程調用&#xff0c;而不必手動編寫低級的 HTTP 客戶端代碼。FeignClient用法參考&#xff1a;FeignClient用法-筆記-CSDN博客。這里梳理Spring Cloud OpenFeign 常用注解。 1…