在接听来电后无法重新开始录制

Thy*_*hys 14 core-audio objective-c++ audiounit ios avaudiosession

我在录制音频时为中断通知添加了一个观察者.

这在执行拨出电话,接听来电而不是应答,Siri等时工作正常.

现在我的应用程序在后台运行,屏幕顶部有红色条,继续上述状态下的录制不是问题.

但是当我真正接听来电时.我收到另一个AVAudioSessionInterruptionTypeBegan通知,然后当我停止通话时,我从未收到通知AVAudioSessionInterruptionTypeEnded类型.

我已经尝试使用CTCallCenter来检测呼叫何时开始,但我无法从该回调重新开始录制.

有谁知道如何让中断机制与实际得到回答的来电一起工作?

这是我正在使用的代码(的一部分);

CFNotificationCenterAddObserver(
                CFNotificationCenterGetLocalCenter(),
                this,
                &handle_interrupt,
                (__bridge CFStringRef) AVAudioSessionInterruptionNotification,
                NULL,
                CFNotificationSuspensionBehaviorDeliverImmediately );
Run Code Online (Sandbox Code Playgroud)

...

static void handle_interrupt( CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo )
{
    au_recorder *recorder = static_cast<au_recorder*>( observer );

    NSNumber* interruptionType = [( ( __bridge  NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionTypeKey];
    switch ( [interruptionType unsignedIntegerValue] )
    {
        case AVAudioSessionInterruptionTypeBegan:
        {
            // pause recorder without stopping recording (already done by OS)
            recorder->pause();
            break;
        }
        case AVAudioSessionInterruptionTypeEnded:
        {
            NSNumber* interruptionOption = [( ( __bridge  NSDictionary* ) userInfo ) objectForKey:AVAudioSessionInterruptionOptionKey];

            if ( interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume )
            {
                recorder->resume();
            }
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我已经尝试将通知绑定到AppDelegate,UIViewController和一个单独的类,但这似乎没有帮助.

编辑 这是我尝试使用CTCallCenter,但这是非常不稳定的.当recorder->resume()从回调调用时,它会工作,剧烈崩溃或根本不做任何事情,直到应用程序再次手动放回到前台.

callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall *call)
{
   if ([call.callState isEqualToString:CTCallStateDisconnected])
   {
      recorder->resume();
   }
};
Run Code Online (Sandbox Code Playgroud)

Rhy*_*man 2

更新
如果您粗鲁地等待一秒钟左右,您可以从您的重新开始录制callEventHandler(尽管您没有描述您的暴力崩溃,但它对我来说效果很好):

if ([call.callState isEqualToString:CTCallStateDisconnected])
{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
        self.recorder->resume();
    });
}
Run Code Online (Sandbox Code Playgroud)

这一切都没有后台任务或更改为独占音频会话。延迟是围绕这样一个事实进行的:呼叫结束通知在 Phone.app 停用其音频会话之前发出,并且 1 秒似乎足够小,足以落入某种后台取消调度宽限期。我已经记不清这里假定了多少实现细节,所以也许您想继续阅读

似乎有文档支持的解决方案:

注意:虽然有一个指向此解决方案的“建议”文档的链接,但它主要是由弹出的这两个错误及其头文件中的附带注释引导的:

AVAudioSessionErrorCodeCannotInterruptOthers
   The app's audio session is non-mixable and trying to go active while in the background.
   This is allowed only when the app is the NowPlaying app.

AVAudioSessionErrorInsufficientPriority
   The app was not allowed to set the audio category because another app (Phone, etc.) is controlling it.
Run Code Online (Sandbox Code Playgroud)

您尚未显示您的AVAudioSession配置方式,但您没有收到AVAudioSessionInterruptionTypeEnded通知的事实表明您没有使用独占音频会话(即您正在设置“与其他人混合”):

[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
Run Code Online (Sandbox Code Playgroud)

过时的CTCallCenter和较新的CXCallObserver回调似乎发生得太早,在中断结束之前,也许通过一些进一步的工作,您可以找到一种方法来运行后台任务,在通话结束后不久重新启动录制(我最初的尝试失败了) )。

所以现在,我知道接到电话后重新开始录音的唯一方法是:

使用独占音频会话(这会给你一个结束中断):

if (!([session setCategory:AVAudioSessionCategoryPlayAndRecord error:&error]
      && [session setActive:YES error:&error])) {
    NSLog(@"Audio session error: %@", error);
}
Run Code Online (Sandbox Code Playgroud)

并与“正在播放”集成(不是开玩笑,这是解决方案的结构部分):

MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
    NSLog(@"PLAY");
    return MPRemoteCommandHandlerStatusSuccess;
}];

[commandCenter.pauseCommand addTargetWithHandler:^(MPRemoteCommandEvent *event) {
    NSLog(@"PAUSE");
    return MPRemoteCommandHandlerStatusSuccess;
}];
// isn't this mutually exclusive with mwo? or maybe that's remote control keys
// not seeing anything. maybe it needs actual playback?
NSDictionary* nowPlaying = @{
    MPMediaItemPropertyTitle: @"foo",
    MPMediaItemPropertyArtist: @"Me",
    MPMediaItemPropertyAlbumTitle: @"brown album",
};

[MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlaying;
Run Code Online (Sandbox Code Playgroud)

该解决方案的各个部分是从用户控制播放和录音应用程序音频指南文档中精心挑选的。

ps,锁屏集成或非独占音频会话可能会破坏您的交易,但在其他情况下,您永远不会遇到结束中断,例如启动另一个独占应用程序,例如音乐(尽管这可能不是问题)mixWithOthers,所以也许您应该采用代码气味延迟解决方案。