Index: pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 162)
+++ pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 163)
@@ -98,4 +98,15 @@
 };
 
+
+/**
+ * Get the state string description of the specified state.
+ *
+ * @param state		Negotiator state.
+ *
+ * @return		String description of the state.
+ */
+PJ_DECL(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state);
+
+
 /**
  * Create the SDP negotiator with local offer. The SDP negotiator then
Index: pjproject/trunk/pjmedia/src/pjmedia/sdp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp.c	(revision 162)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp.c	(revision 163)
@@ -402,4 +402,7 @@
     p = tempbuf;
     endbuf = tempbuf+sizeof(tempbuf);
+
+    /* Add colon */
+    *p++ = ':';
 
     /* Add payload type. */
Index: pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 162)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp_neg.c	(revision 163)
@@ -40,4 +40,24 @@
 			*neg_remote_sdp;    /**< Temporary remote SDP.	     */
 };
+
+static const char *state_str[] = 
+{
+    "STATE_NULL",
+    "STATE_LOCAL_OFFER",
+    "STATE_REMOTE_OFFER",
+    "STATE_WAIT_NEGO",
+    "STATE_DONE",
+};
+
+/*
+ * Get string representation of negotiator state.
+ */
+PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state)
+{
+    if (state >=0 && state < PJ_ARRAY_SIZE(state_str))
+	return state_str[state];
+
+    return "<?UNKNOWN?>";
+}
 
 
Index: pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 162)
+++ pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 163)
@@ -378,4 +378,22 @@
 
 
+#if 0
+ If we implement this, we need to send SDP answer automatically on 
+ subsequent request/response. Something like "has_answer" flag.
+
+/**
+ * Set local answer to respond to remote SDP offer. By calling this function,
+ * application can start SDP negotiation immediately without having to
+ * send any request or response message. This function is normally called
+ * when on_rx_offer() callback is called.
+ *
+ * @param inv		The invite session.
+ * @param sdp		The SDP description which will be set as answer
+ *			to remote.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_set_sdp_answer( pjsip_inv_session *inv,
+					       const pjmedia_sdp_session *sdp );
+#endif
+
 /**
  * Create a SIP message to initiate invite session termination. Depending on 
Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 162)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 163)
@@ -29,4 +29,6 @@
 #include <pj/assert.h>
 #include <pj/os.h>
+#include <pj/log.h>
+
 
 #define THIS_FILE	"sip_invite_session.c"
@@ -87,4 +89,12 @@
 
 
+/* Invite session data to be attached to transaction. */
+struct tsx_inv_data
+{
+    pjsip_inv_session	*inv;
+    pj_bool_t		 sdp_done;
+};
+
+
 /*
  * Module load()
@@ -705,4 +715,5 @@
 {
     pjsip_inv_session *inv;
+    struct tsx_inv_data *tsx_inv_data;
     pjsip_msg *msg;
     pjmedia_sdp_session *rem_sdp = NULL;
@@ -778,5 +789,10 @@
     /* Save the invite transaction. */
     inv->invite_tsx = pjsip_rdata_get_tsx(rdata);
-    inv->invite_tsx->mod_data[mod_inv.mod.id] = inv;
+
+    /* Attach our data to the transaction. */
+    tsx_inv_data = pj_pool_zalloc(inv->invite_tsx->pool, 
+				  sizeof(struct tsx_inv_data));
+    tsx_inv_data->inv = inv;
+    inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
 
     /* Done */
@@ -892,4 +908,124 @@
     return status;
 }
