本文主要讲解 pjsip 中对于 reinvite 的处理方式,其中将主要展现 sdp 的重新协商过程,至于其它的部分暂时不做详细讲解
欢迎进入q群761341723,大家一起讨论问题。hpng该网站为我自己网站,一些想法也会发到这里
pjsua_call_reinvite2
pjsua_call_reinvite2
函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:
/*
* Send re-INVITE (to release hold).
*/
PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id,
const pjsua_call_setting *opt,
const pjsua_msg_data *msg_data)
{
pjmedia_sdp_session *sdp = NULL;
pj_str_t *new_contact = NULL;
pjsip_tx_data *tdata;
pjsua_call *call;
pjsip_dialog *dlg = NULL;
pj_status_t status;
PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
PJ_EINVAL);
PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id));
pj_log_push_indent();
status = acquire_call("pjsua_call_reinvite2()", call_id, &call, &dlg);
if (status != PJ_SUCCESS)
goto on_return;
if (pjsua_call_media_is_changing(call)) {
PJ_LOG(1,(THIS_FILE, "Unable to reinvite" ERR_MEDIA_CHANGING));
status = PJ_EINVALIDOP;
goto on_return;
}
if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) {
PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed"));
status = PJSIP_ESESSIONSTATE;
goto on_return;
}
status = apply_call_setting(call, opt, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Failed to apply call setting", status);
goto on_return;
}
/* Create SDP */
if (call->local_hold && (call->opt.flag & PJSUA_CALL_UNHOLD)==0) {
status = create_sdp_of_call_hold(call, &sdp);
} else if ((call->opt.flag & PJSUA_CALL_NO_SDP_OFFER) == 0) {
status = pjsua_media_channel_create_sdp(call->index,
call->inv->pool_prov,
NULL, &sdp, NULL);
}
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint",
status);
goto on_return;
}
if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) &&
pjsua_acc_is_valid(call->acc_id))
{
call_update_contact(call, &new_contact);
}
if ((call->opt.flag & PJSUA_CALL_UPDATE_VIA) &&
pjsua_acc_is_valid(call->acc_id))
{
dlg_set_via(call->inv->dlg, &pjsua_var.acc[call->acc_id]);
}
if ((call->opt.flag & PJSUA_CALL_UPDATE_TARGET) &&
msg_data && msg_data->target_uri.slen)
{
status = dlg_set_target(dlg, &msg_data->target_uri);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to set new target", status);
goto on_return;
}
}
/* Create re-INVITE with new offer */
status = pjsip_inv_reinvite( call->inv, new_contact, sdp, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status);
goto on_return;
}
/* Add additional headers etc */
pjsua_process_msg_data( tdata, msg_data);
/* Send the request */
call->med_update_success = PJ_FALSE;
status = pjsip_inv_send_msg( call->inv, tdata);
if (status == PJ_SUCCESS &&
((call->opt.flag & PJSUA_CALL_UNHOLD) &&
(call->opt.flag & PJSUA_CALL_NO_SDP_OFFER) == 0))
{
call->local_hold = PJ_FALSE;
} else if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status);
goto on_return;
}
on_return:
if (dlg) pjsip_dlg_dec_lock(dlg);
pj_log_pop_indent();
return status;
}2024-03-02 21:10:23 星期六
- create_sdp_of_call_hold:创建 hold 的 sdp 内容
- pjsua_media_channel_create_sdp: 没有 offer 的情况下创建 sdp
- pjsua_process_msg_data: 创建并处理一些额外的 header 数据
- pjsip_inv_send_msg: 发送 sdp 并修改 sdp 内容
reset_call
reset_call
函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:
/*
* Reset call descriptor.
*/
static void reset_call(pjsua_call_id id)
{
pjsua_call *call = &pjsua_var.calls[id];
unsigned i;
if (call->incoming_data) {
pjsip_rx_data_free_cloned(call->incoming_data);
call->incoming_data = NULL;
}
pj_bzero(call, sizeof(*call));
call->index = id;
call->last_text.ptr = call->last_text_buf_;
call->cname.ptr = call->cname_buf;
call->cname.slen = sizeof(call->cname_buf);
for (i=0; i<PJ_ARRAY_SIZE(call->media); ++i) {
pjsua_call_media *call_med = &call->media[i];
call_med->ssrc = pj_rand();
call_med->strm.a.conf_slot = PJSUA_INVALID_ID;
call_med->strm.v.cap_win_id = PJSUA_INVALID_ID;
call_med->strm.v.rdr_win_id = PJSUA_INVALID_ID;
call_med->strm.v.strm_dec_slot = PJSUA_INVALID_ID;
call_med->strm.v.strm_enc_slot = PJSUA_INVALID_ID;
call_med->call = call;
call_med->idx = i;
call_med->tp_auto_del = PJ_TRUE;
}
pjsua_call_setting_default(&call->opt);
pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,
(void*)(pj_size_t)id, &reinv_timer_cb);
pj_bzero(&call->trickle_ice, sizeof(call->trickle_ice));
pj_timer_entry_init(&call->trickle_ice.timer, 0, call,
&trickle_ice_send_sip_info);
}
- reinv_timer_cb: 用于重置 re-invite
reinv_timer_cb
reinv_timer_cb
函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:
/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
{
pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
pjsip_dialog *dlg;
pjsua_call *call;
pj_status_t status;
PJ_UNUSED_ARG(th);
pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE;
pj_log_push_indent();
status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
if (status != PJ_SUCCESS) {
pj_log_pop_indent();
return;
}
process_pending_reinvite(call);
pjsip_dlg_dec_lock(dlg);
pj_log_pop_indent();
}
- process_pending_reinvite: 对 reinvite 或者 update 进行处理,主要是处理 sdp
process_pending_reinvite
process_pending_reinvite
函数的定义位于 pjsip/src/pjsua-lib/pjsua_call.c 中,如下所示:
//4444
if (rem_can_update) {
status = pjsip_inv_update(inv, NULL, new_offer, &tdata);
} else {
status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata);
}
if (status==PJ_EINVALIDOP &&
++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY)
{
/* Ups, let's reschedule again */
pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL);
return PJ_SUCCESS;
} else if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE",
status);
return status;
}
/* Send the UPDATE/re-INVITE request */
status = pjsip_inv_send_msg(inv, tdata);
if (status != PJ_SUCCESS) {
pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE",
status);
return status;
}
- pjsip_inv_update: 处理 update 的 sdp 信息
- pjsip_inv_reinvite:处理 reinvite 的 sdp 信息
- pjsip_inv_send_msg: 发送 re-invite 到服务器并修改 sdp
pjsip_inv_update
pjsip_inv_update 函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:
/*
* Create UPDATE.
*/
PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv,
const pj_str_t *new_contact,
const pjmedia_sdp_session *offer,
pjsip_tx_data **p_tdata )
{
pjsip_contact_hdr *contact_hdr = NULL;
pjsip_tx_data *tdata = NULL;
pjmedia_sdp_session *sdp_copy;
const pjsip_hdr *hdr;
pjsip_supported_hdr *sup_hdr = NULL;
pj_status_t status = PJ_SUCCESS;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
/* Dialog must have been established */
PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED,
PJ_EINVALIDOP);
/* Invite session must not have been disconnected */
PJ_ASSERT_RETURN(inv->state < PJSIP_INV_STATE_DISCONNECTED,
PJ_EINVALIDOP);
pj_log_push_indent();
/* Lock dialog. */
pjsip_dlg_inc_lock(inv->dlg);
/* Process offer, if any */
if (offer) {
if (inv->state == PJSIP_INV_STATE_EARLY && !inv->sdp_done_early_rel) {
PJ_LOG(4,(inv->dlg->obj_name,
"RFC 3311 section 5.1 recommends against sending UPDATE"
" without reliable prov response"));
#if PJSIP_INV_UPDATE_EARLY_CHECK_RELIABLE
status = PJ_EINVALIDOP;
goto on_error;
#endif
}
if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) {
PJ_LOG(4,(inv->dlg->obj_name,
"Invalid SDP offer/answer state for UPDATE"));
status = PJ_EINVALIDOP;
goto on_error;
}
/* Notify negotiator about the new offer. This will fix the offer
* with correct SDP origin.
*/
status = pjmedia_sdp_neg_modify_local_offer2(inv->pool_prov, inv->neg,
inv->sdp_neg_flags, offer);
if (status != PJ_SUCCESS)
goto on_error;
/* Retrieve the "fixed" offer from negotiator */
pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
}
/* Update Contact if required */
if (new_contact) {
pj_str_t tmp;
const pj_str_t STR_CONTACT = { "Contact", 7 };
pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
contact_hdr = (pjsip_contact_hdr*)
pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT,
tmp.ptr, tmp.slen, NULL);
if (!contact_hdr) {
status = PJSIP_EINVALIDURI;
goto on_error;
}
inv->dlg->local.contact = contact_hdr;
}
/* Create request */
status = pjsip_dlg_create_request(inv->dlg, &pjsip_update_method,
-1, &tdata);
if (status != PJ_SUCCESS)
goto on_error;
/* Attach SDP body */
if (offer) {
sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer);
pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body);
}
/* Session Timers spec (RFC 4028) says that Supported header MUST be put
* in refresh requests. So here we'll just put the Supported header in
* all cases regardless of whether session timers is used or not, just
* in case this is a common behavior.
*/
hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
if (hdr) {
sup_hdr = (pjsip_supported_hdr*)
pjsip_hdr_shallow_clone(tdata->pool, hdr);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr);
}
status = pjsip_timer_update_req(inv, tdata);
if (status != PJ_SUCCESS)
goto on_error;
/* Cleanup Allow & Supported headers from disabled extensions */
cleanup_allow_sup_hdr(inv->options, NULL, NULL, sup_hdr);
/* Unlock dialog. */
pjsip_dlg_dec_lock(inv->dlg);
*p_tdata = tdata;
pj_log_pop_indent();
return PJ_SUCCESS;
on_error:
if (tdata)
pjsip_tx_data_dec_ref(tdata);
/* Unlock dialog. */
pjsip_dlg_dec_lock(inv->dlg);
pj_log_pop_indent();
return status;
}
- pjmedia_sdp_neg_modify_local_offer2: 修改 sdp 中的 origin,
- pjsip_create_sdp_body: 创建 sdp 的 body 内容
pjsip_inv_reinvte
pjsip_inv_reinvte
函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:
/*
* Create re-INVITE.
*/
PJ_DEF(pj_status_t) pjsip_inv_reinvite( pjsip_inv_session *inv,
const pj_str_t *new_contact,
const pjmedia_sdp_session *new_offer,
pjsip_tx_data **p_tdata )
{
pj_status_t status;
pjsip_contact_hdr *contact_hdr = NULL;
/* Check arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
/* Must NOT have a pending INVITE transaction */
if (inv->invite_tsx!=NULL)
return PJ_EINVALIDOP;
pj_log_push_indent();
pjsip_dlg_inc_lock(inv->dlg);
if (new_contact) {
pj_str_t tmp;
const pj_str_t STR_CONTACT = { "Contact", 7 };
pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact);
contact_hdr = (pjsip_contact_hdr*)
pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT,
tmp.ptr, tmp.slen, NULL);
if (!contact_hdr) {
status = PJSIP_EINVALIDURI;
goto on_return;
}
}
if (new_offer) {
if (!inv->neg) {
status = pjmedia_sdp_neg_create_w_local_offer(inv->pool,
new_offer,
&inv->neg);
if (status != PJ_SUCCESS)
goto on_return;
} else switch (pjmedia_sdp_neg_get_state(inv->neg)) {
case PJMEDIA_SDP_NEG_STATE_NULL:
pj_assert(!"Unexpected SDP neg state NULL");
status = PJ_EBUG;
goto on_return;
case PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER:
PJ_LOG(4,(inv->obj_name,
"pjsip_inv_reinvite: already have an offer, new "
"offer is ignored"));
break;
case PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER:
status = pjmedia_sdp_neg_set_local_answer(inv->pool_prov,
inv->neg,
new_offer);
if (status != PJ_SUCCESS)
goto on_return;
break;
case PJMEDIA_SDP_NEG_STATE_WAIT_NEGO:
PJ_LOG(4,(inv->obj_name,
"pjsip_inv_reinvite: SDP in WAIT_NEGO state, new "
"offer is ignored"));
break;
case PJMEDIA_SDP_NEG_STATE_DONE:
status = pjmedia_sdp_neg_modify_local_offer2(
inv->pool_prov, inv->neg,
inv->sdp_neg_flags, new_offer);
if (status != PJ_SUCCESS)
goto on_return;
break;
}
}
if (contact_hdr)
inv->dlg->local.contact = contact_hdr;
status = pjsip_inv_invite(inv, p_tdata);
on_return:
pjsip_dlg_dec_lock(inv->dlg);
pj_log_pop_indent();
return status;
}
此处是没有 offer 或者 answer 就创建对应的内容,当然也有可能在等待协商。
pjsip_inv_invite
pjsip_inv_invite
函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:
/*
* Create initial INVITE request.
*/
PJ_DEF(pj_status_t) pjsip_inv_invite( pjsip_inv_session *inv,
pjsip_tx_data **p_tdata )
{
pjsip_tx_data *tdata;
const pjsip_hdr *hdr;
pjsip_allow_hdr *allow_hdr = NULL;
pjsip_supported_hdr *sup_hdr = NULL;
pj_bool_t has_sdp;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
/* State MUST be NULL or CONFIRMED. */
PJ_ASSERT_RETURN(inv->state == PJSIP_INV_STATE_NULL ||
inv->state == PJSIP_INV_STATE_CONFIRMED,
PJ_EINVALIDOP);
/* Lock dialog. */
pjsip_dlg_inc_lock(inv->dlg);
/* Create the INVITE request. */
status = pjsip_dlg_create_request(inv->dlg, pjsip_get_invite_method(), -1,
&tdata);
if (status != PJ_SUCCESS)
goto on_return;
/* If this is the first INVITE, then copy the headers from inv_hdr.
* These are the headers parsed from the request URI when the
* dialog was created.
*/
if (inv->state == PJSIP_INV_STATE_NULL) {
hdr = inv->dlg->inv_hdr.next;
while (hdr != &inv->dlg->inv_hdr) {
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)
pjsip_hdr_shallow_clone(tdata->pool, hdr));
hdr = hdr->next;
}
}
/* See if we have SDP to send. */
if (inv->neg) {
pjmedia_sdp_neg_state neg_state;
neg_state = pjmedia_sdp_neg_get_state(inv->neg);
has_sdp = (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER ||
(neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
pjmedia_sdp_neg_has_local_answer(inv->neg)));
} else {
has_sdp = PJ_FALSE;
}
/* Add SDP, if any. */
if (has_sdp) {
const pjmedia_sdp_session *offer;
status = pjmedia_sdp_neg_get_neg_local(inv->neg, &offer);
if (status != PJ_SUCCESS) {
pjsip_tx_data_dec_ref(tdata);
goto on_return;
}
tdata->msg->body = create_sdp_body(tdata->pool, offer);
}
/* Add Allow header. */
if (inv->dlg->add_allow) {
hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_ALLOW, NULL);
if (hdr) {
allow_hdr = (pjsip_allow_hdr*)
pjsip_hdr_shallow_clone(tdata->pool, hdr);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)allow_hdr);
}
}
/* Add Supported header */
hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
if (hdr) {
sup_hdr = (pjsip_supported_hdr*)
pjsip_hdr_shallow_clone(tdata->pool, hdr);
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sup_hdr);
}
/* Cleanup Allow & Supported headers from disabled extensions */
cleanup_allow_sup_hdr(inv->options, NULL, allow_hdr, sup_hdr);
/* Add Require header. */
if ((inv->options & PJSIP_INV_REQUIRE_100REL) ||
(inv->options & PJSIP_INV_REQUIRE_TIMER) ||
(inv->options & PJSIP_INV_REQUIRE_TRICKLE_ICE))
{
pjsip_require_hdr *hreq;
hreq = pjsip_require_hdr_create(tdata->pool);
if (inv->options & PJSIP_INV_REQUIRE_100REL)
hreq->values[hreq->count++] = pj_str("100rel");
if (inv->options & PJSIP_INV_REQUIRE_TIMER)
hreq->values[hreq->count++] = pj_str("timer");
if (inv->options & PJSIP_INV_REQUIRE_TRICKLE_ICE)
hreq->values[hreq->count++] = pj_str("trickle-ice");
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) hreq);
}
status = pjsip_timer_update_req(inv, tdata);
if (status != PJ_SUCCESS)
goto on_return;
/* Done. */
*p_tdata = tdata;
on_return:
pjsip_dlg_dec_lock(inv->dlg);
return status;
}
- create_sdp_body: 创建对应的 sdp 的 body 内容
pjmedia_sdp_neg_modify_local_offer2
pjmedia_sdp_neg_modify_local_offer2
函数的定义位于 pjmedia/src/pjmedia/sdp_neg.c 中,如下所示:
PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer2(
pj_pool_t *pool,
pjmedia_sdp_neg *neg,
unsigned flags,
const pjmedia_sdp_session *local)
{
pjmedia_sdp_session *new_offer;
pjmedia_sdp_session *old_offer;
unsigned oi; /* old offer media index */
pj_status_t status;
/* Check arguments are valid. */
PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL);
/* Can only do this in STATE_DONE. */
PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE,
PJMEDIA_SDPNEG_EINSTATE);
/* Validate the new offer */
status = pjmedia_sdp_validate(local);
if (status != PJ_SUCCESS)
return status;
/* Change state to STATE_LOCAL_OFFER */
neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER;
/* When there is no active local SDP in state PJMEDIA_SDP_NEG_STATE_DONE,
* it means that the previous initial SDP nego must have been failed,
* so we'll just set the local SDP offer here.
*/
if (!neg->active_local_sdp) {
neg->initial_sdp_tmp = NULL;
neg->initial_sdp = pjmedia_sdp_session_clone(pool, local);
/* Assign PT numbers for our offer and update the mapping. */
assign_pt_and_update_map(pool, neg, neg->initial_sdp,
PJ_TRUE, PJ_FALSE);
neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp);
if (pjmedia_sdp_session_cmp(neg->last_sent, neg->neg_local_sdp, 0) !=
PJ_SUCCESS)
{
++neg->neg_local_sdp->origin.version;
}
neg->last_sent = neg->neg_local_sdp;
return PJ_SUCCESS;
}
/* Init vars */
old_offer = neg->active_local_sdp;
new_offer = pjmedia_sdp_session_clone(pool, local);
/* RFC 3264 Section 8: When issuing an offer that modifies the session,
* the "o=" line of the new SDP MUST be identical to that in the
* previous SDP, except that the version in the origin field MUST
* increment by one from the previous SDP.
*/
pj_strdup(pool, &new_offer->origin.user, &old_offer->origin.user);
new_offer->origin.id = old_offer->origin.id;
pj_strdup(pool, &new_offer->origin.net_type, &old_offer->origin.net_type);
pj_strdup(pool, &new_offer->origin.addr_type,&old_offer->origin.addr_type);
pj_strdup(pool, &new_offer->origin.addr, &old_offer->origin.addr);
if ((flags & PJMEDIA_SDP_NEG_ALLOW_MEDIA_CHANGE) == 0) {
/* Generating the new offer, in the case media lines doesn't match the
* active SDP (e.g. current/active SDP's have m=audio and m=video lines,
* and the new offer only has m=audio line), the negotiator will fix
* the new offer by reordering and adding the missing media line with
* port number set to zero.
*/
for (oi = 0; oi < old_offer->media_count; ++oi) {
pjmedia_sdp_media *om;
pjmedia_sdp_media *nm;
unsigned ni; /* new offer media index */
pj_bool_t found = PJ_FALSE;
om = old_offer->media[oi];
for (ni = oi; ni < new_offer->media_count; ++ni) {
nm = new_offer->media[ni];
if (pj_strcmp(&nm->desc.media, &om->desc.media) == 0) {
if (ni != oi) {
/* The same media found but the position unmatched to
* the old offer, so let's put this media in the right
* place, and keep the order of the rest.
*/
pj_array_insert(
new_offer->media, /* array */
sizeof(new_offer->media[0]), /* elmt size*/
ni, /* count */
oi, /* pos */
&nm); /* new elmt */
}
found = PJ_TRUE;
break;
}
}
if (!found) {
pjmedia_sdp_media *m;
m = sdp_media_clone_deactivate(pool, om, om, local);
pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
new_offer->media_count++, oi, &m);
}
}
} else {
/* If media type change is allowed, the negotiator only needs to fix
* the new offer by adding the missing media line(s) with port number
* set to zero.
*/
for (oi = new_offer->media_count; oi < old_offer->media_count; ++oi) {
pjmedia_sdp_media *m;
m = sdp_media_clone_deactivate(pool, old_offer->media[oi],
old_offer->media[oi], local);
pj_array_insert(new_offer->media, sizeof(new_offer->media[0]),
new_offer->media_count++, oi, &m);
}
}
/* New_offer fixed */
new_offer->origin.version = old_offer->origin.version;
/* Assign PT numbers for our offer and update the mapping. */
assign_pt_and_update_map(pool, neg, new_offer, PJ_TRUE, PJ_FALSE);
if (pjmedia_sdp_session_cmp(neg->last_sent, new_offer, 0) != PJ_SUCCESS) {
++new_offer->origin.version;
}
neg->initial_sdp_tmp = neg->initial_sdp;
neg->initial_sdp = new_offer;
neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, new_offer);
neg->last_sent = neg->neg_local_sdp;
return PJ_SUCCESS;
}
pjsip_inv_send_msg
pjsip_inv_send_msg
函数的定义位于 pjsip/src/pjsip-ua/sip_inv.c 中,如下所示:
/*
* Send a request or response message.
*/
PJ_DEF(pj_status_t) pjsip_inv_send_msg( pjsip_inv_session *inv,
pjsip_tx_data *tdata)
{
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && tdata, PJ_EINVAL);
pj_log_push_indent();
PJ_LOG(5,(inv->obj_name, "Sending %s",
pjsip_tx_data_get_info(tdata)));
if (tdata->msg->type == PJSIP_REQUEST_MSG) {
struct tsx_inv_data *tsx_inv_data;
pjsip_dlg_inc_lock(inv->dlg);
/* Check again that we didn't receive incoming re-INVITE */
if (tdata->msg->line.req.method.id==PJSIP_INVITE_METHOD &&
inv->invite_tsx)
{
pjsip_tx_data_dec_ref(tdata);
pjsip_dlg_dec_lock(inv->dlg);
status = PJ_EINVALIDOP;
goto on_error;
}
/* Don't send BYE before ACK is received
* https://github.com/pjsip/pjproject/issues/1712
*/
if (tdata->msg->line.req.method.id == PJSIP_BYE_METHOD &&
inv->role == PJSIP_ROLE_UAS &&
inv->state == PJSIP_INV_STATE_CONNECTING &&
inv->cause != PJSIP_SC_REQUEST_TIMEOUT &&
inv->cause != PJSIP_SC_TSX_TRANSPORT_ERROR)
{
if (inv->pending_bye)
pjsip_tx_data_dec_ref(inv->pending_bye);
inv->pending_bye = tdata;
PJ_LOG(4, (inv->obj_name, "Delaying BYE request until "
"ACK is received"));
pjsip_dlg_dec_lock(inv->dlg);
goto on_return;
}
/* Associate our data in outgoing invite transaction */
tsx_inv_data = PJ_POOL_ZALLOC_T(inv->pool, struct tsx_inv_data);
tsx_inv_data->inv = inv;
tsx_inv_data->has_sdp = tx_data_has_sdp(tdata);
pjsip_dlg_dec_lock(inv->dlg);
status = pjsip_dlg_send_request(inv->dlg, tdata, mod_inv.mod.id,
tsx_inv_data);
if (status != PJ_SUCCESS) {
goto on_error;
}
/* Check if this is delayed manual ACK (see #416) */
if (mod_inv.cb.on_send_ack &&
tdata->msg->line.req.method.id == PJSIP_ACK_METHOD &&
tdata == inv->last_ack)
{
pjsip_dlg_inc_lock(inv->dlg);
/* Set state to CONFIRMED (if we're not in CONFIRMED yet).
* But don't set it to CONFIRMED if we're already DISCONNECTED
* (this may have been a late 200/OK response.
*/
if (inv->state < PJSIP_INV_STATE_CONFIRMED) {
pjsip_event ack_e;
PJSIP_EVENT_INIT_TX_MSG(ack_e, inv->last_ack);
inv_set_state(inv, PJSIP_INV_STATE_CONFIRMED, &ack_e);
} else if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
/* Avoid possible leaked tdata when invite session is
* already destroyed.
* https://github.com/pjsip/pjproject/pull/2432
*/
pjsip_tx_data_dec_ref(inv->last_ack);
inv->last_ack = NULL;
}
pjsip_dlg_dec_lock(inv->dlg);
}
} else {
pjsip_cseq_hdr *cseq;
/* Can only do this to send response to original INVITE
* request.
*/
cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
PJ_ASSERT_ON_FAIL(cseq != NULL
&& (inv->invite_tsx && cseq->cseq == inv->invite_tsx->cseq),
{ pjsip_tx_data_dec_ref(tdata); return PJ_EINVALIDOP; });
if (inv->options & PJSIP_INV_REQUIRE_100REL) {
status = pjsip_100rel_tx_response(inv, tdata);
} else
{
status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata);
}
if (status != PJ_SUCCESS) {
goto on_error;
}
}
/* Done */
on_return:
pj_log_pop_indent();
return PJ_SUCCESS;
on_error:
pj_log_pop_indent();
return status;
}
- tx_data_has_sdp: 判断是否有 sdp
- pjsip_dlg_send_request: 将 reinvte 发送出去