Resume AVAudio session after interruption - ios

Issue:
My application's audio is not resuming after an interruption (such as phone call or when I use other applications). This issue happens when my application is already in the background state before the interrupt occurs.
Code to handle interrupts:
- (void)handleInterruption:(NSNotification *) notification{
if (notification.name != AVAudioSessionInterruptionNotification || notification.userInfo == nil) {
return;
}
NSDictionary *info = notification.userInfo;
if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
if ([[info valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
NSLog(#"InterruptionTypeBegan");
} else {
NSLog(#"InterruptionTypeEnded");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
NSLog(#"playing audio");
self->_audioPlayer.numberOfLoops = -1;
[self->_audioPlayer play];
});
}
}
}
In Xcode console, I see logs in the following sequence but no audio is played:
InterruptionTypeBegan
InterruptionTypeEnded
playing audio
I would appreciate any suggestions and thoughts on this topic. Thank you.
EDIT:
I have noticed the same issue with many online music streaming apps such as Saavn and Spotify.

Related

Native incoming call is killing my VOIP app's access to audio

I'm developing a VOIP app that allows people to make VOIP calls. Whenever a user is in a VOIP call and receives a native call on their phone the audio of the VOIP app stops working.
I've read that one must reinitialize the AudioSession and I'm doing so with this block of code but this is not working. Any suggestions?
self.callCenter = [[CTCallCenter alloc] init];
[self handleCall];
- (void)handleCall
{
AVAudioSession *session = [AVAudioSession sharedInstance];
self.callCenter.callEventHandler = ^(CTCall *call){
if ([call.callState isEqualToString: CTCallStateConnected])
{
}
else if ([call.callState isEqualToString: CTCallStateDialing])
{
}
else if ([call.callState isEqualToString: CTCallStateDisconnected])
{
NSLog(#"Call ended");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[session setActive:YES error:nil];
});
}
else if ([call.callState isEqualToString: CTCallStateIncoming])
{
NSLog(#"Call received");
}
};
}
I have worked on VoIP App with SIP library using. If you are also using same library, while getting native call if your VoIP call already existed, then native call terminate your VoIP call audio session. So, while getting native call, just make "Mute" your SIP library and after ended the native call, just "UnMute" thats it.
pjsua_conf_adjust_rx_level(0 /* pjsua_conf_port_id slot*/, 0.0f); //for mute the audio
pjsua_conf_adjust_rx_level(0 /* pjsua_conf_port_id slot*/, 1.0f); //for unmute the audio.
I hope this will help you.

Audio Unit - Messing up recording

Sorry for a long introduction of the problem.
In my app I am using recording audio and set category to record
AVAudioSession *session = [AVAudioSession sharedInstance];
[session setCategory:AVAudioSessionCategoryRecord error:&err];
When an ad network shows interstitial ad, after that recording is all mute (there is no sound in recording). As this happens with users on live network only, I am not able to reproduce it at my end.
In the log I get ">>>> frameSizeChanged = 4096" from users which points to webkit code
https://github.com/WebKit/webkit/blob/master/Source/WebCore/platform/audio/ios/AudioDestinationIOS.cpp
This looks like some process is holding on to Audio Unit setup and not letting it go even after it is completed(freed / released).
Question: Is there a way to clean up audio unit setup by some other module? Just need to reset so that recording can work again in app after the ad is shown.
I have tried to capture "AVAudioSessionInterruptionNotification" but there is none. Any help is really appreciated
You need to handle your audio system getting interrupted. Many things can cause your audio to get interrupted:
- Receive a phone call while your app runs
- Other app running uses audio
- iOS needs to play a sound
- etc
Your app needs to detect this interruption as your app's audio session will be essentially muted. Once the interruption is done, you need to restart your session.
Set up your listener as the first step in your session setup
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(yourInterruptionListener:) name:AVAudioSessionInterruptionNotification object:nil];
In your interruption listener, handle the cases of getting interrupted and handle restarting your session
-(void) yourInterruptionListener:(NSNotification*) aNotification
{
NSLog(#"Interruption happened");
NSDictionary *interruptionDict = aNotification.userInfo;
NSNumber* interruptionTypeValue = [interruptionDict valueForKey:AVAudioSessionInterruptionTypeKey];
NSUInteger interruptionType = [interruptionTypeValue intValue];
if ( interruptionType == AVAudioSessionInterruptionTypeBegan)
{
// stop your audio session here
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *errorInAudio = nil;
[session setActive:NO error:&errorInAudio];
}
else if ( interruptionType == AVAudioSessionInterruptionTypeEnded )
{
// Your interruption ended, restart the session
// call or paste your session startup code here
}
else if ( interruptionType == AVAudioSessionInterruptionOptionShouldResume )
{
}
else
{
NSLog(#"Some other interruption");
}
}
If you need to simulate the behavior of your users, start your app then call your self from a different phone. It will definitely interrupt your audio.

AVAudioSession can't play sound after app re-becomes active

So I have a music app that uses an AVAudioSession to allow it to play when it is in the background. I use this call:
[audioSession setActive:YES
withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation
error:nil];
My problem now is if I go to another app and it steals the audio session (thus now stopping music playback from my app and playing something else), and I come back to my app, no matter what I do to reset my audio session or my audio units, my app's sound is gone.
Does anyone know what to do?
So after registering for the AVAudioSession notifications:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:aSession];
You need to resume/restart you need to restart your player in the handler interruption type is AVAudioSessionInterruptionTypeEnded:
- (void)handleAudioSessionInterruption:(NSNotification*)notification {
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
// • Audio has stopped, already inactive
// • Change state of UI, etc., to reflect non-playing state
} break;
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should continue playback.
[player play];
}
} break;
default:
break;
}
}
You can see a complete explanation here: AVplayer resuming after incoming call

