From 0d2f1a4159b1cfc2640e40ccb9853dd1b9d7957a Mon Sep 17 00:00:00 2001 From: Georgiewskiy Yuriy Date: Sat, 24 Oct 2009 01:48:45 +0000 Subject: [PATCH] initial commit git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@15223 d0543943-73ff-0310-b7d9-9358b9ac24b2 --- src/mod/endpoints/mod_h323/Makefile | 5 + src/mod/endpoints/mod_h323/bugs | 2 + src/mod/endpoints/mod_h323/changes.txt | 10 + src/mod/endpoints/mod_h323/h323.conf.xml | 18 + src/mod/endpoints/mod_h323/mod_h323.cpp | 1485 ++++++++++++++++++++++ src/mod/endpoints/mod_h323/mod_h323.h | 431 +++++++ 6 files changed, 1951 insertions(+) create mode 100644 src/mod/endpoints/mod_h323/Makefile create mode 100644 src/mod/endpoints/mod_h323/bugs create mode 100644 src/mod/endpoints/mod_h323/changes.txt create mode 100644 src/mod/endpoints/mod_h323/h323.conf.xml create mode 100644 src/mod/endpoints/mod_h323/mod_h323.cpp create mode 100644 src/mod/endpoints/mod_h323/mod_h323.h diff --git a/src/mod/endpoints/mod_h323/Makefile b/src/mod/endpoints/mod_h323/Makefile new file mode 100644 index 0000000000..e38e8b6e97 --- /dev/null +++ b/src/mod/endpoints/mod_h323/Makefile @@ -0,0 +1,5 @@ +BASE=../../../.. +LOCAL_CFLAGS+=-g -I/usr/include/ptlib -I/usr/local/src/h323plus/include -I. -DPTRACING=1 -D_REENTRANT -fno-exceptions +LOCAL_LDFLAGS= -L/usr/local/src/h323plus/lib -lh323_linux_x86_ -lpt -lrt + +include $(BASE)/build/modmake.rules diff --git a/src/mod/endpoints/mod_h323/bugs b/src/mod/endpoints/mod_h323/bugs new file mode 100644 index 0000000000..8e08697df2 --- /dev/null +++ b/src/mod/endpoints/mod_h323/bugs @@ -0,0 +1,2 @@ +faststart and codecs v CallProceeding due to h323plus, grep "Very Frustrating - S.H." in h323plus +source and uncomment commented lines. \ No newline at end of file diff --git a/src/mod/endpoints/mod_h323/changes.txt b/src/mod/endpoints/mod_h323/changes.txt new file mode 100644 index 0000000000..1bc56e60de --- /dev/null +++ b/src/mod/endpoints/mod_h323/changes.txt @@ -0,0 +1,10 @@ + +fix misstype in codec conversion. +fix rtp issue causes choppy sound. +fix progress ind handling on outbound calls. +fix crash on log line, btw not understand why. +implement dtmf transfer. +fix codec name conversion a bit. +fix crash on inbound fast start connection. + +initial release. diff --git a/src/mod/endpoints/mod_h323/h323.conf.xml b/src/mod/endpoints/mod_h323/h323.conf.xml new file mode 100644 index 0000000000..0a0fb09587 --- /dev/null +++ b/src/mod/endpoints/mod_h323/h323.conf.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/mod/endpoints/mod_h323/mod_h323.cpp b/src/mod/endpoints/mod_h323/mod_h323.cpp new file mode 100644 index 0000000000..ac04599b32 --- /dev/null +++ b/src/mod/endpoints/mod_h323/mod_h323.cpp @@ -0,0 +1,1485 @@ + + +#include "mod_h323.h" + +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, mod_h323_globals.codec_string); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, mod_h323_globals.context); +SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, mod_h323_globals.dialplan); + + +SWITCH_MODULE_LOAD_FUNCTION(mod_h323_load); +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_h323_shutdown); +SWITCH_MODULE_DEFINITION(mod_h323, mod_h323_load, mod_h323_shutdown, NULL); + +#define CF_NEED_FLUSH (1 << 1) +struct mod_h323_globals mod_h323_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); + +static const char modulename[] = "h323"; +static const char* h323_formats[] = { + "G.711-ALaw-64k", "PCMA", + "G.711-uLaw-64k", "PCMU", + "GSM-06.10", "GSM", + "MS-GSM", "msgsm", + "SpeexNarrow", "speex", + "LPC-10", "lpc10", + "iLBC-15k2", "ilbc20", + "iLBC-13k3", "ilbc30", + "G.723", "G723", + "G.726", "G726", + "G.728", "G728", + "G.729B", "G729b", + "G.729", "G729", + "PCM-16", "slin", + "G.729A", "G729a", + "G.729A/B", "G729ab", + "G.723.1", "G723.1", + "G.723.1(5.3k)", "G723.1-5k3", + "G.723.1A(5.3k)", "G723.1a-5k3", + "G.723.1A(6.3k)", "G723.1a-6k3", + "G.723.1A(6.3k)-Cisco", "g723.1a-6k3-cisco", + "G.726-16k", "G726-16", + "G.726-24k", "G726-24", + "G.726-32k", "G726-32", + "G.726-40k", "G726-40", + "iLBC", "ilbc", + "SpeexNarrow-18.2k", "speex-18k2", + "SpeexNarrow-15k", "speex-15k", + "SpeexNarrow-11k", "speex-11k", + "SpeexNarrow-8k", "speex-8k", + "SpeexNarrow-5.95k", "speex-5k95", + 0 +}; + +static switch_status_t on_hangup(switch_core_session_t *session); +static switch_status_t on_destroy(switch_core_session_t *session); + + +static switch_io_routines_t h323fs_io_routines = { + /*.outgoing_channel */ create_outgoing_channel, + /*.read_frame */ FSH323Connection::read_audio_frame, + /*.write_frame */ FSH323Connection::write_audio_frame, + /*.kill_channel */ FSH323Connection::kill_channel, + /*.send_dtmf */ FSH323Connection::send_dtmf, + /*.receive_message */ FSH323Connection::receive_message, + /*.receive_event */ FSH323Connection::receive_event, + /*.state_change */ FSH323Connection::state_change, + /*.read_video_frame */ FSH323Connection::read_video_frame, + /*.write_video_frame */ FSH323Connection::write_video_frame +}; + +static switch_state_handler_table_t h323fs_event_handlers = { + /*.on_init */ FSH323Connection::on_init, + /*.on_routing */ FSH323Connection::on_routing, + /*.on_execute */ FSH323Connection::on_execute, + /*.on_hangup */ on_hangup, + /*.on_exchange_media */ FSH323Connection::on_exchange_media, + /*.on_soft_execute */ FSH323Connection::on_soft_execute, + /*.on_consume_media*/ NULL, + /*.on_hibernate*/ NULL, + /*.on_reset*/ NULL, + /*.on_park*/ NULL, + /*.on_reporting*/ NULL, + /*.on_destroy*/ on_destroy +}; + + +static FSProcess *opal_process = NULL; +SWITCH_MODULE_LOAD_FUNCTION(mod_h323_load){ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_h323\n"); + + + *module_interface = switch_loadable_module_create_module_interface(pool, modname); + + if (!*module_interface) { + return SWITCH_STATUS_MEMERR; + } + + h323_process = new FSProcess(); + if (h323_process == NULL) { + return SWITCH_STATUS_MEMERR; + } + + if (h323_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; + } + + delete h323_process; + h323_process = NULL; + return SWITCH_STATUS_FALSE; +} + +SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_h323_shutdown){ + + switch_safe_free(mod_h323_globals.context); + switch_safe_free(mod_h323_globals.dialplan); + switch_safe_free(mod_h323_globals.codec_string); + delete h323_process; + h323_process = NULL; + return SWITCH_STATUS_SUCCESS; +} + + + +#if PTRACING + +class FSTrace : public ostream { + public: + FSTrace() + : ostream(&buffer) + { + } + + private: + class Buffer : public streambuf { + char buffer[250]; + + public: + Buffer() + { + setg(buffer, buffer, &buffer[sizeof(buffer)-2]); + setp(buffer, &buffer[sizeof(buffer)-2]); + } + + virtual int sync() + { + return overflow(EOF); + } + + virtual int underflow() + { + return EOF; + } + + 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; +}; + +#endif + +PString GetH245CodecName(const H245_AudioCapability &cap){ + switch (cap.GetTag()) { + case H245_AudioCapability::e_g711Alaw64k: + case H245_AudioCapability::e_g711Alaw56k: + return "PCMA"; + case H245_AudioCapability::e_g711Ulaw64k: + case H245_AudioCapability::e_g711Ulaw56k: + return "PCMU"; + case H245_AudioCapability::e_g722_64k: + case H245_AudioCapability::e_g722_56k: + case H245_AudioCapability::e_g722_48k: + return "G722"; + case H245_AudioCapability::e_g728: + return "G728"; + case H245_AudioCapability::e_g729: + case H245_AudioCapability::e_g729AnnexA: + case H245_AudioCapability::e_g729wAnnexB: + case H245_AudioCapability::e_g729AnnexAwAnnexB: + return "G729"; + case H245_AudioCapability::e_g7231: + case H245_AudioCapability::e_g7231AnnexCCapability: + return "G723"; + case H245_AudioCapability::e_gsmFullRate: + case H245_AudioCapability::e_gsmHalfRate: + case H245_AudioCapability::e_gsmEnhancedFullRate: + return "GSM"; + } + return "Unknown"; +} + +FSProcess::FSProcess() + : PLibraryProcess("Test", "mod_h323", 1, 0, AlphaCode, 1) + , m_h323endpoint(NULL){ +} + + +FSProcess::~FSProcess(){ + delete m_h323endpoint; +} + + +bool FSProcess::Initialise(switch_loadable_module_interface_t *iface){ + PTRACE(4, "mod_h323\t======>FSProcess::Initialise " << *this); + + m_h323endpoint = new FSH323EndPoint(); + return m_h323endpoint != NULL && m_h323endpoint->Initialise(iface); +} + +bool FSH323EndPoint::Initialise(switch_loadable_module_interface_t *iface){ + PTRACE(4, "mod_h323\t======>FSManager::Initialise " << *this); + ReadConfig(false); + + PTrace::SetLevel(mod_h323_globals.trace_level); //just for fun and eyecandy ;) + PTrace::SetOptions(PTrace::TraceLevel); + PTrace::SetStream(new FSTrace); + + m_freeswitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE); + m_freeswitch->interface_name = modulename; + m_freeswitch->io_routines = &h323fs_io_routines; + m_freeswitch->state_handler = &h323fs_event_handlers; + + PString codec = ((const char *)mod_h323_globals.codec_string); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Config capabilliti %s \n",(const char *)codec); + if (!codec.IsEmpty()) { + const char** f = h323_formats; + for (; *f; f += 2) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Find capabilliti %s to %s\n",f[1],(const char *)codec); + if (codec.Find(f[1]) != P_MAX_INDEX) { + PString tmp = f[0]; + tmp += "*{sw}"; + PINDEX init = GetCapabilities().GetSize(); + AddAllCapabilities(0, 0, tmp); + PINDEX num = GetCapabilities().GetSize() - init; + if (!num) { + // failed to add so pretend we support it in hardware + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "H323 failed to add capability '%s' \n",(const char *)tmp); + tmp = f[0]; + tmp += "*{hw}"; + AddAllCapabilities(0, 0, tmp); + num = GetCapabilities().GetSize() - init; + } + if (num) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "H.323 added %d capabilities '%s' \n",num,(const char *)tmp); + else + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "H323 failed to add capability '%s' \n",(const char *)tmp); + + } + } + } + + + + AddAllUserInputCapabilities(0,1); + PTRACE(1, "OpenPhone\tCapability Table:\n" << setprecision(4) << capabilities); + if (m_listeners.empty()) { + StartListener(""); + } else { + for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { + if (!StartListener(it->listenAddress)) { + PTRACE(3, "mod_h323\tCannot start listener for " << it->name); + } + } + } + +/* + 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); + } + } +*/ + + if (!m_gkAddress.IsEmpty()) { + if (UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface)) + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n", + (const char *)GetGatekeeper()->GetName()); + else + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, + "Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n", + (const char *)m_gkAddress, + (const char *)m_gkIdentifer, + (const char *)m_gkInterface); + } + + return TRUE; +} + +switch_status_t FSH323EndPoint::ReadConfig(int reload){ + PTRACE(4, "mod_h323\t======>FSH323EndPoint::ReadConfig " << *this); + const char *cf = "h323.conf"; + switch_status_t status = SWITCH_STATUS_SUCCESS; + + 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); + if (xml == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); + 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"); + + if (!strcasecmp(var, "trace-level")) { + int level = atoi(val); + if (level > 0) { + mod_h323_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{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Set zero Jitter buffer\n"); + SetAudioJitterDelay(0, 0); + } + } else if (!strcasecmp(var, "gk-address")) { + m_gkAddress = val; + } else if (!strcasecmp(var, "gk-identifer")) { + m_gkIdentifer = val; + } else if (!strcasecmp(var, "gk-interface")) { + m_gkInterface = val; + } else if (!strcasecmp(var, "gk-prefix")) { + m_gkPrefixes.AppendString(val); + } + } + } + + switch_xml_t xmlListeners = switch_xml_child(cfg, "listeners"); + if (xmlListeners != NULL) { + for (switch_xml_t xmlListener = switch_xml_child(xmlListeners, "listener"); xmlListener != NULL; xmlListener = xmlListener->next) { + + 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; + + 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); + } + + listener.listenAddress = new H323ListenerTCP(*this,ip,port); + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.name); + } + } + + switch_event_destroy(¶ms); + + if (xml) + switch_xml_free(xml); + + return status; +} + +FSH323EndPoint::FSH323EndPoint(){ + PTRACE(4, "mod_h323\t======>FSH323EndPoint::FSH323EndPoint " << *this); + terminalType = e_GatewayOnly; +} + +H323Connection *FSH323EndPoint::CreateConnection( + unsigned callReference, + void* userData, + H323Transport* transport, + H323SignalPDU* setupPDU){ + PTRACE(4, "mod_h323\t======>FSH323EndPoint::CreateConnection callReference = "<< callReference <<" userDate = "< SWITCH_CALL_DIRECTION_OUTBOUND"); + } else{ + PTRACE(4, "mod_h323\t------> SWITCH_CALL_DIRECTION_INBOUND"); + } + + switch_core_session_t *fsSession = switch_core_session_request(GetSwitchInterface(), + (switch_caller_profile_t *)userData ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND, NULL); + if (fsSession == NULL) + return NULL; + + PTRACE(4, "mod_h323\t------> fsSession = "<FSH323EndPoint::OnSetGatewayPrefixes " << *this); + if(m_gkPrefixes.GetSize() > 0) { + PTRACE(4, "mod_h323\tOnSetGatewayPrefixes " << m_gkPrefixes); + prefixes = m_gkPrefixes; + return true; + } + return false; +} + +FSH323Connection::FSH323Connection(FSH323EndPoint& endpoint, H323Transport* transport, unsigned callReference, switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel) + : H323Connection(endpoint,callReference) + , m_endpoint(&endpoint) + , m_fsSession(fsSession) + , m_fsChannel(fsChannel) + , m_callOnPreAnswer(false) + , m_startRTP(false){ + PTRACE(4, "mod_h323\t======>FSH323Connection::FSH323Connection " << *this); + + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_alloc(m_fsSession, sizeof(*tech_pvt)); + tech_pvt->me = this; + switch_core_session_set_private(m_fsSession, tech_pvt); + + switch_mutex_init(&tech_pvt->flag_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(m_fsSession)); + switch_mutex_init(&tech_pvt->h323_mutex, SWITCH_MUTEX_NESTED, switch_core_session_get_pool(m_fsSession)); + + 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 = "h323/"; + name += outbound_profile->destination_number; + switch_channel_set_name(m_fsChannel, name); + + switch_channel_set_flag(m_fsChannel, CF_OUTBOUND); + switch_channel_set_state(m_fsChannel, CS_INIT); + } + + m_RTPlocalPort = switch_rtp_request_port((const char *)m_RTPlocalIP.AsString()); +} + +FSH323Connection::~FSH323Connection(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::~FSH323Connection ["<<*this<<"]"); + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + tech_pvt->me = NULL; +} + +void FSH323Connection::OnSetLocalCapabilities(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnSetLocalCapabilities() [" << *this<<"]"); + H323Connection::OnSetLocalCapabilities(); + SetLocalCapabilities(); +} + +bool FSH323Connection::SetLocalCapabilities(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::SetLocalCapabilities() Size local capability = "<FSH323Connection::decodeCapability"); + PString fname((const char *)capability.GetFormatName()); + + if (fname.Find("{sw}") == (fname.GetLength() - 4)) + fname = fname.Mid(0,fname.GetLength()-4); + if (fname.Find("{hw}") == (fname.GetLength() - 4)) + fname = fname.Mid(0,fname.GetLength()-4); + + OpalMediaFormat oformat(fname, false); + int pload = oformat.GetPayloadType(); + const char *format = 0; + const char** f = h323_formats; + for (; *f; f += 2) { + if (fname.Find(*f) == 0) { + format = f[1]; + break; + } + } + + PTRACE(1, "mod_h323\tcapability '"<< fname << "' format '"<FSH323Connection::OnAnswerCall caller = "<< caller<<" [" << *this<<"]"); + + if (m_fsSession == NULL) { + PTRACE(1, "mod_h323\tSession request failed."); + return H323Connection::AnswerCallDenied; + } + + 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_h323\tSession does not have a channel"); + return H323Connection::AnswerCallDenied; + } + + const Q931& q931 = setupPDU.GetQ931(); + const H225_Setup_UUIE& setup = setupPDU.m_h323_uu_pdu.m_h323_message_body; + const H225_ArrayOf_AliasAddress& address = setup.m_destinationAddress; + for (int i = 0; idestination_number, sizeof(name)-5); + switch_channel_set_name(channel, name); + switch_channel_set_state(channel, CS_INIT); + + if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) { + PTRACE(1, "mod_h323\tCould not launch session thread"); + return H323Connection::AnswerCallDenied; + } + + return H323Connection::AnswerCallDeferred; +} + +H323Channel* FSH323Connection::CreateRealTimeLogicalChannel(const H323Capability& capability,H323Channel::Directions dir,unsigned sessionID,const H245_H2250LogicalChannelParameters* param, RTP_QOS * rtpqos){ + PTRACE(4, "mod_h323\t======>FSH323Connection::CreateRealTimeLogicalChannel " << *this); + + H323TransportAddress m_h323transportadd = GetSignallingChannel()->GetLocalAddress(); + m_h323transportadd.GetIpAddress(m_RTPlocalIP); +// return H323Connection::CreateRealTimeLogicalChannel(capability,dir,sessionID,param); + return new FSH323_ExternalRTPChannel(*this, capability, dir, sessionID,m_RTPlocalIP,m_RTPlocalPort); +} + +PBoolean FSH323Connection::OnStartLogicalChannel(H323Channel & channel){ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnStartLogicalChannel chennel = "<<&channel<<", "<<*this); + +// return H323Connection::OnStartLogicalChannel(channel); + return true; +} + +PBoolean FSH323Connection::OnCreateLogicalChannel(const H323Capability& capability, H323Channel::Directions dir, unsigned& errorCode){ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnCreateLogicalChannel ('"<< (const char *)capability.GetFormatName()<<"',"<FSH323Connection::OnReceivedReleaseComplete cause = "<FSH323Connection::OnReceivedProgress"); + m_txAudioOpened.Wait(); + switch_channel_mark_pre_answered(m_fsChannel); + return true; +} + + +bool FSH323Connection::OnSendReleaseComplete(H323SignalPDU & pdu) +{ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnSendReleaseComplete cause = "<FSH323Connection::OpenLogicalChannel ('"<< (const char *)capability.GetFormatName()<<"', "<< sessionID<<", "<FSH323Connection::OnReceivedCapabilitySet ["<<*this<<"]"); + if (!H323Connection::OnReceivedCapabilitySet(remoteCaps, muxCap, reject)) { + return false; + } + PTRACE(4, "mod_h323\t======>END H323Connection::OnReceivedCapabilitySet ["<<*this<<"]"); + + for (int i = 0; i < remoteCapabilities.GetSize(); ++i) { + PTRACE(4, "mod_h323\t----> Capabilities = "< Capabilities is NULL "); + return false; + } + PTRACE(4, "mod_h323\t----> Capabilities not NULL "); + return true; +} + + +bool FSH323Connection::OnAlerting(const H323SignalPDU &alertingPDU, const PString &user){ + + PTRACE(4, "mod_h323\t======>PFSH323Connection::OnAlerting user = "<<(const char *)user<<" ["<<*this<<"]"); + + return (switch_channel_mark_ring_ready(m_fsChannel) == SWITCH_STATUS_SUCCESS); + ; +} + +void FSH323Connection::OnEstablished(){ + + PTRACE(4, "mod_h323\t======>PFSH323Connection::OnEstablished ["<<*this<<"]"); + + switch_channel_mark_answered(m_fsChannel); +} + + + +void FSH323Connection::setRemoteAddress(const char* remoteIP, WORD remotePort){ + PTRACE(4, "mod_h323\t======>PFSH323Connection::setRemoteAddress remoteIP ="<FSH323Connection::on_execute " << *this); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::on_routing(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::on_routing " << *this); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::kill_channel(int sig){ + PTRACE(4, "mod_h323\t======>FSH323Connection::kill_channel " << *this); + PTRACE(3, "mod_h323\tKill " << sig << " on connection " << *this); + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + + if (!tech_pvt) { + return SWITCH_STATUS_FALSE; + } + + switch (sig) { + case SWITCH_SIG_BREAK: + if (switch_rtp_ready(tech_pvt->rtp_session)) { + switch_rtp_break(tech_pvt->rtp_session); + } + break; + case SWITCH_SIG_KILL: + default: + m_rxAudioOpened.Signal(); + m_txAudioOpened.Signal(); + if (switch_rtp_ready(tech_pvt->rtp_session)) { + switch_rtp_kill_socket(tech_pvt->rtp_session); + } + break; + } + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::send_dtmf(const switch_dtmf_t *dtmf){ + PTRACE(4, "mod_h323\t======>FSH323Connection::send_dtmf " << *this); + SendUserInputTone(dtmf->digit, dtmf->duration); + return SWITCH_STATUS_SUCCESS; +} + +void FSH323Connection::SendUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp) +{ + PTRACE(4, "mod_h323\t======>FSH323Connection::SendUserInputTone [" << *this<<"]"); + H323Connection::SendUserInputTone(tone, duration); +} + +void FSH323Connection::OnUserInputTone(char tone, unsigned duration, unsigned logicalChannel, unsigned rtpTimestamp) +{ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnUserInputTone [" << *this<<"]"); + switch_dtmf_t dtmf = { tone, duration }; + switch_channel_queue_dtmf(m_fsChannel, &dtmf); + H323Connection::OnUserInputTone( tone, duration, logicalChannel, rtpTimestamp); +} + +void FSH323Connection::OnUserInputString(const PString &value) +{ + PTRACE(4, "mod_h323\t======>FSH323Connection::OnUserInputString [" << *this<<"]"); + switch_dtmf_t dtmf = { value[0], 0 }; + switch_channel_queue_dtmf(m_fsChannel, &dtmf); + H323Connection::OnUserInputString(value); +} + + +switch_status_t FSH323Connection::receive_message(switch_core_session_message_t *msg){ + PTRACE(4, "mod_h323\t======>FSH323Connection::receive_message MSG=" << msg->message_id); + + + switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); + + + + 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; + default: + break; + } + + switch (msg->message_id) { + case SWITCH_MESSAGE_INDICATE_RINGING:{ +// AnsweringCall(AnswerCallAlertWithMedia); + AnsweringCall(AnswerCallPending); + break; + } + case SWITCH_MESSAGE_INDICATE_DEFLECT:{ + /* PSafePtr other = GetOtherPartyConnection(); + if (other != NULL) + other->TransferConnection(msg->string_arg); + break; + */ + } + case SWITCH_MESSAGE_INDICATE_PROGRESS:{ + m_callOnPreAnswer = true; + AnsweringCall(AnswerCallPending); + AnsweringCall(AnswerCallDeferredWithMedia); + m_txAudioOpened.Wait(); + if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { + switch_channel_mark_pre_answered(m_fsChannel); + } + break; + } + case SWITCH_MESSAGE_INDICATE_ANSWER:{ + if (switch_channel_test_flag(channel, CF_OUTBOUND)) { + return SWITCH_STATUS_FALSE; + } + AnsweringCall(H323Connection::AnswerCallNow); + PTRACE(4, "mod_h323\tMedia started on connection " << *this); + + m_rxAudioOpened.Wait(); + m_txAudioOpened.Wait(); + + if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { + PTRACE(4, "mod_h323\t-------------------->switch_channel_mark_answered(m_fsChannel) " << *this); + switch_channel_mark_answered(m_fsChannel); + } + break; + } + default:{ + PTRACE(3, "mod_h323\tReceived message " << msg->message_id << " on connection " << *this); + } + } + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::receive_event(switch_event_t *event){ + PTRACE(4, "mod_h323\t======>FSH323Connection::receive_event " << *this); + PTRACE(3, "mod_h323\tReceived event " << event->event_id << " on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::state_change(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::state_change " << *this); + PTRACE(3, "mod_h323\tState changed on connection " << *this); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::on_init(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::on_init " << *this); + switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); + if (channel == NULL) { + return SWITCH_STATUS_FALSE; + } + + PTRACE(3, "mod_h323\tStarted routing for connection " << *this); + switch_channel_set_state(channel, CS_ROUTING); + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::on_exchange_media(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::on_exchange_media " << *this); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::on_soft_execute(){ + PTRACE(4, "mod_h323\t======>FSH323Connection::on_soft_execute " << *this); + + return SWITCH_STATUS_SUCCESS; +} + +switch_status_t FSH323Connection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id){ + PTRACE(4, "mod_h323\t======>FSH323Connection::read_audio_frame " << *this); + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + tech_pvt->read_frame.flags = 0; +/* + if (switch_channel_test_private_flag(m_fsChannel, CF_NEED_FLUSH)) { + switch_channel_clear_private_flag(m_fsChannel, CF_NEED_FLUSH); + } else { + switch_core_timer_next(&tech_pvt->read_timer); + } +*/ + switch_set_flag_locked(tech_pvt, TFLAG_READING); + + if (!switch_channel_ready(m_fsChannel)) { + PTRACE(4, "mod_h323\t---------> RETURN"); + switch_clear_flag_locked(tech_pvt, TFLAG_READING); + return SWITCH_STATUS_FALSE; + } + + if (!switch_core_codec_ready(&tech_pvt->read_codec )) { + PTRACE(4, "mod_h323\t---------> RETURN"); + switch_clear_flag_locked(tech_pvt, TFLAG_READING); + return SWITCH_STATUS_FALSE; + } + + //switch_core_timer_step(&m_switchTimer); + + + switch_status_t status = switch_rtp_zerocopy_read_frame(tech_pvt->rtp_session, &tech_pvt->read_frame, flags); + if (status != SWITCH_STATUS_SUCCESS && status != SWITCH_STATUS_BREAK) { + PTRACE(4, "mod_h323\t---------> RETURN"); + switch_clear_flag_locked(tech_pvt, TFLAG_READING); + return SWITCH_STATUS_FALSE; + } + PTRACE(4, "mod_h323\t--------->\n source = "<read_frame.source<< "\n packetlen = "<read_frame.packetlen<<"\n datalen = "<read_frame.datalen<<"\n samples = "<read_frame.samples<<"\n rate = "<read_frame.rate<<"\n payload = "<<(int)tech_pvt->read_frame.payload<<"\n timestamp = "<read_frame.timestamp<<"\n seq = "<read_frame.seq<<"\n ssrc = "<read_frame.ssrc); + if (tech_pvt->read_frame.flags & SFF_CNG) { + tech_pvt->read_frame.buflen = sizeof(m_buf); + tech_pvt->read_frame.data = m_buf; + tech_pvt->read_frame.packet = NULL; + tech_pvt->read_frame.packetlen = 0; + tech_pvt->read_frame.timestamp = 0; + tech_pvt->read_frame.m = SWITCH_FALSE; + tech_pvt->read_frame.seq = 0; + tech_pvt->read_frame.ssrc = 0; + tech_pvt->read_frame.codec = &tech_pvt->read_codec ; + } else { + tech_pvt->read_frame.codec = &tech_pvt->read_codec ; + } + switch_clear_flag_locked(tech_pvt, TFLAG_READING); + *frame = &tech_pvt->read_frame; + + return SWITCH_STATUS_SUCCESS; + + +} + +switch_status_t FSH323Connection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id){ + PTRACE(4, "mod_h323\t======>FSH323Connection::write_audio_frame " << *this); + + switch_status_t status = SWITCH_STATUS_SUCCESS; + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + switch_assert(tech_pvt != NULL); + + if (!switch_channel_ready(m_fsChannel)) { + PTRACE(4, "mod_h323\t---------> RETURN"); + return SWITCH_STATUS_FALSE; + } + + while (!(tech_pvt->read_codec.implementation && switch_rtp_ready(tech_pvt->rtp_session))) { + if (switch_channel_ready(m_fsChannel)) { + switch_yield(10000); + } else { + PTRACE(4, "mod_h323\t---------> RETURN"); + return SWITCH_STATUS_GENERR; + } + } + + if (!switch_core_codec_ready(&tech_pvt->read_codec) || !tech_pvt->read_codec.implementation) { + PTRACE(4, "mod_h323\t---------> RETURN"); + return SWITCH_STATUS_GENERR; + } + + if ((frame->flags & SFF_CNG)) { + PTRACE(4, "mod_h323\t---------> RETURN"); + return SWITCH_STATUS_SUCCESS; + } + switch_set_flag_locked(tech_pvt, TFLAG_WRITING); + + if (switch_rtp_write_frame(tech_pvt->rtp_session, frame)< 0) { + status = SWITCH_STATUS_GENERR; + } + + switch_clear_flag_locked(tech_pvt, TFLAG_WRITING); + PTRACE(4, "mod_h323\t---------> RETURN"); + return status; +} + +switch_status_t FSH323Connection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id){ + PTRACE(4, "mod_h323\t======>FSH323Connection::read_video_frame " << *this); + +} + +switch_status_t FSH323Connection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id){ + PTRACE(4, "mod_h323\t======>FSH323Connection::write_video_frame " << *this); +// return write_frame(OpalMediaType::Video(), frame, flag); +} + +/////////////////////////////////////////////////////////////////////// + +FSH323_ExternalRTPChannel::FSH323_ExternalRTPChannel( + FSH323Connection& connection, + const H323Capability& capability, + Directions direction, + unsigned sessionID, + const PIPSocket::Address& ip, + WORD dataPort) + : H323_ExternalRTPChannel(connection, capability, direction, sessionID,ip,dataPort) + , m_conn(&connection) + , m_fsSession(connection.GetSession()) + , m_capability(&capability) + , m_RTPlocalPort(dataPort){ + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + + m_RTPlocalIP = (const char *)ip.AsString(); + SetExternalAddress(H323TransportAddress(ip, dataPort), H323TransportAddress(ip, dataPort+1)); + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::FSH323_ExternalRTPChannel "<< GetDirection()<< " addr="<< m_RTPlocalIP <<":"<< m_RTPlocalPort<<" ["<<*this<<"]"); + + memset(&m_readFrame, 0, sizeof(m_readFrame)); + m_readFrame.codec = m_switchCodec; + m_readFrame.flags = SFF_RAW_RTP; + + m_fsChannel = switch_core_session_get_channel(m_fsSession); + //SetExternalAddress(H323TransportAddress(localIpAddress, m_RTPlocalPort), H323TransportAddress(localIpAddress, m_RTPlocalPort+1)); + PTRACE(4, "mod_h323\t------->capability.GetPayloadType() return = "<capability.GetFormatName() return = "<payloadCode = "<<(int)payloadCode); +} + + +FSH323_ExternalRTPChannel::~FSH323_ExternalRTPChannel(){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::~FSH323_ExternalRTPChannel "<< GetDirection()<<" "<<*this); + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + if (IsRunning()){ + PTRACE(4, "mod_h323\t------------->Running"); + if (switch_rtp_ready(tech_pvt->rtp_session)) { + switch_rtp_kill_socket(tech_pvt->rtp_session); + } + } +} + +PBoolean FSH323_ExternalRTPChannel::Start(){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::Start() "<<*this); + const char *err = NULL; + switch_rtp_flag_t flags; + char * timer_name = NULL; + const char *var; + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession); + if (!(m_conn && H323_ExternalRTPChannel::Start())) + return FALSE; + + + bool isAudio; + if (m_capability->GetMainType() == H323Capability::e_Audio) { + isAudio = true; + PTRACE(4, "mod_h323\t------------------------->H323Capability::e_Audio"); + } else if (m_capability->GetMainType() == H323Capability::e_Video) { + isAudio = false; + PTRACE(4, "mod_h323\t------------------------->H323Capability::e_Video"); + } + + H323Codec *codec = GetCodec(); + + + PTRACE(4, "mod_h323\t------------------->GetFrameSize() return = "<GetFrameSize()); + PTRACE(4, "mod_h323\t------------------->GetFrameTime() return = "<GetFrameTime()); + PTRACE(4, "mod_h323\t------------------->payloadCode = "<<(int)payloadCode); + PTRACE(4, "mod_h323\t------------------->m_capability->GetTxFramesInPacket() return = "<GetTxFramesInPacket()); + PTRACE(4, "mod_h323\t------------------->m_capability->GetFormatName() return = "<GetFormatName()); + + PTRACE(4, "mod_h323\t------------------->GetH245CodecName() return = "<GetSubType())); + + + + + + if (GetDirection() == IsReceiver){ + m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec; + m_switchTimer = isAudio ? &tech_pvt->read_timer : &tech_pvt->vid_read_timer; + }else{ + m_switchCodec = isAudio ? &tech_pvt->write_codec : &tech_pvt->vid_write_codec; + } + + if (m_conn->m_callOnPreAnswer && !(GetDirection() == IsReceiver)){ + m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec; + m_switchTimer = isAudio ? &tech_pvt->read_timer : &tech_pvt->vid_read_timer; + } + + + + if (switch_core_codec_init(m_switchCodec, GetH245CodecName(m_capability->GetSubType()), NULL, // FMTP + 8000, m_capability->GetTxFramesInPacket(), 1, // Channels + SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings + switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) { + + if (switch_core_codec_init(m_switchCodec, GetH245CodecName(m_capability->GetSubType()), NULL, // FMTP + 8000, 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_h323\t" << switch_channel_get_name(m_fsChannel)<< " Cannot initialise " << ((GetDirection() == IsReceiver)? " read" : " write") << ' ' + << m_capability->GetMainType() << " codec " << m_capability << " for connection " << *this); + switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); + return false; + } + PTRACE(2, "mod_h323\t" << switch_channel_get_name(m_fsChannel)<< " Unsupported ptime of " << m_capability->GetTxFramesInPacket() << " on " << ((GetDirection() == IsReceiver)? " read" : " write") << ' ' + << m_capability->GetMainType() << " codec " << m_capability << " for connection " << *this); + } + + PTRACE(1, "mod_h323\t" << switch_channel_get_name(m_fsChannel)<< " initialise " << + switch_channel_get_name(m_fsChannel) << ((GetDirection() == IsReceiver)? " read" : " write") << ' ' + << m_capability->GetMainType() << " codec " << m_capability << " for connection " << *this); + + if (GetDirection() == IsReceiver) { + m_readFrame.rate = tech_pvt->read_codec.implementation->actual_samples_per_second; + + if (isAudio) { + switch_core_session_set_read_codec(m_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_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); + } + } else { + if (isAudio) { + switch_core_session_set_write_codec(m_fsSession, m_switchCodec); + } else { + switch_core_session_set_video_write_codec(m_fsSession, m_switchCodec); + switch_channel_set_flag(m_fsChannel, CF_VIDEO); + } + } + + if (m_conn->m_callOnPreAnswer && !(GetDirection() == IsReceiver)){ + m_readFrame.rate = tech_pvt->read_codec.implementation->actual_samples_per_second; + + if (isAudio) { + switch_core_session_set_read_codec(m_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_codec_destroy(m_switchCodec); + m_switchCodec = NULL; + return false; + } + switch_channel_set_variable(m_fsChannel,"timer_name","soft"); + } + } + + PTRACE(3, "mod_h323\tSet " << ((GetDirection() == IsReceiver)? " read" : " write") << ' ' + << m_capability->GetMainType() << " codec to << " << m_capability << " for connection " << *this); + + switch_mutex_lock(tech_pvt->h323_mutex); + + PIPSocket::Address remoteIpAddress; + GetRemoteAddress(remoteIpAddress,m_RTPremotePort); + m_RTPremoteIP = (const char *)remoteIpAddress.AsString(); + PTRACE(4, "mod_h323\t------------------->tech_pvt->rtp_session = "<rtp_session); + PTRACE(4, "mod_h323\t------------------->samples_per_packet = "<implementation->samples_per_packet); + PTRACE(4, "mod_h323\t------------------->actual_samples_per_second = "<implementation->actual_samples_per_second); + + if (!m_conn->m_startRTP) { + flags = (switch_rtp_flag_t) (SWITCH_RTP_FLAG_DATAWAIT|SWITCH_RTP_FLAG_AUTO_CNG|SWITCH_RTP_FLAG_RAW_WRITE|SWITCH_RTP_FLAG_AUTOADJ); + if ((var = switch_channel_get_variable(m_fsChannel, "timer_name"))) { + timer_name = (char *) var; + } + tech_pvt->rtp_session = switch_rtp_new((const char *)m_RTPlocalIP, + m_RTPlocalPort, + (const char *)m_RTPremoteIP, + m_RTPremotePort, + (switch_payload_t)payloadCode, + m_switchCodec->implementation->samples_per_packet, + m_capability->GetTxFramesInPacket() * 1000, + (switch_rtp_flag_t) flags, timer_name, &err, + switch_core_session_get_pool(m_fsSession)); + PTRACE(4, "mod_h323\t------------------------->tech_pvt->rtp_session = "<rtp_session); + m_conn->m_startRTP = true; + if (switch_rtp_ready(tech_pvt->rtp_session)) { + PTRACE(4, "mod_h323\t+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + switch_channel_set_flag(m_fsChannel, CF_FS_RTP); + + }else{ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "AUDIO RTP REPORTS ERROR: [%s]\n", switch_str_nil(err)); + switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_mutex_unlock(tech_pvt->h323_mutex); + return SWITCH_STATUS_FALSE; + } + } + if (GetDirection() == IsReceiver) m_conn->m_rxAudioOpened.Signal(); + else m_conn->m_txAudioOpened.Signal(); + PTRACE(4, "mod_h323\t------------->External RTP address "<h323_mutex); + + return true; +} + + +PBoolean FSH323_ExternalRTPChannel::OnReceivedPDU( + const H245_H2250LogicalChannelParameters& param, + unsigned& errorCode){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::OnReceivedPDU ["<<*this<<"]"); + if (!H323_ExternalRTPChannel::OnReceivedPDU(param,errorCode)) + return true; +// if (!m_conn || m_conn->hasRemoteAddress()) +// return true; + PIPSocket::Address remoteIpAddress; + WORD remotePort; + GetRemoteAddress(remoteIpAddress,remotePort); + PTRACE(4, "mod_h323\tRemote RTP address "<<(const char *)remoteIpAddress.AsString()<<":"<setRemoteAddress((const char *)remoteIpAddress.AsString(), remotePort); + return true; +} + +PBoolean FSH323_ExternalRTPChannel::OnSendingPDU(H245_H2250LogicalChannelParameters& param){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::OnSendingPDU ["<<*this<<"]"); + return H323_ExternalRTPChannel::OnSendingPDU(param); +} + +PBoolean FSH323_ExternalRTPChannel::OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters& param){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::OnReceivedAckPDU ["<<*this<<"]"); + return H323_ExternalRTPChannel::OnReceivedAckPDU(param); +} + +void FSH323_ExternalRTPChannel::OnSendOpenAck(H245_H2250LogicalChannelAckParameters& param){ + PTRACE(4, "mod_h323\t======>FSH323_ExternalRTPChannel::OnSendOpenAck ["<<*this<<"]"); + H323_ExternalRTPChannel::OnSendOpenAck(param); +} + + +FSH323Connection * FSH323EndPoint::FSMakeCall(const PString & dest, void *userData){ + PTRACE(4, "mod_h323\t======>FSH323EndPoint::FSMakeCall DST NUMBER = "< 0) { + H323TransportAddress taddr = listeners[0].GetTransportAddress(); + PIPSocket::Address addr; + WORD port; + if (taddr.GetIpAndPort(addr, port)) { + if (addr) { + PTRACE(4, "mod_h323\t----> Using "< Unable to create transport for outgoing call"); + } + } else + PTRACE(4, "mod_h323\t----> Unable to get address and port"); + } + +/* + if (!(connection = (FSH323Connection *)H323EndPoint::MakeCallLocked(dest, token, userData, transport))) { + return NULL; + } +*/ + + if (!(connection = (FSH323Connection *)H323EndPoint::MakeCall(dest, token, userData))) { + return NULL; + } + +/* + unsigned int *callReference; + + *callReference = connection->GetCallReference(); + PTRACE(2, "mod_h323\t======>\n\tCreate outgoing cennel\n\tCall token = "<<(const char *)token<<"\n\tCall reference = "<<*callReference); +*/ + return connection; +} + + +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){ + PTRACE(4, "mod_h323\t======>create_outgoing_channel DST NUMBER = "<destination_number); + + FSH323Connection * connection; + if (h323_process == NULL) { + return SWITCH_CAUSE_CRASH; + } + FSH323EndPoint & ep = h323_process->GetH323EndPoint(); + if (!(connection = ep.FSMakeCall(outbound_profile->destination_number,outbound_profile))){ + return SWITCH_CAUSE_PROTOCOL_ERROR; + } +/* 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(); + PTRACE(4, "mod_h323\t--------->GetSession() return = "<GetSession()); + return SWITCH_CAUSE_SUCCESS; +} + + +static switch_status_t on_destroy(switch_core_session_t *session){ + PTRACE(4, "mod_h323\t======>on_destroy "); + + h323_private_t *tech_pvt = (h323_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); + } + + if (tech_pvt->write_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->write_codec); + } + + if (tech_pvt->vid_read_codec.implementation) { + switch_core_codec_destroy(&tech_pvt->vid_read_codec); + } + + 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); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + +static switch_status_t on_hangup(switch_core_session_t *session){ + PTRACE(4, "mod_h323\t======>switch_status_t on_hangup "); + + switch_channel_t *channel = switch_core_session_get_channel(session); + h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(session); + if (tech_pvt->me) { + PTRACE(4, "mod_h323\t----->"); + 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; + } + + return SWITCH_STATUS_SUCCESS; +} + + + + + + + + diff --git a/src/mod/endpoints/mod_h323/mod_h323.h b/src/mod/endpoints/mod_h323/mod_h323.h new file mode 100644 index 0000000000..727169e3c7 --- /dev/null +++ b/src/mod/endpoints/mod_h323/mod_h323.h @@ -0,0 +1,431 @@ + + +#if defined(__GNUC__) && defined(HAVE_VISIBILITY) +#pragma GCC visibility push(default) +#endif + +#include +#include +#include +#include +#include +#include + +#include + + +#if defined(__GNUC__) && defined(HAVE_VISIBILITY) +#pragma GCC visibility pop +#endif + +#undef strcasecmp +#undef strncasecmp + +#define HAVE_APR +#include +#include +#define MODNAME "mod_h323" + + +typedef enum { + TFLAG_IO = (1 << 0), + TFLAG_INBOUND = (1 << 1), + TFLAG_OUTBOUND = (1 << 2), + TFLAG_READING = (1 << 3), + TFLAG_WRITING = (1 << 4), + TFLAG_BYE = (1 << 5), + TFLAG_VOICE = (1 << 6), + TFLAG_RTP_READY = (1 << 7), + TFLAG_CODEC_READY = (1 << 8), + TFLAG_TRANSPORT = (1 << 9), + TFLAG_ANSWER = (1 << 10), + TFLAG_VAD_IN = (1 << 11), + TFLAG_VAD_OUT = (1 << 12), + TFLAG_VAD = (1 << 13), + TFLAG_DO_CAND = (1 << 14), + TFLAG_DO_DESC = (1 << 15), + TFLAG_LANADDR = (1 << 16), + TFLAG_AUTO = (1 << 17), + TFLAG_DTMF = (1 << 18), + TFLAG_TIMER = (1 << 19), + TFLAG_TERM = (1 << 20), + TFLAG_TRANSPORT_ACCEPT = (1 << 21), + TFLAG_READY = (1 << 22), +} TFLAGS; + +struct mod_h323_globals { + int trace_level; + char *codec_string; + char *context; + char *dialplan; +}; + +extern struct mod_h323_globals mod_h323_globals; + +class FSH323Connection; +class FSH323_ExternalRTPChannel; + +typedef struct { + unsigned int flags; + switch_timer_t read_timer; + switch_codec_t read_codec; + switch_codec_t write_codec; + switch_frame_t read_frame; + + switch_timer_t vid_read_timer; + switch_codec_t vid_read_codec; + switch_codec_t vid_write_codec; + switch_rtp_t *rtp_session; + switch_mutex_t *flag_mutex; + switch_mutex_t *h323_mutex; + + FSH323Connection *me; +} h323_private_t; + +#define DECLARE_CALLBACK0(name) \ + static switch_status_t name(switch_core_session_t *session) { \ + h323_private_t *tech_pvt = (h323_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() + +#define DECLARE_CALLBACK1(name, type1, name1) \ + static switch_status_t name(switch_core_session_t *session, type1 name1) { \ + h323_private_t *tech_pvt = (h323_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) + +#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) { \ + h323_private_t *tech_pvt = (h323_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) + +class FSH323EndPoint; +class FSProcess : public PLibraryProcess { + PCLASSINFO(FSProcess, PLibraryProcess); + + public: + FSProcess(); + ~FSProcess(); + + bool Initialise(switch_loadable_module_interface_t *iface); + + FSH323EndPoint & GetH323EndPoint() const { return *m_h323endpoint; } + + protected: + FSH323EndPoint * m_h323endpoint; +}; + +struct FSListener { + FSListener() { + } + + PString name; + H323ListenerTCP *listenAddress; + PString localUserName; + PString gatekeeper; +}; + +class FSH323EndPoint:public H323EndPoint { + + PCLASSINFO(FSH323EndPoint, H323EndPoint); + public: + FSH323EndPoint(); + + + /**Create a connection that uses the specified call. + */ + virtual H323Connection* CreateConnection( + unsigned callReference, + void* userData, + H323Transport* transport, + H323SignalPDU* setupPDU + ); + virtual bool OnSetGatewayPrefixes(PStringList & prefixes) const; + + bool Initialise(switch_loadable_module_interface_t *iface); + + switch_status_t ReadConfig(int reload); + + switch_endpoint_interface_t *GetSwitchInterface() const { + return m_freeswitch; + } + FSH323Connection * FSMakeCall(const PString & dest,void *userData); + list m_listeners; + + protected: + PStringList m_gkPrefixes; + switch_endpoint_interface_t *m_freeswitch; + PString m_gkAddress; + PString m_gkIdentifer; + PString m_gkInterface; + +}; + +class FSH323Connection:public H323Connection { + PCLASSINFO(FSH323Connection, H323Connection) + + public: + FSH323Connection(FSH323EndPoint& endpoint, + H323Transport* transport, + unsigned callReference, + switch_caller_profile_t *outbound_profile, + switch_core_session_t *fsSession, + switch_channel_t *fsChannel); + + ~FSH323Connection(); + + virtual H323Channel* CreateRealTimeLogicalChannel( + const H323Capability& capability, + H323Channel::Directions dir, + unsigned sessionID, + const H245_H2250LogicalChannelParameters* param, + RTP_QOS * rtpqos = NULL + ); + virtual PBoolean OnStartLogicalChannel(H323Channel& channel); + virtual PBoolean OnCreateLogicalChannel(const H323Capability& capability, H323Channel::Directions dir, unsigned& errorCode); + virtual void OnReceivedReleaseComplete(const H323SignalPDU & pdu); + virtual bool OnReceivedProgress(const H323SignalPDU &); + virtual bool OnSendReleaseComplete(H323SignalPDU & pdu); + virtual PBoolean OpenLogicalChannel(const H323Capability& capability, unsigned sessionID, H323Channel::Directions dir); + void setRemoteAddress(const char* remoteIP, WORD remotePort); + virtual void OnSetLocalCapabilities(); + virtual bool OnAlerting(const H323SignalPDU &alertingPDU, const PString &user); + virtual void OnEstablished(); + bool SetLocalCapabilities(); + static bool decodeCapability(const H323Capability& capability, const char** dataFormat, int* payload = 0, PString* capabName = 0); + virtual H323Connection::AnswerCallResponse OnAnswerCall(const PString& caller, + const H323SignalPDU& signalPDU, H323SignalPDU& connectPDU); + virtual bool OnReceivedCapabilitySet(const H323Capabilities & remoteCaps, + const H245_MultiplexCapability * muxCap, + H245_TerminalCapabilitySetReject & reject); + switch_core_session_t *GetSession() const { + return m_fsSession; + } + virtual void SendUserInputTone(char tone, unsigned duration = 0, unsigned logicalChannel = 0, unsigned rtpTimestamp = 0); + virtual void OnUserInputTone(char, unsigned, unsigned, unsigned); + virtual void OnUserInputString(const PString &value); + DECLARE_CALLBACK0(on_init); + DECLARE_CALLBACK0(on_routing); + DECLARE_CALLBACK0(on_execute); + + DECLARE_CALLBACK0(on_exchange_media); + DECLARE_CALLBACK0(on_soft_execute); + + 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); + + bool m_callOnPreAnswer; + bool m_startRTP; + PSyncPoint m_rxAudioOpened; + PSyncPoint m_txAudioOpened; + protected: + FSH323EndPoint * m_endpoint; + PString m_remoteAddr; + int m_remotePort; + switch_core_session_t *m_fsSession; + switch_channel_t *m_fsChannel; + PIPSocket::Address m_RTPlocalIP; + WORD m_RTPlocalPort; + unsigned char m_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; +}; + +class FSH323_ExternalRTPChannel : public H323_ExternalRTPChannel{ + PCLASSINFO(FSH323_ExternalRTPChannel, H323_ExternalRTPChannel); +public: + /* Create a new channel. */ + FSH323_ExternalRTPChannel( + FSH323Connection& connection, + const H323Capability& capability, + Directions direction, + unsigned sessionID, + const PIPSocket::Address& ip, + WORD dataPort + ); + /* Destructor */ + ~FSH323_ExternalRTPChannel(); + + virtual PBoolean Start(); + virtual PBoolean OnReceivedAckPDU(const H245_H2250LogicalChannelAckParameters& param); + virtual PBoolean OnSendingPDU(H245_H2250LogicalChannelParameters& param); + virtual PBoolean OnReceivedPDU(const H245_H2250LogicalChannelParameters& param,unsigned& errorCode); + virtual void OnSendOpenAck(H245_H2250LogicalChannelAckParameters& param); + + +private: + FSH323Connection* m_conn; + const H323Capability* m_capability; + switch_core_session_t *m_fsSession; + switch_channel_t *m_fsChannel; + switch_codec_t *m_switchCodec; + OpalMediaFormat *m_format; + switch_frame_t m_readFrame; + switch_timer_t *m_switchTimer; + PString m_RTPremoteIP; + WORD m_RTPremotePort; + PString m_RTPlocalIP; + WORD m_RTPlocalPort; + BYTE payloadCode; + +}; + +class BaseG7231Capab : public H323AudioCapability +{ + PCLASSINFO(BaseG7231Capab, H323AudioCapability); +public: + BaseG7231Capab(const char* fname, bool annexA = true) + : H323AudioCapability(7,4), m_name(fname), m_aa(annexA) + { } + + virtual PObject* Clone() const{ + return new BaseG7231Capab(*this); + } + + virtual unsigned GetSubType() const{ + return H245_AudioCapability::e_g7231; + } + + virtual PString GetFormatName() const{ + return m_name; + } + + virtual H323Codec* CreateCodec(H323Codec::Direction direction) const{ + return 0; + } + + virtual Comparison Compare(const PObject& obj) const{ + Comparison res = H323AudioCapability::Compare(obj); + if (res != EqualTo) + return res; + bool aa = static_cast(obj).m_aa; + if (aa && !m_aa) + return LessThan; + if (m_aa && !aa) + return GreaterThan; + return EqualTo; + } + + virtual bool OnSendingPDU(H245_AudioCapability& pdu, unsigned packetSize) const { + pdu.SetTag(GetSubType()); + H245_AudioCapability_g7231& g7231 = pdu; + g7231.m_maxAl_sduAudioFrames = packetSize; + g7231.m_silenceSuppression = m_aa; + return true; + } + + virtual bool OnReceivedPDU(const H245_AudioCapability& pdu, unsigned& packetSize){ + if (pdu.GetTag() != H245_AudioCapability::e_g7231) + return false; + const H245_AudioCapability_g7231& g7231 = pdu; + packetSize = g7231.m_maxAl_sduAudioFrames; + m_aa = (g7231.m_silenceSuppression != 0); + return true; + } + +protected: + const char* m_name; + bool m_aa; +}; + +class BaseG729Capab : public H323AudioCapability +{ + PCLASSINFO(BaseG729Capab, H323AudioCapability); +public: + BaseG729Capab(const char* fname, unsigned type = H245_AudioCapability::e_g729) + : H323AudioCapability(24,6), m_name(fname), m_type(type) + { } + virtual PObject* Clone() const + // default copy constructor - take care! + { return new BaseG729Capab(*this); } + virtual unsigned GetSubType() const + { return m_type; } + virtual PString GetFormatName() const + { return m_name; } + virtual H323Codec* CreateCodec(H323Codec::Direction direction) const + { return 0; } +protected: + const char* m_name; + unsigned m_type; +}; + +class BaseGSM0610Cap : public H323AudioCapability +{ + PCLASSINFO(BaseGSM0610Cap, H323AudioCapability); + +public: + + BaseGSM0610Cap(const char* fname, unsigned type = H245_AudioCapability::e_gsmFullRate) + : H323AudioCapability(24,2), m_name(fname), m_type(type),m_comfortNoise(0),m_scrambled(0) + { } + + virtual PObject * Clone() const{ + return new BaseGSM0610Cap(*this); + } + + virtual H323Codec* CreateCodec(H323Codec::Direction direction) const{ + return 0; + } + + virtual unsigned GetSubType() const{ + return H245_AudioCapability::e_gsmFullRate; + } + + virtual PString GetFormatName() const{ + return m_name; + } + + virtual bool OnSendingPDU(H245_AudioCapability & pdu, unsigned packetSize) const{ + pdu.SetTag(H245_AudioCapability::e_gsmFullRate); + H245_GSMAudioCapability & gsm = pdu; + gsm.m_audioUnitSize = packetSize * 33; + gsm.m_comfortNoise = m_comfortNoise; + gsm.m_scrambled = m_scrambled; + return true; + } + + virtual bool OnReceivedPDU(const H245_AudioCapability & pdu, unsigned & packetSize){ + PTRACE(2, "mod_h323\t==============>BaseGSM0610Cap::OnReceivedPDU"); + if (pdu.GetTag() != H245_AudioCapability::e_gsmFullRate) + return false; + const H245_GSMAudioCapability & gsm = pdu; + packetSize = (gsm.m_audioUnitSize + 32) / 33; + m_comfortNoise = gsm.m_comfortNoise; + m_scrambled = gsm.m_scrambled; + return true; + } + +protected: + const char* m_name; + int m_comfortNoise; + int m_scrambled; + unsigned m_type; +}; + + +#define DEFINE_H323_CAPAB(cls,base,param,name) \ +class cls : public base { \ + public: \ + cls() : base(name,param) { } \ +}; \ +H323_REGISTER_CAPABILITY(cls,name) \ + + +DEFINE_H323_CAPAB(FS_G7231_5,BaseG7231Capab,false,OPAL_G7231_5k3"{sw}") +DEFINE_H323_CAPAB(FS_G7231_6,BaseG7231Capab,false,OPAL_G7231_6k3"{sw}") +DEFINE_H323_CAPAB(FS_G7231A_5,BaseG7231Capab,true,OPAL_G7231A_5k3"{sw}") +DEFINE_H323_CAPAB(FS_G7231A_6,BaseG7231Capab,true,OPAL_G7231A_6k3"{sw}") +DEFINE_H323_CAPAB(FS_G729,BaseG729Capab,H245_AudioCapability::e_g729,OPAL_G729"{sw}") +DEFINE_H323_CAPAB(FS_G729A,BaseG729Capab,H245_AudioCapability::e_g729AnnexA,OPAL_G729A"{sw}") +DEFINE_H323_CAPAB(FS_G729B,BaseG729Capab,H245_AudioCapability::e_g729wAnnexB,OPAL_G729B"{sw}") +DEFINE_H323_CAPAB(FS_G729AB,BaseG729Capab,H245_AudioCapability::e_g729AnnexAwAnnexB,OPAL_G729AB"{sw}") +DEFINE_H323_CAPAB(FS_GSM,BaseGSM0610Cap,H245_AudioCapability::e_gsmFullRate,OPAL_GSM0610"{sw}") + +static FSProcess *h323_process = NULL;