Is there anyway to detect an incoming Facetime call in ios. I tried CTCallCenter but it seems like it only works with cellular calls and not facetime calls.. I am using following code to detect facetime call but no success.
self.callCenter = [[CTCallCenter alloc] init];
self.callCenter.callEventHandler = ^(CTCall* myCall) {
NSString *call = myCall.callState;
if ([call isEqualToString:CTCallStateDisconnected])
NSLog(#"Call has been disconnected");
else if([call isEqualToString:CTCallStateDialing])
NSLog(#"Call start");
else if ([call isEqualToString:CTCallStateConnected])
NSLog(#"Call has just been connected");
else if([call isEqualToString:CTCallStateIncoming])
NSLog(#"Call is incoming");
else
NSLog(#"None");
};
Any help?
You don't need an app-specific detection for every app that needs pausing your playback. You should use AVAudioSession to detect any kind of audio interruption and notify your music player to pause.
See this image and explanation from Apple's documentation:
AVAudioSession gives you control your app’s audio behavior. You can:
Select the appropriate input and output routes for your app
Determine how your app integrates audio from other apps
Handle interruptions from other apps
Automatically configure audio for the type of app your are creating
So, you can use the AVAudioSession API to handle any incoming calls that can be a cellular call or a FaceTime one, or even anything third-party like Viber, Tango, Line, etc.
You can also check the AddMusic sample app to see how it's implemented.
Related
We are building voip based application and we have one scenario where we have to identify whether active call is voip call or cellular call(cs call). Before iOS10 and before callkit we used to check through CTCallCenter like below code snap.
- (BOOL)nativeCallPresent
//This only works before callkit and ios 10,
//If iOS is greater or equal 10 then it always return yes for CS and voip call both.
CTCallCenter * callcenter = [[CTCallCenter alloc] init];
BOOL nativeCallPresent = ([callcenter currentCalls] != nil);
return nativeCallPresent;
}
I checked in apple callkit but did not find any way to check that active call is cellular call or voip call.
Can somebody from apple or developer community can help here?
Thanks.
I am working on an app that allows video calls using an SDK that utilises webRTC on iOS.
Intended functionality is that if another call is instantiated after my app has instantiated a call, I want my app to mute audio in both directions in my call until that call has ended, in which case audio is restored.
I have encountered a problem where I want to detect other calls that make VOIP calls (for example WeChat and Facebook Messenger).
In the case of WeChat I have solved this exploiting that it interrupts the shared audio session (of AVAudioSession). The code that handles this is as follows:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector
(audioSessionInterrupted:)
...
name:AVAudioSessionInterruptionNotification object:nil];
- (void) audioSessionInterrupted:(NSNotification*)notification
{
if(notification.name == AVAudioSessionInterruptionNotification)
{
NSDictionary* userInfo = notification.userInfo;
int result = userInfo[AVAudioSessionInterruptionTypeKey];
if(result == AVAudioSessionInterruptionTypeBegan)
{
[[AVAudioSession sharedInstance] setActive:NO error:nil];
[self setAudioEnabled:NO];
}
else if (result == AVAudioSessionInterruptionTypeEnded)
{
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[self setAudioEnabled:YES];
}
}
}
However, for Facebook Messenger, this method is never called. I speculate from this that WeChat may be demanding exclusive access to the shared audio session (and thus causing an interruption of the audio session with my app) whereas Facebook Messenger chooses to mix its audio, or uses a separate audio session when a call is instantiated.
My question is does there exist another way of detecting other VOIP calls, possibly using the CallKit framework? My app uses CallKit to prompt user for incoming calls, and records ingoing/outgoing calls in the iOS phone log.
I would recommend checking all current native calls. All calls that are registered through CallKit are also considered native calls and trigger a call on this object, from CoreTelephony:
CTCallCenter *callCenter = [[CTCallCenter alloc] init];
callCenter.callEventHandler = ^(CTCall* call) {
//Native call changes are triggered here
};
For detecting VOIP calls from applications that do not support CallKit, this is harder. A possibility is listening to changes to the AVAudioUnit.
I have an app that speaks to the user and listens to the user's speech response. I've noticed that when I plug my phone into my car audio system and use the app, when my app is done speaking, it receives an interruption notification and the Music app starts playing music instead of allowing my app to continue.
This doesn't happen if the phone is not attached to an external device, and this doesn't happen the moment I plug the phone in, only when the speech stops and the phone is playing through the car. I have done some testing and determined that this behavior appears when I call the beginReceivingRemoteControlEvents method on my application. If I don't sign up for remote control events when my application loads, the problem does not occur, but I cannot display 'now playing' information for my audio or use the car's controls for controlling playback.
Has anyone found a way to listen for remote control events without forfeiting control of the device's audio playback?
This is often caused by the car stereo rather than your iOS device. Check the stereo's manual and switch it from Audio Mode to iPod Mode (or whatever your manual names these options). Basically, your car stereo is listening for the track ended notification and using that to trigger a 'play next track' notification. This calls the MPMusicPlayer which usually picks the first track alphabetically in your device's library. It could be that there is a workaround in software but I've found that the easiest thing is to change the setting on the car stereo.
Use the following to disable remote control events (you may have to replace togglePlayPauseCommand with playCommand, or do both):
MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.togglePlayPauseCommand addTargetWithHandler:^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent * _Nonnull event) {
NSLog(#"toggle button pressed");
return MPRemoteCommandHandlerStatusSuccess;
}];
or, if you prefer to use a method instead of a block:
[commandCenter.togglePlayPauseCommand addTarget:self action:#selector(toggleButtonAction)];
To stop:
[commandCenter.togglePlayPauseCommand removeTarget:self];
or:
[commandCenter.togglePlayPauseCommand removeTarget:self action:#selector(toggleButtonAction)];
You'll need to add this to the includes area of your file:
#import MediaPlayer;
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!
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.
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... :-)
I have a requirement where my app connects to a country channel (USA) and starts playing records from the channel. This is basically a channel which is run by users, the users upload their records to channel and they are played one by one. The user who connects to channel they start listening to channel.
The server sends the iOS app the URLs for the record that needs to be played via sockets, the iOS app creates AVQueuePlayer to play the URL's (using AVPlayerItems) one by one.
If I keep app in background when the channel is full of records for almost 1 day or so, the app keep running and keep playing all the records one by one. I know that AVQueuePlayer takes care of running the app all the time without killing as it receives new player items to play.
But if there are no records in channel and if user connects to channel, then app doesn't play the records in background if the idle time of the app exceeds 10 minutes.
I have written code with background task identifier which keeps my socket connect open so that new record URLs can be received all the time.
I see some of the crash reports in my device which says "AppName(my app) has active assertions beyond permitted time"
So can I know what wrong is going on here.
I am posting the background task code as well
- (void)keepBroadcastPersistentConnection {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
if(self._bgTaskIdentifier)
self._bgTaskIdentifier = UIBackgroundTaskInvalid;
self._bgTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:self._bgTaskIdentifier];
self._bgTaskIdentifier = UIBackgroundTaskInvalid;
CGLog(#"========================================end bg task at time %#", [NSDate date]);
CGLog(#"Time taken by app to run in bg is %f seconds", [[NSDate date] timeIntervalSinceDate:self.date]);
}];
[[BroadcastSocketConnecter sharedSocketConnecter].socketIO sendHeartbeat]; // this keep the socket alive
self.date = [NSDate date];
CGLog(#"========================================begin bg task at time %#", self.date);
});
}
Thanks
From the audio session programming guide:
Why a Default Audio Session Usually Isn’t What You Want
Scenario 3. You write a streaming radio application that uses Audio
Queue Services for playback. While a user is listening, a phone call
arrives and stops your sound, as expected. The user chooses to ignore
the call and dismisses the alert. The user taps Play again to resume
the music stream, but nothing happens. To resume playback, the user
must quit your application and restart it.
To handle the interruption of an audio queue gracefully, implement
delegate methods or write an audio session callback function to allow
your application to continue playing automatically or to allow the
user to manually resume playing. See “Responding to Audio Session
Interruptions.”
Shortly, the solution would be to implement the AVAudioSessionDelegate protocol'a beginInterruption and endInterruption methods. However, the delegate property of the AvAudioSession class was deprecated in iOS6 and Notifications should be used instead. Namely, you are interested in the AVAudioSessionInterruptionNotification
Solution. According to this story if the playback stops, then you should explicitly activate the audio session again to prevent your app from being terminated.
Below is the source for the delegate implementation but the logic doesn't change much with the notifications so I feel it's still a good source for info.
- (void) beginInterruption {
if (playing) {
playing = NO;
interruptedWhilePlaying = YES;
[self updateUserInterface];
}
}
NSError *activationError = nil;
- (void) endInterruption {
if (interruptedWhilePlaying) {
BOOL success = [[AVAudioSession sharedInstance] setActive: YES error: &activationError];
if (!success) { /* handle the error in activationError */ }
[player play];
playing = YES;
interruptedWhilePlaying = NO;
[self updateUserInterface];
}
}
Old response which is still valid but not an elegant solution
You cannot start playing audio in the background. This answer explains what I mentioned in my comment above: https://stackoverflow.com/a/16568437/768935 Doing tricks with the AudioSession does not seem to have an effect on this policy.
As a solution, you need to keep playing audio. If there is no item in the queue, then insert the "silence" audio track. However, I have my doubts that the app with this trick will be admitted in the App Store. It may be better to inform the user that audio playback will be resumed on if the app is started again.