最近一直在做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 : 表示改变的原因

参考文档:

 
 

网易云信(NeteaseYunXin)是集网易18年IM以及音视频技术打造的PaaS服务产品,来自网易核心技术架构的通信与视频云服务,稳定易用且功能全面,致力于提供全球领先的技术能力和场景化解决方案。开发者通过集成客户端SDK和云端OPEN API,即可快速实现包含IM、音视频通话、直播、点播、互动白板、短信等功能。

使用Core Audio实现VoIP通用音频模块的更多相关文章

  1. Core Audio(二)

    用户模式音频组件 在windows vista中,core audio apis充当用户模式音频子系统的基础,core audio apis作为用户模式系统组件的一个thin layer,它用来将用户 ...

  2. Core Audio(一)

    Core Audio APIs core audio apis是vista之后引入的,不使用与之前的windows版本:core audio apis提供访问endpoint devices,比如耳机 ...

  3. Core Audio 在Vista/Win7上实现

    应用范围:Vista / win7, 不支持XP 1. 关于Windows Core Auido APIs 在Windowss Vista及Windows 7操作系统下,微软为应用程序提供了一套新的音 ...

  4. Abp通用配置模块的设计

    引言 约定优于配置,配置趋于灵活 约定优于配置(convention over configuration),也称作按约定编程,是一种软件设计范式,旨在减少软件开发人员需做决定的数量,获得简单的好处, ...

  5. 使用 Windows Core Audio APs 进行 Loopback Recording 并生成 WAV 文件

    参考文档 COM Coding Practices Audio File Format Specifications Core Audio APIs Loopback Recording #inclu ...

  6. HTML5的Audio标签打造WEB音频播放器

    目前,WEB页面上没有标准的方式来播放音频文件,大多数的音频文件是使用插件来播放,而众多浏览器都使用了不同的插件.而HTML5的到来,给我们提供了一个标准的方式来播放WEB中的音频文件,用户不再为浏览 ...

  7. WebRTC源码分析:音频模块结构分析

    一.概要介绍WebRTC的音频处理流程,见下图: webRTC将音频会话抽象为一个通道Channel,譬如A与B进行音频通话,则A需要建立一个Channel与B进行音频数据传输.上图中有三个Chann ...

  8. ylbtech-Model-Account(通用账户模块设计)

    ylbtech-DatabaseDesgin:ylbtech-Model-Account(通用账户模块设计) ylbtech-Model-Account(通用账户模块设计) 1.A,数据库关系图(Da ...

  9. javaCV开发详解之7:让音频转换更加简单,实现通用音频编码格式转换、重采样等音频参数的转换功能(以pcm16le编码的wav转mp3为例)

    javaCV系列文章: javacv开发详解之1:调用本机摄像头视频 javaCV开发详解之2:推流器实现,推本地摄像头视频到流媒体服务器以及摄像头录制视频功能实现(基于javaCV-FFMPEG.j ...

随机推荐

  1. lzugis——Arcgis Server for JavaScript API在自己的定义InfoWindow

    你看到这个标题嫌烦.因为我最近一直与研究问题,相关文章使这些也可以只,同时要读我文章的朋友.我的文章能够给你带来帮助. 在相关的内部的前两篇文章,达到InfoWindow经div实现的东西,成Info ...

  2. JavaScript取出字符串和尾随空格

    书写JavaScript性能trim()之前和之后删除空字符串格,的功能的应用String物replace()方法去除空字符串端到端格.trim()例如,对于功能键下面的代码: <script ...

  3. Logistic Regression 的简单推导

    Logistic Regression 是一种 Generalized Linear Model(GLM),也即广义线性模型. 1. LR 的基本假设 LR 模型假设观测值 y 成立的对数几率(log ...

  4. A Byte of Python (for Python 3.0) 下载

    在线阅读:http://www.swaroopch.org/notes/Python_en:Table_of_Contents 英文版 下载地址1:http://files.swaroopch.com ...

  5. 运行时动态伪造vsprintf的va_list

    运行时动态伪造vsprintf的va_list #include <stdio.h> int main() { char* m = (char*) malloc(sizeof(int)*2 ...

  6. scipy —— 丰富的子包(io、cluster)

    io,顾名思义,input/output,输入输出接口: 1. io Input and output (scipy.io) - SciPy v0.18.1 Reference Guide wavfi ...

  7. react学习(7)——路由配置

    1. 新建文件routes.js,分别定义页面的路由信息和其他信息 const Routes = { : { title: "home", pageName: "home ...

  8. ubuntu 关闭 phpmyadmin

    apache 有很多管理服务器的命令.apt-get installl phpmyadmin后突然想暂时关闭phpmyadmin,只需要a2disconf phpmyadmin即可.需要启用的时候再输 ...

  9. Genymotion常见问题解决方案

    最近更新了Genymotion之后, 出了一大堆问题, 花了差不多两天的时间, 把一系列问题基本遇到遍了, 终于修成正果, 分享出来希望可以让大家少走一点弯路. 无法启动Genymotion;关键字: ...

  10. C#原子性运算 interlocked.compareExchanged

    缘起: 假设有一个类myClass, myclass里有一个count属性. 在多线程的环境下 每个线程中 直接使用count++,  如果两个线程并行执行时, 两个线程中的一个的结果会被覆掉, 非线 ...