Audio interruption when iOS application is recordi

2020-02-26 11:20发布

问题:

My iPad sound application (iOS 7.1) is able to record while in background. Everything is ok as far as recording is not interrupted while in background. For example if one's has the (stupid?) idea to start listening music while recording something.

I tried to manage this interruption in different ways, with no success. The problem is that the

  - (void)audioRecorderEndInterruption:(AVAudioPlayer *)p withOptions:(NSUInteger)flags

is never fired when application was in background as the interruption occurred. I then tried, in another solution, to implement the AVAudioSessionInterruptionNotification and a handleInterruption: method as

- (void) viewDidAppear:(BOOL)animated
{
......
AVAudioSession *session=[AVAudioSession sharedInstance];
[[NSNotificationCenter defaultCenter] addObserver:self
                                         selector:@selector(handleInterruption:)
                                             name:AVAudioSessionInterruptionNotification
                                           object:session];
.......
}
// interruption handling

- (void)handleInterruption:(NSNotification *)notification
{
    try {
        UInt8 theInterruptionType = [[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] intValue];
        NSLog(@"Session interrupted > --- %s ---\n", theInterruptionType == AVAudioSessionInterruptionTypeBegan ? "Begin Interruption" : "End Interruption");

        .... MANAGE interruption begin    
        interruptionBeganWhileInBackgroundMode = TRUE;

        }            
        if (theInterruptionType == AVAudioSessionInterruptionTypeEnded) {

        .... MANAGE interruption end

        }
    } catch (CAXException e) {
        char buf[256];
        fprintf(stderr, "Error: %s (%s)\n", e.mOperation, e.FormatError(buf));
    }
}

In this case the handleInterruption: is fired when interruption begins, but it is not when interruption ends (shoulld be fired with theInterruptionType set to AVAudioSessionInterruptionTypeEnded)

To circumvent this problem, I decided to set a flag (interruptionBeganWhileInBackgroundMode) when interruption begins to get the application informed that an interruption has occurred when coming foreground. So that I can manage the end of interruption.

It may seem clever, but it is not! Here is why...

I tried two implementations.

Solution 1. Put a [recorder pause] in handleInterruption: when interruption begins, and manage a [recorder record] in the comingForeground: method when the interruption flag is set. In this situation, the recording is going on immediately when application comes back to foreground, BUT instead of resuming, the recorder erase the file and restart a new recording, so that everything recorded before interruption is lost.

Solution 2. Put a [recorder stop] in handleInterruption: when interruption begins, to save the file, to be sure to preserve the recorded data. This recording is saved, BUT when application is coming foreground, it stalls for about ten seconds before the user can interact again, as if there was a process (the file saving?) that keep the UI frozen.

Last point: I have exactly the same problems in iPhone version of this application: when application is foreground and a phone call occurs, everything goes OK. But when a phone call occurs as my application is background I see the same bad behaviour as in iPad's version.

I notice that Apple's Voice Memo app in iOS7 correctly manage these background interruptions (it stops and saves the recording) despite it displays a 00:00:00 file length when coming foreground. The iTalk application manages it perfectly, automatically resuming recording when coming foreground.

Did anybody find a workaround for the audio interruption management for backgrounded recording applications? I found plenty of people looking for that in many developer websites, but no answer... Thanks!

回答1:

I have been going through the same issue myself. It seems as if there is a bug in the iOS7 AVAudioRecorder in how it deals with interrupts. Instead of pausing as I believe the documentation says that is should, it closes the file. I have not been able to figure out what is stalling the app when it comes back to the foreground. In my case, I would see AVAudioRecorder finish (with the success flag set to NO), after 10 seconds.

I ended up rewriting the audio recorder using Audio Queues. I found some sample code here (git@github.com:vecter/Audio-Queue-Services-Example.git) that helped with setting it up in an Objective-C environment and the Apple SpeakHere demo has some code to handle the interrupt notifications.

Essentially, I am stopping the recording on interrupt began and opening an alert for the user to save the file. This alert is deferred until UIApplicationDidBecomeActiveNotification is passed if the interrupt started while the app was in the background.

One other thing to note, there seems to be a minor bug in Audio Queues that the AudioQueueStart method will return -50 sometimes. If you add

AudioSessionInitialize(NULL, NULL,nil,(__bridge  void *)(self));
UInt32 sessionCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory,
                        sizeof(sessionCategory),
                        &sessionCategory
                        );
AudioSessionSetActive(true);

Before any AudioQueue methods, the error goes away. These methods are marked as deprecated but seem to be necessary.



回答2:

Don't know / think this is going to be relevant to the original author of the topic, but here is my experience with it:

I ended up here because of the AVAudioSessionInterruptionNotification thing; the .began flavour came as expected, but the .ended one did not. In my case, it happened because I was using two AVPlayer instances: one to play music, and the other one to play silence while the first struggled to start streaming a new track (otherwise iOS would suspend my app while in background, if next track loading did not happen fast enough).

Turns out it's not the brightest solution, and it somehow messes up the notification system. Giving up the second AVPlayer (the silence playing one) resulted in .ended being triggered as expected. Of course, I found the answer / solution for the notification problem, but I'm now left with the old one... :-)