iOS 音频-AVAudioSession

AVAudioSession 概述

最近在做 webrtc 采集与播放音频,使用AVAudioSession进行播放与录音功能
苹果的官方图:


iOS 音频-AVAudioSession_第1张图片
1320629-cfd1eacecf2cacae.png

可以看到AVAudioSession就是用来管理多个APP对音频硬件设备(麦克风,扬声器)的资源使用。
举例一下AVAudioSession可以做这些事情
设置自己的APP是否和其他APP音频同时存在,还是中断其他APP声音
在手机调到静音模式下,自己的APP音频是否可以播放出声音
电话或者其他APP中断自己APP的音频的事件处理
指定音频输入和输出的设备(比如是听筒输出声音,还是扬声器输出声音)
是否支持录音,录音同时是否支持音频播放

[AVAudioSession sharedInstance] 设置方法

通过主场景、模式、细节选项的设置可以使app在音频方面更加人性化。
每次的麦克风转换都是硬件调用,会有一定几毫秒延时,所以录影录或音后设定延时等待释放后才转换,不然换了也没用


iOS 音频-AVAudioSession_第2张图片
屏幕快照 2019-05-18 下午1.59.22.png

[[AVAudioSession sharedInstance]setActive:YES error:nil];//马上设置
[[AVAudioSession sharedInstance]setActive:NO error:nil];//交出音频会话
配置音频设置,如采样率,I / O缓冲区持续时间和通道数 处理音频输出更改 。
通过overrideOutputAudioPort:error:方法设置音频会话的overrideOutputAudioPort属性

音频会话场景分类设置:

说明:以下分类并不是一个应用只能使用一个分类,可以根据场景来切换不同的分类。

主场景分类;

Category iOS下目前有七种,每种Category都对应是否支持下面四种能力
Interrupts non-mixable apps audio:是否打断不支持混音播放的APP
Silenced by the Silent switch:是否会响应手机静音键开关
Supports audio input:是否支持音频录制
Supports audio output:是否支持音频播放

主场景Category 描述 可与其他app共享混合播放 是否可播放/录音 是否会被静音键或锁屏键静音 是否可在后台运行
AVAudioSessionCategoryAmbient 背景声音,用于以非语音为主的应用。 可混合 只支持播放 会静音 不可运行
AVAudioSessionCategorySoloAmbient 默认category 不可混合 只支持播放 会静音 不可运行
AVAudioSessionCategoryPlayback 用于播放音乐,用于以语音为主的应用 不可混合 只支持播放 不会静音 可运行(另说明)
AVAudioSessionCategoryRecord 用于录音,除了来电铃声、闹钟、日历提醒等系统声音外其他声音都不会被播放,只执行录音 不可混合 只支持录音 不会静音(锁屏仍可录制) 可运行
AVAudioSessionCategoryPlayAndRecord 用于播放和录音同时存在时,只有它允许修改默许音频播放设备:听筒还是外放,在该Category下声音的默认出口为听筒或者耳机。 默认不引起 支持播放,支持录制 不会静音 可运行
AVAudioSessionCategoryMultiRoute 多种输入输出,例如可以耳机、USB设备同时播放 不可混合 支持播放,支持录制 不会静音 可运行
AVAudioSessionCategoryAudioProcessing 硬件解码音频 不可混合 不支持播放,不支持录制 不会静音 不可运行,可请求更多时间完成处理

AVAudioSessionCategoryAmbient : 此类别适用于‘伴奏模式’应用,只支持音频播放。例如用户在使用音乐应用播放时播放的伴奏。比如玩游戏的时候还想听QQ音乐的歌,那么把游戏播放背景音就设置成这种类别。当使用该类别时,来自其他应用程序的音频会与当前的音频混合。这个 Category,音频会被静音键和锁屏键静音。并且不会打断其他应用的音频播放。这种类别基本使用所有App的背景场景。

AVAudioSessionCategorySoloAmbient : 系统默认会话Category, 只支持音频播放。默认情况下,使用该类别意味着 应用程序的音频不可混合,激活应用中的会话将打断其他应用的音频播放。如允许混合,则改用 AVAudioSessionCategoryAmbient。音频会被静音键和锁屏键静音。

