【Audio開發三】音頻audio中幀frameSize ,周期大小periodsize,緩沖區buffer原理詳解以及代碼流程分析

一、基礎概述

在分析獲取最小幀數前,我們先來了解幾個相關的概念。
1,幀
幀(frame):表示一個完整的聲音單元,所謂的聲音單元是指一個采樣樣本。如果是雙聲道,那么一個完整的聲音單元就是 2 個樣本,如果是 5.1 聲道,那么一個完整的聲音單元就是 6 個樣本了。幀的大小(一個完整的聲音單元的數據量)等于聲道數乘以采樣位寬,即 frameSize = channelCount * bytesPerSample。這里bytesPerSample就是量化編碼的字節大小。無論是框架層還是內核層,都是以幀為單位去管理音頻數據緩沖區的。在音頻開發領域通常也會說采樣點來對應幀這個概念。因為將幀的個數除以采樣率就可以直接獲得對應音頻數據的時長。(PCM格式),又因為采樣頻率的倒數,就是一個采樣點的時間,乘以采樣點的數量就是整體音頻的時長了。

2,傳輸延遲
傳輸延遲(latency):一個處理單元引入的delay。

3,周期
周期(period):Linux ALSA 把數據緩沖區劃分為若干個塊,dma 每傳輸完一個塊上的數據即發出一個硬件中斷,CPU 收到中斷信號后,再配置 dma 去傳輸下一個塊上的數據。一個塊即是一個周期。

4,周期大小
周期大小(periodSize):即是一個數據塊的幀數。

再回到傳輸延遲(latency),每次傳輸產生的延遲等于周期大小除以采樣率,即 latency = periodSize / sampleRate。因為periodSize 指的是幀數,而幀大小 channelCount * bytesPerSample,所以幀總數除以采樣頻率就是整個傳輸過程消耗的時間。

5,音頻重采樣
音頻重采樣是指這樣的一個過程——把一個采樣率的數據轉換為另一個采樣率的數據。Android 原生系統上,音頻硬件設備一般都工作在一個固定的采樣率上(如 48 KHz),因此所有音軌數據都需要重采樣到這個固定的采樣率上,然后再輸出。因為系統中可能存在多個音軌同時播放,而每個音軌的采樣率可能是不一致的,比如在播放音樂的過程中,來了一個提示音,這時需要把音樂和提示音混音并輸出到硬件設備,而音樂的采樣率和提示音的采樣率不一致,問題來了,如果硬件設備工作的采樣率設置為音樂的采樣率的話,那么提示音就會失真,因此最簡單見效的解決方法是:硬件設備工作的采樣率固定一個值,所有音軌在 AudioFlinger 都重采樣到這個采樣率上,混音后輸出到硬件設備,保證所有音軌聽起來都不失真。 sample、frame、period、latency 這些概念與 Linux ALSA 及硬件設備的關系非常密切。

二,Audio buffersize實現流程分析

1.參數介紹

AudioSource:采樣通道,一般為MediaRecorder.AudioSource.MIC;

SampleRate:采樣率,一般在8000Hz~48000Hz之間,不同的硬件設備這個值不同;

Channel:采樣聲道數,一般為單通道或立體聲,即 AudioFormat.CHANNEL_CONFIGURATION_MONO或 AudioFormat.CHANNEL_CONFIGURATION_STEREO;

AudioFormat:采樣精度,一般為8位或16位,即AudioFormat.ENCODING_16BIT或8BIT;

bufferSize:緩沖區大小:可以通過getMinBufferSize來獲取;

buffer:DMA緩沖區大小

period: 周期,是每兩個硬件中斷之間的幀數,即傳輸多少個采樣幀數據,DMA反饋一個中斷,一般一個周期包含1024個采樣幀,在HAL層中定義。

frame: 每個采樣幀的大小。為一個采樣點的字節數*聲道數,因為對于多通道的話,用1個采樣點的字節數表示不全,因為播放的時候肯定是多個聲道的數據都要播放出來才行,所以為了方便,就說1秒鐘有多少個frame,這樣就能拋開聲道數,把意思表達全了。

sample: 采樣聲道,一般為2個字節。

在Audio系統中,buffer的組成如圖所示
在這里插入圖片描述

2,Audio子系統之AudioRecord.getMinBufferSize

為了讓音頻數據通路能正常運轉,共享FIFO必須達到最小緩沖區的大小。如果數據緩沖區分配得過小,那么播放聲音會頻繁遭遇 underrun,underrun 是消費者(AudioFlinger::PlaybackThread)不能及時從緩沖區拿到數據,造成的后果就是聲音斷續卡頓。
1、獲取方法

