在前面配置Category时, 我们可以增加Options来简单调整! 注意Options与Category类型相关, 在配置前, 先判断 Options是否匹配Category, 可以使用[AVAudioSession sharedInstance].availableCategories来查看当前Category支持的Options,常规的选项.
目前主要的选项有这几种,都有对应的使用场景,除此之外,在iOS9还提供了AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
iOS10又新加了两个AVAudioSessionCategoryOptionAllowBluetoothA2DP 、AVAudioSessionCategoryOptionAllowAirPlay用来支持蓝牙A2DP耳机和AirPlay
配置选项常见的API:
不过这个过程,感觉缺少一个setOption的接口,既然已经是当前处于的Category,干嘛还要再设置选项的时候再指定Category呢?
通过Category和Options的微调, 基本覆盖了主要的场景, 在上面的逻辑还需要针对不同的子场景进行调整, 就需要配置Mode, 具体7个子mode如下:
我们一般都是在设置Category以后再才设置 Mode:
注意app中的AVAudioSession通常使用方法[AVAudioSession sharedInstance] 使用一个单例对象去配置, 我们在启动时, 可以获取它的内部的一些属性:
默认情况下, availableInputs 只会拥有iPhone的前置内置麦克风!
注意我们可以在初始条件下打印inputDataSources, 通过AVAudioSessionDataSourceDescription的内容, 可以知道设备拥有的麦克风数目.
实际上输入数据源, 可能有多个, 我们直接通过设置Category等配置系统会自动帮我们选择具体使用的麦克风
当我们带上Airpod蓝牙耳机, 并配置Options为AVAudioSessionCategoryOptionAllowBluetooth时, :
AVAudioSessionCategoryOptionAllowBluetooth情况下, availableInputs 会拥有iPhone的前置内置麦克风(有多个)以及airpod蓝牙麦克风!
当我们带上 airpods 蓝牙耳机, 并插上有线耳机, 并配置AVAudioSessionCategoryOptionAllowBluetooth 时, 能看到可用的音频输入方式为: 内置麦克风, 有线麦克风, 耳机麦克风:
当我们带上 airpods 蓝牙耳机, 并插上有线耳机, 并配置Option = 0(不配置蓝牙) 时, 打印的可用音频Inputs只有内置的前置麦克风和有线耳机麦克风:
Apple 的文档中提到, 当我们使用 setCategory...方法时, 如果当前AVAudioSession已经激活, 系统就会立即改变Category, 如果是未激活状态inActive, 则需要我们设置Active==YES才会激活对应状态
常见的激活API:
停止激活API:
当APP在使用时, 可能同时接入多个外设音频(有线耳机, 蓝牙耳机, 默认扬声器), 默认情况下, AudioSession会根据Last in Wins原则选择, 就是声音会导向最后接入的设备.
默认情况下, 声音会从默认扬声器出来. 但是当我们设置Category是在PlayAndRecord时, iPhone会将默认的输出修改成听筒, 也就是前置摄像头旁边的一个扬声器(支持贴耳的近距离检测!!!), 这种场景下, 有以下两种方式将输出修改成扬声器:
简单看一下这个方法的系统解释:
也就是说这个方法只是暂时将AVAudioSessionCategoryPlayAndRecord category情况下, 将音频输出强制输出到AVAudioSessionPortOverrideSpeaker扬声器, 在使用完以后, 再设置回AVAudioSessionPortOverrideNone.
如果需要永久将PlayAndRecord Category中, 音频输出到扬声器, 最好的方式是直接设置Options为AVAudioSessionCategoryOptionDefaultToSpeaker.
具体的代码如下:
系统的中断响应分成两类:
默认情况下, AudioSession会在App启动时选择一个最优的输出方案, 比如插入耳机的时候, 就用耳机. 但是这个过程中, 用户可能拔出耳机, 我们App要如何感知这样的情况呢
系统会通过 AVAudioSessionRouteChangeNotification来进行通知其中在其userInfo中有键:AVAudioSessionRouteChangeReasonKey来表示改变的原因,原因的键值有一下几种:
AVAudioSessionRouteChangeReasonUnknown:未知原因
AVAudioSessionRouteChangeReasonNewDeviceAvailable:有新设备可用
在音频SDK中, 针对AVAudioSession都需要指定对应的Category, mode, options等配置.
具体过程如下:
我们可以在SDK使用完音频资源以后通过如下配置停止Category的服务, 这样之前的背景音乐APP声音会继续播放:
在使用 Category = AVAudioSessionCategoryPlayAndRecord 默认会使用听筒播放音乐!
有如下几种情况:
当不接外设时, 此时音频从前置摄像头的听筒输出, 声音会非常小!!!
当仅插入有线耳机时, 此时音频从有线耳机输出!
当仅接入蓝牙耳机时, 未插入有线耳机时, 音频从听筒输出!!!不会从蓝牙耳机输出
当先插入有线耳机, 然后接入蓝牙耳机时(根据apple策略, 声音会优先从蓝牙耳机输出), 此时还是会从有线耳机输出声音!!!不会从蓝牙耳机输出
简单结论 , Category == PlayAndRecord, Options == 0 ==> 音频从听筒输出:
此时, 在没有有线耳机的情况下, 声音太小!!! 体验不好!!! 我们需要增加Options来进行改进
有如下几种情况:
当不接外设时, 此时音频从扬声器输出, 声音很大!!!
当仅插入有线耳机时, 此时音频从有线耳机输出!!!
当仅接入蓝牙耳机时, 未插入有线耳机时, 音频从扬声器输出!!!不会从蓝牙耳机输出
当先插入有线耳机, 然后接入蓝牙耳机时(根据apple策略, 声音会优先从蓝牙耳机输出), 此时还是会从有线耳机输出声音!!!不会从蓝牙耳机输出
简单结论, Category == PlayAndRecord, Options = AVAudioSessionCategoryOptionDefaultToSpeaker ==> 默认音频从扬声器输出:
有如下几种情况:
当不接外设时, 此时音频从听筒输出, 声音会非常小!!!
当仅插入有线耳机时, 此时音频从有线耳机输出!!!
当仅接入蓝牙耳机时, 未插入有线耳机时, 音频此时会从蓝牙耳机输出!!!
当先插入有线耳机, 然后接入蓝牙耳机时(根据apple策略, 声音会优先从蓝牙耳机输出), 此时会从蓝牙耳机输出
简单结论, Category == PlayAndRecord, Options = AVAudioSessionCategoryOptionAllowBluetooth ==> 默认音频从听筒输出:
结论: 如果没有外设, 会从扬声器输出, 如果有外设, 根据Last in Wins 原则从外设(有线耳机, 蓝牙耳机)输出.
直接使用如下关键的是overrideOutputAudioPort API, 而Options是否使用AVAudioSessionCategoryOptionAllowBluetoot|AVAudioSessionCategoryOptionDefaultToSpeaker并不影响:
在设置overrideOutputAudioPort 为Speaker 以后, 不论是否接入外设, 设备会强制使用扬声器作为音频播放目的!!! 因此该方法的优先级非常高!!!
一定要在设置AVAudioSessionPortOverrideSpeaker 以后, 在结束使用时候调用 [[AVAudioSession sharedInstance] overrideOutputAudioPort:0 error:nil]; 将强制从扬声器输出音频改成系统默认(也就是Options)!!! 否则可能导致SDK影响APP的其他服务(一般SDK不会去修改overrideOutputAudioPort!!!)
我们的SDK在使用AVAudioSession时, 建议使用最小权限使用原则, 在即将要使用音频资源时, 去配置AVAudioSession的相关API, 在音频使用结束以后, 关闭!!!
另外如果需要防止其他模块的影响, 建议使用如下代码 ( 如果有特殊需求, 自行更改. 比如要强制要求不论是否拥有外设, 用扬声器输出音频):
在不使用音频逻辑时, 将AVAudioSession关闭:
另外为了更好的音频体验逻辑, 为了充分利用PlayAndRecord的默认输出是听筒, 其他的Category的默认输出是扬声器, 可以使用距离监听器, 去切换音频播放中的一些逻辑, 如下: