/* * Copyright 2008 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. */ #include #include #include "mrcp_sdp.h" #include "mrcp_session_descriptor.h" #include "mrcp_control_descriptor.h" #include "mpf_rtp_attribs.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; ibase.id == 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->base.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(mrcp_session_descriptor_t*) mrcp_descriptor_generate_by_sdp_session(const sdp_session_t *sdp, const char *force_destination_ip, apr_pool_t *pool) { sdp_media_t *sdp_media; mrcp_session_descriptor_t *descriptor; if(!sdp) { apt_log(APT_LOG_MARK,APT_PRIO_WARNING,"Invalid SDP Message"); return NULL; } descriptor = mrcp_session_descriptor_create(pool); 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->base.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->base.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 = apr_palloc(pool,sizeof(mrcp_control_descriptor_t)); mrcp_control_descriptor_init(control_media); 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 descriptor; } /** 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; int i; mpf_codec_descriptor_t *codec_descriptor; apr_array_header_t *descriptor_arr = audio_media->codec_list.descriptor_arr; if(!descriptor_arr) { return 0; } offset += snprintf(buffer+offset,size-offset, "m=audio %d RTP/AVP", audio_media->base.state == MPF_MEDIA_ENABLED ? audio_media->base.port : 0); for(i=0; inelts; i++) { codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; if(codec_descriptor->enabled == TRUE) { offset += snprintf(buffer+offset,size-offset," %d", codec_descriptor->payload_type); } } offset += snprintf(buffer+offset,size-offset,"\r\n"); if(descriptor->ip.length && audio_media->base.ip.length && apt_string_compare(&descriptor->ip,&audio_media->base.ip) != TRUE) { const char *media_ip = audio_media->base.ext_ip.buf ? audio_media->base.ext_ip.buf : audio_media->base.ip.buf; offset += sprintf(buffer+offset,"c=IN IP4 %s\r\n",media_ip); } if(audio_media->base.state == MPF_MEDIA_ENABLED) { const apt_str_t *mode_str = mpf_stream_mode_str_get(audio_media->mode); for(i=0; inelts; i++) { codec_descriptor = (mpf_codec_descriptor_t*)descriptor_arr->elts + i; 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); } } offset += snprintf(buffer+offset,size-offset,"a=%s\r\n",mode_str ? mode_str->buf : ""); if(audio_media->ptime) { offset += snprintf(buffer+offset,size-offset,"a=ptime:%hu\r\n",audio_media->ptime); } } offset += snprintf(buffer+offset,size-offset,"a=mid:%d\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) { 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" "a=cmid:%d\r\n", control_media->port, proto ? proto->buf : "", setup_type ? setup_type->buf : "", connection_type ? connection_type->buf : "", control_media->resource_name.buf, control_media->cmid); } else { offset += snprintf(buffer+offset,size-offset, "m=application %d %s 1\r\n" "a=resource:%s\r\n" "a=cmid:%d\r\n", control_media->port, proto ? proto->buf : "", control_media->resource_name.buf, control_media->cmid); } } else { /* answer */ if(control_media->port) { offset += sprintf(buffer+offset, "m=application %d %s 1\r\n" "a=setup:%s\r\n" "a=connection:%s\r\n" "a=channel:%s@%s\r\n" "a=cmid:%d\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, control_media->cmid); } else { offset += sprintf(buffer+offset, "m=application %d %s 1\r\n" "a=channel:%s@%s\r\n" "a=cmid:%d\r\n", control_media->port, proto ? proto->buf : "", control_media->session_id.buf, control_media->resource_name.buf, control_media->cmid); } } 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->mode = STREAM_MODE_NONE; break; case sdp_sendonly: rtp_media->mode = STREAM_MODE_SEND; break; case sdp_recvonly: rtp_media->mode = STREAM_MODE_RECEIVE; break; case sdp_sendrecv: rtp_media->mode = STREAM_MODE_SEND_RECEIVE; break; } if(sdp_media->m_connections) { apt_string_assign(&rtp_media->base.ip,sdp_media->m_connections->c_address,pool); } else { rtp_media->base.ip = *ip; } if(sdp_media->m_port) { rtp_media->base.port = (apr_port_t)sdp_media->m_port; rtp_media->base.state = MPF_MEDIA_ENABLED; } else { rtp_media->base.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)); 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: control_media->cmid = 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\r\n" "a=rtpmap:0 PCMU/8000\r\n" "a=rtpmap:8 PCMA/8000\r\n", origin, ip, ip); return offset; }