Re #1395: Backport of PJSIP 1.x branch into PJSIP 2.0 trunk

* Backport of r3557:r3832

TODO: ticket #1268 (Option for automatic/manual sending of RTCP SDES/BYE for the stream) for video stream.



git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@3841 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h
index 4fe4475..f3bceb2 100644
--- a/pjsip/include/pjsip-ua/sip_inv.h
+++ b/pjsip/include/pjsip-ua/sip_inv.h
@@ -367,6 +367,7 @@
     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 78c324b..6398e77 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -668,6 +668,16 @@
 #   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 3f3dbb3..e957271 100644
--- a/pjsip/include/pjsip/sip_endpoint.h
+++ b/pjsip/include/pjsip/sip_endpoint.h
@@ -56,8 +56,8 @@
  *    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 636d095..cedb1d9 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -539,6 +539,22 @@
 
 
 /**
+ * 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 @@
 
 
     /**
+     * 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 @@
 				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 @@
 
 
 /**
+ * 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 @@
     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 @@
 
 
 /**
+ * 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 @@
  * 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 @@
 
 
 /**
+ * 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 @@
     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 @@
     /**
      * 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 @@
      * 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 b15a102..db566ed 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -43,6 +43,7 @@
     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 @@
     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 @@
     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 @@
 /*
  * Shutdown presence.
  */
-void pjsua_pres_shutdown(void);
+void pjsua_pres_shutdown(unsigned flags);
 
 /**
  * Init presence for aoocunt.
@@ -595,12 +598,12 @@
 /**
  *  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 @@
 /**
  * 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 77e4b48..df2dd55 100644
--- a/pjsip/src/pjsip-simple/evsub_msg.c
+++ b/pjsip/src/pjsip-simple/evsub_msg.c
@@ -295,7 +295,7 @@
  */
 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 4787d9a..b90725d 100644
--- a/pjsip/src/pjsip-simple/pidf.c
+++ b/pjsip/src/pjsip-simple/pidf.c
@@ -324,15 +324,16 @@
 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 5d49731..d2bb1d4 100644
--- a/pjsip/src/pjsip-ua/sip_100rel.c
+++ b/pjsip/src/pjsip-ua/sip_100rel.c
@@ -105,8 +105,10 @@
 /* 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 @@
 {
 	pjsip_inv_session	*inv;
 	uas_state_t		*uas_state;
-	uac_state_t		*uac_state;
+	uac_state_t		*uac_state_list;
 };
 
 
@@ -231,6 +233,8 @@
 					       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 @@
 	       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);
 
-    /* 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;
+    /* 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;
     }
 
-    /* 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;
+    /* Create new UAC state if we don't have one */
+    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 != 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 @@
     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 eca737e..46153c9 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -195,8 +195,18 @@
 		   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 @@
     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 @@
 		/* 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 c4ae647..45c7fca 100644
--- a/pjsip/src/pjsip/sip_multipart.c
+++ b/pjsip/src/pjsip/sip_multipart.c
@@ -81,10 +81,13 @@
 	/* 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 bdc7b26..c127162 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -146,13 +146,6 @@
 #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 @@
  */
 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 @@
 	}
     }
 
-    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 @@
 	    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 bdf8fe6..4f82a1a 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -813,6 +813,8 @@
     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 @@
         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 @@
 
 	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 9bcd1dd..226f5d1 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -470,9 +470,10 @@
 	      (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 @@
     }
 
     /* 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 @@
     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 @@
     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 @@
 			       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 @@
 
     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 @@
 	    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 @@
 
 	pjsip_regc_get_info(pjsua_var.acc[acc_id].regc, &reg_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 @@
     /* 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 @@
 				     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 @@
     /* 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 @@
     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 ad96a4d..02a78b9 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -971,7 +971,7 @@
     /* 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 @@
 	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_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_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 @@
 					 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_TIME_VAL_SUB(info->total_duration, call->start_time);
     }
 
-    pjsip_dlg_dec_lock(dlg);
+    PJSUA_UNLOCK();
 
     return PJ_SUCCESS;
 }
@@ -1961,10 +1984,14 @@
     /* 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_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 @@
 						    &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 31133b2..5205422 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -234,6 +234,7 @@
     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 @@
 /*
  * 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 @@
     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 @@
 	}
 
 	/* 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 @@
 		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 @@
 	    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 @@
 		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 @@
 	/* 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 @@
     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 e774365..60aebbe 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -396,7 +396,7 @@
 /*
  * 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 @@
 		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 @@
     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 @@
 	/* 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 @@
                             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 @@
 
 	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_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 @@
 	    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 @@
     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 @@
 	    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 88ea317..c78e8b5 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -1320,13 +1320,17 @@
 
 
 /* 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 @@
 }
 
 /* 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 @@
 	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 @@
     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 @@
     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 @@
 /*
  * Shutdown presence.
  */
-void pjsua_pres_shutdown(void)
+void pjsua_pres_shutdown(unsigned flags)
 {
     unsigned i;
 
@@ -2339,18 +2351,20 @@
     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();