summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLiong Sauw Ming <ming@teluu.com>2011-09-21 10:20:01 +0000
committerLiong Sauw Ming <ming@teluu.com>2011-09-21 10:20:01 +0000
commit8a8b2bba54aedc161159225259b83e6d7c1d17b3 (patch)
tree45a096a1b1cb6f3d5c5d3e016ce6147969a0d9b0
parentbe8d37186b16150716f752883ae6857a0161db40 (diff)
Re #1266: Asynchronous media transport creation
* Add feature that allows ICE media transport to be created asynchronously. * Add new callback, e.g. on_call_media_transport_state(call_id, state_struct) to report media transport status. * Handle outgoing calls while creating media transport asynchronously. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3763 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/include/pjsua-lib/pjsua.h90
-rw-r--r--pjsip/include/pjsua-lib/pjsua_internal.h57
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c279
-rw-r--r--pjsip/src/pjsua-lib/pjsua_core.c36
-rw-r--r--pjsip/src/pjsua-lib/pjsua_media.c399
-rw-r--r--pjsip/src/pjsua-lib/pjsua_vid.c10
6 files changed, 641 insertions, 230 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h
index f79de1b9..f768438d 100644
--- a/pjsip/include/pjsua-lib/pjsua.h
+++ b/pjsip/include/pjsua-lib/pjsua.h
@@ -463,6 +463,78 @@ typedef struct pjsua_reg_info
} 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);
+
+
/**
* This structure describes application callback to receive various event
* notification from PJSUA-API. All of these callbacks are OPTIONAL,
@@ -949,6 +1021,12 @@ typedef struct pjsua_callback
pjsip_tp_state_callback on_transport_state;
/**
+ * 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.
*
@@ -1365,6 +1443,18 @@ 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);
+
+
+/**
* Instantiate pjsua application. Application must call this function before
* calling any other functions, to make sure that the underlying libraries
* are properly initialized. Once this function has returned success,
diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h
index 01d32887..2a2dc095 100644
--- a/pjsip/include/pjsua-lib/pjsua_internal.h
+++ b/pjsip/include/pjsua-lib/pjsua_internal.h
@@ -27,28 +27,12 @@
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;
+
/**
* Call's media stream.
@@ -92,15 +76,23 @@ typedef struct pjsua_call_media
pjmedia_transport *tp; /**< Current media transport (can be 0) */
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
(used to update ICE default
address) */
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;
/**
@@ -132,6 +124,9 @@ struct pjsua_call
unsigned med_cnt; /**< Number of media in SDP. */
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
call was triggered by xfer. */
@@ -147,6 +142,17 @@ struct pjsua_call
} lock_codec; /**< Data for codec locking when answer
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. */
};
@@ -508,7 +514,9 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
int security_level,
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,
const pjmedia_sdp_session *rem_sdp,
@@ -523,12 +531,15 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
pjmedia_type type,
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,
const pjmedia_sdp_session *local_sdp,
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);
/**
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 3deeacca..d3c655f1 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -331,6 +331,152 @@ static int call_get_secure_level(pjsua_call *call)
}
*/
+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;
+}
+
/*
* Make outgoing call to the specified URI using the specified account.
@@ -344,13 +490,10 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
{
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;
@@ -376,8 +519,6 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
if (!pjsua_var.is_mswitch && pjsua_var.snd_port==NULL &&
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)
goto on_error;
@@ -462,123 +603,39 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
/* Increment the dialog's lock otherwise when invite session creation
* 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) {
- 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) {
+ 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 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. */
if (p_call_id)
*p_call_id = call_id;
- pjsip_dlg_dec_lock(dlg);
pj_pool_release(tmp_pool);
PJSUA_UNLOCK();
@@ -589,14 +646,11 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id,
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);
- }
-
if (call_id != -1) {
reset_call(call_id);
pjsua_media_channel_deinit(call_id);
@@ -815,7 +869,8 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
call->secure_level,
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);
pjsip_endpt_respond(pjsua_var.endpt, NULL, rdata,
@@ -833,7 +888,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
goto on_return;
}
-
/* Verify that we can handle the request. */
options |= PJSIP_INV_SUPPORT_100REL;
options |= PJSIP_INV_SUPPORT_TIMER;
@@ -1015,7 +1069,6 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata)
++pjsua_var.call_cnt;
-
/* Check if this request should replace existing call */
if (replaced_dlg) {
pjsip_inv_session *replaced_inv;
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 476e4cd7..31133b2c 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -150,6 +150,42 @@ PJ_DEF(void) pjsua_msg_data_init(pjsua_msg_data *msg_data)
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;
+}
+
PJ_DEF(void) pjsua_transport_config_default(pjsua_transport_config *cfg)
{
pj_bzero(cfg, sizeof(*cfg));
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index ff5a0ef9..932fdc8e 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -710,6 +710,8 @@ static pj_status_t create_udp_media_transport(const pjsua_transport_config *cfg,
pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
pjsua_var.media_cfg.rx_drop_pct);
+ call_med->tp_ready = PJ_SUCCESS;
+
return PJ_SUCCESS;
on_error:
@@ -771,7 +773,10 @@ static void on_ice_complete(pjmedia_transport *tp,
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:
if (result != PJ_SUCCESS) {
@@ -831,6 +836,17 @@ static void on_ice_complete(pjmedia_transport *tp,
"ICE keep alive failure for transport %d:%d",
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;
(*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
@@ -870,7 +886,8 @@ static pj_status_t parse_host_port(const pj_str_t *host_port,
/* Create ICE media transports (when ice is enabled) */
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];
pj_ice_strans_cfg ice_cfg;
@@ -952,12 +969,17 @@ static pj_status_t create_ice_media_transport(
}
/* Wait until transport is initialized, or time out */
- PJSUA_UNLOCK();
- while (call_med->tp_ready == PJ_EPENDING) {
- pjsua_handle_events(100);
+ if (!async) {
+ PJSUA_UNLOCK();
+ while (call_med->tp_ready == PJ_EPENDING) {
+ pjsua_handle_events(100);
+ }
+ PJSUA_LOCK();
}
- PJSUA_LOCK();
- if (call_med->tp_ready != PJ_SUCCESS) {
+
+ 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);
status = call_med->tp_ready;
@@ -1235,59 +1257,42 @@ static pj_status_t call_media_on_event(pjmedia_event_subscription *esub,
return PJ_SUCCESS;
}
-/* Initialize the media line */
-pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
- pjmedia_type type,
- const pjsua_transport_config *tcfg,
- int security_level,
- int *sip_err_code)
+/* 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)
{
- pjsua_acc *acc = &pjsua_var.acc[call_med->call->acc_id];
- pj_status_t status;
+ if (pjsua_var.ua_cfg.cb.on_call_media_transport_state &&
+ call_med->tp_st != tp_st)
+ {
+ pjsua_med_tp_state_info info;
- /*
- * Note: this function may be called when the media already exists
- * (e.g. in reinvites, updates, etc.)
- */
- call_med->type = type;
+ 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);
+ }
- /* Create the media transport for initial call. This is blocking for now */
- 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);
- }
+ call_med->tp_st = tp_st;
+}
- if (status != PJ_SUCCESS) {
- PJ_PERROR(1,(THIS_FILE, status, "Error creating media transport"));
- return status;
- }
-
- call_med->tp_st = PJSUA_MED_TP_IDLE;
+/* 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 defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
- /* While in initial call, set default video devices */
- if (type == PJMEDIA_TYPE_VIDEO) {
- call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev;
- call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev;
- if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) {
- pjmedia_vid_dev_info info;
- pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info);
- call_med->strm.v.rdr_dev = info.id;
- }
- if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
- pjmedia_vid_dev_info info;
- pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info);
- call_med->strm.v.cap_dev = info.id;
- }
- }
-#endif
+ if (status != PJ_SUCCESS)
+ goto on_error;
- } else if (call_med->tp_st == PJSUA_MED_TP_DISABLED) {
- /* Media is being reenabled. */
- call_med->tp_st = PJSUA_MED_TP_INIT;
- }
+ 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
@@ -1300,8 +1305,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
/* 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;
+ err_code = PJSIP_SC_NOT_ACCEPTABLE;
status = PJSIP_ESESSIONINSECURE;
goto on_error;
}
@@ -1322,8 +1326,7 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
call_med->tp,
&srtp_opt, &srtp);
if (status != PJ_SUCCESS) {
- if (sip_err_code)
- *sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
+ err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
goto on_error;
}
@@ -1339,15 +1342,190 @@ pj_status_t pjsua_call_media_init(pjsua_call_media *call_med,
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;
+ call_med);
on_error:
- if (call_med->tp) {
+ 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,
+ pjmedia_type type,
+ const pjsua_transport_config *tcfg,
+ int security_level,
+ 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_SUCCESS;
+
+ /*
+ * Note: this function may be called when the media already exists
+ * (e.g. in reinvites, updates, etc.)
+ */
+ call_med->type = type;
+
+ /* Create the media transport for initial call. */
+ if (call_med->tp == NULL) {
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+ /* While in initial call, set default video devices */
+ if (type == PJMEDIA_TYPE_VIDEO) {
+ call_med->strm.v.rdr_dev = acc->cfg.vid_rend_dev;
+ call_med->strm.v.cap_dev = acc->cfg.vid_cap_dev;
+ if (call_med->strm.v.rdr_dev == PJMEDIA_VID_DEFAULT_RENDER_DEV) {
+ pjmedia_vid_dev_info info;
+ pjmedia_vid_dev_get_info(call_med->strm.v.rdr_dev, &info);
+ call_med->strm.v.rdr_dev = info.id;
+ }
+ if (call_med->strm.v.cap_dev == PJMEDIA_VID_DEFAULT_CAPTURE_DEV) {
+ pjmedia_vid_dev_info info;
+ pjmedia_vid_dev_get_info(call_med->strm.v.cap_dev, &info);
+ call_med->strm.v.cap_dev = info.id;
+ }
+ }
+#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. */
+ 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;
+ }
+
+ set_media_tp_state(call_med, PJSUA_MED_TP_INIT);
+ }
+ }
+
+ call->med_ch_info.status = PJ_SUCCESS;
+
+on_error:
+ if (call->med_ch_cb)
+ (*call->med_ch_cb)(call->index, &call->med_ch_info);
+
return status;
}
@@ -1356,7 +1534,9 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
int security_level,
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 };
const pj_str_t STR_VIDEO = { "video", 5 };
@@ -1368,9 +1548,11 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx);
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);
/*
* Note: this function may be called when the media already exists
@@ -1380,6 +1562,15 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
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));
pj_log_push_indent();
@@ -1451,6 +1642,15 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
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);
+ }
+ }
+
/* Initialize each media line */
for (mi=0; mi < call->med_cnt; ++mi) {
pjsua_call_media *call_med = &call->media[mi];
@@ -1482,9 +1682,28 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
if (enabled) {
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;
}
} else {
@@ -1498,7 +1717,7 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
//call_med->tp = NULL;
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);
}
/* Put media type just for info */
@@ -1511,29 +1730,30 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
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];
+ if (pending_med_tp) {
+ /* We have a pending media transport initialization. */
+ pj_log_pop_indent();
+ return PJ_EPENDING;
+ }
- /* 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;
- }
+ /* Media transport initialization completed immediately, so
+ * we don't need to call the callback.
+ */
+ call->med_ch_cb = NULL;
- call_med->tp_st = PJSUA_MED_TP_INIT;
- }
- }
+ 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;
}
@@ -1562,7 +1782,8 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id,
if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) {
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;
}
@@ -1885,9 +2106,9 @@ pj_status_t pjsua_media_channel_deinit(pjsua_call_id 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);
- call_med->tp_st = PJSUA_MED_TP_IDLE;
+ set_media_tp_state(call_med, PJSUA_MED_TP_IDLE);
}
//if (call_med->tp_orig && call_med->tp &&
@@ -1971,7 +2192,7 @@ static pj_status_t audio_channel_update(pjsua_call_media *call_med,
if (status != PJ_SUCCESS)
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 */
pjmedia_transport_info_init(&tp_info);
@@ -2263,7 +2484,7 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
if (local_sdp->media[mi]->desc.port==0 && call_med->tp) {
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);
}
if (status != PJ_SUCCESS) {
diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c
index 55f892ec..1d0697ed 100644
--- a/pjsip/src/pjsua-lib/pjsua_vid.c
+++ b/pjsip/src/pjsua-lib/pjsua_vid.c
@@ -675,7 +675,7 @@ pj_status_t video_channel_update(pjsua_call_media *call_med,
if (status != PJ_SUCCESS)
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 */
pjmedia_transport_info_init(&tp_info);
@@ -1472,7 +1472,7 @@ static pj_status_t call_add_video(pjsua_call *call,
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;
@@ -1485,7 +1485,7 @@ static pj_status_t call_add_video(pjsua_call *call,
if (status != PJ_SUCCESS)
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 */
pjmedia_transport_info_init(&tpinfo);
@@ -1592,7 +1592,7 @@ static pj_status_t call_modify_video(pjsua_call *call,
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;
@@ -1662,7 +1662,7 @@ on_error:
/* Mark media transport to disabled */
// 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 */
pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]);