IOS你不知道的音频细节,音频会话AVAudioSession
名称
音频会话
解释
设置程序的音频环境,告知系统的音频行为用途。管理多个app对音频硬件的资源分配。通过主场景、模式、细节选项的设置可以使app在音频方面更加人性化。
疑问
有人说,我写音乐播放的时候没有考虑这个都能播放,这个有啥用?当你想要做的更人性化时就该考虑下这点,比如说插耳机、拔耳机的逻辑,是否控制后台播放,是否让当前app的声音高于其他app的声音等。
理论
音频会话场景分类设置:
说明:以下分类并不是一个应用只能使用一个分类,可以根据场景来切换不同的分类。
1、主场景分类;
主场景Category | 描述 | 可与其他app共享混合播放 | 是否可播放 | 是否可录音 | 静音按钮或者锁屏按钮点击后是否会停止静音 | 是否可在后台运行 |
---|---|---|---|---|---|---|
AVAudioSessionCategoryAmbient | 背景声音,可以与其他音乐混合,用于以非语音为主的应用 | 可混合 | 可播放 | 不可录音 | 会静音 | 不可运行 |
AVAudioSessionCategorySoloAmbient | 默认分类,背景声音 | 不可混合 | 可播放 | 不可录音 | 会静音 | 不可运行 |
AVAudioSessionCategoryPlayback | 用于播放音乐,用于以语音为主的应用 | 不可混合 | 可播放 | 不可录音 | 不会静音 | 可运行(另说明) |
AVAudioSessionCategoryRecord | 用于录音,除了来电铃声、闹钟、日历提醒等系统声音外其他声音都不会被播放,只执行录音 | 不可混合 | 不可播放 | 可录音 | 不会静音(锁屏仍可录制) | 可运行 |
AVAudioSessionCategoryPlayAndRecord | 用于播放和录音同时存在时,只有它允许修改默许音频播放设备:听筒还是外放,在该Category下声音的默认出口为听筒或者耳机。 | 默认不引起 | 可播放 | 可录音 | 不会静音 | 可运行 |
AVAudioSessionCategoryMultiRoute | 多种输入输出,例如可以耳机、USB设备同时播放 | 不可混合 | 可播放 | 可录音 | 不会静音 | 可运行 |
AVAudioSessionCategoryAudioProcessing | 硬件解码音频 | 不支持 | 不可播放 | 不可录音 | 不会静音 | 不可运行,可请求更多时间完成处理 |
说明:当使用AVAudioSessionCategoryPlayback分类时,要想实现后台播放,需要在Info.plist文件里添加Required background modes的数组,在下面添加名为App plays audio or streams audio/video using AirPlay的字符串
2、主场景细节options设置;
选项 | 兼容的场景 | 用途 | 描述 |
---|---|---|---|
AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute | 支持和其他APP音频混合 | |
AVAudioSessionCategoryOptionDuckOthers | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryPlayback AVAudioSessionCategoryMultiRoute | 导航地图 | 系统智能调低其他APP音频音量,突出本app的音量 |
AVAudioSessionCategoryOptionAllowBluetooth | AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayAndRecord | 支持蓝牙音频输入 | |
AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryPlayAndRecord | 设置默认输出音频到扬声器,即免提 | |
AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers | AVAudioSessionCategoryPlayback AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryMultiRoute | app偶尔的使用音频播放 | |
AVAudioSessionCategoryOptionAllowBluetoothA2DP | AVAudioSessionCategoryPlayAndRecord | 立体声蓝牙 | |
AVAudioSessionCategoryOptionAllowAirPlay | AVAudioSessionCategoryPlayAndRecord | 远程AirPlay设备 |
3、主场景模式modes设置;
模式 | 兼容的场景 | 用途 | 描述 |
---|---|---|---|
AVAudioSessionModeDefault | 所有场景 | 默认模式 | 通用 |
AVAudioSessionModeVoiceChat | AVAudioSessionCategoryPlayAndRecord | Voice over IP语音应用 | 系统会选择最佳的输入设备,比如插上耳机就使用耳机上的麦克风进行采集 |
AVAudioSessionModeGameChat | AVAudioSessionCategoryPlayAndRecord | 游戏录制 | 适用于游戏App的采集和播放 |
AVAudioSessionModeVideoRecording | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord | 录制视频时 | |
AVAudioSessionModeMeasurement | AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback | 最小化系统 | |
AVAudioSessionModeMoviePlayback | AVAudioSessionCategoryPlayback | 视频播放 | 系统也会选择最佳的输入设备,比如插上耳机就使用耳机上的麦克风进行采集 |
AVAudioSessionModeVideoChat | AVAudioSessionCategoryPlayAndRecord | 视频通话 | |
AVAudioSessionModeSpokenAudio | AVAudioSessionCategoryPlayback AVAudioSessionCategorySoloAmbient AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryMultiRoute | 有声读物 | 播放语音并暂停其他语音app |
处理中断事件
1.当收到中断通知后,userInfo传值的字典包含AVAudioSessionInterruptionTypeKey类型值,
2.当这个类型等于 AVAudioSessionInterruptionTypeBegan时,表示开始中断。
3.当这个类型等于 AVAudioSessionInterruptionTypeEnded时,表示中断结束userInfo字典会传回一个 AVAudioSessionInterruptionOptionKey 值,当这个值等于AVAudioSessionInterruptionOptionShouldResume时,表明音频会话是否已经重新激活,以及是否可以再次播放。
线路改变事件
1.当收到线路变换通知后,userInfo传值的字典包含AVAudioSessionRouteChangeReasonKey值。
2.当这个值等于AVAudioSessionRouteChangeReasonOldDeviceUnavailable时,表示有设备断开。根据人性化原则,在耳机拔出时应该使正在播放的声音暂停。
3.当这个值等于AVAudioSessionRouteChangeReasonNewDeviceAvailable时,表示有设备连接。
核心代码
import <AVFoundation/AVFoundation.h>
该代码应写在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {这个方法里
//设置音频会话
AVAudioSession * session = [AVAudioSession sharedInstance];
NSError * error;
//设置会话的场景、选项、模式
if( ![session setCategory:AVAudioSessionCategoryPlayback error:&error])
{
NSLog(@"音频会话分类设置出错:%@",[error localizedDescription]);
}
//激活会话
if(![session setActive:YES error:&error])
{
NSLog(@"音频会话分类设置出错:%@",[error localizedDescription]);
}
/**
接收中断通知
*/
-(void)notification_Interrupt:(NSNotification *)sender
{
NSDictionary * dic_info = sender.userInfo;
AVAudioSessionInterruptionType type = [dic_info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if(type == AVAudioSessionInterruptionTypeBegan)
{
//表示中断开始
//TODO.. 处理中断后的操作,系统会自动停止音频,在这里可以实现工作:保存播放状态,更改UI状态为暂停状态等。
}
else if(type == AVAudioSessionInterruptionTypeEnded)
{
//表示中断结束
AVAudioSessionInterruptionOptions options = [dic_info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];
if(options == AVAudioSessionInterruptionOptionShouldResume)
{
//表示可以继续播放
//TODO...系统不回自动恢复播放,在这里可以实现工作:自动播放,更改UI状态为播放状态。
}
}
}
/**
接收线路变换通知
@param sender <#sender description#>
*/
-(void)notification_routeChange:(NSNotification *)sender
{
NSDictionary * dic_info = sender.userInfo;
AVAudioSessionRouteChangeReason reason = [dic_info[AVAudioSessionRouteChangeReasonKey] unsignedIntegerValue];
if(reason == AVAudioSessionRouteChangeReasonNewDeviceAvailable)
{
//表示有新设备接入
//TODO...
}
else if(reason == AVAudioSessionRouteChangeReasonOldDeviceUnavailable)
{
//表示有设备拔出
//拔出之前的线路信息
AVAudioSessionRouteDescription * previousRoute = dic_info[AVAudioSessionRouteChangePreviousRouteKey];
//拔出之前的输出线路
AVAudioSessionPortDescription * previousOutput = previousRoute.outputs[0];
NSString * portType = previousOutput.portType;//端口类型
if([portType isEqualToString:AVAudioSessionPortHeadphones])
{
//表示是耳机设备拔出,根据人性化根据,耳机拔出后,正在播放的声音应停止播放
//TODO..
}
}
}