ios AVAudioPlayer rings in background until something else turns it off? - ios

So I successfully created an app, in this case it's a VOIP app using linphone sip library. I was doing some tests where I want a WAV file to always play, regardless of whether my app is in the foreground or background. I was able to successfully implement this test with the following code:
NSString * resourcePath = [NSString stringWithFormat:#"%#/myres/sounds/oldphone-mono.wav",[[NSBundle mainBundle] resourcePath]];
ringer = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:resourcePath] error:nil];
[ringer setNumberOfLoops:100];
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[ringer setVolume:1.0];
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride);
[ringer play];
I think this is pretty straightforward code, because I copied it from another stack overflow answer. I also had to do something to my plist file to allow for audio background playing.
So the problem is that a function from some 3rd party software (in this case LinPhone SIP Library's Receive Incoming Call Function) that when fired will prevent my app from playing my sound file in background.
As an example, here's a test case:
start up my app and i hear the ringing wav file, that's good
i put the app in background mode and i still hear the ringing wav file, that's good
i bring the app back into focus, i still hear the ringing, that's good
i trigger the linphone receive call function, the ringing still continues because i'm in foreground, that's good
i put the app in background mode, the ringing stops, that's not good
So I suspect that linphone has done something to the avaudioplayer/avaudiosession, such that i can no longer play in background. So my question is, can anyone hazard a guess as to what linphone may have done to prevent my app from playing sound in the background, and how i might get around this issue?
ADDITIONAL NOTES
I even tried to instantiate a new avaudioplayer every second while the app was in the background. But it couldn't override whatever linphone has done to silence the playing of audio.

It appears that Linphone messed with the AVAudioSession Category Options. I had to reset the category option to AVAudioSessionCategoryOptionMixWithOthers in order to get around the problem like so:
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil];

Related

AVAudioSession mix with WKWebview

I am working on a VoIP app. For that I had set the category of AVAudioSession to AVAudioSessionCategoryPlayAndRecord.
All the app functionality was working fine till now.
Then we had a new requirement where within app when the voice call is going on, user can play one video embedded in WKWebView.
Now when user plays video from WKWebView, video plays successfully and the volume of video is also as expected. But when user stops/pauses the video then voice call gets disconnected.
So I came to know that WKWebView runs in different process than the app, so in order to make my app's audio mixable with WKWebview I have to set the AVAudioSession as mixwithOthers.
I have done that with the following code...
AVAudioSession *session = [AVAudioSession sharedInstance];
NSError *errorInSettingCategory;
BOOL success = [session setCategory:AVAudioSessionCategoryPlayAndRecord withOptions: AVAudioSessionCategoryOptionDefaultToSpeaker | AVAudioSessionCategoryOptionMixWithOthers | AVAudioSessionCategoryOptionAllowBluetooth error:&errorInSettingCategory];
Now after playing video form WKWebView I am able continue with my voice call.
But this approach introduced the new bug.
As now the AVAudioSession is mixable, volume of the video from WKWebView is quite low and app's audio (voice call audio) is quite dominant.
I have tried different setcategory options but with no luck.
I want to have volume of app's audio and volume of WKWebView's video at same level.
Thank you for any help.
Try setting the duckOthers property of your AVAudioSession to false:
https://developer.apple.com/documentation/avfoundation/avaudiosession/categoryoptions/1616618-duckothers

iPhone: Play sound when in Silent mode iOS 10

I've read many posts and articles and am having no luck playing audio when iPhone is in silent. I'm using react-native-sound which uses AVAudioPlayer under the hood. The following calls succeed without any luck with sound coming through when in silent.
NSError *setCategoryError = nil;
BOOL success = [audioSession setCategory:AVAudioSessionCategoryPlayback error:&setCategoryError];
if (!success) { /* handle the error condition */ }
NSError *activationError = nil;
success = [audioSession setActive:YES error:&activationError];
if (!success) { /* handle the error condition */ }
Any suggestions?
Resources:
https://developer.apple.com/library/content/qa/qa1668/_index.html
I am struggling with this as well. I don't think it is totally possible. I can have sound play in silent mode when the app is in foreground then continue playing while it is in the background, but I don't think iOS allows you to trigger a sound to play while the app is in the background and in silent mode. I am using local notifications to trigger the sound to play.
Some people have had luck setting 'Application does not run in background' to YES in the info.plist which keeps the app in the foreground when someone locks there phone with the app still in the foreground but I need the app to work if the phone is locked when the app is in the background.
Currently the only solutions I know of are to play a silent audio track then tell it to keep it active after playing (to not waste cpu/battery). See link here. Apple normally doesn't approve of this in app review. Or to use location updates.

