/* * This file is part of the Sofia-SIP package * * Copyright (C) 2005 Nokia Corporation. * * Contact: Pekka Pessi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * */ /**@CFILE sdp.c Simple SDP interface. * * @author Pekka Pessi * @author Kai Vehmanen * * @date Created: Fri Feb 18 10:25:08 2000 ppessi */ #include "config.h" #include #include #include #include #include #include #include #include #include "sofia-sip/sdp.h" struct align { void *_a; char _b; }; #define ALIGN(v, n) ((n - (intptr_t)(v)) & (n - 1)) #define STRUCT_ALIGN_ (sizeof(struct align) - offsetof(struct align, _b)) #define STRUCT_ALIGN(v) ALIGN(v, sizeof(void *)) #define ASSERT_STRUCT_ALIGN(p) \ (STRUCT_ALIGN(p) ? (void)assert(!"STRUCT_ALIGNED(" #p ")") : (void)0) const unsigned sdp_struct_align_ = sizeof(void *) - STRUCT_ALIGN_; #define STR_XTRA(rv, s) ((s) ? rv += strlen((s)) + 1 : 0) #define PTR_XTRA(rv, p, f) \ ((p) ? (rv += STRUCT_ALIGN(rv) + f(p)) : 0) #define LST_XTRA(rv, l, f) \ ((l) ? (rv += STRUCT_ALIGN(rv) + list_xtra_all((xtra_f*)f, l)) : 0) #define STRUCT_DUP(p, dst, src) \ ASSERT_STRUCT_ALIGN(p); assert(*(int*)(src) >= (int)sizeof(*src)); \ ((*(int*)(src) >= (int)sizeof(*src) \ ? (dst = memcpy((p), (src), sizeof(*src))) \ : (dst = memcpy((p), (src), *(int*)(src))), \ memset((p)+*(int*)(src), 0, sizeof(*src) - *(int*)(src))), \ ((p) += sizeof(*src))) #define STRUCT_DUP2(p, dst, src) \ ASSERT_STRUCT_ALIGN(p); assert(*(int*)(src) >= (int)sizeof(*src)); \ (dst = memcpy((p), (src), *(int*)(src)), ((p) += *(int*)(src))) #define STR_DUP(p, dst, src, m) \ ((src->m) ? ((dst->m) = strcpy((p), (src->m)), (p) += strlen((p)) + 1) \ : ((dst->m) = 0)) #define PTR_DUP(p, dst, src, m, dup) \ ((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)), ((dup)(&(p), (src->m)))): 0) #define LST_DUP(p, dst, src, m, dup) \ ((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\ list_dup_all((dup_f*)(dup), &(p), src->m)) : 0) #define MED_XTRA_EX(rv, l, c) \ ((l) ? (rv += STRUCT_ALIGN(rv) + media_xtra_ex(l, c)) : 0) #define MED_DUP_EX(p, dst, src, m, dst_c, src_c) \ ((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\ media_dup_all(&(p), src->m, dst, dst_c, src_c)) : 0) #define MED_XTRA_ALL(rv, m) \ ((m) ? (rv += STRUCT_ALIGN(rv) + media_xtra_all(m)) : 0) #define MED_DUP_ALL(p, dst, src, m) \ ((dst->m) = (src->m)?((p += STRUCT_ALIGN(p)),\ media_dup_all(&(p), src->m, dst)) : 0) typedef size_t xtra_f(void const *); typedef void *dup_f(char **bb, void const *src); static size_t list_xtra_all(xtra_f *xtra, void const *v); static void *list_dup_all(dup_f *dup, char **bb, void const *vsrc); static size_t session_xtra(sdp_session_t const *o); static sdp_session_t *session_dup(char **pp, sdp_session_t const *o); static size_t origin_xtra(sdp_origin_t const *o); static sdp_origin_t *origin_dup(char **pp, sdp_origin_t const *o); static size_t connection_xtra(sdp_connection_t const *o); static sdp_connection_t *connection_dup(char **pp, sdp_connection_t const *o); static size_t bandwidth_xtra(sdp_bandwidth_t const *o); static sdp_bandwidth_t *bandwidth_dup(char **pp, sdp_bandwidth_t const *o); static size_t time_xtra(sdp_time_t const *o); static sdp_time_t *time_dup(char **pp, sdp_time_t const *o); static size_t repeat_xtra(sdp_repeat_t const *o); static sdp_repeat_t *repeat_dup(char **pp, sdp_repeat_t const *o); static size_t zone_xtra(sdp_zone_t const *o); static sdp_zone_t *zone_dup(char **pp, sdp_zone_t const *o); static size_t key_xtra(sdp_key_t const *o); static sdp_key_t *key_dup(char **pp, sdp_key_t const *o); static size_t attribute_xtra(sdp_attribute_t const *o); static sdp_attribute_t *attribute_dup(char **pp, sdp_attribute_t const *o); static size_t list_xtra(sdp_list_t const *o); static sdp_list_t *list_dup(char **pp, sdp_list_t const *o); static size_t rtpmap_xtra(sdp_rtpmap_t const *o); static sdp_rtpmap_t *rtpmap_dup(char **pp, sdp_rtpmap_t const *o); static size_t media_xtra(sdp_media_t const *o); static sdp_media_t *media_dup(char **pp, sdp_media_t const *o, sdp_session_t *sdp); #ifdef nomore static size_t media_xtra_ex(sdp_media_t const *o, sdp_connection_t const *c); static sdp_media_t *media_dup_ex(char **pp, sdp_media_t const *o, sdp_session_t *sdp, sdp_connection_t *dst_c, sdp_connection_t const *src_c); #endif static size_t media_xtra_all(sdp_media_t const *o); static sdp_media_t *media_dup_all(char **pp, sdp_media_t const *o, sdp_session_t *sdp); /** Define a function body duplicating an SDP structure. */ #define SDP_DUP(type, name) \ sdp_##type##_t *rv; size_t size; char *p, *end; \ if (!name) return NULL; \ size = type##_xtra(name); \ p = su_alloc(h, size); end = p + size; \ rv = type##_dup(&p, name); \ assert(p == end); \ return rv; /** Define a function body duplicating a list of SDP structures. */ #define SDP_LIST_DUP(type, name) \ sdp_##type##_t *rv; size_t size; char *p, *end; \ if (!name) return NULL; \ size = list_xtra_all((xtra_f*)type##_xtra, name); \ rv = su_alloc(h, size); p = (char *)rv; end = p + size; \ list_dup_all((dup_f*)type##_dup, &p, name); \ assert(p == end); \ return rv; /**Duplicate an SDP origin description. * * The function sdp_origin_dup() duplicates (deeply copies) an SDP origin * description @a o allocating memory using memory @a home. * * @param h Memory home * @param o SDP origin description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_origin_t structure is * returned, otherwise NULL is returned. */ sdp_origin_t *sdp_origin_dup(su_home_t *h, sdp_origin_t const *o) { SDP_DUP(origin, o); } /**Duplicate an SDP connection description. * * The function sdp_connection_dup() duplicates (deeply copies) a list of * SDP connection description @a c allocating memory using memory @a home. * * @param h Memory home * @param c SDP connection description to be duplicated * * @note The duplicated list is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_connection_t structure is * returned, otherwise NULL is returned. */ sdp_connection_t *sdp_connection_dup(su_home_t *h, sdp_connection_t const *c) { SDP_LIST_DUP(connection, c); } /**Duplicate an SDP bandwidth description. * * The function sdp_bandwidth_dup() duplicates (deeply copies) a list of SDP * bandwidth descriptions @a b allocating memory using memory @a home. * * @param h Memory home * @param b SDP bandwidth description to be duplicated * * @note The duplicated list is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_bandwidth_t structure is * returned, otherwise NULL is returned. */ sdp_bandwidth_t *sdp_bandwidth_dup(su_home_t *h, sdp_bandwidth_t const *b) { SDP_LIST_DUP(bandwidth, b); } /**Duplicate an SDP time description. * * The function sdp_time_dup() duplicates (deeply copies) a list of SDP time * descriptions @a t allocating memory using memory @a home. * * @param h Memory home * @param t SDP time description to be duplicated * * @note The duplicated list is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_time_t structure is * returned, otherwise NULL is returned. */ sdp_time_t *sdp_time_dup(su_home_t *h, sdp_time_t const *t) { SDP_LIST_DUP(time, t); } /**Duplicate an SDP repeat description. * * The function sdp_repeat_dup() duplicates (deeply copies) an SDP repeat * description @a r allocating memory using memory @a home. * * @param h Memory home * @param r SDP repeat description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_repeat_t structure is * returned, otherwise NULL is returned. */ sdp_repeat_t *sdp_repeat_dup(su_home_t *h, sdp_repeat_t const *r) { SDP_DUP(repeat, r); } /**Duplicate an SDP zone description. * * The function sdp_zone_dup() duplicates (deeply copies) an SDP zone * description @a z allocating memory using memory @a home. * * @param h Memory home * @param z SDP zone description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_zone_t structure is * returned, otherwise NULL is returned. */ sdp_zone_t *sdp_zone_dup(su_home_t *h, sdp_zone_t const *z) { SDP_DUP(zone, z); } /**Duplicate an SDP key description. * * The function sdp_key_dup() duplicates (deeply copies) an SDP key * description @a k allocating memory using memory @a home. * * @param h Memory home * @param k SDP key description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_key_t structure is * returned, otherwise NULL is returned. */ sdp_key_t *sdp_key_dup(su_home_t *h, sdp_key_t const *k) { SDP_DUP(key, k); } /**Duplicate an SDP attribute list. * * The function sdp_attribute_dup() duplicates (deeply copies) an SDP * attribute list @a a allocating memory using memory @a home. * * @param h Memory home * @param a SDP attribute description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_attribute_t structure is * returned, otherwise NULL is returned. */ sdp_attribute_t *sdp_attribute_dup(su_home_t *h, sdp_attribute_t const *a) { SDP_LIST_DUP(attribute, a); } /**Duplicate an SDP list of text. * * The function sdp_list_dup() duplicates (deeply copies) an SDP text * list @a l allocating memory using memory @a home. * * @param h Memory home * @param l SDP list description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_list_t structure is * returned, otherwise NULL is returned. */ sdp_list_t *sdp_list_dup(su_home_t *h, sdp_list_t const *l) { SDP_LIST_DUP(list, l); } /**Duplicate an SDP rtpmap list. * * The function sdp_rtpmap_dup() duplicates (deeply copies) an SDP rtpmap * list @a rm allocating memory using memory @a home. * * @param h Memory home * @param rm SDP rtpmap description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_rtpmap_t structure is * returned, otherwise NULL is returned. */ sdp_rtpmap_t *sdp_rtpmap_dup(su_home_t *h, sdp_rtpmap_t const *rm) { SDP_LIST_DUP(rtpmap, rm); } /**Duplicate an SDP media description. * * The function sdp_media_dup() duplicates (deeply copies) an SDP media * description @a m allocating memory using memory @a home. * * @param h Memory home * @param m SDP media description to be duplicated * @param sdp SDP session description to which the newly allocated * media description is linked * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_media_t structure is * returned, otherwise NULL is returned. */ sdp_media_t *sdp_media_dup(su_home_t *h, sdp_media_t const *m, sdp_session_t *sdp) { sdp_media_t *rv; size_t size; char *p, *end; size = media_xtra(m); p = su_alloc(h, size); end = p + size; rv = media_dup(&p, m, sdp); assert(p == end); return rv; } /**Duplicate an SDP media description. * * The function sdp_media_dup_all() duplicates (deeply copies) a list of SDP * media descriptions @a m allocating memory using memory @a home. * * @param h Memory home * @param m list of SDP media descriptions to be duplicated * @param sdp SDP session description to which the newly allocated * media descriptions are linked * * @note The duplicated list is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to a newly allocated list of sdp_media_t * structures is returned, otherwise NULL is returned. */ sdp_media_t *sdp_media_dup_all(su_home_t *h, sdp_media_t const *m, sdp_session_t *sdp) { sdp_media_t *rv; size_t size; char *p, *end; size = media_xtra_all(m); p = su_alloc(h, size); end = p + size; rv = media_dup_all(&p, m, sdp); assert(p == end); return rv; } #ifdef nomore /* really deprecated */ /**Duplicate media description with common address. * * This function is provided in order to avoid duplicate @c c= lines. If * the @c c= line in media description equals to @a src_c, it is not * duplicated but replaced with @a dst_c instead. * * @param home Memory home * @param src SDP media description to be duplicated * @param sdp SDP session description to which the newly allocated * media description is linked * @param dst_c Connection description used instead of duplicate of @a src_c. * @param src_c Connection description not to be duplicated * @return * If successful, a pointer to newly allocated sdp_media_t structure is * returned, otherwise NULL is returned. * * @deprecated * This function is deprecated. Use sdp_media_dup() instead. */ sdp_media_t *sdp_media_dup_ex(su_home_t *home, sdp_media_t const *src, sdp_session_t *sdp, sdp_connection_t *dst_c, sdp_connection_t const *src_c) { sdp_media_t *rv; size_t size; char *p, *end; size = media_xtra_all(src, src_c); p = su_alloc(home, size); end = p + size; rv = media_dup_all(&p, src, sdp, dst_c, src_c); assert(p == end); return rv; } #endif /* ---------------------------------------------------------------------- */ static size_t origin_xtra(sdp_origin_t const *o) { size_t rv = sizeof(*o); STR_XTRA(rv, o->o_username); PTR_XTRA(rv, o->o_address, connection_xtra); return rv; } static sdp_origin_t *origin_dup(char **pp, sdp_origin_t const *src) { char *p; sdp_origin_t *o; p = *pp; STRUCT_DUP(p, o, src); STR_DUP(p, o, src, o_username); PTR_DUP(p, o, src, o_address, connection_dup); assert((size_t)(p - *pp) == origin_xtra(src)); *pp = p; return o; } static size_t connection_xtra(sdp_connection_t const *c) { size_t rv = sizeof(*c); STR_XTRA(rv, c->c_address); return rv; } static sdp_connection_t *connection_dup(char **pp, sdp_connection_t const *src) { char *p; sdp_connection_t *c; p = *pp; STRUCT_DUP(p, c, src); c->c_next = NULL; STR_DUP(p, c, src, c_address); assert((size_t)(p - *pp) == connection_xtra(src)); *pp = p; return c; } static size_t bandwidth_xtra(sdp_bandwidth_t const *b) { size_t rv = sizeof(*b); STR_XTRA(rv, b->b_modifier_name); return rv; } static sdp_bandwidth_t *bandwidth_dup(char **pp, sdp_bandwidth_t const *src) { char *p; sdp_bandwidth_t *b; p = *pp; STRUCT_DUP(p, b, src); b->b_next = NULL; STR_DUP(p, b, src, b_modifier_name); assert((size_t)(p - *pp) == bandwidth_xtra(src)); *pp = p; return b; } static size_t time_xtra(sdp_time_t const *t) { size_t rv = sizeof(*t); PTR_XTRA(rv, t->t_repeat, repeat_xtra); PTR_XTRA(rv, t->t_zone, zone_xtra); return rv; } static sdp_time_t *time_dup(char **pp, sdp_time_t const *src) { char *p; sdp_time_t *t; p = *pp; STRUCT_DUP(p, t, src); t->t_next = NULL; PTR_DUP(p, t, src, t_repeat, repeat_dup); PTR_DUP(p, t, src, t_zone, zone_dup); assert((size_t)(p - *pp) == time_xtra(src)); *pp = p; return t; } static size_t repeat_xtra(sdp_repeat_t const *r) { return (size_t)r->r_size; } static sdp_repeat_t *repeat_dup(char **pp, sdp_repeat_t const *src) { char *p; sdp_repeat_t *r; p = *pp; STRUCT_DUP2(p, r, src); assert((size_t)(p - *pp) == repeat_xtra(src)); *pp = p; return r; } static size_t zone_xtra(sdp_zone_t const *z) { return z->z_size; } static sdp_zone_t *zone_dup(char **pp, sdp_zone_t const *src) { char *p; sdp_zone_t *z; p = *pp; STRUCT_DUP2(p, z, src); assert((size_t)(p - *pp) == zone_xtra(src)); *pp = p; return z; } static size_t key_xtra(sdp_key_t const *k) { size_t rv = sizeof(*k); STR_XTRA(rv, k->k_method_name); STR_XTRA(rv, k->k_material); return rv; } static sdp_key_t *key_dup(char **pp, sdp_key_t const *src) { char *p; sdp_key_t *k; p = *pp; STRUCT_DUP(p, k, src); STR_DUP(p, k, src, k_method_name); STR_DUP(p, k, src, k_material); assert((size_t)(p - *pp) == key_xtra(src)); *pp = p; return k; } static size_t attribute_xtra(sdp_attribute_t const *a) { size_t rv = sizeof(*a); STR_XTRA(rv, a->a_name); STR_XTRA(rv, a->a_value); return rv; } static sdp_attribute_t *attribute_dup(char **pp, sdp_attribute_t const *src) { char *p; sdp_attribute_t *a; p = *pp; STRUCT_DUP(p, a, src); a->a_next = NULL; STR_DUP(p, a, src, a_name); STR_DUP(p, a, src, a_value); assert((size_t)(p - *pp) == attribute_xtra(src)); *pp = p; return a; } static size_t media_xtra(sdp_media_t const *m) { size_t rv = sizeof(*m); STR_XTRA(rv, m->m_type_name); STR_XTRA(rv, m->m_proto_name); LST_XTRA(rv, m->m_format, list_xtra); LST_XTRA(rv, m->m_rtpmaps, rtpmap_xtra); STR_XTRA(rv, m->m_information); LST_XTRA(rv, m->m_connections, connection_xtra); LST_XTRA(rv, m->m_bandwidths, bandwidth_xtra); PTR_XTRA(rv, m->m_key, key_xtra); LST_XTRA(rv, m->m_attributes, attribute_xtra); return rv; } static sdp_media_t *media_dup(char **pp, sdp_media_t const *src, sdp_session_t *sdp) { char *p; sdp_media_t *m; p = *pp; STRUCT_DUP(p, m, src); m->m_next = NULL; STR_DUP(p, m, src, m_type_name); STR_DUP(p, m, src, m_proto_name); LST_DUP(p, m, src, m_format, list_dup); LST_DUP(p, m, src, m_rtpmaps, rtpmap_dup); STR_DUP(p, m, src, m_information); LST_DUP(p, m, src, m_connections, connection_dup); LST_DUP(p, m, src, m_bandwidths, bandwidth_dup); PTR_DUP(p, m, src, m_key, key_dup); LST_DUP(p, m, src, m_attributes, attribute_dup); /* note! we must not implicitly use 'src->m_session' as it might point to a temporary session */ m->m_session = sdp; m->m_rejected = src->m_rejected; m->m_mode = src->m_mode; assert((size_t)(p - *pp) == media_xtra(src)); *pp = p; return m; } #ifdef nomore static int media_xtra_ex(sdp_media_t const *m, sdp_connection_t const *c) { int rv = 0; for (; m; m = m->m_next) { rv += STRUCT_ALIGN(rv); rv += sizeof(*m); STR_XTRA(rv, m->m_type_name); STR_XTRA(rv, m->m_proto_name); LST_XTRA(rv, m->m_format, list_xtra); LST_XTRA(rv, m->m_rtpmaps, rtpmap_xtra); STR_XTRA(rv, m->m_information); if (c != m->m_connections) LST_XTRA(rv, m->m_connections, connection_xtra); LST_XTRA(rv, m->m_bandwidths, bandwidth_xtra); PTR_XTRA(rv, m->m_key, key_xtra); LST_XTRA(rv, m->m_attributes, attribute_xtra); } return rv; } static sdp_media_t *media_dup_ex(char **pp, sdp_media_t const *src, sdp_session_t *sdp, sdp_connection_t *dst_c, sdp_connection_t const *src_c) { char *p; sdp_media_t *retval = NULL, *m, **mm = &retval; int xtra = media_xtra_ex(src, src_c); p = *pp; for (; src; src = src->m_next) { p += STRUCT_ALIGN(p); STRUCT_DUP(p, m, src); m->m_next = NULL; STR_DUP(p, m, src, m_type_name); STR_DUP(p, m, src, m_proto_name); LST_DUP(p, m, src, m_format, list_dup); LST_DUP(p, m, src, m_rtpmaps, rtpmap_dup); STR_DUP(p, m, src, m_information); if (src_c != src->m_connections) LST_DUP(p, m, src, m_connections, connection_dup); else m->m_connections = dst_c; LST_DUP(p, m, src, m_bandwidths, bandwidth_dup); PTR_DUP(p, m, src, m_key, key_dup); LST_DUP(p, m, src, m_attributes, attribute_dup); /* note! we must not implicitly use 'src->m_session' as it might point to a temporary session */ m->m_session = sdp; m->m_rejected = src->m_rejected; m->m_mode = src->m_mode; assert(m); *mm = m; mm = &m->m_next; } assert(p - *pp == xtra); *pp = p; return retval; } #endif static size_t media_xtra_all(sdp_media_t const *m) { size_t rv = 0; for (; m; m = m->m_next) { rv += STRUCT_ALIGN(rv); rv += media_xtra(m); } return rv; } static sdp_media_t *media_dup_all(char **pp, sdp_media_t const *src, sdp_session_t *sdp) { char *p; sdp_media_t *retval = NULL, *m, **mm = &retval; p = *pp; for (; src; src = src->m_next) { p += STRUCT_ALIGN(p); m = media_dup(&p, src, sdp); assert(m); *mm = m; mm = &m->m_next; } *pp = p; return retval; } static size_t list_xtra(sdp_list_t const *l) { size_t rv = sizeof(*l); rv += strlen(l->l_text) + 1; return rv; } static sdp_list_t *list_dup(char **pp, sdp_list_t const *src) { char *p; sdp_list_t *l; p = *pp; STRUCT_DUP(p, l, src); l->l_next = NULL; STR_DUP(p, l, src, l_text); assert((size_t)(p - *pp) == list_xtra(src)); *pp = p; return l; } static size_t rtpmap_xtra(sdp_rtpmap_t const *rm) { size_t rv = sizeof(*rm); STR_XTRA(rv, rm->rm_encoding); STR_XTRA(rv, rm->rm_params); STR_XTRA(rv, rm->rm_fmtp); return rv; } static sdp_rtpmap_t *rtpmap_dup(char **pp, sdp_rtpmap_t const *src) { char *p; sdp_rtpmap_t *rm; p = *pp; STRUCT_DUP(p, rm, src); rm->rm_next = NULL; STR_DUP(p, rm, src, rm_encoding); STR_DUP(p, rm, src, rm_params); STR_DUP(p, rm, src, rm_fmtp); assert((size_t)(p - *pp) == rtpmap_xtra(src)); *pp = p; return rm; } /** Return total size of a list, including size of all nodes */ static size_t list_xtra_all(xtra_f *xtra, void const *v) { size_t rv = 0; sdp_list_t const *l; for (l = v; l; l = l->l_next) { rv += STRUCT_ALIGN(rv); rv += xtra(l); } return rv; } static void *list_dup_all(dup_f *dup, char **pp, void const *vsrc) { char *p; sdp_list_t const *src; sdp_list_t *retval = NULL, *l, **ll = &retval; p = *pp; for (src = vsrc; src; src = src->l_next) { p += STRUCT_ALIGN(p); l = dup(&p, src); assert(l); *ll = l; ll = &l->l_next; } *pp = p; return retval; } #if 0 static size_t XXX_xtra(sdp_XXX_t const *YYY) { size_t rv = sizeof(*YYY); rv += strlen(YYY->YYY_encoding) + 1; if (YYY->YYY_params); rv += strlen(YYY->YYY_params) + 1; return rv; } static sdp_XXX_t *XXX_dup(char **pp, sdp_XXX_t const *src) { char *p; sdp_XXX_t *YYY; p = *pp; ASSERT_STRUCT_ALIGN(p); YYY = memcpy(p, src, src->YYY_size); p += src->YYY_size; YYY->YYY_next = NULL; ZZZ *pp = p; return YYY; } #endif static size_t session_xtra(sdp_session_t const *sdp) { size_t rv = sizeof(*sdp); PTR_XTRA(rv, sdp->sdp_origin, origin_xtra); STR_XTRA(rv, sdp->sdp_subject); STR_XTRA(rv, sdp->sdp_information); STR_XTRA(rv, sdp->sdp_uri); LST_XTRA(rv, sdp->sdp_emails, list_xtra); LST_XTRA(rv, sdp->sdp_phones, list_xtra); LST_XTRA(rv, sdp->sdp_connection, connection_xtra); LST_XTRA(rv, sdp->sdp_bandwidths, bandwidth_xtra); LST_XTRA(rv, sdp->sdp_time, time_xtra); PTR_XTRA(rv, sdp->sdp_key, key_xtra); LST_XTRA(rv, sdp->sdp_attributes, attribute_xtra); STR_XTRA(rv, sdp->sdp_charset); MED_XTRA_ALL(rv, sdp->sdp_media); return rv; } static sdp_session_t *session_dup(char **pp, sdp_session_t const *src) { char *p; sdp_session_t *sdp; p = *pp; STRUCT_DUP(p, sdp, src); sdp->sdp_next = NULL; PTR_DUP(p, sdp, src, sdp_origin, origin_dup); STR_DUP(p, sdp, src, sdp_subject); STR_DUP(p, sdp, src, sdp_information); STR_DUP(p, sdp, src, sdp_uri); LST_DUP(p, sdp, src, sdp_emails, list_dup); LST_DUP(p, sdp, src, sdp_phones, list_dup); LST_DUP(p, sdp, src, sdp_connection, connection_dup); LST_DUP(p, sdp, src, sdp_bandwidths, bandwidth_dup); LST_DUP(p, sdp, src, sdp_time, time_dup); PTR_DUP(p, sdp, src, sdp_key, key_dup); LST_DUP(p, sdp, src, sdp_attributes, attribute_dup); STR_DUP(p, sdp, src, sdp_charset); MED_DUP_ALL(p, sdp, src, sdp_media); assert((size_t)(p - *pp) == session_xtra(src)); *pp = p; return sdp; } /**Duplicate an SDP session description. * * The function sdp_session_dup() duplicates (deeply copies) an SDP * session description @a sdp allocating memory using memory @a home. * * @param h Memory home * @param sdp SDP session description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_session_t structure is * returned, otherwise NULL is returned. */ sdp_session_t *sdp_session_dup(su_home_t *h, sdp_session_t const *sdp) { SDP_DUP(session, sdp); } /* ---------------------------------------------------------------------- */ static size_t session_without_media_xtra(sdp_session_t const *sdp) { size_t rv = sizeof(*sdp); PTR_XTRA(rv, sdp->sdp_origin, origin_xtra); STR_XTRA(rv, sdp->sdp_subject); STR_XTRA(rv, sdp->sdp_information); STR_XTRA(rv, sdp->sdp_uri); LST_XTRA(rv, sdp->sdp_emails, list_xtra); LST_XTRA(rv, sdp->sdp_phones, list_xtra); LST_XTRA(rv, sdp->sdp_connection, connection_xtra); LST_XTRA(rv, sdp->sdp_bandwidths, bandwidth_xtra); LST_XTRA(rv, sdp->sdp_time, time_xtra); PTR_XTRA(rv, sdp->sdp_key, key_xtra); LST_XTRA(rv, sdp->sdp_attributes, attribute_xtra); STR_XTRA(rv, sdp->sdp_charset); return rv; } static sdp_session_t *session_without_media_dup(char **pp, sdp_session_t const *src) { char *p; sdp_session_t *sdp; p = *pp; STRUCT_DUP(p, sdp, src); sdp->sdp_next = NULL; PTR_DUP(p, sdp, src, sdp_origin, origin_dup); STR_DUP(p, sdp, src, sdp_subject); STR_DUP(p, sdp, src, sdp_information); STR_DUP(p, sdp, src, sdp_uri); LST_DUP(p, sdp, src, sdp_emails, list_dup); LST_DUP(p, sdp, src, sdp_phones, list_dup); LST_DUP(p, sdp, src, sdp_connection, connection_dup); LST_DUP(p, sdp, src, sdp_bandwidths, bandwidth_dup); LST_DUP(p, sdp, src, sdp_time, time_dup); PTR_DUP(p, sdp, src, sdp_key, key_dup); LST_DUP(p, sdp, src, sdp_attributes, attribute_dup); STR_DUP(p, sdp, src, sdp_charset); sdp->sdp_media = NULL; assert((size_t)(p - *pp) == session_without_media_xtra(src)); *pp = p; return sdp; } /* SDP_DUP macro requires this */ typedef sdp_session_t sdp_session_without_media_t; /**Duplicate an SDP session description without media descriptions. * * The function sdp_session_dup() duplicates (deeply copies) an SDP session * description @a sdp allocating memory using memory @a home. It does not * copy the media descriptions, however. * * @param h memory h * @param sdp SDP session description to be duplicated * * @note The duplicated structure is allocated using a single call to * su_alloc() and it can be freed with su_free(). * * @return * If successful, a pointer to newly allocated sdp_session_t structure is * returned, otherwise NULL is returned. */ sdp_session_t *sdp_session_dup_without_media(su_home_t *h, sdp_session_t const *sdp) { SDP_DUP(session_without_media, sdp); } /* ---------------------------------------------------------------------- */ /* SDP Tag classes */ #include size_t sdptag_session_xtra(tagi_t const *t, size_t offset) { sdp_session_t const *sdp = (sdp_session_t *)t->t_value; if (sdp) return STRUCT_ALIGN(offset) + session_xtra(sdp); else return 0; } tagi_t *sdptag_session_dup(tagi_t *dst, tagi_t const *src, void **bb) { sdp_session_t *sdp; sdp_session_t const *srcsdp; char *b; assert(src); assert(*bb); b = *bb; b += STRUCT_ALIGN(b); srcsdp = (sdp_session_t *)src->t_value; sdp = srcsdp ? session_dup(&b, srcsdp) : NULL; dst->t_tag = src->t_tag; dst->t_value = (tag_value_t)sdp; *bb = b; return dst + 1; } int sdptag_session_snprintf(tagi_t const *t, char b[], size_t size) { sdp_session_t const *sdp; sdp_printer_t *print; size_t retval; assert(t); if (!t || !t->t_value) { if (size && b) b[0] = 0; return 0; } sdp = (sdp_session_t const *)t->t_value; print = sdp_print(NULL, sdp, b, size, 0); retval = sdp_message_size(print); sdp_printer_free(print); return (int)retval; } /** Tag class for SDP tags. @HIDE */ tag_class_t sdptag_session_class[1] = {{ sizeof(sdptag_session_class), /* tc_next */ NULL, /* tc_len */ NULL, /* tc_move */ NULL, /* tc_xtra */ sdptag_session_xtra, /* tc_dup */ sdptag_session_dup, /* tc_free */ NULL, /* tc_find */ NULL, /* tc_snprintf */ sdptag_session_snprintf, /* tc_filter */ NULL /* msgtag_str_filter */, /* tc_ref_set */ t_ptr_ref_set, }}; /* ---------------------------------------------------------------------- */ /* Compare two string pointers */ su_inline int str0cmp(char const *a, char const *b) { if (a == NULL) a = ""; if (b == NULL) b = ""; return strcmp(a, b); } /* Compare two string pointers ignoring case. */ su_inline int str0casecmp(char const *a, char const *b) { if (a == NULL) a = ""; if (b == NULL) b = ""; return strcasecmp(a, b); } /** Compare two session descriptions */ int sdp_session_cmp(sdp_session_t const *a, sdp_session_t const *b) { int rv; sdp_bandwidth_t const *ab, *bb; sdp_attribute_t const *aa, *ba; sdp_media_t const *am, *bm; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a == b) return 0; if ((rv = (a->sdp_version[0] - b->sdp_version[0]))) return rv; if ((rv = sdp_origin_cmp(a->sdp_origin, b->sdp_origin))) return rv; if ((rv = str0cmp(a->sdp_subject, b->sdp_subject))) return rv; if ((rv = str0cmp(a->sdp_information, b->sdp_information))) return rv; if ((rv = str0cmp(a->sdp_uri, b->sdp_uri))) return rv; if ((rv = sdp_list_cmp(a->sdp_emails, b->sdp_emails))) return rv; if ((rv = sdp_list_cmp(a->sdp_phones, b->sdp_phones))) return rv; if ((rv = sdp_connection_cmp(a->sdp_connection, b->sdp_connection))) return rv; for (ab = a->sdp_bandwidths, bb = b->sdp_bandwidths; ab || bb; ab = ab->b_next, bb = bb->b_next) if ((rv = sdp_bandwidth_cmp(a->sdp_bandwidths, b->sdp_bandwidths))) return rv; if ((rv = sdp_time_cmp(a->sdp_time, b->sdp_time))) return rv; if ((rv = sdp_key_cmp(a->sdp_key, b->sdp_key))) return rv; for (aa = a->sdp_attributes, ba = b->sdp_attributes; aa || bb; aa = aa->a_next, ba = ba->a_next) if ((rv = sdp_attribute_cmp(aa, ba))) return rv; for (am = a->sdp_media, bm = b->sdp_media; am || bm; am = am->m_next, bm = bm->m_next) if ((rv = sdp_media_cmp(am, bm))) return rv; return 0; } /** Compare two origin fields */ int sdp_origin_cmp(sdp_origin_t const *a, sdp_origin_t const *b) { int rv; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a == b) return 0; if (a->o_version != b->o_version) return a->o_version < b->o_version ? -1 : 1; if (a->o_id != b->o_id) return a->o_id < b->o_id ? -1 : 1; if ((rv = strcasecmp(a->o_username, b->o_username))) return rv; if ((rv = strcasecmp(a->o_address->c_address, b->o_address->c_address))) return rv; return 0; } /** Compare two connection fields */ int sdp_connection_cmp(sdp_connection_t const *a, sdp_connection_t const *b) { if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if (a->c_nettype != b->c_nettype) return a->c_nettype < b->c_nettype ? -1 : 1; if (a->c_addrtype != b->c_addrtype) return a->c_addrtype < b->c_addrtype ? -1 : 1; if (a->c_ttl != b->c_ttl) return a->c_ttl < b->c_ttl ? -1 : 1; if (a->c_groups != b->c_groups) return a->c_groups < b->c_groups ? -1 : 1; return strcmp(a->c_address, b->c_address); } /** Compare two bandwidth (b=) fields */ int sdp_bandwidth_cmp(sdp_bandwidth_t const *a, sdp_bandwidth_t const *b) { int rv; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if (a->b_modifier != b->b_modifier) return a->b_modifier < b->b_modifier ? -1 : 1; if (a->b_modifier == sdp_bw_x && (rv = strcmp(a->b_modifier_name, b->b_modifier_name))) return rv; if (a->b_value != b->b_value) return a->b_value < b->b_value ? -1 : 1; return 0; } /** Compare two time fields */ int sdp_time_cmp(sdp_time_t const *a, sdp_time_t const *b) { int rv; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a == b) return 0; if (a->t_start != b->t_start) return a->t_start < b->t_start ? -1 : 1; if (a->t_stop != b->t_stop) return a->t_stop < b->t_stop ? -1 : 1; if ((rv = sdp_zone_cmp(a->t_zone, b->t_zone))) return rv; if ((rv = sdp_repeat_cmp(a->t_repeat, b->t_repeat))) return rv; return 0; } /** Compare two repeat (r=) fields */ int sdp_repeat_cmp(sdp_repeat_t const *a, sdp_repeat_t const *b) { int i, n; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if (a->r_interval != b->r_interval) return a->r_interval < b->r_interval ? -1 : 1; if (a->r_duration != b->r_duration) return a->r_duration < b->r_duration ? -1 : 1; n = a->r_number_of_offsets < b->r_number_of_offsets ? a->r_number_of_offsets : b->r_number_of_offsets; for (i = 0; i < n; i++) if (a->r_offsets[i] != b->r_offsets[i]) return a->r_offsets[i] < b->r_offsets[i] ? -1 : 1; if (a->r_number_of_offsets != b->r_number_of_offsets) return a->r_number_of_offsets < b->r_number_of_offsets ? -1 : 1; return 0; } /** Compare two zone (z=) fields */ int sdp_zone_cmp(sdp_zone_t const *a, sdp_zone_t const *b) { int i, n; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; n = a->z_number_of_adjustments < b->z_number_of_adjustments ? a->z_number_of_adjustments : b->z_number_of_adjustments; for (i = 0; i < n; i++) { if (a->z_adjustments[i].z_at != b->z_adjustments[i].z_at) return a->z_adjustments[i].z_at < b->z_adjustments[i].z_at ? -1 : 1; if (a->z_adjustments[i].z_offset != b->z_adjustments[i].z_offset) return a->z_adjustments[i].z_offset < b->z_adjustments[i].z_offset ? -1 : 1; } if (a->z_number_of_adjustments != b->z_number_of_adjustments) return a->z_number_of_adjustments < b->z_number_of_adjustments ? -1 : 1; return 0; } /** Compare two key (k=) fields */ int sdp_key_cmp(sdp_key_t const *a, sdp_key_t const *b) { int rv; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if (a->k_method != b->k_method) return a->k_method < b->k_method ? -1 : 1; if (a->k_method == sdp_key_x && (rv = str0cmp(a->k_method_name, b->k_method_name))) return rv; return str0cmp(a->k_material, b->k_material); } /** Compare two attribute (a=) fields */ int sdp_attribute_cmp(sdp_attribute_t const *a, sdp_attribute_t const *b) { int rv; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if ((rv = str0cmp(a->a_name, b->a_name))) return rv; return str0cmp(a->a_value, b->a_value); } /** Compare two rtpmap structures. */ int sdp_rtpmap_cmp(sdp_rtpmap_t const *a, sdp_rtpmap_t const *b) { int rv; if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if (a->rm_pt != b->rm_pt) return a->rm_pt < b->rm_pt ? -1 : 1; /* Case insensitive encoding */ if ((rv = str0cmp(a->rm_encoding, b->rm_encoding))) return rv; /* Rate */ if (a->rm_rate != b->rm_rate) return a->rm_rate < b->rm_rate ? -1 : 1; { char const *a_param = "1", *b_param = "1"; if (a->rm_params) a_param = a->rm_params; if (b->rm_params) b_param = b->rm_params; rv = strcasecmp(a_param, b_param); if (rv) return rv; } return str0casecmp(a->rm_fmtp, b->rm_fmtp); } /** Compare two lists. */ int sdp_list_cmp(sdp_list_t const *a, sdp_list_t const *b) { int rv; for (;a || b; a = a->l_next, b = b->l_next) { if (a == b) return 0; if ((a != NULL) != (b != NULL)) return (a != NULL) < (b != NULL) ? -1 : 1; if ((rv = str0cmp(a->l_text, b->l_text))) return rv; } return 0; } /** Compare two media (m=) fields */ int sdp_media_cmp(sdp_media_t const *a, sdp_media_t const *b) { int rv; sdp_connection_t const *ac, *bc; sdp_bandwidth_t const *ab, *bb; sdp_rtpmap_t const *arm, *brm; sdp_attribute_t const *aa, *ba; if (a == b) return 0; if ((rv = (a != NULL) - (b != NULL))) return rv; if (a->m_type != b->m_type) return a->m_type < b->m_type ? -1 : 1; if (a->m_type == sdp_media_x) if ((rv = str0cmp(a->m_type_name, b->m_type_name))) return rv; if (a->m_port != b->m_port) return a->m_port < b->m_port ? -1 : 1; if (a->m_port == 0 /* && b->m_port == 0 */) /* Ignore transport protocol and media list if media has been rejected */ return 0; if (a->m_number_of_ports != b->m_number_of_ports) return a->m_number_of_ports < b->m_number_of_ports ? -1 : 1; if (a->m_proto != b->m_proto) return a->m_proto < b->m_proto ? -1 : 1; if (a->m_proto == sdp_media_x) if ((rv = str0cmp(a->m_proto_name, b->m_proto_name))) return rv; if (a->m_mode != b->m_mode) return a->m_mode < b->m_mode ? -1 : 1; for (arm = a->m_rtpmaps, brm = b->m_rtpmaps; arm || brm; arm = arm->rm_next, brm = brm->rm_next) if ((rv = sdp_rtpmap_cmp(arm, brm))) return rv; if ((rv = sdp_list_cmp(a->m_format, b->m_format))) return rv; if ((rv = str0cmp(a->m_information, b->m_information))) return rv; for (ac = a->m_connections, bc = b->m_connections; ac || bc; ac = ac->c_next, bc = bc->c_next) if ((rv = sdp_connection_cmp(ac, bc))) return rv; for (ab = a->m_bandwidths, bb = b->m_bandwidths; ab || bb; ab = ab->b_next, bb = bb->b_next) if ((rv = sdp_bandwidth_cmp(a->m_bandwidths, b->m_bandwidths))) return rv; if ((rv = sdp_key_cmp(a->m_key, b->m_key))) return rv; for (aa = a->m_attributes, ba = b->m_attributes; aa || bb; aa = aa->a_next, ba = ba->a_next) if ((rv = sdp_attribute_cmp(aa, ba))) return rv; return 0; } /* ---------------------------------------------------------------------- */ sdp_connection_t *sdp_media_connections(sdp_media_t const *m) { if (m) { if (m->m_connections) return m->m_connections; if (m->m_session) return m->m_session->sdp_connection; } return NULL; } /* ---------------------------------------------------------------------- */ /** Find named attribute from given list. */ sdp_attribute_t *sdp_attribute_find(sdp_attribute_t const *a, char const *name) { for (; a; a = a->a_next) { if (strcasecmp(a->a_name, name) == 0) break; } return (sdp_attribute_t *)a; } /** Find named attribute from given lists (a or a2). */ sdp_attribute_t *sdp_attribute_find2(sdp_attribute_t const *a, sdp_attribute_t const *a2, char const *name) { for (; a; a = a->a_next) { if (strcasecmp(a->a_name, name) == 0) break; } if (a == 0) for (a = a2; a; a = a->a_next) { if (strcasecmp(a->a_name, name) == 0) break; } return (sdp_attribute_t *)a; } /** Get session mode from attribute list. */ sdp_mode_t sdp_attribute_mode(sdp_attribute_t const *a, sdp_mode_t defmode) { for (; a; a = a->a_next) { if (strcasecmp(a->a_name, "sendrecv") == 0) return sdp_sendrecv; if (strcasecmp(a->a_name, "inactive") == 0) return sdp_inactive; if (strcasecmp(a->a_name, "recvonly") == 0) return sdp_recvonly; if (strcasecmp(a->a_name, "sendonly") == 0) return sdp_sendonly; } return defmode; } /** Convert session mode as #sdp_attribute_t structure. */ sdp_attribute_t *sdp_attribute_by_mode(su_home_t *home, sdp_mode_t mode) { sdp_attribute_t *a; char const *name; if (mode == sdp_inactive) name = "inactive"; else if (mode == sdp_sendonly) name = "sendonly"; else if (mode == sdp_recvonly) name = "recvonly"; else if (mode == sdp_sendrecv) name = "sendrecv"; else return NULL; a = su_salloc(home, sizeof(*a)); if (a) a->a_name = name; return a; } /** Find a mapped attribute. * * A mapped attribute has form 'a=: ' where pt is a RTP * payload type, integer in range 0..127. For example, "a=atmmap" [@RFC3108] * is a mapped attribute. Note that common mapped attributes, "a=rtpmap" and * "a=fmtp" are already parsed as list of #sdp_rtpmap_t in #sdp_media_t. * * @param a pointer to first attribute in the list * @param name name of the attribute * @param pt payload type number (must be 0..127) * @param return_result return value parameter for mapped attribute value * * @return Pointer to a matching attribute structure, or NULL. * * If a matching attribute is found, @a return_result will point to part of * the attribute after the payload type and whitespace. */ sdp_attribute_t *sdp_attribute_mapped_find(sdp_attribute_t const *a, char const *name, int pt, char **return_result) { char pt_value[4]; size_t pt_len; if (return_result) *return_result = NULL; if (0 > pt || pt > 127) return NULL; snprintf(pt_value, sizeof(pt_value), "%u", (unsigned)pt); pt_len = strlen(pt_value); for (; (a = sdp_attribute_find(a, name)); a = a->a_next) { char const *value = a->a_value; size_t wlen; if (strncmp(value, pt_value, pt_len)) continue; wlen = strspn(value + pt_len, " \t"); if (wlen == 0 || value[pt_len + wlen] == '\0') continue; if (return_result) *return_result = (char *)value + pt_len + wlen; return (sdp_attribute_t *)a; } return NULL; } /** Append a (list of) attribute(s) to a list of attributes. */ void sdp_attribute_append(sdp_attribute_t **list, sdp_attribute_t const *a) { assert(list); if (list == NULL || a == NULL) return; for (;*list; list = &(*list)->a_next) ; *list = (sdp_attribute_t *)a; } /**Replace or append a attribute within a list of attributes. * * @retval 1 if replaced existing attribute * @retval 0 if attribute was appended * @retval -1 upon an error */ int sdp_attribute_replace(sdp_attribute_t **list, sdp_attribute_t *a, sdp_attribute_t **return_replaced) { sdp_attribute_t *replaced; assert(list); if (return_replaced) *return_replaced = NULL; if (list == NULL || a == NULL) return -1; assert(a->a_name != NULL); assert(a->a_next == NULL); for (; *list; list = &(*list)->a_next) { if (strcasecmp((*list)->a_name, a->a_name) == 0) break; } replaced = *list, *list = a; if (replaced) { a->a_next = replaced->a_next; replaced->a_next = NULL; if (return_replaced) *return_replaced = replaced; return 1; } return 0; } /** Remove a named attribute from a list of attributes. */ sdp_attribute_t *sdp_attribute_remove(sdp_attribute_t **list, char const *name) { sdp_attribute_t *a; assert(list); if (list == NULL) return NULL; if (name == NULL) return NULL; for (a = *list; a; list = &a->a_next, a = *list) { if (strcasecmp(name, a->a_name) == 0) break; } if (a) { *list = a->a_next; a->a_next = NULL; } return a; } /* Return 1 if m= line struct matches with given type and name */ unsigned sdp_media_match(sdp_media_t const *m, sdp_media_e type, sdp_text_t *type_name, sdp_proto_e proto, sdp_text_t *proto_name) { if (m == NULL) return 0; if (type == sdp_media_any || m->m_type == sdp_media_any) return 1; if (type_name == NULL) type_name = ""; if (type != m->m_type || (type == sdp_media_x && strcasecmp(m->m_type_name, type_name))) return 0; if (proto == sdp_proto_any || m->m_proto == sdp_proto_any) return 1; if (proto_name == NULL) proto_name = ""; if (proto != m->m_proto || (proto == sdp_proto_x && strcasecmp(m->m_proto_name, proto_name))) return 0; return 1; } /* Return 1 if media type and protocol of m= line structs matches */ unsigned sdp_media_match_with(sdp_media_t const *a, sdp_media_t const *b) { if (a == NULL || b == NULL) return a == b; if (a->m_type == sdp_media_any || b->m_type == sdp_media_any) return 1; if (a->m_type != b->m_type || (a->m_type == sdp_media_x && strcasecmp(b->m_type_name, a->m_type_name))) return 0; if (a->m_proto == sdp_proto_any || b->m_proto == sdp_proto_any) return 1; if (a->m_proto != b->m_proto || (a->m_proto == sdp_proto_x && strcasecmp(b->m_proto_name, a->m_proto_name))) return 0; return 1; } /** Count matching media lines in SDP. */ unsigned sdp_media_count(sdp_session_t const *sdp, sdp_media_e type, sdp_text_t *type_name, sdp_proto_e proto, sdp_text_t *proto_name) { unsigned count = 0; sdp_media_t const *m; if (sdp != NULL) for (m = sdp->sdp_media; m; m = m->m_next) count += sdp_media_match(m, type, type_name, proto, proto_name); return count; } /** Count matching media lines in SDP. */ unsigned sdp_media_count_with(sdp_session_t const *sdp, sdp_media_t const *m0) { unsigned count = 0; sdp_media_t const *m; if (sdp != NULL) for (m = sdp->sdp_media; m; m = m->m_next) count += sdp_media_match_with(m, m0); return count; } /** Return true if media uses RTP */ int sdp_media_uses_rtp(sdp_media_t const *m) { return m && (m->m_proto == sdp_proto_rtp || m->m_proto == sdp_proto_srtp || (m->m_proto == sdp_proto_x && m->m_proto_name && strncasecmp(m->m_proto_name, "RTP/", 4) == 0)); } /** Check if payload type, rtp rate and parameters match in rtpmaps*/ int sdp_rtpmap_match(sdp_rtpmap_t const *a, sdp_rtpmap_t const *b) { char const *aparam, *bparam; if (a == b) return 1; if (a == 0 || b == 0) return 0; if (a->rm_rate != b->rm_rate) return 0; if (strcasecmp(a->rm_encoding, b->rm_encoding)) return 0; aparam = a->rm_params; bparam = b->rm_params; if (aparam == bparam) return 1; if (!aparam) aparam = "1"; if (!bparam) bparam = "1"; if (strcasecmp(aparam, bparam)) return 0; return 1; } /** Search for matching rtpmap from list. * * @note * The a=fmtp: for the codecs are not compared. */ sdp_rtpmap_t *sdp_rtpmap_find_matching(sdp_rtpmap_t const *list, sdp_rtpmap_t const *rm) { char const *lparam, *rparam; if (rm == NULL) return NULL; for (; list; list = list->rm_next) { if (rm->rm_rate != list->rm_rate) continue; if (strcasecmp(rm->rm_encoding, list->rm_encoding) != 0) continue; lparam = rm->rm_params; rparam = list->rm_params; if (lparam == rparam) break; if (!lparam) lparam = "1"; if (!rparam) rparam = "1"; if (strcasecmp(lparam, rparam)) continue; break; } return (sdp_rtpmap_t *)list; }