From 2899b4ae95051c8ce5f55ceed4ff8f0351f838bb Mon Sep 17 00:00:00 2001 From: David Yat Sin Date: Mon, 30 Jul 2012 11:07:01 -0400 Subject: [PATCH 1/3] Updated to latest code used on test machines --- src/mod/endpoints/mod_opal/mod_opal.cpp | 1318 +++++++++-------------- src/mod/endpoints/mod_opal/mod_opal.h | 330 +++--- 2 files changed, 713 insertions(+), 935 deletions(-) diff --git a/src/mod/endpoints/mod_opal/mod_opal.cpp b/src/mod/endpoints/mod_opal/mod_opal.cpp index 0760588019..8244737115 100644 --- a/src/mod/endpoints/mod_opal/mod_opal.cpp +++ b/src/mod/endpoints/mod_opal/mod_opal.cpp @@ -4,6 +4,7 @@ * Version: MPL 1.1 * * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) + * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) * * 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 @@ -28,29 +29,34 @@ #include #include -SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, mod_opal_globals.codec_string); -SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, mod_opal_globals.context); -SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, mod_opal_globals.dialplan); + +/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a + MAXIMUM audio frames per packet is nototiated, and there is no + requirement for the remote to actually send that many. So, in say GSM, we + negotiate up to 3 frames or 60ms of data and the remote actually sends one + (20ms) frame per packet. Perfectly legal but blows up the media handling + in FS. + + Eventually we will get around to bundling the packets, but not yet. This + compile flag will just force one frame/packet for all audio codecs. + */ +#define IMPLEMENT_MULTI_FAME_AUDIO 0 -#define CF_NEED_FLUSH (1 << 1) - -struct mod_opal_globals mod_opal_globals = { 0 }; - - -static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, - switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session, - switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause); +static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause); static FSProcess *opal_process = NULL; -static const char ModuleName[] = "opal"; - - -static switch_status_t on_hangup(switch_core_session_t *session); -static switch_status_t on_destroy(switch_core_session_t *session); +static PConstString const ModuleName("opal"); +static char const ConfigFile[] = "opal.conf"; static switch_io_routines_t opalfs_io_routines = { @@ -70,7 +76,7 @@ static switch_state_handler_table_t opalfs_event_handlers = { /*.on_init */ FSConnection::on_init, /*.on_routing */ FSConnection::on_routing, /*.on_execute */ FSConnection::on_execute, - /*.on_hangup */ on_hangup, + /*.on_hangup */ FSConnection::on_hangup, /*.on_exchange_media */ FSConnection::on_exchange_media, /*.on_soft_execute */ FSConnection::on_soft_execute, /*.on_consume_media*/ NULL, @@ -78,7 +84,7 @@ static switch_state_handler_table_t opalfs_event_handlers = { /*.on_reset*/ NULL, /*.on_park*/ NULL, /*.on_reporting*/ NULL, - /*.on_destroy*/ on_destroy + /*.on_destroy*/ FSConnection::on_destroy }; @@ -89,7 +95,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load); SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown); SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL); -SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) { +SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) +{ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n"); /* Prevent the loading of OPAL codecs via "plug ins", this is a directory @@ -110,8 +117,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) { if (opal_process->Initialise(*module_interface)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n"); //unloading causes a seg in linux - return SWITCH_STATUS_NOUNLOAD; - //return SWITCH_STATUS_SUCCESS; + //return SWITCH_STATUS_UNLOAD; + return SWITCH_STATUS_SUCCESS; } delete opal_process; @@ -120,11 +127,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) { } -SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) { - - switch_safe_free(mod_opal_globals.context); - switch_safe_free(mod_opal_globals.dialplan); - switch_safe_free(mod_opal_globals.codec_string); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) +{ delete opal_process; opal_process = NULL; return SWITCH_STATUS_SUCCESS; @@ -133,161 +137,79 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) { SWITCH_END_EXTERN_C /*******************************************************************************/ - - -static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, - switch_event_t *var_event, - switch_caller_profile_t *outbound_profile, - switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause) -{ - if (opal_process == NULL) { - return SWITCH_CAUSE_CRASH; - } - - PString token; - - FSManager & manager = opal_process->GetManager(); - if (!manager.SetUpCall("local:", outbound_profile->destination_number, token, outbound_profile)) { - return SWITCH_CAUSE_INVALID_NUMBER_FORMAT; - } - - PSafePtr < OpalCall > call = manager.FindCallWithLock(token); - - if (call == NULL) { - return SWITCH_CAUSE_PROTOCOL_ERROR; - } - - PSafePtr < FSConnection > connection = call->GetConnectionAs < FSConnection > (0); - - if (connection == NULL) { - return SWITCH_CAUSE_PROTOCOL_ERROR; - } - - *new_session = connection->GetSession(); - - return SWITCH_CAUSE_SUCCESS; -} - - /////////////////////////////////////////////////////////////////////// #if PTRACING -class FSTrace : public ostream { - public: - FSTrace() - : ostream(&buffer) - { - } +class FSTrace : public std::ostream +{ +private: + class Buffer : public std::stringbuf + { + virtual int sync() + { + std::string s = str(); + if (s.empty()) + return 0; - private: - class Buffer : public streambuf { - char buffer[250]; + //Due to explicit setting of flags we know exactly what we are getting + PStringArray fields(6); + static PRegularExpression logRE("^([0-9]+)\t *([^(]+)\\(([0-9]+)\\)\t(.*)", PRegularExpression::Extended); + if (!logRE.Execute(s.c_str(), fields)) { + fields[1] = "4"; + fields[2] = __FILE__; + fields[3] = __LINE__; + fields[4] = s; + } - public: - Buffer() - { - setg(buffer, buffer, &buffer[sizeof(buffer)-2]); - setp(buffer, &buffer[sizeof(buffer)-2]); - } + switch_log_level_t level; + switch (fields[1].AsUnsigned()) { + case 0 : + level = SWITCH_LOG_ALERT; + break; + case 1 : + level = SWITCH_LOG_ERROR; + break; + case 2 : + level = SWITCH_LOG_WARNING; + break; + case 3 : + level = SWITCH_LOG_INFO; + break; + default : + level = SWITCH_LOG_DEBUG; + break; + } - virtual int sync() - { - return overflow(EOF); - } + fields[4].Replace("\t", " ", true); + switch_log_printf(SWITCH_CHANNEL_ID_LOG, + fields[2], + "PTLib-OPAL", + fields[3].AsUnsigned(), + NULL, + level, + "%s", fields[4].GetPointer()); - virtual int underflow() - { - return EOF; - } + // Reset string + str(std::string()); + return 0; + } + } buffer; - virtual int overflow(int c) - { - const char *fmt = "%s"; - char *func = NULL; - - int bufSize = pptr() - pbase(); - - if (c != EOF) { - *pptr() = (char)c; - bufSize++; - } - - if (bufSize != 0) { - char *bufPtr = pbase(); - char *bufEndPtr = NULL; - setp(bufPtr, epptr()); - bufPtr[bufSize] = '\0'; - int line = 0; - char *p; - - char *file = NULL; - switch_log_level_t level; - - - switch (strtoul(bufPtr, &file, 10)) { - case 1 : - level = SWITCH_LOG_INFO; - break; - default : - level = SWITCH_LOG_DEBUG; - break; - } - - if (file) { - while (isspace(*file)) file++; - - if (file && (bufPtr = strchr(file, '(')) && (bufEndPtr = strchr(bufPtr, ')'))) { - char *e; - - for(p = bufPtr; p && *p; p++) { - if (*p == '\t') { - *p = ' '; - } - } - - *bufPtr++ = '\0'; - line = atoi(bufPtr); - while (bufEndPtr && isspace(*(++bufEndPtr))); - bufPtr = bufEndPtr; - if (bufPtr && ((e = strchr(bufPtr, ' ')) || (e = strchr(bufPtr, '\t')))) { - func = bufPtr; - bufPtr = e; - *bufPtr++ = '\0'; - } - } - } - - switch_text_channel_t tchannel = SWITCH_CHANNEL_ID_LOG; - - if (!bufPtr) { - bufPtr = pbase(); - level = SWITCH_LOG_DEBUG; - } - - if (bufPtr) { - if (end_of(bufPtr) != '\n') { - fmt = "%s\n"; - } - if (!(file && func && line)) tchannel = SWITCH_CHANNEL_ID_LOG_CLEAN; - - switch_log_printf(tchannel, file, func, line, NULL, level, fmt, bufPtr); - } - - } - - return 0; - } - } buffer; +public: + FSTrace() + : ostream(&buffer) + { + } }; -#endif +#endif // PTRACING /////////////////////////////////////////////////////////////////////// FSProcess::FSProcess() - : PLibraryProcess("Vox Lucida Pty. Ltd.", "mod_opal", 1, 0, AlphaCode, 1) + : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1) , m_manager(NULL) { } @@ -296,19 +218,24 @@ FSProcess::FSProcess() FSProcess::~FSProcess() { delete m_manager; +#if PTRACING + PTrace::SetStream(NULL); // This will delete the FSTrace object +#endif } bool FSProcess::Initialise(switch_loadable_module_interface_t *iface) { - m_manager = new FSManager(); - return m_manager != NULL && m_manager->Initialise(iface); + m_manager = new FSManager(); + return m_manager != NULL && m_manager->Initialise(iface); } /////////////////////////////////////////////////////////////////////// FSManager::FSManager() + : m_context("default") + , m_dialplan("XML") { // These are deleted by the OpalManager class, no need to have destructor m_h323ep = new H323EndPoint(*this); @@ -321,12 +248,6 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface) { ReadConfig(false); -#if PTRACING - PTrace::SetLevel(mod_opal_globals.trace_level); //just for fun and eyecandy ;) - PTrace::SetOptions(PTrace::TraceLevel); - PTrace::SetStream(new FSTrace); -#endif - m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE); m_FreeSwitch->interface_name = ModuleName; m_FreeSwitch->io_routines = &opalfs_io_routines; @@ -338,8 +259,8 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface) m_h323ep->StartListener(""); } else { for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { - if (!m_h323ep->StartListener(it->listenAddress)) { - PTRACE(3, "mod_opal\tCannot start listener for " << it->name); + if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) { + PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name); } } } @@ -363,15 +284,16 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface) GetOpalGSMAMR(); GetOpaliLBC(); - /* For compatibility with the algorithm in FSConnection::SetCodecs() we need - to set all audio media formats to be 1 frame per packet */ +#if !IMPLEMENT_MULTI_FAME_AUDIO OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats(); for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) { if (it->GetMediaType() == OpalMediaType::Audio()) { it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), 1); it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), 1); + OpalMediaFormat::SetRegisteredMediaFormat(*it); } } +#endif // IMPLEMENT_MULTI_FAME_AUDIO if (!m_gkAddress.IsEmpty()) { if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface)) @@ -391,61 +313,53 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface) switch_status_t FSManager::ReadConfig(int reload) { - const char *cf = "opal.conf"; - switch_status_t status = SWITCH_STATUS_SUCCESS; + switch_event_t *request_params = NULL; + switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS); + switch_assert(request_params); + switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil("")); - switch_memory_pool_t *pool = NULL; - if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); - return status; - } - - set_global_context("default"); - set_global_dialplan("XML"); - - switch_event_t *params = NULL; - switch_event_create(¶ms, SWITCH_EVENT_REQUEST_PARAMS); - switch_assert(params); - switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil("")); switch_xml_t cfg; - switch_xml_t xml = switch_xml_open_cfg(cf, &cfg, params); + switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params); if (xml == NULL) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile); return SWITCH_STATUS_FALSE; } switch_xml_t xmlSettings = switch_xml_child(cfg, "settings"); if (xmlSettings) { for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { - const char *var = switch_xml_attr_soft(xmlParam, "name"); - const char *val = switch_xml_attr_soft(xmlParam, "value"); + PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); + PConstString const val(switch_xml_attr_soft(xmlParam, "value")); - if (!strcasecmp(var, "trace-level")) { - int level = atoi(val); - if (level > 0) { - mod_opal_globals.trace_level = level; - } - } else if (!strcasecmp(var, "context")) { - set_global_context(val); - } else if (!strcasecmp(var, "dialplan")) { - set_global_dialplan(val); - } else if (!strcasecmp(var, "codec-prefs")) { - set_global_codec_string(val); - } else if (!strcasecmp(var, "jitter-size")) { - char * next; - unsigned minJitter = strtoul(val, &next, 10); - if (minJitter >= 10) { - unsigned maxJitter = minJitter; - if (*next == ',') - maxJitter = atoi(next+1); - SetAudioJitterDelay(minJitter, maxJitter); // In milliseconds - } - } else if (!strcasecmp(var, "gk-address")) { + if (var == "context") { + m_context = val; + } else if (var == "dialplan") { + m_dialplan = val; + } else if (var == "codec-prefs") { + m_codecPrefs = val; + } else if (var == "jitter-size") { + SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds + } else if (var == "gk-address") { m_gkAddress = val; - } else if (!strcasecmp(var, "gk-identifer")) { + } else if (var == "gk-identifer") { m_gkIdentifer = val; - } else if (!strcasecmp(var, "gk-interface")) { + } else if (var == "gk-interface") { m_gkInterface = val; +#if PTRACING + } else if (var == "trace-level") { + unsigned level = val.AsUnsigned(); + if (level > 0) { + PTrace::SetLevel(level); + PTrace::ClearOptions(0xffffffff); // Everything off + PTrace::SetOptions( // Except these + PTrace::TraceLevel|PTrace::FileAndLine +#if PTLIB_CHECK_VERSION(2,11,1) + |PTrace::ContextIdentifier +#endif + ); + PTrace::SetStream(new FSTrace); + } +#endif } } } @@ -457,196 +371,205 @@ switch_status_t FSManager::ReadConfig(int reload) m_listeners.push_back(FSListener()); FSListener & listener = m_listeners.back(); - listener.name = switch_xml_attr_soft(xmlListener, "name"); - if (listener.name.IsEmpty()) - listener.name = "unnamed"; - - PIPSocket::Address ip; - WORD port = 1720; + listener.m_name = switch_xml_attr_soft(xmlListener, "name"); + if (listener.m_name.IsEmpty()) + listener.m_name = "unnamed"; for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) { - const char *var = switch_xml_attr_soft(xmlParam, "name"); - const char *val = switch_xml_attr_soft(xmlParam, "value"); - //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Var - '%s' and Val - '%s' \n", var, val); - if (!strcasecmp(var, "h323-ip")) - ip = val; - else if (!strcasecmp(var, "h323-port")) - port = (WORD) atoi(val); + PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name")); + PConstString const val(switch_xml_attr_soft(xmlParam, "value")); + if (var == "h323-ip") + listener.m_address = val; + else if (var == "h323-port") + listener.m_port = (uint16_t)val.AsUnsigned(); } - listener.listenAddress = OpalTransportAddress(ip, port); - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.name); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name); } } - switch_event_destroy(¶ms); + switch_event_destroy(&request_params); if (xml) switch_xml_free(xml); - return status; + return SWITCH_STATUS_SUCCESS; } -OpalCall * FSManager::CreateCall(void * /*userData*/) +static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, + switch_event_t *var_event, + switch_caller_profile_t *outbound_profile, + switch_core_session_t **new_session, + switch_memory_pool_t **pool, + switch_originate_flag_t flags, + switch_call_cause_t *cancel_cause) { - return new FSCall(*this); + if (opal_process == NULL) + return SWITCH_CAUSE_CRASH; + + FSConnection::outgoing_params params; + params.var_event = var_event; + params.outbound_profile = outbound_profile; + params.new_session = new_session; + params.pool = pool; + params.flags = flags; + params.cancel_cause = cancel_cause; + params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT; + + if (opal_process->GetManager().SetUpCall("local:", outbound_profile->destination_number, ¶ms) != NULL) + return SWITCH_CAUSE_SUCCESS; + + if (*new_session != NULL) + switch_core_session_destroy(new_session); + return params.fail_cause; } /////////////////////////////////////////////////////////////////////// FSEndPoint::FSEndPoint(FSManager & manager) -: OpalLocalEndPoint(manager) + : OpalLocalEndPoint(manager) + , m_manager(manager) { - PTRACE(3, "mod_opal\t FSEndPoint Created!"); -} - - -bool FSEndPoint::OnIncomingCall(OpalLocalConnection & connection) -{ - return ((FSConnection &) connection).OnIncoming(); + PTRACE(4, "mod_opal\tFSEndPoint created."); } OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions) { - FSManager & mgr = (FSManager &) GetManager(); - switch_core_session_t *fsSession = switch_core_session_request(mgr.GetSwitchInterface(), - (switch_caller_profile_t *)userData ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL); - if (fsSession == NULL) - return NULL; - - switch_channel_t *fsChannel = switch_core_session_get_channel(fsSession); - - if (fsChannel == NULL) { - switch_core_session_destroy(&fsSession); - return NULL; - } - - return new FSConnection(call, *this, userData, options, stringOptions, (switch_caller_profile_t *)userData, fsSession, fsChannel); + return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData); } /////////////////////////////////////////////////////////////////////// -FSCall::FSCall(OpalManager & manager) - : OpalCall(manager) -{ -} - - -PBoolean FSCall::OnSetUp(OpalConnection & connection) -{ - // Transfer FS caller_id_number & caller_id_name from the FSConnection - // to the protocol connectionm (e.g. H.323) so gets sent correctly - // in outgoing packets - PSafePtr local = GetConnectionAs(); - if (local != NULL) { - PSafePtr proto = local->GetOtherPartyConnection(); - if (proto != NULL) { - proto->SetLocalPartyName(local->GetLocalPartyName()); - proto->SetDisplayName(local->GetDisplayName()); - } - } - - return OpalCall::OnSetUp(connection); -} - - -/////////////////////////////////////////////////////////////////////// - - -FSConnection::FSConnection(OpalCall & call, FSEndPoint & endpoint, void* userData, unsigned options, OpalConnection::StringOptions* stringOptions, switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel) - : OpalLocalConnection(call, endpoint, userData, options, stringOptions) +FSConnection::FSConnection(OpalCall & call, + FSEndPoint & endpoint, + unsigned options, + OpalConnection::StringOptions* stringOptions, + outgoing_params * params) + : OpalLocalConnection(call, endpoint, NULL, options, stringOptions) , m_endpoint(endpoint) - , m_fsSession(fsSession) - , m_fsChannel(fsChannel) + , m_fsSession(NULL) + , m_fsChannel(NULL) + , m_flushAudio(false) { - opal_private_t *tech_pvt; + memset(&m_read_timer, 0, sizeof(m_read_timer)); + memset(&m_read_codec, 0, sizeof(m_read_codec)); + memset(&m_write_codec, 0, sizeof(m_write_codec)); + memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer)); + memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec)); + memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec)); - tech_pvt = (opal_private_t *) switch_core_session_alloc(m_fsSession, sizeof(*tech_pvt)); - tech_pvt->me = this; - switch_core_session_set_private(m_fsSession, tech_pvt); + if (params != NULL) { + // If we fail, this is the cause + params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER; - if (outbound_profile != NULL) { - SetLocalPartyName(outbound_profile->caller_id_number); - SetDisplayName(outbound_profile->caller_id_name); - - switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, outbound_profile); - switch_channel_set_caller_profile(m_fsChannel, caller_profile); - - PString name = "opal/"; - name += outbound_profile->destination_number; - switch_channel_set_name(m_fsChannel, name); - - switch_channel_set_state(m_fsChannel, CS_INIT); + if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), + SWITCH_CALL_DIRECTION_INBOUND, params->flags, params->pool)) == NULL) { + PTRACE(1, "mod_opal\tCannot create session for outgoing call."); + return; + } } + else { + if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(), + SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) { + PTRACE(1, "mod_opal\tCannot create session for incoming call."); + return; + } + } + + if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) { + switch_core_session_destroy(&m_fsSession); + return; + } + + switch_core_session_set_private(m_fsSession, this); + SafeReference(); // Make sure cannot be deleted until on_destroy() + + if (params != NULL) { + switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile); + switch_channel_set_caller_profile(m_fsChannel, caller_profile); + SetLocalPartyName(caller_profile->caller_id_number); + SetDisplayName(caller_profile->caller_id_name); + *params->new_session = m_fsSession; + } + + switch_channel_set_state(m_fsChannel, CS_INIT); +} + + +bool FSConnection::OnOutgoingSetUp() +{ + if (m_fsSession == NULL || m_fsChannel == NULL) { + PTRACE(1, "mod_opal\tSession request failed."); + return false; + } + + // Transfer FS caller_id_number & caller_id_name from the FSConnection + // to the protocol connection (e.g. H.323) so gets sent correctly + // in outgoing packets + PSafePtr proto = GetOtherPartyConnection(); + if (proto == NULL) { + PTRACE(1, "mod_opal\tNo protocol connection in call."); + return false; + } + + proto->SetLocalPartyName(GetLocalPartyName()); + proto->SetDisplayName(GetDisplayName()); + + switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL()); + return true; } bool FSConnection::OnIncoming() { - if (m_fsSession == NULL) { + if (m_fsSession == NULL || m_fsChannel == NULL) { PTRACE(1, "mod_opal\tSession request failed."); return false; } switch_core_session_add_stream(m_fsSession, NULL); - switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); - if (channel == NULL) { - PTRACE(1, "mod_opal\tSession does not have a channel"); - return false; - } - PURL url = GetRemotePartyURL(); - switch_caller_profile_t *caller_profile = switch_caller_profile_new(switch_core_session_get_pool(m_fsSession), - url.GetUserName(), - /** username */ - mod_opal_globals.dialplan, - /** dial plan */ - GetRemotePartyName(), - /** caller_id_name */ - GetRemotePartyNumber(), - /** caller_id_number */ - url.GetHostName(), - /** network addr */ - NULL, - /** ANI */ - NULL, - /** ANI II */ - NULL, - /** RDNIS */ - ModuleName, - /** source */ - mod_opal_globals.context, - /** set context */ - GetCalledPartyNumber() - /** destination_number */ - ); + switch_caller_profile_t *caller_profile = switch_caller_profile_new( + switch_core_session_get_pool(m_fsSession), + url.GetUserName(), /** username */ + m_endpoint.GetManager().GetDialPlan(), /** dial plan */ + GetRemotePartyName(), /** caller_id_name */ + GetRemotePartyNumber(), /** caller_id_number */ + url.GetHostName(), /** network addr */ + NULL, /** ANI */ + NULL, /** ANI II */ + NULL, /** RDNIS */ + ModuleName, /** source */ + m_endpoint.GetManager().GetContext(), /** set context */ + GetCalledPartyNumber() /** destination_number */ + ); if (caller_profile == NULL) { PTRACE(1, "mod_opal\tCould not create caller profile"); return false; } PTRACE(4, "mod_opal\tCreated switch caller profile:\n" - " username = " << caller_profile->username << "\n" - " dialplan = " << caller_profile->dialplan << "\n" - " caller_id_name = " << caller_profile->caller_id_name << "\n" - " caller_id_number = " << caller_profile->caller_id_number << "\n" - " network_addr = " << caller_profile->network_addr << "\n" - " source = " << caller_profile->source << "\n" - " context = " << caller_profile->context << "\n" " destination_number= " << caller_profile->destination_number); - switch_channel_set_caller_profile(channel, caller_profile); + " username = " << caller_profile->username << "\n" + " dialplan = " << caller_profile->dialplan << "\n" + " caller_id_name = " << caller_profile->caller_id_name << "\n" + " caller_id_number = " << caller_profile->caller_id_number << "\n" + " network_addr = " << caller_profile->network_addr << "\n" + " source = " << caller_profile->source << "\n" + " context = " << caller_profile->context << "\n" + " destination_number= " << caller_profile->destination_number); + switch_channel_set_caller_profile(m_fsChannel, caller_profile); - char name[256] = "opal/in:"; - switch_copy_string(name + 8, caller_profile->destination_number, sizeof(name)-8); - switch_channel_set_name(channel, name); - switch_channel_set_state(channel, CS_INIT); + switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number); if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) { PTRACE(1, "mod_opal\tCould not launch session thread"); + switch_core_session_destroy(&m_fsSession); + m_fsChannel = NULL; return false; } @@ -656,57 +579,44 @@ bool FSConnection::OnIncoming() void FSConnection::OnReleased() { - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession); - - /* so FS on_hangup will not try to deref a landmine */ - tech_pvt->me = NULL; - m_rxAudioOpened.Signal(); // Just in case m_txAudioOpened.Signal(); - H225_ReleaseCompleteReason dummy; - switch_channel_hangup(switch_core_session_get_channel(m_fsSession), - (switch_call_cause_t)H323TranslateFromCallEndReason(GetCallEndReason(), dummy)); + + if (m_fsChannel == NULL) { + PTRACE(3, "mod_opal\tHanging up FS side"); + switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931); + } + OpalLocalConnection::OnReleased(); } -void FSConnection::OnAlerting() -{ - switch_channel_mark_ring_ready(m_fsChannel); - return OpalLocalConnection::OnAlerting(); -} - PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia) { + if (PAssertNULL(m_fsChannel) == NULL) + return false; + + switch_channel_mark_ring_ready(m_fsChannel); return OpalLocalConnection::SetAlerting(calleeName, withMedia); } -void FSConnection::OnEstablished() -{ - OpalLocalConnection::OnEstablished(); -} - - PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration) { + if (PAssertNULL(m_fsChannel) == NULL) + return false; + switch_dtmf_t dtmf = { tone, duration }; return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS; } -PBoolean FSConnection::SendUserInputString(const PString & value) -{ - return OpalConnection::SendUserInputString(value); -} - - OpalMediaFormatList FSConnection::GetMediaFormats() const { if (m_switchMediaFormats.IsEmpty()) { const_cast(this)->SetCodecs(); } - + return m_switchMediaFormats; } @@ -714,39 +624,29 @@ OpalMediaFormatList FSConnection::GetMediaFormats() const void FSConnection::SetCodecs() { int numCodecs = 0; - const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; - const char *codec_string = NULL, *abs, *ocodec; - char *tmp_codec_string = NULL; - char *codec_order[SWITCH_MAX_CODECS]; - int codec_order_last; + const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; - - if ((abs = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"))) { - codec_string = abs; - } else { - if ((abs = switch_channel_get_variable(m_fsChannel, "codec_string"))) { - codec_string = abs; - } - - if ((ocodec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { - codec_string = switch_core_session_sprintf(m_fsSession, "%s,%s", ocodec, codec_string); + PString codec_string = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"); + if (codec_string.IsEmpty()) { + codec_string = switch_channel_get_variable(m_fsChannel, "codec_string"); + const char *orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE); + if (orig_codec) { + codec_string.Splice(orig_codec, 0); } } - - if (!codec_string) { - codec_string = mod_opal_globals.codec_string; + + if (codec_string.IsEmpty()) { + codec_string = m_endpoint.GetManager().GetCodecPrefs(); } - if (codec_string) { - if ((tmp_codec_string = strdup(codec_string))) { - codec_order_last = switch_separate_string(tmp_codec_string, ',', codec_order, SWITCH_MAX_CODECS); - numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last); - - } - } else { + if (codec_string.IsEmpty()) { numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0])); + } else { + char *codec_order[SWITCH_MAX_CODECS]; + int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS); + numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last); } - + for (int i = 0; i < numCodecs; i++) { const switch_codec_implementation_t *codec = codecs[i]; @@ -762,12 +662,12 @@ void FSConnection::SetCodecs() continue; } } - // Did we match or create a new media format? if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) { - PTRACE(2, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat); + PTRACE(3, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat); +#if IMPLEMENT_MULTI_FAME_AUDIO // Calculate frames per packet, do not use codec->codec_frames_per_packet as that field // has slightly different semantics when used in streamed codecs such as G.711 int fpp = codec->samples_per_packet/switchFormat.GetFrameTime(); @@ -778,7 +678,7 @@ void FSConnection::SetCodecs() could end up with 60ms and the codec cannot be created. The "holes" are unlikely in all but streamed codecs such as G.711, where it is theoretically possible for OPAL to come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these - scenarios succifiently rare that we can safely ignore them ... for now. */ + scenarios sufficiently rare that we can safely ignore them ... for now. */ if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) { switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp); @@ -787,12 +687,11 @@ void FSConnection::SetCodecs() if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) { switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp); } +#endif // IMPLEMENT_MULTI_FAME_AUDIO } m_switchMediaFormats += switchFormat; } - - switch_safe_free(tmp_codec_string); } @@ -802,110 +701,91 @@ OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFo } -PBoolean FSConnection::OnOpenMediaStream(OpalMediaStream & stream) +void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch) { - if (!OpalConnection::OnOpenMediaStream(stream)) { - return false; - } + OpalConnection::OnPatchMediaStream(isSource, patch); - if (stream.GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) { - return true; - } + if (PAssertNULL(m_fsChannel) == NULL) + return; - if (stream.IsSource()) { - m_rxAudioOpened.Signal(); - } else { - m_txAudioOpened.Signal(); - } + if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) + return; - if (GetMediaStream(stream.GetSessionID(), stream.IsSink()) != NULL) { + if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { + if (isSource) + m_rxAudioOpened.Signal(); + else + m_txAudioOpened.Signal(); + } + else if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) { // Have open media in both directions. - if (GetPhase() == AlertingPhase) { - switch_channel_mark_pre_answered(m_fsChannel); - } else if (GetPhase() < ReleasingPhase) { + if (IsEstablished()) switch_channel_mark_answered(m_fsChannel); - } + else if (!IsReleased()) + switch_channel_mark_pre_answered(m_fsChannel); } - - return true; } switch_status_t FSConnection::on_init() { - switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); - if (channel == NULL) { + if (PAssertNULL(m_fsChannel) == NULL) return SWITCH_STATUS_FALSE; - } - PTRACE(3, "mod_opal\tStarted routing for connection " << *this); - switch_channel_set_state(channel, CS_ROUTING); + PTRACE(4, "mod_opal\tStarted routing for connection " << *this); + switch_channel_set_state(m_fsChannel, CS_ROUTING); return SWITCH_STATUS_SUCCESS; } switch_status_t FSConnection::on_routing() { - PTRACE(3, "mod_opal\tRouting connection " << *this); + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + PTRACE(4, "mod_opal\tRouting connection " << *this); return SWITCH_STATUS_SUCCESS; } switch_status_t FSConnection::on_execute() { - PTRACE(3, "mod_opal\tExecuting connection " << *this); + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + + PTRACE(4, "mod_opal\tExecuting connection " << *this); return SWITCH_STATUS_SUCCESS; } -static switch_status_t on_destroy(switch_core_session_t *session) + +switch_status_t FSConnection::on_destroy() { - //switch_channel_t *channel = switch_core_session_get_channel(session); - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); - - if (tech_pvt) { - if (tech_pvt->read_codec.implementation) { - switch_core_codec_destroy(&tech_pvt->read_codec); - } + PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this); - if (tech_pvt->write_codec.implementation) { - switch_core_codec_destroy(&tech_pvt->write_codec); - } + m_fsChannel = NULL; // Will be destoyed by FS, so don't use it any more. - if (tech_pvt->vid_read_codec.implementation) { - switch_core_codec_destroy(&tech_pvt->vid_read_codec); - } + switch_core_codec_destroy(&m_read_codec); + switch_core_codec_destroy(&m_write_codec); + switch_core_codec_destroy(&m_vid_read_codec); + switch_core_codec_destroy(&m_vid_write_codec); + switch_core_timer_destroy(&m_read_timer); + switch_core_timer_destroy(&m_vid_read_timer); - if (tech_pvt->vid_write_codec.implementation) { - switch_core_codec_destroy(&tech_pvt->vid_write_codec); - } - - if (tech_pvt->read_timer.timer_interface) { - switch_core_timer_destroy(&tech_pvt->read_timer); - } - - if (tech_pvt->vid_read_timer.timer_interface) { - switch_core_timer_destroy(&tech_pvt->vid_read_timer); - } - } + switch_core_session_set_private(m_fsSession, NULL); + SafeDereference(); return SWITCH_STATUS_SUCCESS; } -/* this function has to be called with the original session beause the FSConnection might already be destroyed and we - will can't have it be a method of a dead object - */ -static switch_status_t on_hangup(switch_core_session_t *session) + +switch_status_t FSConnection::on_hangup() { - switch_channel_t *channel = switch_core_session_get_channel(session); - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); - + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; + /* if this is still here it was our idea to hangup not opal's */ - if (tech_pvt->me) { - Q931::CauseValues cause = (Q931::CauseValues)switch_channel_get_cause_q850(channel); - tech_pvt->me->SetQ931Cause(cause); - tech_pvt->me->ClearCallSynchronous(NULL, H323TranslateToCallEndReason(cause, UINT_MAX)); - tech_pvt->me = NULL; - } + ClearCallSynchronous(NULL, H323TranslateToCallEndReason( + (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX)); return SWITCH_STATUS_SUCCESS; } @@ -913,30 +793,30 @@ static switch_status_t on_hangup(switch_core_session_t *session) switch_status_t FSConnection::on_exchange_media() { - PTRACE(3, "mod_opal\tLoopback on connection " << *this); + PTRACE(4, "mod_opal\tExchanging media on connection " << *this); return SWITCH_STATUS_SUCCESS; } switch_status_t FSConnection::on_soft_execute() { - PTRACE(3, "mod_opal\tTransmit on connection " << *this); + PTRACE(4, "mod_opal\tTransmit on connection " << *this); return SWITCH_STATUS_SUCCESS; } switch_status_t FSConnection::kill_channel(int sig) { - PTRACE(3, "mod_opal\tKill " << sig << " on connection " << *this); - switch (sig) { - case SWITCH_SIG_BREAK: - break; case SWITCH_SIG_KILL: m_rxAudioOpened.Signal(); m_txAudioOpened.Signal(); + PTRACE(4, "mod_opal\tSignal channel KILL on connection " << *this); break; + case SWITCH_SIG_XFER: + case SWITCH_SIG_BREAK: default: + PTRACE(4, "mod_opal\tSignal channel " << sig << " on connection " << *this); break; } @@ -953,41 +833,16 @@ switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf) switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg) { - switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); + if (PAssertNULL(m_fsChannel) == NULL) + return SWITCH_STATUS_FALSE; - - /* - SWITCH_MESSAGE_INDICATE_PROGRESS: establish early media now and return SWITCH_STATUS_FALSE if you can't - SWITCH_MESSAGE_INDICATE_ANSWER: answer and set up media now if it's not already and return SWITCH_STATUS_FALSE if you can't - - Neither message means anything on an outbound call.... - - It would only happen if someone called switch_channel_answer() instead of switch_channel_mark_answered() on an outbound call. - it should not do anything if someone does it by accident somewhere hense this in both cases: - - if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { - return SWITCH_STATUS_FALSE; - } - - - When we get these messages the core will trust that you have triggered FSMediaStream::Open and are ready for media if we do not - have media we MUST return SWITCH_STATUS_FALSE or it will cause a CRASH. - - - - */ switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_BRIDGE: - case SWITCH_MESSAGE_INDICATE_UNBRIDGE: - case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: - switch_channel_set_private_flag(channel, CF_NEED_FLUSH); - break; - case SWITCH_MESSAGE_INDICATE_RINGING: case SWITCH_MESSAGE_INDICATE_PROGRESS: case SWITCH_MESSAGE_INDICATE_ANSWER: - { - switch_caller_profile_t * profile = switch_channel_get_caller_profile(channel); + case SWITCH_MESSAGE_INDICATE_DEFLECT: + if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) { + switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel); if (profile != NULL && profile->caller_extension != NULL) { PSafePtr other = GetOtherPartyConnection(); @@ -999,6 +854,9 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg SetDisplayName(profile->caller_extension->extension_name); } } + else { + return SWITCH_STATUS_FALSE; + } break; default: @@ -1006,89 +864,78 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg } switch (msg->message_id) { - case SWITCH_MESSAGE_INDICATE_RINGING: - SetPhase(OpalConnection::AlertingPhase); - OnAlerting(); + case SWITCH_MESSAGE_INDICATE_BRIDGE: + case SWITCH_MESSAGE_INDICATE_UNBRIDGE: + case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: + m_flushAudio = true; break; - case SWITCH_MESSAGE_INDICATE_DEFLECT: - { - PSafePtr other = GetOtherPartyConnection(); - if (other != NULL) - other->TransferConnection(msg->string_arg); + case SWITCH_MESSAGE_INDICATE_RINGING: + AlertingIncoming(); break; - } case SWITCH_MESSAGE_INDICATE_PROGRESS: - case SWITCH_MESSAGE_INDICATE_ANSWER: - { - int fixed = 0; - - if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { - return SWITCH_STATUS_FALSE; - } + AutoStartMediaStreams(); + AlertingIncoming(); - if (msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) { - if (fixed) { - /* this should send alerting + media and wait for it to be established and return SUCCESS or FAIL - depending on if media was able to be established. Need code to tell the other side we want early media here. - */ - GetCall().OpenSourceMediaStreams(*this, OpalMediaType::Audio()); - SetPhase(OpalConnection::AlertingPhase); - /* how do i say please establish early media ? */ - OnAlerting(); - } else { - /* hack to avoid getting stuck, pre_answer will imply answer */ - OnConnectedInternal(); - } - } else { - OnConnectedInternal(); - } - - // Wait for media - PTRACE(2, "mod_opal\tAwaiting media start on connection " << *this); - m_rxAudioOpened.Wait(); - m_txAudioOpened.Wait(); - - if (GetPhase() >= ReleasingPhase) { - // Call got aborted - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n"); - return SWITCH_STATUS_FALSE; - } - - PTRACE(4, "mod_opal\tMedia started on connection " << *this); - - if (msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) { - if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { - switch_channel_mark_pre_answered(m_fsChannel); - } - } else { - if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { - switch_channel_mark_answered(m_fsChannel); - } - } + if (!WaitForMedia()) + return SWITCH_STATUS_FALSE; + if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { + switch_channel_mark_pre_answered(m_fsChannel); } break; + case SWITCH_MESSAGE_INDICATE_ANSWER: + AcceptIncoming(); + + if (!WaitForMedia()) + return SWITCH_STATUS_FALSE; + + if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) { + switch_channel_mark_answered(m_fsChannel); + } + break; + + case SWITCH_MESSAGE_INDICATE_DEFLECT: + ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection()); + break; + default: - PTRACE(3, "mod_opal\tReceived message " << msg->message_id << " on connection " << *this); + PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this); } return SWITCH_STATUS_SUCCESS; } +bool FSConnection::WaitForMedia() +{ + PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this); + m_rxAudioOpened.Wait(); + m_txAudioOpened.Wait(); + + if (IsReleased()) { + // Call got aborted + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n"); + return false; + } + + PTRACE(3, "mod_opal\tMedia started on connection " << *this); + return true; +} + + switch_status_t FSConnection::receive_event(switch_event_t *event) { - PTRACE(3, "mod_opal\tReceived event " << event->event_id << " on connection " << *this); + PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this); return SWITCH_STATUS_SUCCESS; } switch_status_t FSConnection::state_change() { - PTRACE(3, "mod_opal\tState changed on connection " << *this); + PTRACE(4, "mod_opal\tState changed on connection " << *this); return SWITCH_STATUS_SUCCESS; } @@ -1119,14 +966,14 @@ switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags) { - PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, false)); + PSafePtr stream = PSafePtrCast (GetMediaStream(mediaType, false)); return stream != NULL ? stream->read_frame(frame, flags) : SWITCH_STATUS_FALSE; } switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags) { - PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, true)); + PSafePtr stream = PSafePtrCast(GetMediaStream(mediaType, true)); return stream != NULL ? stream->write_frame(frame, flags) : SWITCH_STATUS_FALSE; } @@ -1135,24 +982,24 @@ switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource) : OpalMediaStream(conn, mediaFormat, sessionID, isSource) - , m_fsSession(conn.GetSession()) - , m_readRTP(0, 512) - , m_callOnStart(true) + , m_connection(conn) + , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE) { memset(&m_readFrame, 0, sizeof(m_readFrame)); - m_readFrame.codec = m_switchCodec; - m_readFrame.flags = SFF_RAW_RTP; } PBoolean FSMediaStream::Open() { - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession); - if (IsOpen()) { return true; } + switch_core_session_t *fsSession = m_connection.GetSession(); + switch_channel_t *fsChannel = m_connection.GetChannel(); + if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL) + return false; + bool isAudio; if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) { isAudio = true; @@ -1162,86 +1009,76 @@ PBoolean FSMediaStream::Open() return OpalMediaStream::Open(); } - m_fsChannel = switch_core_session_get_channel(m_fsSession); - int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits(); - if (IsSink()) { - m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec; - m_switchTimer = isAudio ? &tech_pvt->read_timer : &tech_pvt->vid_read_timer; + m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec; + m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer; + m_readFrame.codec = m_switchCodec; + m_readFrame.rate = mediaFormat.GetClockRate(); } else { - m_switchCodec = isAudio ? &tech_pvt->write_codec : &tech_pvt->vid_write_codec; + m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec; } // The following is performed on two different instances of this object. if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP mediaFormat.GetClockRate(), ptime, 1, // Channels SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings - switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) { + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { // Could not select a codecs using negotiated frames/packet, so try using default. if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP mediaFormat.GetClockRate(), 0, 1, // Channels SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings - switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) { - PTRACE(1, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Cannot initialise " << (IsSink()? "read" : "write") << ' ' + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " cannot initialise " << (IsSink()? "read" : "write") << ' ' << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); - switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); + switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); return false; } - PTRACE(2, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' ' + PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' ' << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); } - PTRACE(1, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " initialise " << - switch_channel_get_name(m_fsChannel) << (IsSink()? "read" : "write") << ' ' - << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); - if (IsSink()) { - m_readFrame.rate = mediaFormat.GetClockRate(); - if (isAudio) { - switch_core_session_set_read_codec(m_fsSession, m_switchCodec); + switch_core_session_set_read_codec(fsSession, m_switchCodec); if (switch_core_timer_init(m_switchTimer, "soft", m_switchCodec->implementation->microseconds_per_packet / 1000, m_switchCodec->implementation->samples_per_packet, - switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) { + switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " timer init failed on " << (IsSink()? "read" : "write") << ' ' + << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); switch_core_codec_destroy(m_switchCodec); m_switchCodec = NULL; return false; } } else { - switch_core_session_set_video_read_codec(m_fsSession, m_switchCodec); - switch_channel_set_flag(m_fsChannel, CF_VIDEO); + switch_core_session_set_video_read_codec(fsSession, m_switchCodec); + switch_channel_set_flag(fsChannel, CF_VIDEO); } } else { if (isAudio) { - switch_core_session_set_write_codec(m_fsSession, m_switchCodec); + switch_core_session_set_write_codec(fsSession, m_switchCodec); } else { - switch_core_session_set_video_write_codec(m_fsSession, m_switchCodec); - switch_channel_set_flag(m_fsChannel, CF_VIDEO); + switch_core_session_set_video_write_codec(fsSession, m_switchCodec); + switch_channel_set_flag(fsChannel, CF_VIDEO); } } - PTRACE(3, "mod_opal\tSet " << (IsSink()? "read" : "write") << ' ' - << mediaFormat.GetMediaType() << " codec to << " << mediaFormat << " for connection " << *this); + PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel) + << " initialised " << (IsSink()? "read" : "write") << ' ' + << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); return OpalMediaStream::Open(); } -PBoolean FSMediaStream::Close() +void FSMediaStream::InternalClose() { - if (!IsOpen()) - return false; - - /* forget these FS will properly destroy them for us */ - - m_switchTimer = NULL; - m_switchCodec = NULL; - - return OpalMediaStream::Close(); } @@ -1256,149 +1093,88 @@ PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const return false; } -bool FSMediaStream::CheckPatchAndLock() -{ - if (GetConnection().GetPhase() >= GetConnection().ReleasingPhase || !IsOpen()) - return false; - if (LockReadWrite()) { - if (!GetPatch() || !IsOpen()) { - UnlockReadWrite(); - return false; - } - return true; - } else { - return false; +int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const +{ + if (!IsOpen()) { + PTRACE(2, "mod_opal\tNot open!"); + return -1; } + + if (!m_switchCodec) { + PTRACE(2, "mod_opal\tNo codec!"); + return -1; + } + + if (!m_connection.IsChannelReady()) { + PTRACE(2, "mod_opal\tChannel not ready!"); + return -1; + } + + // We make referenced copy of pointer so can't be deleted out from under us + mediaPatch = m_mediaPatch; + if (mediaPatch == NULL) { + /*There is a race here... sometimes we make it here and m_mediaPatch is NULL + if we wait it shows up in 1ms, maybe there is a better way to wait. */ + PTRACE(3, "mod_opal\tPatch not ready!"); + return 1; + } + + return 0; } + switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags) { - - if (!m_switchCodec) { + PatchPtr mediaPatch; + switch (StartReadWrite(mediaPatch)) { + case -1 : return SWITCH_STATUS_FALSE; + case 1 : + return SWITCH_STATUS_SUCCESS; } - if (m_callOnStart) { - /* - There is a race here... sometimes we make it here and GetPatch() is NULL - if we wait it shows up in 1ms, maybe there is a better way to wait. - - */ - while(!GetPatch()) { - if (!m_fsChannel || !switch_channel_up(m_fsChannel)) { - return SWITCH_STATUS_FALSE; - } - switch_cond_next(); - } - if (CheckPatchAndLock()) { - GetPatch()->OnStartMediaPatch(); - m_callOnStart = false; - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - } - - m_readFrame.flags = 0; - - /* - while (switch_channel_ready(m_fsChannel)) { - if (CheckPatchAndLock()) { - if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) { - UnlockReadWrite(); - return SWITCH_STATUS_FALSE; - } - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - - if ((m_readFrame.datalen = m_readRTP.GetPayloadSize()) || switch_core_timer_check(&m_switchTimer, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { - if (m_readFrame.datalen) { - } else { - m_readFrame.flags = SFF_CNG; - } - break; - } - - switch_yield(1000); - } - */ - - if (switch_channel_test_private_flag(m_fsChannel, CF_NEED_FLUSH)) { - switch_channel_clear_private_flag(m_fsChannel, CF_NEED_FLUSH); - for(;;) { - if (CheckPatchAndLock()) { - if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) { - UnlockReadWrite(); - return SWITCH_STATUS_FALSE; - } - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - - if (!m_readRTP.GetPayloadSize()) { - m_readFrame.flags = SFF_CNG; - break; - } - } + if (m_connection.NeedFlushAudio()) { + mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer + m_readRTP.SetPayloadSize(0); } else { + m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet); - if (CheckPatchAndLock()) { - if (!m_switchTimer || !GetPatch()->GetSource().ReadPacket(m_readRTP)) { - UnlockReadWrite(); - return SWITCH_STATUS_FALSE; - } - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - - switch_core_timer_next(m_switchTimer); - - if (!(m_readFrame.datalen = m_readRTP.GetPayloadSize())) { - m_readFrame.flags = SFF_CNG; + if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) { + return SWITCH_STATUS_FALSE; } } - if (!switch_channel_ready(m_fsChannel)) { + if (!m_switchTimer) { + PTRACE(2, "mod_opal\tread_frame: no timer!"); return SWITCH_STATUS_FALSE; } + switch_core_timer_next(m_switchTimer); if (!switch_core_codec_ready(m_switchCodec)) { + PTRACE(2, "mod_opal\tread_frame: codec not ready!"); return SWITCH_STATUS_FALSE; } - //switch_core_timer_step(&m_switchTimer); +#if IMPLEMENT_MULTI_FAME_AUDIO + // Repackage frames in incoming packet to agree with what FS expects. + // Not implmented yet!!!!!!!!! + // Cheating and only supporting one frame per packet +#endif - if (m_readFrame.payload == RTP_DataFrame::CN || m_readFrame.payload == RTP_DataFrame::Cisco_CN) { - m_readFrame.flags = SFF_CNG; - } - - if (m_readFrame.flags & SFF_CNG) { - m_readFrame.buflen = sizeof(m_buf); - m_readFrame.data = m_buf; - m_readFrame.packet = NULL; - m_readFrame.packetlen = 0; - m_readFrame.timestamp = 0; - m_readFrame.m = SWITCH_FALSE; - m_readFrame.seq = 0; - m_readFrame.ssrc = 0; - m_readFrame.codec = m_switchCodec; - } else { - m_readFrame.buflen = m_readRTP.GetSize(); - m_readFrame.data = m_readRTP.GetPayloadPtr(); - m_readFrame.packet = m_readRTP.GetPointer(); - m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen; - m_readFrame.payload = (switch_payload_t) m_readRTP.GetPayloadType(); - m_readFrame.timestamp = m_readRTP.GetTimestamp(); - m_readFrame.m = (switch_bool_t) m_readRTP.GetMarker(); - m_readFrame.seq = m_readRTP.GetSequenceNumber(); - m_readFrame.ssrc = m_readRTP.GetSyncSource(); - m_readFrame.codec = m_switchCodec; - } + m_readFrame.buflen = m_readRTP.GetSize(); + m_readFrame.data = m_readRTP.GetPayloadPtr(); + m_readFrame.datalen = m_readRTP.GetPayloadSize(); + m_readFrame.packet = m_readRTP.GetPointer(); + m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen; + m_readFrame.timestamp = m_readRTP.GetTimestamp(); + m_readFrame.seq = m_readRTP.GetSequenceNumber(); + m_readFrame.ssrc = m_readRTP.GetSyncSource(); + m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE; + m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType(); + m_readFrame.flags = m_readFrame.datalen == 0 || + m_readFrame.payload == RTP_DataFrame::CN || + m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0; *frame = &m_readFrame; @@ -1408,69 +1184,35 @@ switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags) { - if (!switch_channel_ready(m_fsChannel)) { + PatchPtr mediaPatch; + switch (StartReadWrite(mediaPatch)) { + case -1 : return SWITCH_STATUS_FALSE; - } - - if (m_callOnStart) { - if (CheckPatchAndLock()) { - GetPatch()->OnStartMediaPatch(); - m_callOnStart = false; - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - } - - if ((frame->flags & SFF_CNG)) { + case 1 : return SWITCH_STATUS_SUCCESS; } if ((frame->flags & SFF_RAW_RTP) != 0) { - RTP_DataFrame rtp((const BYTE *) frame->packet, frame->packetlen, false); - - if (CheckPatchAndLock()) { - if (GetPatch()->PushFrame(rtp)) { - UnlockReadWrite(); - return SWITCH_STATUS_SUCCESS; - } - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; - } - } - - /* If we reach this code it means a call to an ivr or something else that does not generate timestamps - Its possible that frame->timestamp is set but not guarenteed and is best ignored for the time being. - We are probably relying on the rtp stack to generate the timestamp and ssrc for us at this point. - As a quick hack I am going to keep a sample counter and increment it by frame->samples but it would be - better if we could engage whatever it is in opal that makes it generate the timestamp. - */ - - RTP_DataFrame rtp(frame->datalen); - rtp.SetPayloadType(mediaFormat.GetPayloadType()); - - m_timeStamp += frame->samples; - rtp.SetTimestamp(m_timeStamp); - - //rtp.SetTimestamp(frame->timestamp); - //rtp.SetSyncSource(frame->ssrc); - //rtp.SetMarker(frame->m); - - memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen); - - if (CheckPatchAndLock()) { - if (GetPatch()->PushFrame(rtp)) { - UnlockReadWrite(); - return SWITCH_STATUS_SUCCESS; - } - UnlockReadWrite(); - } else { - return SWITCH_STATUS_FALSE; + RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false); + return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } + RTP_DataFrame rtp(frame->datalen); + memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen); - return SWITCH_STATUS_FALSE; + rtp.SetPayloadType(mediaFormat.GetPayloadType()); + + /* Not sure what FS is going to give us! + Suspect it depends on the mod on the other side sending it. */ + if (frame->timestamp != 0) + timestamp = frame->timestamp; + else if (frame->samples != 0) + timestamp += frame->samples; + else + timestamp += m_switchCodec->implementation->samples_per_packet; + rtp.SetTimestamp(timestamp); + + return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE; } diff --git a/src/mod/endpoints/mod_opal/mod_opal.h b/src/mod/endpoints/mod_opal/mod_opal.h index 1915d54b90..07ba28a1f3 100644 --- a/src/mod/endpoints/mod_opal/mod_opal.h +++ b/src/mod/endpoints/mod_opal/mod_opal.h @@ -4,6 +4,7 @@ * Version: MPL 1.1 * * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) + * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au) * * 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 @@ -46,6 +47,8 @@ #define HAVE_APR #include #include + + #define MODNAME "mod_opal" @@ -53,206 +56,239 @@ class FSEndPoint; class FSManager; -struct mod_opal_globals { - int trace_level; - char *codec_string; - char *context; - char *dialplan; +class FSProcess : public PLibraryProcess +{ + PCLASSINFO(FSProcess, PLibraryProcess); + public: + FSProcess(); + ~FSProcess(); + + bool Initialise(switch_loadable_module_interface_t *iface); + + FSManager & GetManager() const + { + return *m_manager; + } + + protected: + FSManager * m_manager; }; -extern struct mod_opal_globals mod_opal_globals; + +struct FSListener +{ + FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { } + + PString m_name; + PIPSocket::Address m_address; + uint16_t m_port; +}; -class FSProcess:public PLibraryProcess { - PCLASSINFO(FSProcess, PLibraryProcess); +class FSManager : public OpalManager +{ + PCLASSINFO(FSManager, OpalManager); public: - FSProcess(); - ~FSProcess(); + FSManager(); - bool Initialise(switch_loadable_module_interface_t *iface); + bool Initialise(switch_loadable_module_interface_t *iface); - FSManager & GetManager() const { - return *m_manager; - } protected: - FSManager * m_manager; -}; + switch_status_t ReadConfig(int reload); - -struct FSListener { - FSListener() { - } PString name; - OpalTransportAddress listenAddress; - PString localUserName; - PString gatekeeper; -}; - - -class FSCall:public OpalCall { - PCLASSINFO(FSCall, OpalCall); - public: - FSCall(OpalManager & manager); - virtual PBoolean OnSetUp(OpalConnection & connection); -}; - - -class FSManager:public OpalManager { - PCLASSINFO(FSManager, OpalManager); - - public: - FSManager(); - - bool Initialise(switch_loadable_module_interface_t *iface); - - switch_status_t ReadConfig(int reload); - - switch_endpoint_interface_t *GetSwitchInterface() const { - return m_FreeSwitch; - } virtual OpalCall *CreateCall(void *userData); + switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; } + const PString & GetContext() const { return m_context; } + const PString & GetDialPlan() const { return m_dialplan; } + const PString & GetCodecPrefs() const { return m_codecPrefs; } private: - switch_endpoint_interface_t *m_FreeSwitch; + switch_endpoint_interface_t *m_FreeSwitch; - H323EndPoint *m_h323ep; - IAX2EndPoint *m_iaxep; - FSEndPoint *m_fsep; + H323EndPoint *m_h323ep; + IAX2EndPoint *m_iaxep; + FSEndPoint *m_fsep; - PString m_gkAddress; - PString m_gkIdentifer; - PString m_gkInterface; + PString m_context; + PString m_dialplan; + PString m_codecPrefs; + PString m_gkAddress; + PString m_gkIdentifer; + PString m_gkInterface; - list < FSListener > m_listeners; + list m_listeners; +}; + + +class FSEndPoint : public OpalLocalEndPoint +{ + PCLASSINFO(FSEndPoint, OpalLocalEndPoint); + public: + FSEndPoint(FSManager & manager); + + virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions); + + FSManager & GetManager() const { return m_manager; } + + protected: + FSManager & m_manager; }; class FSConnection; -typedef struct { - switch_timer_t read_timer; - switch_codec_t read_codec; - switch_codec_t write_codec; - - switch_timer_t vid_read_timer; - switch_codec_t vid_read_codec; - switch_codec_t vid_write_codec; - FSConnection *me; -} opal_private_t; -class FSEndPoint:public OpalLocalEndPoint { - PCLASSINFO(FSEndPoint, OpalLocalEndPoint); +class FSMediaStream : public OpalMediaStream +{ + PCLASSINFO(FSMediaStream, OpalMediaStream); public: - FSEndPoint(FSManager & manager); + FSMediaStream( + FSConnection & conn, + const OpalMediaFormat & mediaFormat, ///< Media format for stream + unsigned sessionID, ///< Session number for stream + bool isSource ///< Is a source stream + ); - virtual bool OnIncomingCall(OpalLocalConnection &); - virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions); + virtual PBoolean Open(); + virtual PBoolean IsSynchronous() const; + virtual PBoolean RequiresPatchThread(OpalMediaStream *) const; + + switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags); + switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags); + + protected: + virtual void InternalClose(); + int StartReadWrite(PatchPtr & mediaPatch) const; + + private: + bool CheckPatchAndLock(); + + FSConnection &m_connection; + switch_timer_t *m_switchTimer; + switch_codec_t *m_switchCodec; + switch_frame_t m_readFrame; + RTP_DataFrame m_readRTP; }; #define DECLARE_CALLBACK0(name) \ static switch_status_t name(switch_core_session_t *session) { \ - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \ - return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name() : SWITCH_STATUS_FALSE; } \ -switch_status_t name() + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \ + switch_status_t name() #define DECLARE_CALLBACK1(name, type1, name1) \ static switch_status_t name(switch_core_session_t *session, type1 name1) { \ - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \ - return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1) : SWITCH_STATUS_FALSE; } \ -switch_status_t name(type1 name1) + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \ + switch_status_t name(type1 name1) #define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \ static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \ - opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \ - return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \ -switch_status_t name(type1 name1, type2 name2, type3 name3) + FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \ + return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \ + switch_status_t name(type1 name1, type2 name2, type3 name3) -class FSConnection:public OpalLocalConnection { - PCLASSINFO(FSConnection, OpalLocalConnection) +class FSConnection : public OpalLocalConnection +{ + PCLASSINFO(FSConnection, OpalLocalConnection) public: - FSConnection(OpalCall & call, - FSEndPoint & endpoint, - void *userData, - unsigned options, - OpalConnection::StringOptions * stringOptions, - switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel); + struct outgoing_params { + switch_event_t *var_event; + switch_caller_profile_t *outbound_profile; + switch_core_session_t **new_session; + switch_memory_pool_t **pool; + switch_originate_flag_t flags; + switch_call_cause_t *cancel_cause; + switch_call_cause_t fail_cause; + }; - virtual bool OnIncoming(); - virtual void OnReleased(); - virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia); - virtual void OnAlerting(); - virtual void OnEstablished(); - virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean); - virtual PBoolean OnOpenMediaStream(OpalMediaStream & stream); - virtual OpalMediaFormatList GetMediaFormats() const; - virtual PBoolean SendUserInputTone(char tone, unsigned duration); - virtual PBoolean SendUserInputString(const PString & value); + FSConnection(OpalCall & call, + FSEndPoint & endpoint, + unsigned options, + OpalConnection::StringOptions * stringOptions, + outgoing_params * params); - void SetCodecs(); + virtual bool OnOutgoingSetUp(); + virtual bool OnIncoming(); + virtual void OnReleased(); + virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia); + virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean); + virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch); + virtual OpalMediaFormatList GetMediaFormats() const; + virtual PBoolean SendUserInputTone(char tone, unsigned duration); - DECLARE_CALLBACK0(on_init); - DECLARE_CALLBACK0(on_routing); - DECLARE_CALLBACK0(on_execute); + void SetCodecs(); + bool WaitForMedia(); - DECLARE_CALLBACK0(on_exchange_media); - DECLARE_CALLBACK0(on_soft_execute); + DECLARE_CALLBACK0(on_init); + DECLARE_CALLBACK0(on_destroy); + DECLARE_CALLBACK0(on_routing); + DECLARE_CALLBACK0(on_execute); + DECLARE_CALLBACK0(on_hangup); - DECLARE_CALLBACK1(kill_channel, int, sig); - DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf); - DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg); - DECLARE_CALLBACK1(receive_event, switch_event_t *, event); - DECLARE_CALLBACK0(state_change); - DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id); - DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id); - DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id); - DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id); + DECLARE_CALLBACK0(on_exchange_media); + DECLARE_CALLBACK0(on_soft_execute); - switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags); - switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags); + DECLARE_CALLBACK1(kill_channel, int, sig); + DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf); + DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg); + DECLARE_CALLBACK1(receive_event, switch_event_t *, event); + DECLARE_CALLBACK0(state_change); + DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id); + DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id); + DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id); + DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id); - switch_core_session_t *GetSession() const { - return m_fsSession; - } private: - FSEndPoint & m_endpoint; - switch_core_session_t *m_fsSession; - switch_channel_t *m_fsChannel; - PSyncPoint m_rxAudioOpened; - PSyncPoint m_txAudioOpened; - OpalMediaFormatList m_switchMediaFormats; -}; + switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags); + switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags); + __inline switch_core_session_t *GetSession() const + { + return m_fsSession; + } -class FSMediaStream:public OpalMediaStream { - PCLASSINFO(FSMediaStream, OpalMediaStream); - public: - FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, ///< Media format for stream - unsigned sessionID, ///< Session number for stream - bool isSource ///< Is a source stream - ); + __inline switch_channel_t *GetChannel() const + { + return m_fsChannel; + } - virtual PBoolean Open(); - virtual PBoolean Close(); - virtual PBoolean IsSynchronous() const; - virtual PBoolean RequiresPatchThread(OpalMediaStream *) const; + bool IsChannelReady() const + { + return m_fsChannel != NULL && switch_channel_ready(m_fsChannel); + } - switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags); - switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags); + bool NeedFlushAudio() + { + if (!m_flushAudio) + return false; + m_flushAudio = false; + return true; + } private: - switch_core_session_t *m_fsSession; - switch_channel_t *m_fsChannel; - switch_timer_t *m_switchTimer; - switch_codec_t *m_switchCodec; - switch_frame_t m_readFrame; - unsigned char m_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; - RTP_DataFrame m_readRTP; - bool m_callOnStart; - uint32_t m_timeStamp; + FSEndPoint &m_endpoint; + switch_core_session_t *m_fsSession; + switch_channel_t *m_fsChannel; + PSyncPoint m_rxAudioOpened; + PSyncPoint m_txAudioOpened; + OpalMediaFormatList m_switchMediaFormats; - bool CheckPatchAndLock(); + // If FS ever supports more than one audio and one video, this needs to change + switch_timer_t m_read_timer; + switch_codec_t m_read_codec; + switch_codec_t m_write_codec; + + switch_timer_t m_vid_read_timer; + switch_codec_t m_vid_read_codec; + switch_codec_t m_vid_write_codec; + + bool m_flushAudio; + + friend PBoolean FSMediaStream::Open(); }; From 22bc29b5e0b4be2f342d0f1ec34da89adf2bbd1d Mon Sep 17 00:00:00 2001 From: root Date: Mon, 30 Jul 2012 12:32:46 -0400 Subject: [PATCH 2/3] sending termination in-service/out-of-service service change for termination whenever there is alarm indication from freetdm --- libs/freetdm/mod_freetdm/tdm.c | 7 +- .../mod_media_gateway/media_gateway.c | 39 ++++- .../media_gateway_cmd_handler.c | 13 +- .../mod_media_gateway/media_gateway_stack.h | 6 +- .../mod_media_gateway/media_gateway_utils.c | 12 +- .../mod_media_gateway/media_gateway_xml.c | 154 +++++++++++------- .../mod_media_gateway/mod_media_gateway.c | 30 +--- .../mod_media_gateway/mod_media_gateway.h | 8 +- 8 files changed, 167 insertions(+), 102 deletions(-) diff --git a/libs/freetdm/mod_freetdm/tdm.c b/libs/freetdm/mod_freetdm/tdm.c index 790f7c17d3..0cc02a85c7 100644 --- a/libs/freetdm/mod_freetdm/tdm.c +++ b/libs/freetdm/mod_freetdm/tdm.c @@ -84,7 +84,7 @@ switch_io_routines_t ctdm_io_routines = { .receive_message = channel_receive_message }; -static void ctdm_report_alarms(ftdm_channel_t *channel, const char* mg_profile_name) +static void ctdm_report_alarms(ftdm_channel_t *channel) { switch_event_t *event = NULL; ftdm_alarm_flag_t alarmflag = 0; @@ -109,8 +109,6 @@ static void ctdm_report_alarms(ftdm_channel_t *channel, const char* mg_profile_n switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "ftdm-alarm-trap"); } - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "mg-profile-name", mg_profile_name); - if (alarmflag & FTDM_ALARM_RED) { switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "red"); } @@ -149,7 +147,6 @@ static void ctdm_event_handler(switch_event_t *event) const char *chan_number = NULL; uint32_t chan_id = 0; const char *cond = switch_event_get_header(event, "condition"); - const char *mg_profile_name = switch_event_get_header(event, "mg-profile-name"); if (zstr(cond)) { return; @@ -187,7 +184,7 @@ static void ctdm_event_handler(switch_event_t *event) return; } - ctdm_report_alarms(channel, mg_profile_name); + ctdm_report_alarms(channel); } } break; diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway.c b/src/mod/endpoints/mod_media_gateway/media_gateway.c index 7e13fbaebe..8beeedc3ca 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway.c @@ -22,6 +22,8 @@ megaco_profile_t *megaco_profile_locate(const char *name) return profile; } + + mg_peer_profile_t *megaco_peer_profile_locate(const char *name) { mg_peer_profile_t *profile = switch_core_hash_find_rdlock(megaco_globals.peer_profile_hash, name, megaco_globals.peer_profile_rwlock); @@ -171,7 +173,6 @@ switch_status_t megaco_prepare_tdm_termination(mg_termination_t *term) return SWITCH_STATUS_SUCCESS; } -/* @Kapil Call this function once H.248 link is up */ switch_status_t megaco_check_tdm_termination(mg_termination_t *term) { switch_event_t *event = NULL; @@ -186,7 +187,6 @@ switch_status_t megaco_check_tdm_termination(mg_termination_t *term) switch_event_add_header(event, SWITCH_STACK_BOTTOM, "span-name", "%s", term->u.tdm.span_name); switch_event_add_header(event, SWITCH_STACK_BOTTOM, "chan-number", "%d", term->u.tdm.channel); switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "condition", "mg-tdm-check"); - switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "mg-profile-name", term->profile->name); switch_event_fire(&event); return SWITCH_STATUS_SUCCESS; @@ -241,7 +241,34 @@ mg_termination_t *megaco_choose_termination(megaco_profile_t *profile, const cha return term; } -mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile, char *span_name, char *chan_number) +mg_termination_t *megaco_term_locate_by_span_chan_id(const char *span_name, const char *chan_number) +{ + void *val = NULL; + switch_hash_index_t *hi = NULL; + mg_termination_t *term = NULL; + megaco_profile_t *profile = NULL; + const void *var; + + switch_assert(span_name); + switch_assert(chan_number); + + /* span + chan will be unique across all the mg_profiles * + * loop through all profiles and then all terminations to * + * get the mg termination associated with input span+chan */ + + switch_thread_rwlock_rdlock(megaco_globals.profile_rwlock); + for (hi = switch_hash_first(NULL, megaco_globals.profile_hash); hi; hi = switch_hash_next(hi)) { + switch_hash_this(hi, &var, NULL, &val); + profile = (megaco_profile_t *) val; + + if(NULL != (term = megaco_find_termination_by_span_chan(profile, span_name, chan_number))) break; + } + switch_thread_rwlock_unlock(megaco_globals.profile_rwlock); + + return term; +} + +mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile, const char *span_name, const char *chan_number) { void *val = NULL; switch_hash_index_t *hi = NULL; @@ -262,7 +289,8 @@ mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile if ((!strcasecmp(span_name, term->u.tdm.span_name))&& (atoi(chan_number) == term->u.tdm.channel)) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, - "Got term[%s] associated with span[%s], channel[%s]\n",term->name, span_name, chan_number); + "Got term[%s] associated with span[%s], channel[%s] in MG profile[%s]\n", + term->name, span_name, chan_number, profile->name); found = 0x01; break; } @@ -271,7 +299,8 @@ mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile if(!found){ term = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, - " Not able to find termination associated with span[%s], channel[%s]\n", span_name, chan_number); + " MG profile[%s] does not have termination associated with span[%s], channel[%s]\n", + profile->name, span_name, chan_number); } return term; diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c index ca2209b087..f392612cbb 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c @@ -2288,18 +2288,15 @@ U32 get_txn_id(){ return outgoing_txn_id; } /*****************************************************************************************************************************/ -switch_status_t mg_send_term_service_change(char* mg_profile_name,char *span_name, char *chan_number, mg_term_states_e term_state) +switch_status_t mg_send_term_service_change(char *span_name, char *chan_number, mg_term_states_e term_state) { mg_termination_t* term = NULL; switch_status_t ret = SWITCH_STATUS_SUCCESS; - megaco_profile_t *profile = NULL; switch_assert(span_name); switch_assert(chan_number); - profile = megaco_profile_locate(mg_profile_name); - - term = megaco_find_termination_by_span_chan(profile, span_name, chan_number); + term = megaco_term_locate_by_span_chan_id(span_name, chan_number); if(!term || !term->profile){ return SWITCH_STATUS_FALSE; @@ -2309,11 +2306,17 @@ switch_status_t mg_send_term_service_change(char* mg_profile_name,char *span_nam { case MG_TERM_SERVICE_STATE_IN_SERVICE: { + /* set INS flag...clear oos flag */ + switch_clear_flag(term, MG_OUT_OF_SERVICE); + switch_set_flag(term, MG_IN_SERVICE); ret = mg_send_ins_service_change(term->profile, term->name, 0x00 ); break; } case MG_TERM_SERVICE_STATE_OUT_OF_SERVICE: { + /* set OOS flag...clear ins flag */ + switch_clear_flag(term, MG_IN_SERVICE); + switch_set_flag(term, MG_OUT_OF_SERVICE); ret = mg_send_oos_service_change(term->profile, term->name, 0x00 ); break; } diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h b/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h index 3e2ec81bdb..994b1a573a 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_stack.h @@ -164,8 +164,7 @@ void mg_util_set_err_string ( MgStr *errTxt, char* str); switch_status_t mg_build_sdp(MgMgcoMediaDesc* out, MgMgcoMediaDesc* inc, megaco_profile_t* mg_profile, mg_termination_t* term, CmMemListCp *memCp); switch_status_t mg_add_local_descriptor(MgMgcoMediaDesc* media, megaco_profile_t* mg_profile, mg_termination_t* term, CmMemListCp *memCp); -switch_status_t mg_send_term_service_change(char* mg_profile_name, char *span_name, char *chan_number, mg_term_states_e term_state); -mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile , char *span_name, char *chan_number); +switch_status_t mg_send_term_service_change(char *span_name, char *chan_number, mg_term_states_e term_state); switch_status_t sng_mgco_cfg(megaco_profile_t* profile); @@ -190,7 +189,8 @@ void mg_util_set_txn_string(MgStr *errTxt, U32 *txnId); switch_status_t mg_build_mgco_err_request(MgMgcoInd **errcmd,U32 trans_id, MgMgcoContextId *ctxt_id, U32 err, MgStr *errTxt); switch_status_t mg_send_audit_rsp(SuId suId, MgMgcoCommand *req); switch_status_t handle_mg_audit_cmd(SuId suId, MgMgcoCommand *auditReq); -switch_status_t mg_stack_termination_is_in_service(char* term_str, int len); +switch_status_t mg_stack_termination_is_in_service(megaco_profile_t* mg_profile, char* term_str, int len); +void mg_create_tdm_term(megaco_profile_t *profile, const char *tech, const char *channel_prefix, const char *prefix, int j); void mg_util_set_cmd_name_string (MgStr *errTxt, MgMgcoCommand *cmd); switch_status_t mgco_init_ins_service_change(SuId suId); diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c b/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c index f7e9dde544..c565da7e6b 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_utils.c @@ -80,10 +80,16 @@ switch_status_t mg_stack_free_mem(void* msg) /*****************************************************************************************************************************/ -/* TODO - Matt - to see if term is in service or not */ -switch_status_t mg_stack_termination_is_in_service(char* term_str,int len) +/* To see if term is in service or not */ +switch_status_t mg_stack_termination_is_in_service(megaco_profile_t* mg_profile, char* term_str,int len) { - return SWITCH_STATUS_SUCCESS; + mg_termination_t* term = NULL; + term = megaco_find_termination(mg_profile, term_str); + if(switch_test_flag(term, MG_IN_SERVICE)){ + return SWITCH_STATUS_SUCCESS; + } else { + return SWITCH_STATUS_FALSE; + } } /*****************************************************************************************************************************/ diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c b/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c index 6c87afa780..9890f152c3 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c @@ -65,62 +65,74 @@ switch_status_t config_profile(megaco_profile_t *profile, switch_bool_t reload) profile->idx = ++mg_sap_id; - - - if ((mg_phys_terms = switch_xml_child(mg_interface, "physical_terminations"))) { - for (mg_term = switch_xml_child(mg_phys_terms, "map"); mg_term; mg_term = mg_term->next) { - switch_memory_pool_t *pool; - mg_termination_t *term; - // - const char *prefix = switch_xml_attr(mg_term, "termination-id-prefix"); - //const char *sztermination_id_base = switch_xml_attr(mg_term, "termination-id-base"); - const char *tech = switch_xml_attr(mg_term, "tech"); - const char *channel_prefix = switch_xml_attr(mg_term, "channel-prefix"); - const char *channel_map = switch_xml_attr(mg_term, "channel-map"); - - - if (!zstr(channel_map)) { - /* Split channel-map */ - char *channel_map_dup = strdup(channel_map); - char *chanmap[24]; - int chanmap_count, i; - chanmap_count = switch_split(channel_map_dup, ',', chanmap); - for (i = 0; i < chanmap_count; i++) { - char *p = strchr(chanmap[i], '-'); - if (p) { - int startchan, endchan, j; - *p++ = '\0'; - startchan = atoi(chanmap[i]); - endchan = atoi(p); - - for (j = startchan; j < endchan; j++) { - switch_core_new_memory_pool(&pool); - term = switch_core_alloc(profile->pool, sizeof *term); - term->pool = pool; - term->type = MG_TERM_TDM; - term->profile = profile; - term->mg_ctxt = NULL; - term->tech = switch_core_strdup(pool, tech); - term->active_events = NULL; - term->name = switch_core_sprintf(pool, "%s%d", prefix, j); - term->u.tdm.channel = j; - term->u.tdm.span_name = switch_core_strdup(pool, channel_prefix); - - switch_core_hash_insert_wrlock(profile->terminations, term->name, term, profile->terminations_rwlock); - term->next = profile->physical_terminations; - profile->physical_terminations = term; - - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Mapped termination [%s] to freetdm span: %s chan: %d\n", term->name, term->u.tdm.span_name, term->u.tdm.channel); - megaco_prepare_tdm_termination(term); - } - } - } - - free(channel_map_dup); - } - } - } + + if ((mg_phys_terms = switch_xml_child(mg_interface, "physical_terminations"))) { + for (mg_term = switch_xml_child(mg_phys_terms, "map"); mg_term; mg_term = mg_term->next) { + // + const char *prefix = switch_xml_attr(mg_term, "termination-id-prefix"); + //const char *sztermination_id_base = switch_xml_attr(mg_term, "termination-id-base"); + const char *tech = switch_xml_attr(mg_term, "tech"); + const char *channel_prefix = switch_xml_attr(mg_term, "channel-prefix"); + const char *channel_map = switch_xml_attr(mg_term, "channel-map"); + char *p = NULL; + + if (!zstr(channel_map)) { + /* Split channel-map */ + char *channel_map_dup = strdup(channel_map); + char *chanmap[24] = {0}; + int chanmap_count = 0; + int i = 0; + int startchan, endchan, j; + + /* we can have following combinations * + * i) only one channel i.e. channel-map="1" + * ii) only one chanel range i.e channel-map="1-15" + * iii) full channel range i.e. channel-map="1-15,17-31" + */ + + + chanmap_count = switch_split(channel_map_dup, ',', chanmap); + + if(1 == chanmap_count) { + p = strchr(channel_map_dup, '-'); + if(NULL != p){ + /* case (ii) */ + i = switch_split(channel_map_dup, '-', chanmap); + if(i && chanmap[0] && chanmap[1]) { + startchan = atoi(chanmap[0]); + endchan = atoi(chanmap[1]); + for (j = startchan; j < endchan; j++) { + mg_create_tdm_term(profile, tech, channel_prefix, prefix, j); + } + } + }else{ + /* case (i) */ + p = channel_map_dup; + startchan = endchan = atoi(p); + mg_create_tdm_term(profile, tech, channel_prefix, prefix, startchan); + } + }else + { + /* case (iii) */ + for (i = 0; i < chanmap_count; i++) { + p = strchr(chanmap[i], '-'); + if (p) { + *p++ = '\0'; + startchan = atoi(chanmap[i]); + endchan = atoi(p); + + for (j = startchan; j < endchan; j++) { + mg_create_tdm_term(profile, tech, channel_prefix, prefix, j); + } + } + } + } + + free(channel_map_dup); + } + } + } /* we should break from here , profile name should be unique */ @@ -129,7 +141,7 @@ switch_status_t config_profile(megaco_profile_t *profile, switch_bool_t reload) if (!mg_interface) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Error profile %s not found\n", profile->name); - return SWITCH_STATUS_FALSE; + return SWITCH_STATUS_FALSE; } /* go through the peer configuration and get the mg profile associated peers only */ @@ -169,7 +181,7 @@ switch_status_t config_profile(megaco_profile_t *profile, switch_bool_t reload) } } } - + /* configure the MEGACO stack */ status = sng_mgco_cfg(profile); @@ -184,6 +196,34 @@ done: return status; } +/****************************************************************************************************************************/ +void mg_create_tdm_term(megaco_profile_t *profile, const char *tech, const char *channel_prefix, const char *prefix, int chan_num) +{ + mg_termination_t *term; + switch_memory_pool_t *pool; + + switch_core_new_memory_pool(&pool); + term = switch_core_alloc(profile->pool, sizeof *term); + term->pool = pool; + term->type = MG_TERM_TDM; + term->profile = profile; + term->mg_ctxt = NULL; + term->tech = switch_core_strdup(pool, tech); + term->active_events = NULL; + term->name = switch_core_sprintf(pool, "%s%d", prefix, chan_num); + term->u.tdm.channel = chan_num; + term->u.tdm.span_name = switch_core_strdup(pool, channel_prefix); + + switch_core_hash_insert_wrlock(profile->terminations, term->name, term, profile->terminations_rwlock); + term->next = profile->physical_terminations; + profile->physical_terminations = term; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, + "Mapped termination [%s] to freetdm span: %s chan: %d\n", + term->name, term->u.tdm.span_name, term->u.tdm.channel); + megaco_prepare_tdm_termination(term); + +} /****************************************************************************************************************************/ switch_status_t mg_config_cleanup(megaco_profile_t* profile) { diff --git a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c index 97158fdcea..22c07275a8 100644 --- a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c +++ b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c @@ -64,38 +64,23 @@ static void mg_event_handler(switch_event_t *event) const char *span_name = NULL; const char *chan_number = NULL; const char *cond = NULL; - const char *mg_profile_name = NULL; - cond = switch_event_get_header(event, "condition"); if (zstr(cond)) { - printf("Condition NULL, returning \n"); return; } - mg_profile_name = switch_event_get_header(event, "mg-profile-name"); - if (zstr(mg_profile_name)) { - printf("mg_profile_name NULL, returning \n"); - return; - } - - span_name = switch_event_get_header(event, "span-name"); + span_name = switch_event_get_header(event, "span-name"); chan_number = switch_event_get_header(event, "chan-number"); if (!strcmp(cond, "ftdm-alarm-trap")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, - "ftdm-alarm-trap for span_name[%s] chan_number[%s] associated with MG profile[%s]\n", - span_name,chan_number, mg_profile_name); - /* @KAPIL: TDM is in alarm, notify MGC */ - mg_send_term_service_change( - (char*)mg_profile_name, (char*)span_name, (char*)chan_number, MG_TERM_SERVICE_STATE_OUT_OF_SERVICE); + "ftdm-alarm-trap for span_name[%s] chan_number[%s]\n", span_name,chan_number); + mg_send_term_service_change((char*)span_name, (char*)chan_number, MG_TERM_SERVICE_STATE_OUT_OF_SERVICE); } else if (!strcmp(cond, "ftdm-alarm-clear")) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, - "ftdm-alarm-clear for span_name[%s] chan_number[%s] associated with MG profile[%s] \n", - span_name,chan_number, mg_profile_name); - /* TDM alarm cleared, notify MGC */ - mg_send_term_service_change( - (char*)mg_profile_name, (char*)span_name, (char*)chan_number, MG_TERM_SERVICE_STATE_IN_SERVICE); + "ftdm-alarm-clear for span_name[%s] chan_number[%s] \n", span_name,chan_number); + mg_send_term_service_change( (char*)span_name, (char*)chan_number, MG_TERM_SERVICE_STATE_IN_SERVICE); } } break; @@ -561,8 +546,9 @@ void handle_mgco_cmd_ind(Pst *pst, SuId suId, MgMgcoCommand* cmd) } /*If term type is other then check if that term is configured with us..for term type CHOOSE/ALL , no need to check */ - if (MGT_TERMID_OTHER == termId->type.val){ - if(SWITCH_STATUS_FALSE == mg_stack_termination_is_in_service((char*)termId->name.lcl.val, termId->name.lcl.len)){ + if ((CH_CMD_TYPE_IND == cmd->cmdType.val) && + (MGT_TERMID_OTHER == termId->type.val)){ + if(SWITCH_STATUS_FALSE == mg_stack_termination_is_in_service(mg_profile, (char*)termId->name.lcl.val, termId->name.lcl.len)){ mg_util_set_term_string(&errTxt, termId); err_code = MGT_MGCO_RSP_CODE_UNKNOWN_TERM_ID; goto error; diff --git a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h index 86acb28861..06cbffca11 100644 --- a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h +++ b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.h @@ -89,8 +89,10 @@ typedef struct mg_context_s mg_context_t; typedef struct mg_termination_s mg_termination_t; enum { - MGT_ALLOCATED = (1 << 0), - MGT_ACTIVE = (1 << 1), + MGT_ALLOCATED = (1 << 0), + MGT_ACTIVE = (1 << 1), + MG_IN_SERVICE = (1 << 2), + MG_OUT_OF_SERVICE = (1 << 3), } mg_termination_flags; @@ -222,8 +224,10 @@ static inline megaco_codec_t megaco_codec_parse(const char *codec) { megaco_profile_t *megaco_profile_locate(const char *name); +mg_termination_t *megaco_term_locate_by_span_chan_id(const char *span_name, const char *chan_number); mg_peer_profile_t *megaco_peer_profile_locate(const char *name); void megaco_profile_release(megaco_profile_t *profile); +mg_termination_t* megaco_find_termination_by_span_chan(megaco_profile_t *profile, const char *span_name, const char *chan_number); switch_status_t megaco_profile_start(const char *profilename); switch_status_t megaco_profile_destroy(megaco_profile_t **profile); From 0c87442dd78fd3492aa4006b8a46fdb3bea2dfb0 Mon Sep 17 00:00:00 2001 From: root Date: Mon, 30 Jul 2012 13:17:39 -0400 Subject: [PATCH 3/3] adding termination service states(in-service/out-of-service) flags and reject incoming megaco request if requested termination is not in service --- libs/freetdm/mod_freetdm/tdm.c | 5 +++++ .../mod_media_gateway/media_gateway.c | 3 ++- .../media_gateway_cmd_handler.c | 20 +++++++++++-------- .../mod_media_gateway/media_gateway_xml.c | 2 +- .../mod_media_gateway/mod_media_gateway.c | 1 + 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/libs/freetdm/mod_freetdm/tdm.c b/libs/freetdm/mod_freetdm/tdm.c index 0cc02a85c7..22ffb5463e 100644 --- a/libs/freetdm/mod_freetdm/tdm.c +++ b/libs/freetdm/mod_freetdm/tdm.c @@ -128,6 +128,8 @@ static void ctdm_report_alarms(ftdm_channel_t *channel) switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "alarm", "general"); } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Reporting [%s] alarms for %s:%d\n", + (alarmflag?"ftdm-alarm-clear":"ftdm-alarm-trap"), ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); switch_event_fire(&event); return; @@ -184,6 +186,9 @@ static void ctdm_event_handler(switch_event_t *event) return; } + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Requesting alarm status for %s:%d\n", + ftdm_channel_get_span_name(channel), ftdm_channel_get_id(channel)); + ctdm_report_alarms(channel); } } diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway.c b/src/mod/endpoints/mod_media_gateway/media_gateway.c index 8beeedc3ca..dc789c3511 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway.c @@ -578,8 +578,9 @@ switch_status_t megaco_profile_start(const char *profilename) switch_core_new_memory_pool(&pool); profile = switch_core_alloc(pool, sizeof(*profile)); profile->pool = pool; + profile->physical_terminations = NULL; profile->name = switch_core_strdup(pool, profilename); - profile->next_context_id++; + profile->next_context_id++; profile->inact_tmr = 0x00; profile->inact_tmr_task_id = 0x00; diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c index f392612cbb..5fa9d45663 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_cmd_handler.c @@ -2306,18 +2306,22 @@ switch_status_t mg_send_term_service_change(char *span_name, char *chan_number, { case MG_TERM_SERVICE_STATE_IN_SERVICE: { - /* set INS flag...clear oos flag */ - switch_clear_flag(term, MG_OUT_OF_SERVICE); - switch_set_flag(term, MG_IN_SERVICE); - ret = mg_send_ins_service_change(term->profile, term->name, 0x00 ); + if(switch_test_flag(term, MG_OUT_OF_SERVICE)){ + /* set INS flag...clear oos flag */ + switch_clear_flag(term, MG_OUT_OF_SERVICE); + switch_set_flag(term, MG_IN_SERVICE); + ret = mg_send_ins_service_change(term->profile, term->name, 0x00 ); + } break; } case MG_TERM_SERVICE_STATE_OUT_OF_SERVICE: { - /* set OOS flag...clear ins flag */ - switch_clear_flag(term, MG_IN_SERVICE); - switch_set_flag(term, MG_OUT_OF_SERVICE); - ret = mg_send_oos_service_change(term->profile, term->name, 0x00 ); + if(switch_test_flag(term, MG_IN_SERVICE)){ + /* set OOS flag...clear ins flag */ + switch_clear_flag(term, MG_IN_SERVICE); + switch_set_flag(term, MG_OUT_OF_SERVICE); + ret = mg_send_oos_service_change(term->profile, term->name, 0x00 ); + } break; } default: diff --git a/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c b/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c index 9890f152c3..98b46a9302 100644 --- a/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c +++ b/src/mod/endpoints/mod_media_gateway/media_gateway_xml.c @@ -213,6 +213,7 @@ void mg_create_tdm_term(megaco_profile_t *profile, const char *tech, const char term->name = switch_core_sprintf(pool, "%s%d", prefix, chan_num); term->u.tdm.channel = chan_num; term->u.tdm.span_name = switch_core_strdup(pool, channel_prefix); + switch_set_flag(term, MG_OUT_OF_SERVICE); switch_core_hash_insert_wrlock(profile->terminations, term->name, term, profile->terminations_rwlock); term->next = profile->physical_terminations; @@ -222,7 +223,6 @@ void mg_create_tdm_term(megaco_profile_t *profile, const char *tech, const char "Mapped termination [%s] to freetdm span: %s chan: %d\n", term->name, term->u.tdm.span_name, term->u.tdm.channel); megaco_prepare_tdm_termination(term); - } /****************************************************************************************************************************/ switch_status_t mg_config_cleanup(megaco_profile_t* profile) diff --git a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c index 22c07275a8..95f9d3b2cb 100644 --- a/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c +++ b/src/mod/endpoints/mod_media_gateway/mod_media_gateway.c @@ -549,6 +549,7 @@ void handle_mgco_cmd_ind(Pst *pst, SuId suId, MgMgcoCommand* cmd) if ((CH_CMD_TYPE_IND == cmd->cmdType.val) && (MGT_TERMID_OTHER == termId->type.val)){ if(SWITCH_STATUS_FALSE == mg_stack_termination_is_in_service(mg_profile, (char*)termId->name.lcl.val, termId->name.lcl.len)){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Termination[%s] not in service \n", (char*)termId->name.lcl.val); mg_util_set_term_string(&errTxt, termId); err_code = MGT_MGCO_RSP_CODE_UNKNOWN_TERM_ID; goto error;