/* $Id$ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include //#include #include "ua.h" #define THIS_FILE "symbian_ua.cpp" #define CON_LOG_LEVEL 3 // console log level #define FILE_LOG_LEVEL 4 // logfile log level // // Basic config. // #define SIP_PORT 5060 // // Destination URI (to make call, or to subscribe presence) // #define SIP_DST_URI "" // // Account // #define HAS_SIP_ACCOUNT 0 // 1 to enable registration #define SIP_DOMAIN "pjsip.lab" #define SIP_USER "400" #define SIP_PASSWD "400" // // Outbound proxy for all accounts // #define SIP_PROXY NULL //#define SIP_PROXY "" // // SIP transports // #define ENABLE_SIP_UDP 1 #define ENABLE_SIP_TCP 0 // experimental #define ENABLE_SIP_TLS 0 // experimental // // Configure nameserver if DNS SRV is to be used with both SIP // or STUN (for STUN see other settings below) // #define NAMESERVER NULL //#define NAMESERVER "192.168.0.2" // // STUN server #if 0 // Use this to have the STUN server resolved normally # define STUN_DOMAIN NULL # define STUN_SERVER "stun.pjsip.org" #elif 0 // Use this to have the STUN server resolved with DNS SRV # define STUN_DOMAIN "pjsip.org" # define STUN_SERVER NULL #else // Use this to disable STUN # define STUN_DOMAIN NULL # define STUN_SERVER NULL #endif // // Use ICE? // #define USE_ICE 1 // // Use SRTP? // #define USE_SRTP PJSUA_DEFAULT_USE_SRTP // // Set QoS on transports? Yes! // As an example, we set SIP transports DSCP value to CS3 (DSCP // value 24 or 0x18), for no reason, and tag RTP/RTCP packets // with VOICE type. // #define SIP_QOS_DSCP 0x18 #define RTP_QOS_TYPE PJ_QOS_TYPE_VOICE // // Globals // static pjsua_acc_id g_acc_id = PJSUA_INVALID_ID; static pjsua_call_id g_call_id = PJSUA_INVALID_ID; static pjsua_buddy_id g_buddy_id = PJSUA_INVALID_ID; /* Callback called by the library upon receiving incoming call */ static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id, pjsip_rx_data *rdata) { pjsua_call_info ci; PJ_UNUSED_ARG(acc_id); PJ_UNUSED_ARG(rdata); if (g_call_id != PJSUA_INVALID_ID) { pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, NULL, NULL); return; } pjsua_call_get_info(call_id, &ci); PJ_LOG(3,(THIS_FILE, "Incoming call from %.*s!!", (int)ci.remote_info.slen, ci.remote_info.ptr)); g_call_id = call_id; /* Automatically answer incoming calls with 180/Ringing */ pjsua_call_answer(call_id, 180, NULL, NULL); } /* Callback called by the library when call's state has changed */ 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); if (ci.state == PJSIP_INV_STATE_DISCONNECTED) { if (call_id == g_call_id) g_call_id = PJSUA_INVALID_ID; } else if (ci.state != PJSIP_INV_STATE_INCOMING) { if (g_call_id == PJSUA_INVALID_ID) g_call_id = call_id; } PJ_LOG(3,(THIS_FILE, "Call %d state=%.*s", call_id, (int)ci.state_text.slen, ci.state_text.ptr)); } /* Callback called by the library when call's media state has changed */ static void on_call_media_state(pjsua_call_id call_id) { pjsua_call_info ci; pjsua_call_get_info(call_id, &ci); if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) { // When media is active, connect call to sound device. pjsua_conf_connect(ci.conf_slot, 0); pjsua_conf_connect(0, ci.conf_slot); } } /* Handler on buddy state changed. */ static void on_buddy_state(pjsua_buddy_id buddy_id) { pjsua_buddy_info info; pjsua_buddy_get_info(buddy_id, &info); PJ_LOG(3,(THIS_FILE, "%.*s status is %.*s", (int)info.uri.slen, info.uri.ptr, (int)info.status_text.slen, info.status_text.ptr)); } /* Incoming IM message (i.e. MESSAGE request)! */ static void on_pager(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, const pj_str_t *mime_type, const pj_str_t *text) { /* Note: call index may be -1 */ PJ_UNUSED_ARG(call_id); PJ_UNUSED_ARG(to); PJ_UNUSED_ARG(contact); PJ_UNUSED_ARG(mime_type); PJ_LOG(3,(THIS_FILE,"MESSAGE from %.*s: %.*s", (int)from->slen, from->ptr, (int)text->slen, text->ptr)); } /* Received typing indication */ static void on_typing(pjsua_call_id call_id, const pj_str_t *from, const pj_str_t *to, const pj_str_t *contact, pj_bool_t is_typing) { PJ_UNUSED_ARG(call_id); PJ_UNUSED_ARG(to); PJ_UNUSED_ARG(contact); PJ_LOG(3,(THIS_FILE, "IM indication: %.*s %s", (int)from->slen, from->ptr, (is_typing?"is typing..":"has stopped typing"))); } /* Call transfer request status. */ static void on_call_transfer_status(pjsua_call_id call_id, int status_code, const pj_str_t *status_text, pj_bool_t final, pj_bool_t *p_cont) { PJ_LOG(3,(THIS_FILE, "Call %d: transfer status=%d (%.*s) %s", call_id, status_code, (int)status_text->slen, status_text->ptr, (final ? "[final]" : ""))); if (status_code/100 == 2) { PJ_LOG(3,(THIS_FILE, "Call %d: call transfered successfully, disconnecting call", call_id)); pjsua_call_hangup(call_id, PJSIP_SC_GONE, NULL, NULL); *p_cont = PJ_FALSE; } } /* NAT detection result */ static void on_nat_detect(const pj_stun_nat_detect_result *res) { if (res->status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "NAT detection failed", res->status); } else { PJ_LOG(3, (THIS_FILE, "NAT detected as %s", res->nat_type_name)); } } /* Notification that call is being replaced. */ static void on_call_replaced(pjsua_call_id old_call_id, pjsua_call_id new_call_id) { pjsua_call_info old_ci, new_ci; pjsua_call_get_info(old_call_id, &old_ci); pjsua_call_get_info(new_call_id, &new_ci); PJ_LOG(3,(THIS_FILE, "Call %d with %.*s is being replaced by " "call %d with %.*s", old_call_id, (int)old_ci.remote_info.slen, old_ci.remote_info.ptr, new_call_id, (int)new_ci.remote_info.slen, new_ci.remote_info.ptr)); } /* * Transport status notification */ static void on_transport_state(pjsip_transport *tp, pjsip_transport_state state, const pjsip_transport_state_info *info) { char host_port[128]; pj_ansi_snprintf(host_port, sizeof(host_port), "[%.*s:%d]", (int)tp->remote_name.host.slen, tp->remote_name.host.ptr, tp->remote_name.port); switch (state) { case PJSIP_TP_STATE_CONNECTED: { PJ_LOG(3,(THIS_FILE, "SIP transport %s is connected to %s", tp->type_name, host_port)); } break; case PJSIP_TP_STATE_DISCONNECTED: { char buf[100]; snprintf(buf, sizeof(buf), "SIP transport %s is disconnected from %s", tp->type_name, host_port); pjsua_perror(THIS_FILE, buf, info->status); } break; default: break; } #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0 if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info && state == PJSIP_TP_STATE_CONNECTED) { pjsip_tls_state_info *tls_info = (pjsip_tls_state_info*)info->ext_info; pj_ssl_sock_info *ssl_sock_info = (pj_ssl_sock_info*) tls_info->ssl_sock_info; char buf[2048]; /* Dump server TLS certificate */ pj_ssl_cert_info_dump(ssl_sock_info->remote_cert_info, " ", buf, sizeof(buf)); PJ_LOG(4,(THIS_FILE, "TLS cert info of %s:\n%s", host_port, buf)); } #endif } //#include /* Logging callback */ static void log_writer(int level, const char *buf, int len) { static wchar_t buf16[PJ_LOG_MAX_SIZE]; PJ_UNUSED_ARG(level); pj_ansi_to_unicode(buf, len, buf16, PJ_ARRAY_SIZE(buf16)); TPtrC16 aBuf((const TUint16*)buf16, (TInt)len); //RDebug::Print(aBuf); console->Write(aBuf); } /* * app_startup() * * url may contain URL to call. */ static pj_status_t app_startup() { pj_status_t status; /* Redirect log before pjsua_init() */ pj_log_set_log_func(&log_writer); /* Set log level */ pj_log_set_level(CON_LOG_LEVEL); /* Create pjsua first! */ status = pjsua_create(); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pjsua_create() error", status); return status; } /* Init pjsua */ pjsua_config cfg; pjsua_logging_config log_cfg; pjsua_media_config med_cfg; pjsua_config_default(&cfg); cfg.max_calls = 2; cfg.thread_cnt = 0; // Disable threading on Symbian cfg.use_srtp = USE_SRTP; cfg.srtp_secure_signaling = 0; 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; cfg.cb.on_buddy_state = &on_buddy_state; cfg.cb.on_pager = &on_pager; cfg.cb.on_typing = &on_typing; cfg.cb.on_call_transfer_status = &on_call_transfer_status; cfg.cb.on_call_replaced = &on_call_replaced; cfg.cb.on_nat_detect = &on_nat_detect; cfg.cb.on_transport_state = &on_transport_state; if (SIP_PROXY) { cfg.outbound_proxy_cnt = 1; cfg.outbound_proxy[0] = pj_str(SIP_PROXY); } if (NAMESERVER) { cfg.nameserver_count = 1; cfg.nameserver[0] = pj_str(NAMESERVER); } if (NAMESERVER && STUN_DOMAIN) { cfg.stun_domain = pj_str(STUN_DOMAIN); } else if (STUN_SERVER) { cfg.stun_host = pj_str(STUN_SERVER); } pjsua_logging_config_default(&log_cfg); log_cfg.level = FILE_LOG_LEVEL; log_cfg.console_level = CON_LOG_LEVEL; log_cfg.cb = &log_writer; log_cfg.log_filename = pj_str("C:\\data\\symbian_ua.log"); pjsua_media_config_default(&med_cfg); med_cfg.thread_cnt = 0; // Disable threading on Symbian med_cfg.has_ioqueue = PJ_FALSE; med_cfg.clock_rate = 8000; med_cfg.audio_frame_ptime = 40; med_cfg.ec_tail_len = 0; med_cfg.enable_ice = USE_ICE; med_cfg.snd_auto_close_time = 0; // wait for 0 seconds idle before sound dev get auto-closed //med_cfg.no_vad = PJ_TRUE; status = pjsua_init(&cfg, &log_cfg, &med_cfg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pjsua_init() error", status); pjsua_destroy(); return status; } /* Adjust Speex priority and enable only the narrowband */ { pj_str_t codec_id = pj_str("speex/8000"); pjmedia_codec_mgr_set_codec_priority( pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), &codec_id, PJMEDIA_CODEC_PRIO_NORMAL+1); codec_id = pj_str("speex/16000"); pjmedia_codec_mgr_set_codec_priority( pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), &codec_id, PJMEDIA_CODEC_PRIO_DISABLED); codec_id = pj_str("speex/32000"); pjmedia_codec_mgr_set_codec_priority( pjmedia_endpt_get_codec_mgr(pjsua_var.med_endpt), &codec_id, PJMEDIA_CODEC_PRIO_DISABLED); } pjsua_transport_config tcfg; pjsua_transport_id tid; #if ENABLE_SIP_UDP /* Add UDP transport. */ pjsua_transport_config_default(&tcfg); tcfg.port = SIP_PORT; if (SIP_QOS_DSCP) { tcfg.qos_params.flags |= PJ_QOS_PARAM_HAS_DSCP; tcfg.qos_params.dscp_val = SIP_QOS_DSCP; } status = pjsua_transport_create(PJSIP_TRANSPORT_UDP, &tcfg, &tid); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating UDP transport", status); pjsua_destroy(); return status; } #endif #if ENABLE_SIP_TCP /* Add TCP transport */ pjsua_transport_config_default(&tcfg); tcfg.port = SIP_PORT; if (SIP_QOS_DSCP) { tcfg.qos_params.flags |= PJ_QOS_PARAM_HAS_DSCP; tcfg.qos_params.dscp_val = SIP_QOS_DSCP; } status = pjsua_transport_create(PJSIP_TRANSPORT_TCP, &tcfg, &tid); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating TCP transport", status); pjsua_destroy(); return status; } #endif #if ENABLE_SIP_TLS /* Add TLS transport */ pjsua_transport_config_default(&tcfg); tcfg.port = SIP_PORT + 1; if (SIP_QOS_DSCP) { tcfg.qos_params.flags |= PJ_QOS_PARAM_HAS_DSCP; tcfg.qos_params.dscp_val = SIP_QOS_DSCP; tcfg.tls_setting.qos_params = tcfg.qos_params; } status = pjsua_transport_create(PJSIP_TRANSPORT_TLS, &tcfg, &tid); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating TLS transport", status); pjsua_destroy(); return status; } #endif /* Add account for the transport */ pjsua_acc_add_local(tid, PJ_TRUE, &g_acc_id); #if DISABLED_FOR_TICKET_1185 /* Create media transports */ pjsua_transport_config mtcfg; pjsua_transport_config_default(&mtcfg); mtcfg.port = 4000; mtcfg.qos_type = RTP_QOS_TYPE; status = pjsua_media_transports_create(&mtcfg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating media transports", status); pjsua_destroy(); return status; } #endif /* Initialization is done, now start pjsua */ status = pjsua_start(); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error starting pjsua", status); pjsua_destroy(); return status; } /* Register to SIP server by creating SIP account. */ if (HAS_SIP_ACCOUNT) { pjsua_acc_config cfg; pjsua_acc_config_default(&cfg); cfg.id = pj_str("sip:" SIP_USER "@" SIP_DOMAIN); cfg.reg_uri = pj_str("sip:" SIP_DOMAIN); cfg.cred_count = 1; cfg.cred_info[0].realm = pj_str("*"); cfg.cred_info[0].scheme = pj_str("digest"); cfg.cred_info[0].username = pj_str(SIP_USER); cfg.cred_info[0].data_type = PJSIP_CRED_DATA_PLAIN_PASSWD; cfg.cred_info[0].data = pj_str(SIP_PASSWD); status = pjsua_acc_add(&cfg, PJ_TRUE, &g_acc_id); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error adding account", status); pjsua_destroy(); return status; } } if (SIP_DST_URI) { pjsua_buddy_config bcfg; pjsua_buddy_config_default(&bcfg); bcfg.uri = pj_str(SIP_DST_URI); bcfg.subscribe = PJ_FALSE; pjsua_buddy_add(&bcfg, &g_buddy_id); } return PJ_SUCCESS; } //////////////////////////////////////////////////////////////////////////// /* * The interractive console UI */ #include class ConsoleUI : public CActive { public: ConsoleUI(CConsoleBase *con); ~ConsoleUI(); // Run console UI void Run(); // Stop void Stop(); protected: // Cancel asynchronous read. void DoCancel(); // Implementation: called when read has completed. void RunL(); private: CConsoleBase *con_; }; ConsoleUI::ConsoleUI(CConsoleBase *con) : CActive(EPriorityStandard), con_(con) { CActiveScheduler::Add(this); } ConsoleUI::~ConsoleUI() { Stop(); } // Run console UI void ConsoleUI::Run() { con_->Read(iStatus); SetActive(); } // Stop console UI void ConsoleUI::Stop() { Cancel(); } // Cancel asynchronous read. void ConsoleUI::DoCancel() { con_->ReadCancel(); } static void PrintMainMenu() { const char *menu = "\n\n" "Main Menu:\n" " d Enable/disable codecs\n" " m Call " SIP_DST_URI "\n" " a Answer call\n" " g Hangup all calls\n" " t Toggle audio route\n" #if !defined(PJMEDIA_CONF_USE_SWITCH_BOARD) || PJMEDIA_CONF_USE_SWITCH_BOARD==0 " j Toggle loopback audio\n" #endif "up/dn Increase/decrease output volume\n" " s Subscribe " SIP_DST_URI "\n" " S Unsubscribe presence\n" " o Set account online\n" " O Set account offline\n" " w Quit\n"; PJ_LOG(3, (THIS_FILE, menu)); } static void PrintCodecMenu() { const char *menu = "\n\n" "Codec Menu:\n" " a Enable all codecs\n" #if PJMEDIA_HAS_PASSTHROUGH_CODECS && PJMEDIA_HAS_PASSTHROUGH_CODEC_AMR " d Enable only AMR\n" #endif #if PJMEDIA_HAS_PASSTHROUGH_CODECS && PJMEDIA_HAS_PASSTHROUGH_CODEC_G729 " g Enable only G.729\n" #endif #if PJMEDIA_HAS_PASSTHROUGH_CODECS && PJMEDIA_HAS_PASSTHROUGH_CODEC_ILBC " j Enable only iLBC\n" #endif " m Enable only Speex\n" " p Enable only GSM\n" " t Enable only PCMU\n" " w Enable only PCMA\n"; PJ_LOG(3, (THIS_FILE, menu)); } static void HandleMainMenu(TKeyCode kc) { switch (kc) { case EKeyUpArrow: case EKeyDownArrow: { unsigned vol; pj_status_t status; status = pjsua_snd_get_setting( PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, &vol); if (status == PJ_SUCCESS) { if (kc == EKeyUpArrow) vol = PJ_MIN(100, vol+10); else vol = (vol>=10 ? vol-10 : 0); status = pjsua_snd_set_setting( PJMEDIA_AUD_DEV_CAP_OUTPUT_VOLUME_SETTING, &vol, PJ_TRUE); } if (status == PJ_SUCCESS) { PJ_LOG(3,(THIS_FILE, "Output volume set to %d", vol)); } else { pjsua_perror(THIS_FILE, "Error setting volume", status); } } break; case 't': { pjmedia_aud_dev_route route; pj_status_t status; status = pjsua_snd_get_setting(PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, &route); if (status == PJ_SUCCESS) { if (route == PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER) route = PJMEDIA_AUD_DEV_ROUTE_EARPIECE; else route = PJMEDIA_AUD_DEV_ROUTE_LOUDSPEAKER; status = pjsua_snd_set_setting( PJMEDIA_AUD_DEV_CAP_OUTPUT_ROUTE, &route, PJ_TRUE); } if (status != PJ_SUCCESS) pjsua_perror(THIS_FILE, "Error switch audio route", status); } break; case 'j': { static pj_bool_t loopback_active = PJ_FALSE; if (!loopback_active) pjsua_conf_connect(0, 0); else pjsua_conf_disconnect(0, 0); loopback_active = !loopback_active; } break; case 'm': if (g_call_id != PJSUA_INVALID_ID) { PJ_LOG(3,(THIS_FILE, "Another call is active")); break; } if (pjsua_verify_url(SIP_DST_URI) == PJ_SUCCESS) { pj_str_t dst = pj_str(SIP_DST_URI); pjsua_call_make_call(g_acc_id, &dst, 0, NULL, NULL, &g_call_id); } else { PJ_LOG(3,(THIS_FILE, "Invalid SIP URI")); } break; case 'a': if (g_call_id != PJSUA_INVALID_ID) pjsua_call_answer(g_call_id, 200, NULL, NULL); break; case 'g': pjsua_call_hangup_all(); break; case 's': case 'S': if (g_buddy_id != PJSUA_INVALID_ID) pjsua_buddy_subscribe_pres(g_buddy_id, kc=='s'); break; case 'o': case 'O': pjsua_acc_set_online_status(g_acc_id, kc=='o'); break; default: PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed", kc, kc)); break; } PrintMainMenu(); } static void HandleCodecMenu(TKeyCode kc) { const pj_str_t ID_ALL = {"*", 1}; pj_str_t codec = {NULL, 0}; if (kc == 'a') { pjsua_codec_set_priority(&ID_ALL, PJMEDIA_CODEC_PRIO_NORMAL); PJ_LOG(3,(THIS_FILE, "All codecs activated")); } else { switch (kc) { case 'd': codec = pj_str("AMR"); break; case 'g': codec = pj_str("G729"); break; case 'j': codec = pj_str("ILBC"); break; case 'm': codec = pj_str("SPEEX/8000"); break; case 'p': codec = pj_str("GSM"); break; case 't': codec = pj_str("PCMU"); break; case 'w': codec = pj_str("PCMA"); break; default: PJ_LOG(3,(THIS_FILE, "Keycode '%c' (%d) is pressed", kc, kc)); break; } if (codec.slen) { pj_status_t status; pjsua_codec_set_priority(&ID_ALL, PJMEDIA_CODEC_PRIO_DISABLED); status = pjsua_codec_set_priority(&codec, PJMEDIA_CODEC_PRIO_NORMAL); if (status == PJ_SUCCESS) PJ_LOG(3,(THIS_FILE, "%s activated", codec.ptr)); else PJ_LOG(3,(THIS_FILE, "Failed activating %s, err=%d", codec.ptr, status)); } } } // Implementation: called when read has completed. void ConsoleUI::RunL() { enum { MENU_TYPE_MAIN = 0, MENU_TYPE_CODEC = 1 }; static int menu_type = MENU_TYPE_MAIN; TKeyCode kc = con_->KeyCode(); pj_bool_t reschedule = PJ_TRUE; if (menu_type == MENU_TYPE_MAIN) { if (kc == 'w') { CActiveScheduler::Stop(); reschedule = PJ_FALSE; } else if (kc == 'd') { menu_type = MENU_TYPE_CODEC; PrintCodecMenu(); } else { HandleMainMenu(kc); } } else { HandleCodecMenu(kc); menu_type = MENU_TYPE_MAIN; PrintMainMenu(); } if (reschedule) Run(); } #if 0 // IP networking related testing static pj_status_t test_addr(void) { int af; unsigned i, count; pj_addrinfo ai[8]; pj_sockaddr ifs[8]; const pj_str_t *hostname; pj_hostent he; pj_status_t status; pj_log_set_log_func(&log_writer); status = pj_init(); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pj_init() error", status); return status; } af = pj_AF_INET(); #if 0 pj_in_addr in_addr; pj_str_t aa = pj_str("1.1.1.1"); in_addr = pj_inet_addr(&aa); char *the_addr = pj_inet_ntoa(in_addr); PJ_LOG(3,(THIS_FILE, "IP addr=%s", the_addr)); aa = pj_str("192.168.0.15"); in_addr = pj_inet_addr(&aa); the_addr = pj_inet_ntoa(in_addr); PJ_LOG(3,(THIS_FILE, "IP addr=%s", the_addr)); aa = pj_str("2.2.2.2"); in_addr = pj_inet_addr(&aa); the_addr = pj_inet_ntoa(in_addr); PJ_LOG(3,(THIS_FILE, "IP addr=%s", the_addr)); return -1; #endif // Hostname hostname = pj_gethostname(); if (hostname == NULL) { status = PJ_ERESOLVE; pjsua_perror(THIS_FILE, "pj_gethostname() error", status); goto on_return; } PJ_LOG(3,(THIS_FILE, "Hostname: %.*s", hostname->slen, hostname->ptr)); // Gethostbyname status = pj_gethostbyname(hostname, &he); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pj_gethostbyname() error", status); } else { PJ_LOG(3,(THIS_FILE, "gethostbyname: %s", pj_inet_ntoa(*(pj_in_addr*)he.h_addr))); } // Getaddrinfo count = PJ_ARRAY_SIZE(ai); status = pj_getaddrinfo(af, hostname, &count, ai); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "pj_getaddrinfo() error", status); } else { for (i=0; i #if 0 // Force network connection to use the first IAP, // this is useful for debugging on emulator without GUI. // Include commdb.lib & apengine.lib in symbian_ua.mmp file // if this is enabled. #include inline void ForceUseFirstIAP() { TUint32 rank = 1; TUint32 bearers; TUint32 prompt; TUint32 iap; CCommsDatabase* commDb = CCommsDatabase::NewL(EDatabaseTypeIAP); CleanupStack::PushL(commDb); CApDataHandler* apDataHandler = CApDataHandler::NewLC(*commDb); TCommDbConnectionDirection direction = ECommDbConnectionDirectionOutgoing; apDataHandler->GetPreferredIfDbIapTypeL(rank, direction, bearers, prompt, iap); prompt = ECommDbDialogPrefDoNotPrompt; apDataHandler->SetPreferredIfDbIapTypeL(rank, direction, bearers, (TCommDbDialogPref)prompt, iap, ETrue); CleanupStack::PopAndDestroy(2); // apDataHandler, commDb } static void SelectIAP() { ForceUseFirstIAP(); } #else static void SelectIAP() { } #endif // Class CConnMon to monitor network connection (RConnection). Whenever // the connection is down, it will notify PJLIB and restart PJSUA-LIB. class CConnMon : public CActive { public: static CConnMon* NewL(RConnection &conn, RSocketServ &sserver) { CConnMon *self = new (ELeave) CConnMon(conn, sserver); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(self); return self; } void Start() { conn_.ProgressNotification(nif_progress_, iStatus); SetActive(); } void Stop() { Cancel(); } ~CConnMon() { Stop(); } private: CConnMon(RConnection &conn, RSocketServ &sserver) : CActive(EPriorityHigh), conn_(conn), sserver_(sserver) { CActiveScheduler::Add(this); } void ConstructL() {} void DoCancel() { conn_.CancelProgressNotification(); } void RunL() { int stage = nif_progress_().iStage; if (stage == KLinkLayerClosed) { pj_status_t status; TInt err; // Tell pjlib that connection is down. pj_symbianos_set_connection_status(PJ_FALSE); PJ_LOG(3, (THIS_FILE, "RConnection closed, restarting PJSUA..")); // Destroy pjsua pjsua_destroy(); PJ_LOG(3, (THIS_FILE, "PJSUA destroyed.")); // Reopen the connection err = conn_.Open(sserver_); if (err == KErrNone) err = conn_.Start(); if (err != KErrNone) { CActiveScheduler::Stop(); return; } // Reinit Symbian OS param before pj_init() pj_symbianos_params sym_params; pj_bzero(&sym_params, sizeof(sym_params)); sym_params.rsocketserv = &sserver_; sym_params.rconnection = &conn_; pj_symbianos_set_params(&sym_params); // Reinit pjsua status = app_startup(); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "app_startup() error", status); CActiveScheduler::Stop(); return; } PJ_LOG(3, (THIS_FILE, "PJSUA restarted.")); PrintMainMenu(); } Start(); } RConnection& conn_; RSocketServ& sserver_; TNifProgressBuf nif_progress_; }; //////////////////////////////////////////////////////////////////////////// int ua_main() { RSocketServ aSocketServer; RConnection aConn; TInt err; pj_symbianos_params sym_params; pj_status_t status; SelectIAP(); // Initialize RSocketServ if ((err=aSocketServer.Connect(32)) != KErrNone) return PJ_STATUS_FROM_OS(err); // Open up a connection if ((err=aConn.Open(aSocketServer)) != KErrNone) { aSocketServer.Close(); return PJ_STATUS_FROM_OS(err); } if ((err=aConn.Start()) != KErrNone) { aSocketServer.Close(); return PJ_STATUS_FROM_OS(err); } // Set Symbian OS parameters in pjlib. // This must be done before pj_init() is called. pj_bzero(&sym_params, sizeof(sym_params)); sym_params.rsocketserv = &aSocketServer; sym_params.rconnection = &aConn; pj_symbianos_set_params(&sym_params); // Initialize pjsua status = app_startup(); //status = test_addr(); if (status != PJ_SUCCESS) { aConn.Close(); aSocketServer.Close(); return status; } // Run the UI ConsoleUI *con = new ConsoleUI(console); con->Run(); PrintMainMenu(); // Init & start connection monitor CConnMon *connmon = CConnMon::NewL(aConn, aSocketServer); connmon->Start(); CActiveScheduler::Start(); delete connmon; delete con; // Dump memory statistics PJ_LOG(3,(THIS_FILE, "Max heap usage: %u.%03uMB", pjsua_var.cp.peak_used_size / 1000000, (pjsua_var.cp.peak_used_size % 1000000)/1000)); // check max stack usage #if defined(PJ_OS_HAS_CHECK_STACK) && PJ_OS_HAS_CHECK_STACK!=0 pj_thread_t* this_thread = pj_thread_this(); if (!this_thread) return status; const char* max_stack_file; int max_stack_line; status = pj_thread_get_stack_info(this_thread, &max_stack_file, &max_stack_line); PJ_LOG(3,(THIS_FILE, "Max stack usage: %u at %s:%d", pj_thread_get_stack_max_usage(this_thread), max_stack_file, max_stack_line)); #endif // Shutdown pjsua pjsua_destroy(); // Close connection and socket server aConn.Close(); aSocketServer.Close(); return status; }