+
+/*
+ * Check in incoming message for SDP offer/answer.
+ */
+static void inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
+					   pjsip_transaction *tsx,
+					   pjsip_rx_data *rdata)
+{
+    struct tsx_inv_data *tsx_inv_data;
+    static const pj_str_t str_application = { "application", 11 };
+    static const pj_str_t str_sdp = { "sdp", 3 };
+    pj_status_t status;
+    pjsip_msg *msg;
+    pjmedia_sdp_session *sdp;
+
+    /* Get/attach invite session's transaction data */
+    tsx_inv_data = tsx->mod_data[mod_inv.mod.id];
+    if (tsx_inv_data == NULL) {
+	tsx_inv_data = pj_pool_zalloc(tsx->pool, sizeof(struct tsx_inv_data));
+	tsx_inv_data->inv = inv;
+	tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
+    }
+
+    /* MUST NOT do multiple SDP offer/answer in a single transaction. 
+     */
+
+    if (tsx_inv_data->sdp_done)
+	return;
+
+    /* Check if SDP is present in the message. */
+
+    msg = rdata->msg_info.msg;
+    if (msg->body == NULL) {
+	/* Message doesn't have body. */
+	return;
+    }
+
+    if (pj_stricmp(&msg->body->content_type.type, &str_application) ||
+	pj_stricmp(&msg->body->content_type.subtype, &str_sdp))
+    {
+	/* Message body is not "application/sdp" */
+	return;
+    }
+
+    /* Parse the SDP body. */
+
+    status = pjmedia_sdp_parse(rdata->tp_info.pool, msg->body->data,
+			       msg->body->len, &sdp);
+    if (status != PJ_SUCCESS) {
+	char errmsg[PJ_ERR_MSG_SIZE];
+	pj_strerror(status, errmsg, sizeof(errmsg));
+	PJ_LOG(4,(THIS_FILE, "Error parsing SDP in %s: %s",
+		  pjsip_rx_data_get_info(rdata), errmsg));
+	return;
+    }
+
+    /* The SDP can be an offer or answer, depending on negotiator's state */
+
+    if (inv->neg == NULL ||
+	pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE) 
+    {
+
+	/* This is an offer. */
+
+	if (inv->neg == NULL) {
+	    status=pjmedia_sdp_neg_create_w_remote_offer(inv->pool, NULL, 
+							 sdp, &inv->neg);
+	} else {
+	    status=pjmedia_sdp_neg_set_remote_offer(inv->pool, inv->neg, sdp);
+	}
+
+	if (status != PJ_SUCCESS) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(THIS_FILE, "Error processing SDP offer in %s: %s",
+		      pjsip_rx_data_get_info(rdata), errmsg));
+	    return;
+	}
+
+	/* Inform application about remote offer. */
+
+	if (mod_inv.cb.on_rx_offer)
+	    (*mod_inv.cb.on_rx_offer)(inv);
+
+    } else if (pjmedia_sdp_neg_get_state(inv->neg) == 
+		PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) 
+    {
+
+	/* This is an answer. 
+	 * Process and negotiate remote answer.
+	 */
+
+	status = pjmedia_sdp_neg_set_remote_answer(inv->pool, inv->neg, sdp);
+
+	if (status != PJ_SUCCESS) {
+	    char errmsg[PJ_ERR_MSG_SIZE];
+	    pj_strerror(status, errmsg, sizeof(errmsg));
+	    PJ_LOG(4,(THIS_FILE, "Error processing SDP answer in %s: %s",
+		      pjsip_rx_data_get_info(rdata), errmsg));
+	    return;
+	}
+
+	/* Negotiate SDP */
+
+	inv_negotiate_sdp(inv);
+
+	/* Mark this transaction has having SDP offer/answer done. */
+
+	tsx_inv_data->sdp_done = 1;
+
+    } else {
+	
+	PJ_LOG(5,(THIS_FILE, "Ignored SDP in %s: negotiator state is %s",
+	      pjsip_rx_data_get_info(rdata), 
+	      pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv->neg))));
+    }
+
+}
+
+
 
 /*
@@ -949,12 +1085,22 @@
      */
     if (st_code/10 == 18 || st_code/10 == 20) {
-	const pjmedia_sdp_session *local;
-
-	status = pjmedia_sdp_neg_get_neg_local(inv->neg, &local);
-	if (status == PJ_SUCCESS)
-	    last_res->msg->body = create_sdp_body(last_res->pool, local);
+
+	pjmedia_sdp_neg_state neg_state;
+
+	neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) :
+		    PJMEDIA_SDP_NEG_STATE_NULL;
+
+	if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
+	    neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
+	{
+	    const pjmedia_sdp_session *local;
+
+	    status = pjmedia_sdp_neg_get_neg_local(inv->neg, &local);
+	    if (status == PJ_SUCCESS)
+		last_res->msg->body = create_sdp_body(last_res->pool, local);
+	}
 
 	/* Start negotiation, if ready. */
