freeswitch/libs/unimrcp/modules/mrcp-sofiasip/src/mrcp_sdp.c

416 lines
14 KiB
C

/*
* 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 <stdlib.h>
#include <apr_general.h>
#include <sofia-sip/sdp.h>
#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; i<count; i++) {
audio_media = mrcp_session_audio_media_get(descriptor,audio_index);
if(audio_media && audio_media->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->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; i<descriptor_arr->nelts; 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; i<descriptor_arr->nelts; 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; i<control_media->cmid_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;
}