sip_inv.c

来自「基于sip协议的网络电话源码」· C语言 代码 · 共 2,560 行 · 第 1/5 页

C
2,560
字号
    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);    /* Set cause code. */    inv_set_cause(inv, st_code, st_text);    /* Create appropriate message. */    switch (inv->state) {    case PJSIP_INV_STATE_CALLING:    case PJSIP_INV_STATE_EARLY:    case PJSIP_INV_STATE_INCOMING:	if (inv->role == PJSIP_ROLE_UAC) {	    /* For UAC when session has not been confirmed, create CANCEL. */	    /* MUST have the original UAC INVITE transaction. */	    PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG);	    /* But CANCEL should only be called when we have received a	     * provisional response. If we haven't received any responses,	     * just destroy the transaction.	     */	    if (inv->invite_tsx->status_code < 100) {		pjsip_tsx_terminate(inv->invite_tsx, 487);		*p_tdata = NULL;		return PJ_SUCCESS;	    }	    /* The CSeq here assumes that the dialog is started with an	     * INVITE session. This may not be correct; dialog can be 	     * started as SUBSCRIBE session.	     * So fix this!	     */	    status = pjsip_endpt_create_cancel(inv->dlg->endpt, 					       inv->invite_tsx->last_tx,					       &tdata);	} else {	    /* For UAS, send a final response. */	    tdata = inv->invite_tsx->last_tx;	    PJ_ASSERT_RETURN(tdata != NULL, PJ_EINVALIDOP);	    //status = pjsip_dlg_modify_response(inv->dlg, tdata, st_code,	    //				       st_text);	    status = pjsip_inv_answer(inv, st_code, st_text, NULL, &tdata);	}	break;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?