📄 sip_inv.c
字号:
return PJ_SUCCESS;
}
static void *clone_sdp(pj_pool_t *pool, const void *data, unsigned len)
{
PJ_UNUSED_ARG(len);
return pjmedia_sdp_session_clone(pool, data);
}
static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len)
{
return pjmedia_sdp_print(body->data, buf, len);
}
PJ_DEF(pj_status_t) pjsip_create_sdp_body( pj_pool_t *pool,
pjmedia_sdp_session *sdp,
pjsip_msg_body **p_body)
{
const pj_str_t STR_APPLICATION = { "application", 11};
const pj_str_t STR_SDP = { "sdp", 3 };
pjsip_msg_body *body;
body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body));
PJ_ASSERT_RETURN(body != NULL, PJ_ENOMEM);
body->content_type.type = STR_APPLICATION;
body->content_type.subtype = STR_SDP;
body->data = sdp;
body->len = 0;
body->clone_data = &clone_sdp;
body->print_body = &print_sdp;
*p_body = body;
return PJ_SUCCESS;
}
static pjsip_msg_body *create_sdp_body(pj_pool_t *pool,
const pjmedia_sdp_session *c_sdp)
{
pjsip_msg_body *body;
pj_status_t status;
status = pjsip_create_sdp_body(pool,
pjmedia_sdp_session_clone(pool, c_sdp),
&body);
if (status != PJ_SUCCESS)
return NULL;
return body;
}
/*
* 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;
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_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_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)
goto on_return;
tdata->msg->body = create_sdp_body(tdata->pool, offer);
}
/* Add Allow header. */
hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_ALLOW, NULL);
if (hdr) {
pjsip_msg_add_hdr(tdata->msg,
pjsip_hdr_shallow_clone(tdata->pool, hdr));
}
/* Add Supported header */
hdr = pjsip_endpt_get_capability(inv->dlg->endpt, PJSIP_H_SUPPORTED, NULL);
if (hdr) {
pjsip_msg_add_hdr(tdata->msg,
pjsip_hdr_shallow_clone(tdata->pool, hdr));
}
/* Add Require header. */
PJ_TODO(INVITE_ADD_REQUIRE_HEADER);
/* Done. */
*p_tdata = tdata;
on_return:
pjsip_dlg_dec_lock(inv->dlg);
return status;
}
/*
* Negotiate SDP.
*/
static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv )
{
pj_status_t status;
PJ_ASSERT_RETURN(pjmedia_sdp_neg_get_state(inv->neg) ==
PJMEDIA_SDP_NEG_STATE_WAIT_NEGO,
PJMEDIA_SDPNEG_EINSTATE);
status = pjmedia_sdp_neg_negotiate(inv->pool, inv->neg, 0);
PJ_LOG(5,(inv->obj_name, "SDP negotiation done, status=%d", status));
if (mod_inv.cb.on_media_update && inv->notify)
(*mod_inv.cb.on_media_update)(inv, status);
return status;
}
/*
* Check in incoming message for SDP offer/answer.
*/
static pj_status_t inv_check_sdp_in_incoming_msg( pjsip_inv_session *inv,
pjsip_transaction *tsx,
pjsip_rx_data *rdata)
{
struct tsx_inv_data *tsx_inv_data;
static const pj_str_t str_application = { "application", 11 };
static const pj_str_t str_sdp = { "sdp", 3 };
pj_status_t status;
pjsip_msg *msg;
pjmedia_sdp_session *sdp;
/* Get/attach invite session's transaction data */
tsx_inv_data = tsx->mod_data[mod_inv.mod.id];
if (tsx_inv_data == NULL) {
tsx_inv_data = pj_pool_zalloc(tsx->pool, sizeof(struct tsx_inv_data));
tsx_inv_data->inv = inv;
tsx->mod_data[mod_inv.mod.id] = tsx_inv_data;
}
/* MUST NOT do multiple SDP offer/answer in a single transaction.
*/
if (tsx_inv_data->sdp_done)
return PJ_SUCCESS;
/* Check if SDP is present in the message. */
msg = rdata->msg_info.msg;
if (msg->body == NULL) {
/* Message doesn't have body. */
return PJ_SUCCESS;
}
if (pj_stricmp(&msg->body->content_type.type, &str_application) ||
pj_stricmp(&msg->body->content_type.subtype, &str_sdp))
{
/* Message body is not "application/sdp" */
return PJMEDIA_SDP_EINSDP;
}
/* Parse the SDP body. */
status = pjmedia_sdp_parse(rdata->tp_info.pool, msg->body->data,
msg->body->len, &sdp);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error parsing SDP in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
return PJMEDIA_SDP_EINSDP;
}
/* The SDP can be an offer or answer, depending on negotiator's state */
if (inv->neg == NULL ||
pjmedia_sdp_neg_get_state(inv->neg) == PJMEDIA_SDP_NEG_STATE_DONE)
{
/* This is an offer. */
PJ_LOG(5,(inv->obj_name, "Got SDP offer in %s",
pjsip_rx_data_get_info(rdata)));
if (inv->neg == NULL) {
status=pjmedia_sdp_neg_create_w_remote_offer(inv->pool, NULL,
sdp, &inv->neg);
} else {
status=pjmedia_sdp_neg_set_remote_offer(inv->pool, inv->neg, sdp);
}
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error processing SDP offer in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
return PJMEDIA_SDP_EINSDP;
}
/* Inform application about remote offer. */
if (mod_inv.cb.on_rx_offer && inv->notify) {
(*mod_inv.cb.on_rx_offer)(inv, sdp);
}
} else if (pjmedia_sdp_neg_get_state(inv->neg) ==
PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER)
{
/* This is an answer.
* Process and negotiate remote answer.
*/
PJ_LOG(5,(inv->obj_name, "Got SDP answer in %s",
pjsip_rx_data_get_info(rdata)));
status = pjmedia_sdp_neg_set_remote_answer(inv->pool, inv->neg, sdp);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(4,(THIS_FILE, "Error processing SDP answer in %s: %s",
pjsip_rx_data_get_info(rdata), errmsg));
return PJMEDIA_SDP_EINSDP;
}
/* Negotiate SDP */
inv_negotiate_sdp(inv);
/* Mark this transaction has having SDP offer/answer done. */
tsx_inv_data->sdp_done = 1;
} else {
PJ_LOG(5,(THIS_FILE, "Ignored SDP in %s: negotiator state is %s",
pjsip_rx_data_get_info(rdata),
pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_get_state(inv->neg))));
}
return PJ_SUCCESS;
}
/*
* Process INVITE answer, for both initial and subsequent re-INVITE
*/
static pj_status_t process_answer( pjsip_inv_session *inv,
int st_code,
pjsip_tx_data *tdata,
const pjmedia_sdp_session *local_sdp)
{
pj_status_t status;
const pjmedia_sdp_session *sdp = NULL;
/* If local_sdp is specified, then we MUST NOT have answered the
* offer before.
*/
if (local_sdp && (st_code/100==1 || st_code/100==2)) {
if (inv->neg == NULL) {
status = pjmedia_sdp_neg_create_w_local_offer(inv->pool, local_sdp,
&inv->neg);
} else if (pjmedia_sdp_neg_get_state(inv->neg)==
PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER)
{
status = pjmedia_sdp_neg_set_local_answer(inv->pool, inv->neg,
local_sdp);
} else {
/* Can not specify local SDP at this state. */
pj_assert(0);
status = PJMEDIA_SDPNEG_EINSTATE;
}
if (status != PJ_SUCCESS)
return status;
}
/* If SDP negotiator is ready, start negotiation. */
if (st_code/100==2 || (st_code/10==18 && st_code!=180)) {
pjmedia_sdp_neg_state neg_state;
/* Start nego when appropriate. */
neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) :
PJMEDIA_SDP_NEG_STATE_NULL;
if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) {
status = pjmedia_sdp_neg_get_neg_local(inv->neg, &sdp);
} else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO &&
pjmedia_sdp_neg_has_local_answer(inv->neg) )
{
status = inv_negotiate_sdp(inv);
if (status != PJ_SUCCESS)
return status;
status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp);
}
}
/* Include SDP when it's available for 2xx and 18x (but not 180) response.
* Subsequent response will include this SDP.
*/
if (sdp) {
tdata->msg->body = create_sdp_body(tdata->pool, sdp);
}
return PJ_SUCCESS;
}
/*
* Create first response to INVITE
*/
PJ_DEF(pj_status_t) pjsip_inv_initial_answer( pjsip_inv_session *inv,
pjsip_rx_data *rdata,
int st_code,
const pj_str_t *st_text,
const pjmedia_sdp_session *sdp,
pjsip_tx_data **p_tdata)
{
pjsip_tx_data *tdata;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
/* Must have INVITE transaction. */
PJ_ASSERT_RETURN(inv->invite_tsx, PJ_EBUG);
pjsip_dlg_inc_lock(inv->dlg);
/* Create response */
status = pjsip_dlg_create_response(inv->dlg, rdata, st_code, st_text,
&tdata);
if (status != PJ_SUCCESS)
goto on_return;
/* Process SDP in answer */
status = process_answer(inv, st_code, tdata, sdp);
if (status != PJ_SUCCESS) {
pjsip_tx_data_dec_ref(tdata);
goto on_return;
}
*p_tdata = tdata;
on_return:
pjsip_dlg_dec_lock(inv->dlg);
return status;
}
/*
* Answer initial INVITE
* Re-INVITE will be answered automatically, and will not use this function.
*/
PJ_DEF(pj_status_t) pjsip_inv_answer( pjsip_inv_session *inv,
int st_code,
const pj_str_t *st_text,
const pjmedia_sdp_session *local_sdp,
pjsip_tx_data **p_tdata )
{
pjsip_tx_data *last_res;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
/* Must have INVITE transaction. */
PJ_ASSERT_RETURN(inv->invite_tsx, PJ_EBUG);
/* INVITE transaction MUST have transmitted a response (e.g. 100) */
PJ_ASSERT_RETURN(inv->invite_tsx->last_tx, PJ_EINVALIDOP);
pjsip_dlg_inc_lock(inv->dlg);
/* Modify last response. */
last_res = inv->invite_tsx->last_tx;
status = pjsip_dlg_modify_response(inv->dlg, last_res, st_code, st_text);
if (status != PJ_SUCCESS)
goto on_return;
/* Process SDP in answer */
status = process_answer(inv, st_code, last_res, local_sdp);
if (status != PJ_SUCCESS) {
pjsip_tx_data_dec_ref(last_res);
goto on_return;
}
*p_tdata = last_res;
on_return:
pjsip_dlg_dec_lock(inv->dlg);
return status;
}
/*
* Set SDP answer.
*/
PJ_DEF(pj_status_t) pjsip_inv_set_sdp_answer( pjsip_inv_session *inv,
const pjmedia_sdp_session *sdp )
{
pj_status_t status;
PJ_ASSERT_RETURN(inv && sdp, PJ_EINVAL);
pjsip_dlg_inc_lock(inv->dlg);
status = pjmedia_sdp_neg_set_local_answer( inv->pool, inv->neg, sdp);
pjsip_dlg_dec_lock(inv->dlg);
return status;
}
/*
* End session.
*/
PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv,
int st_code,
const pj_str_t *st_text,
pjsip_tx_data **p_tdata )
{
pjsip_tx_data *tdata;
pj_status_t status;
/* Verify arguments. */
PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -