标签:uid 参数 介绍 min 标记 config suitable queue 输入输出
音频输出作为硬件资源,对于iOS系统来说是唯一的,那么要如何协调和各个App之间对这个稀缺的硬件持有关系呢?
iOS给出的解决方案是"AVAudioSession" ,通过它可以实现对App当前上下文音频资源的控制,比如
插拔耳机、接电话、是否和其他音频数据混音等。当你遇到:
这些场景的时候,就可以考虑一下“AVAudioSession”了。
??????
在很久以前(其实也是不是太久--iOS7以前)还有个AudioSession的存在,其功能与AVAudioSession类似,但是在iOS7 以后就已经被标记为
“Not Applicable”,所以如果Google到了说AudioSession的内容而不是用的AVAudioSession,那么就可以直接PASS了,当然如果要兼容iOS6
就另当别论了,不过现在QQ/微信都是要求iOS7的情况下,是否需要兼容iOS6就看老板们的意思吧。
默认的行为相当于设置了Category为“AVAudioSessionCategorySoloAmbient”
来看Demo。
通过这播放器demo可以验证上面的默认Session行为。
AVAudioSession以一个单例实体的形式存在,通过类方法:
+ (AVAudioSession *)sharedInstance;
获得单例。
虽然系统会在App启动的时候,激活这个唯一的AVAudioSession,但是最好还是在自己ViewController的viewDidLoad
里面再次进行激活:
- (BOOL)setActive:(BOOL)active
error:(NSError * _Nullable *)outError;
通过设置active
为"YES"激活Session,设置为“?NO”解除Session的激活状态。BOOL返回值表示是否成功,如果失败的话可以通过NSError的error.localizedDescription
查看出错原因。
因为AVAudioSession会影响其他App的表现,当自己App的Session被激活,其他App的就会被解除激活,如何要让自己的Session解除激活后恢复其他App Session的激活状态呢?
此时可以使用:
- (BOOL)setActive:(BOOL)active
withOptions:(AVAudioSessionSetActiveOptions)options
error:(NSError * _Nullable *)outError;这里的options传入
AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
即可。当然,也可以通过
otherAudioPlaying
变量来提前判断当前是否有其他App在播放音频。
可以通过:
@property(readonly) NSString *category;
属性,获取当前的Category,比如上面的播放其,默认是
NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category);
输出:
Current Category:AVAudioSessionCategorySoloAmbien
AVAudioSession主要能控制App的哪些表现以及如何控制的呢?首先AVAudioSession将使用音频的场景分成七大类,通过设置Session为不同的类别,可以控制:
类别 | 当按“静音”或者锁屏是是否静音 | 是否引起不支持混音的App中断 | 是否支持录音和播放 |
---|---|---|---|
AVAudioSessionCategoryAmbient | 是 | 否 | 只支持播放 |
AVAudioSessionCategoryAudioProcessing | - | 都不支持 | |
AVAudioSessionCategoryMultiRoute | 否 | 是 | 既可以录音也可以播放 |
AVAudioSessionCategoryPlayAndRecord | 否 | 默认不引起 | 既可以录音也可以播放 |
AVAudioSessionCategoryPlayback | 否 | 默认引起 | 只用于播放 |
AVAudioSessionCategoryRecord | 否 | 是 | 只用于录音 |
AVAudioSessionCategorySoloAmbient | 是 | 是 | 只用于播放 |
可以看到,其实默认的就是“AVAudioSessionCategorySoloAmbient”类别。从表中我们可以总结如下:
了解了这七大类别,我们就可以根据自己的需要进行对应类别的设置了:
- (BOOL)setCategory:(NSString *)category error:(NSError **)outError;
传入对应的列表枚举即可。如果返回"NO"可以通过NSError的error.localizedDescription
查看原因。
可以通过:
@property(readonly) NSArray<NSString *> *availableCategories;
属性,查看当前设备支持哪些类别,然后再进行设置,从而保证传入参数的合法,减少错误的可能。
比如修改上面的Demo例子:
NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category);
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
if (nil != error) {
NSLog(@"set Option error %@", error.localizedDescription);
}
NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category);
此时在播放音乐的时候,再去按下静音键,会发现,音乐还在继续播放,不会被静音。
上面介绍的这个七大类别,可以认为是设定了七种主场景,而这七类肯定是不能满足开发者所有的需求的。CoreAudio提供的方法是,首先定下七种的一种基调,然后在进行微调。CoreAudio为每种Category都提供了些许选项来进行微调。
在设置完类别后,可以通过
@property(readonly) AVAudioSessionCategoryOptions categoryOptions;
属性,查看当前类别设置了哪些选项,注意这里的返回值是AVAudioSessionCategoryOptions,实际是多个options的“|”运算。默认情况下是0。
选项 | 适用类别 | 作用 |
---|---|---|
AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryPlayAndRecord, AVAudioSessionCategoryPlayback, and AVAudioSessionCategoryMultiRoute | 是否可以和其他后台App进行混音 |
AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryAmbient, AVAudioSessionCategoryPlayAndRecord, AVAudioSessionCategoryPlayback, and AVAudioSessionCategoryMultiRoute | 是否压低其他App声音 |
AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayAndRecord | 是否支持蓝牙耳机 |
AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryPlayAndRecord | 是否默认用免提声音 |
目前主要的选项有这几种,都有对应的使用场景,除此之外,在iOS9还提供了AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers
最新的iOS10又新加了两个AVAudioSessionCategoryOptionAllowBluetoothA2DP
、AVAudioSessionCategoryOptionAllowAirPlay
用来支持蓝牙A2DP耳机和AirPlay。
来看每个选项的基本作用:
通过接口:
- (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError
来对当前的类别进行选项的设置。
比如Demo中:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
if (nil != error) {
NSLog(@"set Option error %@", error.localizedDescription);
}
options = [[AVAudioSession sharedInstance] categoryOptions];
NSLog(@"Category[%@] has %lu options", [AVAudioSession sharedInstance].category, options);
此时,先打开QQ音乐播放器,然后再开始进行播放,会发现,QQ和我们的播放器都在播放,并且进行了自动混音。
不过这个过程,感觉CoreAudio缺少一个setOption
的接口,既然已经是当前处于的Category,干嘛还要再设置选项的时候再指定Category呢??疑惑。。。
刚讲完七大类别,现在再来七大模式。通过上面的七大类别,我们基本覆盖了常用的主场景,在每个主场景中可以通过Option进行微调。为此CoreAudio提供了七大比较常见微调后的子场景。叫做各个类别的模式。
模式 | 适用的类别 | 场景 |
---|---|---|
AVAudioSessionModeDefault | 所有类别 | 默认的模式 |
AVAudioSessionModeVoiceChat | AVAudioSessionCategoryPlayAndRecord | VoIP |
AVAudioSessionModeGameChat | AVAudioSessionCategoryPlayAndRecord | 游戏录制,由GKVoiceChat自动设置,无需手动调用 |
AVAudioSessionModeVideoRecording | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord | 录制视频时 |
AVAudioSessionModeMoviePlayback | AVAudioSessionCategoryPlayback | 视频播放 |
AVAudioSessionModeMeasurement | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback | 最小系统 |
AVAudioSessionModeVideoChat | AVAudioSessionCategoryPlayAndRecord | 视频通话 |
每个模式有其适用的类别,所以,并不是有“七七 四十九”种组合。如果当前处于的类别下没有这个模式,那么是设置不成功的。设置完Category后可以通过:
@property(readonly) NSArray<NSString *> *availableModes;
属性,查看其支持哪些属性,做合法性校验。
来看具体应用:
另外几种和音频APP关系不大,一般我们只需要关注VoIP或者视频通话即可。
通过调用:
- (BOOL)setMode:(NSString *)mode error:(NSError **)outError
可以在设置Category之后再设置模式。
当然,这些模式只是CoreAduio总结的,不一定完全满足要求,对于具体的模式,在iOS10中还是可以微调的。通过接口:
- (BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError
但是在iOS9及以下就只能在Category上调了,其实本质是一样的,可以认为是个API糖,接口封装。
上面说的这些Category啊、Option啊以及Mode都是对自己作为播放主体时的表现,但是假设,现在正在播放着,突然来电话了、闹钟响了或者你在后台放歌但是用户启动其他App用上面的方法影响的时候,我们的App该如何表现呢?最常用的场景当然是先暂停,待恢复的时候再继续。那我们的App要如何感知到这个终端以及何时恢复呢?
AVAudioSession提供了多种Notifications来进行此类状况的通知。其中将来电话、闹铃响等都归结为一般性的中断,用
AVAudioSessionInterruptionNotification
来通知。其回调回来的userInfo主要包含两个键:
AVAudioSessionInterruptionTypeBegan
表示中断开始,我们应该暂停播放和采集,取值为AVAudioSessionInterruptionTypeEnded
表示中断结束,我们可以继续播放和采集。AVAudioSessionInterruptionOptionShouldResume
表示此时也应该恢复继续播放和采集。而将其他App占据AudioSession的时候用AVAudioSessionSilenceSecondaryAudioHintNotification
来进行通知。其回调回来的userInfo键为:
AVAudioSessionSilenceSecondaryAudioHintTypeKey
可能包含的值:
除了其他App和系统服务,会对我们的App产生影响以外,用户的手也会对我们产生影响。默认情况下,AudioSession会在App启动时选择一个最优的输出方案,比如插入耳机的时候,就用耳机。但是这个过程中,用户可能拔出耳机,我们App要如何感知这样的情况呢?
同样AVAudioSession也是通过Notifications来进行此类状况的通知。
假设有这样的App:
最开始在录音时,用户插入和拔出耳机我们都停止录音,这里通过Notification来通知有新设备了,或者设备被退出了,然后我们控制停止录音。或者在播放时,当耳机被拔出出时,Notification给了通知,我们先暂停音乐播放,待耳机插回时,在继续播放。
在NSNotificationCenter中对AVAudioSessionRouteChangeNotification进行注册。在其userInfo中有键:
枚举值 | 意义 |
---|---|
AVAudioSessionRouteChangeReasonUnknown | 未知原因 |
AVAudioSessionRouteChangeReasonNewDeviceAvailable | 有新设备可用 |
AVAudioSessionRouteChangeReasonOldDeviceUnavailable | 老设备不可用 |
AVAudioSessionRouteChangeReasonCategoryChange | 类别改变了 |
AVAudioSessionRouteChangeReasonOverride | App重置了输出设置 |
AVAudioSessionRouteChangeReasonWakeFromSleep | 从睡眠状态呼醒 |
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory | 当前Category下没有合适的设备 |
AVAudioSessionRouteChangeReasonRouteConfigurationChange | Rotuer的配置改变了 |
AVAudioSession构建了一个音频使用生命周期的上下文。当前状态是否可以录音、对其他App有怎样的影响、是否响应系统的静音键、如何感知来电话了等都可以通过它来实现。尤为重要的是AVAudioSession不仅可以和AVFoundation中的AVAudioPlyaer/AVAudioRecorder配合,其他录音/播放工具比如AudioUnit、AudioQueueService也都需要他进行录音、静音等上下文配合。
文中Demo参见GitHub
标签:uid 参数 介绍 min 标记 config suitable queue 输入输出
原文地址:https://www.cnblogs.com/Free-Thinker/p/9567565.html