/* * 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): * */ typedef struct mrcp_server_sofia_agent_t mrcp_server_sofia_agent_t; #define NUA_MAGIC_T mrcp_server_sofia_agent_t typedef struct mrcp_server_sofia_session_t mrcp_server_sofia_session_t; #define NUA_HMAGIC_T mrcp_server_sofia_session_t #include #ifdef _MSC_VER #undef strncasecmp #undef strcasecmp #endif #include #include #include #include #include "mrcp_server_signaling_agent.h" #include "apt_producer_task.h" struct mrcp_server_sofia_agent_t { mrcp_signaling_agent_t *signaling_agent; apt_producer_task_t *producer_task; su_root_t *root; nua_t *nua; apr_pool_t *pool; }; struct mrcp_server_sofia_session_t { mrcp_signaling_channel_t *channel; su_home_t *home; nua_handle_t *nh; }; typedef enum { MRCP_SOFIA_EVENT_AGENT_PROCESS, MRCP_SOFIA_EVENT_AGENT_STARTED, MRCP_SOFIA_EVENT_AGENT_TERMINATED } mrcp_sofia_event_id; typedef struct mrcp_sofia_event_t mrcp_sofia_event_t; struct mrcp_sofia_event_t { mrcp_sofia_event_id event_id; nua_event_t nua_event; int status; char const *phrase; nua_t *nua; mrcp_server_sofia_agent_t *sofia_agent; nua_handle_t *nh; mrcp_server_sofia_session_t *sofia_session; sip_t const *sip; tagi_t *tags; }; #define MRCP_SERVER_SOFIA_AGENT "OpenMRCP Sofia-SIP" static mrcp_status_t mrcp_sofia_agent_destroy(mrcp_module_t *module); static mrcp_module_state_t mrcp_sofia_agent_open(mrcp_module_t *module); static mrcp_module_state_t mrcp_sofia_agent_close(mrcp_module_t *module); static mrcp_status_t mrcp_sofia_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg); static const mrcp_module_method_set_t module_method_set = { mrcp_sofia_agent_destroy, mrcp_sofia_agent_open, mrcp_sofia_agent_close, mrcp_sofia_agent_signal_handler }; static mrcp_status_t mrcp_sofia_session_answer(mrcp_signaling_channel_t *channel); static mrcp_status_t mrcp_sofia_session_destroy(mrcp_signaling_channel_t *channel); static const mrcp_signaling_channel_method_set_t signaling_channel_method_set = { NULL, /*offer*/ mrcp_sofia_session_answer, NULL, /*terminate*/ mrcp_sofia_session_destroy }; mrcp_status_t mrcp_descriptor_generate_by_sdp_offer(mrcp_descriptor_t *descriptor, sdp_session_t *sdp, apr_pool_t *pool); size_t mrcp_sdp_answer_generate_by_mrcp_descriptor(char *buffer, size_t size, mrcp_descriptor_t *descriptor); size_t mrcp_resource_discover_sdp_generate(char *buffer, size_t size, mrcp_descriptor_t *descriptor); static APR_INLINE mrcp_server_sofia_agent_t* mrcp_sofia_agent_get(mrcp_module_t *module) { return ((mrcp_signaling_agent_t*)module)->object; } static mrcp_server_sofia_session_t* mrcp_sofia_session_create(mrcp_server_sofia_agent_t *sofia_agent) { mrcp_server_sofia_session_t *sofia_session; apr_pool_t *pool; const mrcp_signaling_agent_event_set_t *event_set = sofia_agent->signaling_agent->agent_event_set; mrcp_session_t *mrcp_session = event_set->on_session_create(sofia_agent->signaling_agent,NULL,&pool); if(!mrcp_session || !pool) { return NULL; } sofia_session = apr_palloc(pool,sizeof(*sofia_session)); sofia_session->home = su_home_new(sizeof(*sofia_session->home)); sofia_session->channel = mrcp_server_signaling_channel_create( sofia_agent->signaling_agent, &signaling_channel_method_set, sofia_session, pool); event_set->on_channel_attach(mrcp_session,sofia_session->channel); return sofia_session; } static mrcp_status_t mrcp_sofia_session_answer(mrcp_signaling_channel_t *channel) { mrcp_server_sofia_session_t *sofia_session = channel->object; const char *local_sdp_str = NULL; char sdp_str[2048]; if(!sofia_session || !sofia_session->nh) { return MRCP_STATUS_FAILURE; } if(mrcp_sdp_answer_generate_by_mrcp_descriptor(sdp_str,sizeof(sdp_str),&channel->local_descriptor) > 0) { local_sdp_str = sdp_str; apt_log(APT_PRIO_INFO,"Local SDP\n[%s]\n", local_sdp_str); } nua_respond(sofia_session->nh, SIP_200_OK, SIPTAG_CONTACT_STR(channel->agent->sip_contact_str), TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)), NUTAG_AUTOANSWER(0), TAG_END()); return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_sofia_session_destroy(mrcp_signaling_channel_t *channel) { mrcp_server_sofia_session_t *sofia_session = channel->object; 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; } channel->object = NULL; } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_sofia_on_call_received(mrcp_sofia_event_t *sofia_event) { int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; const char *local_sdp_str = NULL, *remote_sdp_str = NULL; mrcp_server_sofia_session_t *sofia_session; sdp_parser_t *parser = NULL; sdp_session_t *sdp = NULL; tl_gets(sofia_event->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()); sofia_session = sofia_event->sofia_session; if(!sofia_session) { sofia_session = mrcp_sofia_session_create(sofia_event->sofia_agent); if(!sofia_session) { return MRCP_STATUS_FAILURE; } nua_handle_bind(sofia_event->nh, sofia_session); sofia_session->nh = sofia_event->nh; } if(!sofia_session->channel) { return MRCP_STATUS_FAILURE; } local_sdp_str = NULL; if(remote_sdp_str) { apt_log(APT_PRIO_INFO,"Remote SDP\n[%s]\n", remote_sdp_str); parser = sdp_parse(sofia_session->home,remote_sdp_str,(int)strlen(remote_sdp_str),0); sdp = sdp_session(parser); mrcp_descriptor_generate_by_sdp_offer( &sofia_session->channel->remote_descriptor, sdp, sofia_session->channel->pool); sdp_parser_free(parser); } return sofia_session->channel->event_set->on_offer(sofia_session->channel); } static mrcp_status_t mrcp_sofia_on_call_terminated(mrcp_sofia_event_t *sofia_event) { mrcp_server_sofia_session_t *sofia_session = sofia_event->sofia_session; if(sofia_session) { sofia_session->channel->event_set->on_terminate(sofia_session->channel); } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_sofia_on_state_changed(mrcp_sofia_event_t *sofia_event) { int ss_state = nua_callstate_init; tl_gets(sofia_event->tags, NUTAG_CALLSTATE_REF(ss_state), TAG_END()); apt_log(APT_PRIO_NOTICE,"SIP Call State [%s]\n", nua_callstate_name(ss_state)); switch(ss_state) { case nua_callstate_received: mrcp_sofia_on_call_received(sofia_event); break; case nua_callstate_terminated: mrcp_sofia_on_call_terminated(sofia_event); break; } return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_sofia_on_resource_discover(mrcp_sofia_event_t *sofia_event) { char sdp_str[2048]; char *local_sdp_str = NULL; apt_log(APT_PRIO_NOTICE,"SIP Options\n"); if(mrcp_resource_discover_sdp_generate(sdp_str,sizeof(sdp_str),&sofia_event->sofia_agent->signaling_agent->capabilities) > 0) { local_sdp_str = sdp_str; apt_log(APT_PRIO_INFO,"Local SDP\n[%s]\n", local_sdp_str); } nua_respond(sofia_event->nh, SIP_200_OK, #ifdef NUA_OPTIONS_WITH_SDP NUTAG_WITH_CURRENT(sofia_event->nua), #endif SIPTAG_CONTACT_STR(sofia_event->sofia_agent->signaling_agent->sip_contact_str), TAG_IF(local_sdp_str,SOATAG_USER_SDP_STR(local_sdp_str)), TAG_END()); return MRCP_STATUS_SUCCESS; } static mrcp_status_t mrcp_sofia_agent_signal_handler(mrcp_module_t *module, apt_task_msg_t *msg) { mrcp_sofia_event_t *sofia_event = (mrcp_sofia_event_t*)msg->data; if(!sofia_event) { return MRCP_STATUS_FAILURE; } if(sofia_event->event_id == MRCP_SOFIA_EVENT_AGENT_TERMINATED) { if(module->event_set->on_close) { module->event_set->on_close(module); } return MRCP_STATUS_SUCCESS; } if(!sofia_event->sofia_agent) { return MRCP_STATUS_FAILURE; } apt_log(APT_PRIO_DEBUG,"Process SIP Event [%s] Status %d %s\n", nua_event_name(sofia_event->nua_event), sofia_event->status, sofia_event->phrase); switch(sofia_event->nua_event) { case nua_i_state: mrcp_sofia_on_state_changed(sofia_event); break; case nua_i_options: mrcp_sofia_on_resource_discover(sofia_event); break; default: break; } return MRCP_STATUS_SUCCESS; } static void mrcp_sofia_agent_on_terminate_requested(void *data) { mrcp_server_sofia_agent_t *sofia_agent = data; apt_log(APT_PRIO_DEBUG,"Sofia SIP Terminate Requested\n"); while(!sofia_agent->nua) { /* wait for nua to start to be able to terminate */ apt_log(APT_PRIO_DEBUG,"Wait for NUA to Start\n"); apt_task_delay(500); } if(sofia_agent->nua) { apt_log(APT_PRIO_DEBUG,"Send Shutdown Signal to NUA\n"); nua_shutdown(sofia_agent->nua); } } static void mrcp_sofia_agent_on_terminate_complete(void *data) { mrcp_server_sofia_agent_t *sofia_agent = data; apt_task_msg_t *task_msg; task_msg = apt_producer_task_msg_get(sofia_agent->producer_task); if(task_msg) { mrcp_sofia_event_t *sofia_event = (mrcp_sofia_event_t*)task_msg->data; sofia_event->event_id = MRCP_SOFIA_EVENT_AGENT_TERMINATED; sofia_agent->signaling_agent->module.signal( &sofia_agent->signaling_agent->module, task_msg); } } /* 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_server_sofia_agent_t *sofia_agent, nua_handle_t *nh, mrcp_server_sofia_session_t *sofia_session, sip_t const *sip, tagi_t tags[]) { mrcp_status_t mrcp_process = MRCP_STATUS_FAILURE; apt_log(APT_PRIO_INFO,"Recieve SIP Event [%s] Status %d %s\n",nua_event_name(nua_event),status,phrase); switch(nua_event) { case nua_i_state: case nua_i_options: mrcp_process = MRCP_STATUS_SUCCESS; break; case nua_r_shutdown: /* break main loop of sofia thread */ su_root_break(sofia_agent->root); break; default: break; } if(mrcp_process == MRCP_STATUS_SUCCESS) { apt_task_msg_t *task_msg = apt_producer_task_msg_get(sofia_agent->producer_task); mrcp_sofia_event_t *sofia_event = (mrcp_sofia_event_t*)task_msg->data; sofia_event->event_id = MRCP_SOFIA_EVENT_AGENT_PROCESS; sofia_event->nua_event = nua_event; sofia_event->status = status; sofia_event->phrase = phrase; sofia_event->nua = nua; sofia_event->sofia_agent = sofia_agent; sofia_event->nh = nh; sofia_event->sofia_session = sofia_session; sofia_event->sip = sip; sofia_event->tags = tags; sofia_agent->signaling_agent->module.signal(&sofia_agent->signaling_agent->module, task_msg); } } static void mrcp_sofia_main_loop(void *data) { char sip_bind_url[128]; mrcp_server_sofia_agent_t *sofia_agent = data; if(!sofia_agent) { return; } /* 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. */ sprintf(sip_bind_url,"sip:%s:%d","0.0.0.0",sofia_agent->signaling_agent->sip_port); 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(sip_bind_url), /* 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(MRCP_SERVER_SOFIA_AGENT), TAG_END()); /* Run event loop */ su_root_run(sofia_agent->root); /* Destroy allocated resources */ nua_destroy(sofia_agent->nua); } su_root_destroy(sofia_agent->root); su_deinit(); } static mrcp_module_state_t mrcp_sofia_agent_open(mrcp_module_t *module) { mrcp_server_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(module); apt_log(APT_PRIO_INFO,"Open Sofia SIP Agent\n"); apt_producer_task_start(sofia_agent->producer_task); return MODULE_STATE_OPEN_INPROGRESS; } static mrcp_module_state_t mrcp_sofia_agent_close(mrcp_module_t *module) { mrcp_server_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(module); apt_log(APT_PRIO_INFO,"Close Sofia SIP Agent\n"); apt_producer_task_terminate(sofia_agent->producer_task,MRCP_STATUS_FAILURE); return MODULE_STATE_CLOSE_INPROGRESS; } static void mrcp_sofia_agent_config(mrcp_server_sofia_agent_t *sofia_agent, const char *ip, unsigned short sip_port, unsigned short mrcp_port) { char sip_contact_buf[128]; if(ip) { sofia_agent->signaling_agent->capabilities.ip = apr_pstrdup(sofia_agent->pool,ip); } sofia_agent->signaling_agent->mrcp_port = mrcp_port; sofia_agent->signaling_agent->sip_port = sip_port; sprintf(sip_contact_buf,"sip:%s:%d",sofia_agent->signaling_agent->capabilities.ip,sofia_agent->signaling_agent->sip_port); sofia_agent->signaling_agent->sip_contact_str = apr_pstrdup(sofia_agent->pool,sip_contact_buf); } static mrcp_status_t mrcp_sofia_agent_destroy(mrcp_module_t *module) { mrcp_server_sofia_agent_t *sofia_agent = mrcp_sofia_agent_get(module); apt_log(APT_PRIO_NOTICE,"Destroy Sofia SIP Agent\n"); apt_producer_task_destroy(sofia_agent->producer_task); sofia_agent->pool = NULL; return MRCP_STATUS_SUCCESS; } mrcp_signaling_agent_t* mrcp_server_sofia_agent_create(const char *ip, unsigned short sip_port, unsigned short mrcp_port, const char *resource_location, apr_pool_t *pool) { apt_task_msg_pool_t *msg_pool; mrcp_server_sofia_agent_t *sofia_agent = apr_palloc(pool,sizeof(mrcp_server_sofia_agent_t)); mrcp_signaling_agent_t *agent = mrcp_server_signaling_agent_create(&module_method_set,sofia_agent,pool); apt_log(APT_PRIO_NOTICE,"Create Sofia SIP Agent %s:%hu\n",ip,sip_port); sofia_agent->pool = pool; sofia_agent->signaling_agent = agent; sofia_agent->root = NULL; sofia_agent->nua = NULL; mrcp_sofia_agent_config(sofia_agent,ip,sip_port,mrcp_port); msg_pool = apt_task_msg_pool_create_waitable_static(sizeof(mrcp_sofia_event_t),pool); sofia_agent->producer_task = apt_producer_task_create(sofia_agent,mrcp_sofia_main_loop,msg_pool,pool); if(sofia_agent->producer_task) { apt_task_t *task = apt_producer_task_get(sofia_agent->producer_task); apt_task_event_handler_set(task,TASK_STATE_TERMINATE_REQUESTED,sofia_agent,mrcp_sofia_agent_on_terminate_requested); apt_task_event_handler_set(task,TASK_STATE_TERMINATE_COMPLETED,sofia_agent,mrcp_sofia_agent_on_terminate_complete); } return agent; }