AVAudioSessionCategoryPlayback :只支持音频播放。静音开关或者锁屏不会音响音频的播放。默认情况下,使用此类别意味着,应用的音频不可混合,激活音频会话将中断其它不可混合的音频会话。适用于音频是主要功能的APP,像网易云这些音乐app,锁屏后依然可以播放。

需要注意一下,选择支持在静音键切到静音状态以及锁屏键切到锁屏状态下仍然可以播放音频 Category 时,必须在应用中开启支持后台音频功能,详见 UIBackgroundModes。

AVAudioSessionCategoryRecord :录制音频的Category,只支持音频录制,不支持播放。只要该会话处于活动状态,此类别会使系统上的所有输出停止,比如微信语音的录制,就要用到这个类别。除非需要防止播放其它的声音,否则建议使用 AVAudioSessionCategoryPlayAndRecord。

用户必须授权音频录制权限(iPhone 麦克风权限)。
此类别会话会被 电话呼叫、闹钟或者其它非混音音频会话中断

AVAudioSessionCategoryPlayAndRecord : 支持音频播放和录制的Category,用于语音聊天应用,例如VoIP(互联网语音协议)、打电话 应用程序(如微信)。音频的输入和输出不需要同步进行,也可以同步进行。
静音键开启和锁屏都不会影响音频继续播放。如要在应用程序转换到后台时继续播放(锁屏情况下)在xcode中设置 UIBackgroundModes 即可。此类别适用于同时录制和播放(语音连麦),也适用于录音/播放(IM语音条)。 但不能同时播放。默认情况下,使用此类别意味着应用程序的音频不可混合。激活会话将终端任何其他音频会话也是不可混合的。要允许为此类别混音,请使用AVAudioSessionCategoryOptionMixWithOthers选项

只有它允许修改默许音频播放设备:听筒还是外放,在该Category下声音的默认出口为听筒或者耳机。
用户必须授权音频录制权限(iPhone 麦克风权限)
此类别支持Airplay的镜像版本。但是,如果AVAudioSessionModeVoiceChat模式与此类别一起使用,则AirPlay镜像将被禁用。

AVAudioSessionCategoryAudioProcessing,主要用于音频格式处理,硬件解码音频,不支持播放和录制,一般可以配合AudioUnit进行使用。例如,在执行离线音频格式转换时。此类别禁用播放(音频输出)和禁用录音(音频输入)。当您的应用处于后台时,音频处理通常不会继续。 但是,当您的应用移至后台时,您可以请求更多时间来完成处理。

AVAudioSessionCategoryMultiRoute : 用于将不同音频数据流同时路由到不同输出设备的Category。此类别支持音频播放和录制,允许多条音频流的同步输入和输出。 例如,比如USB连接外部扬声器输出音频,蓝牙耳机同时播放另一路音频这种特殊需求。 使用这个类别需要对可用音频路由的功能有更详细的了解,并与之互动。
路由更改可能会使部分或全部多路由配置失效。 使用AVAudioSessionCategoryMultiRoute类别时,必须注册以观察AVAudioSessionRouteChangeNotification通知并根据需要更新配置。 这个类别可以支持多个设备输入输出。

说明:当使用AVAudioSessionCategoryPlayback、AVAudioSessionCategoryRecord分类时,要想实现后台播放,需要在Info.plist文件里添加Required background modes的数组,在下面添加名为App plays audio or streams audio/video using AirPlay的字符串。

主场景细节options、modes设置

上面介绍了Category七种主场景,实际开发需求中有时候需要对Category进行微调整,这就需要设置两个参数Mode和Options。通常在激活会话之前设置类别和模式。但也可在会话处于活跃状态时设置类别模式,但这会导致立即更改。

建议使用setCategory:mode:options:error:method同时设置它们,而不是单独设置类别和模式属性。(根据APP 兼容版本不同,使用不同的方法)

AVAudioSession Mode

通过上面的七大类别,我们基本覆盖了常用的主场景,在每个主场景中可以通过Option进行微调。为此CoreAudio提供了七大比较常见微调后的子场景。叫做各个类别的模式。

