How to implement mute functionality in a PJSIP call on iOS - iphone

I wanted to implement Mute button in my call. I am working on a VOIP application for iPhone. Now when a call comes and user picks up, I want to display a Mute button so the user can mute the call or conference. I did the same through the PJSIP API.
-(int) mutethecall
{
pj_status_t status = pjsua_conf_adjust_rx_level (0,0);
status = pjsua_conf_adjust_tx_level (0,0);
return (PJ_SUCCESS == status);
}
-(int) unmutethecall
{
pj_status_t status = pjsua_conf_adjust_rx_level (0,1);
status = pjsua_conf_adjust_tx_level (0,1);
return (PJ_SUCCESS == status);
}
The problem is that while this code is working for one to one call, it's not working for conference scenarios.
I wonder if I could turn off the mic directly: could I implement the same using iOS bypassing the PJSIP API?
Is this possible?

You can completely disconnect the microphone from the conference using pjsua_conf_disconnect and pjsua_conf_connect when you want to unmute.
Here's some Objective-C code that does the trick:
+(void)muteMicrophone
{
#try {
if( pjsipConfAudioId != 0 ) {
NSLog(#"WC_SIPServer microphone disconnected from call");
pjsua_conf_disconnect(0, pjsipConfAudioId);
}
}
#catch (NSException *exception) {
NSLog(#"Unable to mute microphone: %#", exception);
}
}
+(void)unmuteMicrophone
{
#try {
if( pjsipConfAudioId != 0 ) {
NSLog(#"WC_SIPServer microphone reconnected to call");
pjsua_conf_connect(0,pjsipConfAudioId);
}
}
#catch (NSException *exception) {
NSLog(#"Unable to un-mute microphone: %#", exception);
}
}
Note that the pjsipConfAudioID was retrieved when the call was established, again in Objective-C...
static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
{
pjsua_call_info ci;
PJ_UNUSED_ARG(e);
pjsua_call_get_info(call_id, &ci);
pjsipConfAudioId = ci.conf_slot;
...
}
Hope that helps!

Related

how to stop audio when iOS Sleep Timer gets called

I want to stop my audio app when iOS sleep timer gets called.
Just like Pandora app.
http://help.pandora.com/customer/portal/articles/24324-ios-sleep-timer-with-pandora
Tap the Clock app, Tap Timer, Select a time, Tap When Timer Ends, Tap Stop
Playing
This will sleep your Pandora app if it is running.
I can see inInterruptionState == kAudioSessionBeginInterruption gets called when iOS sleep timer ends, but how can I detect if it's sleep timer or just interruptions like phone call?
Here is my codes.
Currently, my app just starts playing again after iOS sleep timer ends.
// Audio Interruption Listener
void MyInterruptionListener(void *inClientData, UInt32 inInterruptionState) {
if (inInterruptionState == kAudioSessionBeginInterruption) {
[[DOSpeechManager sharedInstance] audioSessionBeginInterruption];
}
if (inInterruptionState == kAudioSessionEndInterruption) {
[[DOSpeechManager sharedInstance] audioSessionEndInterruption];
}
}
- (void)audioSessionBeginInterruption {
if ([_MyAcaTTS isSpeaking] && [_MyAcaTTS isPaused] == NO) {
[_MyAcaTTS pauseSpeakingAtBoundary:AcapelaSpeechImmediateBoundary];
[self setAudioSettionStatus:NO];
_audioInterruptedWhileSpeaking = YES;
}
}
- (void)audioSessionEndInterruption {
if (_audioInterruptedWhileSpeaking) {
[self setAudioSettionStatus:YES];
[_MyAcaTTS continueSpeaking];
}
}
- (void)setAudioSettionStatus:(BOOL)status {
AudioSessionSetActive(status);
[_MyAcaTTS setActive:status];
//cancel audio interrupted flag
if (status) {
_audioInterruptedWhileSpeaking = NO;
}
}
The trick is not to detect the source of the interruption, but to know whether your app should resume after the interruption.
The AVAudioSession API will send a notification when the audio session is interrupted. Within this notification, the OS gives a "hint" as to whether the app should resume playback or not.
See below:
//Add notification observer
__weak typeof(self) weakSelf = self;
self.audioSessionInterruptionNotification =
[[NSNotificationCenter defaultCenter] addObserverForName:AVAudioSessionInterruptionNotification
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSNumber* interruptionType = note.userInfo[AVAudioSessionInterruptionTypeKey];
NSNumber* interruptionOption = note.userInfo[AVAudioSessionInterruptionOptionKey];
BOOL shouldResume = interruptionOption.integerValue == AVAudioSessionInterruptionOptionShouldResume;
switch (interruptionType.integerValue) {
case AVAudioSessionInterruptionTypeBegan:
[weakSelf beginInterruption];
break;
case AVAudioSessionInterruptionTypeEnded:
[weakSelf endInterruption:shouldResume];
break;
default:
break;
}
}];
}

Game Center Matchmaking GKTurnBasedMatch has significant lag (~1 min)

I'm implementing a turn-based game with multiplayer mode through gamecenter. I have 2 devices (1 ipad, 1 iphone) to test in sandbox mode which were working fine but lately it has started to struggle in auto matchmaking process. After I send the first turn from one user, the other device doesn't immediately recognize that game but opens up its own fresh game. Before it was able to immediately spot the game started in the other device and matchmaking was fairly straightforward. And I don't remember changing any parts relevant to matchmaking (NSCoding, GKTurnBasedEventHandler, GKTurnBasedMatchmakerViewControllerDelegate delegate methods etc).
Now I send the first turn from one device and need to wait around 1 min so the other device can successfully connect to that game. After connection occurs endTurnWithMatchData calls work without any problems, it can send and receive data within 1-2 secs. But it won't be a good UX if users start a fresh game and had to wait 1 min so another user can connect to his game. Has anyone been experiencing significant lag in auto matchmaking process? I didn't implement invitations yet, so I cannot check it. The matchdata I archive with NSKeyedArchiver seemed quite big, 3396 bytes, even for a fresh game with almost no data. And here are relevant parts of my code:
GameOptionsViewController:
- (void)turnBasedMatchmakerViewControllerWasCancelled:(GKTurnBasedMatchmakerViewController *)viewController
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFailWithError:(NSError *)error
{
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)turnBasedMatchmakerViewController:(GKTurnBasedMatchmakerViewController *)viewController didFindMatch:(GKTurnBasedMatch *)match
{
[self dismissViewControllerAnimated:NO completion:nil];
self.gcMatch = match;
[self performSegueWithIdentifier:#"GameMultiplayer" sender:self];
}
- (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"GameMultiplayer"])
{
GameViewController *GameVC = (GameViewController *)segue.destinationViewController;
[GameVC setGameMode:GAMEMODE_MULTIPLAYER_SAMEDEVICE];
//Multiplayer game it is
if(self.gcMatch != nil)
{
[GameVC setGameMode:GAMEMODE_MULTIPLAYER_GAMECENTER];
GameVC.gcMatchDelegate = self;
GameVC.gcMatch = self.gcMatch;
NSLog(#"Game OVC Segue: Match ID | %#", self.gcMatch.matchID);
}
}
else
{
...
}
}
GameViewController:
//This method is called according to user actions
//It's the only method I use to send data to other participant
-(void) sendCurrentGameDataWithNewTurn:(BOOL) newTurn
{
NSLog(#"Sending game data current participant : %#", gcMatch.currentParticipant.playerID);
//Update match data if it is corrupted anyhow
if (gcMatch.currentParticipant == nil)
{
[GKTurnBasedMatch loadMatchWithID:gcMatch.matchID withCompletionHandler:^(GKTurnBasedMatch *match, NSError *error)
{
if (error != nil)
{
NSLog(#"Error :%#", error);
return ;
}
[self sendCurrentGameDataWithNewTurn:newTurn];
}];
}
else
{
NSData *matchData = [NSKeyedArchiver archivedDataWithRootObject:game];
//Game advances to new player, buttons are disabled
if(newTurn)
{
NSLog(#"SENDING NEW TURN");
NSUInteger currentIndex = [gcMatch.participants
indexOfObject:gcMatch.currentParticipant];
GKTurnBasedParticipant *nextParticipant;
nextParticipant = [gcMatch.participants objectAtIndex:
((currentIndex + 1) % [gcMatch.participants count])];
[gcMatch endTurnWithNextParticipants:[NSArray arrayWithObject:nextParticipant] turnTimeout:GC_TURN_TIMEOUT matchData:matchData completionHandler:^(NSError *error) {
NSLog(#"Sent");
if (error) {
NSLog(#"SNT - %#", error);
}
}];
}
else
{
NSLog(#"ONLY UPDATING DATA");
[gcMatch saveCurrentTurnWithMatchData:matchData completionHandler:^(NSError *error) {
NSLog(#"Sent");
if (error) {
NSLog(#"OUD - %#", error);
}
}];
}
}
}
-(void) updateGameDataWithGCMatch
{
//Update whole game data
self.game = [NSKeyedUnarchiver unarchiveObjectWithData:self.gcMatch.matchData];
//Update game ui
...
}
-(void) handleTurnEventForMatch:(GKTurnBasedMatch *)match didBecomeActive:(BOOL)didBecomeActive
{
//Check if I got data for the currently active match that options vc forwarded me here, if not do some debug print and return
if(![self.gcMatch.matchID isEqual:match.matchID])
{
//For debugging reasons I skip if i get info for any previous match (other player quit etc)
NSLog(#"GCMatch matchID: %# match matchID: %#",self.gcMatch.matchID,match.matchID);
return;
}
NSLog(#"Turn event handle");
self.gcMatch = match;
if([match.currentParticipant.playerID isEqualToString: [GKLocalPlayer localPlayer].playerID ])
{
//Disable field buttons
[self setFieldButtonsEnabled:TRUE];
[self turnChangeAnimationFromLeftToRight:FALSE];
}
[self updateGameDataWithGCMatch];
}
As for your question:
I myself tempered with matchmaking over Game Center quite a bit and also experienced lags quite frequently, which have been proven to not have been caused by my site but by apples game center servers.
As for additional guidance:
As far as I can see your current approach to matchmaking on a device is:
look if there is a match I can connect to --> If YES request gamedata and connect to the match ELSE start your own match and broadcast the matchdata
From my experience it is better practice to start with matchrequestbroadcasts, wait until you find a second player, define the server device (e.g. by lower checksum of game-center names) and then start the game on that device.

iPhone: first song not pause before start next song

In My app Song play through buffering,
I have two music player in my app,
But it plays both musicplayer songs together. It does not pause the first song even when I try to pause.
i refer this tutorial:
http://cocoawithlove.com/2008/09/streaming-and-playing-live-mp3-stream.html
for that i try to call
[streamer pause];
- (void)pause
{
#synchronized(self)
{
if (state == AS_PLAYING)
{
err = AudioQueuePause(audioQueue);
if (err)
{
[self failWithErrorCode:AS_AUDIO_QUEUE_PAUSE_FAILED];
return;
}
self.state = AS_PAUSED;
}
else if (state == AS_PAUSED)
{
err = AudioQueueStart(audioQueue, NULL);
if (err)
{
[self failWithErrorCode:AS_AUDIO_QUEUE_START_FAILED];
return;
}
self.state = AS_PLAYING;
}
}
}
streamer calls the pause method of buffering.
How can i do that?
if (audioPlayer.playing==YES) {
[audioPlayer pause];
}
try this.

try & catch in iPhone?

The try{}catch construct is common to C++, Java & related languages. In the iOS SDK is there and any functionality like this?
#try {
// Try something
}
#catch (NSException * e) {
NSLog(#"Exception: %#", e);
}
#finally {
// Added to show finally works as well
}

How to programmatically detect earpiece in iphone?

I'm currently working on a project that involves playing music from the iphone music library within the app inside. I'm using MPMediaPickerController to allow the user to select their music and play it using the iPod music player within the iPhone.
However, i ran into problem when the user insert his earpiece and removes it. The music will suddenly stop playing for no reason. After some testing, i found out that the iPod player will pause playing when the user unplug his earpiece from the device. So is there any way to programatically detect if the earpiece has been unplug so that i can resume playing the music? Or is there any way to prevent iPod player from pausing when the user unplug his earpiece?
You should register for AudioRoute changed notification and implement how you want to handle the rout changes
// Registers the audio route change listener callback function
AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback,
self);
and within the callback, you can get the reason for route change
CFDictionaryRef routeChangeDictionary = inPropertyValue;
CFNumberRef routeChangeReasonRef =
CFDictionaryGetValue (routeChangeDictionary,
CFSTR (kAudioSession_AudioRouteChangeKey_Reason));
SInt32 routeChangeReason;
CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason);
if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable)
{
// Headset is unplugged..
}
if (routeChangeReason == kAudioSessionRouteChangeReason_NewDeviceAvailable)
{
// Headset is plugged in..
}
If you just want to check whether headphones are plugged in at any given time, without listening to route changes, you can simply do the following:
OSStatus error = AudioSessionInitialize(NULL, NULL, NULL, NULL);
if (error)
NSLog("Error %d while initializing session", error);
UInt32 routeSize = sizeof (CFStringRef);
CFStringRef route;
error = AudioSessionGetProperty (kAudioSessionProperty_AudioRoute,
&routeSize,
&route);
if (error)
NSLog("Error %d while retrieving audio property", error);
else if (route == NULL) {
NSLog(#"Silent switch is currently on");
} else if([route isEqual:#"Headset"]) {
NSLog(#"Using headphones");
} else {
NSLog(#"Using %#", route);
}
Cheers,
Raffaello Colasante
I see you are using the MPMediaPlayer Framework however the microphone handling is done using the AVAudioPlayer framework, which you will need to add to your project.
Apple's website has code from the AVAudioPlayer framework which I use to handle interruptions from a user plugging in or removing the Apple microphone headphones.
Check out Apple's iPhone Dev Center Audio Session Programming Guide.
- (void) beginInterruption {
if (playing) {
playing = NO;
interruptedWhilePlaying = YES;
[self updateUserInterface];
}
}
NSError *activationError = nil;
- (void) endInterruption {
if (interruptedWhilePlaying) {
[[AVAudioSession sharedInstance] setActive: YES error: &activationError];
[player play];
playing = YES;
interruptedWhilePlaying = NO;
[self updateUserInterface];
}
}
My code is a little different and some of this may help you:
void interruptionListenerCallback (
void *inUserData,
UInt32 interruptionState
) {
// This callback, being outside the implementation block, needs a reference
// to the AudioViewController object
RecordingListViewController *controller = (RecordingListViewController *) inUserData;
if (interruptionState == kAudioSessionBeginInterruption) {
//NSLog (#"Interrupted. Stopping playback or recording.");
if (controller.audioRecorder) {
// if currently recording, stop
[controller recordOrStop: (id) controller];
} else if (controller.audioPlayer) {
// if currently playing, pause
[controller pausePlayback];
controller.interruptedOnPlayback = YES;
}
} else if ((interruptionState == kAudioSessionEndInterruption) && controller.interruptedOnPlayback) {
// if the interruption was removed, and the app had been playing, resume playback
[controller resumePlayback];
controller.interruptedOnPlayback = NO;
}
}
void recordingListViewMicrophoneListener (
void *inUserData,
AudioSessionPropertyID inPropertyID,
UInt32 inPropertyValueSize,
const void *isMicConnected
) {
// ensure that this callback was invoked for a change to microphone connection
if (inPropertyID != kAudioSessionProperty_AudioInputAvailable) {
return;
}
RecordingListViewController *controller = (RecordingListViewController *) inUserData;
// kAudioSessionProperty_AudioInputAvailable is a UInt32 (see Apple Audio Session Services Reference documentation)
// to read isMicConnected, convert the const void pointer to a UInt32 pointer
// then dereference the memory address contained in that pointer
UInt32 connected = * (UInt32 *) isMicConnected;
if (connected){
[controller setMicrophoneConnected : YES];
}
else{
[controller setMicrophoneConnected: NO];
}
// check to see if microphone disconnected while recording
// cancel the recording if it was
if(controller.isRecording && !connected){
[controller cancelDueToMicrophoneError];
}
}
Hey guys just check AddMusic sample app. Will solve all your iPod related issues
First register iPod player for notification with following code
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter
addObserver: self
selector: #selector (handle_PlaybackStateChanged:)
name: MPMusicPlayerControllerPlaybackStateDidChangeNotification
object: musicPlayer];
[musicPlayer beginGeneratingPlaybackNotifications];
And implement the following code in the notification
- (void) handle_PlaybackStateChanged: (id) notification
{
MPMusicPlaybackState playbackState = [musicPlayer playbackState];
if (playbackState == MPMusicPlaybackStatePaused)
{
[self playiPodMusic];
}
else if (playbackState == MPMusicPlaybackStatePlaying)
{
}
else if (playbackState == MPMusicPlaybackStateStopped)
{
[musicPlayer stop];
}
}