-	if (pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
+	if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) {
 	    status = inv_negotiate_sdp(inv);
 	    if (status != PJ_SUCCESS) {
@@ -1107,4 +1253,5 @@
     if (tdata->msg->type == PJSIP_REQUEST_MSG) {
 	pjsip_transaction *tsx;
+	struct tsx_inv_data *tsx_inv_data;
 
 	status = pjsip_dlg_send_request(inv->dlg, tdata, &tsx);
@@ -1112,5 +1259,8 @@
 	    return status;
 
-	tsx->mod_data[mod_inv.mod.id] = inv;
+	tsx_inv_data = pj_pool_zalloc(tsx->pool, sizeof(struct tsx_inv_data));
+	tsx_inv_data->inv = inv;
+
+	tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
 	tsx->mod_data[mod_inv.app_user->id] = token;
 
@@ -1332,5 +1482,10 @@
 	case PJSIP_TSX_STATE_PROCEEDING:
 	    if (dlg->remote.info->tag.slen) {
+
 		inv_set_state(inv, PJSIP_INV_STATE_EARLY, e);
+
+		inv_check_sdp_in_incoming_msg(inv, tsx, 
+					      e->body.tsx_state.src.rdata);
+
 	    } else {
 		/* Ignore 100 (Trying) response, as it doesn't change
@@ -1348,4 +1503,7 @@
 		pj_assert(0);
 		inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e);
+    
+		inv_check_sdp_in_incoming_msg(inv, tsx, 
+					      e->body.tsx_state.src.rdata);
 
 	    } else if (tsx->status_code==401 || tsx->status_code==407) {
@@ -1396,8 +1554,12 @@
 		inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e);
 
+		inv_check_sdp_in_incoming_msg(inv, tsx, 
+					      e->body.tsx_state.src.rdata);
+
 		/* Send ACK */
 		pj_assert(e->body.tsx_state.type == PJSIP_EVENT_RX_MSG);
 
 		inv_send_ack(inv, e->body.tsx_state.src.rdata);
+
 
 	    } else  {
@@ -1497,10 +1659,20 @@
 	    /* Send/received another provisional response. */
 	    inv_set_state(inv, PJSIP_INV_STATE_EARLY, e);
+
+	    if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
+		inv_check_sdp_in_incoming_msg(inv, tsx, 
+					      e->body.tsx_state.src.rdata);
+	    }
 	    break;
 
 	case PJSIP_TSX_STATE_COMPLETED:
-	    if (tsx->status_code/100 == 2)
+	    if (tsx->status_code/100 == 2) {
 		inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e);
-	    else
+		if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
+		    inv_check_sdp_in_incoming_msg(inv, tsx, 
+						  e->body.tsx_state.src.rdata);
+		}
+
+	    } else
 		inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
 	    break;
@@ -1517,4 +1689,9 @@
 		/* Set state to CONNECTING */
 		inv_set_state(inv, PJSIP_INV_STATE_CONNECTING, e);
+
+		if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG) {
+		    inv_check_sdp_in_incoming_msg(inv, tsx, 
+						  e->body.tsx_state.src.rdata);
+		}
 
 		/* if UAC, send ACK and move state to confirmed. */
@@ -1606,5 +1783,6 @@
     } else if (tsx->method.id == PJSIP_BYE_METHOD &&
 	       tsx->role == PJSIP_ROLE_UAC &&
-	       tsx->state == PJSIP_TSX_STATE_COMPLETED)
+	       (tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	        tsx->state == PJSIP_TSX_STATE_TERMINATED))
     {
 
@@ -1631,5 +1809,6 @@
     if (tsx->method.id == PJSIP_BYE_METHOD &&
 	tsx->role == PJSIP_ROLE_UAC &&
-	tsx->state == PJSIP_TSX_STATE_COMPLETED)
+	(tsx->state == PJSIP_TSX_STATE_COMPLETED ||
+	 tsx->state == PJSIP_TSX_STATE_TERMINATED))
     {
 
@@ -1638,8 +1817,5 @@
 	 */
 
-	if (e->body.tsx_state.type == PJSIP_EVENT_RX_MSG)
-	    inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e);
-	else
-	    inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+	inv_handle_bye_response( inv, tsx, e->body.tsx_state.src.rdata, e);
 
     }
Index: pjproject/trunk/pjsip/src/pjsip/sip_transport.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 162)
+++ pjproject/trunk/pjsip/src/pjsip/sip_transport.c	(revision 163)
@@ -794,5 +794,9 @@
      * destroyed).
      */
-    pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
+    //pj_assert(pj_atomic_get(mgr->tdata_counter) == 0);
+    if (pj_atomic_get(mgr->tdata_counter) != 0) {
+	PJ_LOG(4,(THIS_FILE, "Warning: %d transmit buffers are not freed!",
+		  pj_atomic_get(mgr->tdata_counter)));
+    }
 #endif
 
Index: pjproject/trunk/pjsip/src/pjsua/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 162)
+++ pjproject/trunk/pjsip/src/pjsua/pjsua_core.c	(revision 163)
@@ -78,5 +78,5 @@
     pj_strerror(status, errmsg, sizeof(errmsg));
 
-    PJ_LOG(1,(THIS_FILE, "%s: %s", title, errmsg));
+    PJ_LOG(1,(THIS_FILE, "%s: %s [code=%d]", title, errmsg, status));
 }
 
@@ -230,4 +230,6 @@
 	    pjmedia_session_destroy(inv_data->session);
 	    inv_data->session = NULL;
+
+	    PJ_LOG(3,(THIS_FILE,"Media session is destroyed"));
 	}
 
@@ -302,4 +304,5 @@
     }
 
+    PJ_LOG(3,(THIS_FILE,"Media has been started successfully"));
 }
 