模式 兼容的 Category 场景
AVAudioSessionModeDefault All 默认模式
AVAudioSessionModeVoiceChat AVAudioSessionCategoryPlayAndRecord 双向语音通信 VoIP
AVAudioSessionModeVideoChat AVAudioSessionCategoryPlayAndRecord 在线视频会议,视频通话
AVAudioSessionModeGameChat AVAudioSessionCategoryPlayAndRecord Game Kit的语音聊天服务,GKVoiceChat自动设置,适用于游戏App的采集和播放
AVAudioSessionModeVideoRecording AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord 录制视频
AVAudioSessionModeMoviePlayback AVAudioSessionCategoryPlayback 视频播放。如果应用正在播放电影内容,请指定此模式
AVAudioSessionModeMeasurement AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback 最小系统

下面逐一介绍下每个Mode

AVAudioSessionModeDefault,默认模式,与所有的 Category 兼容

AVAudioSessionModeVoiceChat,主要用于执行双向语音通信VoIP场景。只能是 AVAudioSessionCategoryPlayAndRecord Category下。在这个模式系统会自动配置AVAudioSessionCategoryOptionAllowBluetooth 这个选项。系统会自动选择最佳的内置麦克风组合支持语音聊天,比如插上耳机就使用耳机上的麦克风进行采集。使用此模式时,该设备的音调君合针对语音进行了优化,并且允许路线组仅缩小为适用于语音聊天的路线。如果应用程序未将其模式设置为其中一个聊天模式(语音,视频或游戏),则AVAudioSessionModeVoiceChat模式将被隐式设置。另一方面,如果应用程序先前已将其类别设置为AVAudioSessionCategoryPlayAndRecord并将其模式设置为AVAudioSessionModeVideoChat或AVAudioSessionModeGameChat,则实例化语音处理I / O音频单元不会导致模式发生更改。

AVAudioSessionModeVideoChat,主要用于视频聊天、在线视频会议类型应用,比如QQ视频、FaceTime。只能是 AVAudioSessionCategoryPlayAndRecord Category下。适在这个模式系统会自动配置 AVAudioSessionCategoryOptionAllowBluetooth 和 AVAudioSessionCategoryOptionDefaultToSpeaker 选项。系统会自动选择最佳的内置麦克风组合支持视频聊天。使用此模式时,设备的音调均衡针对语音进行了优化,并且允许音频路由组仅缩减为适合视频聊天的设置。比如插上耳机就使用耳机上的麦克风进行采集并且会设置类别的选项为"AVAudioSessionCategoryOptionAllowBluetooth" 和 "AVAudioSessionCategoryOptionDefaultToSpeaker"。

AVAudioSessionModeGameChat,该模式由Game Kit代表使用Game Kit的语音聊天服务的应用程序设置,适用于游戏App的采集和播放。使用 GKVoiceChat 对象的应用会自动设置这个模式和 AVAudioSessionCategoryPlayAndRecord Category。实际参数和AVAudioSessionModeVideoChat一致。不要直接设置此模式。 如果需要类似的行为并且未使用GKVoiceChat对象,请改为使用AVAudioSessionModeVoiceChat或AVAudioSessionModeVideoChat。

AVAudioSessionModeVideoRecording,适用于使用摄像头采集视频录制的应用。只能是 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord 这两个 Category下。在具有多个内置麦克风的设备上,使用距摄像头最近的麦克风。此模式会导致系统提供适当的音频信号处理。这个模式搭配 AVCaptureSession API 结合来用可以更好地控制音视频的输入输出路径。例如,设置 automaticallyConfiguresApplicationAudioSession 属性,系统会自动选择最佳输出路径。(设置自动配置应用音频会话属性会根据使用的设备和摄像机自动选择最佳输入路由。)

AVAudioSessionModeMeasurement,最小化系统。如果应用正在执行音频输入或输出的测试。此模式适用于需要将输入和输出信号的系统提供的信号处理量将至最低的应用程序。如果在具有多个内置麦克风的设备上录制,则使用主麦克风。只用于 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayback 这几种 Category。

