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 + -
显示快捷键?