/* * Copyright 2008-2014 Arsen Chaloyan * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: mrcp_sdp.c 2136 2014-07-04 06:33:36Z achaloyan@gmail.com $ */ #include #include #include #include "mrcp_sdp.h" #include "mrcp_session_descriptor.h" #include "mrcp_control_descriptor.h" #include "mpf_rtp_attribs.h" #include "mpf_rtp_pt.h" #include "apt_text_stream.h" #include "apt_log.h" static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_descriptor); static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer); static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool); static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *mrcp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool); /** Generate SDP string by MRCP descriptor */ MRCP_DECLARE(apr_size_t) sdp_string_generate_by_mrcp_descriptor(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, apt_bool_t offer) { apr_size_t i; apr_size_t count; apr_size_t audio_index = 0; mpf_rtp_media_descriptor_t *audio_media; apr_size_t video_index = 0; mpf_rtp_media_descriptor_t *video_media; apr_size_t control_index = 0; mrcp_control_descriptor_t *control_media; apr_size_t offset = 0; const char *ip = descriptor->ext_ip.buf ? descriptor->ext_ip.buf : (descriptor->ip.buf ? descriptor->ip.buf : "0.0.0.0"); buffer[0] = '\0'; offset += snprintf(buffer+offset,size-offset, "v=0\r\n" "o=%s 0 0 IN IP4 %s\r\n" "s=-\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n", descriptor->origin.buf ? descriptor->origin.buf : "-", ip, ip); count = mrcp_session_media_count_get(descriptor); for(i=0; iid == i) { /* generate audio media */ audio_index++; offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,audio_media); continue; } video_media = mrcp_session_video_media_get(descriptor,video_index); if(video_media && video_media->id == i) { /* generate video media */ video_index++; offset += sdp_rtp_media_generate(buffer+offset,size-offset,descriptor,video_media); continue; } control_media = mrcp_session_control_media_get(descriptor,control_index); if(control_media && control_media->id == i) { /** generate mrcp control media */ control_index++; offset += sdp_control_media_generate(buffer+offset,size-offset,descriptor,control_media,offer); continue; } } return offset; } /** Generate MRCP descriptor by SDP session */ MRCP_DECLARE(apt_bool_t) mrcp_descriptor_generate_by_sdp_session(mrcp_session_descriptor_t* descriptor, const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool) { sdp_media_t *sdp_media; if(!sdp) { apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid SDP Message"); return FALSE; } if(force_destination_ip) { apt_string_assign(&descriptor->ip,force_destination_ip,pool); } else if(sdp->sdp_connection) { apt_string_assign(&descriptor->ip,sdp->sdp_connection->c_address,pool); } for(sdp_media=sdp->sdp_media; sdp_media; sdp_media=sdp_media->m_next) { switch(sdp_media->m_type) { case sdp_media_audio: { mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); mpf_rtp_media_descriptor_init(media); media->id = mrcp_session_audio_media_add(descriptor,media); mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); break; } case sdp_media_video: { mpf_rtp_media_descriptor_t *media = apr_palloc(pool,sizeof(mpf_rtp_media_descriptor_t)); mpf_rtp_media_descriptor_init(media); media->id = mrcp_session_video_media_add(descriptor,media); mpf_rtp_media_generate(media,sdp_media,&descriptor->ip,pool); break; } case sdp_media_application: { mrcp_control_descriptor_t *control_media = mrcp_control_descriptor_create(pool); control_media->id = mrcp_session_control_media_add(descriptor,control_media); mrcp_control_media_generate(control_media,sdp_media,&descriptor->ip,pool); break; } default: apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not Supported SDP Media [%s]", sdp_media->m_type_name); break; } } return TRUE; } /** Generate SDP media by RTP media descriptor */ static apr_size_t sdp_rtp_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mpf_rtp_media_descriptor_t *audio_media) { apr_size_t offset = 0; if(audio_media->state == MPF_MEDIA_ENABLED) { int codec_count = 0; int i; mpf_codec_descriptor_t *codec_descriptor; apr_array_header_t *descriptor_arr = audio_media->codec_list.descriptor_arr; const apt_str_t *direction_str; if(!descriptor_arr) { return 0; } offset += snprintf(buffer+offset,size-offset,"m=audio %d RTP/AVP",audio_media->port); for(i=0; inelts; i++) { codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t); if(codec_descriptor->enabled == TRUE) { offset += snprintf(buffer+offset,size-offset," %d",codec_descriptor->payload_type); codec_count++; } } if(!codec_count){ /* SDP m line should have at least one media format listed; use a reserved RTP payload type */ offset += snprintf(buffer+offset,size-offset," %d",RTP_PT_RESERVED); } offset += snprintf(buffer+offset,size-offset,"\r\n"); if(descriptor->ip.length && audio_media->ip.length && apt_string_compare(&descriptor->ip,&audio_media->ip) != TRUE) { const char *media_ip = audio_media->ext_ip.buf ? audio_media->ext_ip.buf : audio_media->ip.buf; offset += snprintf(buffer+offset,size-offset,"c=IN IP4 %s\r\n",media_ip); } for(i=0; inelts; i++) { codec_descriptor = &APR_ARRAY_IDX(descriptor_arr,i,mpf_codec_descriptor_t); if(codec_descriptor->enabled == TRUE && codec_descriptor->name.buf) { offset += snprintf(buffer+offset,size-offset,"a=rtpmap:%d %s/%d\r\n", codec_descriptor->payload_type, codec_descriptor->name.buf, codec_descriptor->sampling_rate); if(codec_descriptor->format.buf) { offset += snprintf(buffer+offset,size-offset,"a=fmtp:%d %s\r\n", codec_descriptor->payload_type, codec_descriptor->format.buf); } } } direction_str = mpf_rtp_direction_str_get(audio_media->direction); if(direction_str) { offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",direction_str->buf); } if(audio_media->ptime) { offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",audio_media->ptime); } } else { offset += snprintf(buffer+offset,size-offset,"m=audio 0 RTP/AVP %d\r\n",RTP_PT_RESERVED); } offset += snprintf(buffer+offset,size-offset,"a=mid:%"APR_SIZE_T_FMT"\r\n",audio_media->mid); return offset; } /** Generate SDP media by MRCP control media descriptor */ static apr_size_t sdp_control_media_generate(char *buffer, apr_size_t size, const mrcp_session_descriptor_t *descriptor, const mrcp_control_descriptor_t *control_media, apt_bool_t offer) { int i; apr_size_t offset = 0; const apt_str_t *proto; const apt_str_t *setup_type; const apt_str_t *connection_type; proto = mrcp_proto_get(control_media->proto); setup_type = mrcp_setup_type_get(control_media->setup_type); connection_type = mrcp_connection_type_get(control_media->connection_type); if(offer == TRUE) { /* offer */ if(control_media->port) { offset += snprintf(buffer+offset,size-offset, "m=application %d %s 1\r\n" "a=setup:%s\r\n" "a=connection:%s\r\n" "a=resource:%s\r\n", control_media->port, proto ? proto->buf : "", setup_type ? setup_type->buf : "", connection_type ? connection_type->buf : "", control_media->resource_name.buf); } else { offset += snprintf(buffer+offset,size-offset, "m=application %d %s 1\r\n" "a=resource:%s\r\n", control_media->port, proto ? proto->buf : "", control_media->resource_name.buf); } } else { /* answer */ if(control_media->port) { offset += snprintf(buffer+offset,size-offset, "m=application %d %s 1\r\n" "a=setup:%s\r\n" "a=connection:%s\r\n" "a=channel:%s@%s\r\n", control_media->port, proto ? proto->buf : "", setup_type ? setup_type->buf : "", connection_type ? connection_type->buf : "", control_media->session_id.buf, control_media->resource_name.buf); } else { offset += snprintf(buffer+offset,size-offset, "m=application %d %s 1\r\n" "a=channel:%s@%s\r\n", control_media->port, proto ? proto->buf : "", control_media->session_id.buf, control_media->resource_name.buf); } } for(i=0; icmid_arr->nelts; i++) { offset += snprintf(buffer+offset,size-offset, "a=cmid:%"APR_SIZE_T_FMT"\r\n", APR_ARRAY_IDX(control_media->cmid_arr,i,apr_size_t)); } return offset; } /** Generate RTP media descriptor by SDP media */ static apt_bool_t mpf_rtp_media_generate(mpf_rtp_media_descriptor_t *rtp_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool) { mpf_rtp_attrib_e id; apt_str_t name; sdp_attribute_t *attrib = NULL; sdp_rtpmap_t *map; mpf_codec_descriptor_t *codec; for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { apt_string_set(&name,attrib->a_name); id = mpf_rtp_attrib_id_find(&name); switch(id) { case RTP_ATTRIB_MID: rtp_media->mid = atoi(attrib->a_value); break; case RTP_ATTRIB_PTIME: rtp_media->ptime = (apr_uint16_t)atoi(attrib->a_value); break; default: break; } } mpf_codec_list_init(&rtp_media->codec_list,5,pool); for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) { codec = mpf_codec_list_add(&rtp_media->codec_list); if(codec) { codec->payload_type = (apr_byte_t)map->rm_pt; apt_string_assign(&codec->name,map->rm_encoding,pool); codec->sampling_rate = (apr_uint16_t)map->rm_rate; codec->channel_count = 1; } } switch(sdp_media->m_mode) { case sdp_inactive: rtp_media->direction = STREAM_DIRECTION_NONE; break; case sdp_sendonly: rtp_media->direction = STREAM_DIRECTION_SEND; break; case sdp_recvonly: rtp_media->direction = STREAM_DIRECTION_RECEIVE; break; case sdp_sendrecv: rtp_media->direction = STREAM_DIRECTION_DUPLEX; break; } if(sdp_media->m_connections) { apt_string_assign(&rtp_media->ip,sdp_media->m_connections->c_address,pool); } else { rtp_media->ip = *ip; } if(sdp_media->m_port) { rtp_media->port = (apr_port_t)sdp_media->m_port; rtp_media->state = MPF_MEDIA_ENABLED; } else { rtp_media->state = MPF_MEDIA_DISABLED; } return TRUE; } /** Generate MRCP control media by SDP media */ static apt_bool_t mrcp_control_media_generate(mrcp_control_descriptor_t *control_media, const sdp_media_t *sdp_media, const apt_str_t *ip, apr_pool_t *pool) { mrcp_attrib_e id; apt_str_t name; apt_str_t value; sdp_attribute_t *attrib = NULL; apt_string_set(&name,sdp_media->m_proto_name); control_media->proto = mrcp_proto_find(&name); if(control_media->proto != MRCP_PROTO_TCP) { apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Not supported SDP Proto [%s], expected [%s]",sdp_media->m_proto_name,mrcp_proto_get(MRCP_PROTO_TCP)->buf); return FALSE; } for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { apt_string_set(&name,attrib->a_name); id = mrcp_attrib_id_find(&name); switch(id) { case MRCP_ATTRIB_SETUP: apt_string_set(&value,attrib->a_value); control_media->setup_type = mrcp_setup_type_find(&value); break; case MRCP_ATTRIB_CONNECTION: apt_string_set(&value,attrib->a_value); control_media->connection_type = mrcp_connection_type_find(&value); break; case MRCP_ATTRIB_RESOURCE: apt_string_assign(&control_media->resource_name,attrib->a_value,pool); break; case MRCP_ATTRIB_CHANNEL: apt_string_set(&value,attrib->a_value); apt_id_resource_parse(&value,'@',&control_media->session_id,&control_media->resource_name,pool); break; case MRCP_ATTRIB_CMID: mrcp_cmid_add(control_media->cmid_arr,atoi(attrib->a_value)); break; default: break; } } if(sdp_media->m_connections) { apt_string_assign(&control_media->ip,sdp_media->m_connections->c_address,pool); } else { control_media->ip = *ip; } control_media->port = (apr_port_t)sdp_media->m_port; return TRUE; } /** Generate SDP resource discovery string */ MRCP_DECLARE(apr_size_t) sdp_resource_discovery_string_generate(const char *ip, const char *origin, char *buffer, apr_size_t size) { apr_size_t offset = 0; if(!ip) { ip = "0.0.0.0"; } if(!origin) { origin = "-"; } buffer[0] = '\0'; offset += snprintf(buffer+offset,size-offset, "v=0\r\n" "o=%s 0 0 IN IP4 %s\r\n" "s=-\r\n" "c=IN IP4 %s\r\n" "t=0 0\r\n" "m=application 0 TCP/MRCPv2 1\r\n" "a=resource:speechsynth\r\n" "a=resource:speechrecog\r\n" "m=audio 0 RTP/AVP 0 8 96 101\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n" "a=rtpmap:96 L16/8000\r\n" "a=rtpmap:101 telephone-event/8000\r\n", origin, ip, ip); return offset; }