张东轩的博客

合抱之木,生于毫末;九层之台,起于累土;千里之行,始于足下。

0%

Audio Session编程指南(二)

一、Audio Session的默认行为

  • 可以播放,但不能进行录制;
  • 静音开关设置为静音模式的时候,这个App的任何的音频播放都会被设置为静音;
  • 当设备锁屏时,这个app的音频会被静音;
  • 当App播放音频时,其他在后台播放的音频都会被静音;

二、配置Audio Session

在categories设置了Audio最基本的行为的同时,可以通过设置category的mode更进一步的设置Audio的行为。例如拥有Voice over IP (VoIP)功能的App,它会在使用AVAudioSessionCategoryPlayAndRecord的同时,将Audio Session的mode也设置为AVAudioSessionModeVoiceChat,这样可以通过系统级别的数字信号处理使音频信号得到提升。
某些categories支持使用设置options重写category默认的Audio行为,举例来讲,Category为AVAudioSessionCategoryPlayback的Audio Session 在active的时候,会打断系统其他的Audio,大多数场景我们都需要这样的表现,但是如果想和系统其他音频进行混播,可以通过设置Category的options为AVAudioSessionCategoryOptionMixWithOthers来进行实现,使用的方法为 setCategory:mode:options:error:

三、冲突的音频需求

同一时间内可能有多个app需要使用Audio device,系统这里做了一个比较形象的比。将使用Audio Device比作机场的跑道,将app比作正在飞机,系统服务作为塔台:

图中,第一步,你的App请求激活audio session;第二步,系统会判断你配置的category,这里你的app要求其他App进行静音;第三步和第四部,系统deactivates了音乐App的Audio Session,停止了它的音频播放;最后,系统激活了你的audio session,然后你就可以开始播放了;

四、Audio Session的激活和释放

虽然AVFoundation的播放和录音会自动激活Audio Session,但是手动激活可以让你知道active audio session是否成功。
闹钟、来电或者日历提醒的时候,系统会将你的App deactive掉,当用户dismiss或者挂断电话后,系统允许app再次active audio session,这个时候你可以决定是否将自己的AudioSession进行激活。

激活AudioSession的方法如下

1
2
3
4
5
6
7
// Configure your audio session category, options, and mode
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:0 error:nil];

// Activate your audio session to enable your custom configuration
NSError *outError = nil;
BOOL ret = [[AVAudioSession sharedInstance] setActive:YES error:&outError];

可以将setActive 的参数设置为NO来释放AudioSession。除了一些VoIP、录音这些App,大多数App并不需要对AudioSession进行显式的释放。

对拥有VoIP功能的App,要确保在后台待机运行的情况下不激活AudioSession,确保只有收到呼叫的时候才激活AudioSession;

对于使用recording category的App,要确保只有它在录音的时候才处于激活状态。在录音前和录音结束后,要将AudioSession设置为未激活态,这样其他功能的音频提示、消息才能被正确播放出来;

五、处理中断

为了让App的可以在AudioSesson被电话呼叫、闹钟和其他App的Active事件打断后正常工作,我们需要去监听AudioSession的打断事件。音频的打断来自于那些被激活且未配置mix with others的AudioSession,打断会使我们的AudioSession处于未激活状态,然后结束我们对Audio的使用。

以上图片演示了一个播放应用程序的AudioSessionz在中断之前、期间和之后的事件序列。

可以使用AVAudioSessionInterruptionNotification 来监听AudioSession的变化:

1
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:[AVAudioSession sharedInstance]];

在interrupt begin和interrupt end的时候做对应处理,当interruptType为AVAudioSessionInterruptionTypeEnded时,可以从userInfo中通过AVAudioSessionInterruptionOptionKey拿到option,当option的值为AVAudioSessionInterruptionOptionShouldResume时,可以重新激活AudioSession并开始进行record或者play。

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)handleInterruption:(NSNotification *)notification {
NSDictionary *info = notification.userInfo;
AVAudioSessionInterruptionType interruptType = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];

if (interruptType == AVAudioSessionInterruptionTypeBegan) {
// Interruption began, take appropriate actions (save state, update user interface)
} else if (interruptType == AVAudioSessionInterruptionTypeEnded) {
AVAudioSessionInterruptionOptions option = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
if (option == AVAudioSessionInterruptionOptionShouldResume) {
// reactive audio session & play
}
}
}

六、响应媒体服务重置

媒体服务通过共享服务进程来提供音频和其他多媒体功能。虽然很罕见,但是在你的App处于active的时候,媒体服务重置的情况还是有可能发生的。可以通过注册 AVAudioSessionMediaServicesWereResetNotification 来监听媒体服务被重置的情况。当这种情况发生时,你需要做一些处理:

  • 清理之前的audio对象(例如players、recorders、converters和audio queue),重新创建一个新的;
  • 重置所有正在跟踪的内部音频状态,包括AVAudioSession的所有属性;
  • 适当时,使用setActive:error:方法重新激活AVAudioSession实例;

Apps 无需重新注册AudioSession的notification,无需对AudioSession的属性进行 key-value 监控进行重置;