/* * OpenMRCP - Open Source Media Resource Control Protocol Stack * Copyright (C) 2007, Cepstral LLC * * Version: MPL 1.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (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.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * Author(s): * Arsen Chaloyan * * Contributor(s): * */ #include #include #include "mrcp_description.h" #include "apt_text_stream.h" #define MRCP_CLIENT_SDP_ORIGIN "OpenMRCPClient" #define MRCP_SERVER_SDP_ORIGIN "OpenMRCPServer" /* generates sdp_media_audio (sdp string) by mrcp_media_audio */ static size_t mrcp_sdp_media_audio_generate(char *buffer, size_t size, mrcp_descriptor_t *descriptor, mrcp_media_audio_t *audio_media) { size_t i; size_t offset = 0; offset += sprintf(buffer+offset,"m=audio %d RTP/AVP", audio_media->base.state == MRCP_MEDIA_ENABLED ? audio_media->base.port : 0); for(i=0; icodec_count; i++) { offset += sprintf(buffer+offset," %d", audio_media->codec_list[i]->payload_type); } offset += sprintf(buffer+offset,"\r\n"); if(descriptor->ip && audio_media->base.ip && apt_str_compare(descriptor->ip,audio_media->base.ip) != TRUE) { offset += sprintf(buffer+offset,"c=IN IP4 %s\r\n",audio_media->base.ip); } if(audio_media->base.state == MRCP_MEDIA_ENABLED) { for(i=0; icodec_count; i++) { offset += sprintf(buffer+offset,"a=rtpmap:%d %s/%d\r\n", audio_media->codec_list[i]->payload_type, audio_media->codec_list[i]->name, audio_media->codec_list[i]->sampling_rate); } offset += sprintf(buffer+offset,"a=%s\r\n", mrcp_audio_direction_str_get(audio_media->direction)); if(audio_media->ptime) { offset += sprintf(buffer+offset,"a=ptime:%hu\r\n", audio_media->ptime); } } offset += sprintf(buffer+offset,"a=mid:%d\r\n",audio_media->mid); return offset; } /* generates offer (sdp string) by mrcp_descriptor */ size_t mrcp_sdp_offer_generate_by_mrcp_descriptor(char *buffer, size_t size, mrcp_descriptor_t *descriptor) { size_t i; size_t offset = 0; mrcp_media_control_t *control_media; mrcp_media_audio_t *audio_media; buffer[0] = '\0'; offset += sprintf(buffer+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", MRCP_CLIENT_SDP_ORIGIN, descriptor->ip, descriptor->ip); for(i=0;imedia_count; i++) { if(!descriptor->media[i]) { continue; } switch(descriptor->media[i]->type) { case MRCP_MEDIA_TYPE_CONTROL: control_media = (mrcp_media_control_t*)descriptor->media[i]; offset += sprintf(buffer+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->base.state == MRCP_MEDIA_ENABLED) ? control_media->base.port : 0, mrcp_proto_get(control_media->proto), mrcp_control_setup_type_get(control_media->setup_type), mrcp_control_connection_type_get(control_media->connection_type), control_media->resource_name, control_media->cmid); break; case MRCP_MEDIA_TYPE_AUDIO: audio_media = (mrcp_media_audio_t*)descriptor->media[i]; offset += mrcp_sdp_media_audio_generate(buffer+offset,size-offset,descriptor,audio_media); break; } } return offset; } /* generates answer (sdp string) by mrcp_descriptor */ size_t mrcp_sdp_answer_generate_by_mrcp_descriptor(char *buffer, size_t size, mrcp_descriptor_t *descriptor) { size_t i; size_t offset = 0; mrcp_media_control_t *control_media; mrcp_media_audio_t *audio_media; buffer[0] = '\0'; offset += sprintf(buffer+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", MRCP_SERVER_SDP_ORIGIN, descriptor->ip, descriptor->ip); for(i=0;imedia_count; i++) { if(!descriptor->media[i]) { continue; } switch(descriptor->media[i]->type) { case MRCP_MEDIA_TYPE_CONTROL: control_media = (mrcp_media_control_t*)descriptor->media[i]; 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->base.state == MRCP_MEDIA_ENABLED) ? control_media->base.port : 0, mrcp_proto_get(control_media->proto), mrcp_control_setup_type_get(control_media->setup_type), mrcp_control_connection_type_get(control_media->connection_type), control_media->session_id.hex_str, control_media->resource_name, control_media->cmid); break; case MRCP_MEDIA_TYPE_AUDIO: audio_media = (mrcp_media_audio_t*)descriptor->media[i]; offset += mrcp_sdp_media_audio_generate(buffer+offset,size-offset,descriptor,audio_media); break; } } return offset; } /* generates mrcp_media_control by sdp_media_application */ static mrcp_status_t mrcp_control_media_generate_by_sdp_media(mrcp_media_control_t *mrcp_media, sdp_media_t *sdp_media, apr_pool_t *pool) { mrcp_control_attrib_id id; sdp_attribute_t *attrib = NULL; mrcp_media->proto = mrcp_proto_find(sdp_media->m_proto_name); if(mrcp_media->proto != MRCP_PROTO_TCP) { apt_log(APT_PRIO_INFO,"Not supported SDP Proto [%s], expected [%s]\n",sdp_media->m_proto_name,mrcp_proto_get(MRCP_PROTO_TCP)); return MRCP_STATUS_FAILURE; } for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { id = mrcp_control_attrib_id_find(attrib->a_name); switch(id) { case MRCP_CONTROL_ATTRIB_SETUP: mrcp_media->setup_type = mrcp_control_setup_type_find(attrib->a_value); break; case MRCP_CONTROL_ATTRIB_CONNECTION: mrcp_media->connection_type = mrcp_control_connection_type_find(attrib->a_value); break; case MRCP_CONTROL_ATTRIB_RESOURCE: mrcp_media->resource_name = apr_pstrdup(pool,attrib->a_value); break; case MRCP_CONTROL_ATTRIB_CHANNEL: { char *str = apr_pstrdup(pool,attrib->a_value); mrcp_media->session_id.hex_str = apt_read_field(&str,'@',1); mrcp_media->session_id.length = str - mrcp_media->session_id.hex_str - 1; mrcp_media->resource_name = str; break; } case MRCP_CONTROL_ATTRIB_CMID: mrcp_media->cmid = atoi(attrib->a_value); break; default: break; } } return MRCP_STATUS_SUCCESS; } /* generates mrcp_media_audio by sdp_media_audio */ static mrcp_status_t mrcp_audio_media_generate_by_sdp_media(mrcp_media_audio_t *audio_media, sdp_media_t *sdp_media, apr_pool_t *pool) { mrcp_audio_attrib_id id; sdp_attribute_t *attrib = NULL; sdp_rtpmap_t *map; codec_descriptor_t *codec; for(attrib = sdp_media->m_attributes; attrib; attrib=attrib->a_next) { id = mrcp_audio_attrib_id_find(attrib->a_name); switch(id) { case MRCP_AUDIO_ATTRIB_MID: audio_media->mid = atoi(attrib->a_value); break; case MRCP_AUDIO_ATTRIB_PTIME: audio_media->ptime = (unsigned short)atoi(attrib->a_value); break; default: break; } } audio_media->codec_count = 0; for(map = sdp_media->m_rtpmaps; map; map = map->rm_next) { codec = apr_palloc(pool,sizeof(codec_descriptor_t)); codec_descriptor_init(codec); codec->payload_type = (apr_byte_t)map->rm_pt; codec->name = apr_pstrdup(pool,map->rm_encoding); codec->sampling_rate = (apr_uint16_t)map->rm_rate; codec->channel_count = 1; if(audio_media->codec_count < MRCP_MAX_CODEC_COUNT) { audio_media->codec_list[audio_media->codec_count++] = codec; } } switch(sdp_media->m_mode) { case sdp_inactive: audio_media->direction = MRCP_AUDIO_NONE; break; case sdp_sendonly: audio_media->direction = MRCP_AUDIO_SEND; break; case sdp_recvonly: audio_media->direction = MRCP_AUDIO_RECEIVE; break; case sdp_sendrecv: audio_media->direction = MRCP_AUDIO_DUPLEX; break; } return MRCP_STATUS_SUCCESS; } /* generates mrcp_media_base by sdp_media */ static mrcp_status_t mrcp_media_generate_by_sdp_media(mrcp_media_base_t *mrcp_media, sdp_media_t *sdp_media, const char *ip, apr_pool_t *pool) { if(sdp_media->m_connections) { mrcp_media->ip = apr_pstrdup(pool,sdp_media->m_connections->c_address); } else { mrcp_media->ip = ip; } if(sdp_media->m_port) { mrcp_media->port = (unsigned short)sdp_media->m_port; mrcp_media->state = MRCP_MEDIA_ENABLED; } else { mrcp_media->state = MRCP_MEDIA_DISABLED; } return MRCP_STATUS_SUCCESS; } /* generates mrcp_descriptor by sdp_session */ mrcp_status_t mrcp_descriptor_generate_by_sdp_session(mrcp_descriptor_t *descriptor, sdp_session_t *sdp, apr_pool_t *pool) { sdp_media_t *sdp_media; size_t i=0; mrcp_media_base_t *mrcp_media; if(sdp->sdp_connection) { descriptor->ip = apr_pstrdup(pool,sdp->sdp_connection->c_address); } for(sdp_media=sdp->sdp_media; sdp_media; sdp_media=sdp_media->m_next, i++) { if(i >= descriptor->media_count) { apt_log(APT_PRIO_INFO,"Media Count Mismatch Detected\n"); break; } mrcp_media = descriptor->media[i]; if(!mrcp_media) { continue; } switch(sdp_media->m_type) { case sdp_media_audio: if(mrcp_media->type == MRCP_MEDIA_TYPE_AUDIO) { mrcp_media_audio_t *audio_media = (mrcp_media_audio_t*)mrcp_media; mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool); } else { apt_log(APT_PRIO_INFO,"Unexpected SDP Media [%s]\n", sdp_media->m_type_name); mrcp_media = NULL; } break; case sdp_media_application: if(mrcp_media->type == MRCP_MEDIA_TYPE_CONTROL) { mrcp_media_control_t *control_media = (mrcp_media_control_t*)mrcp_media; mrcp_control_media_generate_by_sdp_media(control_media,sdp_media,pool); } else { apt_log(APT_PRIO_INFO,"Unexpected SDP Media [%s]\n", sdp_media->m_type_name); mrcp_media = NULL; } break; default: apt_log(APT_PRIO_INFO,"Not Supported SDP Media [%s]\n", sdp_media->m_type_name); break; } if(mrcp_media) { mrcp_media_generate_by_sdp_media(mrcp_media,sdp_media,descriptor->ip,pool); } } return MRCP_STATUS_SUCCESS; } /* generates mrcp_descriptor by sdp offer */ mrcp_status_t mrcp_descriptor_generate_by_sdp_offer(mrcp_descriptor_t *descriptor, sdp_session_t *sdp, apr_pool_t *pool) { sdp_media_t *sdp_media; size_t i=0; mrcp_media_base_t *mrcp_media; if(sdp->sdp_connection) { descriptor->ip = apr_pstrdup(pool,sdp->sdp_connection->c_address); } /* updated media */ for(sdp_media=sdp->sdp_media; sdp_media && imedia_count; sdp_media=sdp_media->m_next, i++) { mrcp_media = descriptor->media[i]; if(!mrcp_media) { continue; } switch(sdp_media->m_type) { case sdp_media_audio: if(mrcp_media->type == MRCP_MEDIA_TYPE_AUDIO) { mrcp_media_audio_t *audio_media = (mrcp_media_audio_t*)mrcp_media; mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool); } else { apt_log(APT_PRIO_INFO,"Unexpected SDP Media [%s]\n", sdp_media->m_type_name); mrcp_media = NULL; } break; case sdp_media_application: if(mrcp_media->type == MRCP_MEDIA_TYPE_CONTROL) { mrcp_media_control_t *control_media = (mrcp_media_control_t*)mrcp_media; mrcp_control_media_generate_by_sdp_media(control_media,sdp_media,pool); } else { apt_log(APT_PRIO_INFO,"Unexpected SDP Media [%s]\n", sdp_media->m_type_name); mrcp_media = NULL; } break; default: apt_log(APT_PRIO_INFO,"Not Supported SDP Media [%s]\n", sdp_media->m_type_name); break; } if(mrcp_media) { mrcp_media_generate_by_sdp_media(mrcp_media,sdp_media,descriptor->ip,pool); } } /* new media */ for(; sdp_media; sdp_media=sdp_media->m_next) { mrcp_media = NULL; switch(sdp_media->m_type) { case sdp_media_audio: { mrcp_media_audio_t *audio_media = mrcp_audio_media_create(pool); mrcp_media = &audio_media->base; mrcp_descriptor_media_add(descriptor,mrcp_media); mrcp_audio_media_generate_by_sdp_media(audio_media,sdp_media,pool); break; } case sdp_media_application: { mrcp_media_control_t *control_media = mrcp_control_media_create(pool); mrcp_media = &control_media->base; mrcp_descriptor_media_add(descriptor,mrcp_media); mrcp_control_media_generate_by_sdp_media(control_media,sdp_media,pool); break; } default: apt_log(APT_PRIO_INFO,"Not Supported SDP Media [%s]\n", sdp_media->m_type_name); break; } if(mrcp_media) { mrcp_media_generate_by_sdp_media(mrcp_media,sdp_media,descriptor->ip,pool); } } return MRCP_STATUS_SUCCESS; } /* generates resource discover answer (sdp string) by mrcp_description */ size_t mrcp_resource_discover_sdp_generate(char *buffer, size_t size, mrcp_descriptor_t *descriptor) { size_t i; size_t offset = 0; mrcp_status_t initiated = MRCP_STATUS_FAILURE; mrcp_media_control_t *control_media = NULL; mrcp_media_audio_t *audio_media = NULL; buffer[0] = '\0'; offset += sprintf(buffer+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", MRCP_SERVER_SDP_ORIGIN, descriptor->ip, descriptor->ip); for(i=0;imedia_count; i++) { if(!descriptor->media[i]) { continue; } if(descriptor->media[i]->type == MRCP_MEDIA_TYPE_CONTROL) { control_media = (mrcp_media_control_t*)descriptor->media[i]; if(initiated == MRCP_STATUS_FAILURE) { initiated = MRCP_STATUS_SUCCESS; offset += sprintf(buffer+offset, "m=application 0 %s 1\r\n", mrcp_proto_get(control_media->proto)); } offset += sprintf(buffer+offset, "a=resource:%s\r\n", control_media->resource_name); } else if(descriptor->media[i]->type == MRCP_MEDIA_TYPE_AUDIO) { audio_media = (mrcp_media_audio_t*)descriptor->media[i]; } } if(audio_media) { offset += sprintf(buffer+offset,"m=audio 0 RTP/AVP 0 8\r\n"); } return offset; }