David Rainò David Rainò - 5 months ago 93
iOS Question

iOS: PJSIP background no incoming calls after 30-40 seconds

I have a problem with a voip app. I'm using PJSIP 2.4.5 (which is the latest version) and I have a problem when my app goes in background. Everything is ok for 30-40 seconds, then I'm not able to receive incoming calls until the app come back to foreground. I've searched a lot around but i didn't find a solution for my problem.

This is my code, where I initialize PJSIP

static pjsua_acc_id acc_id;
static pjsua_call_id incoming_call_id;
static pjsua_call_id current_call_id;
int imcalling=1; //this variable tell the program if I'm currently calling

const size_t MAX_SIP_ID_LENGTH = 50;
const size_t MAX_SIP_REG_URI_LENGTH = 50;

static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata);
static void on_call_state(pjsua_call_id call_id, pjsip_event *e);
static void on_call_media_state(pjsua_call_id call_id);
static void error_exit(const char *title, pj_status_t status);

//pointers for the callbacks to objective-c
void (*callee_response)();
void (*callee_hangup)(int);
void (*call_incoming)(char*);
void (*call_incoming_canceled)(char*);
void (*call_get_statistics)(char*);

int startPjsip(const char *sipUser, const char *sipPassword, const char* sipDomain, const char* realm, unsigned int reg_timeout, int transport_protocol, void(*incoming_call)(char*), void(*incoming_call_cancel)(char*), void(*get_call_statistics)(char*))
{
pj_status_t status;

pjsip_cfg_t *mysipcfg = pjsip_cfg();
mysipcfg->tcp.keep_alive_interval = 20;

// Create pjsua first
status = pjsua_create();
if (status != PJ_SUCCESS) error_exit("Error in pjsua_create()", status);

// Init pjsua
{
// Init the config structure
pjsua_config cfg;
pjsua_config_default (&cfg);

cfg.cb.on_incoming_call = &on_incoming_call;
cfg.cb.on_call_media_state = &on_call_media_state;
cfg.cb.on_call_state = &on_call_state;

// Init the logging config structure
pjsua_logging_config log_cfg;
pjsua_logging_config_default(&log_cfg);
log_cfg.console_level = 4;

// Init the pjsua
status = pjsua_init(&cfg, &log_cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error in pjsua_init()", status);
}

// Add UDP transport.
{
// Init transport config structure
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);
cfg.port = 5060;

// Add UDP transport.
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
}

// Add TCP transport.
{
// Init transport config structure
pjsua_transport_config cfg;
pjsua_transport_config_default(&cfg);

cfg.port = 5060;

// Add TCP transport.
status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &cfg, NULL);
if (status != PJ_SUCCESS) error_exit("Error creating transport", status);
}

// Initialization is done, now start pjsua
status = pjsua_start();
if (status != PJ_SUCCESS) error_exit("Error starting pjsua", status);

call_incoming=incoming_call; //pointer to the function that will tell to the app that a call is incoming
call_incoming_canceled=incoming_call_cancel; //pointer to the function that will tell to the app that a call is canceled
call_get_statistics=get_call_statistics; //pointer to the function that send the call statistics to the app

// Register the account on sip server
{
pjsua_acc_config cfg;

pjsua_acc_config_default(&cfg);

char sipId[MAX_SIP_ID_LENGTH];
sprintf(sipId, "sip:%s@%s", sipUser, sipDomain);
cfg.id = pj_str(sipId);

char regUri[MAX_SIP_REG_URI_LENGTH];
if(transport_protocol==1) sprintf(regUri, "sip:%s;transport=tcp", sipDomain);
else sprintf(regUri, "sip:%s", sipDomain);
cfg.reg_uri = pj_str(regUri);

cfg.reg_timeout = reg_timeout;
//cfg.ka_interval = 30;

cfg.cred_count = 1;
cfg.cred_info[0].realm = pj_str((char*)realm);
cfg.cred_info[0].scheme = pj_str("digest");
cfg.cred_info[0].username = pj_str((char*)sipUser);
cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD;
cfg.cred_info[0].data = pj_str((char*)sipPassword);
cfg.reg_retry_interval = 30;

//pj_log_set_level(6);

status = pjsua_acc_add(&cfg, PJ_TRUE, &acc_id);
if (status != PJ_SUCCESS) error_exit("Error adding account", status);
}

return 0;


}

Those are the first lines printed by pjsip after the activation

