【实战分享】使用Core Audio实现VoIP通用音频模块
最近一直在做iOS音频技术相关的项目,由于单项直播SDK,互动直播SDK(iOS/Mac),短视频SDK,都会用到音频技术,因此在这里收集三个SDK的音频技术需求,开发一个通用的音频模块用于三个SDK,同时支持iOS和Mac。
需求实现
主要包括音频采集,音频格式转换,音频多路混音(本地文件和网络文件),写WAV/AAC音频文件,通话录制,音频文件播放,耳返,自定义音频输入,音视频设备管理等功能。
本文大部分图片和技术概念阐述均来自Apple官网。
概念介绍
Core Audio 是iOS和 Mac 的关于数字音频处理的基础,它提供应用程序用来处理音频的一组软件框架,所有关于iOS音频开发的接口都是由Core Audio来提供或者经过它提供的接口来进行封装的,按照官方的说法是集播放、音频处理、录制为一体的专业技术,通过它我们的程序可以同时录制,播放一个或者多个音频流,自动适应耳机,蓝牙耳机等硬件,响应各种电话中断,静音,震动等。
Low-Level
I/O Kit:与硬件驱动交互
Audio HAL:音频硬件抽象层,使API调用与实际硬件相分离,保持独立
Core MIDI:为MIDI流和设备提供软件抽象工作层
Host Time Services:访问电脑硬件时钟
Mid-Level
Audio Convert Services 负责音频数据格式的转换
Audio File Services 负责音频数据的读写
Audio Unit Services 和 Audio Processing Graph
Services 支持均衡器和混音器等数字信号处理的插件
Audio File Scream Services 负责流解析
Core Audio Clock Services 负责音频时钟同步
High-Level
Audio Queue Services 提供录制、播放、暂停、循环、和同步音频,它自动采用必要的编解码器处理压缩的音频格式
AVAudioPlayer 是专为iOS平台提供的基于Objective-C接口的音频播放类,可以支持iOS所支持的所有音频的播放
Extended Audio File Services 由Audio File与Audio Converter组合而成,提供压缩及无压缩音频文件的读写能力
OpenAL 是CoreAudio对OpenAL标准的实现,可以播放3D混音效果
OS X 和 iOS 的核心音频架构
Audio Unit
iOS提供了混音、均衡、格式转换、实时IO录制、回放、离线渲染、语音对讲(VoIP)等音频处理插件,它们都属于不同AudioUnit,支持动态载入和使用。AudioUnit可以单独创建使用,但更多的是被组合使用在Audio Processing Graph容器中以达到多样的处理需要。
一个I/O Unit包含两个实体对象,两个实体对象(Element 0、Element 1)相互独立,根据需求可通过kAudioOutputUnitProperty_EnableIO属性去开关它们。Element 1与硬件输入连接,并且Element 1的输入域(input scope)对你不可见,你只能读取它的输出域的数据及设置其输出域的音频格式;Element 0与硬件输出连接,并且Element 0的输出域(ouput scope)对你不可见,你只能写入它的输入域的数据及设置其输入域的音频格式。
Audio Session
AVAudioSession构建了一个音频使用生命周期的上下文。当前状态是否可以录音、对其他App有怎样的影响、是否响应系统的静音键、如何感知来电话了等都可以通过它来实现。
Audio Processing Graphs
AUGraph可以用来构建和管理一个音频单元处理链。能够利用多个音频单元的功能和多个渲染回调函数,允许您创建几乎任何你可以想象的音频处理的解决方案。同时它也是线程安全的。
Audio Flows Through a Graph Using “Pull”
在一个音频处理图,当需要更多的音频数据时,使用者调用提供者。有源源不断的音频数据流的请求,这个控制流的方向和音频流方向相反。
具体实现
一. 音频采集
iOS采集:
kAudioUnitSubType_RemoteIO
kAudioUnitSubType_VoiceProcessingIO
Mac采集:
kAudioUnitSubType_VoiceProcessingIO
一个I/O Unit包含两个实体对象,两个实体对象(Element 0、Element 1)相互独立。Element 1与硬件输入(麦克风或者听筒)连接,并且Element 1的输入域(input scope)对你不可见,你只能读取它的输出域的数据及设置其输出域的音频格式;Element 0与硬件输出(扬声器或者听筒)连接,并且Element 0的输出域(ouput scope)对你不可见,你只能写入它的输入域的数据及设置其输入域的音频格式。
操作步骤:
创建AudioUnit。
开启麦克风或者听筒的输入开关;开启扬声器或者听筒的输出开关。
设置输入和输出的采集回调和播放回调。
设置输入和输出的音频格式。
初始化AudioUnit。
开启AudioUnit。
Mac采集:
kAudioUnitSubType_HALOutput
Mac的音频采集使用的是kAudioUnitSubType_HALOutput,音频硬件抽象层HAL。因此它使用的是2个I/O Uint串联,前一个I/O Uint的输出作为后一个I/O Uint的输入。
操作步骤:
创建2个AudioUnit。
开启第一个I/O Uint的麦克风或者听筒的输入开关,关闭第一个I/O Uint的扬声器或者听筒的输出开关;开启第二个I/O Uint的扬声器或者听筒的输出开关,关闭第二个I/O Uint的麦克风或者听筒的输入开关。
将第一个I/O Unit设为Mac的kAudioHardwarePropertyDefaultInputDevice,第二个I/O Unit设为Mac的kAudioHardwarePropertyDefaultOutputDevice,
设置第二个I/O Uint的输入和第一个I/O Uint的输出的采集回调和播放回调。
设置第二个I/O Uint的输入和第一个I/O Uint的输出的音频格式。
初始化2个AudioUnit。
开启2个AudioUnit。
二. 音频架构
从图中可以看出,我们使用了一个I/O Unit作为最核心的部件,用于驱动整个流程,同时使用三个Audio Processing Graphs作为混音器。三个Audio Processing Graphs分别代表播放混音器,发送混音器,录制混音器。每个混音器有三个Unit最为其部件,音频混音Mixing(kAudioUnitSubType_MultiChannelMixer),音频格式转换(kAudioUnitSubType_AUConverter),音频通用输出(kAudioUnitSubType_GenericOutput)。同时支持多路输入,一路输出。
1. 播放混音器支持来自服务器的多路音频流和一路本地伴音以及一路耳返音频,每一路输入都会接一个音频格式转换,同时设置一个输入回调,用于音频数据的主动拉取。并将混音器的输出作为Audio Unit的输入。
2. 发送混音器支持一路Audio Unit的采集和本地多路音频伴音的输入,每一路输入都会接一个音频格式转换,同时设置一个输入回调,用于音频数据的主动拉取。并将混音器的输出作为音频编码和发送的输入。
3. 录制混音器支持Audio Unit的一路采集和Audio Unit的一路播放,将整个通话过程涉及到的音频数据都合成一路。每一路输入都会接一个音频格式转换,同时设置一个输入回调,用于音频数据的主动拉取。并将混音器的输出作为通话录制的输入,并写WAV/AAC文件。
4. Audio Unit的采集回调驱动音频编码,从而驱动整个发送混音器;Audio Unit的采集回调驱动通话录制,从而驱动整个录制混音器;Audio Unit的播放回调驱动播放,从而驱动整个播放混音器。
5. 目前最新的音频架构,我们使用了两个I/O Unit作为最核心的部件,用于驱动整个流程。同时统一了iOS和Mac 2个版本,也解决了采集和播放同一个线程的问题,为我们的音频前处理提供了安全的线程保障。
三. AVAudioSeeion管理
AVAudioSession 的主要功能包括以下几点功能:
向系统说明你的app使用音频的模式(比如是播放还是录音,是否支持蓝牙播放,是否支持后台播放)
为你的app选择音频的输入输出设备(比如输入用的麦克风,输出是耳机、手机功放或者airplay)
协助管理多个音源需要播放时的行为(例如同时使用多个音乐播放app,或者突然有电话接入)
如果需要音频支持后台运行,需要按下图配置:
在需要完成上述功能点的前提下,我们需要监听中断响应,外设改变,媒体服务器终止,媒体服务器重新启动,前后台切换的通知。在不同的通知下,做出相应的调整。
系统中断响应:
AVAudioSession提供了多种Notifications来进行此类状况的通知。其中将来电话、闹铃响等都归结为一般性的中断,用AVAudioSessionInterruptionNotification来通知。其回调回来的userInfo主要包含两个键:AVAudioSessionInterruptionTypeKey: 取值为AVAudioSessionInterruptionTypeBegan表示中断开始,我们应该暂停播放和采集,取值为AVAudioSessionInterruptionTypeEnded表示中断结束,我们可以继续播放和采集。
AVAudioSessionInterruptionOptionKey: 当前只有一种值AVAudioSessionInterruptionOptionShouldResume表示此时也应该恢复继续播放和采集。
外设改变:
在NSNotificationCenter中对AVAudioSessionRouteChangeNotification进行注册。在其userInfo中有键:AVAudioSessionRouteChangeReasonKey : 表示改变的原因
参考文档:
https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/AudioUnitHostingGuide_iOS/AudioUnitHostingFundamentals/AudioUnitHostingFundamentals.html#//apple_ref/doc/uid/TP40009492-CH3-SW11
https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/CoreAudioOverview/Introduction/Introduction.html#//apple_ref/doc/uid/TP40003577-CH1-SW1
点击“阅读原文”,技术干货
【实战分享】使用Core Audio实现VoIP通用音频模块相关推荐
- 使用Core Audio实现VoIP通用音频模块
最近一直在做iOS音频技术相关的项目,由于单项直播SDK,互动直播SDK(iOS/Mac),短视频SDK,都会用到音频技术,因此在这里收集三个SDK的音频技术需求,开发一个通用的音频模块用于三个SDK ...
- Core Audio音频基础概述
Core Audio Core Audio提供了数字音频服务为iOS与OS X, 它提供了一系列框架去处理音频. Core Audio中包含我们最常用的Audio Toolbox与Audio Unit ...
- .NET Core中的验证组件FluentValidation的实战分享
今天有人问我能不能出一篇FluentValidation的教程,刚好今天在实现我们的.NET Core实战项目之CMS的修改密码部分的功能中有用到FluentValidation,所以就以修改用户密码 ...
- Windows下Core Audio APIs的使用简介
文|网易云信资深PC端开发工程师 Windows Vista 之后的系统中,音频系统相比之前的系统有很大的变化,产生了一套新的底层 API 即 Core Audio APIs . 该低层 API 为高 ...
- 开发shell脚本检查Nginx实战分享
开发shell脚本检查Nginx实战分享 点这里查看视频讲解 一.本脚本实现功能: 1.自动检查Nginx下面的代理节点是否正常 2.通过页面显示状态,有问题的节点给出页面报警及声音报警. 3.增加新 ...
- Windows Core Audio APIs(一)介绍
文章目录 Windows Core Audio APIs(一)介绍 Core Audio 架构介绍 Core Audio 架构图 音频高级API Core Audio APIs 共享模式和独占模式 音 ...
- 让你在 API 设计中少踩坑的实战分享
本文来自作者 奔跑吧架构师 在 GitChat 上分享 「让你在 API 设计中少踩坑的实战分享」,「阅读原文」查看交流实录. 「文末高能」 编辑 | 哈比 在项目开发中,实际的编码只占用了整个项目不 ...
- Weex实战分享|Weex在极客时间APP中的实践
本文是根据 WeexConf2018 中议题<Weex在极客时间APP中的实践>内容文档整理而成.主要分享极客时间在深度使用Weex过程中的一些经验和体会. 孙涛 极客邦前端负责人 大家 ...
- Web项目实战分享——小米官网
Web项目实战分享--小米官网 大约每个学习过前端的学习者来说,都接触过web项目的实战,业内最真实的的,应该是自己独立编写一个网页项目,当然如果你是大佬的话,这只是小菜一碟,而如果你是小白或正在学习 ...
最新文章
- RSA体系 c++/java相互进行加签验签--转
- Apache Kafka:下一代分布式消息系统
- Javascript 动态修改select方法大全【转】
- 解决Yii2邮件发送问题(结果返回成功,但接收不到邮件)
- block引用c语言数组,Block在ARC和MRC下的使用分析
- C#编写串口监控软件的详细教程
- Linux kali 安装 qq Tim
- sql语句合集大全(个人总结)
- 二维数组与指针(详解)
- 怎样更改itunes备份位置_iTunes备份路径在哪?iTunes备份路径如何修改
- 阿里云商标优选怎么样上传商标挂售?
- 天猫总裁靖捷详解新零售:传统商圈平均增长超50%
- linux 性能分析工具——perf
- 四足机器人发展史及机器人盘点
- LDA模型中需要输入的数据格式
- IPO案例:未办理境外投资手续的影响及解决措施
- ESP8266连接腾讯云物联网平台
- 【题解】【PTA-Python题库】第4章-13 求误差小于输入值的e的近似值 (20 分)
- C语言-分支结构if-else
- v8,spidermonkey,chakra,spidermonkey四大主流JS引擎安装教程
热门文章
- select下拉列表选中后,跳转新链接
- vuejs mvvm图解
- Eclipse导入工程后显示小红叉叉怎么解决?
- ASP.Net开发新手常见问题备忘录
- 计算机网络 ip协议是,计算机网络知识:TCP/IP协议
- 对话框中WaitForSingleObject等待线程退出导致程序阻塞的原因及解决
- 2011计算机2级,全国计算机等级考试四合一过关训练:2级C语言程序设计(2011版)...
- python顺序结构实验_Python程序设计实验报告二:顺序结构程序设计(验证性实验)...
- java的jps命令怎么使用_jps命令的使用方法
- rust(67)-rust元组与空元组