]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
initial commit
authorGeorgiewskiy Yuriy <bottleman@icf.org.ru>
Sat, 24 Oct 2009 01:48:45 +0000 (01:48 +0000)
committerGeorgiewskiy Yuriy <bottleman@icf.org.ru>
Sat, 24 Oct 2009 01:48:45 +0000 (01:48 +0000)
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@15223 d0543943-73ff-0310-b7d9-9358b9ac24b2

src/mod/endpoints/mod_h323/Makefile [new file with mode: 0644]
src/mod/endpoints/mod_h323/bugs [new file with mode: 0644]
src/mod/endpoints/mod_h323/changes.txt [new file with mode: 0644]
src/mod/endpoints/mod_h323/h323.conf.xml [new file with mode: 0644]
src/mod/endpoints/mod_h323/mod_h323.cpp [new file with mode: 0644]
src/mod/endpoints/mod_h323/mod_h323.h [new file with mode: 0644]

diff --git a/src/mod/endpoints/mod_h323/Makefile b/src/mod/endpoints/mod_h323/Makefile
new file mode 100644 (file)
index 0000000..e38e8b6
--- /dev/null
@@ -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 (file)
index 0000000..8e08697
--- /dev/null
@@ -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 (file)
index 0000000..1bc56e6
--- /dev/null
@@ -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 (file)
index 0000000..0a0fb09
--- /dev/null
@@ -0,0 +1,18 @@
+<configuration name="h323.conf" description="H323 Endpoints">
+  <settings>
+    <param name="trace-level" value="10"/>
+    <param name="context" value="default"/>
+    <param name="dialplan" value="XML"/>
+    <param name="codec-prefs" value="PCMA,GSM,G729,G726"/>
+    <param name="gk-address" value=""/>    <!-- empty to disable, "*" to search LAN -->
+    <param name="gk-identifer" value=""/>  <!-- optional name of gk -->
+    <param name="gk-interface" value=""/>  <!-- optional listener interface name -->
+  </settings>
+  <listeners>
+    <listener name="default">
+      <param name="h323-ip" value="$${local_ip_v4}"/>
+      <param name="h323-port" value="1720"/>
+    </listener>
+  </listeners>
+</configuration>
+
diff --git a/src/mod/endpoints/mod_h323/mod_h323.cpp b/src/mod/endpoints/mod_h323/mod_h323.cpp
new file mode 100644 (file)
index 0000000..ac04599
--- /dev/null
@@ -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(&params, 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(&params);
+
+    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 = "<<userData<<" [" << *this<<"]");
+
+       if ((switch_caller_profile_t *)userData){
+               PTRACE(4, "mod_h323\t------> 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 = "<<fsSession);
+    switch_channel_t *fsChannel = switch_core_session_get_channel(fsSession);
+
+       if (fsChannel == NULL) {
+        switch_core_session_destroy(&fsSession);
+        return NULL;
+       }
+       
+  return new FSH323Connection(*this,transport,callReference,(switch_caller_profile_t *)userData, fsSession, fsChannel);
+  
+}
+
+bool FSH323EndPoint::OnSetGatewayPrefixes(PStringList & prefixes) const{
+       PTRACE(4, "mod_h323\t======>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 = "<<localCapabilities.GetSize()<<" [" << *this<<"]");
+    if (!mod_h323_globals.codec_string)
+               return false;
+    
+    bool nocodecs = true;
+    bool changed = false;
+    for (int i = 0; i < localCapabilities.GetSize(); i++) {
+               const char* format = 0;
+               PString fname;
+               decodeCapability(localCapabilities[i],&format,0,&fname);
+               if (format) {
+                       PString m_globalcodec = ((const char *)mod_h323_globals.codec_string);
+                       if (m_globalcodec.Find(format) < 0) {
+                               PTRACE(1, "mod_h323\tRemoving capability '"<<fname<<"' ("<<format<<") not in remote '"<<m_globalcodec<<"'");
+                               changed = true;
+                               for (PINDEX idx = 0; idx < fastStartChannels.GetSize(); idx++) {
+                                       if (fastStartChannels[idx].GetCapability() == localCapabilities[i]) {
+                                               PTRACE(1, "mod_h323\tRemoving fast start channel "<<fastStartChannels[idx].GetDirection()<<" '"<<fname<<"' ("<<format<<")");
+                                               fastStartChannels.RemoveAt(idx--);
+                                       }               
+                               }
+                               localCapabilities.Remove(fname);
+                               i--;
+                       } else  nocodecs = false;
+               }
+    }
+    if (nocodecs) {
+               PTRACE(3, "mod_h323\tNo codecs remaining for H323 connection ["<<*this<<"]");
+               changed = false;
+               ClearCall(EndedByCapabilityExchange);           
+    }
+    return changed;
+}
+
+bool FSH323Connection::decodeCapability(const H323Capability& capability, const char** dataFormat, int* payload, PString* capabName){
+       PTRACE(4, "mod_h323\t======>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 '"<<format<<"' payload "<<pload);
+    if (format) {
+               if (capabName)
+                       *capabName = fname;
+               if (dataFormat)
+                       *dataFormat = format;
+               if (payload)
+                       *payload = pload;
+               return true;
+    }
+    return false;
+}
+
+H323Connection::AnswerCallResponse FSH323Connection::OnAnswerCall(const PString &caller,
+    const H323SignalPDU &setupPDU, H323SignalPDU &connectPDU){
+       PTRACE(4, "mod_h323\t======>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; i<address.GetSize(); i++)
+               PTRACE(2, "mod_h323\t address index = "<<i<<" value = "<<(const char *)H323GetAliasAddressString(address[i]));
+       PString called;
+    if (address.GetSize() > 0)
+               called = (const char *)H323GetAliasAddressString(address[0]);
+    if (!called.IsEmpty())
+               PTRACE(2, "mod_h323\t Called number or alias = "<<called);
+    else {
+               PString callnam;
+               if (q931.GetCalledPartyNumber(callnam)) {
+                       called=(const char *)callnam;                   
+                       PTRACE(2, "mod_h323\t Called-Party-Number = "<<called);
+               }
+    }
+       
+       
+    switch_caller_profile_t *caller_profile = switch_caller_profile_new(switch_core_session_get_pool(m_fsSession),
+                                                                        NULL,
+                                                                        /** username */
+                                                                        mod_h323_globals.dialplan,
+                                                                        /** dial plan */
+                                                                        GetRemotePartyName(),
+                                                                        /** caller_id_name */
+                                                                        GetRemotePartyNumber(),
+                                                                        /** caller_id_number */
+                                                                        NULL,
+                                                                        /** network addr */
+                                                                        NULL,
+                                                                        /** ANI */
+                                                                        NULL,
+                                                                        /** ANI II */
+                                                                        NULL,
+                                                                        /** RDNIS */
+                                                                        modulename,
+                                                                        /** source */
+                                                                        mod_h323_globals.context,
+                                                                        /** set context  */
+                                                                        called
+                                                                        /** destination_number */
+                                                                        );
+    if (caller_profile == NULL) {
+        PTRACE(1, "mod_h323\tCould not create caller profile");
+        return H323Connection::AnswerCallDenied;
+    }
+
+    PTRACE(4, "mod_h323\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);
+
+    char name[256] = "h323/";
+    switch_copy_string(name + 5, caller_profile->destination_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()<<"',"<<dir<<") "<<*this);
+    
+       return H323Connection::OnCreateLogicalChannel(capability,dir,errorCode);
+}
+
+void FSH323Connection::OnReceivedReleaseComplete(const H323SignalPDU & pdu){
+       PTRACE(4, "mod_h323\t======>FSH323Connection::OnReceivedReleaseComplete cause = "<<pdu.GetQ931().GetCause()<<" value = "<<(switch_call_cause_t)pdu.GetQ931().GetCause());
+       h323_private_t *tech_pvt = (h323_private_t *) switch_core_session_get_private(m_fsSession);
+//     tech_pvt->me = NULL;
+       
+       switch_channel_hangup(switch_core_session_get_channel(m_fsSession),(switch_call_cause_t)pdu.GetQ931().GetCause()); 
+//     on_hangup(GetCallReference(), (const char *)GetCallToken(), pdu.GetQ931().GetCause());
+       return H323Connection::OnReceivedReleaseComplete(pdu);
+}
+
+bool FSH323Connection::OnReceivedProgress(const H323SignalPDU &pdu)
+{
+       PTRACE(4, "mod_h323\t======>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 = "<<pdu.GetQ931().GetCause()<<" value = "<<(switch_call_cause_t)pdu.GetQ931().GetCause());
+       
+       switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER);                      
+       return H323Connection::OnSendReleaseComplete(pdu);
+}
+
+PBoolean FSH323Connection::OpenLogicalChannel(const H323Capability& capability, unsigned sessionID, H323Channel::Directions dir){
+       PTRACE(4, "mod_h323\t======>FSH323Connection::OpenLogicalChannel ('"<< (const char *)capability.GetFormatName()<<"', "<< sessionID<<", "<<dir <<") "<<*this);
+    
+    return H323Connection::OpenLogicalChannel(capability,sessionID,dir);
+}
+
+
+bool FSH323Connection::OnReceivedCapabilitySet(const H323Capabilities & remoteCaps,
+                                                       const H245_MultiplexCapability * muxCap,
+                                                       H245_TerminalCapabilitySetReject & reject){
+       
+       PTRACE(4, "mod_h323\t======>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 = "<<remoteCapabilities[i]);
+       }
+       
+       H323Capability * cap = remoteCapabilities.FindCapability(H323Capability::e_Audio);
+       if (cap == NULL) {
+               PTRACE(4, "mod_h323\t----> 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 ="<<remoteIP<<", remotePort = "<<remotePort<<" "<<*this);
+    if (!m_remotePort) {
+       PTRACE(4, "mod_h323\tGot remote RTP address "<<remoteIP<<":"<<remotePort<<" "<<*this);
+       m_remotePort = remotePort;
+       m_remoteAddr = remoteIP;
+    }
+}
+
+switch_status_t FSH323Connection::on_execute(){
+       PTRACE(4, "mod_h323\t======>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<OpalConnection> 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 = "<<tech_pvt->read_frame.source<< "\n packetlen = "<<tech_pvt->read_frame.packetlen<<"\n datalen = "<<tech_pvt->read_frame.datalen<<"\n samples = "<<tech_pvt->read_frame.samples<<"\n rate = "<<tech_pvt->read_frame.rate<<"\n payload = "<<(int)tech_pvt->read_frame.payload<<"\n timestamp = "<<tech_pvt->read_frame.timestamp<<"\n seq = "<<tech_pvt->read_frame.seq<<"\n ssrc = "<<tech_pvt->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.GetPayloadType());
+       PTRACE(4, "mod_h323\t------->capability.GetFormatName() return = "<<capability.GetFormatName());
+       
+       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 format(fname, FALSE);
+       m_format = &format;
+       payloadCode = format.GetPayloadType();
+       PTRACE(4, "mod_h323\t------->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 = "<<m_format->GetFrameSize());
+       PTRACE(4, "mod_h323\t------------------->GetFrameTime() return = "<<m_format->GetFrameTime());
+       PTRACE(4, "mod_h323\t------------------->payloadCode = "<<(int)payloadCode);
+       PTRACE(4, "mod_h323\t------------------->m_capability->GetTxFramesInPacket() return =  "<<m_capability->GetTxFramesInPacket());
+       PTRACE(4, "mod_h323\t------------------->m_capability->GetFormatName() return =  "<<m_capability->GetFormatName());
+       
+       PTRACE(4, "mod_h323\t------------------->GetH245CodecName() return =  "<<GetH245CodecName(m_capability->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 = "<<tech_pvt->rtp_session);
+       PTRACE(4, "mod_h323\t------------------->samples_per_packet = "<<m_switchCodec->implementation->samples_per_packet);
+       PTRACE(4, "mod_h323\t------------------->actual_samples_per_second = "<<m_switchCodec->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 = "<<tech_pvt->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 "<<m_RTPremoteIP<<":"<<m_RTPremotePort);
+       switch_mutex_unlock(tech_pvt->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()<<":"<<remotePort);
+    m_conn->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 = "<<dest<<" ["<<*this<<"]");
+       
+       FSH323Connection * connection;
+       PString token;
+       H323Transport *transport = NULL;
+       
+    if (listeners.GetSize() > 0) {
+                       H323TransportAddress taddr = listeners[0].GetTransportAddress();
+                       PIPSocket::Address addr;
+                       WORD port;
+                       if (taddr.GetIpAndPort(addr, port)) {                           
+                               if (addr) {
+                                       PTRACE(4, "mod_h323\t----> Using "<<addr<<" for outbound call");
+                                       transport = new H323TransportTCP(*this, addr,false);
+                                       if (!transport)                                         
+                                               PTRACE(4, "mod_h323\t----> 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 = "<<outbound_profile->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 = "<<connection->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 (file)
index 0000000..727169e
--- /dev/null
@@ -0,0 +1,431 @@
+
+
+#if defined(__GNUC__) && defined(HAVE_VISIBILITY)
+#pragma GCC visibility push(default)
+#endif
+
+#include <ptlib.h>
+#include <h323.h>
+#include <h323neg.h>
+#include <h323pdu.h>
+#include <h323caps.h>
+#include <ptclib/delaychan.h>
+
+#include <list>
+
+
+#if defined(__GNUC__) && defined(HAVE_VISIBILITY)
+#pragma GCC visibility pop
+#endif
+
+#undef strcasecmp
+#undef strncasecmp
+
+#define HAVE_APR
+#include <switch.h>
+#include <switch_version.h>
+#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 <FSListener> 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<const BaseG7231Capab&>(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;