Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3758)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 3763)
@@ -462,4 +462,76 @@
 						 registration callback.	*/
 } pjsua_reg_info;
+
+
+/** 
+ * Enumeration of media transport state types.
+ */
+typedef enum pjsua_med_tp_st
+{
+    /** Null, this is the state before media transport is created. */
+    PJSUA_MED_TP_NULL,
+
+    /**
+     * Just before media transport is created, which can finish
+     * asynchronously later.
+     */
+    PJSUA_MED_TP_CREATING,
+
+    /** Media transport creation is completed, but not initialized yet. */
+    PJSUA_MED_TP_IDLE,
+
+    /** Initialized (media_create() has been called). */
+    PJSUA_MED_TP_INIT,
+
+    /** Running (media_start() has been called). */
+    PJSUA_MED_TP_RUNNING,
+
+    /** Disabled (transport is initialized, but media is being disabled). */
+    PJSUA_MED_TP_DISABLED
+
+} pjsua_med_tp_st;
+
+
+/**
+ * Structure to be passed on media transport state callback.
+ */
+typedef struct pjsua_med_tp_state_info
+{
+    /**
+     * The media index.
+     */
+    unsigned             med_idx;
+
+    /**
+     * The media transport state
+     */
+    pjsua_med_tp_st      state;
+
+    /**
+     * The last error code related to the media transport state.
+     */
+    pj_status_t		 status;
+
+    /**
+     * Optional SIP error code.
+     */
+    int		         sip_err_code;
+
+    /**
+     * Optional extended info, the content is specific for each transport type.
+     */
+    void		*ext_info;
+
+} pjsua_med_tp_state_info;
+
+
+/**
+  * Type of callback to be called when media transport state is changed.
+  *
+  * @param call_id	The call ID.
+  * @param info         The media transport state info.
+  */
+typedef void (*pjsua_med_tp_state_cb)(pjsua_call_id call_id,
+                                      const pjsua_med_tp_state_info *info);
 
 
@@ -950,4 +1022,10 @@
 
     /**
+     * This callback is called when media transport state is changed. See
+     * also #pjsua_med_tp_state_cb.
+     */
+    pjsua_med_tp_state_cb on_call_media_transport_state;
+
+    /**
      * This callback is called to report error in ICE media transport.
      * Currently it is used to report TURN Refresh error.
@@ -1363,4 +1441,16 @@
  */
 PJ_DECL(void) pjsua_msg_data_init(pjsua_msg_data *msg_data);
+
+
+/**
+ * Clone message data.
+ *
+ * @param pool	    Pool to allocate memory for the new message data.
+ * @param rhs       Message data to be cloned.
+ *
+ * @return          The new message data.
+ */
+PJ_DECL(pjsua_msg_data*) pjsua_msg_data_clone(pj_pool_t *pool,
+                                              const pjsua_msg_data *rhs);
 
 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 3758)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 3763)
@@ -28,25 +28,9 @@
 PJ_BEGIN_DECL
 
-/** 
- * Media transport state.
- */
-typedef enum pjsua_med_tp_st
-{
-    /** Not initialized */
-    PJSUA_MED_TP_IDLE,
-
-    /** Initialized (media_create() has been called) */
-    PJSUA_MED_TP_INIT,
-
-    /** Running (media_start() has been called) */
-    PJSUA_MED_TP_RUNNING,
-
-    /** Disabled (transport is initialized, but media is being disabled) */
-    PJSUA_MED_TP_DISABLED
-
-} pjsua_med_tp_st;
-
 /** Forward decl of pjsua call */
 typedef struct pjsua_call pjsua_call;
+
+/** Forward decl of pjsua call media */
+typedef struct pjsua_call_media pjsua_call_media;
 
 
@@ -93,5 +77,5 @@
     pj_status_t		 tp_ready;  /**< Media transport status.	    */
     pjmedia_transport	*tp_orig;   /**< Original media transport	    */
-    pj_bool_t		 tp_auto_del; /**< May delete media transport   */
+    pj_bool_t		 tp_auto_del; /**< May delete media transport       */
     pjsua_med_tp_st	 tp_st;     /**< Media transport state		    */
     pj_sockaddr		 rtp_addr;  /**< Current RTP source address
@@ -100,6 +84,14 @@
     pjmedia_srtp_use	 rem_srtp_use; /**< Remote's SRTP usage policy.	    */
 
-    pjmedia_event_subscription esub_rend;/**< Subscribe renderer events.     */
+    pjmedia_event_subscription esub_rend;/**< Subscribe renderer events.    */
     pjmedia_event_subscription esub_cap;/**< Subscribe capture events.      */
+
+    pjsua_med_tp_state_cb      med_init_cb;/**< Media transport
+                                                initialization callback.    */
+
+    /** Media transport creation callback. */
+    pj_status_t (*med_create_cb)(pjsua_call_media *call_med,
+                                 pj_status_t status, int security_level,
+                                 int *sip_err_code);
 } pjsua_call_media;
 
@@ -133,4 +125,7 @@
     pjsua_call_media     media[PJSUA_MAX_CALL_MEDIA]; /**< Array of media   */
     int			 audio_idx; /**< First active audio media.	    */
+    pj_mutex_t          *med_ch_mutex;/**< Media channel callback's mutex.  */
+    pjsua_med_tp_state_cb   med_ch_cb;/**< Media channel callback.	    */
+    pjsua_med_tp_state_info med_ch_info;/**< Media channel info.            */
 
     pjsip_evsub		*xfer_sub;  /**< Xfer server subscription, if this
@@ -148,4 +143,15 @@
 					  contains multiple codecs.	    */
 
+    struct {
+        pjsip_dialog        *dlg;    /**< Call dialog.                      */
+        pjmedia_sdp_session *rem_sdp;/**< Remote SDP.                       */
+        union {
+            struct {
+                unsigned         options; /**< Outgoing call options.       */
+                pjsua_msg_data  *msg_data;/**< Headers for outgoing INVITE. */
+            } out_call;
+        } call_var;
+    } async_call;                      /**< Temporary storage for async
+                                            outgoing/incoming call.         */
 };
 
@@ -509,5 +515,7 @@
 				     pj_pool_t *tmp_pool,
 				     const pjmedia_sdp_session *rem_sdp,
-				     int *sip_err_code);
+				     int *sip_err_code,
+                                     pj_bool_t async,
+                                     pjsua_med_tp_state_cb cb);
 pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, 
 					   pj_pool_t *pool,
@@ -524,5 +532,7 @@
 				  const pjsua_transport_config *tcfg,
 				  int security_level,
-				  int *sip_err_code);
+				  int *sip_err_code,
+                                  pj_bool_t async,
+                                  pjsua_med_tp_state_cb cb);
 pj_status_t video_channel_update(pjsua_call_media *call_med,
                                  pj_pool_t *tmp_pool,
@@ -530,4 +540,5 @@
 			         const pjmedia_sdp_session *remote_sdp);
 void stop_video_stream(pjsua_call_media *call_med);
+void set_media_tp_state(pjsua_call_media *call_med, pjsua_med_tp_st tp_st);
 
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 3758)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 3763)
@@ -332,4 +332,150 @@
 */
 
+static pj_status_t
+on_make_call_med_tp_complete(pjsua_call_id call_id,
+                             const pjsua_med_tp_state_info *info)
+{
+    pjmedia_sdp_session *offer;
+    pjsip_inv_session *inv = NULL;
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pjsua_acc *acc = &pjsua_var.acc[call->acc_id];
+    pjsip_dialog *dlg = call->async_call.dlg;
+    unsigned options = call->async_call.call_var.out_call.options;
+    pjsip_tx_data *tdata;
+    pj_status_t status = (info? info->status: PJ_SUCCESS);
+
+    PJSUA_LOCK();
+
+    /* Increment the dialog's lock otherwise when invite session creation
+     * fails the dialog will be destroyed prematurely.
+     */
+    pjsip_dlg_inc_lock(dlg);
+
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
+	goto on_error;
+    }
+
+    /* Create offer */
+    status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
+					    &offer, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
+	goto on_error;
+    }
+
+    /* Create the INVITE session: */
+    options |= PJSIP_INV_SUPPORT_100REL;
+    if (acc->cfg.require_100rel)
+	options |= PJSIP_INV_REQUIRE_100REL;
+    if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
+	options |= PJSIP_INV_SUPPORT_TIMER;
+	if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
+	    options |= PJSIP_INV_REQUIRE_TIMER;
+	else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
+	    options |= PJSIP_INV_ALWAYS_USE_TIMER;
+    }
+
+    status = pjsip_inv_create_uac( dlg, offer, options, &inv);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Invite session creation failed", status);
+	goto on_error;
+    }
+
+    /* Init Session Timers */
+    status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Session Timer init failed", status);
+	goto on_error;
+    }
+
+    /* Create and associate our data in the session. */
+    call->inv = inv;
+
+    dlg->mod_data[pjsua_var.mod.id] = call;
+    inv->mod_data[pjsua_var.mod.id] = call;
+
+    /* If account is locked to specific transport, then lock dialog
+     * to this transport too.
+     */
+    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
+	pjsip_tpselector tp_sel;
+
+	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
+	pjsip_dlg_set_transport(dlg, &tp_sel);
+    }
+
+    /* Set dialog Route-Set: */
+    if (!pj_list_empty(&acc->route_set))
+	pjsip_dlg_set_route_set(dlg, &acc->route_set);
+
+
+    /* Set credentials: */
+    if (acc->cred_cnt) {
+	pjsip_auth_clt_set_credentials( &dlg->auth_sess, 
+					acc->cred_cnt, acc->cred);
+    }
+
+    /* Set authentication preference */
+    pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
+
+    /* Create initial INVITE: */
+
+    status = pjsip_inv_invite(inv, &tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", 
+		     status);
+	goto on_error;
+    }
+
+
+    /* Add additional headers etc */
+
+    pjsua_process_msg_data( tdata,
+                            call->async_call.call_var.out_call.msg_data);
+
+    /* Must increment call counter now */
+    ++pjsua_var.call_cnt;
+
+    /* Send initial INVITE: */
+
+    status = pjsip_inv_send_msg(inv, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", 
+		     status);
+
+	/* Upon failure to send first request, the invite 
+	 * session would have been cleared.
+	 */
+	inv = NULL;
+	goto on_error;
+    }
+
+    /* Done. */
+
+    pjsip_dlg_dec_lock(dlg);
+    PJSUA_UNLOCK();
+
+    return PJ_SUCCESS;
+
+on_error:
+    if (dlg) {
+	/* This may destroy the dialog */
+	pjsip_dlg_dec_lock(dlg);
+    }
+
+    if (inv != NULL) {
+	pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
+    }
+
+    if (call_id != -1) {
+	reset_call(call_id);
+	pjsua_media_channel_deinit(call_id);
+    }
+
+    PJSUA_UNLOCK();
+    return status;
+}
+
 
 /*
@@ -345,11 +491,8 @@
     pj_pool_t *tmp_pool = NULL;
     pjsip_dialog *dlg = NULL;
-    pjmedia_sdp_session *offer;
-    pjsip_inv_session *inv = NULL;
     pjsua_acc *acc;
     pjsua_call *call;
     int call_id = -1;
     pj_str_t contact;
-    pjsip_tx_data *tdata;
     pj_status_t status;
 
@@ -377,6 +520,4 @@
 	pjsua_var.null_snd==NULL && !pjsua_var.no_snd) 
     {
-	pj_status_t status;
-
 	status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
 	if (status != PJ_SUCCESS)
@@ -463,115 +604,32 @@
      * fails the dialog will be destroyed prematurely.
      */
-    pjsip_dlg_inc_lock(dlg);
+//    pjsip_dlg_inc_lock(dlg);
 
     /* Calculate call's secure level */
     call->secure_level = get_secure_level(acc_id, dest_uri);
+
+    /* Attach user data */
+    call->user_data = user_data;
+    
+    call->async_call.call_var.out_call.options = options;
+    call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone(
+                                                      dlg->pool, msg_data);
+    call->async_call.dlg = dlg;
 
     /* Init media channel */
     status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC, 
 				      call->secure_level, dlg->pool,
-				      NULL, NULL);
-    if (status != PJ_SUCCESS) {
+				      NULL, NULL, PJ_TRUE,
+                                      (pjsua_med_tp_state_cb)
+                                      &on_make_call_med_tp_complete);
+    if (status == PJ_SUCCESS) {
+        status = on_make_call_med_tp_complete(call->index, NULL);
+        if (status != PJ_SUCCESS)
+	    goto on_error;
+    } else if (status != PJ_EPENDING) {
 	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
 	goto on_error;
     }
 
-    /* Create offer */
-    status = pjsua_media_channel_create_sdp(call->index, dlg->pool, NULL,
-					    &offer, NULL);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
-	goto on_error;
-    }
-
-    /* Create the INVITE session: */
-    options |= PJSIP_INV_SUPPORT_100REL;
-    if (acc->cfg.require_100rel)
-	options |= PJSIP_INV_REQUIRE_100REL;
-    if (acc->cfg.use_timer != PJSUA_SIP_TIMER_INACTIVE) {
-	options |= PJSIP_INV_SUPPORT_TIMER;
-	if (acc->cfg.use_timer == PJSUA_SIP_TIMER_REQUIRED)
-	    options |= PJSIP_INV_REQUIRE_TIMER;
-	else if (acc->cfg.use_timer == PJSUA_SIP_TIMER_ALWAYS)
-	    options |= PJSIP_INV_ALWAYS_USE_TIMER;
-    }
-
-    status = pjsip_inv_create_uac( dlg, offer, options, &inv);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Invite session creation failed", status);
-	goto on_error;
-    }
-
-    /* Init Session Timers */
-    status = pjsip_timer_init_session(inv, &acc->cfg.timer_setting);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Session Timer init failed", status);
-	goto on_error;
-    }
-
-    /* Create and associate our data in the session. */
-    call->inv = inv;
-
-    dlg->mod_data[pjsua_var.mod.id] = call;
-    inv->mod_data[pjsua_var.mod.id] = call;
-
-    /* Attach user data */
-    call->user_data = user_data;
-
-    /* If account is locked to specific transport, then lock dialog
-     * to this transport too.
-     */
-    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
-	pjsip_tpselector tp_sel;
-
-	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
-	pjsip_dlg_set_transport(dlg, &tp_sel);
-    }
-
-    /* Set dialog Route-Set: */
-    if (!pj_list_empty(&acc->route_set))
-	pjsip_dlg_set_route_set(dlg, &acc->route_set);
-
-
-    /* Set credentials: */
-    if (acc->cred_cnt) {
-	pjsip_auth_clt_set_credentials( &dlg->auth_sess, 
-					acc->cred_cnt, acc->cred);
-    }
-
-    /* Set authentication preference */
-    pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
-
-    /* Create initial INVITE: */
-
-    status = pjsip_inv_invite(inv, &tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to create initial INVITE request", 
-		     status);
-	goto on_error;
-    }
-
-
-    /* Add additional headers etc */
-
-    pjsua_process_msg_data( tdata, msg_data);
-
-    /* Must increment call counter now */
-    ++pjsua_var.call_cnt;
-
-    /* Send initial INVITE: */
-
-    status = pjsip_inv_send_msg(inv, tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Unable to send initial INVITE request", 
-		     status);
-
-	/* Upon failure to send first request, the invite 
-	 * session would have been cleared.
-	 */
-	inv = NULL;
-	goto on_error;
-    }
-
     /* Done. */
 
@@ -579,5 +637,4 @@
 	*p_call_id = call_id;
 
-    pjsip_dlg_dec_lock(dlg);
     pj_pool_release(tmp_pool);
     PJSUA_UNLOCK();
@@ -590,10 +647,7 @@
 on_error:
     if (dlg) {
+        pjsip_dlg_inc_lock(dlg);
 	/* This may destroy the dialog */
 	pjsip_dlg_dec_lock(dlg);
-    }
-
-    if (inv != NULL) {
-	pjsip_inv_terminate(inv, PJSIP_SC_OK, PJ_FALSE);
     }
 
@@ -816,5 +870,6 @@
 				      rdata->tp_info.pool,
 				      offer,
-				      &sip_err_code);
+				      &sip_err_code, PJ_FALSE,
+                                      NULL);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
@@ -833,5 +888,4 @@
 	goto on_return;
     }
-
 
     /* Verify that we can handle the request. */
@@ -1015,5 +1069,4 @@
 
     ++pjsua_var.call_cnt;
-
 
     /* Check if this request should replace existing call */
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 3758)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 3763)
@@ -149,4 +149,40 @@
     pjsip_media_type_init(&msg_data->multipart_ctype, NULL, NULL);
     pj_list_init(&msg_data->multipart_parts);
+}
+
+PJ_DEF(pjsua_msg_data*) pjsua_msg_data_clone(pj_pool_t *pool,
+                                             const pjsua_msg_data *rhs)
+{
+    pjsua_msg_data *msg_data;
+    const pjsip_hdr *hdr;
+    const pjsip_multipart_part *mpart;
+
+    PJ_ASSERT_RETURN(pool && rhs, NULL);
+
+    msg_data = PJ_POOL_ZALLOC_T(pool, pjsua_msg_data);
+    PJ_ASSERT_RETURN(msg_data != NULL, NULL);
+
+    pj_list_init(&msg_data->hdr_list);
+    hdr = rhs->hdr_list.next;
+    while (hdr != &rhs->hdr_list) {
+	pj_list_push_back(&msg_data->hdr_list, pjsip_hdr_clone(pool, hdr));
+	hdr = hdr->next;
+    }
+
+    pj_strdup(pool, &msg_data->content_type, &rhs->content_type);
+    pj_strdup(pool, &msg_data->msg_body, &rhs->msg_body);
+
+    pjsip_media_type_cp(pool, &msg_data->multipart_ctype,
+                        &rhs->multipart_ctype);
+
+    pj_list_init(&msg_data->multipart_parts);
+    mpart = rhs->multipart_parts.next;
+    while (mpart != &rhs->multipart_parts) {
+	pj_list_push_back(&msg_data->multipart_parts,
+                          pjsip_multipart_clone_part(pool, mpart));
+	mpart = mpart->next;
+    }
+
+    return msg_data;
 }
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3758)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 3763)
@@ -711,4 +711,6 @@
 				    pjsua_var.media_cfg.rx_drop_pct);
 
+    call_med->tp_ready = PJ_SUCCESS;
+
     return PJ_SUCCESS;
 
