Подтвердить что ты не робот

Не удалось перезапустить запись после ответа на входящий телефонный звонок

Я добавил наблюдателя для уведомления об прерывании при записи звука.

Это прекрасно работает при выполнении исходящего вызова, входящего вызова и ответа не, 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 и отдельным классом, но это не помогает.

Edit Это то, что я пытался использовать CTCallCenter, но это очень шелушащийся. Когда вызов recorder->resume() вызывается из обратного вызова, он либо работает, либо сильно сбой, либо ничего не делает, пока приложение не будет снова возвращено на передний план вручную.

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

Ответ 1

UPDATE
Если вы ненадолго дожидаетесь секунды или около того, вы можете перезапустить запись с вашего 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 Пока есть ссылка на "наводящую на размышления" документацию для этого решения, в основном это объясняется появлением этих двух ошибок и сопровождающих их комментариев в файлах заголовков:

AVAudioSessionErrorCodeCannotInterruptOthers
   The app 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 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. возможно, интеграция экрана блокировки или неэксклюзивная аудиосессия - это разрывы транзакций для вас, но есть и другие ситуации, когда вы никогда не получите прерывания конца, например. запуск другого эксклюзивного приложения, такого как Music (хотя это может быть не проблема с mixWithOthers, так что, возможно, вам стоит пойти с решением запаздывания кода.