白话Android音频系统原理(上)(标贝科技)

2023-05-16

白话Android音频系统原理(上)

  • 一、基本原理
  • 二、初始化准备工作
  • 三、播放流程
  • 四、录音流程

标贝科技 https://ai.data-baker.com/#/?source=qwer12

填写邀请码fwwqgs,每日免费调用量还可以翻倍
在这里插入图片描述
在这里插入图片描述

一、基本原理

  1. 谁来发起(录音和播放)?AudioRecorder,AudioTrack。
  2. 谁来处理(录音和播放)? AudioFlinger!
  3. 怎么处理?AudioPolicyService !
  4. 什么是output?为了便于管理, 把一个设备上具有相同参数的一组播放device组织称为一个output。它描述一些实际支持的设备(有实际硬件)一个output对应一个处理线程thread。

二、初始化准备工作

  1. 从frameworks/av/media/audioserver/main_audioserver.cpp开始,系统启动后,会初始化AudioFlinger和AudioPolicyService。先初始化AudioFlinger,后面AudioPolicyService初始化的时候,会用到AudioFlinger里面的方法。
  2. 初始化AudioPolicyService的时候,实际上创建了一个AudioPolicyManager,主要做3件事:
    a. 加载配置文件 audio_policy.conf (AudioPolicyManager完成
    b. 加载audio policy硬件抽象库(AudioFlinger完成
    c. openOutput(创建一个MixerThread线程与对应的output相关联,output对应着设备。即:将创建的线程添加到播放线程列表 mPlaybackThreads中,之后app就可以把数据传递给线程了,进而传递给硬件设备了。每一个线程都对应一个output 。AudioFlinger完成

三、播放流程

  1. Java层的AudioTrack的使用流程是:
    a. 构建参数并传递给AudioTrack进行初始化。
    b. 执行AudioTrack.wrtie写入数据。
    c. 执行AudioTrack.start操作开始播放。

  2. AudioTrack创建
    第一步,创建启动AudioTrackThread线程来提供音频数据。
    即:AudioTrack中的AudioTrackThread提供数据。
    第二步,根据APP构造AudioTrack时指定的声音属性找到对应的output。
    即:先通过声音属性获取strategy类别,再通过strategy类别,确定播放设备(耳机/蓝牙/喇叭),最后根据设备device,去查找哪些output上有这些device,然后从output中找到最合适的那个。
    第三步,创建playbackthread中的Track
    即:根据第二步获取到的output来确定对应的PlaybackThread类型的播放线程,在playbackthread中创建新的track,并把track加入到PlayBackThread的mTracks表中。这一步代表着从app中的AudioTrack创建会同时让AudioFlinger中的与output相关联的PlaybackThread内也创建一个track,app中的AudioTrack提供数据,PlaybackThread内的track处理数据。这二者通过跨进程共享的缓冲区buffer 实现共享进行通信。

  3. “生产者-消费者”数据交互
    a. 这里的生产者实际是App中的AudioTrack。app不断给AudioTrack wrtie数据,提供数据。后文我们称AudioTrack提供了数据。
    b. 消费者是AudioFlinger中的与output相关联的playbackThread中的track。后文我们称Ctrack消费了数据。
    c. AudioTrack( APP应用端)通过 AudioTrackClientProxy向共享 buffer 写入数据, Ctrack(server 端)通过 AudioTrackServerProxy从共享内存中 读出数据。这样 client、server 通过 proxy 对共享内存 形成了生产者、消费者模型。

  4. 往AudioTrack中写数据支持2种不同的模式,MODE_STREAM 和 MODE_STATIC。这两种模式在共享内存方式上有所不同。
    a. MODE_STATIC模式:一次性提前提供数据。APP创建共享内存, APP一次性填充数据。Ctrack等数据构造好,取出数据就可以直接使用了,不存在同步问题。对应的Ctrack工作是:获得含有数据的obtainBuffer(APP一次性提交共享内存的数据有可能很多,Ctrack需要进行多次播放)。完成后释放buffer。
    b. MODE_STREAM模式:边播放边提供。由playbackThread创建共享内存。APP使用obtainBuffer获得空buffer, 填充数据后使用releaseBuffer释放buffer。Ctrack使用obtainBuffer获取填充了数据的buffer,Ctrack一样需要进行多次播放。只不过这里使用的是 环形缓冲区机制来,不断传递数据。完成后releaseBuffer释放buffer。
    c. MODE_STREAM模式 会使用到环形缓存区来同步数据,一个生产数据,一个消费数据,这个时候使用环形缓冲区是最可靠的。实际上就是 ClientProxy::obtainBuffer、ClientProxy::releaseBuffer、ServerProxy::obtainBuffer、ServerProxy::releaseBuffer之间的协作。

    Client 端:
    AudioTrackClientProxy:: obtainBuffer()从 audio buffer 获取连续的空buffer;
    AudioTrackClientProxy:: releaseBuffer ()将填充了数据的 buffer 放回 audio buffer。

    Server 端:
    AudioTrackServerProxy:: obtainBuffer()从 audio buffer 获取连续的填充了数据的 buffer;
    AudioTrackServerProxy:: releaseBuffer ()将使用完的空buffer 放回 audio buffer

  5. AudioTrackThread实现两个核心功能:
    a. AudioTrack与AudioFlinger之间 数据传输,AudioFlinger启动了一个线程专门用于接收客户端的 音频数据,同时客户端也需要一个线程来“不断”的传送音频数据

    b. 用于报告数据的传输状态,AudioTrack中保存了一个callback_t类型的回调函数(即全局变量mCbf)用于事件发生时进行回传。

  6. 混音
    多个应用程序播放,每个APP端都会创建AudioTrack,每个AudioTrack都会 通过共享内存 和 播放线程的Track 传递数据,每个应用发送的音频数据 格式可能都不相同,而声卡仅支持固定的几种格式,除非发送的格式就是 声卡本身支持的格式,否则这里都需要进行重采样/混音(playbackthread中使用mAudioMixer的一些操作把硬件不支持的音频格式转化为硬件支持的音频格式,这个过程叫做重采样)。
    AudioPolicyService创建之初就开始创建对应output的线程(创建MixerThread,是playbackthread的子线程)。MixerThread主要做3件事,准备混音,处理数据(比如重采样)、混音,输出声音到声卡上。

四、录音流程

  1. 录音流程
    a. 创建AudioRecord
    b. AudioRecord->start启动对应的线程开始录音
    c. AudioRecord->read 循环从底层读取音频数据, 将音频数据写入指定文件中
    d. AudioRecord->stop停止对应线程

  2. 初始化AudioRecord
    第一步,基本上AudioRecord同AudioTrack一样,在创建、设置AudioRecord, 指定了声音来源inputSource( 比如: AUDIO_SOURCE_MIC),还指定了采样率、通道数、格式等参数。同时会创建一个AudioRecordThread。
    第二步,AudioPolicyManager通过设置的参数属性,获取到对应的Input。Input对应着一组属性相同的device,根据device找到profile(audio_policy.conf产生的),根据profile找到module,即对应一个声卡,然后加载对应声卡的HAL文件。调用AudioFlinger->openInput创建RecordThread录音线程,绑定对应的input索引和声卡,并将它加入到mRecordThreads中。
    第三步,调用AudioFlinger->openRecord。即通过 RecordThread 会创建recordTrack,类似于playbackThread创建自己的Track。APP的AudioRecord 与 RecordThread内部的RecordTrack 通过共享内存传递数据。

  3. 开始录音
    mAudioRecord->start的实现(mAudioRecord是audioFlinger->openRecord返回得到的 RecordHandle 类型,RecordHandle使用recordTrack初始化),实际上 调用的是AudioFlinger::RecordThread::RecordTrack::start方法。 在这个start方法中调用到了recordThread的start方法。
    这里会唤醒线程,线程的主要内容是从HAL中得到数据, 再通过内部的RecordTrack把数据传给APP的AudioRecord。

  4. AudioRecord的read方法
    这里也是使用obtainBuffer和releaseBuffer来操作共享内存,从audioBuffer中读取数据到Buffer中。

  5. 在原生代码中,APP的一个AudioRecord会导致创建一个RecordThread,在一个device上有可能存在多个RecordThread,任意时刻只能有一个RecordThread在运行(在AudioPolicyManager这一层做了限制,限制线程的数量只能有一个),所以只能有一个APP在录音,不能多个APP同时录音。

  6. 原生AudioRecord接口是完全不支持多channel录音数据的采集的,硬件上、驱动上设置双声道; 但是系统接口只接了一个MIC,所以驱动程序录音时得到的双声道数据中,其中一个声道数据恒为0。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

白话Android音频系统原理(上)(标贝科技) 的相关文章

  • Nexus One / Android“CPU 可能被固定”错误

    我正在使用 NDK 修订版 4 和 OpenGL ES 2 0 为 Nexus One 编写一款图形密集型游戏 我们真的在这里推动硬件 并且在大多数情况下它运行良好 除了偶尔我会因以下日志消息而严重崩溃 W SharedBufferStac
  • Android spinner 将多列(连接)Sqlite 数据库加载到表中

    我正在学习如何创建一个从 SQLite 加载下拉列表的微调器 我有一个由旋转器和表格组成的用户界面 如果用户单击微调器 表的内容将根据微调器上选定的 ID 根据数据库加载 如果未选择名称 它将加载表中的所有内容 但是我找不到如何根据微调器上
  • 如何创建轮播ViewPager?

    我想做的只是 Android 中的水平轮播 如果我有 3 个屏幕 A B 和 C 那么我希望我的 ViewPager 允许我像这样移动 A B B C C A GTalk for Android 的对话可以这样切换 三星的主屏幕和应用程序屏
  • 需要在状态改变时关闭MediaRecorder播放的声音

    我已尝试在下面的链接中找到的更改 但没有效果 如何在状态改变时关闭 MediaRecorder 播放的声音 https stackoverflow com questions 6804205 how to shut off the soun
  • 即使具有用户权限,也无法在外部存储上保存文件 [Android]

    我正在 Android 上开发一个用于图像处理的应用程序 但我一直在编写图像保存代码 这是我使用的方法 private void saveImageToExternalStorage Bitmap finalBitmap String ro
  • 在 Android Studio 中调试服务的正确方法?

    在 Android Studio 中调试服务有正确的方法吗 或者 Android Studio 无法做到这一点 当我尝试调试时 它只会断开套接字 与目标虚拟机断开连接 地址 localhost 8600 传输 socket 希望你能在这里帮
  • 更新到最新版本(3.1)后缺少 google-play-services_lib.jar

    我在我的应用程序中使用 Google Play 服务已经有一段时间了 没有出现任何问题 我正在使用 Eclipse 我已在 I O 后将播放服务 以及 ADT 和 SDK 更新到最新版本 但现在我无法构建我的应用程序 我已将 google
  • 使用 OpenWeatherMap API 密钥

    我得到异常 http api openweathermap org data 2 5 weather q 悉尼 http api openweathermap org data 2 5 weather q Sydney 有人可以帮助如何使用
  • onPictureTaken 之后旋转 JPEG 的字节数组

    有没有办法旋转字节数组而不将其解码为位图 目前在 jpeg PictureCallback 中我只是将字节数组直接写入文件 但图片是旋转的 我想旋转它们而不解码为位图 希望这能节省我的记忆 BitmapFactory Options o n
  • 将对象传递给活动

    我可以在第一个活动中初始化对象并在所有活动中初始化对象吗 public class Calc int x int y public Calc int x int y this x x this y y public int sum retu
  • 如何在没有片段的情况下使用导航抽屉?

    我正在尝试遵循这个tutorial https developer android com training implementing navigation nav drawer html关于如何创建导航抽屉 但我不想在用户从抽屉列表中选择
  • ListPreferences 的异常

    这是我的第一个 Android 应用程序 在尝试使用 ListPreference 时遇到了异常 应用程序与首选项一起加载 但是当我触摸 ListPreference 条目时 应用程序 意外停止 设置 java public class S
  • 致命异常:OkHttp 调度程序

    我在 Android 应用程序中使用 OkHttp 库向天气 API 发出 Web 请求 我已经实现了我的代码 但在执行请求时遇到了致命异常 我也已经在我的清单中添加了互联网权限 MainActivity java private Curr
  • 如何在flutter app android中添加Startapp广告?

    我想用其他广告更改 AdMob 广告 一些个人问题 如何在flutter app android中添加Startapp广告 有什么方法可以将启动广告添加到我的 flutter 应用程序 android 中 StartApp 现已更名为 St
  • 如何使用Android Studio录制Android屏幕?

    我已经在 Mac 上的 Applications 文件夹中安装了 Android Studio 我认为 sdk 位于包内容中 但现在我想使用 AShot jar 录制屏幕以获取应用程序的演示视频 我必须放弃SDK的路径 当我给出我认为正确的
  • 使用 Ant 构建 Android 库项目

    我在使用 ant 构建 Android 库项目时遇到问题 当我尝试跑步时ant release 它说Target release does not exist in the project MyProject 然后我假设库可能无法通过发布
  • 我可以像 justify 这样设置 Textview 的属性吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我想设置 TextView 属性 如右对齐 左对齐 对齐 不 你不能设置像重力这样的属性 但您仍然可以通过采用 web 视图而不是文本视图
  • Android Google 地图:在 2 个位置设置边界并以其中一个位置为中心

    我知道如何在两个位置设置边界 以便两个位置都以最大缩放显示 使用LatLngBounds 我知道如何以某个位置为中心 请问我该如何同时做这两件事 在 2 个位置设置边界AND将地图置于 2 个位置之一的中心 googleMap setCen
  • 片段已添加 IllegalStateException

    我在容器 Activity 上使用此方法来显示 BFrag public void showBFrag Start a new FragmentTransaction FragmentTransaction fragmentTransact
  • Android NDK - 仅用 C/C++ 编写

    有没有一种可能的方法可以使用 C C 编写整个 NDK 应用程序 而无需像 hello jni 示例项目 HelloJni java 中那样的 Java 入门 类 以某种方式创建一个 HelloJni c 来执行相同的操作 从 Androi

随机推荐

  • Python画图常用的颜色及标识

    matplotlib中color可用的颜色 xff1a aliceblue F0F8FF antiquewhite FAEBD7 aqua 00FFFF aquamarine 7FFFD4 azure F0FFFF beige F5F5DC
  • .npy文件打开方式

    在用evo评测slam算法定位精度时 xff0c 使用 save results a zip 生成的结果中包含的 npy文件 xff0c 如果要查看里面详细的数值 xff0c 则可通过以下方式打开文件并查看 1 xff09 找到要查看的 n
  • IMU误差模型简介及VINS使用说明

    1 IMU误差来源 2 IMU噪声模型 Noise and Bias kalibr中的imu noise model 参考 xff1a https github com ethz asl kalibr wiki IMU Noise Mode
  • 泰勒公式(泰勒展开式)通俗介绍+本质详解

    比较通俗地讲解一下泰勒公式是什么 泰勒公式 xff0c 也称泰勒展开式 是用一个函数在某点的信息 xff0c 描述其附近取值的公式 如果函数足够平滑 xff0c 在已知函数在某一点的各阶导数值的情况下 xff0c 泰勒公式可以利用这些导数值
  • Leetcode ->94 二叉树的中序遍历

    题目 算法思路及代码实现 span class token macro property span class token directive hash span span class token directive keyword inc
  • Unix、UTC、GPS时间戳及转换

    UTC时间 UTC时间的英文全称 xff1a Universal Time Coordinated xff0c 中文名称 xff1a 协调世界时 俗的理解为 xff0c 这个时间是全世界通用的 xff0c 即全世界都公用的一个时间 可以认为
  • 自动驾驶中地图匹配定位技术总结

    引言 汽车定位是让自动驾驶汽车知道自身确切位置的技术 xff0c 在自动驾驶系统中担负着相当重要的职责 汽车定位涉及多种传感器类型和相关技术 xff0c 主要可分为卫星定位 惯性导航定位 地图匹配定位以及多传感器融合定位几大类 其中地图匹配
  • Firefox 常用扩展插件及脚本推荐 打造你不一样的Firefox

    前言 xff1a 进入2011年以来 xff0c 整个浏览器领域的竞争越发激烈 xff0c 随着IE9正式版的发布 Chrome Opera等浏览器厂商也相继更新 发布了新的版本 经过多次跳票之后 xff0c FireFox4 0正式版终于
  • mysql 锁(标贝科技)

    标贝科技 https ai data baker com source 61 qwer12 填写邀请码fwwqgs xff0c 每日免费调用量还可以翻倍 mysql 锁 锁类型 类型 表级锁 xff1a 开销小 xff0c 加锁快 xff1
  • STM32G4学习笔记之DAC+FMAC

    1 高性能DAC G4系列一共有四个DAC 前两个为低速采集DAC xff08 1MHz xff09 xff0c 后两个为高速 可达15MHz DAC1 DAC2的采样速率最大为1MHz xff0c DAC3 DAC4的采样速率最大为15M
  • 逆变电路之单极性调制与双极性调制

    就调制脉冲的极性而言 单相全桥逆变器pwm调制技术主要分为单极性调制与双极性调制 下面就其调制原理分别做简单的介绍 1 单极性调制 单极性调制的原理如图所示 调制信号ur为正弦波 载波uc在ur的正半周为正极性的三角波 在ur的负半周 为负
  • OTL电路与OCL功放电路的区别

    一 OTL电路 OTL xff08 Outputtransformerless xff09 电路是一种没有输出变压器的功率放大电路 过去大功率的功率放大器多采用变压器耦合方式 xff0c 以解决阻抗变换问题 xff0c 使电路得到最佳负载值
  • 运放失真罪魁祸首之压摆率和增益带宽积

    1 什么叫压摆率 xff1f 做1pps驱动电路 1 xff0c 1PPS xff1a 秒脉冲英文全称 xff1a Pulse Per Second 1pps概念 要求上升沿 5ns xff0c FPGA输出的信号用运放跟随增强驱动后 xf
  • 电源反馈电路设计经验(PC817光耦隔离+TL431)

    身边的很多小伙伴好像都对电源的反馈电路的计算以及原理不太了解 xff0c 这里给大家系统的讲解一下 xff1a 废话不都说 xff0c 咱们直接上干货 在分析电路前需要注意的关键点 1 光耦的输入端 xff08 二极管端 xff09 的电流
  • 一分钟带你了解常用的V-I、I-V转换电路

    下面我将给大家介绍6种常用的V I I V转换电路 1 0 xff0d 5V 0 xff0d 10mA的V I变换电路 图1是由运放和阻容等元件组成的V I变换电路 xff0c 能将0 5V的直流电压信号线性地转换成0 xff0d 10mA
  • 奈奎斯特采样定理-为什么采样率需要时被测信号最高频率的两倍

    奈奎斯特采样定理 采样定理在1928年由美国电信工程师H 奈奎斯特首先提出来的 xff0c 因此称为奈奎斯特采样定理 1933年由苏联工程师科捷利尼科夫首次用公式严格地表述这一定理 xff0c 因此在苏联文献中称为科捷利尼科夫采样定理 19
  • 傅里叶变换简单解析

    这里有两个频率 一个是信号本身的频率 位1 3sec 一个下面矢量绕圆的频率 为1 0 79sec 下面这个频率是我们可以改变的 这就是所谓的窗 即我们选取多长的一段信号用来进行傅里叶变换分析 当截取的窗的周期等于信号周期1 3sec时 就
  • 组合逻辑与时序逻辑

    我前段时间对这两个逻辑的概念有点混淆 xff0c 所以就百度查看了一下 xff0c 排名第一的结果就出现了这样的解释 xff1a 数字电路根据逻辑功能的不同特点 xff0c 可以分成两大类 xff0c 一类叫组合逻辑电路 xff08 简称组
  • AD2020如何安装库

    首先快捷键op打开下面界面 然后找到库的位置安装就可以了
  • 白话Android音频系统原理(上)(标贝科技)

    白话Android音频系统原理 xff08 上 xff09 一 基本原理二 初始化准备工作三 播放流程四 录音流程 标贝科技 https ai data baker com source 61 qwer12 填写邀请码fwwqgs xff0