最近在处理录音方面的问题,做个转载
参考链接
参考链接
AVAudioSession就是用来管理多个APP对音频硬件设备(麦克风,扬声器)的资源使用。
举例一下AVAudioSession可以做这些事情
- 设置自己的APP是否和其他APP音频同时存在,还是中断其他APP声音
- 在手机调到静音模式下,自己的APP音频是否可以播放出声音
- 电话或者其他APP中断自己APP的音频的事件处理
- 指定音频输入和输出的设备(比如是听筒输出声音,还是扬声器输出声音)
- 是否支持录音,录音同时是否支持音频播放
AVAudioSession Category
AVAudioSession的接口比较简单。APP启动的时候会自动激活AVAudioSession,当然我们可以手动激活代码如下。
//导入头文件
#import
//AVAudioSession是一个单例类
AVAudioSession *session = [AVAudioSession sharedInstance];
//AVAudioSessionCategorySoloAmbient是系统默认的category
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
//激活AVAudioSession
[session setActive:YES error:nil];
可以看到设置session这里有两个参数,category和options
Category iOS下目前有七种,每种Category都对应是否支持下面四种能力
Interrupts non-mixable apps audio:是否打断不支持混音播放的APP
Silenced by the Silent switch:是否会响应手机静音键开关
Supports audio input:是否支持音频录制
-
Supports audio output:是否支持音频播放
Category 是否允许音频播放/录音 是否打断其他不支持混音APP 是否会被静音键或锁屏键静音 AVAudioSessionCategoryAmbient 支持 否 是 AVAudioSessionCategoryAudioProcessing 不支持播放,不支持录制 是 否 AVAudioSessionCategoryPlayback 只支持播放 默认YES,可以重写为NO 否 AVAudioSessionCategoryRecord 只支持录制 是 否(锁屏下仍可录制) AVAudioSessionCategorySoloAmbient 只支持播放 是 是 AVAudioSessionCategoryMultiRoute 支持播放,支持录制 是 否 AVAudioSessionCategoryPlayAndRecord 支持播放,支持录制 默认YES,可以重写为NO 否
AVAudioSessionCategoryAmbient,只支持音频播放。这个 Category,音频会被静音键和锁屏键静音。并且不会打断其他应用的音频播放。
AVAudioSessionCategorySoloAmbient,这个是系统默认使用的 Category,只支持音频播放。音频会被静音键和锁屏键静音。和AVAudioSessionCategoryAmbient不同的是,这个会打断其他应用的音频播放
AVAudioSessionCategoryPlayback,只支持音频播放。你的音频不会被静音键和锁屏键静音。适用于音频是主要功能的APP,像网易云这些音乐app,锁屏后依然可以播放。
需要注意一下,选择支持在静音键切到静音状态以及锁屏键切到锁屏状态下仍然可以播放音频 Category 时,必须在应用中开启支持后台音频功能,详见 UIBackgroundModes。
- AVAudioSessionCategoryRecord,只支持音频录制。不支持播放。
- AVAudioSessionCategoryPlayAndRecord,支持音频播放和录制。音频的输入和输出不需要同步进行,也可以同步进行。需要音频通话类应用,可以使用这个 Category。
- AVAudioSessionCategoryAudioProcessing,只支持本地音频编解码处理。不支持播放和录制。
- AVAudioSessionCategoryMultiRoute,支持音频播放和录制。允许多条音频流的同步输入和输出。(比如USB连接外部扬声器输出音频,蓝牙耳机同时播放另一路音频这种特殊需求)
可以通过AVAudioSession的属性来读取当前设备支持的Category
@property(readonly) NSArray *availableCategories;
设置Category代码
NSError *setCategoryError = nil;
BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&setCategoryError];
if (!success) {
//这里可以读取setCategoryError.localizedDescription查看错误原因
}
AVAudioSession Mode&&Options
刚刚介绍的Category定义了七种主场景,实际开发需求中有时候需要对Category进行微调整,我们发现这个接口还有两个参数Mode和Options。
AVAudioSession Mode
我们通过读取下面这条属性获取当前设备支持的Mode
@property(readonly) NSArray *availableModes;
iOS下有七种mode来定制我们的Category行为
模式 | 兼容的 | 场景 |
---|---|---|
AVAudioSessionModeDefault | All | 默认模式 |
AVAudioSessionModeVoiceChat | AVAudioSessionCategoryPlayAndRecord | VoIP |
AVAudioSessionModeGameChat | AVAudioSessionCategoryPlayAndRecord | 游戏录制,GKVoiceChat自动设置 |
AVAudioSessionModeVideoRecording | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord | 录制视频 |
AVAudioSessionModeMoviePlayback | AVAudioSessionCategoryPlayback | 视频播放 |
AVAudioSessionModeMeasurement | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback | 最小系统 |
AVAudioSessionModeVideoChat | AVAudioSessionCategoryPlayAndRecord | 视频通话 |
下面逐一介绍下每个Mode
AVAudioSessionModeDefault,默认模式,与所有的 Category 兼容
AVAudioSessionModeVoiceChat,适用于VoIP 类型的应用。只能是 - AVAudioSessionCategoryPlayAndRecord Category下。在这个模式系统会自动配置AVAudioSessionCategoryOptionAllowBluetooth 这个选项。系统会自动选择最佳的内置麦克风组合支持语音聊天。
AVAudioSessionModeVideoChat,用于视频聊天类型应用,只能是 AVAudioSessionCategoryPlayAndRecord Category下。适在这个模式系统会自动配置 AVAudioSessionCategoryOptionAllowBluetooth 和 AVAudioSessionCategoryOptionDefaultToSpeaker 选项。系统会自动选择最佳的内置麦克风组合支持视频聊天。
AVAudioSessionModeGameChat,适用于游戏类应用。使用 GKVoiceChat 对象的应用会自动设置这个模式和 AVAudioSessionCategoryPlayAndRecord Category。实际参数和AVAudioSessionModeVideoChat一致
AVAudioSessionModeVideoRecording,适用于使用摄像头采集视频的应用。只能是 AVAudioSessionCategoryPlayAndRecord 和 AVAudioSessionCategoryRecord 这两个 Category下。这个模式搭配 AVCaptureSession API 结合来用可以更好地控制音视频的输入输出路径。(例如,设置 automaticallyConfiguresApplicationAudioSession 属性,系统会自动选择最佳输出路径。
AVAudioSessionModeMeasurement,最小化系统。只用于 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryRecord、AVAudioSessionCategoryPlayback 这几种 Category。
AVAudioSessionModeMoviePlayback,适用于播放视频的应用。只用于 AVAudioSessionCategoryPlayback 这个Category。
AVAudioSession Options
我们还可以使用options去微调Category行为,如下表
Option | Option功能说明 | 兼容的 Category |
---|---|---|
AVAudioSessionCategoryOptionMixWithOthers | 支持和其他APP音频 mix | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute |
AVAudioSessionCategoryOptionDuckOthers | 系统智能调低其他APP音频音量 | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute |
AVAudioSessionCategoryOptionAllowBluetooth | 支持蓝牙音频输入 | AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord |
AVAudioSessionCategoryOptionDefaultToSpeaker | 设置默认输出音频到扬声器 | AVAudioSessionCategoryPlayAndRecord |
调优我们的Category
通过Category和合适的Mode和Options的搭配我们可以调优出我们的效果,下面举两个应用场景:
用过高德地图的都知道,在后台播放QQ音乐的时候,如果导航语音出来,QQ音乐不会停止,而是被智能压低和混音,等导航语音播报完后,QQ音乐正常播放,这里我们需要后台播放音乐,所以Category使用AVAudioSessionCategoryPlayback,需要混音和智能压低其他APP音量,所以Options选用 AVAudioSessionCategoryOptionMixWithOthers和AVAudioSessionCategoryOptionDuckOthers
BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionDuckOthers error:&setCategoryError];
音频中断处理
中断发生时,应用程序的AVAudioSession会发送通知AVAudioSessionInterruptionNotification,注册通知代码如下:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];
在接收到通知的userInfo中,会包含一个AVAudioSessionInterruptionTypeKey,用来标识中断开始和中断结束.
当中断类型为AVAudioSessionInterruptionTypeKeyEnded时,userInfo中还会包含一个AVAudioSessionInterruptionOptions来表明音频会话是否已经重新激活以及是否可以再次播放.示例代码如下:
- (void)handleInterruption:(NSNotification *)notification
{
_descriptionLabel.text = @"handleInterruption";
NSDictionary *info = notification.userInfo;
AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if (type == AVAudioSessionInterruptionTypeBegan) {
//Handle InterruptionBegan
}else{
AVAudioSessionInterruptionOptions options = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (options == AVAudioSessionInterruptionOptionShouldResume) {
//Handle Resume
}
}
}
在iOS设备上添加或移除音频输入,输出线路时,会发生线路改变,比如用户插入耳机或断开USB麦克风.当这些事件发生时,音频会根据情况改变输入或输入线路,同时AVAudioSession会发送一个相关变化的通知AVAudioSessionRouteChangeNotification.注册通知的相关代码如下:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleRouteChange:) name:AVAudioSessionRouteChangeNotification object:[AVAudioSession sharedInstance]];
根据苹果公司的文档,当用户插入耳机时,隐含的意思是用户不希望外界听到具体的音频了内容,这就意味着当用户断开耳机时,播放的内容可能需要保密,所以我们需要在断开耳机时停止音频播放.
AVAudioSessionRouteChangeNotification通知的userinfo中会带有通知发送的原因信息及前一个线路的描述.线路变更的原因保存在userinfo的AVAudioSessionRouteChangeReasonKey值中,通过返回值可以推断出不同的事件,对于旧音频设备中断对应的reason为AVAudioSessionRouteChangeReasonOldDeviceUnavailable.但光凭这个reason并不能断定是耳机断开,所以还需要使用通过AVAudioSessionRouteChangePreviousRouteKey获得上一线路的描述信息,注意线路的描述信息整合在一个输入NSArray和一个输出NSArray中,数组中的元素都是AVAudioSessionPortDescription对象.我们需要从线路描述中找到第一个输出接口并判断其是否为耳机接口,如果为耳机,则停止播放.
- (void)handleRouteChange:(NSNotification *)notification
{
NSDictionary *info = notification.userInfo;
AVAudioSessionRouteChangeReason reason = [info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
if (reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable) { //旧音频设备断开
//获取上一线路描述信息
AVAudioSessionRouteDescription *previousRoute = info[AVAudioSessionRouteChangePreviousRouteKey];
//获取上一线路的输出设备类型
AVAudioSessionPortDescription *previousOutput = previousRoute.outputs[0];
NSString *portType = previousOutput.portType;
if ([portType isEqualToString:AVAudioSessionPortHeadphones]) {
}
}
}