AudioTrack.getMinBufferSize(48000, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

從函數參數來看,返回值取決于采樣率、音頻格式、聲道數這三個屬性。

2,接下來進入系統分析具體實現
調用服務端的函數,獲取frameCount大小,最后返回了frameCount聲道數采樣精度,其中frameCount表示最小采樣幀數,繼續分析frameCount的計算方法

    frameworks/av/media/libmedia/AudioRecord.cpp
status_t AudioRecord::getMinFrameCount(size_t* frameCount,uint32_t sampleRate,audio_format_t format,audio_channel_mask_t channelMask)
{if (frameCount == NULL) {return BAD_VALUE;}size_t size;status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &size);if (status != NO_ERROR) {ALOGE("AudioSystem could not query the input buffer size for sampleRate %u, format %#x, ""channelMask %#x; status %d", sampleRate, format, channelMask, status);return status;}//計算出最小的frame// We double the size of input buffer for ping pong use of record buffer.// Assumes audio_is_linear_pcm(format)if ((*frameCount = (size * 2) / (audio_channel_count_from_in_mask(channelMask) *audio_bytes_per_sample(format))) == 0) {ALOGE("Unsupported configuration: sampleRate %u, format %#x, channelMask %#x",sampleRate, format, channelMask);return BAD_VALUE;}return NO_ERROR;
}

此時frameCount= size2/(聲道數采樣精度),注意這里需要double一下,而size是由hal層得到的,AudioSystem::getInputBufferSize()函數最終會調用到HAL層

    frameworks/av/media/libmedia/AudioSystem.cpp
status_t AudioSystem::getInputBufferSize(uint32_t sampleRate, audio_format_t format,audio_channel_mask_t channelMask, size_t* buffSize)
{const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();if (af == 0) {return PERMISSION_DENIED;}Mutex::Autolock _l(gLockCache);// Do we have a stale gInBufferSize or are we requesting the input buffer size for new valuessize_t inBuffSize = gInBuffSize;if ((inBuffSize == 0) || (sampleRate != gPrevInSamplingRate) || (format != gPrevInFormat)|| (channelMask != gPrevInChannelMask)) {gLockCache.unlock();inBuffSize = af->getInputBufferSize(sampleRate, format, channelMask);gLockCache.lock();if (inBuffSize == 0) {ALOGE("AudioSystem::getInputBufferSize failed sampleRate %d format %#x channelMask %x",sampleRate, format, channelMask);return BAD_VALUE;}// A benign race is possible here: we could overwrite a fresher cache entry// save the request paramsgPrevInSamplingRate = sampleRate;gPrevInFormat = format;gPrevInChannelMask = channelMask;gInBuffSize = inBuffSize;}*buffSize = inBuffSize;return NO_ERROR;
}

所以實際上會通過binder到達AudioFlinger中

frameworks\av\services\audioflinger\AudioFlinger.cpp

size_t AudioFlinger::getInputBufferSize(uint32_t sampleRate, audio_format_t format,audio_channel_mask_t channelMask) const
{status_t ret = initCheck();if (ret != NO_ERROR) {return 0;}AutoMutex lock(mHardwareLock);mHardwareStatus = AUDIO_HW_GET_INPUT_BUFFER_SIZE;audio_config_t config;memset(&config, 0, sizeof(config));config.sample_rate = sampleRate;config.channel_mask = channelMask;config.format = format;audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();size_t size = dev->get_input_buffer_size(dev, &config);mHardwareStatus = AUDIO_HW_IDLE;return size;
}

把參數傳遞給hal層,獲取buffer大小

hardware\aw\audio\tulip\audio_hw.c

