【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.mzph.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请求绕过在中间件中执行…