无法在接听来电后重新启动录音

15

我已经为录制音频添加了一个中断通知的观察者。

当执行呼出电话、接收来电但接听、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和一个独立的类,但似乎没有帮助。

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

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

我有两个解决方案,尽管你应该展示如何配置你的音频会话并描述与CTCallCenter相关的“暴力崩溃”。 - Rhythmic Fistman
1个回答

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

这一切都不需要后台任务或者切换到独占音频会话。延迟是为了解决电话结束通知在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.

你没有展示你的AVAudioSession是如何配置的,但是你没有收到AVAudioSessionInterruptionTypeEnded通知表明你没有使用一个独占的音频会话(即你正在设置为“与其他人混合”):

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

过时的CTCallCenter和更新的CXCallObserver回调似乎发生得太早了,在中断结束之前,也许经过进一步的努力,您可以找到一种方法,在通话结束后重新启动录制的后台任务(我最初的尝试失败了)。
所以,目前我知道的唯一重新启动录制的方法是:
使用独占音频会话(这会给您一个结束中断的机会):
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;

这个解决方案的部分内容是从音频会话编程指南中的用户控制回放和录制应用程序的音频指南文档中精选出来的。

p.s. 也许锁屏集成或非独占音频会话对您来说是致命问题,但在其他情况下,例如启动另一个独占应用程序(尽管使用mixWithOthers可能不会出现此问题),您将永远不会遇到终端中断,因此也许您应该选择代码气味延迟解决方案。


其中一个问题确实是mixWithOthers设置,所以我已经将其删除。您知道在使用此功能时未收到ended回调的原因吗?我尝试了延迟,但只有10次中的1次有效。我已经删除了mixWithOthers选项,现在它似乎可以正常工作了。 - Thizzer
我不知道其背后的逻辑 - mixWithOthers 似乎经验性地将开始时的中断降至只有来电声(也许?没有检查闹钟),并消除了结束时的中断。听到延迟的消息很遗憾,没能有太多机会去测试。iOS 音频是神秘的黑魔法,在这些重要情况下的文档很少,你只需要尝试一切。 - Rhythmic Fistman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接