@@ -772,5 +774,8 @@
     switch (op) {
     case PJ_ICE_STRANS_OP_INIT:
-	call_med->tp_ready = result;
+        call_med->tp_ready = result;
+        if (call_med->med_create_cb)
+            (*call_med->med_create_cb)(call_med, result,
+                                       call_med->call->secure_level, NULL);
 	break;
     case PJ_ICE_STRANS_OP_NEGOTIATION:
@@ -832,4 +837,15 @@
 		         call_med->call->index, call_med->idx));
 	}
+        if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
+            pjsua_med_tp_state_info info;
+
+            pj_bzero(&info, sizeof(info));
+            info.med_idx = call_med->idx;
+            info.state = call_med->tp_st;
+            info.status = result;
+            info.ext_info = &op;
+	    (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
+                call_med->call->index, &info);
+        }
 	if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
 	    pjsua_call_id id = call_med->call->index;
@@ -871,5 +887,6 @@
 static pj_status_t create_ice_media_transport(
 				const pjsua_transport_config *cfg,
-				pjsua_call_media *call_med)
+				pjsua_call_media *call_med,
+                                pj_bool_t async)
 {
     char stunip[PJ_INET6_ADDRSTRLEN];
@@ -953,10 +970,15 @@
 
     /* Wait until transport is initialized, or time out */
-    PJSUA_UNLOCK();
-    while (call_med->tp_ready == PJ_EPENDING) {
-	pjsua_handle_events(100);
-    }
-    PJSUA_LOCK();
-    if (call_med->tp_ready != PJ_SUCCESS) {
+    if (!async) {
+        PJSUA_UNLOCK();
+        while (call_med->tp_ready == PJ_EPENDING) {
+	    pjsua_handle_events(100);
+        }
+        PJSUA_LOCK();
+    }
+
+    if (async && call_med->tp_ready == PJ_EPENDING) {
+        return PJ_EPENDING;
+    } else if (call_med->tp_ready != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
 		     call_med->tp_ready);
@@ -1236,4 +1258,114 @@
 }
 
+/* Set media transport state and notify the application via the callback. */
+void set_media_tp_state(pjsua_call_media *call_med,
+                        pjsua_med_tp_st tp_st)
+{
+    if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
+        call_med->tp_st != tp_st)
+    {
+        pjsua_med_tp_state_info info;
+
+        pj_bzero(&info, sizeof(info));
+        info.med_idx = call_med->idx;
+        info.state = tp_st;
+        info.status = call_med->tp_ready;
+	(*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
+            call_med->call->index, &info);
+    }
+
+    call_med->tp_st = tp_st;
+}
+
+/* Callback to resume pjsua_call_media_init() after media transport
+ * creation is completed.
+ */
+static pj_status_t call_media_init_cb(pjsua_call_media *call_med,
+                                      pj_status_t status,
+                                      int security_level,
+                                      int *sip_err_code)
+{
+    pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
+    int err_code = 0;
+
+    if (status != PJ_SUCCESS)
+        goto on_error;
+
+    if (call_med->tp_st == PJSUA_MED_TP_CREATING)
+        set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
+
+#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) {
+	pjmedia_srtp_setting srtp_opt;
+	pjmedia_transport *srtp = NULL;
+
+	/* Check if SRTP requires secure signaling */
+	if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
+	    if (security_level < acc->cfg.srtp_secure_signaling) {
+		err_code = PJSIP_SC_NOT_ACCEPTABLE;
+		status = PJSIP_ESESSIONINSECURE;
+		goto on_error;
+	    }
+	}
+
+	/* 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
+	 * preference in SRTP usage policy, especially when it is stricter.
+	 */
+	if (call_med->rem_srtp_use > acc->cfg.use_srtp)
+	    srtp_opt.use = call_med->rem_srtp_use;
+	else
+	    srtp_opt.use = acc->cfg.use_srtp;
+
+	status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
+					       call_med->tp,
+					       &srtp_opt, &srtp);
+	if (status != PJ_SUCCESS) {
+	    err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
+	    goto on_error;
+	}
+
+	/* Set SRTP as current media transport */
+	call_med->tp_orig = call_med->tp;
+	call_med->tp = srtp;
+    }
+#else
+    call_med->tp_orig = call_med->tp;
+    PJ_UNUSED_ARG(security_level);
+#endif
+
+    pjmedia_event_subscription_init(&call_med->esub_rend, &call_media_on_event,
+                                    call_med);
+    pjmedia_event_subscription_init(&call_med->esub_cap, &call_media_on_event,
+                                    call_med);
+
+on_error:
+    if (status != PJ_SUCCESS && call_med->tp) {
+	pjmedia_transport_close(call_med->tp);
+	call_med->tp = NULL;
+    }
+
+    if (sip_err_code)
+        *sip_err_code = err_code;
+
+    if (call_med->med_init_cb) {
+        pjsua_med_tp_state_info info;
+
+        pj_bzero(&info, sizeof(info));
+        info.status = status;
+        info.state = call_med->tp_st;
+        info.med_idx = call_med->idx;
+        info.sip_err_code = err_code;
+        (*call_med->med_init_cb)(call_med->call->index, &info);
+    }
+
+    return status;
+}
+
 /* Initialize the media line */
 pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
@@ -1241,8 +1373,10 @@
 				  const pjsua_transport_config *tcfg,
 				  int security_level,
-				  int *sip_err_code)
+				  int *sip_err_code,
+                                  pj_bool_t async,
+                                  pjsua_med_tp_state_cb cb)
 {
     pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
-    pj_status_t status;
+    pj_status_t status = PJ_SUCCESS;
 
     /*
@@ -1252,19 +1386,6 @@
     call_med->type = type;
 
-    /* Create the media transport for initial call. This is blocking for now */
+    /* Create the media transport for initial call. */
     if (call_med->tp == NULL) {
-	if (pjsua_var.media_cfg.enable_ice) {
-	    status = create_ice_media_transport(tcfg, call_med);
-	} else {
-	    status = create_udp_media_transport(tcfg, call_med);
-	}
-
-	if (status != PJ_SUCCESS) {
-	    PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
-	    return status;
-	}
-	
-	call_med->tp_st = PJSUA_MED_TP_IDLE;
-
 #if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
 	/* While in initial call, set default video devices */
@@ -1285,68 +1406,125 @@
 #endif
 
+        set_media_tp_state(call_med, PJSUA_MED_TP_CREATING);
+
+        if (async) {
+            call_med->med_create_cb = &call_media_init_cb;
+            call_med->med_init_cb = cb;
+        }
+
+	if (pjsua_var.media_cfg.enable_ice) {
+	    status = create_ice_media_transport(tcfg, call_med, async);
+	} else {
+	    status = create_udp_media_transport(tcfg, call_med);
+	}
+
+        if (status == PJ_EPENDING) {
+            /* We will resume call media initialization in the
+             * on_ice_complete() callback.
+             */
+            return PJ_EPENDING;
+        } else if (status != PJ_SUCCESS) {
+	    PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
+	    return status;
+	}
+
+        /* Media transport creation completed immediately, so 
+         * we don't need to call the callback.
+         */
+        call_med->med_init_cb = NULL;
+
     } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
 	/* Media is being reenabled. */
-	call_med->tp_st = PJSUA_MED_TP_INIT;
-    }
-
-#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) {
-	pjmedia_srtp_setting srtp_opt;
-	pjmedia_transport *srtp = NULL;
-
-	/* Check if SRTP requires secure signaling */
-	if (acc->cfg.use_srtp != PJMEDIA_SRTP_DISABLED) {
-	    if (security_level < acc->cfg.srtp_secure_signaling) {
-		if (sip_err_code)
-		    *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
-		status = PJSIP_ESESSIONINSECURE;
+	set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
+    }
+
+    return call_media_init_cb(call_med, status, security_level,
+                              sip_err_code);
+}
+
+/* Callback to resume pjsua_media_channel_init() after media transport
+ * initialization is completed.
+ */
+static pj_status_t media_channel_init_cb(pjsua_call_id call_id,
+                                         const pjsua_med_tp_state_info *info)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pj_status_t status = (info? info->status : PJ_SUCCESS);
+    unsigned mi;
+
+    if (info) {
+        pj_mutex_lock(call->med_ch_mutex);
+
+        /* Set the callback to NULL to indicate that the async operation
+         * has completed.
+         */
+        call->media[info->med_idx].med_init_cb = NULL;
+
+        /* In case of failure, save the information to be returned
+         * by the last media transport to finish.
+         */
+        if (info->status != PJ_SUCCESS)
+            pj_memcpy(&call->med_ch_info, info, sizeof(info));
+
+        /* Check whether all the call's medias have finished calling their
+         * callbacks.
+         */
+        for (mi=0; mi < call->med_cnt; ++mi) {
+            pjsua_call_media *call_med = &call->media[mi];
+
+            if (call_med->med_init_cb) {
+                pj_mutex_unlock(call->med_ch_mutex);
+                return PJ_SUCCESS;
+            }
+
+            if (call_med->tp_ready != PJ_SUCCESS)
+                status = call_med->tp_ready;
+        }
+
+        /* OK, we are called by the last media transport finished. */
+        pj_mutex_unlock(call->med_ch_mutex);
+    }
+
+    if (call->med_ch_mutex) {
+        pj_mutex_destroy(call->med_ch_mutex);
+        call->med_ch_mutex = NULL;
+    }
+
+    if (status != PJ_SUCCESS) {
+        pjsua_media_channel_deinit(call_id);
+        goto on_error;
+    }
+
+    /* Tell the media transport of a new offer/answer session */
+    for (mi=0; mi < call->med_cnt; ++mi) {
+	pjsua_call_media *call_med = &call->media[mi];
+
+	/* Note: tp may be NULL if this media line is disabled */
+	if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
+            pj_pool_t *tmp_pool = (call->inv? call->inv->pool_prov:
+                                   call->async_call.dlg->pool);
+
+	    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;
+                call->med_ch_info.state = call_med->tp_st;
+                call->med_ch_info.sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
+		pjsua_media_channel_deinit(call_id);
 		goto on_error;
 	    }
-	}
-
-	/* 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
-	 * preference in SRTP usage policy, especially when it is stricter.
-	 */
-	if (call_med->rem_srtp_use > acc->cfg.use_srtp)
-	    srtp_opt.use = call_med->rem_srtp_use;
-	else
-	    srtp_opt.use = acc->cfg.use_srtp;
-
-	status = pjmedia_transport_srtp_create(pjsua_var.med_endpt,
-					       call_med->tp,
-					       &srtp_opt, &srtp);
-	if (status != PJ_SUCCESS) {
-	    if (sip_err_code)
-		*sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
-	    goto on_error;
-	}
-
-	/* Set SRTP as current media transport */
-	call_med->tp_orig = call_med->tp;
-	call_med->tp = srtp;
-    }
-#else
-    call_med->tp_orig = call_med->tp;
-    PJ_UNUSED_ARG(security_level);
-#endif
-
-    pjmedia_event_subscription_init(&call_med->esub_rend, &call_media_on_event,
-                                    call_med);
-    pjmedia_event_subscription_init(&call_med->esub_cap, &call_media_on_event,
-                                        call_med);
-
-    return PJ_SUCCESS;
+
+	    set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
+	}
+    }
+
+    call->med_ch_info.status = PJ_SUCCESS;
 
 on_error:
-    if (call_med->tp) {
-	pjmedia_transport_close(call_med->tp);
-	call_med->tp = NULL;
-    }
+    if (call->med_ch_cb)
+        (*call->med_ch_cb)(call->index, &call->med_ch_info);
+
     return status;
 }
@@ -1357,5 +1535,7 @@
 				     pj_pool_t *tmp_pool,
 				     const pjmedia_sdp_session *rem_sdp,
-				     int *sip_err_code)
+				     int *sip_err_code,
+                                     pj_bool_t async,
+                                     pjsua_med_tp_state_cb cb)
 {
     const pj_str_t STR_AUDIO = { "audio", 5 };
@@ -1369,7 +1549,9 @@
     pjmedia_type media_types[PJSUA_MAX_CALL_MEDIA];
     unsigned mi;
+    pj_bool_t pending_med_tp = PJ_FALSE;
     pj_status_t status;
 
     PJ_UNUSED_ARG(role);
+    PJ_UNUSED_ARG(tmp_pool);
 
     /*
@@ -1380,4 +1562,13 @@
     if (pjsua_get_state() != PJSUA_STATE_RUNNING)
 	return PJ_EBUSY;
+
+    if (async) {
+        pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
+                              call->async_call.dlg->pool);
+
+        status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
+        if (status != PJ_SUCCESS)
+            return status;
+    }
 
     PJ_LOG(4,(THIS_FILE, "Call %d: initializing media..", call_id));
@@ -1450,4 +1641,13 @@
 	status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
 	goto on_error;
+    }
+
+    if (async) {
+        call->med_ch_cb = cb;
+        if (rem_sdp) {
+            /* TODO: change rem_sdp to non-const parameter. */
+            call->async_call.rem_sdp =
+                pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
+        }
     }
 
@@ -1483,7 +1683,26 @@
 	    status = pjsua_call_media_init(call_med, media_type,
 	                                   &acc->cfg.rtp_cfg,
-					   security_level, sip_err_code);
-	    if (status != PJ_SUCCESS) {
-		pjsua_media_channel_deinit(call_id);
+					   security_level, sip_err_code,
+                                           async,
+                                           (async? (pjsua_med_tp_state_cb)
+                                           &media_channel_init_cb: NULL));
+            if (status == PJ_EPENDING) {
+                pending_med_tp = PJ_TRUE;
+            } else if (status != PJ_SUCCESS) {
+                if (pending_med_tp) {
+                    /* Save failure information. */
+                    call_med->tp_ready = status;
+                    pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
+                    call->med_ch_info.status = status;
+                    call->med_ch_info.state = call_med->tp_st;
+                    call->med_ch_info.med_idx = call_med->idx;
+                    if (sip_err_code)
+                        call->med_ch_info.sip_err_code = *sip_err_code;
+
+                    /* We will return failure in the callback later. */
+                    return PJ_EPENDING;
+                }
+
+                pjsua_media_channel_deinit(call_id);
 		goto on_error;
 	    }
@@ -1499,5 +1718,5 @@
 		pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT || 
 			  call_med->tp_st == PJSUA_MED_TP_RUNNING);
-		call_med->tp_st = PJSUA_MED_TP_DISABLED;
+		set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
 	    }
 
@@ -1512,27 +1731,28 @@
 	      call->audio_idx, call->index));
 
-    /* Tell the media transport of a new offer/answer session */
-    for (mi=0; mi < call->med_cnt; ++mi) {
-	pjsua_call_media *call_med = &call->media[mi];
-
-	/* Note: tp may be NULL if this media line is disabled */
-	if (call_med->tp && call_med->tp_st == PJSUA_MED_TP_IDLE) {
-	    status = pjmedia_transport_media_create(call_med->tp,
-						    tmp_pool, 0,
-						    rem_sdp, mi);
-	    if (status != PJ_SUCCESS) {
-		if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE;
-		pjsua_media_channel_deinit(call_id);
-		goto on_error;
-	    }
-
-	    call_med->tp_st = PJSUA_MED_TP_INIT;
-	}
-    }
+    if (pending_med_tp) {
+        /* We have a pending media transport initialization. */
+        pj_log_pop_indent();
+        return PJ_EPENDING;
+    }
+
+    /* Media transport initialization completed immediately, so 
+     * we don't need to call the callback.
+     */
+    call->med_ch_cb = NULL;
+
+    status = media_channel_init_cb(call_id, NULL);
+    if (status != PJ_SUCCESS && sip_err_code)
+        *sip_err_code = call->med_ch_info.sip_err_code;
 
     pj_log_pop_indent();
-    return PJ_SUCCESS;
+    return status;
 
 on_error:
+    if (call->med_ch_mutex) {
+        pj_mutex_destroy(call->med_ch_mutex);
+        call->med_ch_mutex = NULL;
+    }
+
     pj_log_pop_indent();
     return status;
@@ -1563,5 +1783,6 @@
 	    status = pjsua_media_channel_init(call_id, PJSIP_ROLE_UAS,
 					      call->secure_level, pool,
-					      rem_sdp, sip_err_code);
+					      rem_sdp, sip_err_code,
+                                              PJ_FALSE, NULL);
 	    if (status != PJ_SUCCESS)
 		return status;
@@ -1886,7 +2107,7 @@
 	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);
-	    call_med->tp_st = PJSUA_MED_TP_IDLE;
+	    set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 	}
 
@@ -1972,5 +2193,5 @@
 	    goto on_return;
 
-	call_med->tp_st = PJSUA_MED_TP_RUNNING;
+	set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
 
 	/* Get remote SRTP usage policy */
@@ -2264,5 +2485,5 @@
 	    pjmedia_transport_close(call_med->tp);
 	    call_med->tp = call_med->tp_orig = NULL;
-	    call_med->tp_st = PJSUA_MED_TP_IDLE;
+	    set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
 	}
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 3758)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_vid.c	(revision 3763)
@@ -676,5 +676,5 @@
 	    goto on_error;
 
-	call_med->tp_st = PJSUA_MED_TP_RUNNING;
+	set_media_tp_state(call_med, PJSUA_MED_TP_RUNNING);
 
 	/* Get remote SRTP usage policy */
@@ -1473,5 +1473,5 @@
     status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
 				   &acc_cfg->rtp_cfg, call->secure_level,
-				   NULL);
+				   NULL, PJ_FALSE, NULL);
     if (status != PJ_SUCCESS)
 	goto on_error;
@@ -1486,5 +1486,5 @@
 	goto on_error;
 
-    call_med->tp_st = PJSUA_MED_TP_INIT;
+    set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
 
     /* Get transport address info */
@@ -1593,5 +1593,5 @@
 	status = pjsua_call_media_init(call_med, PJMEDIA_TYPE_VIDEO,
 				       &acc_cfg->rtp_cfg, call->secure_level,
-				       NULL);
+				       NULL, PJ_FALSE, NULL);
 	if (status != PJ_SUCCESS)
 	    goto on_error;
@@ -1663,5 +1663,5 @@
 	// Don't close this here, as SDP negotiation has not been
 	// done and stream may be still active.
-	call_med->tp_st = PJSUA_MED_TP_DISABLED;
+	set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
 
 	/* Deactivate the stream */
