blob: fb4b3c923a073afca297a2138dbacf15744b9f2d [file] [log] [blame]
/* $Id$ */
/*
* Copyright (C) 2008-2009 Teluu Inc. (http://www.teluu.com)
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
*
* 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 <pjsua-lib/pjsua.h>
#include <pjsua-lib/pjsua_internal.h>
//#include <pjmedia/symbian_sound_aps.h>
#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 "<sip:100@pjsip.lab>"
//
// 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:192.168.0.8;lr>"
//
// SIP transports
//
#define ENABLE_SIP_UDP 1
#define ENABLE_SIP_TCP 0 // experimental
#define ENABLE_SIP_TLS 0 // experimental
#define TLS_SRV_NAME "pjsip.org" // TLS servername (required for
// TLS transport)
//
// 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<e32debug.h>
/* 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;
}
tcfg.tls_setting.server_name = pj_str(TLS_SRV_NAME);
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);
/* 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;
}
/* 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 <e32base.h>
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_CODEC_AMR
" d Enable only AMR\n"
#endif
#if PJMEDIA_HAS_PASSTHROUGH_CODEC_G729
" g Enable only G.729\n"
#endif
#if 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_sip_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<count; ++i) {
char ipaddr[PJ_INET6_ADDRSTRLEN+2];
PJ_LOG(3,(THIS_FILE, "Addrinfo: %s",
pj_sockaddr_print(&ai[i].ai_addr, ipaddr, sizeof(ipaddr), 2)));
}
}
// Enum interface
count = PJ_ARRAY_SIZE(ifs);
status = pj_enum_ip_interface(af, &count, ifs);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "pj_enum_ip_interface() error", status);
} else {
for (i=0; i<count; ++i) {
char ipaddr[PJ_INET6_ADDRSTRLEN+2];
PJ_LOG(3,(THIS_FILE, "Interface: %s",
pj_sockaddr_print(&ifs[i], ipaddr, sizeof(ipaddr), 2)));
}
}
// Get default iinterface
status = pj_getdefaultipinterface(af, &ifs[0]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "pj_getdefaultipinterface() error", status);
} else {
char ipaddr[PJ_INET6_ADDRSTRLEN+2];
PJ_LOG(3,(THIS_FILE, "Default IP: %s",
pj_sockaddr_print(&ifs[0], ipaddr, sizeof(ipaddr), 2)));
}
// Get default IP address
status = pj_gethostip(af, &ifs[0]);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "pj_gethostip() error", status);
} else {
char ipaddr[PJ_INET6_ADDRSTRLEN+2];
PJ_LOG(3,(THIS_FILE, "Host IP: %s",
pj_sockaddr_print(&ifs[0], ipaddr, sizeof(ipaddr), 2)));
}
status = -1;
on_return:
pj_shutdown();
return status;
}
#endif
#include <es_sock.h>
#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 <apdatahandler.h>
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;
}