Do not allow other apps play music

We are developing a VOIP application and while there is an active call, we want not to allow other apps play music or something (like Whatsapp does). Is there any way of doing it in a normal way, can you help please?
Edit 1: I think we need to start with listening the observer AVAudioSessionInterruptionNotification.
Accepted Answer Edit 2:
When sound interruption begins, try to only active the AudioSession again. This works. Thank you KudoCC.
Here is a link talks about audio category, there is a table in it:
As your app supports VoIP, so it need to play and record audio, and you need interrupt non-mixable apps audio, so AVAudioSessionCategoryPlayAndRecord is the right category. Also you should re-active your audio session when you're playing/recording audio so that the category takes effect.
Try this code in your app to pause the background audio when calls,
[[AVAudioSession sharedInstance] setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error];
It is helpful when other apps uses AVAudioPlayer.
For more detail refer this Link

iOS 7+ AVAudioPlayer from phone's Internal Ear Piece

I'm trying to figure how i can play audio using AVAudioPlayer to the phone's internal ear piece.
When headphones are connected the audio should play on those else it must play from internal ear piece (AVAudioPlayer should never play from Phone's Bottom Speaker).
Is there any way to achieve this? iOS 7+ answers are welcome!
It's interesting to see how many more answers there are regarding Android questions, iOS questions are much less answered.
This is the solution that i managed to find:
NSError *error;
// Initialize Audio Player
_player = [[AVAudioPlayer alloc]initWithContentsOfURL:[NSURL fileURLWithPath:_audioPath] error:&error];
// Set Audio Session to "Play and Record", UNFORTUNATELY this is the only way to play
// from the internal speaker. It asks the user to grant Recording permission to the app.
NSError *setCategoryError = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error: &setCategoryError];
[[AVAudioSession sharedInstance] setActive: YES error: nil];
if (setCategoryError) {
CLS_LOG(#"Error routing audio: %#",setCategoryError);
[self.navigationController popViewControllerAnimated:YES];
}
[_player prepareToPlay];
// And now you can play from the internal speaker.
You can see that i had to use AVAudioSessionCategoryPlayAndRecord because it's the only category that routes the audio to the receiver (the small speaker you hold to your ear when on a phone call) see the audio session programming guide for reference.
Actually, there is a method named overrideOutputAudioPort:error: that looks promising but it can only be used AVAudioSessionCategoryPlayAndRecord to force the device to use the external speaker, and not vice versa.

iOS 7 Bluetooth Output not working for AVAudioSessionCategoryPlayAndRecord

In my app, if iPhone is connected to Bluetooth headset, play sound with Bluetooth. Else use default speaker.
Here's my code
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionAllowBluetooth error:nil];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeVoiceChat error:nil];
[[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
However, when I tried to use category setCategory:AVAudioSessionCategoryPlayAndRecord. Route to bluetooth device stopped working.. I don't know why. Because by right, the bluetooth option should only work for Record or PlayAndRecord category...
/* only valid with AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayAndRecord */
Addition: Can I have bluetooth for output ONLY (input still use default Mic from phone)?
UPDATE
I changed the order by calling setMode first, followed by setCategory. It works for the first time running the application (a call). When I tried to do the same action again, currentRoute still gave me output = Bluetooth but the actual output returns to default phone front speaker.
It has been a while since I post this question and I kind forgot what exactly the problem is... If I remembered correctly it's due to another lib that kept rewriting the output route. somehow I fixed it and made it work the way I want.
I contributed the code to Jawbone's AudioSessionManager lib. Hope that helps.

Resources