sdp_neg.c
来自「基于sip协议的网络电话源码」· C语言 代码 · 共 1,051 行 · 第 1/2 页
C
1,051 行
/* $Id: sdp_neg.c 1071 2007-03-15 21:56:33Z bennylp $ *//* * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <pjmedia/sdp_neg.h>#include <pjmedia/sdp.h>#include <pjmedia/errno.h>#include <pj/assert.h>#include <pj/pool.h>#include <pj/string.h>#include <pj/ctype.h>#include <pj/array.h>/** * This structure describes SDP media negotiator. */struct pjmedia_sdp_neg{ pjmedia_sdp_neg_state state; /**< Negotiator state. */ pj_bool_t prefer_remote_codec_order; pj_bool_t has_remote_answer; pj_bool_t answer_was_remote; pjmedia_sdp_session *initial_sdp, /**< Initial local SDP */ *active_local_sdp, /**< Currently active local SDP. */ *active_remote_sdp, /**< Currently active remote's. */ *neg_local_sdp, /**< Temporary local SDP. */ *neg_remote_sdp; /**< Temporary remote SDP. */};static const char *state_str[] = { "STATE_NULL", "STATE_LOCAL_OFFER", "STATE_REMOTE_OFFER", "STATE_WAIT_NEGO", "STATE_DONE",};/* * Get string representation of negotiator state. */PJ_DEF(const char*) pjmedia_sdp_neg_state_str(pjmedia_sdp_neg_state state){ if (state >=0 && state < PJ_ARRAY_SIZE(state_str)) return state_str[state]; return "<?UNKNOWN?>";}/* * Create with local offer. */PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_local_offer( pj_pool_t *pool, const pjmedia_sdp_session *local, pjmedia_sdp_neg **p_neg){ pjmedia_sdp_neg *neg; pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && local && p_neg, PJ_EINVAL); *p_neg = NULL; /* Validate local offer. */ PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(local))==PJ_SUCCESS, status); /* Create and initialize negotiator. */ neg = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_neg)); PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM); neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER; neg->initial_sdp = pjmedia_sdp_session_clone(pool, local); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local); *p_neg = neg; return PJ_SUCCESS;}/* * Create with remote offer and initial local offer/answer. */PJ_DEF(pj_status_t) pjmedia_sdp_neg_create_w_remote_offer(pj_pool_t *pool, const pjmedia_sdp_session *initial, const pjmedia_sdp_session *remote, pjmedia_sdp_neg **p_neg){ pjmedia_sdp_neg *neg; pj_status_t status; /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && remote && p_neg, PJ_EINVAL); *p_neg = NULL; /* Validate remote offer and initial answer */ status = pjmedia_sdp_validate(remote); if (status != PJ_SUCCESS) return status; /* Create and initialize negotiator. */ neg = pj_pool_zalloc(pool, sizeof(pjmedia_sdp_neg)); PJ_ASSERT_RETURN(neg != NULL, PJ_ENOMEM); neg->prefer_remote_codec_order = PJMEDIA_SDP_NEG_PREFER_REMOTE_CODEC_ORDER; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); if (initial) { PJ_ASSERT_RETURN((status=pjmedia_sdp_validate(initial))==PJ_SUCCESS, status); neg->initial_sdp = pjmedia_sdp_session_clone(pool, initial); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, initial); neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; } else { neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER; } *p_neg = neg; return PJ_SUCCESS;}/* * Set codec order preference. */PJ_DEF(pj_status_t)pjmedia_sdp_neg_set_prefer_remote_codec_order(pjmedia_sdp_neg *neg, pj_bool_t prefer_remote){ PJ_ASSERT_RETURN(neg, PJ_EINVAL); neg->prefer_remote_codec_order = prefer_remote; return PJ_SUCCESS;}/* * Get SDP negotiator state. */PJ_DEF(pjmedia_sdp_neg_state)pjmedia_sdp_neg_get_state( pjmedia_sdp_neg *neg ){ /* Check arguments are valid. */ PJ_ASSERT_RETURN(neg != NULL, PJMEDIA_SDP_NEG_STATE_NULL); return neg->state;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local){ PJ_ASSERT_RETURN(neg && local, PJ_EINVAL); PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE); *local = neg->active_local_sdp; return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_active_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote){ PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL); PJ_ASSERT_RETURN(neg->active_remote_sdp, PJMEDIA_SDPNEG_ENOACTIVE); *remote = neg->active_remote_sdp; return PJ_SUCCESS;}PJ_DEF(pj_bool_t)pjmedia_sdp_neg_was_answer_remote(pjmedia_sdp_neg *neg){ PJ_ASSERT_RETURN(neg, PJ_FALSE); return neg->answer_was_remote;}PJ_DEF(pj_status_t)pjmedia_sdp_neg_get_neg_remote( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **remote){ PJ_ASSERT_RETURN(neg && remote, PJ_EINVAL); PJ_ASSERT_RETURN(neg->neg_remote_sdp, PJMEDIA_SDPNEG_ENONEG); *remote = neg->neg_remote_sdp; return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_get_neg_local( pjmedia_sdp_neg *neg, const pjmedia_sdp_session **local){ PJ_ASSERT_RETURN(neg && local, PJ_EINVAL); PJ_ASSERT_RETURN(neg->neg_local_sdp, PJMEDIA_SDPNEG_ENONEG); *local = neg->neg_local_sdp; return PJ_SUCCESS;}/* * Modify local SDP and wait for remote answer. */PJ_DEF(pj_status_t) pjmedia_sdp_neg_modify_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local){ /* 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); /* Change state to STATE_LOCAL_OFFER */ neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; neg->initial_sdp = pjmedia_sdp_session_clone(pool, local); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local); return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_send_local_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session **offer){ /* Check arguments are valid. */ PJ_ASSERT_RETURN(neg && offer, PJ_EINVAL); *offer = NULL; /* Can only do this in STATE_DONE or STATE_LOCAL_OFFER. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE || neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDPNEG_EINSTATE); if (neg->state == PJMEDIA_SDP_NEG_STATE_DONE) { /* If in STATE_DONE, set the active SDP as the offer. */ PJ_ASSERT_RETURN(neg->active_local_sdp, PJMEDIA_SDPNEG_ENOACTIVE); neg->state = PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER; neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->active_local_sdp); *offer = neg->active_local_sdp; } else { /* We assume that we're in STATE_LOCAL_OFFER. * In this case set the neg_local_sdp as the offer. */ *offer = neg->neg_local_sdp; } return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote){ /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL); /* Can only do this in STATE_LOCAL_OFFER. * If we haven't provided local offer, then rx_remote_offer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER, PJMEDIA_SDPNEG_EINSTATE); /* We're ready to negotiate. */ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; neg->has_remote_answer = PJ_TRUE; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_remote_offer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *remote){ /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && remote, PJ_EINVAL); /* Can only do this in STATE_DONE. * If we already provide local offer, then rx_remote_answer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_DONE, PJMEDIA_SDPNEG_EINSTATE); /* State now is STATE_REMOTE_OFFER. */ neg->state = PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER; neg->neg_remote_sdp = pjmedia_sdp_session_clone(pool, remote); return PJ_SUCCESS;}PJ_DEF(pj_status_t) pjmedia_sdp_neg_set_local_answer( pj_pool_t *pool, pjmedia_sdp_neg *neg, const pjmedia_sdp_session *local){ /* Check arguments are valid. */ PJ_ASSERT_RETURN(pool && neg && local, PJ_EINVAL); /* Can only do this in STATE_REMOTE_OFFER. * If we already provide local offer, then rx_remote_answer() should * be called instead of this function. */ PJ_ASSERT_RETURN(neg->state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER, PJMEDIA_SDPNEG_EINSTATE); /* State now is STATE_WAIT_NEGO. */ neg->state = PJMEDIA_SDP_NEG_STATE_WAIT_NEGO; if (local) neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, local); else { PJ_ASSERT_RETURN(neg->initial_sdp, PJMEDIA_SDPNEG_ENOINITIAL); neg->neg_local_sdp = pjmedia_sdp_session_clone(pool, neg->initial_sdp); } return PJ_SUCCESS;}PJ_DEF(pj_bool_t) pjmedia_sdp_neg_has_local_answer(pjmedia_sdp_neg *neg){ pj_assert(neg && neg->state==PJMEDIA_SDP_NEG_STATE_WAIT_NEGO); return !neg->has_remote_answer;}/* Swap string. */static void str_swap(pj_str_t *str1, pj_str_t *str2){ pj_str_t tmp = *str1; *str1 = *str2; *str2 = tmp;}static void remove_all_media_directions(pjmedia_sdp_media *m){ pjmedia_sdp_media_remove_all_attr(m, "inactive"); pjmedia_sdp_media_remove_all_attr(m, "sendrecv"); pjmedia_sdp_media_remove_all_attr(m, "sendonly"); pjmedia_sdp_media_remove_all_attr(m, "recvonly");}/* Update media direction based on peer's media direction */static void update_media_direction(pj_pool_t *pool, const pjmedia_sdp_media *remote, pjmedia_sdp_media *local){ pjmedia_dir old_dir = PJMEDIA_DIR_ENCODING_DECODING, new_dir; /* Get the media direction of local SDP */ if (pjmedia_sdp_media_find_attr2(local, "sendonly", NULL)) old_dir = PJMEDIA_DIR_ENCODING; else if (pjmedia_sdp_media_find_attr2(local, "recvonly", NULL)) old_dir = PJMEDIA_DIR_DECODING; else if (pjmedia_sdp_media_find_attr2(local, "inactive", NULL)) old_dir = PJMEDIA_DIR_NONE; new_dir = old_dir; /* Adjust local media direction based on remote media direction */ if (pjmedia_sdp_media_find_attr2(remote, "inactive", NULL) != NULL) { /* If remote has "a=inactive", then local is inactive too */ new_dir = PJMEDIA_DIR_NONE; } else if(pjmedia_sdp_media_find_attr2(remote, "sendonly", NULL) != NULL) { /* If remote has "a=sendonly", then set local to "recvonly" if * it is currently "sendrecv". Otherwise if local is NOT "recvonly", * then set local direction to "inactive". */ switch (old_dir) { case PJMEDIA_DIR_ENCODING_DECODING: new_dir = PJMEDIA_DIR_DECODING; break; case PJMEDIA_DIR_DECODING: /* No change */ break; default: new_dir = PJMEDIA_DIR_NONE; break; } } else if(pjmedia_sdp_media_find_attr2(remote, "recvonly", NULL) != NULL) { /* If remote has "a=recvonly", then set local to "sendonly" if * it is currently "sendrecv". Otherwise if local is NOT "sendonly", * then set local direction to "inactive" */ switch (old_dir) { case PJMEDIA_DIR_ENCODING_DECODING: new_dir = PJMEDIA_DIR_ENCODING; break; case PJMEDIA_DIR_ENCODING: /* No change */ break; default: new_dir = PJMEDIA_DIR_NONE; break; } } else { /* Remote indicates "sendrecv" capability. No change to local * direction */ } if (new_dir != old_dir) { pjmedia_sdp_attr *a = NULL; remove_all_media_directions(local); switch (new_dir) { case PJMEDIA_DIR_NONE: a = pjmedia_sdp_attr_create(pool, "inactive", NULL); break; case PJMEDIA_DIR_ENCODING: a = pjmedia_sdp_attr_create(pool, "sendonly", NULL); break; case PJMEDIA_DIR_DECODING: a = pjmedia_sdp_attr_create(pool, "recvonly", NULL); break; default: /* sendrecv */ break; } if (a) { pjmedia_sdp_media_add_attr(local, a); } }}/* Update single local media description to after receiving answer * from remote. */static pj_status_t process_m_answer( pj_pool_t *pool, pjmedia_sdp_media *offer, pjmedia_sdp_media *answer, pj_bool_t allow_asym){ unsigned i; /* Check that the media type match our offer. */ if (pj_strcmp(&answer->desc.media, &offer->desc.media)!=0) { /* The media type in the answer is different than the offer! */ return PJMEDIA_SDPNEG_EINVANSMEDIA; } /* Chec that transport in the answer match our offer. */ if (pj_strcmp(&answer->desc.transport, &offer->desc.transport)!=0) { /* The transport in the answer is different than the offer! */ return PJMEDIA_SDPNEG_EINVANSTP; } /* Check if remote has rejected our offer */ if (answer->desc.port == 0) { /* Remote has rejected our offer. * Set our port to zero too in active SDP. */ offer->desc.port = 0; } /* Process direction attributes */ update_media_direction(pool, answer, offer); /* If asymetric media is allowed, then just check that remote answer has * codecs that are within the offer. * * Otherwise if asymetric media is not allowed, then we will choose only * one codec in our initial offer to match the answer. */ if (allow_asym) { for (i=0; i<answer->desc.fmt_count; ++i) { unsigned j; pj_str_t *rem_fmt = &answer->desc.fmt[i]; for (j=0; j<offer->desc.fmt_count; ++j) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?