程序员

iOS音频-AVAudioSession

2018-12-01  本文已影响6人  iOSTbag

最近在处理录音方面的问题,做个转载
参考链接
参考链接

AVAudioSession就是用来管理多个APP对音频硬件设备(麦克风,扬声器)的资源使用。

举例一下AVAudioSession可以做这些事情

AVAudioSession Category

AVAudioSession的接口比较简单。APP启动的时候会自动激活AVAudioSession,当然我们可以手动激活代码如下。

//导入头文件
#import <AVFoundation/AVFoundation.h>

//AVAudioSession是一个单例类
AVAudioSession *session = [AVAudioSession sharedInstance];
//AVAudioSessionCategorySoloAmbient是系统默认的category
[session setCategory:AVAudioSessionCategorySoloAmbient error:nil];
//激活AVAudioSession
[session setActive:YES error:nil];

可以看到设置session这里有两个参数,category和options
Category iOS下目前有七种,每种Category都对应是否支持下面四种能力

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

可以通过AVAudioSession的属性来读取当前设备支持的Category

@property(readonly) NSArray<NSString *> *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<NSString *> *availableModes;

iOS下有七种mode来定制我们的Category行为

模式 兼容的 场景
AVAudioSessionModeDefault All 默认模式
AVAudioSessionModeVoiceChat AVAudioSessionCategoryPlayAndRecord VoIP
AVAudioSessionModeGameChat AVAudioSessionCategoryPlayAndRecord 游戏录制,GKVoiceChat自动设置
AVAudioSessionModeVideoRecording AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord 录制视频
AVAudioSessionModeMoviePlayback AVAudioSessionCategoryPlayback 视频播放
AVAudioSessionModeMeasurement AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback 最小系统
AVAudioSessionModeVideoChat AVAudioSessionCategoryPlayAndRecord 视频通话

下面逐一介绍下每个Mode

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]) {
    }
  }
}
总结:AVAudioSession的作用就是管理音频这一唯一硬件资源的分配,通过调优合适的AVAudioSession来适配我们的APP对于音频的功能需求。切换音频场景时候,需要相应的切换AVAudioSession。
上一篇 下一篇

猜你喜欢

热点阅读