15:51:51.442 os_core_unix.c !pjlib 2.4.5 for POSIX initialized
15:51:51.444 sip_endpoint.c .Creating endpoint instance...
15:51:51.444 pjlib .select() I/O Queue created (0x16295940)
15:51:51.444 sip_endpoint.c .Module "mod-msg-print" registered
15:51:51.445 sip_transport. .Transport manager created.
15:51:51.445 pjsua_core.c .PJSUA state changed: NULL --> CREATED
15:51:51.447 sip_endpoint.c .Module "mod-pjsua-log" registered
15:51:51.447 sip_endpoint.c .Module "mod-tsx-layer" registered
15:51:51.447 sip_endpoint.c .Module "mod-stateful-util" registered
15:51:51.447 sip_endpoint.c .Module "mod-ua" registered
15:51:51.448 sip_endpoint.c .Module "mod-100rel" registered
15:51:51.448 sip_endpoint.c .Module "mod-pjsua" registered
15:51:51.448 sip_endpoint.c .Module "mod-invite" registered
15:51:51.490 coreaudio_dev. .. dev_id 0: iPhone IO device (in=1, out=1) 8000Hz
15:51:51.508 coreaudio_dev. ..core audio initialized
15:51:51.509 pjlib ..select() I/O Queue created (0x16a63e14)
15:51:51.547 sip_endpoint.c .Module "mod-evsub" registered
15:51:51.547 sip_endpoint.c .Module "mod-presence" registered
15:51:51.547 sip_endpoint.c .Module "mod-mwi" registered
15:51:51.548 sip_endpoint.c .Module "mod-refer" registered
15:51:51.548 sip_endpoint.c .Module "mod-pjsua-pres" registered
15:51:51.548 sip_endpoint.c .Module "mod-pjsua-im" registered
15:51:51.548 sip_endpoint.c .Module "mod-pjsua-options" registered
15:51:51.549 pjsua_core.c .1 SIP worker threads created
15:51:51.549 pjsua_core.c .pjsua version 2.4.5 for iPhone OS-9.2/arm-iPhone4,1/iOS-SDK-9.2 initialized
15:51:51.549 pjsua_core.c .PJSUA state changed: CREATED --> INIT
15:51:51.554 pjsua_core.c SIP UDP socket reachable at 192.168.100.174:5060
15:51:51.556 udp0x16ab8400 SIP UDP transport started, published address is 192.168.100.174:5060
15:51:51.559 tcptp:5060 SIP TCP listener ready for incoming connections at 192.168.100.174:5060
15:51:51.559 pjsua_core.c PJSUA state changed: INIT --> STARTING
15:51:51.559 sip_endpoint.c .Module "mod-unsolicited-mwi" registered
15:51:51.559 pjsua_core.c .PJSUA state changed: STARTING --> RUNNING
15:51:51.560 pjsua_acc.c Adding account: id=sip:2011@37.187.161.173
15:51:51.561 pjsua_acc.c .Account sip:2011@37.187.161.173 added with id 0
15:51:51.561 pjsua_acc.c .Acc 0: setting registration..
15:51:51.563 tcpc0x1629d414 ..TCP client transport created
15:51:51.564 tcpc0x1629d414 ..TCP transport 192.168.100.174:49474 is connecting to 37.187.161.173:5060...
15:51:51.564 pjsua_acc.c ..Contact for acc 0 updated for SIP outbound: <sip:2011@192.168.100.174:49474;transport=TCP;ob>;reg-id=1;+sip.instance="<urn:uuid:00000000-0000-0000-0000-0000d3a3575f>"
15:51:51.566 pjsua_core.c ...TX 629 bytes Request msg REGISTER/cseq=17104 (tdta0x16abce00) to TCP 37.187.161.173:5060:
REGISTER sip:37.187.161.173;transport=tcp SIP/2.0


I'm sure that i'm using TCP for the socket and I have just tried to remove the udp socket but nothing changed. Searching around I saw that someone has my same problem but after some minutes while my problem happens after half a minute or a little more.

I have tried even to set the keepalive interval of pjsip as you can see from these lines:

pjsip_cfg_t *mysipcfg = pjsip_cfg();
mysipcfg->tcp.keep_alive_interval = 20;


I set it to 20 seconds but nothing changed. Voip and audio capabilities are added to background modes.

I saw through the network monitor that there is an active connection with the voip server while the app is in foreground. When the app goes in background this connection disappear after 3-4 seconds. Anyway i'm still able to get incoming calls for 30-40 seconds.

Any idea on what i'm doing wrong ?

Answer

Just found the solution myself. The problem was the reg_timeout of the pjsua config. I used the value passed from the server (60 seconds). This value was right when the app was in the foreground. When my app was in the background, instead of the registration expiring after 60 seconds and pjsip was not able to renew it because an iOS app has a minimum keep alive time of 600 seconds to wake up from the background.

The solution was to change the reg timeout when the app switched to background.

You need a function like this:

void change timeout() {
    pjsua_acc_config = cgf;
    pjsua_acc_get_config(acc_id, pool, cfg);
    cgf.reg_timeout = 600;
    pjsua_acc_modify_config(acc_id, cfg)
}

Simply you call this function when the app switches to the background and change the reg_timeout to 600. Then you register again. When the app comes back to the foreground you do the same thing with the original timeout.