在我使用Linphone进行VoIP通话的应用程序上进行了调用。我正在使用CallKit接听来电,以便为我的应用提供原生触摸。
iOS 11之前的所有功能都很好。问题出现在Audio会话的创建上。第一次它工作正常但在后续运行中它无法在耳机中发出声音。确切的问题是与音频会话有关的不一致意味着我无法使用麦克风来捕捉语音而另一端的用户无法听到我的声音。
我注意到它在创建AVSession的共享实例并设置类别类型之后触发AVAudioSessionRouteChangeNotification,并且在AVAudioSessionRouteChangeReasonCategoryChange的情况下进入switch-case。当我打印类别类型时 - 它给出了右侧选定的名为AVAudioSessionCategoryPlayAndRecord的类别。
让我们知道如果我在用于AVAudioSessionRouteChangeReasonCategoryChange通知的方法中的switch-case中设置断点并打印命名的类别那么它工作正常。可能就在那时它有足够的时间来创建音频会话并通过麦克风捕捉声音。
使用以下代码设置AudioBox示例中给出的音频会话 -
- (void)setupAudioSession
{
try {
// Configure the audio session
AVAudioSession *sessionInstance = [AVAudioSession sharedInstance];
// we are going to play and record so we pick that category
NSError *error = nil;
[sessionInstance setCategory:AVAudioSessionCategoryPlayAndRecord error:&error];
XThrowIfError((OSStatus)error.code, "couldn't set session's audio category");
// set the mode to voice chat
[sessionInstance setMode:AVAudioSessionModeVoiceChat error:&error];
XThrowIfError((OSStatus)error.code, "couldn't set session's audio mode");
// set the buffer duration to 5 ms
NSTimeInterval bufferDuration = .005;
[sessionInstance setPreferredIOBufferDuration:bufferDuration error:&error];
XThrowIfError((OSStatus)error.code, "couldn't set session's I/O buffer duration");
// set the session's sample rate
[sessionInstance setPreferredSampleRate:44100 error:&error];
XThrowIfError((OSStatus)error.code, "couldn't set session's preferred sample rate");
// add interruption handler
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleInterruption:)
name:AVAudioSessionInterruptionNotification
object:sessionInstance];
// we don't do anything special in the route change notification
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleRouteChange:)
name:AVAudioSessionRouteChangeNotification
object:sessionInstance];
// if media services are reset, we need to rebuild our audio chain
[[NSNotificationCenter defaultCenter] addObserver: self
selector: @selector(handleMediaServerReset:)
name: AVAudioSessionMediaServicesWereResetNotification
object: sessionInstance];
}
catch (CAXException &e) {
NSLog(@"Error returned from setupAudioSession: %d: %s", (int)e.mError, e.mOperation);
}
catch (...) {
NSLog(@"Unknown error returned from setupAudioSession");
}
return;
}
以下是针对AVAudioSessionRouteChangeNotification通知触发的方法 -
- (void)handleRouteChange:(NSNotification *)notification
{
UInt8 reasonValue = [[notification.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] intValue];
AVAudioSessionRouteDescription *routeDescription = [notification.userInfo valueForKey:AVAudioSessionRouteChangePreviousRouteKey];
NSLog(@"Route change:");
switch (reasonValue) {
case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
NSLog(@" NewDeviceAvailable");
break;
case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
NSLog(@" OldDeviceUnavailable");
break;
case AVAudioSessionRouteChangeReasonCategoryChange:
NSLog(@" CategoryChange");
NSLog(@" New Category: %@", [[AVAudioSession sharedInstance] category]);
[self setupAudioSession];
break;
case AVAudioSessionRouteChangeReasonOverride:
NSLog(@" Override");
break;
case AVAudioSessionRouteChangeReasonWakeFromSleep:
NSLog(@" WakeFromSleep");
break;
case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
NSLog(@" NoSuitableRouteForCategory");
break;
default:
NSLog(@" ReasonUnknown");
}
NSLog(@"Previous route:\n");
NSLog(@"%@", routeDescription);
}