AVAudioSessionModeMoviePlayback,适用于播放视频的应用。只用于 AVAudioSessionCategoryPlayback 这个Category。使用此模式时,将采用信号处理来增强某些音频路由(如内置扬声器或耳机)的电影播放。系统也会选择最佳的输入设备,比如插上耳机就使用耳机上的麦克风进行采集

AVAudioSessionModeSpokenAudio : 用于播放语音并暂停其他语音app。当想要在另一个应用播放短语音频时暂停当前音频时,用于持续说话音频的模式。在iOS 8和更低版本以及iOS 9中,如果不设置此模式,偶尔从导航和应用程序中听到的语音与音频混合在一起,或造成两种音频的混淆。 此模式通过为中断应用程序使用AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers音频会话类别选项来避免此问题。 中断应用程序的音频结束后,可以恢复中断的语音。

可以在设置Category之后再设置模式。

当然,这些模式只是CoreAduio总结的,不一定完全满足要求,对于具体的模式,在iOS10中还是可以微调的。通过接口:
(BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError
但是在iOS9及以下就只能在Category上调了,其实本质是一样的,可以认为是个API接口封装。

AVAudioSession Options

在设置完类别后,可以通过

@property(readonly) AVAudioSessionCategoryOptions categoryOptions;
属性,查看当前类别设置了哪些选项,注意这里的返回值是AVAudioSessionCategoryOptions,实际是多个options的“|”运算。默认情况下是0。
可以使用options去微调Category行为,如下表

Option Option功能说明 兼容的 Category
AVAudioSessionCategoryOptionMixWithOthers 支持和其他APP音频混合 AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionDuckOthers 系统智能调低其他APP音频音量,突出本app的音量(导航地图) AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetooth 支持蓝牙音频输入 AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionDefaultToSpeaker 设置默认输出音频到扬声器,即免提 AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionInterrupt SpokenAudioAndMixWithOthers app偶尔的使用音频播放 AVAudioSessionCategoryPlayback AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryMultiRoute
AVAudioSessionCategoryOptionAllowBluetoothA2DP 立体声蓝牙 AVAudioSessionCategoryPlayAndRecord
AVAudioSessionCategoryOptionAllowAirPlay 远程AirPlay设备 AVAudioSessionCategoryPlayAndRecord

AVAudioSessionCategoryOptionMixWithOthers :确定来自此会话的音频是否与来自其他音频应用中活动会话的音频混合。当一个app即包含audio输入又包含输出的时候,设置这个选项在激活会话时不会打断其他应用程序的音频播放 。
如果会话类别是AVAudioSessionCategoryAmbient,则此选项会自动设置。如果设置了AVAudioSessionCategoryOptionDuckOthers或AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers选项,则会自动设置此选项。如果清除此选项,激活会话会中断其他音频会话。 如果设置了此选项,则应用程序的音频会与后台应用程序中的音频(如音乐应用程序)混合在一起。
例如:AVAudioSessionCategoryPlayback实现的一个背景音,但是呢,又想和QQ音乐并存,那么可以在AVAudioSessionCategoryPlayback类别下再设置这个选项,就可以实现共存了。

AVAudioSessionCategoryOptionDuckOthers : 激活会话时降低其他程序的音频播放声音(音量降低) ,主要是体现当前音频的重要性,此时就是通过设置这个选项来对其他音乐App进行了压制。比如说开车听歌的时候,导航的声音就属于比较重要的或者是需要特别关注的就可以是用这个选项 。会影响其他应用,不用时需要deactivate audio session
设置此标志隐式设置AVAudioSessionCategoryOptionMixWithOthers标志。如果清除此选项,激活会话会中断其他音频会话。如果设置了此选项,则应用程序的音频会与后台应用程序中的音频(如音乐应用程序)混合在一起。与当前应用混合时,来自其他应用的音频会减少音量。如果您希望通过音乐或其他当前正在播放的音频听到您应用中的音频(例如,导航应用中的语音提示),请设置此选项。如果您的应用程序提供了偶尔的口语音频,例如在转弯的导航应用程序或练习应用程序中,则还应该设置AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers选项。

AVAudioSessionCategoryOptionAllowBluetooth : 蓝牙免提设备是否显示为可用输入路由。如果要支持蓝牙耳机电话,则需要设置这个选项。
需要设置此选项才能将音频输入和输出路由到配对的蓝牙免提模式(HFP)设备。 如果清除此选项,则配对的蓝牙HFP不会显示为可用的音频输入路由。
如果应用程序使用setPreferredInput:error:方法选择蓝牙HFP输入,则输出将自动更改为相应的蓝牙HFP输出。 同样,使用MPVolumeView对象的路由选择器选择蓝牙HFP输出,会自动将输入更改为相应的蓝牙HFP输入。 因此,即使仅选择了输入或输出,音频输入和输出也将始终路由至Bluetooth HFP设备。
只有音频会话类别为AVAudioSessionCategoryPlayAndRecord或AVAudioSessionCategoryRecord时,才能设置此选项。

AVAudioSessionCategoryOptionDefaultToSpeaker :在没有其他通道的时候默认选择内置扬声器而不是接收器。如果在VoIP模式下,希望默认打开免提功能,需要设置这个选项。此选项只能在使用AVAudioSessionCategoryPlayAndRecord类别时设置。 它用于修改类别的路由行为,以便在没有使用其他配件(如耳机)的情况下,音频始终会路由至扬声器而不是接收器。 例如,插入头戴式耳机将导致路由变为头戴式耳机麦克风/耳机,并且拔下头戴式耳机将导致路由更换为内置麦克风/扬声器(与内置麦克风/接收器相反) 已设置。
如果使用USB输入专用附件,音频输入将来自附件,如果没有插入耳机,则输出将路由到耳机(如果连接)或扬声器。

AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers :播放此应用的音频内容时,是否暂停来自其他应用的连续语音内容。
设置此选项还会设置AVAudioSessionCategoryOptionMixWithOthers。
如果清除此选项,音频会话中的音频会中断其他会话。
如果设置了此选项,则您的音频会与其他音频会话混合使用,但会中断(并停止)使用AVAudioSessionModeSpokenAudio音频会话模式的音频会话。只要会话处于活动状态,其他应用程序的音频就会暂停。音频会话停用后,中断的应用程序的音频恢复。
如果突然播放一段语音,例如导航应用,使用此选项。这可以避免两个口语音频应用程序混合时出现干扰问题。除非有特殊的原因,否则建议设置为AVAudioSessionCategoryOptionDuckOthers选项。当其他音频不是说音频时,避免其他音频而不是中断它。

AVAudioSessionCategoryOptionAllowBluetoothA2DP : 立体声蓝牙。
高级音频分布配置文件(A2DP)是一种立体声,仅输出配置文件,适用于较高带宽音频使用情况,如音乐播放。如果应用程序的音频会话配置为使用AVAudioSessionCategoryAmbient,AVAudioSessionCategorySoloAmbient或AVAudioSessionCategoryPlayback类别,系统将自动路由至A2DP端口。
从iOS 10.0开始,使用AVAudioSessionCategoryPlayAndRecord类别的应用程序还可以允许将输出路由到配对的蓝牙A2DP设备。要启用此行为,您需要在设置音频会话的类别时传递此类别选项。使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord类别的音频会话隐式清除此选项。
如果清除此选项,则配对的蓝牙A2DP设备不会显示为可用的音频输出路由。
如果此选项和AVAudioSessionCategoryOptionAllowBluetooth选项均已设置,则当单个设备同时支持免提配置文件(HFP)和高级音频分布配置文件(A2DP)时,免提端口将获得更高的路由优先级。

AVAudioSessionCategoryOptionAllowAirPlay : 远程AirPlay设备。
只有在音频会话类别为AVAudioSessionCategoryPlayAndRecord时,才能显式设置此选项。 对于大多数其他音频会话类别,此选项是隐式设置的。 使用AVAudioSessionCategoryMultiRoute或AVAudioSessionCategoryRecord类别的音频会话隐式清除此选项。
如果清除此选项,则AirPlay设备不会显示为可用的音频输出路线。
如果设置了此选项,这些设备将显示为可用的输出路径。

通过Category和合适的Mode和Options的搭配我们可以调优出我们的效果,下面举两个应用场景:
用过高德地图的都知道,在后台播放QQ音乐的时候,如果导航语音出来,QQ音乐不会停止,而是被智能压低和混音,等导航语音播报完后,QQ音乐正常播放,这里我们需要后台播放音乐,所以Category使用AVAudioSessionCategoryPlayback,需要混音和智能压低其他APP音量,所以Options选用 AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers
代码示例如下

BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];