How does app like heard works?

I want to make app which continuously records voice in background. App like heard does exactly that.
But I am stuck on interruptions. The app fail to resume recording when it faces interruptions.
I have used this for notification :
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleAudioSessionInterruption:)
name:AVAudioSessionInterruptionNotification
object:nil];
and to resume recording I have:
- (void)handleAudioSessionInterruption:(NSNotification*)notification {
NSNumber *interruptionType = [[notification userInfo] objectForKey:AVAudioSessionInterruptionTypeKey];
NSNumber *interruptionOption = [[notification userInfo] objectForKey:AVAudioSessionInterruptionOptionKey];
switch (interruptionType.unsignedIntegerValue) {
case AVAudioSessionInterruptionTypeBegan:{
// • Audio has stopped, already inactive
// • Change state of UI, etc., to reflect non-playing state
NSLog(#"Audio has stopped, already inactive");
NSLog(#"is recording: %#",recorder.isRecording?#"yes":#"no");
[self.recorder pause];
NSLog(#"is recording after pause: %#",recorder.isRecording?#"yes":#"no");
//[recorder pause];
} break;
case AVAudioSessionInterruptionTypeEnded:{
// • Make session active
// • Update user interface
// • AVAudioSessionInterruptionOptionShouldResume option
if (interruptionOption.unsignedIntegerValue == AVAudioSessionInterruptionOptionShouldResume) {
// Here you should continue playback.
//NSLog(#"completed");
NSLog(#"start recording again %d");
//NSLog(#"completed: %d",[recorder record]);
// Set the audio file
[self.recorder record];
NSLog(#"is recording after again : %#",recorder.isRecording?#"yes":#"no");
//[player play];
}
} break;
default:
break;
}
}
Add in the plist the UIBackgroundModes an 'audio' as Item 0. Hope it will help - at least it works for me when playing audio.

Stop video recording by pressing Home button

How do I safely stop video recording on AVFramework, when home button is pressed?
I want my app to work like the native camera app: when you press the home button during recording it stops the recording process and then goes to the background mode.
On my app delegate, I call [[videoController captureManager] stopRecording]; but in recordingDidFinishToOutputFileURL I get an error which says:
Stop any other actions using the recording device and try again.
I would suggest putting the call to stop your video recording in your app delegate's applicationWillResignActive: method. This method is triggered when your app moves from being active to being inactive, which, according to the UIApplicationDelegate docs, occurs:
... for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
The code would look something like this:
- (void)applicationWillResignActive:(UIApplication *)application
{
[[videoController captureManager] stopRecording];
// Do anything else before app becomes inactive
}
Try recovering recorded video...
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error
{
BOOL recordedSuccessfully = NO;
if ([error code] != noErr)
{
if(outputFileURL)
{
NSFileManager* manager = [NSFileManager defaultManager];
NSString* path = outputFileURL.path;
if([manager fileExistsAtPath:path])
{
NSError *attributesError = nil;
NSDictionary *fileAttributes = [manager attributesOfItemAtPath:path error:&attributesError];
NSNumber *fileSizeNumber = [fileAttributes objectForKey:NSFileSize];
long long fileSize = [fileSizeNumber longLongValue];
if(fileSize > 0)
{
NSLog(#"Recording finished with error, but trying to save whatever recorded");
// save whatever we have to camera roll.
}
}
}
}
}

Resources