diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-05-28 14:58:12 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-05-28 14:58:12 +0000 |
commit | 4d82c2e0f6790422e6e319ad80983e1e002fca85 (patch) | |
tree | 0df2957447b298b2d7429e1f98b7dd3199bda592 | |
parent | e92fc1e1389daa666961bedb4aecefdd4334c391 (diff) |
More changes in pjsua API to make it more complete high level API
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@482 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r-- | pjsip-apps/src/pjsua/main.c | 9 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 463 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 204 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_console_app.c | 276 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 783 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_im.c | 19 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_imp.h | 162 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 169 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_reg.c | 49 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_settings.c | 14 |
10 files changed, 1628 insertions, 520 deletions
diff --git a/pjsip-apps/src/pjsua/main.c b/pjsip-apps/src/pjsua/main.c index 1e060f4e..5355975f 100644 --- a/pjsip-apps/src/pjsua/main.c +++ b/pjsip-apps/src/pjsua/main.c @@ -27,7 +27,6 @@ */ int main(int argc, char *argv[]) { - pjsua_config cfg; /* Init default settings. */ @@ -55,13 +54,12 @@ int main(int argc, char *argv[]) /* Register message logger to print incoming and outgoing * messages. */ - pjsip_endpt_register_module(pjsua.endpt, + pjsip_endpt_register_module(pjsua_get_pjsip_endpt(), &pjsua_console_app_msg_logger); /* Start pjsua! */ if (pjsua_start() != PJ_SUCCESS) { - pjsua_destroy(); return 1; } @@ -78,6 +76,11 @@ int main(int argc, char *argv[]) /* Destroy pjsua: */ pjsua_destroy(); + /* This is for internal testing, to make sure that pjsua_destroy() + * can be called multiple times. + */ + pjsua_destroy(); + /* Close logging: */ pjsua_console_app_logging_shutdown(); diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 6484d729..cb2e8b7a 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -68,63 +68,6 @@ PJ_BEGIN_DECL #endif - -/** - * Structure to be attached to invite dialog. - * Given a dialog "dlg", application can retrieve this structure - * by accessing dlg->mod_data[pjsua.mod.id]. - */ -struct pjsua_call -{ - unsigned index; /**< Index in pjsua array. */ - pjsip_inv_session *inv; /**< The invite session. */ - pj_time_val start_time;/**< First INVITE sent/received. */ - pj_time_val res_time; /**< First response sent/received. */ - pj_time_val conn_time; /**< Connected/confirmed time. */ - pj_time_val dis_time; /**< Disconnect time. */ - int acc_index; /**< Account index being used. */ - pjmedia_session *session; /**< The media session. */ - unsigned conf_slot; /**< Slot # in conference bridge. */ - pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this - call was triggered by xfer. */ - pjmedia_sock_info skinfo; /**< Preallocated media sockets. */ - pjmedia_transport *med_tp; /**< Media transport. */ - void *app_data; /**< Application data. */ - pj_timer_entry refresh_tm;/**< Timer to send re-INVITE. */ - pj_timer_entry hangup_tm; /**< Timer to hangup call. */ -}; - -typedef struct pjsua_call pjsua_call; - - -/** - * Buddy data. - */ -struct pjsua_buddy -{ - pj_str_t uri; /**< Buddy URI */ - int acc_index; /**< Which account to use. */ - pj_bool_t monitor; /**< Should we monitor? */ - pjsip_evsub *sub; /**< Buddy presence subscription */ - pjsip_pres_status status; /**< Buddy presence status. */ -}; - -typedef struct pjsua_buddy pjsua_buddy; - - -/** - * Server presence subscription list head. - */ -struct pjsua_srv_pres -{ - PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); - pjsip_evsub *sub; - char *remote; -}; - -typedef struct pjsua_srv_pres pjsua_srv_pres; - - /** * Account configuration. */ @@ -161,35 +104,6 @@ typedef struct pjsua_acc_config pjsua_acc_config; /** - * Account - */ -struct pjsua_acc -{ - int index; /**< Index in accounts array. */ - pj_str_t user_part; /**< User part of local URI. */ - pj_str_t host_part; /**< Host part of local URI. */ - - pjsip_regc *regc; /**< Client registration session. */ - pj_timer_entry reg_timer; /**< Registration timer. */ - pj_status_t reg_last_err; /**< Last registration error. */ - int reg_last_code; /**< Last status last register. */ - - pjsip_route_hdr route_set; /**< Route set. */ - - pj_bool_t online_status; /**< Our online status. */ - pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ - - void *app_data; /**< Application data. */ -}; - - -/** - * @see pjsua_acc - */ -typedef struct pjsua_acc pjsua_acc; - - -/** * PJSUA settings. */ struct pjsua_config @@ -247,6 +161,12 @@ struct pjsua_config /** Second STUN server port number */ unsigned stun_port2; + /** Sound player device ID (default: 0) */ + unsigned snd_player_id; + + /** Sound capture device ID (default: 0) */ + unsigned snd_capture_id; + /** Internal clock rate (to be applied to sound devices and conference * bridge, default is 0/follows the codec, or 44100 for MacOS). */ @@ -339,17 +259,38 @@ typedef struct pjsua_config pjsua_config; struct pjsua_callback { /** - * Notify UI when invite state has changed. + * Notify application when invite state has changed. + * Application may then query the call info to get the + * detail call states. */ void (*on_call_state)(int call_index, pjsip_event *e); /** - * Notify UI when registration status has changed. + * Notify application on call being transfered. + * Application can decide to accept/reject transfer request + * by setting the code (default is 200). When this callback + * is not defined, the default behavior is to accept the + * transfer. + */ + void (*on_call_transfered)(int call_index, + const pj_str_t *dst, + pjsip_status_code *code); + + /** + * Notify application when registration status has changed. + * Application may then query the account info to get the + * registration details. */ void (*on_reg_state)(int acc_index); /** - * Notify UI on incoming pager (i.e. MESSAGE request). + * Notify application when the buddy state has changed. + * Application may then query the buddy into to get the details. + */ + void (*on_buddy_state)(int buddy_index); + + /** + * Notify application on incoming pager (i.e. MESSAGE request). * Argument call_index will be -1 if MESSAGE request is not related to an * existing call. */ @@ -357,7 +298,7 @@ struct pjsua_callback const pj_str_t *to, const pj_str_t *txt); /** - * Notify UI about typing indication. + * Notify application about typing indication. */ void (*on_typing)(int call_index, const pj_str_t *from, const pj_str_t *to, pj_bool_t is_typing); @@ -370,55 +311,79 @@ struct pjsua_callback typedef struct pjsua_callback pjsua_callback; -/* PJSUA application variables. */ -struct pjsua +/** + * Call info. + */ +struct pjsua_call_info { - /* Control: */ - pj_caching_pool cp; /**< Global pool factory. */ - pjsip_endpoint *endpt; /**< Global endpoint. */ - pj_pool_t *pool; /**< pjsua's private pool. */ - pjsip_module mod; /**< pjsua's PJSIP module. */ - - - /* Config: */ - pjsua_config config; /**< PJSUA configs */ - - /* Application callback: */ - pjsua_callback cb; /**< Application callback. */ - - /* Media: */ - pjmedia_endpt *med_endpt; /**< Media endpoint. */ - pjmedia_conf *mconf; /**< Media conference. */ - unsigned wav_slot; /**< WAV player slot in bridge */ - pjmedia_port *file_port; /**< WAV player port. */ + unsigned index; + pj_bool_t active; + pjsip_role_e role; + pj_str_t local_info; + pj_str_t remote_info; + pjsip_inv_state state; + pj_str_t state_text; + pj_time_val connect_duration; + pj_time_val total_duration; + pjsip_status_code cause; + pj_str_t cause_text; + pj_bool_t has_media; + unsigned conf_slot; +}; +typedef struct pjsua_call_info pjsua_call_info; - /* Account: */ - pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */ +enum pjsua_buddy_status +{ + PJSUA_BUDDY_STATUS_UNKNOWN, + PJSUA_BUDDY_STATUS_ONLINE, + PJSUA_BUDDY_STATUS_OFFLINE, +}; - /* Threading (optional): */ - pj_thread_t *threads[8]; /**< Thread instances. */ - pj_bool_t quit_flag; /**< To signal thread to quit. */ +typedef enum pjsua_buddy_status pjsua_buddy_status; - /* Transport (UDP): */ - pj_sock_t sip_sock; /**< SIP UDP socket. */ - pj_sockaddr_in sip_sock_name; /**< Public/STUN UDP socket addr. */ +/** + * Buddy info. + */ +struct pjsua_buddy_info +{ + unsigned index; + pj_bool_t is_valid; + pj_str_t name; + pj_str_t display_name; + pj_str_t host; + unsigned port; + pj_str_t uri; + pjsua_buddy_status status; + pj_str_t status_text; + pj_bool_t monitor; +}; - /* PJSUA Calls: */ - int call_cnt; /**< Number of calls. */ - pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */ +typedef struct pjsua_buddy_info pjsua_buddy_info; - /* SIMPLE and buddy status: */ - int buddy_cnt; - pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; +/** + * Account info. + */ +struct pjsua_acc_info +{ + unsigned index; + pj_str_t acc_id; + pj_bool_t has_registration; + int expires; + pjsip_status_code status; + pj_str_t status_text; + pj_bool_t online_status; + char buf[PJ_ERR_MSG_SIZE]; }; +typedef struct pjsua_acc_info pjsua_acc_info; -/** PJSUA instance. */ -extern struct pjsua pjsua; + +typedef int pjsua_player_id; +typedef int pjsua_recorder_id; @@ -433,7 +398,7 @@ PJ_DECL(void) pjsua_default_config(pjsua_config *cfg); /** - * Test configuration. + * Validate configuration. */ PJ_DECL(pj_status_t) pjsua_test_config(const pjsua_config *cfg, char *errmsg, @@ -441,9 +406,8 @@ PJ_DECL(pj_status_t) pjsua_test_config(const pjsua_config *cfg, /** - * Create pjsua application. - * This initializes pjlib/pjlib-util, and creates memory pool factory to - * be used by application. + * Instantiate pjsua application. This initializes pjlib/pjlib-util, and + * creates memory pool factory to be used by application. */ PJ_DECL(pj_status_t) pjsua_create(void); @@ -469,22 +433,78 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, */ PJ_DECL(pj_status_t) pjsua_start(void); - /** * Destroy pjsua. */ PJ_DECL(pj_status_t) pjsua_destroy(void); +/** + * Get SIP endpoint instance. + * Only valid after pjsua_init(). + */ +PJ_DECL(pjsip_endpoint*) pjsua_get_pjsip_endpt(void); + +/** + * Get media endpoint instance. + * Only valid after pjsua_init(). + */ +PJ_DECL(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void); + +/** + * Replace media transport. + */ +PJ_DECL(pj_status_t) pjsua_set_call_media_transport(unsigned call_index, + const pjmedia_sock_info *i, + pjmedia_transport *tp); + /***************************************************************************** * PJSUA Call API (defined in pjsua_call.c). */ /** + * Get maximum number of calls configured in pjsua. + */ +PJ_DECL(unsigned) pjsua_get_max_calls(void); + +/** + * Get current number of active calls. + */ +PJ_DECL(unsigned) pjsua_get_call_count(void); + +/** + * Check if the specified call has active INVITE session and the INVITE + * session has not been disconnected. + */ +PJ_DECL(pj_bool_t) pjsua_call_is_active(unsigned call_index); + + +/** + * Check if call has a media session. + */ +PJ_DECL(pj_bool_t) pjsua_call_has_media(unsigned call_index); + + +/** + * Get call info. + */ +PJ_DECL(pj_status_t) pjsua_get_call_info(unsigned call_index, + pjsua_call_info *info); + + +/** + * Duplicate call info. + */ +PJ_DECL(void) pjsua_dup_call_info(pj_pool_t *pool, + pjsua_call_info *dst_info, + const pjsua_call_info *src_info); + + +/** * Make outgoing call. */ -PJ_DECL(pj_status_t) pjsua_make_call(int acc_index, - const char *cstr_dest_uri, +PJ_DECL(pj_status_t) pjsua_make_call(unsigned acc_index, + const pj_str_t *dst_uri, int *p_call_index); @@ -514,13 +534,19 @@ PJ_DECL(void) pjsua_call_reinvite(int call_index); /** * Transfer call. */ -PJ_DECL(void) pjsua_call_xfer(int call_index, const char *dest); +PJ_DECL(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest); + +/** + * Dial DTMF. + */ +PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(unsigned call_index, + const pj_str_t *digits); /** * Send instant messaging inside INVITE session. */ -PJ_DECL(void) pjsua_call_send_im(int call_index, const char *text); +PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *text); /** @@ -535,14 +561,44 @@ PJ_DECL(void) pjsua_call_hangup_all(void); /***************************************************************************** - * PJSUA Client Registration API (defined in pjsua_reg.c). + * PJSUA Account and Client Registration API (defined in pjsua_reg.c). + */ + + +/** + * Get number of accounts. */ +PJ_DECL(unsigned) pjsua_get_acc_count(void); + +/** + * Get account info. + */ +PJ_DECL(pj_status_t) pjsua_acc_get_info(unsigned acc_index, + pjsua_acc_info *info); + +/** + * Add a new account. + * This function should be called after pjsua_init(). + * Application should call pjsua_acc_set_registration() to start + * registration for this account. + */ +PJ_DECL(pj_status_t) pjsua_acc_add(const pjsua_acc_config *cfg, + int *acc_index); + + +/** + * Set account's presence status. + * Must call pjsua_pres_refresh() after this. + */ +PJ_DECL(pj_status_t) pjsua_acc_set_online_status(unsigned acc_index, + pj_bool_t is_online); + /** * Update registration or perform unregistration. If renew argument is zero, * this will start unregistration process. */ -PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew); +PJ_DECL(void) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew); @@ -552,6 +608,33 @@ PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew); */ /** + * Get buddy count. + */ +PJ_DECL(unsigned) pjsua_get_buddy_count(void); + + +/** + * Get buddy info. + */ +PJ_DECL(pj_status_t) pjsua_buddy_get_info(unsigned buddy_index, + pjsua_buddy_info *info); + +/** + * Add new buddy. + */ +PJ_DECL(pj_status_t) pjsua_buddy_add(const pj_str_t *uri, + int *buddy_index); + + +/** + * Enable/disable buddy's presence monitoring. + * Must call pjsua_pres_refresh() after this. + */ +PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(unsigned buddy_index, + pj_bool_t monitor); + + +/** * Refresh both presence client and server subscriptions. */ PJ_DECL(void) pjsua_pres_refresh(int acc_index); @@ -576,19 +659,112 @@ extern const pjsip_method pjsip_message_method; /** * Send IM outside dialog. */ -PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, - const char *text); +PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, + const pj_str_t *text); /** * Send typing indication outside dialog. */ -PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri, +PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, pj_bool_t is_typing); /***************************************************************************** + * Media. + */ + +/** + * Get maxinum number of conference ports. + */ +PJ_DECL(unsigned) pjsua_conf_max_ports(void); + + +/** + * Enum all conference ports. + */ +PJ_DECL(pj_status_t) pjsua_conf_enum_ports(unsigned *count, + pjmedia_conf_port_info info[]); + + +/** + * Connect conference port. + */ +PJ_DECL(pj_status_t) pjsua_conf_connect(unsigned src_port, + unsigned dst_port); + + +/** + * Connect conference port connection. + */ +PJ_DECL(pj_status_t) pjsua_conf_disconnect(unsigned src_port, + unsigned dst_port); + + +/** + * Create a file player. + */ +PJ_DECL(pj_status_t) pjsua_player_create(const pj_str_t *filename, + pjsua_player_id *id); + + +/** + * Get conference port associated with player. + */ +PJ_DECL(unsigned) pjsua_player_get_conf_port(pjsua_player_id id); + + +/** + * Set playback position. + */ +PJ_DECL(pj_status_t) pjsua_player_set_pos(pjsua_player_id id, + pj_uint32_t samples); + + +/** + * Destroy player. + */ +PJ_DECL(pj_status_t) pjsua_player_destroy(pjsua_player_id id); + + + +/** + * Create a file recorder. + */ +PJ_DECL(pj_status_t) pjsua_recorder_create(const pj_str_t *filename, + pjsua_recorder_id *id); + + +/** + * Get conference port associated with recorder. + */ +PJ_DECL(unsigned) pjsua_recorder_get_conf_port(pjsua_recorder_id id); + + +/** + * Destroy recorder (will complete recording). + */ +PJ_DECL(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id); + + +/** + * Enum sound devices. + */ +PJ_DECL(pj_status_t) pjsua_enum_snd_devices(unsigned *count, + pjmedia_snd_dev_info info[]); + + +/** + * Select or change sound device. + * This will only change the device ID in configuration (not changing + * the current device). + */ +PJ_DECL(pj_status_t) pjsua_set_snd_dev(int snd_capture_id, + int snd_player_id); + + +/***************************************************************************** * Utilities. * */ @@ -609,7 +785,14 @@ PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename, pjsua_config *cfg); /** + * Get pjsua running config. + */ +PJ_DECL(const pjsua_config*) pjsua_get_config(void); + + +/** * Dump settings. + * If cfg is NULL, it will dump current settings. */ PJ_DECL(int) pjsua_dump_settings(const pjsua_config *cfg, char *buf, pj_size_t max); diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index d6013a13..70d9a088 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -125,13 +125,130 @@ static pj_status_t call_destroy_media(int call_index) /** + * Get maximum number of calls configured in pjsua. + */ +PJ_DEF(unsigned) pjsua_get_max_calls(void) +{ + return pjsua.config.max_calls; +} + + +/** + * Get current number of active calls. + */ +PJ_DEF(unsigned) pjsua_get_call_count(void) +{ + return pjsua.call_cnt; +} + + +/** + * Check if the specified call is active. + */ +PJ_DEF(pj_bool_t) pjsua_call_is_active(unsigned call_index) +{ + PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, + PJ_EINVAL); + return pjsua.calls[call_index].inv != NULL && + pjsua.calls[call_index].inv->state != PJSIP_INV_STATE_DISCONNECTED; +} + +/** + * Check if call has a media session. + */ +PJ_DEF(pj_bool_t) pjsua_call_has_media(unsigned call_index) +{ + PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL); + return pjsua.calls[call_index].session != NULL; +} + + +/** + * Get call info. + */ +PJ_DEF(pj_status_t) pjsua_get_call_info( unsigned call_index, + pjsua_call_info *info) +{ + pjsua_call *call; + + PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, + PJ_EINVAL); + + pj_memset(info, 0, sizeof(pjsua_call_info)); + + call = &pjsua.calls[call_index]; + info->active = pjsua_call_is_active(call_index); + + if (call->inv == NULL) + return PJ_SUCCESS; + + info->index = call_index; + info->role = call->inv->role; + info->local_info = call->inv->dlg->local.info_str; + info->remote_info = call->inv->dlg->remote.info_str; + info->state = call->inv->state; + info->state_text = pj_str((char*)pjsip_inv_state_name(info->state)); + + if (info->state >= PJSIP_INV_STATE_DISCONNECTED) { + + info->total_duration = call->dis_time; + PJ_TIME_VAL_SUB(info->total_duration, call->start_time); + + if (call->conn_time.sec) { + info->connect_duration = call->dis_time; + PJ_TIME_VAL_SUB(info->total_duration, call->conn_time); + } + + } else if (info->state == PJSIP_INV_STATE_CONFIRMED) { + + pj_gettimeofday(&info->total_duration); + PJ_TIME_VAL_SUB(info->total_duration, call->start_time); + + pj_gettimeofday(&info->connect_duration); + PJ_TIME_VAL_SUB(info->connect_duration, call->conn_time); + + } else { + pj_gettimeofday(&info->total_duration); + PJ_TIME_VAL_SUB(info->total_duration, call->start_time); + } + + info->cause = call->inv->cause; + info->cause_text = *pjsip_get_status_text(info->cause); + + info->has_media = (call->session != NULL); + info->conf_slot = call->conf_slot; + + return PJ_SUCCESS; +} + + +/** + * Duplicate call info. + */ +PJ_DEF(void) pjsua_dup_call_info( pj_pool_t *pool, + pjsua_call_info *dst_info, + const pjsua_call_info *src_info) +{ + PJ_ASSERT_ON_FAIL(pool && dst_info && src_info, return); + + pj_memcpy(dst_info, src_info, sizeof(pjsua_call_info)); + + pj_strdup(pool, &dst_info->local_info, &src_info->local_info); + pj_strdup(pool, &dst_info->remote_info, &src_info->remote_info); + + /* state_text and cause_text belong to pjsip, so don't need to be + * duplicated because they'll always be available. + */ +} + + +/** * Make outgoing call. */ -PJ_DEF(pj_status_t) pjsua_make_call(int acc_index, - const char *cstr_dest_uri, +PJ_DEF(pj_status_t) pjsua_make_call(unsigned acc_index, + const pj_str_t *dest_uri, int *p_call_index) { - pj_str_t dest_uri; pjsip_dialog *dlg = NULL; pjmedia_sdp_session *offer; pjsip_inv_session *inv = NULL; @@ -139,9 +256,10 @@ PJ_DEF(pj_status_t) pjsua_make_call(int acc_index, pjsip_tx_data *tdata; pj_status_t status; - /* Convert cstr_dest_uri to dest_uri */ - - dest_uri = pj_str((char*)cstr_dest_uri); + + PJ_ASSERT_RETURN(acc_index==0 || acc_index < pjsua.config.acc_cnt, + PJ_EINVAL); + /* Find free call slot. */ for (call_index=0; call_index<pjsua.config.max_calls; ++call_index) { @@ -164,7 +282,7 @@ PJ_DEF(pj_status_t) pjsua_make_call(int acc_index, status = pjsip_dlg_create_uac( pjsip_ua_instance(), &pjsua.config.acc_config[acc_index].id, &pjsua.config.acc_config[acc_index].contact, - &dest_uri, &dest_uri, + dest_uri, dest_uri, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Dialog creation failed", status); @@ -655,7 +773,9 @@ static void on_call_transfered( pjsip_inv_session *inv, const pj_str_t str_refer_to = { "Refer-To", 8}; pjsip_generic_string_hdr *refer_to; char *uri; + pj_str_t tmp; struct pjsip_evsub_user xfer_cb; + pjsip_status_code code; pjsip_evsub *sub; existing_call = inv->dlg->mod_data[pjsua.mod.id]; @@ -673,6 +793,20 @@ static void on_call_transfered( pjsip_inv_session *inv, return; } + /* Notify callback */ + code = PJSIP_SC_OK; + if (pjsua.cb.on_call_transfered) + (*pjsua.cb.on_call_transfered)(existing_call->index, + &refer_to->hvalue, &code); + + if (code < 200) + code = 200; + if (code >= 300) { + /* Application rejects call transfer request */ + pjsip_dlg_respond( inv->dlg, rdata, code, NULL, NULL, NULL); + return; + } + PJ_LOG(3,(THIS_FILE, "Call to %.*s is being transfered to %.*s", (int)inv->dlg->remote.info_str.slen, inv->dlg->remote.info_str.ptr, @@ -692,7 +826,7 @@ static void on_call_transfered( pjsip_inv_session *inv, } /* Accept the REFER request, send 200 (OK). */ - pjsip_xfer_accept(sub, rdata, 200, NULL); + pjsip_xfer_accept(sub, rdata, code, NULL); /* Create initial NOTIFY request */ status = pjsip_xfer_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, @@ -718,7 +852,8 @@ static void on_call_transfered( pjsip_inv_session *inv, uri[refer_to->hvalue.slen] = '\0'; /* Now make the outgoing call. */ - status = pjsua_make_call(existing_call->acc_index, uri, &new_call); + tmp = pj_str(uri); + status = pjsua_make_call(existing_call->acc_index, &tmp, &new_call); if (status != PJ_SUCCESS) { /* Notify xferer about the error */ @@ -1083,7 +1218,7 @@ static void pjsua_call_on_media_update(pjsip_inv_session *inv, call->inv->role == PJSIP_ROLE_UAS) { - pjmedia_conf_connect_port( pjsua.mconf, pjsua.wav_slot, + pjmedia_conf_connect_port( pjsua.mconf, pjsua.player[0].slot, call->conf_slot, 0); } @@ -1307,12 +1442,11 @@ PJ_DEF(void) pjsua_call_reinvite(int call_index) /* * Transfer call. */ -PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest) +PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) { pjsip_evsub *sub; pjsip_tx_data *tdata; pjsua_call *call; - pj_str_t tmp; pj_status_t status; @@ -1336,7 +1470,7 @@ PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest) /* * Create REFER request. */ - status = pjsip_xfer_initiate(sub, pj_cstr(&tmp, dest), &tdata); + status = pjsip_xfer_initiate(sub, dest, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create REFER request", status); return; @@ -1357,14 +1491,32 @@ PJ_DEF(void) pjsua_call_xfer(int call_index, const char *dest) /** + * Dial DTMF. + */ +PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( unsigned call_index, + const pj_str_t *digits) +{ + pjsua_call *call = &pjsua.calls[call_index]; + + PJ_ASSERT_RETURN(call_index < pjsua.config.max_calls, PJ_EINVAL); + + if (!call->session) { + PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); + return -1; + } + + return pjmedia_session_dial_dtmf( call->session, 0, digits); +} + + +/** * Send instant messaging inside INVITE session. */ -PJ_DECL(void) pjsua_call_send_im(int call_index, const char *str) +PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *str) { pjsua_call *call; const pj_str_t mime_text = pj_str("text"); const pj_str_t mime_plain = pj_str("plain"); - pj_str_t text; pjsip_tx_data *tdata; pj_status_t status; @@ -1392,8 +1544,7 @@ PJ_DECL(void) pjsua_call_send_im(int call_index, const char *str) /* Create "text/plain" message body. */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text, - &mime_plain, - pj_cstr(&text, str)); + &mime_plain, str); if (tdata->msg->body == NULL) { pjsua_perror(THIS_FILE, "Unable to create msg body", PJ_ENOMEM); pjsip_tx_data_dec_ref(tdata); @@ -1504,3 +1655,22 @@ pj_status_t pjsua_call_init(void) return status; } + +/** + * Replace media transport. + */ +PJ_DEF(pj_status_t) pjsua_set_call_media_transport( unsigned call_index, + const pjmedia_sock_info *i, + pjmedia_transport *tp) +{ + pjsua_call *call = &pjsua.calls[call_index]; + + if (i) + pj_memcpy(&call->skinfo, i, sizeof(pjmedia_sock_info)); + + if (call->med_tp) + (*call->med_tp->op->destroy)(call->med_tp); + + call->med_tp = tp; + return PJ_SUCCESS; +} diff --git a/pjsip/src/pjsua-lib/pjsua_console_app.c b/pjsip/src/pjsua-lib/pjsua_console_app.c index c2f94e62..62069b9a 100644 --- a/pjsip/src/pjsua-lib/pjsua_console_app.c +++ b/pjsip/src/pjsua-lib/pjsua_console_app.c @@ -34,17 +34,18 @@ static int current_call = -1; */ static pj_bool_t find_next_call(void) { - int i; + int i, max; - for (i=current_call+1; i<(int)pjsua.config.max_calls; ++i) { - if (pjsua.calls[i].inv != NULL) { + max = pjsua_get_max_calls(); + for (i=current_call+1; i<max; ++i) { + if (pjsua_call_is_active(i)) { current_call = i; return PJ_TRUE; } } for (i=0; i<current_call; ++i) { - if (pjsua.calls[i].inv != NULL) { + if (pjsua_call_is_active(i)) { current_call = i; return PJ_TRUE; } @@ -60,17 +61,18 @@ static pj_bool_t find_next_call(void) */ static pj_bool_t find_prev_call(void) { - int i; + int i, max; + max = pjsua_get_max_calls(); for (i=current_call-1; i>=0; --i) { - if (pjsua.calls[i].inv != NULL) { + if (pjsua_call_is_active(i)) { current_call = i; return PJ_TRUE; } } - for (i=pjsua.config.max_calls-1; i>current_call; --i) { - if (pjsua.calls[i].inv != NULL) { + for (i=max-1; i>current_call; --i) { + if (pjsua_call_is_active(i)) { current_call = i; return PJ_TRUE; } @@ -87,19 +89,20 @@ static pj_bool_t find_prev_call(void) */ static void console_on_call_state(int call_index, pjsip_event *e) { - pjsua_call *call = &pjsua.calls[call_index]; + pjsua_call_info call_info; PJ_UNUSED_ARG(e); - if (call->inv->state == PJSIP_INV_STATE_DISCONNECTED) { + pjsua_get_call_info(call_index, &call_info); + + if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]", call_index, - call->inv->cause, - pjsip_get_status_text(call->inv->cause)->ptr)); + call_info.cause, + call_info.cause_text.ptr)); - call->inv = NULL; - if ((int)call->index == current_call) { + if ((int)call_index == current_call) { find_next_call(); } @@ -107,10 +110,10 @@ static void console_on_call_state(int call_index, pjsip_event *e) PJ_LOG(3,(THIS_FILE, "Call %d state changed to %s", call_index, - pjsua_inv_state_names[call->inv->state])); + call_info.state_text.ptr)); - if (call && current_call==-1) - current_call = call->index; + if (current_call==-1) + current_call = call_index; } } @@ -127,6 +130,22 @@ static void console_on_reg_state(int acc_index) /** + * Notify UI on buddy state changed. + */ +static void console_on_buddy_state(int buddy_index) +{ + pjsua_buddy_info info; + pjsua_buddy_get_info(buddy_index, &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 console_on_pager(int call_index, const pj_str_t *from, @@ -162,28 +181,28 @@ static void console_on_typing(int call_index, const pj_str_t *from, */ static void print_buddy_list(void) { - int i; + int i, count; puts("Buddy list:"); - if (pjsua.buddy_cnt == 0) + count = pjsua_get_buddy_count(); + + if (count == 0) puts(" -none-"); else { - for (i=0; i<pjsua.buddy_cnt; ++i) { - const char *status; + for (i=0; i<count; ++i) { + pjsua_buddy_info info; - if (pjsua.buddies[i].sub == NULL || - pjsua.buddies[i].status.info_cnt==0) - { - status = " ? "; - } - else if (pjsua.buddies[i].status.info[0].basic_open) - status = " Online"; - else - status = "Offline"; - - printf(" [%2d] <%s> %s\n", - i+1, status, pjsua.buddies[i].uri.ptr); + if (pjsua_buddy_get_info(i, &info) != PJ_SUCCESS) + continue; + + if (!info.is_valid) + continue; + + printf(" [%2d] <%7s> %.*s\n", + i+1, info.status_text.ptr, + (int)info.uri.slen, + info.uri.ptr); } } puts(""); @@ -195,38 +214,28 @@ static void print_buddy_list(void) */ static void print_acc_status(int acc_index) { - char reg_status[128]; - - if (pjsua.acc[acc_index].regc == NULL) { - pj_ansi_strcpy(reg_status, " -not registered to server-"); - - } else if (pjsua.acc[acc_index].reg_last_err != PJ_SUCCESS) { - pj_strerror(pjsua.acc[acc_index].reg_last_err, reg_status, sizeof(reg_status)); - - } else if (pjsua.acc[acc_index].reg_last_code>=200 && - pjsua.acc[acc_index].reg_last_code<=699) { + char buf[80]; + pjsua_acc_info info; - pjsip_regc_info info; - const pj_str_t *status_str; + pjsua_acc_get_info(acc_index, &info); - pjsip_regc_get_info(pjsua.acc[acc_index].regc, &info); - - status_str = pjsip_get_status_text(pjsua.acc[acc_index].reg_last_code); - pj_ansi_snprintf(reg_status, sizeof(reg_status), - "%s (%.*s;expires=%d)", - status_str->ptr, - (int)info.client_uri.slen, - info.client_uri.ptr, - info.next_reg); + if (!info.has_registration) { + pj_ansi_strcpy(buf, " -not registered to server-"); } else { - pj_ansi_sprintf(reg_status, "in progress (%d)", - pjsua.acc[acc_index].reg_last_code); + pj_ansi_snprintf(buf, sizeof(buf), + "%.*s (%.*s;expires=%d)", + (int)info.status_text.slen, + info.status_text.ptr, + (int)info.acc_id.slen, + info.acc_id.ptr, + info.expires); + } - printf("[%2d] Registration status: %s\n", acc_index, reg_status); + printf("[%2d] Registration status: %s\n", acc_index, buf); printf(" Online status: %s\n", - (pjsua.acc[acc_index].online_status ? "Online" : "Invisible")); + (info.online_status ? "Online" : "Invisible")); } /* @@ -238,7 +247,7 @@ static void keystroke_help(void) printf(">>>>\n"); - for (i=0; i<(int)pjsua.config.acc_cnt; ++i) + for (i=0; i<(int)pjsua_get_acc_count(); ++i) print_acc_status(i); print_buddy_list(); @@ -311,7 +320,7 @@ static void ui_input_url(const char *title, char *buf, int len, " [1 -%2d] Select from buddy list\n" " URL An URL\n" " <Enter> Empty input (or 'q') to cancel\n" - , pjsua.buddy_cnt, pjsua.buddy_cnt); + , pjsua_get_buddy_count(), pjsua_get_buddy_count()); printf("%s: ", title); fflush(stdout); @@ -349,7 +358,9 @@ static void ui_input_url(const char *title, char *buf, int len, result->nb_result = atoi(buf); - if (result->nb_result >= 0 && result->nb_result <= (int)pjsua.buddy_cnt) { + if (result->nb_result >= 0 && + result->nb_result <= (int)pjsua_get_buddy_count()) + { return; } if (result->nb_result == -1) @@ -379,7 +390,7 @@ static void conf_list(void) printf("Conference ports:\n"); count = PJ_ARRAY_SIZE(info); - pjmedia_conf_get_ports_info(pjsua.mconf, &count, info); + pjsua_conf_enum_ports(&count, info); for (i=0; i<count; ++i) { char txlist[PJSUA_MAX_CALLS*4+10]; int j; @@ -413,12 +424,16 @@ void pjsua_console_app_main(void) char text[128]; int i, count; char *uri; + pj_str_t tmp; struct input_result result; + pjsua_call_info call_info; + pjsua_acc_info acc_info; /* If user specifies URI to call, then call the URI */ - if (pjsua.config.uri_to_call.slen) { - pjsua_make_call( current_acc, pjsua.config.uri_to_call.ptr, NULL); + if (pjsua_get_config()->uri_to_call.slen) { + pjsua_make_call( current_acc, &pjsua_get_config()->uri_to_call, + NULL); } keystroke_help(); @@ -434,7 +449,7 @@ void pjsua_console_app_main(void) case 'm': /* Make call! : */ - printf("(You currently have %d calls)\n", pjsua.call_cnt); + printf("(You currently have %d calls)\n", pjsua_get_call_count()); uri = NULL; ui_input_url("Make call", buf, sizeof(buf), &result); @@ -444,19 +459,22 @@ void pjsua_console_app_main(void) puts("You can't do that with make call!"); continue; } else { - uri = pjsua.buddies[result.nb_result-1].uri.ptr; + pjsua_buddy_info binfo; + pjsua_buddy_get_info(result.nb_result-1, &binfo); + uri = binfo.uri.ptr; } } else if (result.uri_result) { uri = result.uri_result; } - pjsua_make_call( current_acc, uri, NULL); + tmp = pj_str(uri); + pjsua_make_call( current_acc, &tmp, NULL); break; case 'M': /* Make multiple calls! : */ - printf("(You currently have %d calls)\n", pjsua.call_cnt); + printf("(You currently have %d calls)\n", pjsua_get_call_count()); if (!simple_input("Number of calls", menuin, sizeof(menuin))) continue; @@ -467,19 +485,22 @@ void pjsua_console_app_main(void) ui_input_url("Make call", buf, sizeof(buf), &result); if (result.nb_result != NO_NB) { + pjsua_buddy_info binfo; if (result.nb_result == -1 || result.nb_result == 0) { puts("You can't do that with make call!"); continue; } - uri = pjsua.buddies[result.nb_result-1].uri.ptr; + pjsua_buddy_get_info(result.nb_result-1, &binfo); + uri = binfo.uri.ptr; } else { uri = result.uri_result; } for (i=0; i<atoi(menuin); ++i) { pj_status_t status; - - status = pjsua_make_call(current_acc, uri, NULL); + + tmp = pj_str(uri); + status = pjsua_make_call(current_acc, &tmp, NULL); if (status != PJ_SUCCESS) break; } @@ -507,7 +528,9 @@ void pjsua_console_app_main(void) i = current_call; } else { - uri = pjsua.buddies[result.nb_result-1].uri.ptr; + pjsua_buddy_info binfo; + pjsua_buddy_get_info(result.nb_result-1, &binfo); + uri = binfo.uri.ptr; } } else if (result.uri_result) { @@ -518,8 +541,10 @@ void pjsua_console_app_main(void) /* Send typing indication. */ if (i != -1) pjsua_call_typing(i, PJ_TRUE); - else - pjsua_im_typing(current_acc, uri, PJ_TRUE); + else { + pj_str_t tmp_uri = pj_str(uri); + pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE); + } /* Input the IM . */ if (!simple_input("Message", text, sizeof(text))) { @@ -529,24 +554,40 @@ void pjsua_console_app_main(void) */ if (i != -1) pjsua_call_typing(i, PJ_FALSE); - else - pjsua_im_typing(current_acc, uri, PJ_FALSE); + else { + pj_str_t tmp_uri = pj_str(uri); + pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE); + } continue; } + tmp = pj_str(text); + /* Send the IM */ if (i != -1) - pjsua_call_send_im(i, text); - else - pjsua_im_send(current_acc, uri, text); + pjsua_call_send_im(i, &tmp); + else { + pj_str_t tmp_uri = pj_str(uri); + pjsua_im_send(current_acc, &tmp_uri, &tmp); + } break; case 'a': + if (current_call != -1) { + pjsua_get_call_info(current_call, &call_info); + } else { + /* Make compiler happy */ + call_info.active = 0; + call_info.role = PJSIP_ROLE_UAC; + call_info.state = PJSIP_INV_STATE_DISCONNECTED; + } + if (current_call == -1 || - pjsua.calls[current_call].inv->role != PJSIP_ROLE_UAS || - pjsua.calls[current_call].inv->state >= PJSIP_INV_STATE_CONNECTING) + call_info.active==0 || + call_info.role != PJSIP_ROLE_UAS || + call_info.state >= PJSIP_INV_STATE_CONNECTING) { puts("No pending incoming call"); fflush(stdout); @@ -608,19 +649,11 @@ void pjsua_console_app_main(void) } if (current_call != -1) { - char url[PJSIP_MAX_URL_SIZE]; - int len; - const pjsip_uri *u; - - u = pjsua.calls[current_call].inv->dlg->remote.info->uri; - len = pjsip_uri_print(0, u, url, sizeof(url)-1); - if (len < 1) { - pj_ansi_strcpy(url, "<uri is too long>"); - } else { - url[len] = '\0'; - } - - PJ_LOG(3,(THIS_FILE,"Current dialog: %s", url)); + + pjsua_get_call_info(current_call, &call_info); + PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", + (int)call_info.remote_info.slen, + call_info.remote_info.ptr)); } else { PJ_LOG(3,(THIS_FILE,"No current dialog")); @@ -676,12 +709,17 @@ void pjsua_console_app_main(void) if (result.nb_result != NO_NB) { if (result.nb_result == -1 || result.nb_result == 0) puts("You can't do that with transfer call!"); - else + else { + pjsua_buddy_info binfo; + pjsua_buddy_get_info(result.nb_result-1, &binfo); pjsua_call_xfer( current_call, - pjsua.buddies[result.nb_result-1].uri.ptr); + &binfo.uri); + } } else if (result.uri_result) { - pjsua_call_xfer( current_call, result.uri_result); + pj_str_t tmp; + tmp = pj_str(result.uri_result); + pjsua_call_xfer( current_call, &tmp); } } break; @@ -694,7 +732,7 @@ void pjsua_console_app_main(void) PJ_LOG(3,(THIS_FILE, "No current call")); - } else if (pjsua.calls[current_call].session == NULL) { + } else if (!pjsua_call_has_media(current_call)) { PJ_LOG(3,(THIS_FILE, "Media is not established yet!")); @@ -715,8 +753,7 @@ void pjsua_console_app_main(void) } digits = pj_str(buf); - status = pjmedia_session_dial_dtmf(pjsua.calls[current_call].session, 0, - &digits); + status = pjsua_call_dial_dtmf(current_call, &digits); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send DTMF", status); } else { @@ -733,14 +770,15 @@ void pjsua_console_app_main(void) ui_input_url("(un)Subscribe presence of", buf, sizeof(buf), &result); if (result.nb_result != NO_NB) { if (result.nb_result == -1) { - int i; - for (i=0; i<pjsua.buddy_cnt; ++i) - pjsua.buddies[i].monitor = (menuin[0]=='s'); + int i, count; + count = pjsua_get_buddy_count(); + for (i=0; i<count; ++i) + pjsua_buddy_subscribe_pres(i, menuin[0]=='s'); } else if (result.nb_result == 0) { puts("Sorry, can only subscribe to buddy's presence, " "not from existing call"); } else { - pjsua.buddies[result.nb_result-1].monitor = (menuin[0]=='s'); + pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s')); } pjsua_pres_refresh(current_acc); @@ -758,23 +796,24 @@ void pjsua_console_app_main(void) /* * Re-Register. */ - pjsua_regc_update(current_acc, PJ_TRUE); + pjsua_acc_set_registration(current_acc, PJ_TRUE); break; case 'u': /* * Unregister */ - pjsua_regc_update(current_acc, PJ_FALSE); + pjsua_acc_set_registration(current_acc, PJ_FALSE); break; } break; case 't': - pjsua.acc[current_acc].online_status = - !pjsua.acc[current_acc].online_status; + pjsua_acc_get_info(current_acc, &acc_info); + acc_info.online_status = !acc_info.online_status; + pjsua_acc_set_online_status(current_acc, acc_info.online_status); printf("Setting %s online status to %s\n", - pjsua.config.acc_config[current_acc].id.ptr, - (pjsua.acc[current_acc].online_status?"online":"offline")); + acc_info.acc_id.ptr, + (acc_info.online_status?"online":"offline")); pjsua_pres_refresh(current_acc); break; @@ -806,14 +845,11 @@ void pjsua_console_app_main(void) break; if (menuin[1]=='c') { - status = pjmedia_conf_connect_port(pjsua.mconf, - atoi(src_port), - atoi(dst_port), - 0); + status = pjsua_conf_connect(atoi(src_port), + atoi(dst_port)); } else { - status = pjmedia_conf_disconnect_port(pjsua.mconf, - atoi(src_port), - atoi(dst_port)); + status = pjsua_conf_disconnect(atoi(src_port), + atoi(dst_port)); } if (status == PJ_SUCCESS) { puts("Success"); @@ -830,7 +866,7 @@ void pjsua_console_app_main(void) char settings[2000]; int len; - len = pjsua_dump_settings(&pjsua.config, settings, + len = pjsua_dump_settings(NULL, settings, sizeof(settings)); if (len < 1) PJ_LOG(3,(THIS_FILE, "Error: not enough buffer")); @@ -946,7 +982,7 @@ static void app_log_writer(int level, const char *buffer, int len) { /* Write to both stdout and file. */ - if (level <= (int)pjsua.config.app_log_level) + if (level <= (int)pjsua_get_config()->app_log_level) pj_log_write(level, buffer, len); if (log_file) { @@ -1009,7 +1045,9 @@ void pjsua_perror(const char *sender, const char *title, pjsua_callback console_callback = { &console_on_call_state, + NULL, /* default accept transfer */ &console_on_reg_state, + &console_on_buddy_state, &console_on_pager, &console_on_typing, }; diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index dba563ce..3ec5947f 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -84,6 +84,7 @@ PJ_DEF(void) pjsua_default_config(pjsua_config *cfg) for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { cfg->acc_config[i].reg_timeout = 55; } + } @@ -525,8 +526,6 @@ static pj_status_t init_media(void) { int i; unsigned options; - unsigned clock_rate; - unsigned samples_per_frame; pj_str_t codec_id; pj_status_t status; @@ -603,7 +602,7 @@ static pj_status_t init_media(void) /* Init options for conference bridge. */ - options = 0; + options = PJMEDIA_CONF_NO_DEVICE; /* Calculate maximum number of ports, if it's not specified */ if (pjsua.config.conf_ports == 0) { @@ -611,13 +610,13 @@ static pj_status_t init_media(void) } /* Init conference bridge. */ - clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000; - samples_per_frame = clock_rate * 10 / 1000; + pjsua.clock_rate = pjsua.config.clock_rate ? pjsua.config.clock_rate : 16000; + pjsua.samples_per_frame = pjsua.clock_rate * 10 / 1000; status = pjmedia_conf_create(pjsua.pool, pjsua.config.conf_ports, - clock_rate, + pjsua.clock_rate, 1, /* mono */ - samples_per_frame, + pjsua.samples_per_frame, 16, options, &pjsua.mconf); @@ -628,32 +627,58 @@ static pj_status_t init_media(void) return status; } - /* Create WAV file player if required: */ + if (pjsua.config.null_audio == PJ_FALSE) { + pjmedia_port *conf_port; - if (pjsua.config.wav_file.slen) { - pj_str_t port_name; + /* Create sound device port */ + status = pjmedia_snd_port_create(pjsua.pool, + pjsua.config.snd_capture_id, + pjsua.config.snd_player_id, + pjsua.clock_rate, 1 /* mono */, + pjsua.samples_per_frame, 16, + 0, &pjsua.snd_port); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create sound device", status); + return status; + } - /* Create the file player port. */ - status = pjmedia_wav_player_port_create( pjsua.pool, - pjsua.config.wav_file.ptr, - 0, 0, -1, NULL, - &pjsua.file_port); + /* Get the port interface of the conference bridge */ + conf_port = pjmedia_conf_get_master_port(pjsua.mconf); + + /* Connect conference port interface to sound port */ + pjmedia_snd_port_connect( pjsua.snd_port, conf_port); + + } else { + pjmedia_port *null_port, *conf_port; + + /* Create NULL port */ + status = pjmedia_null_port_create(pjsua.pool, pjsua.clock_rate, + 1, pjsua.samples_per_frame, 16, + &null_port); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Error playing media file", - status); + pjsua_perror(THIS_FILE, "Unable to create NULL port", status); return status; } - /* Add port to conference bridge: */ - port_name = pjsua.config.wav_file; - status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, - pjsua.file_port, - &port_name, - &pjsua.wav_slot); + /* Get the port interface of the conference bridge */ + conf_port = pjmedia_conf_get_master_port(pjsua.mconf); + + /* Create master port to control conference bridge's clock */ + status = pjmedia_master_port_create(pjsua.pool, null_port, conf_port, + 0, &pjsua.master_port); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create master port", status); + return status; + } + } + + /* Create WAV file player if required: */ + + if (pjsua.config.wav_file.slen) { + + status = pjsua_player_create(&pjsua.config.wav_file, NULL); if (status != PJ_SUCCESS) { - pjsua_perror(THIS_FILE, - "Unable to add file player to conference bridge", + pjsua_perror(THIS_FILE, "Unable to create file player", status); return status; } @@ -665,6 +690,33 @@ static pj_status_t init_media(void) /* + * Copy account configuration. + */ +static void copy_acc_config(pj_pool_t *pool, + pjsua_acc_config *dst_acc, + const pjsua_acc_config *src_acc) +{ + unsigned j; + + pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id); + pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri); + pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact); + pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy); + + for (j=0; j<src_acc->cred_count; ++j) { + pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, + &src_acc->cred_info[j].realm); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, + &src_acc->cred_info[j].scheme); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, + &src_acc->cred_info[j].username); + pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, + &src_acc->cred_info[j].data); + } +} + + +/* * Copy configuration. */ static void copy_config(pj_pool_t *pool, pjsua_config *dst, @@ -691,23 +743,7 @@ static void copy_config(pj_pool_t *pool, pjsua_config *dst, for (i=0; i<src->acc_cnt; ++i) { pjsua_acc_config *dst_acc = &dst->acc_config[i]; const pjsua_acc_config *src_acc = &src->acc_config[i]; - unsigned j; - - pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id); - pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri); - pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact); - pj_strdup_with_null(pool, &dst_acc->proxy, &src_acc->proxy); - - for (j=0; j<src_acc->cred_count; ++j) { - pj_strdup_with_null(pool, &dst_acc->cred_info[j].realm, - &src_acc->cred_info[j].realm); - pj_strdup_with_null(pool, &dst_acc->cred_info[j].scheme, - &src_acc->cred_info[j].scheme); - pj_strdup_with_null(pool, &dst_acc->cred_info[j].username, - &src_acc->cred_info[j].username); - pj_strdup_with_null(pool, &dst_acc->cred_info[j].data, - &src_acc->cred_info[j].data); - } + copy_acc_config(pool, dst_acc, src_acc); } pj_strdup_with_null(pool, &dst->log_filename, &src->log_filename); @@ -747,6 +783,11 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, pjsua.calls[i].conf_slot = 0; } + /* Init buddies array */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.buddies); ++i) { + pjsua.buddies[i].index = i; + } + /* Copy configuration */ copy_config(pjsua.pool, &pjsua.config, cfg); @@ -756,7 +797,8 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, /* Test configuration */ if (pjsua_test_config(&pjsua.config, errmsg, sizeof(errmsg))) { PJ_LOG(1,(THIS_FILE, "Error in configuration: %s", errmsg)); - return -1; + status = -1; + goto on_error; } @@ -773,7 +815,7 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, &pjsua.sip_sock, &pjsua.sip_sock_name); if (status != PJ_SUCCESS) - return status; + goto on_error; pj_strdup2_with_null(pjsua.pool, &pjsua.config.sip_host, pj_inet_ntoa(pjsua.sip_sock_name.sin_addr)); @@ -785,7 +827,8 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, if (cfg->sip_host.slen == 0 || cfg->sip_port == 0) { PJ_LOG(1,(THIS_FILE, "Error: sip_host and sip_port must be specified")); - return PJ_EINVAL; + status = PJ_EINVAL; + goto on_error; } pjsua.sip_sock = PJ_INVALID_SOCKET; @@ -795,7 +838,7 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, /* Init media endpoint */ status = init_media(); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Init RTP sockets, only when UDP transport is enabled */ @@ -804,9 +847,10 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, if (status != PJ_SUCCESS) { unsigned j; for (j=0; j<i; ++j) { - pjmedia_transport_udp_close(pjsua.calls[j].med_tp); + if (pjsua.calls[i].med_tp) + pjsua.calls[i].med_tp->op->destroy(pjsua.calls[i].med_tp); } - return status; + goto on_error; } status = pjmedia_transport_udp_attach(pjsua.med_endpt, NULL, &pjsua.calls[i].skinfo, @@ -896,7 +940,7 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, return PJ_SUCCESS; on_error: - pj_caching_pool_destroy(&pjsua.cp); + pjsua_destroy(); return status; } @@ -963,6 +1007,144 @@ int pjsua_find_account_for_outgoing(const pj_str_t *url) } +/* + * Init account + */ +static pj_status_t init_acc(unsigned acc_index) +{ + pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index]; + pjsua_acc *acc = &pjsua.acc[acc_index]; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + + /* Need to parse local_uri to get the elements: */ + + uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr, + acc_cfg->id.slen, 0); + if (uri == NULL) { + pjsua_perror(THIS_FILE, "Invalid local URI", + PJSIP_EINVALIDURI); + return PJSIP_EINVALIDURI; + } + + /* Local URI MUST be a SIP or SIPS: */ + + if (!PJSIP_URI_SCHEME_IS_SIP(uri) && + !PJSIP_URI_SCHEME_IS_SIPS(uri)) + { + pjsua_perror(THIS_FILE, "Invalid local URI", + PJSIP_EINVALIDSCHEME); + return PJSIP_EINVALIDSCHEME; + } + + + /* Get the SIP URI object: */ + + sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); + + acc->user_part = sip_uri->user; + acc->host_part = sip_uri->host; + + /* Build Contact header */ + + if (acc_cfg->contact.slen == 0) { + char contact[128]; + const char *addr; + int port; + int len; + + addr = pjsua.config.sip_host.ptr; + port = pjsua.config.sip_port; + + /* The local Contact is the username@ip-addr, where + * - username is taken from the local URI, + * - ip-addr in UDP transport's address name (which may have been + * resolved from STUN. + */ + + /* Build temporary contact string. */ + + if (sip_uri->user.slen) { + + /* With the user part. */ + len = pj_ansi_snprintf(contact, sizeof(contact), + "<sip:%.*s@%s:%d>", + (int)sip_uri->user.slen, + sip_uri->user.ptr, + addr, port); + } else { + + /* Without user part */ + + len = pj_ansi_snprintf(contact, sizeof(contact), + "<sip:%s:%d>", + addr, port); + } + + if (len < 1 || len >= sizeof(contact)) { + pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG); + return PJSIP_EURITOOLONG; + } + + /* Duplicate Contact uri. */ + + pj_strdup2(pjsua.pool, &acc_cfg->contact, contact); + + } + + + /* Build route-set for this account */ + if (pjsua.config.outbound_proxy.slen) { + pj_str_t hname = { "Route", 5}; + pjsip_route_hdr *r; + pj_str_t tmp; + + pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy); + r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); + pj_list_push_back(&acc->route_set, r); + } + + if (acc_cfg->proxy.slen) { + pj_str_t hname = { "Route", 5}; + pjsip_route_hdr *r; + pj_str_t tmp; + + pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy); + r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); + pj_list_push_back(&acc->route_set, r); + } + + return PJ_SUCCESS; +} + +/* + * Add a new account. + */ +PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, + int *acc_index) +{ + pj_status_t status; + + PJ_ASSERT_RETURN(pjsua.config.acc_cnt<PJ_ARRAY_SIZE(pjsua.config.acc_config), + PJ_ETOOMANY); + + copy_acc_config(pjsua.pool, &pjsua.config.acc_config[pjsua.config.acc_cnt], cfg); + + status = init_acc(pjsua.config.acc_cnt); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error adding account", status); + return status; + } + + if (acc_index) + *acc_index = pjsua.config.acc_cnt; + + pjsua.config.acc_cnt++; + + return PJ_SUCCESS; +} + + /* * Start pjsua stack. @@ -971,6 +1153,7 @@ int pjsua_find_account_for_outgoing(const pj_str_t *url) PJ_DEF(pj_status_t) pjsua_start(void) { int i; /* Must be signed */ + unsigned count; pj_status_t status = PJ_SUCCESS; @@ -994,138 +1177,45 @@ PJ_DEF(pj_status_t) pjsua_start(void) if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to start UDP transport", status); - return status; + goto on_error; } } - /* The last account is default account to be used when nothing match - * any configured accounts. + /* Initialize all unused accounts with default id and contact. */ { char buf[80]; - pj_str_t tmp; - pjsua_acc_config *acc_cfg = - &pjsua.config.acc_config[pjsua.config.acc_cnt]; + pj_str_t tmp, id; tmp.ptr = buf; tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>", pjsua.config.sip_host.ptr, pjsua.config.sip_port); + pj_strdup_with_null( pjsua.pool, &id, &tmp); - pj_strdup_with_null( pjsua.pool, &acc_cfg->id, &tmp); - acc_cfg->contact = acc_cfg->id; + for (i=pjsua.config.acc_cnt; i<PJ_ARRAY_SIZE(pjsua.config.acc_config); + ++i) + { + pjsua_acc_config *acc_cfg = + &pjsua.config.acc_config[pjsua.config.acc_cnt]; + + acc_cfg->id = id; + acc_cfg->contact = id; + } } /* Initialize accounts: */ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { - - pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[i]; - pjsua_acc *acc = &pjsua.acc[i]; - pjsip_uri *uri; - pjsip_sip_uri *sip_uri; - - /* Need to parse local_uri to get the elements: */ - - uri = pjsip_parse_uri(pjsua.pool, acc_cfg->id.ptr, - acc_cfg->id.slen, 0); - if (uri == NULL) { - pjsua_perror(THIS_FILE, "Invalid local URI", - PJSIP_EINVALIDURI); - return PJSIP_EINVALIDURI; - } - - /* Local URI MUST be a SIP or SIPS: */ - - if (!PJSIP_URI_SCHEME_IS_SIP(uri) && - !PJSIP_URI_SCHEME_IS_SIPS(uri)) - { - pjsua_perror(THIS_FILE, "Invalid local URI", - PJSIP_EINVALIDSCHEME); - return PJSIP_EINVALIDSCHEME; - } - - - /* Get the SIP URI object: */ - - sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); - - acc->user_part = sip_uri->user; - acc->host_part = sip_uri->host; - - /* Build Contact header */ - - if (acc_cfg->contact.slen == 0) { - char contact[128]; - const char *addr; - int port; - int len; - - addr = pjsua.config.sip_host.ptr; - port = pjsua.config.sip_port; - - /* The local Contact is the username@ip-addr, where - * - username is taken from the local URI, - * - ip-addr in UDP transport's address name (which may have been - * resolved from STUN. - */ - - /* Build temporary contact string. */ - - if (sip_uri->user.slen) { - - /* With the user part. */ - len = pj_ansi_snprintf(contact, sizeof(contact), - "<sip:%.*s@%s:%d>", - (int)sip_uri->user.slen, - sip_uri->user.ptr, - addr, port); - } else { - - /* Without user part */ - - len = pj_ansi_snprintf(contact, sizeof(contact), - "<sip:%s:%d>", - addr, port); - } - - if (len < 1 || len >= sizeof(contact)) { - pjsua_perror(THIS_FILE, "Invalid Contact", PJSIP_EURITOOLONG); - return PJSIP_EURITOOLONG; - } - - /* Duplicate Contact uri. */ - - pj_strdup2(pjsua.pool, &acc_cfg->contact, contact); - - } - - - /* Build route-set for this account */ - if (pjsua.config.outbound_proxy.slen) { - pj_str_t hname = { "Route", 5}; - pjsip_route_hdr *r; - pj_str_t tmp; - - pj_strdup_with_null(pjsua.pool, &tmp, &pjsua.config.outbound_proxy); - r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); - pj_list_push_back(&acc->route_set, r); - } - - if (acc_cfg->proxy.slen) { - pj_str_t hname = { "Route", 5}; - pjsip_route_hdr *r; - pj_str_t tmp; - - pj_strdup_with_null(pjsua.pool, &tmp, &acc_cfg->proxy); - r = pjsip_parse_hdr(pjsua.pool, &hname, tmp.ptr, tmp.slen, NULL); - pj_list_push_back(&acc->route_set, r); + status = init_acc(i); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error initializing account", status); + goto on_error; } } - /* Create worker thread(s), if required: */ for (i=0; i<(int)pjsua.config.thread_cnt; ++i) { @@ -1137,7 +1227,7 @@ PJ_DEF(pj_status_t) pjsua_start(void) pj_thread_join(pjsua.threads[i]); pj_thread_destroy(pjsua.threads[i]); } - return status; + goto on_error; } } @@ -1147,30 +1237,31 @@ PJ_DEF(pj_status_t) pjsua_start(void) for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { status = pjsua_regc_init(i); if (status != PJ_SUCCESS) - return status; + goto on_error; /* Perform registration, if required. */ if (pjsua.acc[i].regc) { - pjsua_regc_update(i, 1); + pjsua_acc_set_registration(i, PJ_TRUE); } } - /* Init buddies */ - for (i=0; i<(int)pjsua.config.buddy_cnt; ++i) { - pjsua.buddies[i].uri = pjsua.config.buddy_uri[i]; + /* Re-init buddies */ + count = pjsua.config.buddy_cnt; + pjsua.config.buddy_cnt = 0; + for (i=0; i<(int)count; ++i) { + pj_str_t uri = pjsua.config.buddy_uri[i]; + pjsua_buddy_add(&uri, NULL); } - pjsua.buddy_cnt = pjsua.config.buddy_cnt; - /* Find account for outgoing preence subscription */ - for (i=0; i<pjsua.buddy_cnt; ++i) { - pjsua.buddies[i].acc_index = - pjsua_find_account_for_outgoing(&pjsua.buddies[i].uri); - } PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION)); return PJ_SUCCESS; + +on_error: + pjsua_destroy(); + return status; } @@ -1189,6 +1280,237 @@ static void busy_sleep(unsigned msec) } while (PJ_TIME_VAL_LT(now, timeout)); } +/** + * Get maxinum number of conference ports. + */ +PJ_DEF(unsigned) pjsua_conf_max_ports(void) +{ + return pjsua.config.conf_ports; +} + + +/** + * Enum all conference ports. + */ +PJ_DEF(pj_status_t) pjsua_conf_enum_ports( unsigned *count, + pjmedia_conf_port_info info[]) +{ + return pjmedia_conf_get_ports_info(pjsua.mconf, count, info); +} + + + + +/** + * Connect conference port. + */ +PJ_DEF(pj_status_t) pjsua_conf_connect( unsigned src_port, + unsigned dst_port) +{ + return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0); +} + + +/** + * Connect conference port connection. + */ +PJ_DEF(pj_status_t) pjsua_conf_disconnect( unsigned src_port, + unsigned dst_port) +{ + return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port); +} + + + +/** + * Create a file player. + */ +PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename, + pjsua_player_id *id) +{ + unsigned slot; + char path[128]; + pjmedia_port *port; + pj_status_t status; + + if (pjsua.player_cnt >= PJ_ARRAY_SIZE(pjsua.player)) + return PJ_ETOOMANY; + + pj_memcpy(path, filename->ptr, filename->slen); + path[filename->slen] = '\0'; + status = pjmedia_wav_player_port_create(pjsua.pool, path, + pjsua.samples_per_frame * + 1000 / pjsua.clock_rate, + 0, 0, NULL, + &port); + if (status != PJ_SUCCESS) + return status; + + status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, + port, filename, &slot); + if (status != PJ_SUCCESS) { + pjmedia_port_destroy(port); + return status; + } + + pjsua.player[pjsua.player_cnt].port = port; + pjsua.player[pjsua.player_cnt].slot = slot; + + if (*id) + *id = pjsua.player_cnt; + + ++pjsua.player_cnt; + + return PJ_SUCCESS; +} + + +/** + * Get conference port associated with player. + */ +PJ_DEF(unsigned) pjsua_player_get_conf_port(pjsua_player_id id) +{ + PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL); + return pjsua.player[id].slot; +} + + +/** + * Re-wind playback. + */ +PJ_DEF(pj_status_t) pjsua_player_set_pos(pjsua_player_id id, + pj_uint32_t samples) +{ + PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua.player[id].port != NULL, PJ_EINVALIDOP); + + return pjmedia_wav_player_port_set_pos(pjsua.player[id].port, samples); +} + + +/** + * Get conference port associated with player. + */ +PJ_DEF(pj_status_t) pjsua_player_destroy(pjsua_player_id id) +{ + PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL); + + if (pjsua.player[id].port) { + pjmedia_port_destroy(pjsua.player[id].port); + pjsua.player[id].port = NULL; + pjsua.player[id].slot = 0xFFFF; + pjsua.player_cnt--; + } + + return PJ_SUCCESS; +} + + +/** + * Create a file recorder. + */ +PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename, + pjsua_recorder_id *id) +{ + unsigned slot; + char path[128]; + pjmedia_port *port; + pj_status_t status; + + if (pjsua.recorder_cnt >= PJ_ARRAY_SIZE(pjsua.recorder)) + return PJ_ETOOMANY; + + pj_memcpy(path, filename->ptr, filename->slen); + path[filename->slen] = '\0'; + status = pjmedia_wav_writer_port_create(pjsua.pool, path, + pjsua.clock_rate, 1, + pjsua.samples_per_frame, + 16, 0, 0, NULL, + &port); + if (status != PJ_SUCCESS) + return status; + + status = pjmedia_conf_add_port(pjsua.mconf, pjsua.pool, + port, filename, &slot); + if (status != PJ_SUCCESS) { + pjmedia_port_destroy(port); + return status; + } + + pjsua.recorder[pjsua.recorder_cnt].port = port; + pjsua.recorder[pjsua.recorder_cnt].slot = slot; + + if (*id) + *id = pjsua.recorder_cnt; + + ++pjsua.recorder_cnt; + + return PJ_SUCCESS; +} + + +/** + * Get conference port associated with recorder. + */ +PJ_DEF(unsigned) pjsua_recorder_get_conf_port(pjsua_recorder_id id) +{ + PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL); + return pjsua.recorder[id].slot; +} + + +/** + * Destroy recorder (will complete recording). + */ +PJ_DEF(pj_status_t) pjsua_recorder_destroy(pjsua_recorder_id id) +{ + PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL); + + if (pjsua.recorder[id].port) { + pjmedia_port_destroy(pjsua.recorder[id].port); + pjsua.recorder[id].port = NULL; + pjsua.recorder[id].slot = 0xFFFF; + pjsua.recorder_cnt--; + } + + return PJ_SUCCESS; +} + +/** + * Enum sound devices. + */ +PJ_DEF(pj_status_t) pjsua_enum_snd_devices( unsigned *count, + pjmedia_snd_dev_info info[]) +{ + int i, dev_count; + + dev_count = pjmedia_snd_get_dev_count(); + if (dev_count > (int)*count) + dev_count = *count; + + for (i=0; i<dev_count; ++i) { + const pjmedia_snd_dev_info *dev_info; + dev_info = pjmedia_snd_get_dev_info(i); + pj_memcpy(&info[i], dev_info, sizeof(pjmedia_snd_dev_info)); + } + + *count = dev_count; + return PJ_SUCCESS; +} + + +/** + * Select or change sound device. + */ +PJ_DEF(pj_status_t) pjsua_set_snd_dev( int snd_capture_id, + int snd_player_id) +{ + pjsua.config.snd_capture_id = snd_capture_id; + pjsua.config.snd_player_id = snd_player_id; + return PJ_SUCCESS; +} + + /* * Destroy pjsua. */ @@ -1208,7 +1530,7 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) /* Unregister, if required: */ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { if (pjsua.acc[i].regc) { - pjsua_regc_update(i, 0); + pjsua_acc_set_registration(i, PJ_FALSE); } } @@ -1224,52 +1546,86 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) /* Wait for some time to allow unregistration to complete: */ - PJ_LOG(4,(THIS_FILE, "Shutting down...")); - busy_sleep(1000); - - /* Destroy conference bridge. */ - if (pjsua.mconf) - pjmedia_conf_destroy(pjsua.mconf); - - /* Destroy file port */ - if (pjsua.file_port) - pjmedia_port_destroy(pjsua.file_port); - + if (pjsua.endpt) { + PJ_LOG(4,(THIS_FILE, "Shutting down...")); + busy_sleep(1000); + } - /* Shutdown all codecs: */ -#if PJMEDIA_HAS_SPEEX_CODEC - pjmedia_codec_speex_deinit(); -#endif /* PJMEDIA_HAS_SPEEX_CODEC */ + /* If we have master port, destroying master port will recursively + * destroy conference bridge, otherwise must destroy it manually. + */ + if (pjsua.master_port) { + pjmedia_master_port_destroy(pjsua.master_port); + pjsua.master_port = NULL; + } else { + if (pjsua.snd_port) { + pjmedia_snd_port_destroy(pjsua.snd_port); + pjsua.snd_port = NULL; + } + if (pjsua.mconf) { + pjmedia_conf_destroy(pjsua.mconf); + pjsua.mconf = NULL; + } + } -#if PJMEDIA_HAS_GSM_CODEC - pjmedia_codec_gsm_deinit(); -#endif /* PJMEDIA_HAS_GSM_CODEC */ + /* Destroy file players */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.player); ++i) { + if (pjsua.player[i].port) { + pjmedia_port_destroy(pjsua.player[i].port); + pjsua.player[i].port = NULL; + } + } -#if PJMEDIA_HAS_G711_CODEC - pjmedia_codec_g711_deinit(); -#endif /* PJMEDIA_HAS_G711_CODEC */ -#if PJMEDIA_HAS_L16_CODEC - pjmedia_codec_l16_deinit(); -#endif /* PJMEDIA_HAS_L16_CODEC */ + /* Destroy file recorders */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.recorder); ++i) { + if (pjsua.recorder[i].port) { + pjmedia_port_destroy(pjsua.recorder[i].port); + pjsua.recorder[i].port = NULL; + } + } /* Close transports */ - for (i=0; pjsua.config.start_rtp_port && i<(int)pjsua.config.max_calls; ++i) { - pjmedia_transport_udp_close(pjsua.calls[i].med_tp); + for (i=0; i<(int)pjsua.config.max_calls; ++i) { + if (pjsua.calls[i].med_tp) { + (*pjsua.calls[i].med_tp->op->destroy)(pjsua.calls[i].med_tp); + pjsua.calls[i].med_tp = NULL; + } } /* Destroy media endpoint. */ + if (pjsua.med_endpt) { - pjmedia_endpt_destroy(pjsua.med_endpt); + /* Shutdown all codecs: */ +# if PJMEDIA_HAS_SPEEX_CODEC + pjmedia_codec_speex_deinit(); +# endif /* PJMEDIA_HAS_SPEEX_CODEC */ - /* Destroy endpoint. */ +# if PJMEDIA_HAS_GSM_CODEC + pjmedia_codec_gsm_deinit(); +# endif /* PJMEDIA_HAS_GSM_CODEC */ - pjsip_endpt_destroy(pjsua.endpt); - pjsua.endpt = NULL; +# if PJMEDIA_HAS_G711_CODEC + pjmedia_codec_g711_deinit(); +# endif /* PJMEDIA_HAS_G711_CODEC */ - /* Destroy caching pool. */ +# if PJMEDIA_HAS_L16_CODEC + pjmedia_codec_l16_deinit(); +# endif /* PJMEDIA_HAS_L16_CODEC */ + + + pjmedia_endpt_destroy(pjsua.med_endpt); + pjsua.med_endpt = NULL; + } + /* Destroy endpoint. */ + if (pjsua.endpt) { + pjsip_endpt_destroy(pjsua.endpt); + pjsua.endpt = NULL; + } + + /* Destroy caching pool. */ pj_caching_pool_destroy(&pjsua.cp); @@ -1278,3 +1634,22 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) return PJ_SUCCESS; } + +/** + * Get SIP endpoint instance. + * Only valid after pjsua_init(). + */ +PJ_DEF(pjsip_endpoint*) pjsua_get_pjsip_endpt(void) +{ + return pjsua.endpt; +} + +/** + * Get media endpoint instance. + * Only valid after pjsua_init(). + */ +PJ_DEF(pjmedia_endpt*) pjsua_get_pjmedia_endpt(void) +{ + return pjsua.med_endpt; +} + diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c index a5d96df4..efd8cbf6 100644 --- a/pjsip/src/pjsua-lib/pjsua_im.c +++ b/pjsip/src/pjsua-lib/pjsua_im.c @@ -18,6 +18,7 @@ */ #include <pjsua-lib/pjsua.h> #include <pj/log.h> +#include "pjsua_imp.h" /* * pjsua_im.c @@ -271,22 +272,21 @@ static void im_callback(void *token, pjsip_event *e) /** * Send IM outside dialog. */ -PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, - const char *str) +PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, + const pj_str_t *str) { pjsip_tx_data *tdata; const pj_str_t STR_CONTACT = { "Contact", 7 }; const pj_str_t mime_text = pj_str("text"); const pj_str_t mime_plain = pj_str("plain"); pj_str_t *text; - const pj_str_t dst = pj_str((char*)dst_uri); pj_status_t status; /* Create request. */ status = pjsip_endpt_create_request(pjsua.endpt, &pjsip_message_method, - &dst, + dst_uri, &pjsua.config.acc_config[acc_index].id, - &dst, NULL, NULL, -1, NULL, &tdata); + dst_uri, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; @@ -307,7 +307,7 @@ PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, * send the message. */ text = pj_pool_alloc(tdata->pool, sizeof(pj_str_t)); - pj_strdup2_with_null(tdata->pool, text, str); + pj_strdup_with_null(tdata->pool, text, str); /* Add message body */ tdata->msg->body = pjsip_msg_body_create( tdata->pool, &mime_text, @@ -333,18 +333,17 @@ PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const char *dst_uri, /** * Send typing indication outside dialog. */ -PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const char *dst_uri, +PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, pj_bool_t is_typing) { - const pj_str_t dst = pj_str((char*)dst_uri); pjsip_tx_data *tdata; pj_status_t status; /* Create request. */ status = pjsip_endpt_create_request( pjsua.endpt, &pjsip_message_method, - &dst, + dst_uri, &pjsua.config.acc_config[acc_index].id, - &dst, NULL, NULL, -1, NULL, &tdata); + dst_uri, NULL, NULL, -1, NULL, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create request", status); return status; diff --git a/pjsip/src/pjsua-lib/pjsua_imp.h b/pjsip/src/pjsua-lib/pjsua_imp.h index b406415f..208c999d 100644 --- a/pjsip/src/pjsua-lib/pjsua_imp.h +++ b/pjsip/src/pjsua-lib/pjsua_imp.h @@ -21,6 +21,168 @@ + + +/** + * Structure to be attached to invite dialog. + * Given a dialog "dlg", application can retrieve this structure + * by accessing dlg->mod_data[pjsua.mod.id]. + */ +struct pjsua_call +{ + unsigned index; /**< Index in pjsua array. */ + pjsip_inv_session *inv; /**< The invite session. */ + pj_time_val start_time;/**< First INVITE sent/received. */ + pj_time_val res_time; /**< First response sent/received. */ + pj_time_val conn_time; /**< Connected/confirmed time. */ + pj_time_val dis_time; /**< Disconnect time. */ + int acc_index; /**< Account index being used. */ + pjmedia_session *session; /**< The media session. */ + unsigned conf_slot; /**< Slot # in conference bridge. */ + pjsip_evsub *xfer_sub; /**< Xfer server subscription, if this + call was triggered by xfer. */ + pjmedia_sock_info skinfo; /**< Preallocated media sockets. */ + pjmedia_transport *med_tp; /**< Media transport. */ + void *app_data; /**< Application data. */ + pj_timer_entry refresh_tm;/**< Timer to send re-INVITE. */ + pj_timer_entry hangup_tm; /**< Timer to hangup call. */ +}; + +typedef struct pjsua_call pjsua_call; + + +/** + * Buddy data. + */ +struct pjsua_buddy +{ + unsigned index; /**< Buddy index. */ + pj_str_t name; /**< Buddy name. */ + pj_str_t display; /**< Buddy display name. */ + pj_str_t host; /**< Buddy host. */ + unsigned port; /**< Buddy port. */ + int acc_index; /**< Which account to use. */ + pj_bool_t monitor; /**< Should we monitor? */ + pjsip_evsub *sub; /**< Buddy presence subscription */ + pjsip_pres_status status; /**< Buddy presence status. */ +}; + +typedef struct pjsua_buddy pjsua_buddy; + + +/** + * Server presence subscription list head. + */ +struct pjsua_srv_pres +{ + PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); + pjsip_evsub *sub; + char *remote; +}; + +typedef struct pjsua_srv_pres pjsua_srv_pres; + + + +/** + * Account + */ +struct pjsua_acc +{ + int index; /**< Index in accounts array. */ + pj_str_t user_part; /**< User part of local URI. */ + pj_str_t host_part; /**< Host part of local URI. */ + + pjsip_regc *regc; /**< Client registration session. */ + pj_timer_entry reg_timer; /**< Registration timer. */ + pj_status_t reg_last_err; /**< Last registration error. */ + int reg_last_code; /**< Last status last register. */ + + pjsip_route_hdr route_set; /**< Route set. */ + + pj_bool_t online_status; /**< Our online status. */ + pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ + + void *app_data; /**< Application data. */ +}; + + +/** + * @see pjsua_acc + */ +typedef struct pjsua_acc pjsua_acc; + + +/* PJSUA application variables. */ +struct pjsua +{ + /* Control: */ + pj_caching_pool cp; /**< Global pool factory. */ + pjsip_endpoint *endpt; /**< Global endpoint. */ + pj_pool_t *pool; /**< pjsua's private pool. */ + pjsip_module mod; /**< pjsua's PJSIP module. */ + + + /* Config: */ + pjsua_config config; /**< PJSUA configs */ + + /* Application callback + : */ + pjsua_callback cb; /**< Application callback. */ + + /* Media: */ + pjmedia_endpt *med_endpt; /**< Media endpoint. */ + unsigned clock_rate; /**< Conference bridge's clock rate.*/ + unsigned samples_per_frame; /**< Bridge's frame size. */ + pjmedia_conf *mconf; /**< Media conference. */ + + pjmedia_snd_port *snd_port; /**< Sound device port. */ + pjmedia_master_port *master_port; /**< Master port, when no snd dev */ + + unsigned player_cnt; /**< Number of file player. */ + + /** Array of file players */ + struct { + unsigned slot; /**< WAV player slot in bridge */ + pjmedia_port *port; /**< WAV player port. */ + } player[32]; + + unsigned recorder_cnt; /**< Number of file recorders. */ + + /** Array of file recorders */ + struct { + unsigned slot; /**< Slot # in conf bridge. */ + pjmedia_port *port; /**< The recorder media port. */ + } recorder[32]; + + /* Account: */ + pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */ + + + /* Threading (optional): */ + pj_thread_t *threads[8]; /**< Thread instances. */ + pj_bool_t quit_flag; /**< To signal thread to quit. */ + + /* Transport (UDP): */ + pj_sock_t sip_sock; /**< SIP UDP socket. */ + pj_sockaddr_in sip_sock_name; /**< Public/STUN UDP socket addr. */ + + + /* PJSUA Calls: */ + unsigned call_cnt; /**< Number of calls. */ + pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */ + + + /* SIMPLE and buddy status: */ + pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; +}; + + +/** PJSUA instance. */ +extern struct pjsua pjsua; + + + /** * Find account for incoming request. */ diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index be31e2e1..fe6eedce 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -235,8 +235,9 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event) buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id); if (buddy) { PJ_LOG(3,(THIS_FILE, - "Presence subscription to %s is %s", - buddy->uri.ptr, + "Presence subscription to %.*s is %s", + (int)pjsua.config.buddy_uri[buddy->index].slen, + pjsua.config.buddy_uri[buddy->index].ptr, pjsip_evsub_get_state_name(sub))); if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) { @@ -244,6 +245,10 @@ static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event) buddy->status.info_cnt = 0; pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL); } + + /* Call callback */ + if (pjsua.cb.on_buddy_state) + (*pjsua.cb.on_buddy_state)(buddy->index); } } @@ -261,15 +266,6 @@ static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub, if (buddy) { /* Update our info. */ pjsip_pres_get_status(sub, &buddy->status); - - if (buddy->status.info_cnt) { - PJ_LOG(3,(THIS_FILE, "%s is %s", - buddy->uri.ptr, - (buddy->status.info[0].basic_open?"online":"offline"))); - } else { - PJ_LOG(3,(THIS_FILE, "No presence info for %s", - buddy->uri.ptr)); - } } /* The default is to send 200 response to NOTIFY. @@ -320,7 +316,7 @@ static void subscribe_buddy_presence(unsigned index) status = pjsip_dlg_create_uac( pjsip_ua_instance(), &acc_config->id, &acc_config->contact, - &pjsua.buddies[index].uri, + &pjsua.config.buddy_uri[index], NULL, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create dialog", @@ -400,9 +396,9 @@ static void unsubscribe_buddy_presence(unsigned index) /* It does what it says.. */ static void refresh_client_subscription(void) { - int i; + unsigned i; - for (i=0; i<pjsua.buddy_cnt; ++i) { + for (i=0; i<pjsua.config.buddy_cnt; ++i) { if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) { subscribe_buddy_presence(i); @@ -432,6 +428,125 @@ pj_status_t pjsua_pres_init() } /* + * Get buddy count. + */ +PJ_DEF(unsigned) pjsua_get_buddy_count(void) +{ + return pjsua.config.buddy_cnt; +} + + +/** + * Get buddy info. + */ +PJ_DEF(pj_status_t) pjsua_buddy_get_info(unsigned index, + pjsua_buddy_info *info) +{ + pjsua_buddy *buddy; + + PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL); + + pj_memset(info, 0, sizeof(pjsua_buddy_info)); + + buddy = &pjsua.buddies[index]; + info->index = buddy->index; + info->is_valid = pjsua.config.buddy_uri[index].slen; + if (!info->is_valid) + return PJ_SUCCESS; + + info->name = buddy->name; + info->display_name = buddy->display; + info->host = buddy->host; + info->port = buddy->port; + info->uri = pjsua.config.buddy_uri[index]; + + if (buddy->sub == NULL || buddy->status.info_cnt==0) { + info->status = PJSUA_BUDDY_STATUS_UNKNOWN; + info->status_text = pj_str("?"); + } else if (pjsua.buddies[index].status.info[0].basic_open) { + info->status = PJSUA_BUDDY_STATUS_ONLINE; + info->status_text = pj_str("Online"); + } else { + info->status = PJSUA_BUDDY_STATUS_OFFLINE; + info->status_text = pj_str("Offline"); + } + + return PJ_SUCCESS; +} + + +/** + * Add new buddy. + */ +PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri, + int *buddy_index) +{ + pjsip_name_addr *url; + pjsip_sip_uri *sip_uri; + int index; + pj_str_t tmp; + + PJ_ASSERT_RETURN(pjsua.config.buddy_cnt <= PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_ETOOMANY); + + index = pjsua.config.buddy_cnt; + + /* Get name and display name for buddy */ + pj_strdup_with_null(pjsua.pool, &tmp, uri); + url = (pjsip_name_addr*)pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen, + PJSIP_PARSE_URI_AS_NAMEADDR); + + if (url == NULL) + return PJSIP_EINVALIDURI; + + /* Save URI */ + pjsua.config.buddy_uri[index] = tmp; + + sip_uri = (pjsip_sip_uri*) url->uri; + pjsua.buddies[index].name = sip_uri->user; + pjsua.buddies[index].display = url->display; + pjsua.buddies[index].host = sip_uri->host; + pjsua.buddies[index].port = sip_uri->port; + if (pjsua.buddies[index].port == 0) + pjsua.buddies[index].port = 5060; + + /* Find account for outgoing preence subscription */ + pjsua.buddies[index].acc_index = + pjsua_find_account_for_outgoing(&pjsua.config.buddy_uri[index]); + + if (buddy_index) + *buddy_index = index; + + pjsua.config.buddy_cnt++; + + return PJ_SUCCESS; +} + + + +PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( unsigned index, + pj_bool_t monitor) +{ + pjsua_buddy *buddy; + + PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL); + + buddy = &pjsua.buddies[index]; + buddy->monitor = monitor; + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjsua_acc_set_online_status( unsigned acc_index, + pj_bool_t is_online) +{ + PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL); + pjsua.acc[acc_index].online_status = is_online; + return PJ_SUCCESS; +} + + +/* * Refresh presence */ PJ_DEF(void) pjsua_pres_refresh(int acc_index) @@ -446,14 +561,14 @@ PJ_DEF(void) pjsua_pres_refresh(int acc_index) */ void pjsua_pres_shutdown(void) { - int acc_index; - int i; + unsigned acc_index; + unsigned i; for (acc_index=0; acc_index<(int)pjsua.config.acc_cnt; ++acc_index) { pjsua.acc[acc_index].online_status = 0; } - for (i=0; i<pjsua.buddy_cnt; ++i) { + for (i=0; i<pjsua.config.buddy_cnt; ++i) { pjsua.buddies[i].monitor = 0; } @@ -467,8 +582,8 @@ void pjsua_pres_shutdown(void) */ void pjsua_pres_dump(pj_bool_t detail) { - int acc_index; - int i; + unsigned acc_index; + unsigned i; /* @@ -497,7 +612,7 @@ void pjsua_pres_dump(pj_bool_t detail) count = 0; - for (i=0; i<pjsua.buddy_cnt; ++i) { + for (i=0; i<pjsua.config.buddy_cnt; ++i) { if (pjsua.buddies[i].sub) { ++count; } @@ -544,21 +659,23 @@ void pjsua_pres_dump(pj_bool_t detail) */ PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:")); - if (pjsua.buddy_cnt == 0) { + if (pjsua.config.buddy_cnt == 0) { PJ_LOG(3,(THIS_FILE, " - no buddy list - ")); } else { - for (i=0; i<pjsua.buddy_cnt; ++i) { + for (i=0; i<pjsua.config.buddy_cnt; ++i) { if (pjsua.buddies[i].sub) { - PJ_LOG(3,(THIS_FILE, " %10s %s", + PJ_LOG(3,(THIS_FILE, " %10s %.*s", pjsip_evsub_get_state_name(pjsua.buddies[i].sub), - pjsua.buddies[i].uri.ptr)); + (int)pjsua.config.buddy_uri[i].slen, + pjsua.config.buddy_uri[i].ptr)); } else { - PJ_LOG(3,(THIS_FILE, " %10s %s", + PJ_LOG(3,(THIS_FILE, " %10s %.*s", "(null)", - pjsua.buddies[i].uri.ptr)); + (int)pjsua.config.buddy_uri[i].slen, + pjsua.config.buddy_uri[i].ptr)); } } } diff --git a/pjsip/src/pjsua-lib/pjsua_reg.c b/pjsip/src/pjsua-lib/pjsua_reg.c index 64ebe97e..3774740a 100644 --- a/pjsip/src/pjsua-lib/pjsua_reg.c +++ b/pjsip/src/pjsua-lib/pjsua_reg.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pjsua-lib/pjsua.h> +#include "pjsua_imp.h" /* @@ -82,10 +83,56 @@ static void regc_cb(struct pjsip_regc_cbparam *param) } +/** + * Get number of accounts. + */ +PJ_DEF(unsigned) pjsua_get_acc_count(void) +{ + return pjsua.config.acc_cnt; +} + + +/** + * Get account info. + */ +PJ_DEF(pj_status_t) pjsua_acc_get_info( unsigned acc_index, + pjsua_acc_info *info) +{ + pjsua_acc *acc = &pjsua.acc[acc_index]; + pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index]; + + PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL); + + pj_memset(info, 0, sizeof(pjsua_acc_info)); + + info->index = acc_index; + info->acc_id = acc_cfg->id; + info->has_registration = (acc->regc != NULL); + info->online_status = acc->online_status; + + if (acc->reg_last_err) { + info->status = acc->reg_last_err; + pj_strerror(acc->reg_last_err, info->buf, sizeof(info->buf)); + info->status_text = pj_str(info->buf); + } else { + info->status = acc->reg_last_code; + info->status_text = *pjsip_get_status_text(acc->reg_last_code); + } + + if (acc->regc) { + pjsip_regc_info regc_info; + pjsip_regc_get_info(acc->regc, ®c_info); + info->expires = regc_info.next_reg; + } + + return PJ_SUCCESS; +} + + /* * Update registration. If renew is false, then unregistration will be performed. */ -PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew) +PJ_DECL(void) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew) { pj_status_t status = 0; pjsip_tx_data *tdata = 0; diff --git a/pjsip/src/pjsua-lib/pjsua_settings.c b/pjsip/src/pjsua-lib/pjsua_settings.c index e1bdd34f..b0cd8558 100644 --- a/pjsip/src/pjsua-lib/pjsua_settings.c +++ b/pjsip/src/pjsua-lib/pjsua_settings.c @@ -18,6 +18,8 @@ */ #include <pjsua-lib/pjsua.h> #include <stdio.h> +#include "pjsua_imp.h" + /* * pjsua_settings.c @@ -1007,6 +1009,9 @@ PJ_DEF(int) pjsua_dump_settings(const pjsua_config *config, PJ_UNUSED_ARG(max); + if (config == NULL) + config = &pjsua.config; + cfg.ptr = buf; cfg.slen = 0; @@ -1210,3 +1215,12 @@ PJ_DEF(pj_status_t) pjsua_save_settings(const char *filename, pj_pool_release(pool); return PJ_SUCCESS; } + +/** + * Get pjsua running config. + */ +PJ_DEF(const pjsua_config*) pjsua_get_config(void) +{ + return &pjsua.config; +} + |