⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sip_inv.c

📁 一个开源SIP协议栈
💻 C
📖 第 1 页 / 共 5 页
字号:
    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 + -