iOS pjsip 2.2 loud speaker switch fails

1.6k views Asked by At

During the call I try to switch voice from internal speaker to Loud speaker on iOS device using pjsip 2.2 library. It returns TRUE as success, but physically it doesn't change sound destination.

I use the next code

- (BOOL)setLoud:(BOOL)loud {
if (loud) {
    @try {

        pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER;

        pj_status_t pj_status =   pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
                                                        &route, PJ_TRUE);
        if (pj_status == PJ_SUCCESS) {
          return YES;
        }
        else
        {
            return NO;
        }

    }
    @catch (NSException *exception) {
        return NO;
    }
} else {
    @try {
        pjmedia_aud_dev_route route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE;

        pj_status_t pj_status =   pjsua_snd_set_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE,
                                                        &route, PJ_TRUE);
        if (pj_status == PJ_SUCCESS) {
            return YES;
        }
        else
        {
            return NO;
        }
    }
    @catch (NSException *exception) {
        return NO;
    }
}
}

Could you suggest how can we make this work?

1

There are 1 answers

1
Snwspeckle On

With the introduction of iOS 7, you should now be using AVAudioSession to handle any audio management. It took me a long time to finally get this to work but I finally figured out the problem of why my audio was not automatically routing to my iPhone Speaker. The problem is that when you answer a call, pjsip was automatically overriding the AVAudioSessionPortOverride I was performing before the call is answered. To tackle this problem, you simply just have to override the output audio port AFTER answering the call.

To make my VoIP application work efficiently with the background mode, I decided to handle the audio routing in a custom callback method named on_call_state. This method, on_call_state, is called by pjsip when a call state has changed. As you can read here, http://www.pjsip.org/pjsip/docs/html/group__PJSIP__INV.htm, there are many different flags you can check for when a call state has changed. The states I used in this example are PJSIP_INV_STATE_CONNECTING and PJSIP_INV_STATE_DISCONNECTED.

PJSIP_INV_STATE_CONNECTING is called when a audio call connects to another peer.

PJSIP_INV_STATE_DISCONNECTED is called when a audio call ends with another peer.

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);
    PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id,
              (int)ci.state_text.slen,
              ci.state_text.ptr));
    if (ci.state == PJSIP_INV_STATE_CONNECTING) {
        BOOL success;
        AVAudioSession *session = [AVAudioSession sharedInstance];
        NSError *error = nil;

        success = [session setCategory:AVAudioSessionCategoryPlayAndRecord
                           withOptions:AVAudioSessionCategoryOptionMixWithOthers
                                 error:&error];
        if (!success) NSLog(@"AVAudioSession error setCategory: %@", [error localizedDescription]);

        success = [session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:&error];
        if (!success) NSLog(@"AVAudioSession error overrideOutputAudioPort: %@", [error localizedDescription]);

        success = [session setActive:YES error:&error];
        if (!success) NSLog(@"AVAudioSession error setActive: %@", [error localizedDescription]);
    } else if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
        BOOL success;
        AVAudioSession *session = [AVAudioSession sharedInstance];
        NSError *error = nil;

        success = [session setActive:NO error:&error];
        if (!success) NSLog(@"AVAudioSession error setActive: %@", [error localizedDescription]);
    }
}