接听来电 phone 后无法重新开始录音

Unable to restart recording after answering incoming phone call

录音时的中断通知我添加了观察者

这在执行拨出电话、接听电话和接听、Siri 等时工作正常。

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

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

我已尝试使用 CTCallCenter 来检测通话何时开始,但我无法从该回调重新开始录音。

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

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

CFNotificationCenterAddObserver(
                CFNotificationCenterGetLocalCenter(),
                this,
                &handle_interrupt,
                (__bridge CFStringRef) AVAudioSessionInterruptionNotification,
                NULL,
                CFNotificationSuspensionBehaviorDeliverImmediately );

...

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;
        }
    }
}

我已经尝试将通知绑定到 AppDelegate、UIViewController 和单独的 class,但这似乎没有帮助。

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

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

更新
如果你笨拙地等待一秒钟左右,你可以从你的 callEventHandler 重新开始录制(虽然你没有描述你的暴力崩溃,但它对我来说很好):

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

这一切都没有后台任务或更改为独占音频会话。延迟解决了这样一个事实,即呼叫结束通知在 Phone.app 停用其音频会话之前出现并且 1 秒似乎小到足以进入某种后台取消安排宽限期。我已经记不清其中假设了多少实现细节,所以也许你想继续阅读

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

N.B 虽然此解决方案有 link 到 "suggestive" 文档,但主要是由弹出的这两个错误和他们在头文件中的附带注释:

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.

您没有显示您的 AVAudioSession 是如何配置的,但您没有收到 AVAudioSessionInterruptionTypeEnded 通知这一事实表明您没有使用独占音频会话(即您重新设置 "mix with others"):

[session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];

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

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

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

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

并与"Now Playing"集成(不是开玩笑,这是解决方案的结构部分):

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;

此解决方案的一部分是从 Audio Guidelines for User-Controlled Playback and Recording Apps 文档中挑选出来的。

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