More changes in pjsua API to make it more complete high level API
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@482 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
parent
e69d49e4c5
commit
9fc735d65f
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
@ -160,35 +103,6 @@ struct pjsua_acc_config
|
|||
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.
|
||||
*/
|
||||
|
@ -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. */
|
||||
|
||||
|
||||
/* 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: */
|
||||
int call_cnt; /**< Number of calls. */
|
||||
pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */
|
||||
|
||||
|
||||
/* SIMPLE and buddy status: */
|
||||
int buddy_cnt;
|
||||
pjsua_buddy buddies[PJSUA_MAX_BUDDIES];
|
||||
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;
|
||||
|
||||
/** PJSUA instance. */
|
||||
extern struct pjsua pjsua;
|
||||
|
||||
enum pjsua_buddy_status
|
||||
{
|
||||
PJSUA_BUDDY_STATUS_UNKNOWN,
|
||||
PJSUA_BUDDY_STATUS_ONLINE,
|
||||
PJSUA_BUDDY_STATUS_OFFLINE,
|
||||
};
|
||||
|
||||
typedef enum pjsua_buddy_status pjsua_buddy_status;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
typedef struct pjsua_buddy_info pjsua_buddy_info;
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
@ -551,6 +607,33 @@ PJ_DECL(void) pjsua_regc_update(int acc_index, pj_bool_t renew);
|
|||
* PJSUA Presence (pjsua_pres.c)
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
@ -576,18 +659,111 @@ 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.
|
||||
*
|
||||
|
@ -608,8 +784,15 @@ PJ_DECL(pj_status_t) pjsua_parse_args(int argc, char *argv[],
|
|||
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);
|
||||
|
|
|
@ -124,14 +124,131 @@ 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;
|
||||
|
@ -1356,15 +1490,33 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +129,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)!
|
||||
*/
|
||||
|
@ -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";
|
||||
if (pjsua_buddy_get_info(i, &info) != PJ_SUCCESS)
|
||||
continue;
|
||||
|
||||
printf(" [%2d] <%s> %s\n",
|
||||
i+1, status, pjsua.buddies[i].uri.ptr);
|
||||
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];
|
||||
char buf[80];
|
||||
pjsua_acc_info info;
|
||||
|
||||
if (pjsua.acc[acc_index].regc == NULL) {
|
||||
pj_ansi_strcpy(reg_status, " -not registered to server-");
|
||||
pjsua_acc_get_info(acc_index, &info);
|
||||
|
||||
} 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) {
|
||||
|
||||
pjsip_regc_info info;
|
||||
const pj_str_t *status_str;
|
||||
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -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 the file player port. */
|
||||
status = pjmedia_wav_player_port_create( pjsua.pool,
|
||||
pjsua.config.wav_file.ptr,
|
||||
0, 0, -1, NULL,
|
||||
&pjsua.file_port);
|
||||
/* 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,
|
||||
"Error playing media file",
|
||||
status);
|
||||
pjsua_perror(THIS_FILE, "Unable to create sound device", 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);
|
||||
|
||||
/* 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,
|
||||
"Unable to add file player to conference bridge",
|
||||
pjsua_perror(THIS_FILE, "Unable to create NULL port", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* 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 create file player",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
@ -664,6 +689,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.
|
||||
*/
|
||||
|
@ -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);
|
||||
if (pjsua.endpt) {
|
||||
PJ_LOG(4,(THIS_FILE, "Shutting down..."));
|
||||
busy_sleep(1000);
|
||||
}
|
||||
|
||||
/* Destroy conference bridge. */
|
||||
if (pjsua.mconf)
|
||||
pjmedia_conf_destroy(pjsua.mconf);
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy file port */
|
||||
if (pjsua.file_port)
|
||||
pjmedia_port_destroy(pjsua.file_port);
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Shutdown all codecs: */
|
||||
#if PJMEDIA_HAS_SPEEX_CODEC
|
||||
pjmedia_codec_speex_deinit();
|
||||
#endif /* PJMEDIA_HAS_SPEEX_CODEC */
|
||||
|
||||
#if PJMEDIA_HAS_GSM_CODEC
|
||||
pjmedia_codec_gsm_deinit();
|
||||
#endif /* PJMEDIA_HAS_GSM_CODEC */
|
||||
|
||||
#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 */
|
||||
|
||||
# if PJMEDIA_HAS_GSM_CODEC
|
||||
pjmedia_codec_gsm_deinit();
|
||||
# endif /* PJMEDIA_HAS_GSM_CODEC */
|
||||
|
||||
# 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 */
|
||||
|
||||
|
||||
pjmedia_endpt_destroy(pjsua.med_endpt);
|
||||
pjsua.med_endpt = NULL;
|
||||
}
|
||||
|
||||
/* Destroy endpoint. */
|
||||
|
||||
pjsip_endpt_destroy(pjsua.endpt);
|
||||
pjsua.endpt = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
@ -431,6 +427,125 @@ pj_status_t pjsua_pres_init()
|
|||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue