/* belle-sip - SIP (RFC3261) library. Copyright (C) 2010-2018 Belledonne Communications SARL 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, see . */ #include "belle-sip/belle-sip.h" #include "grammars/belle_sdpParser.h" #include "grammars/belle_sdpLexer.h" #include "belle_sip_internal.h" struct _belle_sdp_mime_parameter { belle_sip_object_t base; int rate; int channel_count; int ptime; int max_ptime; int media_format; const char* type; const char* parameters; }; static void belle_sip_object_freefunc(void* obj) { belle_sip_object_unref(BELLE_SIP_OBJECT(obj)); } static void* belle_sip_object_copyfunc(void* obj) { return belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(obj)); } static void * belle_sip_string_copyfunc(void *obj) { return (void *)belle_sip_strdup((const char *)obj); } /*************************************************************************************** * Attribute * **************************************************************************************/ typedef belle_sdp_attribute_t* (*attribute_parse_func)(const char*) ; struct attribute_name_func_pair { const char* name; attribute_parse_func func; }; static struct attribute_name_func_pair attribute_table[] = { { "rtcp-fb", (attribute_parse_func)belle_sdp_rtcp_fb_attribute_parse }, { "rtcp-xr", (attribute_parse_func)belle_sdp_rtcp_xr_attribute_parse } }; struct _belle_sdp_attribute { belle_sip_object_t base; const char* name; char *unparsed_value; }; void belle_sdp_attribute_destroy(belle_sdp_attribute_t* attribute) { DESTROY_STRING(attribute,name) DESTROY_STRING(attribute,unparsed_value) } void belle_sdp_attribute_clone(belle_sdp_attribute_t *attribute, const belle_sdp_attribute_t *orig){ CLONE_STRING(belle_sdp_attribute,name,attribute,orig) } belle_sip_error_code belle_sdp_attribute_marshal(belle_sdp_attribute_t* attribute, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff, buff_size, offset, "a=%s", attribute->name); } belle_sdp_attribute_t* belle_sdp_attribute_create(const char* name, const char* value) { belle_sdp_attribute_t* ret; size_t i; size_t elements = sizeof(attribute_table) / sizeof(attribute_table[0]); if (!name || name[0] == '\0') { belle_sip_error("Cannot create SDP attribute without name"); return NULL; } for (i = 0; i < elements; i++) { if (strcasecmp(attribute_table[i].name, name) == 0) { char* raw; if (value) raw = belle_sip_strdup_printf("a=%s:%s", name, value); else raw = belle_sip_strdup_printf("a=%s", name); ret = attribute_table[i].func(raw); belle_sip_free(raw); return ret; } } /* Not a specialized SDP attribute */ return BELLE_SDP_ATTRIBUTE(belle_sdp_raw_attribute_create(name, value)); } const char *belle_sdp_attribute_get_value(belle_sdp_attribute_t *attribute) { char *ret; char *end; if (attribute->unparsed_value) { belle_sip_free(attribute->unparsed_value); attribute->unparsed_value = NULL; } attribute->unparsed_value = belle_sip_object_to_string(attribute); ret = attribute->unparsed_value; ret += strlen(attribute->name) + 2; /* "a=" + name*/ if (*ret==':') ret++; for (; *ret == ' '; ret++) {}; /* skip eventual spaces */ return ret; } unsigned int belle_sdp_attribute_has_value(belle_sdp_attribute_t* attribute) { return belle_sdp_attribute_get_value(attribute) != NULL; } BELLE_SDP_NEW(attribute,belle_sip_object) BELLE_SDP_PARSE(attribute) GET_SET_STRING(belle_sdp_attribute,name); /*************************************************************************************** * RAW Attribute * **************************************************************************************/ struct _belle_sdp_raw_attribute { belle_sdp_attribute_t base; const char* value; }; void belle_sdp_raw_attribute_destroy(belle_sdp_raw_attribute_t* attribute) { DESTROY_STRING(attribute,value) } void belle_sdp_raw_attribute_clone(belle_sdp_raw_attribute_t* attribute, const belle_sdp_raw_attribute_t* orig) { if (belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig))) { belle_sdp_raw_attribute_set_value(attribute, belle_sdp_attribute_get_value(BELLE_SDP_ATTRIBUTE(orig))); } } belle_sip_error_code belle_sdp_raw_attribute_marshal(belle_sdp_raw_attribute_t* attribute, char* buff, size_t buff_size, size_t* offset) { belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; if (attribute->value) { error = belle_sip_snprintf(buff, buff_size, offset, ":%s", attribute->value); if (error != BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(raw_attribute,belle_sdp_attribute) belle_sdp_raw_attribute_t* belle_sdp_raw_attribute_create(const char* name, const char* value) { belle_sdp_raw_attribute_t* attribute = belle_sdp_raw_attribute_new(); belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), name); belle_sdp_raw_attribute_set_value(attribute, value); return attribute; } void belle_sdp_raw_attribute_set_value(belle_sdp_raw_attribute_t* attribute, const char* value) { if (attribute->value != NULL) belle_sip_free((void*)attribute->value); if (value) { attribute->value = belle_sip_strdup(value); } else attribute->value = NULL; } /*************************************************************************************** * RTCP-FB Attribute * **************************************************************************************/ struct _belle_sdp_rtcp_fb_attribute { belle_sdp_attribute_t base; belle_sdp_rtcp_fb_val_type_t type; belle_sdp_rtcp_fb_val_param_t param; uint32_t smaxpr; uint16_t trr_int; int8_t id; }; BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_pli(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_pli(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_sli(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_sli(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_rpsi(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_rpsi(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); BELLESIP_EXPORT unsigned int belle_sdp_rtcp_fb_attribute_has_app(const belle_sdp_rtcp_fb_attribute_t* attribute); BELLESIP_EXPORT void belle_sdp_rtcp_fb_attribute_set_app(belle_sdp_rtcp_fb_attribute_t* attribute, unsigned int enable); void belle_sdp_rtcp_fb_attribute_destroy(belle_sdp_rtcp_fb_attribute_t* attribute) { } void belle_sdp_rtcp_fb_attribute_clone(belle_sdp_rtcp_fb_attribute_t* attribute, const belle_sdp_rtcp_fb_attribute_t *orig) { attribute->type = orig->type; attribute->param = orig->param; attribute->trr_int = orig->trr_int; attribute->id = orig->id; attribute->smaxpr = orig->smaxpr; } belle_sip_error_code belle_sdp_rtcp_fb_attribute_marshal(belle_sdp_rtcp_fb_attribute_t* attribute, char * buff, size_t buff_size, size_t *offset) { int8_t id = belle_sdp_rtcp_fb_attribute_get_id(attribute); belle_sdp_rtcp_fb_val_type_t type = belle_sdp_rtcp_fb_attribute_get_type(attribute); belle_sdp_rtcp_fb_val_param_t param = belle_sdp_rtcp_fb_attribute_get_param(attribute); belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; if (id < 0) { error = belle_sip_snprintf(buff, buff_size, offset, ":* "); } else { error = belle_sip_snprintf(buff, buff_size, offset, ":%u ", id); } if (error != BELLE_SIP_OK) return error; switch (type) { case BELLE_SDP_RTCP_FB_ACK: error = belle_sip_snprintf(buff, buff_size, offset, "ack"); if (error != BELLE_SIP_OK) return error; switch (param) { default: case BELLE_SDP_RTCP_FB_NONE: break; case BELLE_SDP_RTCP_FB_RPSI: error = belle_sip_snprintf(buff, buff_size, offset, " rpsi"); break; case BELLE_SDP_RTCP_FB_APP: error = belle_sip_snprintf(buff, buff_size, offset, " app"); break; } break; case BELLE_SDP_RTCP_FB_NACK: error = belle_sip_snprintf(buff, buff_size, offset, "nack"); if (error != BELLE_SIP_OK) return error; switch (param) { default: case BELLE_SDP_RTCP_FB_NONE: break; case BELLE_SDP_RTCP_FB_PLI: error = belle_sip_snprintf(buff, buff_size, offset, " pli"); break; case BELLE_SDP_RTCP_FB_SLI: error = belle_sip_snprintf(buff, buff_size, offset, " sli"); break; case BELLE_SDP_RTCP_FB_RPSI: error = belle_sip_snprintf(buff, buff_size, offset, " rpsi"); break; case BELLE_SDP_RTCP_FB_APP: error = belle_sip_snprintf(buff, buff_size, offset, " app"); break; } break; case BELLE_SDP_RTCP_FB_TRR_INT: error = belle_sip_snprintf(buff, buff_size, offset, "trr-int %u", belle_sdp_rtcp_fb_attribute_get_trr_int(attribute)); break; case BELLE_SDP_RTCP_FB_CCM: error = belle_sip_snprintf(buff, buff_size, offset, "ccm"); if (error != BELLE_SIP_OK) return error; switch (param) { case BELLE_SDP_RTCP_FB_FIR: error = belle_sip_snprintf(buff, buff_size, offset, " fir"); break; case BELLE_SDP_RTCP_FB_TMMBR: error = belle_sip_snprintf(buff, buff_size, offset, " tmmbr"); if (belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute) > 0) { error = belle_sip_snprintf(buff, buff_size, offset, " smaxpr=%u", belle_sdp_rtcp_fb_attribute_get_smaxpr(attribute)); } break; default: break; } break; } return error; } static void belle_sdp_rtcp_fb_attribute_init(belle_sdp_rtcp_fb_attribute_t* attribute) { belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-fb"); attribute->id = -1; attribute->type = BELLE_SDP_RTCP_FB_TRR_INT; attribute->param = BELLE_SDP_RTCP_FB_NONE; attribute->trr_int = 0; attribute->smaxpr = 0; } BELLE_SDP_NEW_WITH_CTR(rtcp_fb_attribute,belle_sdp_attribute) BELLE_SDP_PARSE(rtcp_fb_attribute) GET_SET_INT(belle_sdp_rtcp_fb_attribute,id,int8_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,type,belle_sdp_rtcp_fb_val_type_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,param,belle_sdp_rtcp_fb_val_param_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,trr_int,uint16_t) GET_SET_INT(belle_sdp_rtcp_fb_attribute,smaxpr,uint32_t) /*************************************************************************************** * RTCP-XR Attribute * **************************************************************************************/ struct _belle_sdp_rtcp_xr_attribute { belle_sdp_attribute_t base; const char* rcvr_rtt_mode; int rcvr_rtt_max_size; unsigned int stat_summary; belle_sip_list_t* stat_summary_flags; unsigned int voip_metrics; }; const belle_sip_list_t* belle_sdp_rtcp_xr_attribute_get_stat_summary_flags(const belle_sdp_rtcp_xr_attribute_t* attribute) { return attribute->stat_summary_flags; } void belle_sdp_rtcp_xr_attribute_add_stat_summary_flag(belle_sdp_rtcp_xr_attribute_t* attribute, const char* flag) { attribute->stat_summary_flags = belle_sip_list_append(attribute->stat_summary_flags, belle_sip_strdup(flag)); } void belle_sdp_rtcp_xr_attribute_destroy(belle_sdp_rtcp_xr_attribute_t* attribute) { DESTROY_STRING(attribute,rcvr_rtt_mode) belle_sip_list_free_with_data(attribute->stat_summary_flags, belle_sip_free); } void belle_sdp_rtcp_xr_attribute_clone(belle_sdp_rtcp_xr_attribute_t* attribute, const belle_sdp_rtcp_xr_attribute_t *orig) { CLONE_STRING(belle_sdp_rtcp_xr_attribute,rcvr_rtt_mode,attribute,orig) attribute->rcvr_rtt_max_size = orig->rcvr_rtt_max_size; attribute->stat_summary = orig->stat_summary; attribute->stat_summary_flags = belle_sip_list_copy_with_data(orig->stat_summary_flags, belle_sip_string_copyfunc); attribute->voip_metrics = orig->voip_metrics; } belle_sip_error_code belle_sdp_rtcp_xr_attribute_marshal(belle_sdp_rtcp_xr_attribute_t* attribute, char * buff, size_t buff_size, size_t *offset) { const char *rcvr_rtt_mode = NULL; int rcvr_rtt_max_size = -1; int nb_xr_formats = 0; belle_sip_error_code error = belle_sdp_attribute_marshal(BELLE_SDP_ATTRIBUTE(attribute), buff, buff_size, offset); if (error != BELLE_SIP_OK) return error; rcvr_rtt_mode = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_mode(attribute); if (rcvr_rtt_mode != NULL) { error = belle_sip_snprintf(buff, buff_size, offset, "%srcvr-rtt=%s", nb_xr_formats++ == 0 ? ":" : " ", rcvr_rtt_mode); if (error != BELLE_SIP_OK) return error; rcvr_rtt_max_size = belle_sdp_rtcp_xr_attribute_get_rcvr_rtt_max_size(attribute); if (rcvr_rtt_max_size > 0) { error = belle_sip_snprintf(buff, buff_size, offset, ":%u", rcvr_rtt_max_size); if (error != BELLE_SIP_OK) return error; } } if (belle_sdp_rtcp_xr_attribute_has_stat_summary(attribute)) { belle_sip_list_t* list; int nb_stat_flags = 0; error = belle_sip_snprintf(buff, buff_size, offset, "%sstat-summary", nb_xr_formats++ == 0 ? ":" : " "); if (error != BELLE_SIP_OK) return error; for (list = attribute->stat_summary_flags; list != NULL; list = list->next) { error = belle_sip_snprintf(buff, buff_size, offset, "%s%s", nb_stat_flags++ == 0 ? "=" : ",", (const char*)list->data); if (error != BELLE_SIP_OK) return error; } } if (belle_sdp_rtcp_xr_attribute_has_voip_metrics(attribute)) { error = belle_sip_snprintf(buff, buff_size, offset, "%svoip-metrics", nb_xr_formats++ == 0 ? ":" : " "); if (error != BELLE_SIP_OK) return error; } return error; } static void belle_sdp_rtcp_xr_attribute_init(belle_sdp_rtcp_xr_attribute_t* attribute) { belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute), "rtcp-xr"); } BELLE_SDP_NEW_WITH_CTR(rtcp_xr_attribute,belle_sdp_attribute) BELLE_SDP_PARSE(rtcp_xr_attribute) GET_SET_STRING(belle_sdp_rtcp_xr_attribute,rcvr_rtt_mode) GET_SET_INT(belle_sdp_rtcp_xr_attribute,rcvr_rtt_max_size,int) GET_SET_BOOL(belle_sdp_rtcp_xr_attribute,stat_summary,has) GET_SET_BOOL(belle_sdp_rtcp_xr_attribute,voip_metrics,has) /*************************************************************************************** * Bandwidth * **************************************************************************************/ struct _belle_sdp_bandwidth { belle_sip_object_t base; const char* type; int value; }; void belle_sdp_bandwidth_destroy(belle_sdp_bandwidth_t* bandwidth) { if (bandwidth->type) belle_sip_free((void*)bandwidth->type); } void belle_sdp_bandwidth_clone(belle_sdp_bandwidth_t *bandwidth, const belle_sdp_bandwidth_t *orig){ CLONE_STRING(belle_sdp_bandwidth,type,bandwidth,orig) bandwidth->value=orig->value; } belle_sip_error_code belle_sdp_bandwidth_marshal(belle_sdp_bandwidth_t* bandwidth, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"b=%s:%i",bandwidth->type,bandwidth->value); } BELLE_SDP_NEW(bandwidth,belle_sip_object) BELLE_SDP_PARSE(bandwidth) GET_SET_STRING(belle_sdp_bandwidth,type); GET_SET_INT(belle_sdp_bandwidth,value,int) /************************ * connection ***********************/ struct _belle_sdp_connection { belle_sip_object_t base; const char* network_type; const char* address_type; const char* address; int ttl; int range; }; void belle_sdp_connection_destroy(belle_sdp_connection_t* connection) { DESTROY_STRING(connection,network_type) DESTROY_STRING(connection,address_type) DESTROY_STRING(connection,address) } void belle_sdp_connection_clone(belle_sdp_connection_t *connection, const belle_sdp_connection_t *orig){ CLONE_STRING(belle_sdp_connection,network_type,connection,orig) CLONE_STRING(belle_sdp_connection,address_type,connection,orig) CLONE_STRING(belle_sdp_connection,address,connection,orig) connection->range=orig->range; connection->ttl=orig->ttl; } belle_sip_error_code belle_sdp_connection_marshal(belle_sdp_connection_t* connection, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error = belle_sip_snprintf(buff,buff_size,offset,"c=%s %s %s",connection->network_type,connection->address_type,connection->address); if (error!=BELLE_SIP_OK) return error; if (connection->ttl>0) error = belle_sip_snprintf(buff,buff_size,offset,"/%i",connection->ttl); if (error!=BELLE_SIP_OK) return error; if (connection->range>0) error = belle_sip_snprintf(buff,buff_size,offset,"/%i",connection->range); return error; } BELLE_SDP_NEW(connection,belle_sip_object) BELLE_SDP_PARSE(connection) belle_sdp_connection_t* belle_sdp_connection_create(const char* net_type, const char* addr_type, const char* addr) { belle_sdp_connection_t* connection = belle_sdp_connection_new(); belle_sdp_connection_set_network_type(connection,net_type); belle_sdp_connection_set_address_type(connection,addr_type); belle_sdp_connection_set_address(connection,addr); return connection; } GET_SET_STRING(belle_sdp_connection,network_type); GET_SET_STRING(belle_sdp_connection,address_type); GET_SET_STRING(belle_sdp_connection,address); GET_SET_INT(belle_sdp_connection,ttl,int); GET_SET_INT(belle_sdp_connection,range,int); /************************ * email ***********************/ struct _belle_sdp_email { belle_sip_object_t base; char* value; }; void belle_sdp_email_destroy(belle_sdp_email_t* email) { DESTROY_STRING(email,value) } void belle_sdp_email_clone(belle_sdp_email_t *email, const belle_sdp_email_t *orig){ CLONE_STRING(belle_sdp_email,value,email,orig) } belle_sip_error_code belle_sdp_email_marshal(belle_sdp_email_t* email, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"e=%s",email->value); } BELLE_SDP_NEW(email,belle_sip_object) BELLE_SDP_PARSE(email) GET_SET_STRING(belle_sdp_email,value); /************************ * info ***********************/ struct _belle_sdp_info { belle_sip_object_t base; const char* value; }; void belle_sdp_info_destroy(belle_sdp_info_t* info) { DESTROY_STRING(info,value) } void belle_sdp_info_clone(belle_sdp_info_t *info, const belle_sdp_info_t *orig){ CLONE_STRING(belle_sdp_info,value,info,orig) } belle_sip_error_code belle_sdp_info_marshal(belle_sdp_info_t* info, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"i=%s",info->value); } BELLE_SDP_NEW(info,belle_sip_object) BELLE_SDP_PARSE(info) GET_SET_STRING(belle_sdp_info,value); /************************ * media ***********************/ struct _belle_sdp_media { belle_sip_object_t base; const char* media_type; int media_port; belle_sip_list_t* media_formats; int port_count; const char* protocol; const char* raw_fmt; }; belle_sip_list_t* belle_sdp_media_get_media_formats(const belle_sdp_media_t* media) { return media->media_formats; } void belle_sdp_media_set_media_formats( belle_sdp_media_t* media, belle_sip_list_t* formats) { /*belle_sip_list_free(media->media_formats); to allow easy list management might be better to add an append format method*/ media->media_formats = formats; } void belle_sdp_media_destroy(belle_sdp_media_t* media) { DESTROY_STRING(media,media_type) belle_sip_list_free(media->media_formats); DESTROY_STRING(media,protocol) } static void belle_sdp_media_init(belle_sdp_media_t* media) { media->port_count=1; } void belle_sdp_media_clone(belle_sdp_media_t *media, const belle_sdp_media_t *orig){ CLONE_STRING(belle_sdp_media,media_type,media,orig) media->media_port=orig->media_port; media->media_formats = belle_sip_list_copy(orig->media_formats); media->port_count=orig->port_count; CLONE_STRING(belle_sdp_media,protocol,media,orig) } belle_sip_error_code belle_sdp_media_marshal(belle_sdp_media_t* media, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* list=media->media_formats; belle_sip_error_code error=belle_sip_snprintf(buff,buff_size,offset,"m=%s %i",media->media_type,media->media_port); if (error!=BELLE_SIP_OK) return error; if (media->port_count>1) { error=belle_sip_snprintf(buff,buff_size,offset,"/%i",media->port_count); if (error!=BELLE_SIP_OK) return error; } error=belle_sip_snprintf(buff,buff_size,offset," %s",media->protocol); if (error!=BELLE_SIP_OK) return error; for(;list!=NULL;list=list->next){ error=belle_sip_snprintf(buff,buff_size,offset," %li",(long)(intptr_t)list->data); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW_WITH_CTR(media,belle_sip_object) BELLE_SDP_PARSE(media) belle_sdp_media_t* belle_sdp_media_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats) { belle_sdp_media_t* media= belle_sdp_media_new(); belle_sdp_media_set_media_type(media,media_type); belle_sdp_media_set_media_port(media,media_port); belle_sdp_media_set_port_count(media,port_count); belle_sdp_media_set_protocol(media,protocol); if (static_media_formats) belle_sdp_media_set_media_formats(media,static_media_formats); return media; } GET_SET_STRING(belle_sdp_media,media_type); GET_SET_STRING(belle_sdp_media,protocol); GET_SET_INT(belle_sdp_media,media_port,int) GET_SET_INT(belle_sdp_media,port_count,int) /************************ * base_description ***********************/ typedef struct _belle_sdp_base_description { belle_sip_object_t base; belle_sdp_info_t* info; belle_sdp_connection_t* connection; belle_sip_list_t* bandwidths; belle_sip_list_t* attributes; } belle_sdp_base_description_t; static void belle_sdp_base_description_destroy(belle_sdp_base_description_t* base_description) { if (base_description->info) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->info)); if (base_description->connection) belle_sip_object_unref(BELLE_SIP_OBJECT(base_description->connection)); belle_sip_list_free_with_data(base_description->bandwidths,belle_sip_object_freefunc); belle_sip_list_free_with_data(base_description->attributes,belle_sip_object_freefunc); } static void belle_sdp_base_description_init(belle_sdp_base_description_t* base_description) { } static void belle_sdp_base_description_clone(belle_sdp_base_description_t *base_description, const belle_sdp_base_description_t *orig){ if (orig->info) base_description->info = BELLE_SDP_INFO(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->info))); if (orig->connection) base_description->connection = BELLE_SDP_CONNECTION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->connection))); base_description->bandwidths = belle_sip_list_copy_with_data(orig->bandwidths,belle_sip_object_copyfunc); base_description->attributes = belle_sip_list_copy_with_data(orig->attributes,belle_sip_object_copyfunc); } belle_sip_error_code belle_sdp_base_description_marshal(belle_sdp_base_description_t* base_description, char* buff, size_t buff_size, size_t *offset) { belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* bandwidths; // belle_sip_list_t* attributes; if (base_description->info) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->info),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (base_description->connection) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(base_description->connection),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(bandwidths=base_description->bandwidths;bandwidths!=NULL;bandwidths=bandwidths->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(bandwidths->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } // for(attributes=base_description->attributes;attributes!=NULL;attributes=attributes->next){ // error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); // error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); // } return error; } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_base_description_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_base_description_t ,belle_sip_object_t ,belle_sdp_base_description_destroy ,belle_sdp_base_description_clone ,belle_sdp_base_description_marshal ,FALSE); static int belle_sdp_base_description_attribute_comp_func(const belle_sdp_attribute_t* a, const char*b) { return strcmp(a->name,b); } belle_sdp_attribute_t* belle_sdp_base_description_get_attribute(const belle_sdp_base_description_t* base_description, const char* name) { belle_sip_list_t* attribute; attribute = belle_sip_list_find_custom(base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name); if (attribute) { return ((belle_sdp_attribute_t*)attribute->data); } else { return NULL; } } const char* belle_sdp_base_description_get_attribute_value(const belle_sdp_base_description_t* base_description, const char* name) { belle_sdp_attribute_t* attribute = belle_sdp_base_description_get_attribute(base_description,name); if (attribute) { return belle_sdp_attribute_get_value(attribute); } else return NULL; } belle_sip_list_t* belle_sdp_base_description_get_attributes(const belle_sdp_base_description_t* base_description) { return base_description->attributes; } static int belle_sdp_base_description_bandwidth_comp_func(const belle_sdp_bandwidth_t* a, const char*b) { return strcmp(a->type,b); } belle_sdp_bandwidth_t* belle_sdp_base_description_get_bandwidth(const belle_sdp_base_description_t *base_description, const char *name){ belle_sip_list_t* found = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if( found ){ return ((belle_sdp_bandwidth_t*)found->data); } else { return NULL; } } int belle_sdp_base_description_get_bandwidth_value(const belle_sdp_base_description_t* base_description, const char* name) { belle_sip_list_t* bandwidth; bandwidth = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if (bandwidth) { return ((belle_sdp_bandwidth_t*)bandwidth->data)->value; } else { return -1; } } void belle_sdp_base_description_remove_attribute(belle_sdp_base_description_t* base_description,const char* name) { belle_sip_list_t* attribute; attribute = belle_sip_list_find_custom(base_description->attributes, (belle_sip_compare_func)belle_sdp_base_description_attribute_comp_func, name); if (attribute) { belle_sip_object_unref(BELLE_SIP_OBJECT(attribute->data)); base_description->attributes = belle_sip_list_delete_link(base_description->attributes,attribute); } } void belle_sdp_base_description_remove_bandwidth(belle_sdp_base_description_t* base_description,const char* name) { belle_sip_list_t* bandwidth; bandwidth = belle_sip_list_find_custom(base_description->bandwidths, (belle_sip_compare_func)belle_sdp_base_description_bandwidth_comp_func, name); if (bandwidth) { belle_sip_object_unref(BELLE_SIP_OBJECT(bandwidth->data)); base_description->bandwidths = belle_sip_list_delete_link(base_description->bandwidths,bandwidth); } } void belle_sdp_base_description_set_attribute_value(belle_sdp_base_description_t* base_description, const char* name, const char* value) { belle_sdp_raw_attribute_t* attribute = belle_sdp_raw_attribute_new(); belle_sdp_attribute_set_name(BELLE_SDP_ATTRIBUTE(attribute),name); belle_sdp_raw_attribute_set_value(attribute,value); base_description->attributes = belle_sip_list_append(base_description->attributes,belle_sip_object_ref(attribute)); } void belle_sdp_base_description_add_attribute(belle_sdp_base_description_t* base_description, const belle_sdp_attribute_t* attribute) { base_description->attributes = belle_sip_list_append(base_description->attributes,(void*)belle_sip_object_ref(BELLE_SIP_OBJECT(attribute))); } #define SET_LIST(list_name,value) \ belle_sip_list_t* list;\ if (list_name) {\ belle_sip_list_free_with_data(list_name,belle_sip_object_unref);\ } \ for (list=value;list !=NULL; list=list->next) {\ belle_sip_object_ref(BELLE_SIP_OBJECT(list->data));\ }\ list_name=value; void belle_sdp_base_description_set_attributes(belle_sdp_base_description_t* base_description, belle_sip_list_t* attributes) { SET_LIST(base_description->attributes,attributes) } void belle_sdp_base_description_set_bandwidth(belle_sdp_base_description_t* base_description, const char* type, int value) { belle_sdp_bandwidth_t* bandwidth = BELLE_SDP_BANDWIDTH(belle_sdp_base_description_get_bandwidth(base_description, type)); if( bandwidth == NULL ){ bandwidth= belle_sdp_bandwidth_new(); belle_sdp_bandwidth_set_type(bandwidth,type); belle_sdp_bandwidth_set_value(bandwidth,value); base_description->bandwidths = belle_sip_list_append(base_description->bandwidths,belle_sip_object_ref(bandwidth)); } else { belle_sdp_bandwidth_set_value(bandwidth,value); } } void belle_sdp_base_description_add_bandwidth(belle_sdp_base_description_t* base_description, const belle_sdp_bandwidth_t* bandwidth) { base_description->bandwidths = belle_sip_list_append(base_description->bandwidths,(void *)belle_sip_object_ref((void *)bandwidth)); } void belle_sdp_base_description_set_bandwidths(belle_sdp_base_description_t* base_description, belle_sip_list_t* bandwidths) { SET_LIST(base_description->bandwidths,bandwidths) } /************************ * media_description ***********************/ struct _belle_sdp_media_description { belle_sdp_base_description_t base_description; belle_sdp_media_t* media; }; void belle_sdp_media_description_destroy(belle_sdp_media_description_t* media_description) { if (media_description->media) belle_sip_object_unref(BELLE_SIP_OBJECT((media_description->media))); } void belle_sdp_media_description_clone(belle_sdp_media_description_t *media_description, const belle_sdp_media_description_t *orig){ if (orig->media) media_description->media = BELLE_SDP_MEDIA(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT((orig->media)))); } belle_sip_error_code belle_sdp_media_description_marshal(belle_sdp_media_description_t* media_description, char* buff, size_t buff_size, size_t *offset) { belle_sip_list_t* attributes; belle_sip_error_code error=belle_sip_object_marshal(BELLE_SIP_OBJECT(media_description->media),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; error=belle_sdp_base_description_marshal(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; for(attributes=media_description->base_description.attributes;attributes!=NULL;attributes=attributes->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(media_description,belle_sdp_base_description) belle_sdp_media_description_t* belle_sdp_media_description_create(const char* media_type ,int media_port ,int port_count ,const char* protocol ,belle_sip_list_t* static_media_formats) { belle_sdp_media_description_t* media_desc=belle_sdp_media_description_new(); belle_sdp_media_description_set_media(media_desc,belle_sdp_media_create(media_type,media_port,port_count,protocol,static_media_formats)); return media_desc; } BELLE_SDP_PARSE(media_description) void belle_sdp_media_description_add_dynamic_payloads(belle_sdp_media_description_t* media_description, belle_sip_list_t* payloadNames, belle_sip_list_t* payloadValues) { belle_sip_error("belle_sdp_media_description_add_dynamic_payloads not implemented yet"); } belle_sdp_attribute_t* belle_sdp_media_description_get_attribute(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } const char* belle_sdp_media_description_get_attribute_value(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_attribute_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_media_description_get_attributes(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->attributes; } int belle_sdp_media_description_get_bandwidth(const belle_sdp_media_description_t* media_description, const char* name) { return belle_sdp_base_description_get_bandwidth_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_media_description_get_bandwidths(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->bandwidths; } belle_sdp_connection_t* belle_sdp_media_description_get_connection(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->connection; } belle_sdp_info_t* belle_sdp_media_description_get_info(const belle_sdp_media_description_t* media_description) { return BELLE_SIP_CAST(media_description,belle_sdp_base_description_t)->info; } /*belle_sdp_key_t* belle_sdp_media_description_get_key(const belle_sdp_media_description_t* media_description);*/ belle_sdp_media_t* belle_sdp_media_description_get_media(const belle_sdp_media_description_t* media_description) { return media_description->media; } struct static_payload { unsigned char number; int channel_count; const char* type; int rate; }; #define STATIC_PAYLOAD_LIST_LENTH 8 /* * rfc 3551 * PT encoding media type clock rate channels name (Hz) ___________________________________________________ 0 PCMU A 8,000 1 1 reserved A 2 reserved A 3 GSM A 8,000 1 4 G723 A 8,000 1 5 DVI4 A 8,000 1 6 DVI4 A 16,000 1 7 LPC A 8,000 1 8 PCMA A 8,000 1 9 G722 A 8,000 1 10 L16 A 44,100 2 11 L16 A 44,100 1 12 QCELP A 8,000 1 13 CN A 8,000 1 14 MPA A 90,000 (see text) 15 G728 A 8,000 1 16 DVI4 A 11,025 1 17 DVI4 A 22,050 1 18 G729 A 8,000 1 Table 4: Payload types (PT) for audio encodings PT encoding media type clock rate name (Hz) _____________________________________________ 24 unassigned V 25 CelB V 90,000 26 JPEG V 90,000 27 unassigned V 28 nv V 90,000 29 unassigned V 30 unassigned V 31 H261 V 90,000 32 MPV V 90,000 33 MP2T AV 90,000 34 H263 V 90,000 Table 5: Payload types (PT) for video and combined encodings * * */ const struct static_payload static_payload_list [] ={ /*audio*/ {0,1,"PCMU",8000}, {3,1,"GSM",8000}, {4,1,"G723",8000}, {5,1,"DVI4",8000}, {6,1,"DVI4",16000}, {7,1,"LPC",8000}, {8,1,"PCMA",8000}, {9,1,"G722",8000}, {10,2,"L16",44100}, {11,1,"L16",44100}, {12,1,"QCELP",8000}, {13,1,"CN",8000}, {14,1,"MPA",90000}, {15,1,"G728",8000}, {16,1,"DVI4",11025}, {17,1,"DVI4",22050}, {18,1,"G729",8000}, /*video*/ {25,0,"CelB",90000}, {26,0,"JPEG",90000}, {28,0,"nv",90000}, {31,0,"H261",90000}, {32,0,"MPV",90000}, {33,0,"MP2T",90000}, {34,0,"H263",90000} }; static const size_t payload_list_elements=sizeof(static_payload_list)/sizeof(struct static_payload); static int mime_parameter_is_static(const belle_sdp_mime_parameter_t *param){ const struct static_payload* iterator; size_t i; for (iterator = static_payload_list,i=0;inumber == param->media_format && strcasecmp(iterator->type,param->type)==0 && iterator->channel_count==param->channel_count && iterator->rate==param->rate ) { return TRUE; } } return FALSE; } static int mime_parameter_fill_from_static(belle_sdp_mime_parameter_t *mime_parameter,int format) { const struct static_payload* iterator; size_t i; for (iterator = static_payload_list,i=0;inumber == format) { belle_sdp_mime_parameter_set_type(mime_parameter,iterator->type); belle_sdp_mime_parameter_set_rate(mime_parameter,iterator->rate); belle_sdp_mime_parameter_set_channel_count(mime_parameter,iterator->channel_count); break; } } return 0; } static int mime_parameter_fill_from_rtpmap(belle_sdp_mime_parameter_t *mime_parameter, const char *rtpmap, int is_audio){ char *mime=belle_sip_strdup(rtpmap); char *p=strchr(mime,'/'); if (p){ char *chans; *p='\0'; p++; chans=strchr(p,'/'); if (chans){ *chans='\0'; chans++; belle_sdp_mime_parameter_set_channel_count(mime_parameter,atoi(chans)); }else if (is_audio) belle_sdp_mime_parameter_set_channel_count(mime_parameter,1); /*in absence of channel count, 1 is implicit for audio streams*/ belle_sdp_mime_parameter_set_rate(mime_parameter,atoi(p)); } belle_sdp_mime_parameter_set_type(mime_parameter,mime); belle_sip_free(mime); return 0; } /* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/ static const char *belle_sdp_media_description_a_attr_value_get_with_pt(const belle_sdp_media_description_t* media_description,int pt,const char *field) { int tmppt=0,scanned=0; const char *tmp; belle_sdp_attribute_t *attr; belle_sip_list_t* attribute_list; for ( attribute_list =belle_sdp_media_description_get_attributes(media_description) ;attribute_list!=NULL ;attribute_list=attribute_list->next) { attr = BELLE_SDP_ATTRIBUTE(attribute_list->data); if (strcmp(field,belle_sdp_attribute_get_name(attr))==0 && belle_sdp_attribute_get_value(attr)!=NULL){ int nb = sscanf(belle_sdp_attribute_get_value(attr),"%i %n",&tmppt,&scanned); /* the return value may depend on how %n is interpreted by the libc: see manpage*/ if (nb == 1 || nb==2 ){ if (pt==tmppt){ tmp=belle_sdp_attribute_get_value(attr)+scanned; if (strlen(tmp)>0) return tmp; } }else belle_sip_warning("sdp has a strange a= line (%s) nb=%i",belle_sdp_attribute_get_value(attr),nb); } } return NULL; } belle_sip_list_t* belle_sdp_media_description_build_mime_parameters(const belle_sdp_media_description_t* media_description) { /*First, get media type*/ belle_sdp_media_t* media = belle_sdp_media_description_get_media(media_description); belle_sip_list_t* mime_parameter_list=NULL; belle_sip_list_t* media_formats=NULL; belle_sdp_mime_parameter_t* mime_parameter; const char* rtpmap=NULL; const char* fmtp=NULL; const char* ptime=NULL; const char* max_ptime=NULL; int ptime_as_int=-1; int max_ptime_as_int=-1; int is_audio=0; if (!media) { belle_sip_error("belle_sdp_media_description_build_mime_parameters: no media"); return NULL; } if (strcasecmp(belle_sdp_media_get_media_type(media),"audio")==0) is_audio=1; ptime = belle_sdp_media_description_get_attribute_value(media_description,"ptime"); ptime?ptime_as_int=atoi(ptime):-1; max_ptime = belle_sdp_media_description_get_attribute_value(media_description,"maxptime"); max_ptime?max_ptime_as_int=atoi(max_ptime):-1; for (media_formats = belle_sdp_media_get_media_formats(media);media_formats!=NULL;media_formats=media_formats->next) { /*create mime parameters with format*/ mime_parameter = belle_sdp_mime_parameter_new(); belle_sdp_mime_parameter_set_ptime(mime_parameter,ptime_as_int); belle_sdp_mime_parameter_set_max_ptime(mime_parameter,max_ptime_as_int); belle_sdp_mime_parameter_set_media_format(mime_parameter,(int)(intptr_t)media_formats->data); /*get rtpmap*/ rtpmap = belle_sdp_media_description_a_attr_value_get_with_pt(media_description ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,"rtpmap"); if (rtpmap) { mime_parameter_fill_from_rtpmap(mime_parameter,rtpmap,is_audio); }else{ mime_parameter_fill_from_static(mime_parameter,belle_sdp_mime_parameter_get_media_format(mime_parameter)); } fmtp = belle_sdp_media_description_a_attr_value_get_with_pt(media_description ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,"fmtp"); if (fmtp) { belle_sdp_mime_parameter_set_parameters(mime_parameter,fmtp); } mime_parameter_list=belle_sip_list_append(mime_parameter_list,mime_parameter); } return mime_parameter_list; } #define MAX_FMTP_LENGTH 512 void belle_sdp_media_description_append_values_from_mime_parameter(belle_sdp_media_description_t* media_description, const belle_sdp_mime_parameter_t* mime_parameter) { belle_sdp_media_t* media = belle_sdp_media_description_get_media(media_description); char atribute_value [MAX_FMTP_LENGTH]; int current_ptime=0; int current_max_ptime=0; belle_sdp_media_set_media_formats(media,belle_sip_list_append(belle_sdp_media_get_media_formats(media) ,(void*)(intptr_t)(belle_sdp_mime_parameter_get_media_format(mime_parameter)))); if (belle_sdp_media_description_get_attribute_value(media_description,"ptime")) { current_ptime=atoi(belle_sdp_media_description_get_attribute_value(media_description,"ptime")); belle_sdp_media_description_remove_attribute(media_description,"ptime"); } if (belle_sdp_media_description_get_attribute_value(media_description,"maxptime")) { current_max_ptime=atoi(belle_sdp_media_description_get_attribute_value(media_description,"maxptime")); belle_sdp_media_description_remove_attribute(media_description,"maxptime"); } #ifndef BELLE_SDP_FORCE_RTP_MAP /* defined to for RTP map even for static codec*/ if (!mime_parameter_is_static(mime_parameter)) { /*dynamic payload*/ #endif if (belle_sdp_mime_parameter_get_channel_count(mime_parameter)>1) { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s/%i/%i" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_type(mime_parameter) ,belle_sdp_mime_parameter_get_rate(mime_parameter) ,belle_sdp_mime_parameter_get_channel_count(mime_parameter)); } else { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s/%i" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_type(mime_parameter) ,belle_sdp_mime_parameter_get_rate(mime_parameter)); } belle_sdp_media_description_set_attribute_value(media_description,"rtpmap",atribute_value); #ifndef BELLE_SDP_FORCE_RTP_MAP } #endif // always include fmtp parameters if available if (belle_sdp_mime_parameter_get_parameters(mime_parameter)) { snprintf(atribute_value,MAX_FMTP_LENGTH,"%i %s" ,belle_sdp_mime_parameter_get_media_format(mime_parameter) ,belle_sdp_mime_parameter_get_parameters(mime_parameter)); belle_sdp_media_description_set_attribute_value(media_description,"fmtp",atribute_value); } if (belle_sdp_mime_parameter_get_ptime(mime_parameter)>current_ptime) { current_ptime=belle_sdp_mime_parameter_get_ptime(mime_parameter); } if (current_ptime>0){ char ptime[12]; snprintf(ptime,sizeof(ptime),"%i",current_ptime); belle_sdp_media_description_set_attribute_value(media_description,"ptime",ptime); } if (belle_sdp_mime_parameter_get_max_ptime(mime_parameter)>current_max_ptime) { current_max_ptime=belle_sdp_mime_parameter_get_max_ptime(mime_parameter); } if (current_max_ptime>0){ char max_ptime[12]; snprintf(max_ptime,sizeof(max_ptime),"%i",current_max_ptime); belle_sdp_media_description_set_attribute_value(media_description,"maxptime",max_ptime); } } belle_sip_list_t* belle_sdp_media_description_get_mime_types(const belle_sdp_media_description_t* media_description) { belle_sip_error("belle_sdp_media_description_get_mime_types: not implemented yet"); return NULL; } void belle_sdp_media_description_remove_attribute(belle_sdp_media_description_t* media_description,const char* name) { belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } void belle_sdp_media_description_remove_bandwidth(belle_sdp_media_description_t* media_description,const char* name) { belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name); } void belle_sdp_media_description_set_attribute_value(belle_sdp_media_description_t* media_description, const char* name, const char* value) { belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),name,value); } void belle_sdp_media_description_set_attributes(belle_sdp_media_description_t* media_description, belle_sip_list_t* value) { belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),value); } void belle_sdp_media_description_add_attribute(belle_sdp_media_description_t* media_description, const belle_sdp_attribute_t* attribute) { belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),attribute); } void belle_sdp_media_description_set_bandwidth(belle_sdp_media_description_t* media_description, const char* type, int value) { belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),type,value); } void belle_sdp_media_description_add_bandwidth(belle_sdp_media_description_t* media_description, const belle_sdp_bandwidth_t* bandwidth) { belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),bandwidth); } void belle_sdp_media_description_set_bandwidths(belle_sdp_media_description_t* media_description, belle_sip_list_t* bandwidths) { belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),bandwidths); } #define SET_OBJECT(object,param,param_type) \ param_type** current = &object->param; \ if (param) belle_sip_object_ref(param); \ if (*current) { \ belle_sip_object_unref(BELLE_SIP_OBJECT(*current)); \ } \ *current=param; \ void belle_sdp_media_description_set_connection(belle_sdp_media_description_t* media_description, belle_sdp_connection_t* connection) { SET_OBJECT(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),connection,belle_sdp_connection_t) } void belle_sdp_media_description_set_info(belle_sdp_media_description_t* media_description,belle_sdp_info_t* info) { SET_OBJECT(BELLE_SIP_CAST(media_description,belle_sdp_base_description_t),info,belle_sdp_info_t) } /*void belle_sdp_media_description_set_key(belle_sdp_media_description_t* media_description,belle_sdp_key_t* key);*/ void belle_sdp_media_description_set_media(belle_sdp_media_description_t* media_description, belle_sdp_media_t* media) { SET_OBJECT(media_description,media,belle_sdp_media_t) } /************************ * origin ***********************/ struct _belle_sdp_origin { belle_sip_object_t base; const char* address; const char* address_type; const char* network_type; const char* username; unsigned int session_id; unsigned int session_version; }; void belle_sdp_origin_destroy(belle_sdp_origin_t* origin) { DESTROY_STRING(origin,address) DESTROY_STRING(origin,address_type) DESTROY_STRING(origin,network_type) DESTROY_STRING(origin,username) } void belle_sdp_origin_clone(belle_sdp_origin_t *origin, const belle_sdp_origin_t *orig){ CLONE_STRING(belle_sdp_origin,username,origin,orig); CLONE_STRING(belle_sdp_origin,address,origin,orig); CLONE_STRING(belle_sdp_origin,address_type,origin,orig); CLONE_STRING(belle_sdp_origin,network_type,origin,orig); origin->session_id = orig->session_id; origin->session_version = orig->session_version; } belle_sip_error_code belle_sdp_origin_marshal(belle_sdp_origin_t* origin, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf( buff ,buff_size ,offset ,"o=%s %u %u %s %s %s" ,origin->username ,origin->session_id ,origin->session_version ,origin->network_type ,origin->address_type ,origin->address); } BELLE_SDP_NEW(origin,belle_sip_object) belle_sdp_origin_t* belle_sdp_origin_create(const char* user_name , unsigned int session_id , unsigned int session_version , const char* network_type , const char* addr_type , const char* address) { belle_sdp_origin_t* origin=belle_sdp_origin_new(); belle_sdp_origin_set_username(origin,user_name); belle_sdp_origin_set_session_id(origin,session_id); belle_sdp_origin_set_session_version(origin,session_version); belle_sdp_origin_set_network_type(origin,network_type); belle_sdp_origin_set_address_type(origin,addr_type); belle_sdp_origin_set_address(origin,address); return origin; } BELLE_SDP_PARSE(origin) GET_SET_STRING(belle_sdp_origin,username); GET_SET_STRING(belle_sdp_origin,address); GET_SET_STRING(belle_sdp_origin,address_type); GET_SET_STRING(belle_sdp_origin,network_type); GET_SET_INT(belle_sdp_origin,session_id,unsigned int); GET_SET_INT(belle_sdp_origin,session_version,unsigned int); /************************ * session_name ***********************/ struct _belle_sdp_session_name { belle_sip_object_t base; const char* value; }; void belle_sdp_session_name_destroy(belle_sdp_session_name_t* session_name) { DESTROY_STRING(session_name,value) } void belle_sdp_session_name_clone(belle_sdp_session_name_t *session_name, const belle_sdp_session_name_t *orig){ CLONE_STRING(belle_sdp_session_name,value,session_name,orig); } belle_sip_error_code belle_sdp_session_name_marshal(belle_sdp_session_name_t* session_name, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"s=%s",session_name->value); } BELLE_SDP_NEW(session_name,belle_sip_object) belle_sdp_session_name_t* belle_sdp_session_name_create (const char* name) { belle_sdp_session_name_t* n=belle_sdp_session_name_new(); belle_sdp_session_name_set_value(n,name); return n; } //BELLE_SDP_PARSE(session_name) GET_SET_STRING(belle_sdp_session_name,value); /************************ * session_description ***********************/ struct _belle_sdp_session_description { belle_sdp_base_description_t base_description; belle_sdp_version_t* version; belle_sip_list_t* emails; belle_sdp_origin_t* origin; belle_sdp_session_name_t* session_name; belle_sip_list_t* phones; belle_sip_list_t* times; belle_sdp_uri_t* uri; belle_sdp_uri_t* zone_adjustments; belle_sip_list_t* media_descriptions; }; void belle_sdp_session_description_destroy(belle_sdp_session_description_t* session_description) { if (session_description->version) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->version)); belle_sip_list_free_with_data(session_description->emails,belle_sip_object_freefunc); if (session_description->origin) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->origin)); if (session_description->session_name) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->session_name)); belle_sip_list_free_with_data(session_description->phones,belle_sip_object_freefunc); belle_sip_list_free_with_data(session_description->times,belle_sip_object_freefunc); if (session_description->uri) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->uri)); if (session_description->zone_adjustments) belle_sip_object_unref(BELLE_SIP_OBJECT(session_description->zone_adjustments)); belle_sip_list_free_with_data(session_description->media_descriptions,belle_sip_object_freefunc); } void belle_sdp_session_description_clone(belle_sdp_session_description_t *session_description, const belle_sdp_session_description_t *orig){ if (orig->version) session_description->version = BELLE_SDP_VERSION(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->version))); session_description->emails = belle_sip_list_copy_with_data(orig->emails,belle_sip_object_copyfunc); if (orig->origin) session_description->origin = BELLE_SDP_ORIGIN(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->origin))); if (orig->session_name) session_description->session_name = BELLE_SDP_SESSION_NAME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->session_name))); session_description->phones = belle_sip_list_copy_with_data(orig->phones,belle_sip_object_copyfunc); session_description->times = belle_sip_list_copy_with_data(orig->times,belle_sip_object_copyfunc); if (orig->uri) session_description->uri = BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->uri))); if (orig->zone_adjustments) session_description->zone_adjustments = BELLE_SDP_URI(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->zone_adjustments))); session_description->media_descriptions = belle_sip_list_copy_with_data(orig->media_descriptions,belle_sip_object_copyfunc); } belle_sip_error_code belle_sdp_session_description_marshal(belle_sdp_session_description_t* session_description, char* buff, size_t buff_size, size_t *offset) { /*session_description: proto_version CR LF origin_field session_name_field (info CR LF)? uri_field? (email CR LF)* phone_field* (connection CR LF)? (bandwidth CR LF)* time_field (repeat_time CR LF)? (zone_adjustments CR LF)? (key_field CR LF)? (attribute CR LF)* media_descriptions; */ belle_sip_error_code error=BELLE_SIP_OK; belle_sip_list_t* media_descriptions; belle_sip_list_t* times; belle_sip_list_t* attributes; if (session_description->version) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->version),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (session_description->origin) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->origin),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } if (session_description->session_name) { error=belle_sip_object_marshal(BELLE_SIP_OBJECT(session_description->session_name),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } error=belle_sdp_base_description_marshal((belle_sdp_base_description_t*)(&session_description->base_description),buff,buff_size, offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "t="); if (error!=BELLE_SIP_OK) return error; for(times=session_description->times;times!=NULL;times=times->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(times->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(attributes=session_description->base_description.attributes;attributes!=NULL;attributes=attributes->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(attributes->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; error=belle_sip_snprintf(buff, buff_size, offset, "\r\n"); if (error!=BELLE_SIP_OK) return error; } for(media_descriptions=session_description->media_descriptions;media_descriptions!=NULL;media_descriptions=media_descriptions->next){ error=belle_sip_object_marshal(BELLE_SIP_OBJECT(media_descriptions->data),buff,buff_size,offset); if (error!=BELLE_SIP_OK) return error; } return error; } BELLE_SDP_NEW(session_description,belle_sdp_base_description) BELLE_SDP_PARSE(session_description) belle_sip_list_t * belle_sdp_session_description_get_attributes(const belle_sdp_session_description_t *session_description) { return belle_sdp_base_description_get_attributes(BELLE_SIP_CAST(session_description, belle_sdp_base_description_t)); } const char* belle_sdp_session_description_get_attribute_value(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_attribute_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } const belle_sdp_attribute_t* belle_sdp_session_description_get_attribute(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } int belle_sdp_session_description_get_bandwidth(const belle_sdp_session_description_t* session_description, const char* name) { return belle_sdp_base_description_get_bandwidth_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } belle_sip_list_t* belle_sdp_session_description_get_bandwidths(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->bandwidths; } belle_sdp_connection_t* belle_sdp_session_description_get_connection(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->connection; } belle_sip_list_t* belle_sdp_session_description_get_emails(const belle_sdp_session_description_t* session_description){ return session_description->emails; } belle_sdp_info_t* belle_sdp_session_description_get_info(const belle_sdp_session_description_t* session_description) { return BELLE_SIP_CAST(session_description,belle_sdp_base_description_t)->info; } /*belle_sdp_key_t* belle_sdp_session_description_get_key(const belle_sdp_session_description_t* session_description);*/ belle_sip_list_t* belle_sdp_session_description_get_media_descriptions(const belle_sdp_session_description_t* session_description) { return session_description->media_descriptions; } belle_sdp_origin_t* belle_sdp_session_description_get_origin(const belle_sdp_session_description_t* session_description){ return session_description->origin; } belle_sip_list_t* belle_sdp_session_description_get_phones(const belle_sdp_session_description_t* session_description) { return session_description->phones; } belle_sdp_session_name_t* belle_sdp_session_description_get_session_name(const belle_sdp_session_description_t* session_description) { return session_description->session_name; } belle_sip_list_t* belle_sdp_session_description_get_time_descriptions(const belle_sdp_session_description_t* session_description) { return session_description->times; } belle_sdp_uri_t* belle_sdp_session_description_get_uri(const belle_sdp_session_description_t* session_description) { return session_description->uri; } belle_sdp_version_t* belle_sdp_session_description_get_version(const belle_sdp_session_description_t* session_description) { return session_description->version; } belle_sdp_uri_t* belle_sdp_session_description_get_zone_adjustments(const belle_sdp_session_description_t* session_description) { return session_description->zone_adjustments; } void belle_sdp_session_description_remove_attribute(belle_sdp_session_description_t* session_description, const char* name) { belle_sdp_base_description_remove_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } void belle_sdp_session_description_remove_bandwidth(belle_sdp_session_description_t* session_description, const char* name) { belle_sdp_base_description_remove_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name); } void belle_sdp_session_description_set_attribute_value(belle_sdp_session_description_t* session_description, const char* name, const char* value) { belle_sdp_base_description_set_attribute_value(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),name,value); } void belle_sdp_session_description_set_attributes(belle_sdp_session_description_t* session_description, belle_sip_list_t* attributes) { belle_sdp_base_description_set_attributes(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),attributes); } void belle_sdp_session_description_add_attribute(belle_sdp_session_description_t* session_description, const belle_sdp_attribute_t* attribute) { belle_sdp_base_description_add_attribute(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),attribute); } void belle_sdp_session_description_set_bandwidth(belle_sdp_session_description_t* session_description, const char* type, int value) { belle_sdp_base_description_set_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),type,value); } void belle_sdp_session_description_set_bandwidths(belle_sdp_session_description_t* session_description, belle_sip_list_t* bandwidths) { belle_sdp_base_description_set_bandwidths(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),bandwidths); } void belle_sdp_session_description_add_bandwidth(belle_sdp_session_description_t* session_description, const belle_sdp_bandwidth_t* bandwidth) { belle_sdp_base_description_add_bandwidth(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),bandwidth); } void belle_sdp_session_description_set_connection(belle_sdp_session_description_t* session_description, belle_sdp_connection_t* connection) { SET_OBJECT(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),connection,belle_sdp_connection_t) } void belle_sdp_session_description_set_emails(belle_sdp_session_description_t* session_description, belle_sip_list_t* emails) { SET_LIST(session_description->emails,emails) } void belle_sdp_session_description_set_info(belle_sdp_session_description_t* session_description, belle_sdp_info_t* info) { SET_OBJECT(BELLE_SIP_CAST(session_description,belle_sdp_base_description_t),info,belle_sdp_info_t) } /*void belle_sdp_session_description_set_key(belle_sdp_session_description_t* session_description, belle_sdp_key_t* key);*/ void belle_sdp_session_description_set_media_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* media_descriptions) { SET_LIST(session_description->media_descriptions,media_descriptions) } void belle_sdp_session_description_add_media_description(belle_sdp_session_description_t* session_description, belle_sdp_media_description_t* media_description) { session_description->media_descriptions = belle_sip_list_append(session_description->media_descriptions,belle_sip_object_ref(media_description)); } void belle_sdp_session_description_set_origin(belle_sdp_session_description_t* session_description, belle_sdp_origin_t* origin) { SET_OBJECT(session_description,origin,belle_sdp_origin_t) } void belle_sdp_session_description_set_phones(belle_sdp_session_description_t* session_description, belle_sip_list_t* phones) { SET_LIST(session_description->phones,phones) } void belle_sdp_session_description_set_session_name(belle_sdp_session_description_t* session_description, belle_sdp_session_name_t* session_name) { SET_OBJECT(session_description,session_name,belle_sdp_session_name_t) } void belle_sdp_session_description_set_time_descriptions(belle_sdp_session_description_t* session_description, belle_sip_list_t* times) { SET_LIST(session_description->times,times) } void belle_sdp_session_description_set_time_description(belle_sdp_session_description_t* session_description, belle_sdp_time_description_t* time_desc) { belle_sdp_session_description_set_time_descriptions(session_description,belle_sip_list_new(time_desc)); } void belle_sdp_session_description_set_uri(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* uri) { SET_OBJECT(session_description,uri,belle_sdp_uri_t) } void belle_sdp_session_description_set_version(belle_sdp_session_description_t* session_description, belle_sdp_version_t* version) { SET_OBJECT(session_description,version,belle_sdp_version_t) } void belle_sdp_session_description_set_zone_adjustments(belle_sdp_session_description_t* session_description, belle_sdp_uri_t* zone_adjustments) { SET_OBJECT(session_description,zone_adjustments,belle_sdp_uri_t) } /************************ * time ***********************/ struct _belle_sdp_time { belle_sip_object_t base; int start; int stop; }; void belle_sdp_time_destroy(belle_sdp_time_t* time) { } void belle_sdp_time_clone(belle_sdp_time_t *time, const belle_sdp_time_t *orig){ time->start=orig->start; time->stop=orig->stop; } belle_sip_error_code belle_sdp_time_marshal(belle_sdp_time_t* time, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"%i %i",time->start,time->stop); } BELLE_SDP_NEW(time,belle_sip_object) //BELLE_SDP_PARSE(version) GET_SET_INT(belle_sdp_time,start,int); GET_SET_INT(belle_sdp_time,stop,int); /************************ * time description ***********************/ struct _belle_sdp_time_description { belle_sip_object_t base; belle_sdp_time_t* time; }; void belle_sdp_time_description_destroy(belle_sdp_time_description_t* time_description) { if (time_description->time) belle_sip_object_unref(BELLE_SIP_OBJECT(time_description->time)); } void belle_sdp_time_description_clone(belle_sdp_time_description_t *time_description, const belle_sdp_time_description_t *orig){ if (orig->time) time_description->time = BELLE_SDP_TIME(belle_sip_object_clone_and_ref(BELLE_SIP_OBJECT(orig->time))); } belle_sip_error_code belle_sdp_time_description_marshal(belle_sdp_time_description_t* time_description, char* buff, size_t buff_size, size_t *offset) { return belle_sip_object_marshal(BELLE_SIP_OBJECT(time_description->time),buff,buff_size,offset); } BELLE_SDP_NEW(time_description,belle_sip_object) belle_sdp_time_description_t* belle_sdp_time_description_create (int start,int stop) { belle_sdp_time_description_t* time_desc= belle_sdp_time_description_new(); belle_sdp_time_t* time = belle_sdp_time_new(); belle_sdp_time_set_start(time,start); belle_sdp_time_set_stop(time,stop); belle_sdp_time_description_set_time(time_desc,time); return time_desc; } belle_sip_list_t* belle_sdp_time_description_get_repeate_times(const belle_sdp_time_description_t* time_description) { return NULL; } belle_sdp_time_t* belle_sdp_time_description_get_time(const belle_sdp_time_description_t* time_description) { return time_description->time; } void belle_sdp_time_description_set_repeate_times(belle_sdp_time_description_t* time_description, belle_sip_list_t* times) { belle_sip_error("time description repeat time not implemented"); } void belle_sdp_time_description_set_time(belle_sdp_time_description_t* time_description, belle_sdp_time_t* time) { SET_OBJECT(time_description,time,belle_sdp_time_t) } /************************ * version ***********************/ struct _belle_sdp_version { belle_sip_object_t base; int version; }; void belle_sdp_version_destroy(belle_sdp_version_t* version) { } void belle_sdp_version_clone(belle_sdp_version_t *version, const belle_sdp_version_t *orig){ version->version = orig->version; } belle_sip_error_code belle_sdp_version_marshal(belle_sdp_version_t* version, char* buff, size_t buff_size, size_t *offset) { return belle_sip_snprintf(buff,buff_size,offset,"v=%i",version->version); } BELLE_SDP_NEW(version,belle_sip_object) belle_sdp_version_t* belle_sdp_version_create(int version) { belle_sdp_version_t* v = belle_sdp_version_new(); belle_sdp_version_set_version(v,version); return v; } //BELLE_SDP_PARSE(version) GET_SET_INT(belle_sdp_version,version,int); /*************************************************************************************** * mime_parameter * **************************************************************************************/ static void belle_sdp_mime_parameter_destroy(belle_sdp_mime_parameter_t *mime_parameter) { if (mime_parameter->type) belle_sip_free((void*)mime_parameter->type); if (mime_parameter->parameters) belle_sip_free((void*)mime_parameter->parameters); } static void belle_sdp_mime_parameter_clone(belle_sdp_mime_parameter_t *mime_parameter,belle_sdp_mime_parameter_t *orig) { mime_parameter->rate = orig->rate; mime_parameter->channel_count = orig->channel_count; mime_parameter->ptime = orig->ptime; mime_parameter->max_ptime = orig->max_ptime; mime_parameter->media_format = orig->media_format; CLONE_STRING(belle_sdp_mime_parameter,type,mime_parameter,orig); CLONE_STRING(belle_sdp_mime_parameter,parameters,mime_parameter,orig); } BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(belle_sdp_mime_parameter_t); BELLE_SIP_INSTANCIATE_VPTR(belle_sdp_mime_parameter_t ,belle_sip_object_t ,belle_sdp_mime_parameter_destroy ,belle_sdp_mime_parameter_clone ,NULL ,TRUE); belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_new() { belle_sdp_mime_parameter_t* l_param = belle_sip_object_new(belle_sdp_mime_parameter_t); l_param->ptime = -1; l_param->max_ptime = -1; return l_param; } belle_sdp_mime_parameter_t* belle_sdp_mime_parameter_create(const char* type, int media_format, int rate,int channel_count) { belle_sdp_mime_parameter_t* mime_param= belle_sdp_mime_parameter_new(); belle_sdp_mime_parameter_set_type(mime_param,type); belle_sdp_mime_parameter_set_media_format(mime_param,media_format); belle_sdp_mime_parameter_set_rate(mime_param,rate); belle_sdp_mime_parameter_set_channel_count(mime_param,channel_count); return mime_param; } GET_SET_INT(belle_sdp_mime_parameter,rate,int); GET_SET_INT(belle_sdp_mime_parameter,channel_count,int); GET_SET_INT(belle_sdp_mime_parameter,ptime,int); GET_SET_INT(belle_sdp_mime_parameter,max_ptime,int); GET_SET_INT(belle_sdp_mime_parameter,media_format,int); GET_SET_STRING(belle_sdp_mime_parameter,type); GET_SET_STRING(belle_sdp_mime_parameter,parameters);