static size_t get_input_buffer_size(uint32_t sample_rate, int format, int channel_count)
{size_t size;size_t device_rate;if (check_input_parameters(sample_rate, format, channel_count) != 0)return 0;/* take resampling into account and return the closest majoringmultiple of 16 frames, as audioflinger expects audio buffers tobe a multiple of 16 frames */size = (pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate;size = ((size + 15) / 16) * 16;return size * channel_count * sizeof(short);
}

這里包含一個結構體struct pcm_config,定義了一個周期包含了多少采樣幀,并根據結構體的rate數據進行重采樣計算,這里的rate是以MM_SAMPLING_RATE為標準,即44100,一個采樣周期有1024個采樣幀,然后計算出重采樣之后的size

同時audioflinger的音頻buffer是16的整數倍,所以再次計算得出一個最接近16倍的整數,最后返回size聲道數1幀數據所占字節數

struct pcm_config pcm_config_mm_in = {.channels = 2,.rate = MM_SAMPLING_RATE,.period_size = 1024,.period_count = CAPTURE_PERIOD_COUNT,.format = PCM_FORMAT_S16_LE,
};

3,總結:

minBuffSize = ((((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2) / (audio_channel_count_from_in_mask(channelMask) * audio_bytes_per_sample(format))) * channelCount * audio_bytes_per_sample(format);

=(((((((pcm_config_mm_in.period_size * sample_rate) / pcm_config_mm_in.rate) + 15) / 16) * 16) * channel_count * sizeof(short)) * 2)

其中:pcm_config_mm_in.period_size=1024;pcm_config_mm_in.rate=44100;這里我們可以看到他除掉(channelCount*format),后面又乘回來了,這個是因為在AudioRecord.cpp對frameCount進行了一次校驗,判斷是否支持該參數的設置。

以getMinBufferSize(44100, MONO, 16BIT);為例,即sample_rate=44100,channel_count=1, format=2,那么

BufferSize = (((1024sample_rate/44100)+15)/16)16channel_countsizeof(short)*2 = 4096

即最小緩沖區大小為:周期大小 * 重采樣 * 采樣聲道數 * 2 * 采樣精度所占字節數;這里的2的解釋為We double the size of input buffer for ping pong use of record buffer,采樣精度:PCM_8_BIT為unsigned char,PCM_16_BIT為short,PCM_32_BIT為int。

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

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

相關文章

K8S學習之基礎七十五:istio實現灰度發布

istio實現灰度發布 上傳鏡像到harbor 創建兩個版本的pod vi deployment-v1.yaml apiVersion: apps/v1 kind: Deployment metadata:name: appv1labels:app: v1 spec:replicas: 1selector:matchLabels:app: v1apply: canarytemplate:metadata:labels:app: v1apply: canaryspec…

C++藍橋杯填空題(攻克版)

片頭 嗨~小伙伴們&#xff0c;咱們繼續攻克填空題&#xff0c;先把5分拿到手~ 第1題 數位遞增的數 這道題&#xff0c;需要我們計算在整數 1 至 n 中有多少個數位遞增的數。 什么是數位遞增的數呢&#xff1f;一個正整數如果任何一個數位不大于右邊相鄰的數位。比如&#xf…

【Python】數據結構

【Python】數據結構&#xff1a; Series&#xff1a;1、通過列表創建Series類對象2、顯示地給數據指定標簽索引3、通過字典創建Series類對象4、獲取索引5、獲取數據 DataFrame&#xff1a;1、通過數組創建一個DataFrame類對象2、指定列索引3、指定行索引4、獲取列的數據5、查看…

Android XML布局與Compose組件對照手冊

下面我將詳細列出傳統 XML 布局中的組件與 Compose 組件的對應關系&#xff0c;幫助您更好地進行遷移或混合開發。 基礎布局對應 XML 布局Compose 組件說明LinearLayout (vertical)Column垂直排列子項LinearLayout (horizontal)Row水平排列子項FrameLayoutBox層疊子項Relativ…

云原生運維在 2025 年的發展藍圖

隨著云計算技術的不斷發展和普及&#xff0c;云原生已經成為了現代應用開發和運維的主流趨勢。云原生運維是指在云原生環境下&#xff0c;對應用進行部署、監控、管理和優化的過程。在 2025 年&#xff0c;云原生運維將迎來更加廣闊的發展前景&#xff0c;同時也將面臨著一系列…

js day5

復習模板字符串&#xff1a; 在輸出語句里面 document.write(我今年${a}歲了)中間是反引號&#xff1b;里面是${變量}&#xff1b; 復習基本類型 number String null undefined boolean 檢測數據類型輸出typedf 變量則可&#xff1b; 添加鏈接描述 復習樣式變量table什么的邊…

SmolVLM2: The Smollest Video Model Ever(三)

這是對《SmolLM2: When Smol Goes Big — Data-Centric Training of a Small Language Model》的翻譯閱讀 摘要 雖然大語言模型在人工智能的許多應用中取得了突破&#xff0c;但其固有的大規模特性使得它們在計算上成本高昂&#xff0c;并且在資源受限的環境中部署具有挑戰性。…

汽車軟件開發常用的需求管理工具匯總

目錄 往期推薦 DOORS&#xff08;IBM &#xff09; 行業應用企業&#xff1a; 應用背景&#xff1a; 主要特點&#xff1a; Polarion ALM&#xff08;Siemens&#xff09; 行業應用企業&#xff1a; 應用背景&#xff1a; 主要特點&#xff1a; Codebeamer ALM&#x…

爬蟲工程師雜活工具人

30歲的年齡;這個年齡大家都是成年人;都是做父母的年齡了;你再工位上的心態會發生很大變化的; 爬蟲工程師基本都是如此;社會最low的一幫連銷售都做不了的;單子都開不出來的然后轉行做爬蟲工程師的;這樣的人基本不太和社會接觸; 你作為爬蟲初級工程師就敲著鍵盤然后解析著html;…

如何使用Tomcat

1 簡介 Tomcat是Apache 軟件基金會&#xff08;Apache Software Foundation&#xff09;的Jakarta 項目中的一個核心項目&#xff0c;由Apache、Sun 和其他一些公司及個人共同開發而成。因為Tomcat 技術先進、性能穩定&#xff0c;而且免費&#xff0c;成為目前比較流行的Web 應…

【AI工具】FastGPT:開啟高效智能問答新征程

前言 在人工智能飛速發展的當下&#xff0c;各類 AI 工具如雨后春筍般涌現。FastGPT 作為一款基于大語言模型&#xff08;LLM&#xff09;的知識圖譜問答系統&#xff0c;憑借其強大的數據處理和模型調校能力&#xff0c;為用戶帶來了便捷的使用體驗。今天&#xff0c;就讓我們…

14. git remote

基本概述 git remote 的作用是&#xff1a;查看、添加、修改和刪除與本地倉庫關聯的遠程倉庫。 基本用法 1.查看遠程倉庫 git remote # 顯示所有關聯的遠程倉庫&#xff08;名稱&#xff09; git remote -v # 顯示所有關聯的遠程倉庫&a…

【spark-submit】--提交任務

Spark-submit spark-submit 是 Apache Spark 提供的用于提交 Spark 應用程序到集群的命令行工具。 基本語法 spark-submit [options] <app-jar> [app-arguments]常用參數說明 應用程序配置 --class <class-name>: 指定應用程序的主類&#xff08;對于 Java/Sc…

2025.4.10總結

今日記錄&#xff1a;今天提了兩個問題單&#xff0c;最近要關注一下產出了&#xff0c;上半年的考核如今還剩兩個月了&#xff0c;然后發現一同入職的同事&#xff0c;有的人進步得很快&#xff0c;得向優秀得同事看齊了&#xff0c;不然幾年過去&#xff0c;別人連升好幾年&a…

SvelteKit 最新中文文檔教程(18)—— 淺層路由和 Packaging

前言 Svelte&#xff0c;一個語法簡潔、入門容易&#xff0c;面向未來的前端框架。 從 Svelte 誕生之初&#xff0c;就備受開發者的喜愛&#xff0c;根據統計&#xff0c;從 2019 年到 2024 年&#xff0c;連續 6 年一直是開發者最感興趣的前端框架 No.1&#xff1a; Svelte …

Winform入門進階企業級開發示例:http接口數據清洗轉換、斷線續傳、mqtt數據傳輸實例詳解(附代碼資源下載)

場景 C#/Winform入門、進階、強化、擴展、知識體系完善等知識點學習、性能優化、源碼分析專欄分享: C#/Winform入門、進階、強化、擴展、知識體系完善等知識點學習、性能優化、源碼分析專欄分享_winform 強化學習-CSDN博客 如何將以上相關理論知識學以致用。下面針對Winform…

Python代碼縮進統一規范

一、Python縮進的重要性:邏輯與可讀性的橋梁 1. 語法規則的核心 Python與其他編程語言顯著不同之處在于,它使用縮進來表示代碼塊的層次結構。不像C、Java等語言依靠大括號{}來明確函數體、循環體和條件語句的范圍,Python完全依賴縮進來界定這些邏輯單元。例如,在一個if條…

asp.net core 項目發布到 IIS 服務器

目錄 一、VS2022 發布 二、設置IIS服務 三、配置IIS管理器 &#xff08;一&#xff09;打開IIS管理器 &#xff08;二&#xff09;添加站臺 &#xff08;三&#xff09;配置應用程式集區 四、安裝ASP.NET Core Hosting Bundle 五、設定IIS的日志位置 六、測試 一、VS2…

spring mvc中不同服務調用類型(聲明式(Feign)、基于模板(RestTemplate)、基于 SDK、消息隊列、gRPC)對比詳解

RestControllerAdvice 和 ControllerAdvice 對比詳解 1. 基本概念 注解等效組合核心作用ControllerAdviceComponent RequestMapping&#xff08;隱式&#xff09;定義全局控制器增強類&#xff0c;處理跨控制器的異常、數據綁定或全局響應邏輯。RestControllerAdviceControll…

CVE-2025-29927 Next.js 中間件鑒權繞過漏洞

Next.js Next.js 是一個基于 React 的現代 Web 開發框架&#xff0c;用來構建高性能、可擴展的 Web 應用和網站。 CVE-2025-29927 Next.js 中間件鑒權繞過漏洞 CVE-2025-29927是Next.js框架中的一個授權繞過漏洞&#xff0c;允許攻擊者通過特制的HTTP請求繞過在中間件中執行…