AVAudioRecorder - как восстановить входящий звонок - PullRequest
0 голосов
/ 13 января 2019

Я пытаюсь достичь чего-то, что, как я думал, будет супер легким, но оказывается, что это не так.

Я играю с исходным кодом библиотеки "Reaction-native-audio". Но вы можете предположить, что я работаю изначально ради этого вопроса.

Вот ссылка на исходный код , с которой я играю.

Моя цель проста, я использую AVAudioRecorder, чтобы записать встречу (примерно 30 минут). В случае входящего звонка в середине записи я бы хотел, чтобы мое приложение могло «восстановиться», выполнив одно из следующих действий:

1) «приостановить» запись «входящих вызовов» и «возобновить», когда приложение вернется на передний план.

2) при входящем звонке - закрыть текущий файл, а когда приложение вернется на передний план, начать новую запись (часть 2) с новым файлом.

Очевидно, вариант (1) предпочтителен.

Обратите внимание, что я хорошо знаю, что использую AVAudioSessionInterruptionNotification и использую его в своих экспериментах, но пока не повезло, например:

- (void) receiveAudioSessionNotification:(NSNotification *) notification
{
    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
        NSLog(@"AVAudioSessionInterruptionNotification");
        NSNumber *type = [notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey];

        if ([type isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
            NSLog(@"*** InterruptionTypeBegan");
            [self pauseRecording];
        } else {
            NSLog(@"*** InterruptionTypeEnded");
            [_recordSession setActive:YES error:nil];            
        }
    }
}

Обратите внимание, я назначу вознаграждение за этот вопрос, но единственным приемлемым ответом будет реальный рабочий код, а не то, что "должно работать в теории". Большое спасибо за помощь:)

1 Ответ

0 голосов
/ 16 января 2019

Я выбрал AVAudioEngine и AVAudioFile в качестве решения, потому что код короткий, а обработка прерываний AVFoundation особенно проста (ваши объекты проигрывателя / рекордера приостановлены, и бездействие возобновляет ваш аудио сеанс).

N.B AVAudioFile не имеет явного метода закрытия, вместо этого пишется заголовок и закрывается файл во время dealloc выбор, который, к сожалению, усложняет то, что в противном случае было бы простым API.

@interface ViewController ()

@property (nonatomic) AVAudioEngine *audioEngine;
@property AVAudioFile *outputFile;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    AVAudioSession *session = [AVAudioSession sharedInstance];
    NSError *error;
    if (![session setCategory:AVAudioSessionCategoryRecord error:&error])  {
        NSLog(@"Failed to set session category: %@", error);
    }

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioInterruptionHandler:) name:AVAudioSessionInterruptionNotification object:nil];

    NSURL *outputURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask][0] URLByAppendingPathComponent:@"output.aac"];

    __block BOOL outputFileInited = NO;

    self.audioEngine = [[AVAudioEngine alloc] init];

    AVAudioInputNode *inputNode = self.audioEngine.inputNode;

    [inputNode installTapOnBus:0 bufferSize:512 format:nil block:^(AVAudioPCMBuffer *buffer, AVAudioTime * when) {
        NSError *error;

        if (self.outputFile == nil && !outputFileInited) {
            NSDictionary *settings = @{
               AVFormatIDKey: @(kAudioFormatMPEG4AAC),
               AVNumberOfChannelsKey: @(buffer.format.channelCount),
               AVSampleRateKey: @(buffer.format.sampleRate)
            };

            self.outputFile = [[AVAudioFile alloc] initForWriting:outputURL settings:settings error:&error];

            if (!self.outputFile) {
                NSLog(@"output file error: %@", error);
                abort();
            }

            outputFileInited = YES;
        }

        if (self.outputFile && ![self.outputFile writeFromBuffer:buffer error:&error]) {
            NSLog(@"AVAudioFile write error: %@", error);
        }
    }];

    if (![self.audioEngine startAndReturnError:&error]) {
        NSLog(@"engine start error: %@", error);
    }

    // To stop recording, nil the outputFile at some point in the future.
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(20 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"Finished");
         self.outputFile = nil;
    });
}

// https://developer.apple.com/library/archive/documentation/Audio/Conceptual/AudioSessionProgrammingGuide/HandlingAudioInterruptions/HandlingAudioInterruptions.html
- (void)audioInterruptionHandler:(NSNotification *)notification {
    NSDictionary *info = notification.userInfo;
    AVAudioSessionInterruptionType type = [info[AVAudioSessionInterruptionTypeKey] unsignedIntegerValue];

    switch (type) {
        case AVAudioSessionInterruptionTypeBegan:
            NSLog(@"Begin interruption");
            break;
        case AVAudioSessionInterruptionTypeEnded:
            NSLog(@"End interruption");
            // or ignore shouldResume if you're really keen to resume recording
            AVAudioSessionInterruptionOptions endOptions = [info[AVAudioSessionInterruptionOptionKey] unsignedIntegerValue];
            if (AVAudioSessionInterruptionOptionShouldResume == endOptions) {
                NSError *error;
                if (![self.audioEngine startAndReturnError:&error]) {
                    NSLog(@"Error restarting engine: %@", error);
                }
            }
            break;
    }
}
@end

N.B. Вы, вероятно, хотите включить фоновое аудио (и, конечно, добавить строку NSMicrophoneUsageDescription в свой Info.plist).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...