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