代码之家  ›  专栏  ›  技术社区  ›  Oded Regev

AvaudioRecorder-如何从来电中恢复

  •  2
  • Oded Regev  · 技术社区  · 6 年前

    我在努力实现一些我认为会非常容易但事实证明不是的事情。

    我在玩“react native audio”库的源代码。但你可以假设我是为这个问题而工作的。

    这里是一个 reference for the source code 我在玩。

    我的目标很简单,我用 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 回复  |  直到 6 年前
        1
  •  2
  •   Gordon Childs    6 年前

    我选择 AVAudioEngine AVAudioFile 因为代码简短,而且avfoundation的中断处理是 particularly simple (播放器/录音机对象已暂停,取消暂停将重新激活音频会话)。

    N.B 阿维多菲勒 没有显式的close方法,而是在 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
    

    注意,您可能希望启用背景音频(并添加 NSMicrophoneUsageDescription 当然了。