又或者我希望AVAudioSessionCategoryPlayAndRecord这个Category默认的音频由扬声器播放,那么可以调用这个接口去调整Category

  • (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError

通过选择合适和Category,mode和options,就可以调优音频的输入输出,来满足日常开发需求(需要注意的是Category,mode,option是搭配使用的,而不是简单组合,也就是说某种Category支持某些mode和option,从上面的表中也可以看出这一点)

音频中断处理

AVAudioSession提供了多种Notifications来进行此类状况的通知。其中将来电话、闹铃响等都归结为一般性的中断,用AVAudioSessionInterruptionNotification来通知。
我们可以通过监听AVAudioSessionInterruptionNotification这个key获取音频中断事件
回调回来Userinfo有键值

AVAudioSessionInterruptionTypeKey:
取值AVAudioSessionInterruptionTypeBegan表示中断开始,我们应该暂停播放和采集。
取值AVAudioSessionInterruptionTypeEnded表示中断结束,我们可以继续播放和采集。
AVAudioSessionInterruptionOptionKey: 当前只有一种值AVAudioSessionInterruptionOptionShouldResume表示此时也应该恢复继续播放和采集。
中断开始:我们需要做的是保存好播放状态,上下文,更新用户界面等
中断结束:我们要做的是恢复好状态和上下文,更新用户界面,根据需求准备好之后选择是否激活我们session。

而将其他App占据AudioSession的时候用AVAudioSessionSilenceSecondaryAudioHintNotification来进行通知。其回调回来的userInfo键为:
AVAudioSessionSilenceSecondaryAudioHintTypeKey
可能包含的值:
AVAudioSessionSilenceSecondaryAudioHintTypeBegin: 表示其他App开始占据Session
AVAudioSessionSilenceSecondaryAudioHintTypeEnd: 表示其他App开始释放Session

选择不同的音频播放技术,处理中断方式也有差别,具体如下:
System Sound Services:使用 System Sound Services 播发音频,系统会自动处理,不受APP控制,当中断发生时,音频播放会静音,当中断结束后,音频播放会恢复。
AV Foundation framework:AVAudioPlayer 类和 AVAudioRecorder 类提供了中断开始和结束的 Delegate 回调方法来处理中断。中断发生,系统会自动停止播放,需要做的是记录播放时间等状态,更新用户界面,等中断结束后,再次调用播放方法,系统会自动激活session。
Audio Queue Services, I/O audio unit:使用aduio unit这些技术需要处理中断,需要做的是记录播放或者录制的位置,中断结束后自己恢复audio session。
OpenAL:使用 OpenAL 播放时,同样需要自己监听中断。管理 OpenAL上下文,用户中断结束后恢复audio session。

需要注意的是:1. 有中断开始事件,不一定对应有中断结束事件,所以需要在用户进入前台,点击UI操作的时候,需要保存好播放状态和对Audio Session管理,以便不影响APP的音频功能。2.音频资源竞争上,一定是电话优先。

线路改变事件比如耳机拔入拔出

当收到线路变换通知后,在NSNotificationCenter中对AVAudioSessionRouteChangeNotification进行注册。在其userInfo中有键:AVAudioSessionRouteChangeReasonKey
取值AVAudioSessionRouteChangeReasonOldDeviceUnavailable时,表示有设备断开。根据人性化原则,在耳机拔出时应该使正在播放的声音暂停。
取值AVAudioSessionRouteChangeReasonNewDeviceAvailable时,表示有设备连接。
AVAudioSessionRouteChangeReasonKey : 表示改变的原因

枚举值 意义
AVAudioSessionRouteChangeReasonUnknown 未知原因
AVAudioSessionRouteChangeReasonNewDeviceAvailable 有新设备可用
AVAudioSessionRouteChangeReasonOldDeviceUnavailable 老设备不可用
AVAudioSessionRouteChangeReasonCategoryChange 类别改变了
AVAudioSessionRouteChangeReasonOverride App重置了输出设置
AVAudioSessionRouteChangeReasonWakeFromSleep 从睡眠状态呼醒
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory 当前Category下没有合适的设备

Audio Session Error Codes

由AVAudioSession方法返回的NSError对象中使用的错误代码。
AVAudioSessionErrorCodeNone
操作成功。
AVAudioSessionErrorCodeMediaServicesFailed
尝试在媒体服务失败期间或之后使用音频会话。
AVAudioSessionErrorCodeIsBusy
尝试将其音频会话设置为非活动状态,但仍在播放和/或录制。
AVAudioSessionErrorCodeIncompatibleCategory
试图执行当前类别中不允许的操作。
AVAudioSessionErrorCodeCannotInterruptOthers
尝试在应用程序处于后台时使不可混音的音频会话处于活动状态。
AVAudioSessionErrorCodeMissingEntitlement
试图执行应用程序没有所需权利的操作。
AVAudioSessionErrorCodeSiriIsRecording
Siri正在录制时尝试执行不允许的操作。
AVAudioSessionErrorCodeCannotStartPlaying
试图开始音频播放,但不允许播放。
AVAudioSessionErrorCodeCannotStartRecording
试图开始录音,但失败了。
AVAudioSessionErrorCodeBadParam
试图将属性设置为非法值。
AVAudioSessionErrorInsufficientPriority
该应用程序不允许设置音频类别,因为它正在被另一个应用程序使用。
AVAudioSessionErrorCodeResourceNotAvailable
由于设备没有足够的硬件资源来完成操作而失败的操作。
AVAudioSessionErrorCodeUnspecified
没有更多的错误信息可用。当音频系统处于不一致状态时,通常会产生这种错误类型。

代码:

AVAudioSession* session = [AVAudioSession sharedInstance];
  [session setCategory:AVAudioSessionCategoryPlayAndRecord
           withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker |
                       AVAudioSessionCategoryOptionDuckOthers |
                       AVAudioSessionCategoryOptionAllowBluetooth
                 error:nil];
  [session setMode:AVAudioSessionModeVoiceChat error:nil];
[[NSNotificationCenter defaultCenter]
      addObserver:self
         selector:@selector(handleRouteChange:)
             name:AVAudioSessionRouteChangeNotification
           object:[AVAudioSession sharedInstance]];
- (void)handleRouteChange:(NSNotification*)notification {
  NSDictionary* interuptionDict = notification.userInfo;
  NSInteger routeChangeReason = [[interuptionDict
      valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];
  switch (routeChangeReason) {
    case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
      ISLog(@"AVAudioSessionRouteChangeReasonNewDeviceAvailable");
      // 插入耳机时关闭扬声器播放
      break;
    case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
      ISLog(@"AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
      // 拔出耳机时的处理为开启扬声器播放
      break;
    case AVAudioSessionRouteChangeReasonCategoryChange:
      // called at start - also when other audio wants to play
      ISLog(@"AVAudioSessionRouteChangeReasonCategoryChange");
      break;
  }
}
 [[NSNotificationCenter defaultCenter]
      addObserver:self
         selector:@selector(handleAudioInterrupted:)
             name:AVAudioSessionInterruptionNotification
           object:[AVAudioSession sharedInstance]];
- (void)handleAudioInterrupted:(NSNotification*)notification {
  ISLog(@"音频打断通知 the notification is %@", notification);
  if (!_webRtcAvailable) {
    return;
  }
  if (AVAudioSessionInterruptionTypeBegan ==
      [notification.userInfo[AVAudioSessionInterruptionTypeKey] intValue]) {
    LOGE("音频打断开始 begin");
  } else if (AVAudioSessionInterruptionTypeEnded ==
             [notification.userInfo[AVAudioSessionInterruptionTypeKey]
                 intValue]) {
    LOGE("音频打断结束 begin - end");
  }
}

杂记

AudioSession Route

默认音频输出为扬声器
在PlayAndRecord这个category下,默认音频输出为听筒。
扬声器,听筒,耳机,蓝牙音箱之间的切换
利用MPVolumeView来让用户切换到扬声器,
通过overrideOutputAudioPort方法来切换到扬声器
修改category 的option为AVAudioSessionCategoryOptionDefaultToSpeaker
多个外接音频接收设备时(耳机,蓝牙音箱等),将遵循last-in wins的原则来选择外接设备,即声音将被导向最后接入的设备。

通过AVAudioSessionPortOverride选择音频输出

AVAudioSessionPortOverrideNone
不要覆盖输出音频端口。使用此选项将音频输出设备返回到当前音频类别的默认状态。
AVAudioSessionPortOverrideSpeaker
覆盖当前输入和输出,设置音频输出设备为内置扬声器和麦克风。仅适用于AVAudioSessionCategoryPlayAndRecord类别。
代码例子

[audioSession overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:error];

通过AVAudioSessionCategoryOptions选择音频输出设备

会话的类别和模式一起定义应用程序如何使用音频。通常,在激活会话之前设置类别和模式。您还可以在会话处于活动状态时设置类别或模式,但这会导致立即更改音频输出设备。
代码例子:
[audioSession setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:&error];

AVAudioSessionCategoryOptions 枚举值:

Input

AVAudioSessionPortUSBAudio
AVAudioSessionPortHeadsetMic
AVAudioSessionPortBuiltInMic

Output

AVAudioSessionPortUSBAudio
AVAudioSessionPortLineOut
AVAudioSessionPortHeadphones
AVAudioSessionPortHDMI
AVAudioSessionPortBuiltInSpeaker

overrideOutputAudioPort:方法和AVAudioSessionPortOverride的对比

共同点:使用仅适用于AVAudioSessionCategoryPlayAndRecord类别。
区别
overrideOutputAudioPort:是临时覆盖。任何音频输出改变或中断将导致音频被路由回到其正常输出设备,遵循最后入胜的规则
AVAudioSessionCategoryOptionDefaultToSpeaker,在没有使用耳机等其他附件时,音频将总是路由到扬声器而不是接收器。
举个例子
方法1, 设置之后,如果此时插入耳机,在拔掉。播放的声音会从听筒输出,而不是回到扬声器。
方法2, 设置之后,始终输出到扬声器,而不是其他接收器,如果没有耳机。(简要的说,就是如果有个蓝牙音箱,哪怕接上都不会有声音输出到蓝牙音响,插上耳机,则会有声音输出到耳机。)

AVAudioSession总结

AVAudioSession的作用就是管理音频这一唯一硬件资源的分配,通过调优合适的AVAudioSession来适配我们的APP对于音频的功能需求。切换音频场景时候,需要相应的切换AVAudioSession。
AVAudioSession构建了一个音频使用生命周期的上下文。当前状态是否可以录音、对其他App有怎样的影响、是否响应系统的静音键、如何感知来电话了等都可以通过它来实现。尤为重要的是AVAudioSession不仅可以和AVFoundation中的AVAudioPlyaer/AVAudioRecorder配合,其他录音/播放工具比如AudioUnit、AudioQueueService也都需要他进行录音、静音等上下文配合。

AVAudioSessionCategoryPlayAndRecord类别下

  • (BOOL)setMode:(AVAudioSessionMode)mode error:(NSError **)outError
    设置这个属性会导致蓝牙耳机连接失败,不去设置这个属性蓝牙耳机可以使用,一直找不到原因,不知道是不是跟 webrtc 有关系。。。 后来找到一个方案,在 系统版本 10.0 以上设置AVAudioSessionCategoryOptionAllowBluetoothA2DP 这个Option使用蓝牙耳机,10.0 以下首次设置AVAudioSession不设置mode,音频打断时候再去设置就不会出现蓝牙不能使用的问题了,具体为什么还在查找中
    感觉CoreAudio缺少一个setOption的接口,Category已经设置过,为何设置选项的时候再指定Category,这里不太理解。。。

结合网上文案总结只为自己解惑加深印象。

你可能感兴趣的:(iOS 音频-AVAudioSession)