diff options
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/include/pjsip-ua/sip_inv.h | 1 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_config.h | 10 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_endpoint.h | 4 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 168 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_internal.h | 11 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/evsub_msg.c | 2 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/pidf.c | 7 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_100rel.c | 70 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 25 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_multipart.c | 5 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transaction.c | 28 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util.c | 17 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 56 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 73 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 35 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_media.c | 55 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 44 |
17 files changed, 499 insertions, 112 deletions
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h index 4fe44758..f3bceb28 100644 --- a/pjsip/include/pjsip-ua/sip_inv.h +++ b/pjsip/include/pjsip-ua/sip_inv.h @@ -367,6 +367,7 @@ struct pjsip_inv_session pjsip_status_code cause; /**< Disconnect cause. */ pj_str_t cause_text; /**< Cause text. */ pj_bool_t notify; /**< Internal. */ + unsigned cb_called; /**< Cb has been called */ pjsip_dialog *dlg; /**< Underlying dialog. */ pjsip_role_e role; /**< Invite role. */ unsigned options; /**< Options in use. */ diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h index 78c324b8..6398e774 100644 --- a/pjsip/include/pjsip/sip_config.h +++ b/pjsip/include/pjsip/sip_config.h @@ -668,6 +668,16 @@ PJ_INLINE(pjsip_cfg_t*) pjsip_cfg(void) # define PJSIP_POOL_TSX_INC 256 #endif +/** + * Delay for non-100 1xx retransmission, in seconds. + * Set to 0 to disable this feature. + * + * Default: 60 seconds + */ +#ifndef PJSIP_TSX_1XX_RETRANS_DELAY +# define PJSIP_TSX_1XX_RETRANS_DELAY 60 +#endif + #define PJSIP_MAX_TSX_KEY_LEN (PJSIP_MAX_URL_SIZE*2) /* User agent. */ diff --git a/pjsip/include/pjsip/sip_endpoint.h b/pjsip/include/pjsip/sip_endpoint.h index 3f3dbb39..e957271c 100644 --- a/pjsip/include/pjsip/sip_endpoint.h +++ b/pjsip/include/pjsip/sip_endpoint.h @@ -56,8 +56,8 @@ PJ_BEGIN_DECL * existing modules (such as when incoming request has unsupported method). * - and so on.. * - * Theoritically application can have multiple instances of SIP endpoint, - * although it's not clear why application may want to do it. + * Application should only instantiate one SIP endpoint instance for every + * process. * * @{ */ diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 636d0955..cedb1d97 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -539,6 +539,22 @@ typedef pj_status_t /** + * This enumeration specifies the options for custom media transport creation. + */ +typedef enum pjsua_create_media_transport_flag +{ + /** + * This flag indicates that the media transport must also close its + * "member" or "child" transport when pjmedia_transport_close() is + * called. If this flag is not specified, then the media transport + * must not call pjmedia_transport_close() of its member transport. + */ + PJSUA_MED_TP_CLOSE_MEMBER = 1 + +} pjsua_create_media_transport_flag; + + +/** * This structure describes application callback to receive various event * notification from PJSUA-API. All of these callbacks are OPTIONAL, * although definitely application would want to implement some of @@ -706,6 +722,18 @@ typedef struct pjsua_callback /** + * Notify application when registration or unregistration has been + * initiated. Note that this only notifies the initial registration + * and unregistration. Once registration session is active, subsequent + * refresh will not cause this callback to be called. + * + * @param acc_id The account ID. + * @param renew Non-zero for registration and zero for + * unregistration. + */ + void (*on_reg_started)(pjsua_acc_id acc_id, pj_bool_t renew); + + /** * Notify application when registration status has changed. * Application may then query the account info to get the * registration details. @@ -1074,6 +1102,32 @@ typedef struct pjsua_callback unsigned med_idx, pjmedia_event *event); + /** + * This callback can be used by application to implement custom media + * transport adapter for the call, or to replace the media transport + * with something completely new altogether. + * + * This callback is called when a new call is created. The library has + * created a media transport for the call, and it is provided as the + * \a base_tp argument of this callback. Upon returning, the callback + * must return an instance of media transport to be used by the call. + * + * @param call_id Call ID + * @param media_idx The media index in the SDP for which this media + * transport will be used. + * @param base_tp The media transport which otherwise will be + * used by the call has this callback not been + * implemented. + * @param flags Bitmask from pjsua_create_media_transport_flag. + * + * @return The callback must return an instance of media + * transport to be used by the call. + */ + pjmedia_transport* (*on_create_media_transport)(pjsua_call_id call_id, + unsigned media_idx, + pjmedia_transport *base_tp, + unsigned flags); + } pjsua_callback; @@ -1110,6 +1164,34 @@ typedef enum pjsua_sip_timer_use /** + * This constants controls the use of 100rel extension. + */ +typedef enum pjsua_100rel_use +{ + /** + * Not used. For UAC, support for 100rel will be indicated in Supported + * header so that peer can opt to use it if it wants to. As UAS, this + * option will NOT cause 100rel to be used even if UAC indicates that + * it supports this feature. + */ + PJSUA_100REL_NOT_USED, + + /** + * Mandatory. UAC will place 100rel in Require header, and UAS will + * reject incoming calls unless it has 100rel in Supported header. + */ + PJSUA_100REL_MANDATORY, + + /** + * Optional. Similar to PJSUA_100REL_NOT_USED, except that as UAS, this + * option will cause 100rel to be used if UAC indicates that it supports it. + */ + PJSUA_100REL_OPTIONAL + +} pjsua_100rel_use; + + +/** * This structure describes the settings to control the API and * user agent behavior, and can be specified when calling #pjsua_init(). * Before setting the values, application must call #pjsua_config_default() @@ -1245,13 +1327,13 @@ typedef struct pjsua_config int nat_type_in_sdp; /** - * Specify whether support for reliable provisional response (100rel and - * PRACK) should be required by default. Note that this setting can be + * Specify how the support for reliable provisional response (100rel/ + * PRACK) should be used by default. Note that this setting can be * further customized in account configuration (#pjsua_acc_config). * - * Default: PJ_FALSE + * Default: PJSUA_100REL_NOT_USED */ - pj_bool_t require_100rel; + pjsua_100rel_use require_100rel; /** * Specify the usage of Session Timers for all sessions. See the @@ -1366,6 +1448,35 @@ typedef struct pjsua_config /** + * Flags to be given to pjsua_destroy2() + */ +typedef enum pjsua_destroy_flag +{ + /** + * Allow sending outgoing messages (such as unregistration, event + * unpublication, BYEs, unsubscription, etc.), but do not wait for + * responses. This is useful to perform "best effort" clean up + * without delaying the shutdown process waiting for responses. + */ + PJSUA_DESTROY_NO_RX_MSG = 1, + + /** + * If this flag is set, do not send any outgoing messages at all. + * This flag is useful if application knows that the network which + * the messages are to be sent on is currently down. + */ + PJSUA_DESTROY_NO_TX_MSG = 2, + + /** + * Do not send or receive messages during destroy. This flag is + * shorthand for PJSUA_DESTROY_NO_RX_MSG + PJSUA_DESTROY_NO_TX_MSG. + */ + PJSUA_DESTROY_NO_NETWORK = PJSUA_DESTROY_NO_RX_MSG | + PJSUA_DESTROY_NO_TX_MSG + +} pjsua_destroy_flag; + +/** * Use this function to initialize pjsua config. * * @param cfg pjsua config to be initialized. @@ -1513,6 +1624,8 @@ PJ_DECL(pj_status_t) pjsua_start(void); * Application.may safely call this function more than once if it doesn't * keep track of it's state. * + * @see pjsua_destroy2() + * * @return PJ_SUCCESS on success, or the appropriate error code. */ PJ_DECL(pj_status_t) pjsua_destroy(void); @@ -1527,6 +1640,16 @@ PJ_DECL(pjsua_state) pjsua_get_state(void); /** + * Variant of destroy with additional flags. + * + * @param flags Combination of pjsua_destroy_flag enumeration. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_destroy2(unsigned flags); + + +/** * Poll pjsua for events, and if necessary block the caller thread for * the specified maximum interval (in miliseconds). * @@ -2395,12 +2518,14 @@ typedef struct pjsua_acc_config pj_str_t contact_uri_params; /** - * Specify whether support for reliable provisional response (100rel and - * PRACK) should be required for all sessions of this account. + * Specify how support for reliable provisional response (100rel/ + * PRACK) should be used for all sessions in this account. See the + * documentation of pjsua_100rel_use enumeration for more info. * - * Default: PJ_FALSE + * Default: The default value is taken from the value of + * require_100rel in pjsua_config. */ - pj_bool_t require_100rel; + pjsua_100rel_use require_100rel; /** * Specify the usage of Session Timers for all sessions. See the @@ -2676,13 +2801,28 @@ typedef struct pjsua_acc_config /** * Specify interval of auto registration retry upon registration failure * (including caused by transport problem), in second. Set to 0 to - * disable auto re-registration. + * disable auto re-registration. Note that if the registration retry + * occurs because of transport failure, the first retry will be done + * after \a reg_first_retry_interval seconds instead. Also note that + * the interval will be randomized slightly by approximately +/- ten + * seconds to avoid all clients re-registering at the same time. + * + * See also \a reg_first_retry_interval setting. * * Default: #PJSUA_REG_RETRY_INTERVAL */ unsigned reg_retry_interval; /** + * This specifies the interval for the first registration retry. The + * registration retry is explained in \a reg_retry_interval. Note that + * the value here will also be randomized by +/- ten seconds. + * + * Default: 0 + */ + unsigned reg_first_retry_interval; + + /** * Specify whether calls of the configured account should be dropped * after registration failure and an attempt of re-registration has * also failed. @@ -2721,6 +2861,16 @@ typedef struct pjsua_acc_config * Default: PJSUA_CALL_HOLD_TYPE_DEFAULT */ pjsua_call_hold_type call_hold_type; + + + /** + * Specify whether the account should register as soon as it is + * added to the UA. Application can set this to PJ_FALSE and control + * the registration manually with pjsua_acc_set_registration(). + * + * Default: PJ_TRUE + */ + pj_bool_t register_on_acc_add; } pjsua_acc_config; diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index b15a102e..db566ed5 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -43,6 +43,7 @@ struct pjsua_call_media pjmedia_type type; /**< Media type. */ unsigned idx; /**< This media index in parent call. */ pjsua_call_media_status state; /**< Media state. */ + pjsua_call_media_status prev_state;/**< Previous media state. */ pjmedia_dir dir; /**< Media direction. */ /** The stream */ @@ -78,6 +79,7 @@ struct pjsua_call_media pjmedia_transport *tp_orig; /**< Original media transport */ pj_bool_t tp_auto_del; /**< May delete media transport */ pjsua_med_tp_st tp_st; /**< Media transport state */ + pj_bool_t use_custom_med_tp;/**< Use custom media transport? */ pj_sockaddr rtp_addr; /**< Current RTP source address (used to update ICE default address) */ @@ -130,6 +132,7 @@ struct pjsua_call int secure_level;/**< Signaling security level. */ pjsua_call_hold_type call_hold_type; /**< How to do call hold. */ pj_bool_t local_hold;/**< Flag for call-hold by local. */ + void *hold_msg; /**< Outgoing hold tx_data. */ unsigned med_cnt; /**< Number of media in SDP. */ pjsua_call_media media[PJSUA_MAX_CALL_MEDIA]; /**< Array of media */ @@ -580,7 +583,7 @@ void pjsua_pres_update_acc(int acc_id, pj_bool_t force); /* * Shutdown presence. */ -void pjsua_pres_shutdown(void); +void pjsua_pres_shutdown(unsigned flags); /** * Init presence for aoocunt. @@ -595,12 +598,12 @@ pj_status_t pjsua_pres_init_publish_acc(int acc_id); /** * Send un-PUBLISH */ -void pjsua_pres_unpublish(pjsua_acc *acc); +void pjsua_pres_unpublish(pjsua_acc *acc, unsigned flags); /** * Terminate server subscription for the account */ -void pjsua_pres_delete_acc(int acc_id); +void pjsua_pres_delete_acc(int acc_id, unsigned flags); /** * Init IM module handler to handle incoming MESSAGE outside dialog. @@ -635,7 +638,7 @@ pj_status_t pjsua_media_subsys_start(void); /** * Destroy pjsua media subsystem. */ -pj_status_t pjsua_media_subsys_destroy(void); +pj_status_t pjsua_media_subsys_destroy(unsigned flags); /** * Private: check if we can accept the message. diff --git a/pjsip/src/pjsip-simple/evsub_msg.c b/pjsip/src/pjsip-simple/evsub_msg.c index 77e4b489..df2dd550 100644 --- a/pjsip/src/pjsip-simple/evsub_msg.c +++ b/pjsip/src/pjsip-simple/evsub_msg.c @@ -295,7 +295,7 @@ static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) */ PJ_DEF(void) pjsip_evsub_init_parser(void) { - pjsip_register_hdr_parser( "Event", NULL, + pjsip_register_hdr_parser( "Event", "o", &parse_hdr_event); pjsip_register_hdr_parser( "Subscription-State", NULL, diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c index 4787d9a8..b90725d4 100644 --- a/pjsip/src/pjsip-simple/pidf.c +++ b/pjsip/src/pjsip-simple/pidf.c @@ -324,15 +324,16 @@ PJ_DEF(void) pjpidf_status_construct(pj_pool_t *pool, pjpidf_status *st) PJ_DEF(pj_bool_t) pjpidf_status_is_basic_open(const pjpidf_status *st) { pj_xml_node *node = pj_xml_find_node((pj_xml_node*)st, &BASIC); - pj_assert(node != NULL); + if (!node) + return PJ_FALSE; return pj_stricmp(&node->content, &OPEN)==0; } PJ_DEF(void) pjpidf_status_set_basic_open(pjpidf_status *st, pj_bool_t open) { pj_xml_node *node = pj_xml_find_node(st, &BASIC); - pj_assert(node != NULL); - node->content = open ? OPEN : CLOSED; + if (node) + node->content = open ? OPEN : CLOSED; } PJ_DEF(pjpidf_pres*) pjpidf_create(pj_pool_t *pool, const pj_str_t *entity) diff --git a/pjsip/src/pjsip-ua/sip_100rel.c b/pjsip/src/pjsip-ua/sip_100rel.c index 5d49731e..d2bb1d42 100644 --- a/pjsip/src/pjsip-ua/sip_100rel.c +++ b/pjsip/src/pjsip-ua/sip_100rel.c @@ -105,8 +105,10 @@ typedef struct uas_state_t /* UAC state */ typedef struct uac_state_t { - pj_int32_t cseq; - pj_uint32_t rseq; /* Initialized to -1 */ + pj_str_t tag; /* To tag */ + pj_int32_t cseq; + pj_uint32_t rseq; /* Initialized to -1 */ + struct uac_state_t *next; /* next call leg */ } uac_state_t; @@ -115,7 +117,7 @@ struct dlg_data { pjsip_inv_session *inv; uas_state_t *uas_state; - uac_state_t *uac_state; + uac_state_t *uac_state_list; }; @@ -231,6 +233,8 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_tx_data **p_tdata) { dlg_data *dd; + uac_state_t *uac_state = NULL; + const pj_str_t *to_tag = &rdata->msg_info.to->tag; pjsip_transaction *tsx; pjsip_msg *msg; pjsip_generic_string_hdr *rseq_hdr; @@ -261,41 +265,51 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); if (rseq_hdr == NULL) { PJ_LOG(4,(dd->inv->dlg->obj_name, - "Ignoring provisional response with no RSeq header")); + "Ignoring 100rel response with no RSeq header")); return PJSIP_EMISSINGHDR; } rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); + /* Find UAC state for the specified call leg */ + uac_state = dd->uac_state_list; + while (uac_state) { + if (pj_strcmp(&uac_state->tag, to_tag)==0) + break; + uac_state = uac_state->next; + } + /* Create new UAC state if we don't have one */ - if (dd->uac_state == NULL) { - dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, - uac_state_t); - dd->uac_state->cseq = rdata->msg_info.cseq->cseq; - dd->uac_state->rseq = rseq - 1; + if (uac_state == NULL) { + uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, uac_state_t); + uac_state->cseq = rdata->msg_info.cseq->cseq; + uac_state->rseq = rseq - 1; + pj_strdup(dd->inv->dlg->pool, &uac_state->tag, to_tag); + uac_state->next = dd->uac_state_list; + dd->uac_state_list = uac_state; } - /* If this is from new INVITE transaction, reset UAC state */ - if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) { - dd->uac_state->cseq = rdata->msg_info.cseq->cseq; - dd->uac_state->rseq = rseq - 1; + /* If this is from new INVITE transaction, reset UAC state. */ + if (rdata->msg_info.cseq->cseq != uac_state->cseq) { + uac_state->cseq = rdata->msg_info.cseq->cseq; + uac_state->rseq = rseq - 1; } /* Ignore provisional response retransmission */ - if (rseq <= dd->uac_state->rseq) { + if (rseq <= uac_state->rseq) { /* This should have been handled before */ return PJ_EIGNORED; /* Ignore provisional response with out-of-order RSeq */ - } else if (rseq != dd->uac_state->rseq + 1) { + } else if (rseq != uac_state->rseq + 1) { PJ_LOG(4,(dd->inv->dlg->obj_name, - "Ignoring provisional response because RSeq jump " + "Ignoring 100rel response because RSeq jump " "(expecting %u, got %u)", - dd->uac_state->rseq+1, rseq)); + uac_state->rseq+1, rseq)); return PJ_EIGNORED; } /* Update our RSeq */ - dd->uac_state->rseq = rseq; + uac_state->rseq = rseq; /* Create PRACK */ status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, @@ -303,6 +317,26 @@ PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, if (status != PJ_SUCCESS) return status; + /* If this response is a forked response from a different call-leg, + * update the req URI (https://trac.pjsip.org/repos/ticket/1364) + */ + if (pj_strcmp(&uac_state->tag, &dd->inv->dlg->remote.info->tag)) { + const pjsip_contact_hdr *mhdr; + + mhdr = (const pjsip_contact_hdr*) + pjsip_msg_find_hdr(rdata->msg_info.msg, + PJSIP_H_CONTACT, NULL); + if (!mhdr || !mhdr->uri) { + PJ_LOG(4,(dd->inv->dlg->obj_name, + "Ignoring 100rel response with no or " + "invalid Contact header")); + pjsip_tx_data_dec_ref(tdata); + return PJ_EIGNORED; + } + tdata->msg->line.req.uri = (pjsip_uri*) + pjsip_uri_clone(tdata->pool, mhdr->uri); + } + /* Create RAck header */ rack.ptr = rack_buf; rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index eca737eb..46153c9f 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -195,8 +195,18 @@ void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, pjsip_event *e) { pjsip_inv_state prev_state = inv->state; + pj_bool_t dont_notify = PJ_FALSE; pj_status_t status; + /* Prevent STATE_CALLING from being reported more than once because + * of authentication + * https://trac.pjsip.org/repos/ticket/1318 + */ + if (state==PJSIP_INV_STATE_CALLING && + (inv->cb_called & (1 << PJSIP_INV_STATE_CALLING)) != 0) + { + dont_notify = PJ_TRUE; + } /* If state is confirmed, check that SDP negotiation is done, * otherwise disconnect the session. @@ -224,8 +234,11 @@ void inv_set_state(pjsip_inv_session *inv, pjsip_inv_state state, pj_assert(inv->state != PJSIP_INV_STATE_DISCONNECTED || inv->cause != 0); + /* Mark the callback as called for this state */ + inv->cb_called |= (1 << state); + /* Call on_state_changed() callback. */ - if (mod_inv.cb.on_state_changed && inv->notify) + if (mod_inv.cb.on_state_changed && inv->notify && !dont_notify) (*mod_inv.cb.on_state_changed)(inv, e); /* Only decrement when previous state is not already DISCONNECTED */ @@ -4116,6 +4129,16 @@ static void inv_on_state_confirmed( pjsip_inv_session *inv, pjsip_event *e) /* Not Acceptable */ const pjsip_hdr *accept; + /* The incoming SDP is unacceptable. If the SDP negotiator + * state has just been changed, i.e: DONE -> REMOTE_OFFER, + * revert it back. + */ + if (pjmedia_sdp_neg_get_state(inv->neg) == + PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) + { + pjmedia_sdp_neg_cancel_offer(inv->neg); + } + status = pjsip_dlg_create_response(inv->dlg, rdata, 488, NULL, &tdata); if (status != PJ_SUCCESS) diff --git a/pjsip/src/pjsip/sip_multipart.c b/pjsip/src/pjsip/sip_multipart.c index c4ae647e..45c7fcaa 100644 --- a/pjsip/src/pjsip/sip_multipart.c +++ b/pjsip/src/pjsip/sip_multipart.c @@ -81,10 +81,13 @@ static int multipart_print_body(struct pjsip_msg_body *msg_body, /* Print optional headers */ hdr = part->hdr.next; while (hdr != &part->hdr) { - int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, SIZE_LEFT()); + int printed = pjsip_hdr_print_on((pjsip_hdr*)hdr, p, + SIZE_LEFT()-2); if (printed < 0) return -1; p += printed; + *p++ = '\r'; + *p++ = '\n'; hdr = hdr->next; } diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index bdc7b26f..c127162b 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -146,13 +146,6 @@ static pj_time_val timeout_timer_val = { (64*PJSIP_T1_TIMEOUT)/1000, #define TIMER_INACTIVE 0 #define TIMER_ACTIVE 1 -/* Delay for 1xx retransmission (should be 60 seconds). - * Specify 0 to disable this feature - */ -#ifndef PJSIP_TSX_1XX_RETRANS_DELAY -# define PJSIP_TSX_1XX_RETRANS_DELAY 60 -#endif - /* Prototypes. */ static void lock_tsx(pjsip_transaction *tsx, struct tsx_lock_data *lck); @@ -2118,7 +2111,6 @@ PJ_DEF(pj_status_t) pjsip_tsx_retransmit_no_state(pjsip_transaction *tsx, */ static void tsx_resched_retransmission( pjsip_transaction *tsx ) { - pj_time_val timeout; pj_uint32_t msec_time; pj_assert((tsx->transport_flag & TSX_HAS_PENDING_TRANSPORT) == 0); @@ -2151,11 +2143,15 @@ static void tsx_resched_retransmission( pjsip_transaction *tsx ) } } - timeout.sec = msec_time / 1000; - timeout.msec = msec_time % 1000; - tsx->retransmit_timer.id = TIMER_ACTIVE; - pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, - &timeout); + if (msec_time != 0) { + pj_time_val timeout; + + timeout.sec = msec_time / 1000; + timeout.msec = msec_time % 1000; + tsx->retransmit_timer.id = TIMER_ACTIVE; + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->retransmit_timer, + &timeout); + } } /* @@ -2987,6 +2983,12 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, timeout.sec = timeout.msec = 0; } lock_timer(tsx); + /* In the short period above timer may have been inserted + * by set_timeout() (by CANCEL). Cancel it if necessary. See: + * https://trac.pjsip.org/repos/ticket/1374 + */ + if (tsx->timeout_timer.id) + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); tsx->timeout_timer.id = TIMER_ACTIVE; pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); unlock_timer(tsx); diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index bdf8fe6a..4f82a1a6 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -813,6 +813,8 @@ static pj_status_t get_dest_info(const pjsip_uri *target_uri, if (PJSIP_URI_SCHEME_IS_SIPS(target_uri)) { pjsip_uri *uri = (pjsip_uri*) target_uri; const pjsip_sip_uri *url=(const pjsip_sip_uri*)pjsip_uri_get_uri(uri); + unsigned flag; + dest_info->flag |= (PJSIP_TRANSPORT_SECURE | PJSIP_TRANSPORT_RELIABLE); if (url->maddr_param.slen) pj_strdup(pool, &dest_info->addr.host, &url->maddr_param); @@ -821,6 +823,18 @@ static pj_status_t get_dest_info(const pjsip_uri *target_uri, dest_info->addr.port = url->port; dest_info->type = pjsip_transport_get_type_from_name(&url->transport_param); + /* Double-check that the transport parameter match. + * Sample case: sips:host;transport=tcp + * See https://trac.pjsip.org/repos/ticket/1319 + */ + flag = pjsip_transport_get_flag_from_type(dest_info->type); + if ((flag & dest_info->flag) != dest_info->flag) { + pjsip_transport_type_e t; + + t = pjsip_transport_get_type_from_flag(dest_info->flag); + if (t != PJSIP_TRANSPORT_UNSPECIFIED) + dest_info->type = t; + } } else if (PJSIP_URI_SCHEME_IS_SIP(target_uri)) { pjsip_uri *uri = (pjsip_uri*) target_uri; @@ -1390,6 +1404,9 @@ static void send_raw_resolver_callback( pj_status_t status, pj_assert(addr->count != 0); + /* Avoid tdata destroyed by pjsip_tpmgr_send_raw(). */ + pjsip_tx_data_add_ref(sraw_data->tdata); + data_len = sraw_data->tdata->buf.cur - sraw_data->tdata->buf.start; status = pjsip_tpmgr_send_raw(pjsip_endpt_get_tpmgr(sraw_data->endpt), addr->entry[0].type, diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 9bcd1ddd..226f5d1f 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -470,9 +470,10 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, (int)cfg->id.slen, cfg->id.ptr, id)); /* If accounts has registration enabled, start registration */ - if (pjsua_var.acc[id].cfg.reg_uri.slen) - pjsua_acc_set_registration(id, PJ_TRUE); - else { + if (pjsua_var.acc[id].cfg.reg_uri.slen) { + if (pjsua_var.acc[id].cfg.register_on_acc_add) + pjsua_acc_set_registration(id, PJ_TRUE); + } else { /* Otherwise subscribe to MWI, if it's enabled */ if (pjsua_var.acc[id].cfg.mwi_enabled) pjsua_start_mwi(&pjsua_var.acc[id]); @@ -603,7 +604,7 @@ PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id) } /* Delete server presence subscription */ - pjsua_pres_delete_acc(acc_id); + pjsua_pres_delete_acc(acc_id, 0); /* Release account pool */ if (pjsua_var.acc[acc_id].pool) { @@ -833,7 +834,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, if (acc->cfg.publish_enabled != cfg->publish_enabled) { acc->cfg.publish_enabled = cfg->publish_enabled; if (!acc->cfg.publish_enabled) - pjsua_pres_unpublish(acc); + pjsua_pres_unpublish(acc, 0); else update_reg = PJ_TRUE; } @@ -992,6 +993,7 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, acc->cfg.unreg_timeout = cfg->unreg_timeout; acc->cfg.allow_contact_rewrite = cfg->allow_contact_rewrite; acc->cfg.reg_retry_interval = cfg->reg_retry_interval; + acc->cfg.reg_first_retry_interval = cfg->reg_first_retry_interval; acc->cfg.drop_calls_on_reg_fail = cfg->drop_calls_on_reg_fail; if (acc->cfg.reg_delay_before_refresh != cfg->reg_delay_before_refresh) { acc->cfg.reg_delay_before_refresh = cfg->reg_delay_before_refresh; @@ -1393,7 +1395,7 @@ static pj_bool_t acc_check_nat_addr(pjsua_acc *acc, tp->type_name, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, - ob, + (acc->cfg.use_rfc5626? ob: ""), (int)acc->cfg.contact_params.slen, acc->cfg.contact_params.ptr); if (len < 1) { @@ -1691,11 +1693,14 @@ static void regc_cb(struct pjsip_regc_cbparam *param) pjsua_acc *acc = (pjsua_acc*) param->token; - if (param->regc != acc->regc) + PJSUA_LOCK(); + + if (param->regc != acc->regc) { + PJSUA_UNLOCK(); return; + } pj_log_push_indent(); - PJSUA_LOCK(); /* * Print registration status. @@ -2054,7 +2059,7 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, goto on_return; } - pjsua_pres_unpublish(&pjsua_var.acc[acc_id]); + pjsua_pres_unpublish(&pjsua_var.acc[acc_id], 0); status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata); } @@ -2070,6 +2075,10 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, ®_info); pjsua_var.acc[acc_id].auto_rereg.reg_tp = reg_info.transport; + + if (pjsua_var.ua_cfg.cb.on_reg_started) { + (*pjsua_var.ua_cfg.cb.on_reg_started)(acc_id, renew); + } } if (status != PJ_SUCCESS) { @@ -2529,10 +2538,11 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, /* Create the contact header */ contact->ptr = (char*)pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, - "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s", + "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s%s>%.*s", + (acc->display.slen?"\"" : ""), (int)acc->display.slen, acc->display.ptr, - (acc->display.slen?" " : ""), + (acc->display.slen?"\" " : ""), (secure ? PJSUA_SECURE_SCHEME : "sip"), (int)acc->user_part.slen, acc->user_part.ptr, @@ -2545,7 +2555,7 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uac_contact( pj_pool_t *pool, transport_param, (int)acc->cfg.contact_uri_params.slen, acc->cfg.contact_uri_params.ptr, - ob, + (acc->cfg.use_rfc5626? ob: ""), (int)acc->cfg.contact_params.slen, acc->cfg.contact_params.ptr); @@ -2687,10 +2697,11 @@ PJ_DEF(pj_status_t) pjsua_acc_create_uas_contact( pj_pool_t *pool, /* Create the contact header */ contact->ptr = (char*) pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); contact->slen = pj_ansi_snprintf(contact->ptr, PJSIP_MAX_URL_SIZE, - "%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + "%s%.*s%s<%s:%.*s%s%s%.*s%s:%d%s%.*s>%.*s", + (acc->display.slen?"\"" : ""), (int)acc->display.slen, acc->display.ptr, - (acc->display.slen?" " : ""), + (acc->display.slen?"\" " : ""), (secure ? PJSUA_SECURE_SCHEME : "sip"), (int)acc->user_part.slen, acc->user_part.ptr, @@ -2807,8 +2818,23 @@ static void schedule_reregistration(pjsua_acc *acc) acc->auto_rereg.timer.user_data = acc; /* Reregistration attempt. The first attempt will be done immediately. */ - delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : 0; + delay.sec = acc->auto_rereg.attempt_cnt? acc->cfg.reg_retry_interval : + acc->cfg.reg_first_retry_interval; delay.msec = 0; + + /* Randomize interval by +/- 10 secs */ + if (delay.sec >= 10) { + delay.msec = -10000 + (pj_rand() % 20000); + } else { + delay.sec = 0; + delay.msec = (pj_rand() % 10000); + } + pj_time_val_normalize(&delay); + + PJ_LOG(4,(THIS_FILE, + "Scheduling re-registration retry for acc %d in %u seconds..", + acc->index, delay.sec)); + pjsua_schedule_timer(&acc->auto_rereg.timer, &delay); } diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index ad96a4d6..02a78b93 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -971,7 +971,7 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) /* Verify that we can handle the request. */ options |= PJSIP_INV_SUPPORT_100REL; options |= PJSIP_INV_SUPPORT_TIMER; - if (pjsua_var.acc[acc_id].cfg.require_100rel) + if (pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_MANDATORY) options |= PJSIP_INV_REQUIRE_100REL; if (pjsua_var.media_cfg.enable_ice) options |= PJSIP_INV_SUPPORT_ICE; @@ -1047,6 +1047,19 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) options &= ~(PJSIP_INV_SUPPORT_TIMER); } + /* If 100rel is optional and UAC supports it, use it. */ + if ((options & PJSIP_INV_REQUIRE_100REL)==0 && + pjsua_var.acc[acc_id].cfg.require_100rel == PJSUA_100REL_OPTIONAL) + { + const pj_str_t token = { "100rel", 6}; + pjsip_dialog_cap_status cap_status; + + cap_status = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_SUPPORTED, NULL, + &token); + if (cap_status == PJSIP_DIALOG_CAP_SUPPORTED) + options |= PJSIP_INV_REQUIRE_100REL; + } + /* Create invite session: */ status = pjsip_inv_create_uas( dlg, rdata, NULL, options, &inv); if (status != PJ_SUCCESS) { @@ -1288,6 +1301,7 @@ pj_status_t acquire_call(const char *title, pj_time_val time_start, timeout; pj_gettimeofday(&time_start); + timeout.sec = 0; timeout.msec = PJSUA_ACQUIRE_CALL_TIMEOUT; pj_time_val_normalize(&timeout); @@ -1356,20 +1370,24 @@ pj_status_t acquire_call(const char *title, PJ_DEF(pjsua_conf_port_id) pjsua_call_get_conf_port(pjsua_call_id call_id) { pjsua_call *call; - pjsua_conf_port_id port_id; - pjsip_dialog *dlg; - pj_status_t status; + pjsua_conf_port_id port_id = PJSUA_INVALID_ID; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); - status = acquire_call("pjsua_call_get_conf_port()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) - return PJSUA_INVALID_ID; + /* Use PJSUA_LOCK() instead of acquire_call(): + * https://trac.pjsip.org/repos/ticket/1371 + */ + PJSUA_LOCK(); + if (!pjsua_call_is_active(call_id)) + goto on_return; + + call = &pjsua_var.calls[call_id]; port_id = call->media[call->audio_idx].strm.a.conf_slot; - pjsip_dlg_dec_lock(dlg); +on_return: + PJSUA_UNLOCK(); return port_id; } @@ -1383,18 +1401,23 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, pjsua_call_info *info) { pjsua_call *call; - pjsip_dialog *dlg; unsigned mi; - pj_status_t status; PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); pj_bzero(info, sizeof(*info)); - status = acquire_call("pjsua_call_get_info()", call_id, &call, &dlg); - if (status != PJ_SUCCESS) { - return status; + /* Use PJSUA_LOCK() instead of acquire_call(): + * https://trac.pjsip.org/repos/ticket/1371 + */ + PJSUA_LOCK(); + + call = &pjsua_var.calls[call_id]; + + if (!call->inv) { + PJSUA_UNLOCK(); + return PJSIP_ESESSIONTERMINATED; } /* id and role */ @@ -1520,7 +1543,7 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, PJ_TIME_VAL_SUB(info->total_duration, call->start_time); } - pjsip_dlg_dec_lock(dlg); + PJSUA_UNLOCK(); return PJ_SUCCESS; } @@ -1961,10 +1984,14 @@ PJ_DEF(pj_status_t) pjsua_call_set_hold(pjsua_call_id call_id, /* Add additional headers etc */ pjsua_process_msg_data( tdata, msg_data); + /* Record the tx_data to keep track the operation */ + call->hold_msg = (void*) tdata; + /* Send the request */ status = pjsip_inv_send_msg( call->inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); + call->hold_msg = NULL; goto on_return; } @@ -2531,14 +2558,15 @@ PJ_DEF(void) pjsua_call_hangup_all(void) PJ_LOG(4,(THIS_FILE, "Hangup all calls..")); pj_log_push_indent(); - PJSUA_LOCK(); + // This may deadlock, see https://trac.pjsip.org/repos/ticket/1305 + //PJSUA_LOCK(); for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) { if (pjsua_var.calls[i].inv) pjsua_call_hangup(i, 0, NULL, NULL); } - PJSUA_UNLOCK(); + //PJSUA_UNLOCK(); pj_log_pop_indent(); } @@ -3971,9 +3999,22 @@ static void pjsua_call_on_tsx_state_changed(pjsip_inv_session *inv, &tsx->status_text); } } + } else if (tsx->role == PJSIP_ROLE_UAC && + tsx->last_tx == (pjsip_tx_data*)call->hold_msg && + tsx->state >= PJSIP_TSX_STATE_COMPLETED) + { + /* Monitor the status of call hold request */ + call->hold_msg = NULL; + if (tsx->status_code/100 != 2) { + /* Outgoing call hold failed */ + call->local_hold = PJ_FALSE; + PJ_LOG(3,(THIS_FILE, "Error putting call %d on hold (reason=%d)", + call->index, tsx->status_code)); + } } on_return: + PJSUA_UNLOCK(); pj_log_pop_indent(); } diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 31133b2c..52054226 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -234,6 +234,7 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) pj_list_init(&cfg->reg_hdr_list); pj_list_init(&cfg->sub_hdr_list); cfg->call_hold_type = PJSUA_CALL_HOLD_TYPE_DEFAULT; + cfg->register_on_acc_add = PJ_TRUE; } PJ_DEF(void) pjsua_buddy_config_default(pjsua_buddy_config *cfg) @@ -1340,7 +1341,7 @@ pj_status_t resolve_stun_server(pj_bool_t wait) /* * Destroy pjsua. */ -PJ_DEF(pj_status_t) pjsua_destroy(void) +PJ_DEF(pj_status_t) pjsua_destroy2(unsigned flags) { int i; /* Must be signed */ @@ -1365,12 +1366,14 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) if (pjsua_var.endpt) { unsigned max_wait; - PJ_LOG(4,(THIS_FILE, "Shutting down...")); + PJ_LOG(4,(THIS_FILE, "Shutting down, flags=%d...", flags)); pj_log_push_indent(); /* Terminate all calls. */ - pjsua_call_hangup_all(); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + pjsua_call_hangup_all(); + } /* Set all accounts to offline */ for (i=0; i<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { @@ -1381,10 +1384,10 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) } /* Terminate all presence subscriptions. */ - pjsua_pres_shutdown(); + pjsua_pres_shutdown(flags); /* Destroy media (to shutdown media transports etc) */ - pjsua_media_subsys_destroy(); + pjsua_media_subsys_destroy(flags); /* Wait for sometime until all publish client sessions are done * (ticket #364) @@ -1398,6 +1401,11 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) max_wait = pjsua_var.acc[i].cfg.unpublish_max_wait_time_msec; } + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; + } + /* Second stage, wait for unpublications to complete */ for (i=0; i<(int)(max_wait/50); ++i) { unsigned j; @@ -1427,7 +1435,8 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) if (!pjsua_var.acc[i].valid) continue; - if (pjsua_var.acc[i].regc) { + if (pjsua_var.acc[i].regc && (flags & PJSUA_DESTROY_NO_TX_MSG)==0) + { pjsua_acc_set_registration(i, PJ_FALSE); } } @@ -1452,6 +1461,11 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) max_wait = pjsua_var.acc[i].cfg.unreg_timeout; } + /* No waiting if RX is disabled */ + if (flags & PJSUA_DESTROY_NO_RX_MSG) { + max_wait = 0; + } + /* Second stage, wait for unregistrations to complete */ for (i=0; i<(int)(max_wait/50); ++i) { unsigned j; @@ -1472,8 +1486,9 @@ PJ_DEF(pj_status_t) pjsua_destroy(void) /* Wait for some time to allow unregistration and ICE/TURN * transports shutdown to complete: */ - if (i < 20) + if (i < 20 && (flags & PJSUA_DESTROY_NO_RX_MSG) == 0) { busy_sleep(1000 - i*50); + } PJ_LOG(4,(THIS_FILE, "Destroying...")); @@ -1560,6 +1575,12 @@ PJ_DEF(pjsua_state) pjsua_get_state(void) return pjsua_var.state; } +PJ_DEF(pj_status_t) pjsua_destroy(void) +{ + return pjsua_destroy2(0); +} + + /** * Application is recommended to call this function after all initialization * is done, so that the library can do additional checking set up diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index e7743659..60aebbeb 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -396,7 +396,7 @@ pj_status_t pjsua_media_subsys_start(void) /* * Destroy pjsua media subsystem. */ -pj_status_t pjsua_media_subsys_destroy(void) +pj_status_t pjsua_media_subsys_destroy(unsigned flags) { unsigned i; @@ -441,6 +441,10 @@ pj_status_t pjsua_media_subsys_destroy(void) pjsua_media_channel_deinit(i); } if (call_med->tp && call_med->tp_auto_del) { + /* TODO: check if we're not allowed to send to network in the + * "flags", and if so do not do TURN allocation... + */ + PJ_UNUSED_ARG(flags); pjmedia_transport_close(call_med->tp); } call_med->tp = NULL; @@ -1294,11 +1298,18 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med, if (call_med->tp_st == PJSUA_MED_TP_CREATING) set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); + if (!call_med->tp_orig && + pjsua_var.ua_cfg.cb.on_create_media_transport) + { + call_med->use_custom_med_tp = PJ_TRUE; + } else + call_med->use_custom_med_tp = PJ_FALSE; + #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0) /* This function may be called when SRTP transport already exists * (e.g: in re-invite, update), don't need to destroy/re-create. */ - if (!call_med->tp_orig || call_med->tp == call_med->tp_orig) { + if (!call_med->tp_orig) { pjmedia_srtp_setting srtp_opt; pjmedia_transport *srtp = NULL; @@ -1314,7 +1325,7 @@ static pj_status_t call_media_init_cb(pjsua_call_media *call_med, /* Always create SRTP adapter */ pjmedia_srtp_setting_default(&srtp_opt); srtp_opt.close_member_tp = PJ_TRUE; - /* If media session has been ever established, let's use remote's + /* If media session has been ever established, let's use remote's * preference in SRTP usage policy, especially when it is stricter. */ if (call_med->rem_srtp_use > acc->cfg.use_srtp) @@ -1519,9 +1530,25 @@ static pj_status_t media_channel_init_cb(pjsua_call_id call_id, call->async_call.dlg->pool); } - status = pjmedia_transport_media_create( - call_med->tp, tmp_pool, - 0, call->async_call.rem_sdp, mi); + if (call_med->use_custom_med_tp) { + unsigned custom_med_tp_flags = 0; + + /* Use custom media transport returned by the application */ + call_med->tp = + (*pjsua_var.ua_cfg.cb.on_create_media_transport) + (call_id, mi, call_med->tp, + custom_med_tp_flags); + if (!call_med->tp) { + status = + PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); + } + } + + if (call_med->tp) { + status = pjmedia_transport_media_create( + call_med->tp, tmp_pool, + 0, call->async_call.rem_sdp, mi); + } if (status != PJ_SUCCESS) { call->med_ch_info.status = status; call->med_ch_info.med_idx = mi; @@ -2104,6 +2131,7 @@ static void stop_media_session(pjsua_call_id call_id) PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed", call_id, mi)); + call_med->prev_state = call_med->state; call_med->state = PJSUA_CALL_MEDIA_NONE; } @@ -2133,12 +2161,19 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) PJ_LOG(4,(THIS_FILE, "Call %d: deinitializing media..", call_id)); pj_log_push_indent(); + for (mi=0; mi<call->med_cnt; ++mi) { + pjsua_call_media *call_med = &call->media[mi]; + + if (call_med->type == PJMEDIA_TYPE_AUDIO && call_med->strm.a.stream) + pjmedia_stream_send_rtcp_bye(call_med->strm.a.stream); + } + stop_media_session(call_id); for (mi=0; mi<call->med_cnt; ++mi) { pjsua_call_media *call_med = &call->media[mi]; - if (call_med->tp_st > PJSUA_MED_TP_IDLE) { + if (call_med->tp_st > PJSUA_MED_TP_IDLE) { pjmedia_transport_media_stop(call_med->tp); set_media_tp_state(call_med, PJSUA_MED_TP_IDLE); } @@ -2153,6 +2188,7 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id) pjmedia_transport_close(call_med->tp); call_med->tp = call_med->tp_orig = NULL; } + call_med->tp_orig = NULL; } check_snd_dev_idle(); @@ -2206,6 +2242,8 @@ static pj_status_t audio_channel_update(pjsua_call_media *call_med, if (status != PJ_SUCCESS) goto on_return; + si->rtcp_sdes_bye_disabled = PJ_TRUE; + /* Check if no media is active */ if (si->dir == PJMEDIA_DIR_NONE) { /* Call media state */ @@ -2296,6 +2334,9 @@ static pj_status_t audio_channel_update(pjsua_call_media *call_med, goto on_return; } + if (call_med->prev_state == PJSUA_CALL_MEDIA_NONE) + pjmedia_stream_send_rtcp_sdes(call_med->strm.a.stream); + /* If DTMF callback is installed by application, install our * callback to the session. */ diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index 88ea317f..c78e8b5c 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -1320,13 +1320,17 @@ pj_status_t pjsua_pres_init_acc(int acc_id) /* Unpublish presence publication */ -void pjsua_pres_unpublish(pjsua_acc *acc) +void pjsua_pres_unpublish(pjsua_acc *acc, unsigned flags) { if (acc->publish_sess) { pjsua_acc_config *acc_cfg = &acc->cfg; acc->online_status = PJ_FALSE; - send_publish(acc->index, PJ_FALSE); + + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + send_publish(acc->index, PJ_FALSE); + } + /* By ticket #364, don't destroy the session yet (let the callback destroy it) if (acc->publish_sess) { @@ -1339,7 +1343,7 @@ void pjsua_pres_unpublish(pjsua_acc *acc) } /* Terminate server subscription for the account */ -void pjsua_pres_delete_acc(int acc_id) +void pjsua_pres_delete_acc(int acc_id, unsigned flags) { pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_srv_pres *uapres; @@ -1361,11 +1365,15 @@ void pjsua_pres_delete_acc(int acc_id) pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; pjsip_pres_set_status(uapres->sub, &pres_status); - if (pjsip_pres_notify(uapres->sub, - PJSIP_EVSUB_STATE_TERMINATED, NULL, - &reason, &tdata)==PJ_SUCCESS) - { - pjsip_pres_send_request(uapres->sub, tdata); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + if (pjsip_pres_notify(uapres->sub, + PJSIP_EVSUB_STATE_TERMINATED, NULL, + &reason, &tdata)==PJ_SUCCESS) + { + pjsip_pres_send_request(uapres->sub, tdata); + } + } else { + pjsip_pres_terminate(uapres->sub, PJ_FALSE); } uapres = next; @@ -1376,7 +1384,7 @@ void pjsua_pres_delete_acc(int acc_id) pj_list_init(&acc->pres_srv_list); /* Terminate presence publication, if any */ - pjsua_pres_unpublish(acc); + pjsua_pres_unpublish(acc, flags); } @@ -2251,6 +2259,10 @@ static void pres_timer_cb(pj_timer_heap_t *th, for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { pjsua_acc *acc = &pjsua_var.acc[i]; + /* Acc may not be ready yet, otherwise assertion will happen */ + if (!pjsua_acc_is_valid(i)) + continue; + /* Retry PUBLISH */ if (acc->cfg.publish_enabled && acc->publish_sess==NULL) pjsua_pres_init_publish_acc(acc->index); @@ -2324,7 +2336,7 @@ pj_status_t pjsua_pres_start(void) /* * Shutdown presence. */ -void pjsua_pres_shutdown(void) +void pjsua_pres_shutdown(unsigned flags) { unsigned i; @@ -2339,18 +2351,20 @@ void pjsua_pres_shutdown(void) for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { if (!pjsua_var.acc[i].valid) continue; - pjsua_pres_delete_acc(i); + pjsua_pres_delete_acc(i, flags); } for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { pjsua_var.buddy[i].monitor = 0; } - refresh_client_subscriptions(); + if ((flags & PJSUA_DESTROY_NO_TX_MSG) == 0) { + refresh_client_subscriptions(); - for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { - if (pjsua_var.acc[i].valid) - pjsua_pres_update_acc(i, PJ_FALSE); + for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { + if (pjsua_var.acc[i].valid) + pjsua_pres_update_acc(i, PJ_FALSE); + } } pj_log_pop_indent(); |