mirror of
https://github.com/signalwire/freeswitch.git
synced 2026-10-31 23:51:10 +00:00
inital checkin of unimrcp svn revision 985 from http://unimrcp.googlecode.com/svn/trunk/
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@13790 d0543943-73ff-0310-b7d9-9358b9ac24b2
This commit is contained in:
1 parent
ec90221783
commit
da925bddf8
349 files changed
+47866
No files matched your search
@@ -0,0 +1,395 @@
|
||||
/*
|
||||
* 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 <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 "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->base.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; i<descriptor_arr->nelts; 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; i<descriptor_arr->nelts; 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;
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct mrcp_sofia_agent_t mrcp_sofia_agent_t;
|
||||
#define NUA_MAGIC_T mrcp_sofia_agent_t
|
||||
|
||||
typedef struct mrcp_sofia_session_t mrcp_sofia_session_t;
|
||||
#define NUA_HMAGIC_T mrcp_sofia_session_t
|
||||
|
||||
#include <apr_general.h>
|
||||
#include <sofia-sip/su.h>
|
||||
#include <sofia-sip/nua.h>
|
||||
#include <sofia-sip/sip_status.h>
|
||||
#include <sofia-sip/sdp.h>
|
||||
#include <sofia-sip/sofia_features.h>
|
||||
|
||||
#include "mrcp_sofiasip_client_agent.h"
|
||||
#include "mrcp_session.h"
|
||||
#include "mrcp_session_descriptor.h"
|
||||
#include "mrcp_sdp.h"
|
||||
#include "apt_log.h"
|
||||
|
||||
#define SOFIA_TASK_NAME "SofiaSIP Agent"
|
||||
|
||||
struct mrcp_sofia_agent_t {
|
||||
mrcp_sig_agent_t *sig_agent;
|
||||
|
||||
mrcp_sofia_client_config_t *config;
|
||||
char *sip_contact_str;
|
||||
char *sip_to_str;
|
||||
char *sip_from_str;
|
||||
char *sip_bind_str;
|
||||
|
||||
su_root_t *root;
|
||||
nua_t *nua;
|
||||
};
|
||||
|
||||
struct mrcp_sofia_session_t {
|
||||
mrcp_session_t *session;
|
||||
su_home_t *home;
|
||||
nua_handle_t *nh;
|
||||
|
||||
apt_bool_t terminate_requested;
|
||||
apr_thread_mutex_t *mutex;
|
||||
};
|
||||
|
||||
/* Task Interface */
|
||||
static void mrcp_sofia_task_initialize(apt_task_t *task);
|
||||
static apt_bool_t mrcp_sofia_task_run(apt_task_t *task);
|
||||
static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task);
|
||||
|
||||
/* MRCP Signaling Interface */
|
||||
static apt_bool_t mrcp_sofia_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor);
|
||||
static apt_bool_t mrcp_sofia_session_terminate_request(mrcp_session_t *session);
|
||||
static apt_bool_t mrcp_sofia_session_discover_request(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor);
|
||||
|
||||
static const mrcp_session_request_vtable_t session_request_vtable = {
|
||||
mrcp_sofia_session_offer,
|
||||
mrcp_sofia_session_terminate_request,
|
||||
NULL,
|
||||
mrcp_sofia_session_discover_request
|
||||
};
|
||||
|
||||
static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_client_config_t *config, apr_pool_t *pool);
|
||||
static apt_bool_t mrcp_sofia_session_create(mrcp_session_t *session);
|
||||
|
||||
static void mrcp_sofia_event_callback( nua_event_t nua_event,
|
||||
int status,
|
||||
char const *phrase,
|
||||
nua_t *nua,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[]);
|
||||
|
||||
|
||||
/** Create Sofia-SIP Signaling Agent */
|
||||
MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_client_agent_create(mrcp_sofia_client_config_t *config, apr_pool_t *pool)
|
||||
{
|
||||
apt_task_t *task;
|
||||
apt_task_vtable_t *vtable;
|
||||
mrcp_sofia_agent_t *sofia_agent;
|
||||
sofia_agent = apr_palloc(pool,sizeof(mrcp_sofia_agent_t));
|
||||
sofia_agent->sig_agent = mrcp_signaling_agent_create(sofia_agent,MRCP_VERSION_2,pool);
|
||||
sofia_agent->sig_agent->create_client_session = mrcp_sofia_session_create;
|
||||
sofia_agent->root = NULL;
|
||||
sofia_agent->nua = NULL;
|
||||
|
||||
if(mrcp_sofia_config_validate(sofia_agent,config,pool) == FALSE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task = apt_task_create(sofia_agent,NULL,pool);
|
||||
if(!task) {
|
||||
return NULL;
|
||||
}
|
||||
apt_task_name_set(task,SOFIA_TASK_NAME);
|
||||
vtable = apt_task_vtable_get(task);
|
||||
if(vtable) {
|
||||
vtable->on_pre_run = mrcp_sofia_task_initialize;
|
||||
vtable->run = mrcp_sofia_task_run;
|
||||
vtable->terminate = mrcp_sofia_task_terminate;
|
||||
}
|
||||
sofia_agent->sig_agent->task = task;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "SOFIA_TASK_NAME" ["SOFIA_SIP_VERSION"] %s:%hu -> %s:%hu %s",
|
||||
config->local_ip,config->local_port,
|
||||
config->remote_ip,config->remote_port,
|
||||
config->transport ? config->transport : "");
|
||||
return sofia_agent->sig_agent;
|
||||
}
|
||||
|
||||
/** Allocate Sofia-SIP config */
|
||||
MRCP_DECLARE(mrcp_sofia_client_config_t*) mrcp_sofiasip_client_config_alloc(apr_pool_t *pool)
|
||||
{
|
||||
mrcp_sofia_client_config_t *config = apr_palloc(pool,sizeof(mrcp_sofia_client_config_t));
|
||||
config->local_ip = NULL;
|
||||
config->ext_ip = NULL;
|
||||
config->local_port = 0;
|
||||
config->local_user_name = NULL;
|
||||
config->remote_ip = NULL;
|
||||
config->remote_port = 0;
|
||||
config->remote_user_name = NULL;
|
||||
|
||||
config->force_destination = FALSE;
|
||||
|
||||
config->user_agent_name = NULL;
|
||||
config->origin = NULL;
|
||||
config->transport = NULL;
|
||||
return config;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_client_config_t *config, apr_pool_t *pool)
|
||||
{
|
||||
const char *local_ip = config->ext_ip ? config->ext_ip : config->local_ip;
|
||||
if(!config->local_ip || !config->remote_ip) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sofia_agent->config = config;
|
||||
sofia_agent->sip_contact_str = apr_psprintf(pool,"sip:%s:%d", local_ip, config->local_port);
|
||||
sofia_agent->sip_from_str = apr_psprintf(pool,"sip:%s:%d", local_ip, config->local_port);
|
||||
|
||||
if(config->remote_user_name && config->remote_user_name != '\0') {
|
||||
sofia_agent->sip_to_str = apr_psprintf(pool,"sip:%s@%s:%d",
|
||||
config->remote_user_name,
|
||||
config->remote_ip,
|
||||
config->remote_port);
|
||||
}
|
||||
else {
|
||||
sofia_agent->sip_to_str = apr_psprintf(pool,"sip:%s:%d",
|
||||
config->remote_ip,
|
||||
config->remote_port);
|
||||
}
|
||||
if(config->transport) {
|
||||
sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d;transport=%s",
|
||||
config->local_ip,
|
||||
config->local_port,
|
||||
config->transport);
|
||||
}
|
||||
else {
|
||||
sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d",
|
||||
config->local_ip,
|
||||
config->local_port);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void mrcp_sofia_task_initialize(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
|
||||
/* Initialize Sofia-SIP library and create event loop */
|
||||
su_init();
|
||||
sofia_agent->root = su_root_create(NULL);
|
||||
|
||||
/* Create a user agent instance. The stack will call the 'event_callback()'
|
||||
* callback when events such as succesful registration to network,
|
||||
* an incoming call, etc, occur.
|
||||
*/
|
||||
sofia_agent->nua = nua_create(
|
||||
sofia_agent->root, /* Event loop */
|
||||
mrcp_sofia_event_callback, /* Callback for processing events */
|
||||
sofia_agent, /* Additional data to pass to callback */
|
||||
NUTAG_URL(sofia_agent->sip_bind_str), /* Address to bind to */
|
||||
TAG_END()); /* Last tag should always finish the sequence */
|
||||
if(sofia_agent->nua) {
|
||||
nua_set_params(
|
||||
sofia_agent->nua,
|
||||
NUTAG_AUTOANSWER(0),
|
||||
NUTAG_APPL_METHOD("OPTIONS"),
|
||||
SIPTAG_USER_AGENT_STR(sofia_agent->config->user_agent_name),
|
||||
TAG_END());
|
||||
}
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_task_run(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
|
||||
if(sofia_agent->nua) {
|
||||
/* Run event loop */
|
||||
su_root_run(sofia_agent->root);
|
||||
|
||||
/* Destroy allocated resources */
|
||||
nua_destroy(sofia_agent->nua);
|
||||
sofia_agent->nua = NULL;
|
||||
}
|
||||
su_root_destroy(sofia_agent->root);
|
||||
sofia_agent->root = NULL;
|
||||
su_deinit();
|
||||
|
||||
apt_task_child_terminate(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
if(sofia_agent->nua) {
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Send Shutdown Signal to NUA");
|
||||
nua_shutdown(sofia_agent->nua);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static APR_INLINE mrcp_sofia_agent_t* mrcp_sofia_agent_get(mrcp_session_t *session)
|
||||
{
|
||||
return session->signaling_agent->obj;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_create(mrcp_session_t *session)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(session);
|
||||
mrcp_sofia_session_t *sofia_session;
|
||||
session->request_vtable = &session_request_vtable;
|
||||
|
||||
sofia_session = apr_palloc(session->pool,sizeof(mrcp_sofia_session_t));
|
||||
sofia_session->mutex = NULL;
|
||||
sofia_session->home = su_home_new(sizeof(*sofia_session->home));
|
||||
sofia_session->session = session;
|
||||
sofia_session->terminate_requested = FALSE;
|
||||
session->obj = sofia_session;
|
||||
|
||||
sofia_session->nh = nua_handle(
|
||||
sofia_agent->nua,
|
||||
sofia_session,
|
||||
SIPTAG_TO_STR(sofia_agent->sip_to_str),
|
||||
SIPTAG_FROM_STR(sofia_agent->sip_from_str),
|
||||
SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str),
|
||||
TAG_END());
|
||||
|
||||
apr_thread_mutex_create(&sofia_session->mutex,APR_THREAD_MUTEX_DEFAULT,session->pool);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_destroy(mrcp_sofia_session_t *sofia_session)
|
||||
{
|
||||
if(sofia_session->mutex) {
|
||||
apr_thread_mutex_destroy(sofia_session->mutex);
|
||||
sofia_session->mutex = NULL;
|
||||
}
|
||||
if(sofia_session->home) {
|
||||
su_home_unref(sofia_session->home);
|
||||
sofia_session->home = NULL;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_unref(mrcp_sofia_session_t *sofia_session)
|
||||
{
|
||||
if(sofia_session->nh) {
|
||||
nua_handle_bind(sofia_session->nh, NULL);
|
||||
nua_handle_destroy(sofia_session->nh);
|
||||
sofia_session->nh = NULL;
|
||||
}
|
||||
sofia_session->session = NULL;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_offer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor)
|
||||
{
|
||||
char sdp_str[2048];
|
||||
char *local_sdp_str = NULL;
|
||||
apt_bool_t res = FALSE;
|
||||
mrcp_sofia_session_t *sofia_session = session->obj;
|
||||
if(!sofia_session) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(session->signaling_agent) {
|
||||
mrcp_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(session);
|
||||
if(sofia_agent) {
|
||||
if(sofia_agent->config->origin) {
|
||||
apt_string_set(&descriptor->origin,sofia_agent->config->origin);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(sdp_string_generate_by_mrcp_descriptor(sdp_str,sizeof(sdp_str),descriptor,TRUE) > 0) {
|
||||
local_sdp_str = sdp_str;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Local SDP "APT_PTRSID_FMT"\n%s",
|
||||
MRCP_SESSION_PTRSID(session),
|
||||
local_sdp_str);
|
||||
}
|
||||
|
||||
apr_thread_mutex_lock(sofia_session->mutex);
|
||||
|
||||
if(sofia_session->nh) {
|
||||
res = TRUE;
|
||||
nua_invite(sofia_session->nh,
|
||||
TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)),
|
||||
TAG_END());
|
||||
}
|
||||
|
||||
apr_thread_mutex_unlock(sofia_session->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_terminate_request(mrcp_session_t *session)
|
||||
{
|
||||
mrcp_sofia_session_t *sofia_session = session->obj;
|
||||
if(!sofia_session) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
sofia_session->terminate_requested = FALSE;
|
||||
apr_thread_mutex_lock(sofia_session->mutex);
|
||||
if(sofia_session->nh) {
|
||||
sofia_session->terminate_requested = TRUE;
|
||||
nua_bye(sofia_session->nh,TAG_END());
|
||||
}
|
||||
apr_thread_mutex_unlock(sofia_session->mutex);
|
||||
|
||||
if(sofia_session->terminate_requested == FALSE) {
|
||||
mrcp_sofia_session_destroy(sofia_session);
|
||||
mrcp_session_terminate_response(session);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_session_discover_request(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor)
|
||||
{
|
||||
apt_bool_t res = FALSE;
|
||||
mrcp_sofia_session_t *sofia_session = session->obj;
|
||||
if(!sofia_session) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
apr_thread_mutex_lock(sofia_session->mutex);
|
||||
if(sofia_session->nh) {
|
||||
res = TRUE;
|
||||
nua_options(sofia_session->nh,TAG_END());
|
||||
}
|
||||
apr_thread_mutex_unlock(sofia_session->mutex);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_session_ready(
|
||||
int status,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
mrcp_session_t *session = sofia_session->session;
|
||||
if(session) {
|
||||
const char *local_sdp_str = NULL, *remote_sdp_str = NULL;
|
||||
mrcp_session_descriptor_t *descriptor = NULL;
|
||||
|
||||
tl_gets(tags,
|
||||
SOATAG_LOCAL_SDP_STR_REF(local_sdp_str),
|
||||
SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
|
||||
TAG_END());
|
||||
|
||||
if(remote_sdp_str) {
|
||||
sdp_parser_t *parser = NULL;
|
||||
sdp_session_t *sdp = NULL;
|
||||
const char *force_destination_ip = NULL;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP "APT_PTRSID_FMT"\n%s",
|
||||
MRCP_SESSION_PTRSID(session),
|
||||
remote_sdp_str);
|
||||
|
||||
parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
|
||||
sdp = sdp_session(parser);
|
||||
if(sofia_agent && sofia_agent->config->force_destination == TRUE) {
|
||||
force_destination_ip = sofia_agent->config->remote_ip;
|
||||
}
|
||||
descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,force_destination_ip,session->pool);
|
||||
sdp_parser_free(parser);
|
||||
}
|
||||
|
||||
mrcp_session_answer(session,descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_session_terminate(
|
||||
int status,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
mrcp_session_t *session = sofia_session->session;
|
||||
if(session) {
|
||||
apt_bool_t terminate_requested;
|
||||
|
||||
apr_thread_mutex_lock(sofia_session->mutex);
|
||||
terminate_requested = sofia_session->terminate_requested;
|
||||
session = sofia_session->session;
|
||||
mrcp_sofia_session_unref(sofia_session);
|
||||
apr_thread_mutex_unlock(sofia_session->mutex);
|
||||
|
||||
if(terminate_requested == TRUE) {
|
||||
mrcp_sofia_session_destroy(sofia_session);
|
||||
mrcp_session_terminate_response(session);
|
||||
}
|
||||
else {
|
||||
mrcp_session_terminate_event(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_state_change(
|
||||
int status,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
int ss_state = nua_callstate_init;
|
||||
tl_gets(tags,
|
||||
NUTAG_CALLSTATE_REF(ss_state),
|
||||
TAG_END());
|
||||
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"SIP Call State "APT_PTR_FMT" [%s]",
|
||||
sofia_session ? MRCP_SESSION_PTR(sofia_session->session) : NULL,
|
||||
nua_callstate_name(ss_state));
|
||||
|
||||
switch(ss_state) {
|
||||
case nua_callstate_ready:
|
||||
mrcp_sofia_on_session_ready(status,sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_callstate_terminated:
|
||||
mrcp_sofia_on_session_terminate(status,sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_resource_discover(
|
||||
int status,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
mrcp_session_t *session = sofia_session->session;
|
||||
if(session) {
|
||||
const char *remote_sdp_str = NULL;
|
||||
mrcp_session_descriptor_t *descriptor = NULL;
|
||||
|
||||
tl_gets(tags,
|
||||
SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
|
||||
TAG_END());
|
||||
|
||||
if(remote_sdp_str) {
|
||||
sdp_parser_t *parser = NULL;
|
||||
sdp_session_t *sdp = NULL;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP "APT_PTR_FMT"\n%s",
|
||||
MRCP_SESSION_PTR(session),
|
||||
remote_sdp_str);
|
||||
|
||||
parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
|
||||
sdp = sdp_session(parser);
|
||||
descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,session->pool);
|
||||
sdp_parser_free(parser);
|
||||
}
|
||||
|
||||
mrcp_session_discover_response(session,descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
/** This callback will be called by SIP stack to process incoming events */
|
||||
static void mrcp_sofia_event_callback(
|
||||
nua_event_t nua_event,
|
||||
int status,
|
||||
char const *phrase,
|
||||
nua_t *nua,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive SIP Event [%s] Status %d %s",
|
||||
nua_event_name(nua_event),status,phrase);
|
||||
|
||||
switch(nua_event) {
|
||||
case nua_i_state:
|
||||
mrcp_sofia_on_state_change(status,sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_r_options:
|
||||
mrcp_sofia_on_resource_discover(status,sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_r_shutdown:
|
||||
/* break main loop of sofia thread */
|
||||
su_root_break(sofia_agent->root);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
typedef struct mrcp_sofia_agent_t mrcp_sofia_agent_t;
|
||||
#define NUA_MAGIC_T mrcp_sofia_agent_t
|
||||
|
||||
typedef struct mrcp_sofia_session_t mrcp_sofia_session_t;
|
||||
#define NUA_HMAGIC_T mrcp_sofia_session_t
|
||||
|
||||
#include <apr_general.h>
|
||||
#include <sofia-sip/su.h>
|
||||
#include <sofia-sip/nua.h>
|
||||
#include <sofia-sip/sip_status.h>
|
||||
#include <sofia-sip/sdp.h>
|
||||
#include <sofia-sip/sofia_features.h>
|
||||
|
||||
#include "mrcp_sofiasip_server_agent.h"
|
||||
#include "mrcp_session.h"
|
||||
#include "mrcp_session_descriptor.h"
|
||||
#include "mrcp_sdp.h"
|
||||
#include "apt_log.h"
|
||||
|
||||
#define SOFIA_TASK_NAME "SofiaSIP Agent"
|
||||
|
||||
struct mrcp_sofia_agent_t {
|
||||
mrcp_sig_agent_t *sig_agent;
|
||||
|
||||
mrcp_sofia_server_config_t *config;
|
||||
char *sip_contact_str;
|
||||
char *sip_bind_str;
|
||||
|
||||
su_root_t *root;
|
||||
nua_t *nua;
|
||||
};
|
||||
|
||||
struct mrcp_sofia_session_t {
|
||||
mrcp_session_t *session;
|
||||
su_home_t *home;
|
||||
nua_handle_t *nh;
|
||||
};
|
||||
|
||||
/* Task Interface */
|
||||
static void mrcp_sofia_task_initialize(apt_task_t *task);
|
||||
static apt_bool_t mrcp_sofia_task_run(apt_task_t *task);
|
||||
static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task);
|
||||
|
||||
/* MRCP Signaling Interface */
|
||||
static apt_bool_t mrcp_sofia_on_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor);
|
||||
static apt_bool_t mrcp_sofia_on_session_terminate(mrcp_session_t *session);
|
||||
|
||||
static const mrcp_session_response_vtable_t session_response_vtable = {
|
||||
mrcp_sofia_on_session_answer,
|
||||
mrcp_sofia_on_session_terminate
|
||||
};
|
||||
|
||||
static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_server_config_t *config, apr_pool_t *pool);
|
||||
|
||||
static void mrcp_sofia_event_callback( nua_event_t nua_event,
|
||||
int status,
|
||||
char const *phrase,
|
||||
nua_t *nua,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[]);
|
||||
|
||||
|
||||
/** Create Sofia-SIP Signaling Agent */
|
||||
MRCP_DECLARE(mrcp_sig_agent_t*) mrcp_sofiasip_server_agent_create(mrcp_sofia_server_config_t *config, apr_pool_t *pool)
|
||||
{
|
||||
apt_task_t *task;
|
||||
apt_task_vtable_t *vtable;
|
||||
mrcp_sofia_agent_t *sofia_agent;
|
||||
sofia_agent = apr_palloc(pool,sizeof(mrcp_sofia_agent_t));
|
||||
sofia_agent->sig_agent = mrcp_signaling_agent_create(sofia_agent,MRCP_VERSION_2,pool);
|
||||
sofia_agent->config = config;
|
||||
sofia_agent->root = NULL;
|
||||
sofia_agent->nua = NULL;
|
||||
|
||||
if(mrcp_sofia_config_validate(sofia_agent,config,pool) == FALSE) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
task = apt_task_create(sofia_agent,NULL,pool);
|
||||
if(!task) {
|
||||
return NULL;
|
||||
}
|
||||
apt_task_name_set(task,SOFIA_TASK_NAME);
|
||||
vtable = apt_task_vtable_get(task);
|
||||
if(vtable) {
|
||||
vtable->on_pre_run = mrcp_sofia_task_initialize;
|
||||
vtable->run = mrcp_sofia_task_run;
|
||||
vtable->terminate = mrcp_sofia_task_terminate;
|
||||
}
|
||||
sofia_agent->sig_agent->task = task;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Create "SOFIA_TASK_NAME" ["SOFIA_SIP_VERSION"] %s:%hu %s",
|
||||
config->local_ip,
|
||||
config->local_port,
|
||||
config->transport ? config->transport : "");
|
||||
return sofia_agent->sig_agent;
|
||||
}
|
||||
|
||||
/** Allocate Sofia-SIP config */
|
||||
MRCP_DECLARE(mrcp_sofia_server_config_t*) mrcp_sofiasip_server_config_alloc(apr_pool_t *pool)
|
||||
{
|
||||
mrcp_sofia_server_config_t *config = apr_palloc(pool,sizeof(mrcp_sofia_server_config_t));
|
||||
config->local_ip = NULL;
|
||||
config->ext_ip = NULL;
|
||||
config->local_port = 0;
|
||||
config->user_name = NULL;
|
||||
config->user_agent_name = NULL;
|
||||
config->origin = NULL;
|
||||
config->transport = NULL;
|
||||
config->force_destination = FALSE;
|
||||
return config;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_config_validate(mrcp_sofia_agent_t *sofia_agent, mrcp_sofia_server_config_t *config, apr_pool_t *pool)
|
||||
{
|
||||
const char *local_ip = config->ext_ip ? config->ext_ip : config->local_ip;
|
||||
sofia_agent->config = config;
|
||||
sofia_agent->sip_contact_str = apr_psprintf(pool,"sip:%s:%d",local_ip,config->local_port);
|
||||
if(config->transport) {
|
||||
sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d;transport=%s",
|
||||
config->local_ip,
|
||||
config->local_port,
|
||||
config->transport);
|
||||
}
|
||||
else {
|
||||
sofia_agent->sip_bind_str = apr_psprintf(pool,"sip:%s:%d",
|
||||
config->local_ip,
|
||||
config->local_port);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void mrcp_sofia_task_initialize(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
|
||||
/* Initialize Sofia-SIP library and create event loop */
|
||||
su_init();
|
||||
sofia_agent->root = su_root_create(NULL);
|
||||
|
||||
/* Create a user agent instance. The stack will call the 'event_callback()'
|
||||
* callback when events such as succesful registration to network,
|
||||
* an incoming call, etc, occur.
|
||||
*/
|
||||
sofia_agent->nua = nua_create(
|
||||
sofia_agent->root, /* Event loop */
|
||||
mrcp_sofia_event_callback, /* Callback for processing events */
|
||||
sofia_agent, /* Additional data to pass to callback */
|
||||
NUTAG_URL(sofia_agent->sip_bind_str), /* Address to bind to */
|
||||
TAG_END()); /* Last tag should always finish the sequence */
|
||||
if(sofia_agent->nua) {
|
||||
nua_set_params(
|
||||
sofia_agent->nua,
|
||||
NUTAG_AUTOANSWER(0),
|
||||
NUTAG_APPL_METHOD("OPTIONS"),
|
||||
SIPTAG_USER_AGENT_STR(sofia_agent->config->user_agent_name),
|
||||
TAG_END());
|
||||
}
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_task_run(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
|
||||
if(sofia_agent->nua) {
|
||||
/* Run event loop */
|
||||
su_root_run(sofia_agent->root);
|
||||
|
||||
/* Destroy allocated resources */
|
||||
nua_destroy(sofia_agent->nua);
|
||||
sofia_agent->nua = NULL;
|
||||
}
|
||||
su_root_destroy(sofia_agent->root);
|
||||
sofia_agent->root = NULL;
|
||||
su_deinit();
|
||||
|
||||
apt_task_child_terminate(task);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_task_terminate(apt_task_t *task)
|
||||
{
|
||||
mrcp_sofia_agent_t *sofia_agent = apt_task_object_get(task);
|
||||
if(sofia_agent->nua) {
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_DEBUG,"Send Shutdown Signal to NUA");
|
||||
nua_shutdown(sofia_agent->nua);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static mrcp_sofia_session_t* mrcp_sofia_session_create(mrcp_sofia_agent_t *sofia_agent, nua_handle_t *nh)
|
||||
{
|
||||
mrcp_sofia_session_t *sofia_session;
|
||||
mrcp_session_t* session = sofia_agent->sig_agent->create_server_session(sofia_agent->sig_agent);
|
||||
if(!session) {
|
||||
return NULL;
|
||||
}
|
||||
session->response_vtable = &session_response_vtable;
|
||||
session->event_vtable = NULL;
|
||||
|
||||
sofia_session = apr_palloc(session->pool,sizeof(mrcp_sofia_session_t));
|
||||
sofia_session->home = su_home_new(sizeof(*sofia_session->home));
|
||||
sofia_session->session = session;
|
||||
session->obj = sofia_session;
|
||||
|
||||
nua_handle_bind(nh, sofia_session);
|
||||
sofia_session->nh = nh;
|
||||
return sofia_session;
|
||||
}
|
||||
|
||||
static int sip_status_get(mrcp_session_status_e status)
|
||||
{
|
||||
switch (status) {
|
||||
case MRCP_SESSION_STATUS_OK:
|
||||
return 200;
|
||||
case MRCP_SESSION_STATUS_NO_SUCH_RESOURCE:
|
||||
return 404;
|
||||
case MRCP_SESSION_STATUS_UNACCEPTABLE_RESOURCE:
|
||||
return 406;
|
||||
case MRCP_SESSION_STATUS_UNAVAILABLE_RESOURCE:
|
||||
return 480;
|
||||
case MRCP_SESSION_STATUS_ERROR:
|
||||
return 500;
|
||||
}
|
||||
return 200;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_on_session_answer(mrcp_session_t *session, mrcp_session_descriptor_t *descriptor)
|
||||
{
|
||||
mrcp_sofia_session_t *sofia_session = session->obj;
|
||||
mrcp_sofia_agent_t *sofia_agent = session->signaling_agent->obj;
|
||||
const char *local_sdp_str = NULL;
|
||||
char sdp_str[2048];
|
||||
|
||||
if(!sofia_agent || !sofia_session || !sofia_session->nh) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if(descriptor->status != MRCP_SESSION_STATUS_OK) {
|
||||
int status = sip_status_get(descriptor->status);
|
||||
nua_respond(sofia_session->nh, status, sip_status_phrase(status),
|
||||
SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str),
|
||||
TAG_END());
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if(sofia_agent->config->origin) {
|
||||
apt_string_set(&descriptor->origin,sofia_agent->config->origin);
|
||||
}
|
||||
|
||||
if(sdp_string_generate_by_mrcp_descriptor(sdp_str,sizeof(sdp_str),descriptor,FALSE) > 0) {
|
||||
local_sdp_str = sdp_str;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Local SDP\n%s", local_sdp_str);
|
||||
}
|
||||
|
||||
nua_respond(sofia_session->nh, SIP_200_OK,
|
||||
SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str),
|
||||
TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)),
|
||||
NUTAG_AUTOANSWER(0),
|
||||
TAG_END());
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static apt_bool_t mrcp_sofia_on_session_terminate(mrcp_session_t *session)
|
||||
{
|
||||
mrcp_sofia_session_t *sofia_session = session->obj;
|
||||
if(sofia_session) {
|
||||
if(sofia_session->nh) {
|
||||
nua_handle_bind(sofia_session->nh, NULL);
|
||||
nua_handle_destroy(sofia_session->nh);
|
||||
}
|
||||
if(sofia_session->home) {
|
||||
su_home_unref(sofia_session->home);
|
||||
sofia_session->home = NULL;
|
||||
}
|
||||
sofia_session->session = NULL;
|
||||
}
|
||||
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"Destroy Session "APT_SID_FMT, MRCP_SESSION_SID(session));
|
||||
mrcp_session_destroy(session);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_call_receive(mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0;
|
||||
const char *local_sdp_str = NULL, *remote_sdp_str = NULL;
|
||||
mrcp_session_descriptor_t *descriptor = NULL;
|
||||
|
||||
tl_gets(tags,
|
||||
NUTAG_OFFER_RECV_REF(offer_recv),
|
||||
NUTAG_ANSWER_RECV_REF(answer_recv),
|
||||
NUTAG_OFFER_SENT_REF(offer_sent),
|
||||
NUTAG_ANSWER_SENT_REF(answer_sent),
|
||||
SOATAG_LOCAL_SDP_STR_REF(local_sdp_str),
|
||||
SOATAG_REMOTE_SDP_STR_REF(remote_sdp_str),
|
||||
TAG_END());
|
||||
|
||||
if(!sofia_session) {
|
||||
sofia_session = mrcp_sofia_session_create(sofia_agent,nh);
|
||||
if(!sofia_session) {
|
||||
nua_respond(nh, SIP_488_NOT_ACCEPTABLE, TAG_END());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(remote_sdp_str) {
|
||||
sdp_parser_t *parser = NULL;
|
||||
sdp_session_t *sdp = NULL;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Remote SDP\n%s", remote_sdp_str);
|
||||
|
||||
parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0);
|
||||
sdp = sdp_session(parser);
|
||||
descriptor = mrcp_descriptor_generate_by_sdp_session(sdp,NULL,sofia_session->session->pool);
|
||||
sdp_parser_free(parser);
|
||||
}
|
||||
|
||||
if(!descriptor) {
|
||||
nua_respond(nh, SIP_400_BAD_REQUEST, TAG_END());
|
||||
return;
|
||||
}
|
||||
|
||||
mrcp_session_offer(sofia_session->session,descriptor);
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_call_terminate(mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
if(sofia_session) {
|
||||
mrcp_session_terminate_request(sofia_session->session);
|
||||
}
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_state_change(mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
int ss_state = nua_callstate_init;
|
||||
tl_gets(tags,
|
||||
NUTAG_CALLSTATE_REF(ss_state),
|
||||
TAG_END());
|
||||
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_NOTICE,"SIP Call State [%s]", nua_callstate_name(ss_state));
|
||||
|
||||
switch(ss_state) {
|
||||
case nua_callstate_received:
|
||||
mrcp_sofia_on_call_receive(sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_callstate_terminated:
|
||||
mrcp_sofia_on_call_terminate(sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void mrcp_sofia_on_resource_discover(mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
char sdp_str[2048];
|
||||
char *local_sdp_str = NULL;
|
||||
|
||||
const char *ip = sofia_agent->config->ext_ip ?
|
||||
sofia_agent->config->ext_ip : sofia_agent->config->local_ip;
|
||||
|
||||
if(sdp_resource_discovery_string_generate(ip,sofia_agent->config->origin,sdp_str,sizeof(sdp_str)) > 0) {
|
||||
local_sdp_str = sdp_str;
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Resource Discovery SDP\n[%s]\n", local_sdp_str);
|
||||
}
|
||||
|
||||
nua_respond(nh, SIP_200_OK,
|
||||
NUTAG_WITH_CURRENT(sofia_agent->nua),
|
||||
SIPTAG_CONTACT_STR(sofia_agent->sip_contact_str),
|
||||
TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)),
|
||||
TAG_END());
|
||||
}
|
||||
|
||||
/** This callback will be called by SIP stack to process incoming events */
|
||||
static void mrcp_sofia_event_callback( nua_event_t nua_event,
|
||||
int status,
|
||||
char const *phrase,
|
||||
nua_t *nua,
|
||||
mrcp_sofia_agent_t *sofia_agent,
|
||||
nua_handle_t *nh,
|
||||
mrcp_sofia_session_t *sofia_session,
|
||||
sip_t const *sip,
|
||||
tagi_t tags[])
|
||||
{
|
||||
apt_log(APT_LOG_MARK,APT_PRIO_INFO,"Receive SIP Event [%s] Status %d %s",nua_event_name(nua_event),status,phrase);
|
||||
|
||||
switch(nua_event) {
|
||||
case nua_i_state:
|
||||
mrcp_sofia_on_state_change(sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_i_options:
|
||||
mrcp_sofia_on_resource_discover(sofia_agent,nh,sofia_session,sip,tags);
|
||||
break;
|
||||
case nua_r_shutdown:
|
||||
/* break main loop of sofia thread */
|
||||
su_root_break(sofia_agent->root);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user