]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
Updated to latest code used on test machines
authorDavid Yat Sin <dyatsin@sangoma.com>
Mon, 30 Jul 2012 15:07:01 +0000 (11:07 -0400)
committerDavid Yat Sin <dyatsin@sangoma.com>
Mon, 30 Jul 2012 15:07:01 +0000 (11:07 -0400)
src/mod/endpoints/mod_opal/mod_opal.cpp
src/mod/endpoints/mod_opal/mod_opal.h

index 0760588019b674449d826ab62d50dcf075b08239..8244737115ab9fd316e58ce02ee22aee23070c0c 100644 (file)
@@ -4,6 +4,7 @@
  * Version: MPL 1.1
  *
  * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)
+ * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
 #include <h323/h323pdu.h>
 #include <h323/gkclient.h>
 
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, mod_opal_globals.codec_string);
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, mod_opal_globals.context);
-SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, mod_opal_globals.dialplan);
 
+/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a
+   MAXIMUM audio frames per packet is nototiated, and there is no
+   requirement for the remote to actually send that many. So, in say GSM, we
+   negotiate up to 3 frames or 60ms of data and the remote actually sends one
+   (20ms) frame per packet. Perfectly legal but blows up the media handling
+   in FS.
 
-#define CF_NEED_FLUSH (1 << 1)
-
-struct mod_opal_globals mod_opal_globals = { 0 };
+   Eventually we will get around to bundling the packets, but not yet. This
+   compile flag will just force one frame/packet for all audio codecs.
+ */
+#define IMPLEMENT_MULTI_FAME_AUDIO 0
 
 
-static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
-                                                   switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session,
-                                                   switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause);
+static switch_call_cause_t create_outgoing_channel(switch_core_session_t   *session,
+                                                   switch_event_t          *var_event,
+                                                   switch_caller_profile_t *outbound_profile,
+                                                   switch_core_session_t  **new_session,
+                                                   switch_memory_pool_t   **pool,
+                                                   switch_originate_flag_t  flags,
+                                                   switch_call_cause_t     *cancel_cause);
 
 
 static FSProcess *opal_process = NULL;
 
 
-static const char ModuleName[] = "opal";
-
-
-static switch_status_t on_hangup(switch_core_session_t *session);
-static switch_status_t on_destroy(switch_core_session_t *session);
+static PConstString const ModuleName("opal");
+static char const ConfigFile[] = "opal.conf";
 
 
 static switch_io_routines_t opalfs_io_routines = {
@@ -70,7 +76,7 @@ static switch_state_handler_table_t opalfs_event_handlers = {
     /*.on_init */ FSConnection::on_init,
     /*.on_routing */ FSConnection::on_routing,
     /*.on_execute */ FSConnection::on_execute,
-    /*.on_hangup */ on_hangup,
+    /*.on_hangup */ FSConnection::on_hangup,
     /*.on_exchange_media */ FSConnection::on_exchange_media,
     /*.on_soft_execute */ FSConnection::on_soft_execute,
     /*.on_consume_media*/ NULL,
@@ -78,7 +84,7 @@ static switch_state_handler_table_t opalfs_event_handlers = {
     /*.on_reset*/ NULL,
     /*.on_park*/ NULL,
     /*.on_reporting*/ NULL,
-    /*.on_destroy*/ on_destroy
+    /*.on_destroy*/ FSConnection::on_destroy
 };
 
 
@@ -89,7 +95,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load);
 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown);
 SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL);
 
-SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
+SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load)
+{
     switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n");
 
     /* Prevent the loading of OPAL codecs via "plug ins", this is a directory
@@ -110,8 +117,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
     if (opal_process->Initialise(*module_interface)) {
         switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n");
         //unloading causes a seg in linux
-        return SWITCH_STATUS_NOUNLOAD;
-        //return SWITCH_STATUS_SUCCESS;
+        //return SWITCH_STATUS_UNLOAD;
+        return SWITCH_STATUS_SUCCESS;
     }
 
     delete opal_process;
@@ -120,11 +127,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
 }
 
 
-SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) {
-    
-    switch_safe_free(mod_opal_globals.context);
-    switch_safe_free(mod_opal_globals.dialplan);
-    switch_safe_free(mod_opal_globals.codec_string);
+SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown)
+{
     delete opal_process;
     opal_process = NULL;
     return SWITCH_STATUS_SUCCESS;
@@ -133,161 +137,79 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) {
 SWITCH_END_EXTERN_C
 /*******************************************************************************/
 
-
-
-static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
-                                                   switch_event_t *var_event,
-                                                   switch_caller_profile_t *outbound_profile,
-                                                   switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause)
-{
-    if (opal_process == NULL) {
-        return SWITCH_CAUSE_CRASH;
-    }
-
-    PString token;
-
-    FSManager & manager = opal_process->GetManager();
-    if (!manager.SetUpCall("local:", outbound_profile->destination_number, token, outbound_profile)) {
-        return SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
-    }
-
-    PSafePtr < OpalCall > call = manager.FindCallWithLock(token);
-
-    if (call == NULL) {
-        return SWITCH_CAUSE_PROTOCOL_ERROR;
-    }
-
-    PSafePtr < FSConnection > connection = call->GetConnectionAs < FSConnection > (0);
-
-    if (connection == NULL) {
-        return SWITCH_CAUSE_PROTOCOL_ERROR;
-    }
-
-    *new_session = connection->GetSession();
-
-    return SWITCH_CAUSE_SUCCESS;
-}
-
-
 ///////////////////////////////////////////////////////////////////////
 
 #if PTRACING
 
-class FSTrace : public ostream {
- public:
- FSTrace()
-     : ostream(&buffer)
-        {
-        }
-
- 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;
-                }
+class FSTrace : public std::ostream
+{
+private:
+  class Buffer : public std::stringbuf
+  {
+    virtual int sync()
+    {
+      std::string s = str();
+      if (s.empty())
+        return 0;
+
+      //Due to explicit setting of flags we know exactly what we are getting
+      PStringArray fields(6);
+      static PRegularExpression logRE("^([0-9]+)\t *([^(]+)\\(([0-9]+)\\)\t(.*)", PRegularExpression::Extended);
+      if (!logRE.Execute(s.c_str(), fields)) {
+        fields[1] = "4";
+        fields[2] = __FILE__;
+        fields[3] = __LINE__;
+        fields[4] = s;
+      }
 
-                if (bufPtr) {
-                    if (end_of(bufPtr) != '\n') {
-                        fmt = "%s\n";
-                    }
-                    if (!(file && func && line)) tchannel = SWITCH_CHANNEL_ID_LOG_CLEAN;
+      switch_log_level_t level;
+      switch (fields[1].AsUnsigned()) {
+        case 0 :
+          level = SWITCH_LOG_ALERT;
+          break;
+        case 1 :
+          level = SWITCH_LOG_ERROR;
+          break;
+        case 2 :
+          level = SWITCH_LOG_WARNING;
+          break;
+        case 3 :
+          level = SWITCH_LOG_INFO;
+          break;
+        default :
+          level = SWITCH_LOG_DEBUG;
+          break;
+      }
 
-                    switch_log_printf(tchannel, file, func, line, NULL, level, fmt, bufPtr);
-                }
-                
-            }
+      fields[4].Replace("\t", " ", true);
+      switch_log_printf(SWITCH_CHANNEL_ID_LOG,
+                        fields[2],
+                        "PTLib-OPAL",
+                        fields[3].AsUnsigned(),
+                        NULL,
+                        level,
+                        "%s", fields[4].GetPointer());
+
+      // Reset string
+      str(std::string());
+      return 0;
+    }
+  } buffer;
 
-            return 0;
-        }
-    } buffer;
+public:
+  FSTrace()
+    : ostream(&buffer)
+  {
+  }
 };
 
-#endif
+#endif // PTRACING
 
 
 ///////////////////////////////////////////////////////////////////////
 
 FSProcess::FSProcess()
-  : PLibraryProcess("Vox Lucida Pty. Ltd.", "mod_opal", 1, 0, AlphaCode, 1)
+  : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1)
   , m_manager(NULL)
 {
 }
@@ -296,19 +218,24 @@ FSProcess::FSProcess()
 FSProcess::~FSProcess()
 {
   delete m_manager;
+#if PTRACING
+    PTrace::SetStream(NULL); // This will delete the FSTrace object
+#endif
 }
 
 
 bool FSProcess::Initialise(switch_loadable_module_interface_t *iface)
 {
-  m_manager = new FSManager();
-  return m_manager != NULL && m_manager->Initialise(iface);
+    m_manager = new FSManager();
+    return m_manager != NULL && m_manager->Initialise(iface);
 }
 
 
 ///////////////////////////////////////////////////////////////////////
 
 FSManager::FSManager()
+  : m_context("default")
+  , m_dialplan("XML")
 {
     // These are deleted by the OpalManager class, no need to have destructor
     m_h323ep = new H323EndPoint(*this);
@@ -321,12 +248,6 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
 {
     ReadConfig(false);
 
-#if PTRACING
-    PTrace::SetLevel(mod_opal_globals.trace_level);        //just for fun and eyecandy ;)
-    PTrace::SetOptions(PTrace::TraceLevel);
-    PTrace::SetStream(new FSTrace);
-#endif
-
     m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE);
     m_FreeSwitch->interface_name = ModuleName;
     m_FreeSwitch->io_routines = &opalfs_io_routines;
@@ -338,8 +259,8 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
         m_h323ep->StartListener("");
     } else {
         for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) {
-            if (!m_h323ep->StartListener(it->listenAddress)) {
-                PTRACE(3, "mod_opal\tCannot start listener for " << it->name);
+            if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) {
+                PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name);
             }
         }
     }
@@ -363,15 +284,16 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
     GetOpalGSMAMR();
     GetOpaliLBC();
 
-    /* For compatibility with the algorithm in FSConnection::SetCodecs() we need
-       to set all audio media formats to be 1 frame per packet */
+#if !IMPLEMENT_MULTI_FAME_AUDIO
     OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats();
     for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) {
       if (it->GetMediaType() == OpalMediaType::Audio()) {
         it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), 1);
         it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), 1);
+        OpalMediaFormat::SetRegisteredMediaFormat(*it);
       }
     }
+#endif // IMPLEMENT_MULTI_FAME_AUDIO
 
     if (!m_gkAddress.IsEmpty()) {
       if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface))
@@ -391,61 +313,53 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
 
 switch_status_t FSManager::ReadConfig(int reload)
 {
-    const char *cf = "opal.conf";
-    switch_status_t status = SWITCH_STATUS_SUCCESS;
-
-    switch_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;
-    }
+    switch_event_t *request_params = NULL;
+    switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS);
+    switch_assert(request_params);
+    switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil(""));
 
-    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);
+    switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params);
     if (xml == NULL) {
-        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf);
+        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile);
         return SWITCH_STATUS_FALSE;
     }
 
     switch_xml_t xmlSettings = switch_xml_child(cfg, "settings");
     if (xmlSettings) {
         for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
-            const char *var = switch_xml_attr_soft(xmlParam, "name");
-            const char *val = switch_xml_attr_soft(xmlParam, "value");
-
-            if (!strcasecmp(var, "trace-level")) {
-                int level = atoi(val);
-                if (level > 0) {
-                    mod_opal_globals.trace_level = level;
-                }
-            } else if (!strcasecmp(var, "context")) {
-                set_global_context(val);
-            } else if (!strcasecmp(var, "dialplan")) {
-                set_global_dialplan(val);
-            } else if (!strcasecmp(var, "codec-prefs")) {
-                set_global_codec_string(val);
-            } else if (!strcasecmp(var, "jitter-size")) {
-                char * next;
-                unsigned minJitter = strtoul(val, &next, 10);
-                if (minJitter >= 10) {
-                    unsigned maxJitter = minJitter;
-                    if (*next == ',')
-                      maxJitter = atoi(next+1);
-                    SetAudioJitterDelay(minJitter, maxJitter); // In milliseconds
-                }
-            } else if (!strcasecmp(var, "gk-address")) {
+            PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
+            PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
+
+            if (var == "context") {
+                m_context = val;
+            } else if (var == "dialplan") {
+                m_dialplan = val;
+            } else if (var == "codec-prefs") {
+                m_codecPrefs = val;
+            } else if (var == "jitter-size") {
+                SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds
+            } else if (var == "gk-address") {
                 m_gkAddress = val;
-            } else if (!strcasecmp(var, "gk-identifer")) {
+            } else if (var == "gk-identifer") {
                 m_gkIdentifer = val;
-            } else if (!strcasecmp(var, "gk-interface")) {
+            } else if (var == "gk-interface") {
                 m_gkInterface = val;
+#if PTRACING
+            } else if (var == "trace-level") {
+                unsigned level = val.AsUnsigned();
+                if (level > 0) {
+                    PTrace::SetLevel(level);
+                    PTrace::ClearOptions(0xffffffff); // Everything off
+                    PTrace::SetOptions(               // Except these
+                      PTrace::TraceLevel|PTrace::FileAndLine
+#if PTLIB_CHECK_VERSION(2,11,1)
+                      |PTrace::ContextIdentifier
+#endif
+                    );
+                    PTrace::SetStream(new FSTrace);
+                }
+#endif
             }
         }
     }
@@ -457,196 +371,205 @@ switch_status_t FSManager::ReadConfig(int reload)
             m_listeners.push_back(FSListener());
             FSListener & listener = m_listeners.back();
 
-            listener.name = switch_xml_attr_soft(xmlListener, "name");
-            if (listener.name.IsEmpty())
-                listener.name = "unnamed";
-
-            PIPSocket::Address ip;
-            WORD port = 1720;
+            listener.m_name = switch_xml_attr_soft(xmlListener, "name");
+            if (listener.m_name.IsEmpty())
+                listener.m_name = "unnamed";
 
             for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
-                const char *var = switch_xml_attr_soft(xmlParam, "name");
-                const char *val = switch_xml_attr_soft(xmlParam, "value");
-                //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Var - '%s' and Val - '%s' \n", var, val);
-                if (!strcasecmp(var, "h323-ip"))
-                    ip = val;
-                else if (!strcasecmp(var, "h323-port"))
-                    port = (WORD) atoi(val);
+                PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
+                PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
+                if (var == "h323-ip")
+                    listener.m_address = val;
+                else if (var == "h323-port")
+                    listener.m_port = (uint16_t)val.AsUnsigned();
             }
 
-            listener.listenAddress = OpalTransportAddress(ip, port);
-            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.name);
+            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name);
         }
     }
 
-    switch_event_destroy(&params);
+    switch_event_destroy(&request_params);
 
     if (xml)
         switch_xml_free(xml);
 
-    return status;
+    return SWITCH_STATUS_SUCCESS;
 }
 
 
-OpalCall * FSManager::CreateCall(void * /*userData*/)
+static switch_call_cause_t create_outgoing_channel(switch_core_session_t   *session,
+                                                   switch_event_t          *var_event,
+                                                   switch_caller_profile_t *outbound_profile,
+                                                   switch_core_session_t  **new_session,
+                                                   switch_memory_pool_t   **pool,
+                                                   switch_originate_flag_t  flags,
+                                                   switch_call_cause_t     *cancel_cause)
 {
-  return new FSCall(*this);
+    if (opal_process == NULL)
+      return SWITCH_CAUSE_CRASH;
+
+    FSConnection::outgoing_params params;
+    params.var_event        = var_event;
+    params.outbound_profile = outbound_profile;
+    params.new_session      = new_session;
+    params.pool             = pool;
+    params.flags            = flags;
+    params.cancel_cause     = cancel_cause;
+    params.fail_cause       = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
+
+    if (opal_process->GetManager().SetUpCall("local:", outbound_profile->destination_number, &params) != NULL)
+        return SWITCH_CAUSE_SUCCESS;
+
+    if (*new_session != NULL)
+        switch_core_session_destroy(new_session);
+    return params.fail_cause;
 }
 
 
 ///////////////////////////////////////////////////////////////////////
 
 FSEndPoint::FSEndPoint(FSManager & manager)
-:   OpalLocalEndPoint(manager)
-{
-    PTRACE(3, "mod_opal\t FSEndPoint Created!");
-}
-
-
-bool FSEndPoint::OnIncomingCall(OpalLocalConnection & connection)
+  : OpalLocalEndPoint(manager)
+  , m_manager(manager)
 {
-    return ((FSConnection &) connection).OnIncoming();
+    PTRACE(4, "mod_opal\tFSEndPoint created.");
 }
 
 
 OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions)
 {
-    FSManager & mgr = (FSManager &) GetManager();
-    switch_core_session_t *fsSession = switch_core_session_request(mgr.GetSwitchInterface(), 
-                                       (switch_caller_profile_t *)userData ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL);
-    if (fsSession == NULL)
-        return NULL;
-
-    switch_channel_t *fsChannel = switch_core_session_get_channel(fsSession);
-
-    if (fsChannel == NULL) {
-        switch_core_session_destroy(&fsSession);
-        return NULL;
-    }
-
-    return new FSConnection(call, *this, userData, options, stringOptions, (switch_caller_profile_t *)userData, fsSession, fsChannel);
+    return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData);
 }
 
 
 ///////////////////////////////////////////////////////////////////////
 
-FSCall::FSCall(OpalManager & manager)
-  : OpalCall(manager)
+FSConnection::FSConnection(OpalCall & call,
+                           FSEndPoint & endpoint,
+                           unsigned options,
+                           OpalConnection::StringOptions* stringOptions,
+                           outgoing_params * params)
+  : OpalLocalConnection(call, endpoint, NULL, options, stringOptions)
+  , m_endpoint(endpoint)
+  , m_fsSession(NULL)
+  , m_fsChannel(NULL)
+  , m_flushAudio(false)
 {
-}
-
+    memset(&m_read_timer, 0, sizeof(m_read_timer));
+    memset(&m_read_codec, 0, sizeof(m_read_codec));
+    memset(&m_write_codec, 0, sizeof(m_write_codec));
+    memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer));
+    memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec));
+    memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec));
+
+    if (params != NULL) {
+        // If we fail, this is the cause
+        params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
+
+        if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),
+                  SWITCH_CALL_DIRECTION_INBOUND, params->flags, params->pool)) == NULL) {
+            PTRACE(1, "mod_opal\tCannot create session for outgoing call.");
+            return;
+        }
+    }
+    else {
+      if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),
+                                                     SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) {
+            PTRACE(1, "mod_opal\tCannot create session for incoming call.");
+            return;
+      }
+    }
 
-PBoolean FSCall::OnSetUp(OpalConnection & connection)
-{
-  // Transfer FS caller_id_number & caller_id_name from the FSConnection
-  // to the protocol connectionm (e.g. H.323) so gets sent correctly
-  // in outgoing packets
-  PSafePtr<FSConnection> local = GetConnectionAs<FSConnection>();
-  if (local != NULL) {
-    PSafePtr<OpalConnection> proto = local->GetOtherPartyConnection();
-    if (proto != NULL) {
-      proto->SetLocalPartyName(local->GetLocalPartyName());
-      proto->SetDisplayName(local->GetDisplayName());
+    if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) {
+        switch_core_session_destroy(&m_fsSession);
+        return;
     }
-  }
 
-  return OpalCall::OnSetUp(connection);
-}
+    switch_core_session_set_private(m_fsSession, this);
+    SafeReference(); // Make sure cannot be deleted until on_destroy()
 
+    if (params != NULL) {
+        switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile);
+        switch_channel_set_caller_profile(m_fsChannel, caller_profile);
+        SetLocalPartyName(caller_profile->caller_id_number);
+        SetDisplayName(caller_profile->caller_id_name);
+        *params->new_session = m_fsSession;
+    }
 
-///////////////////////////////////////////////////////////////////////
+    switch_channel_set_state(m_fsChannel, CS_INIT);
+}
 
 
-FSConnection::FSConnection(OpalCall & call, FSEndPoint & endpoint, void* userData, unsigned options, OpalConnection::StringOptions* stringOptions, switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel)
-  : OpalLocalConnection(call, endpoint, userData, options, stringOptions)
-  , m_endpoint(endpoint)
-  , m_fsSession(fsSession)
-  , m_fsChannel(fsChannel)
+bool FSConnection::OnOutgoingSetUp()
 {
-    opal_private_t *tech_pvt;
-
-    tech_pvt = (opal_private_t *) switch_core_session_alloc(m_fsSession, sizeof(*tech_pvt));
-    tech_pvt->me = this;
-    switch_core_session_set_private(m_fsSession, tech_pvt);
-
-    if (outbound_profile != NULL) {
-        SetLocalPartyName(outbound_profile->caller_id_number);
-        SetDisplayName(outbound_profile->caller_id_name);
+    if (m_fsSession == NULL || m_fsChannel == NULL) {
+        PTRACE(1, "mod_opal\tSession request failed.");
+        return false;
+    }
 
-        switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, outbound_profile);
-        switch_channel_set_caller_profile(m_fsChannel, caller_profile);
+    // Transfer FS caller_id_number & caller_id_name from the FSConnection
+    // to the protocol connection (e.g. H.323) so gets sent correctly
+    // in outgoing packets
+    PSafePtr<OpalConnection> proto = GetOtherPartyConnection();
+    if (proto == NULL) {
+        PTRACE(1, "mod_opal\tNo protocol connection in call.");
+        return false;
+    }
 
-        PString name = "opal/";
-        name += outbound_profile->destination_number;
-        switch_channel_set_name(m_fsChannel, name);
+    proto->SetLocalPartyName(GetLocalPartyName());
+    proto->SetDisplayName(GetDisplayName());
 
-        switch_channel_set_state(m_fsChannel, CS_INIT);
-    }
+    switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL());
+    return true;
 }
 
 
 bool FSConnection::OnIncoming()
 {
-    if (m_fsSession == NULL) {
+    if (m_fsSession == NULL || m_fsChannel == NULL) {
         PTRACE(1, "mod_opal\tSession request failed.");
         return false;
     }
 
     switch_core_session_add_stream(m_fsSession, NULL);
 
-    switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
-    if (channel == NULL) {
-        PTRACE(1, "mod_opal\tSession does not have a channel");
-        return false;
-    }
-
     PURL url = GetRemotePartyURL();
-    switch_caller_profile_t *caller_profile = switch_caller_profile_new(switch_core_session_get_pool(m_fsSession),
-                                                                        url.GetUserName(),
-                                                                        /** username */
-                                                                        mod_opal_globals.dialplan,
-                                                                        /** dial plan */
-                                                                        GetRemotePartyName(),
-                                                                        /** caller_id_name */
-                                                                        GetRemotePartyNumber(),
-                                                                        /** caller_id_number */
-                                                                        url.GetHostName(),
-                                                                        /** network addr */
-                                                                        NULL,
-                                                                        /** ANI */
-                                                                        NULL,
-                                                                        /** ANI II */
-                                                                        NULL,
-                                                                        /** RDNIS */
-                                                                        ModuleName,
-                                                                        /** source */
-                                                                        mod_opal_globals.context,
-                                                                        /** set context  */
-                                                                        GetCalledPartyNumber()
-                                                                        /** destination_number */
-                                                                        );
+    switch_caller_profile_t *caller_profile = switch_caller_profile_new(
+          switch_core_session_get_pool(m_fsSession),
+          url.GetUserName(),      /** username */
+          m_endpoint.GetManager().GetDialPlan(), /** dial plan */
+          GetRemotePartyName(),   /** caller_id_name */
+          GetRemotePartyNumber(), /** caller_id_number */
+          url.GetHostName(),      /** network addr */
+          NULL,                   /** ANI */
+          NULL,                   /** ANI II */
+          NULL,                   /** RDNIS */
+          ModuleName,             /** source */
+          m_endpoint.GetManager().GetContext(), /** set context  */
+          GetCalledPartyNumber()  /** destination_number */
+    );
     if (caller_profile == NULL) {
         PTRACE(1, "mod_opal\tCould not create caller profile");
         return false;
     }
 
     PTRACE(4, "mod_opal\tCreated switch caller profile:\n"
-           "  username       = " << caller_profile->username << "\n"
-           "  dialplan       = " << caller_profile->dialplan << "\n"
-           "  caller_id_name     = " << caller_profile->caller_id_name << "\n"
-           "  caller_id_number   = " << caller_profile->caller_id_number << "\n"
-           "  network_addr   = " << caller_profile->network_addr << "\n"
-           "  source         = " << caller_profile->source << "\n"
-           "  context        = " << caller_profile->context << "\n" "  destination_number= " << caller_profile->destination_number);
-    switch_channel_set_caller_profile(channel, caller_profile);
-
-    char name[256] = "opal/in:";
-    switch_copy_string(name + 8, caller_profile->destination_number, sizeof(name)-8);
-    switch_channel_set_name(channel, name);
-    switch_channel_set_state(channel, CS_INIT);
+              "  username          = " << caller_profile->username << "\n"
+              "  dialplan          = " << caller_profile->dialplan << "\n"
+              "  caller_id_name    = " << caller_profile->caller_id_name << "\n"
+              "  caller_id_number  = " << caller_profile->caller_id_number << "\n"
+              "  network_addr      = " << caller_profile->network_addr << "\n"
+              "  source            = " << caller_profile->source << "\n"
+              "  context           = " << caller_profile->context << "\n"
+              "  destination_number= " << caller_profile->destination_number);
+    switch_channel_set_caller_profile(m_fsChannel, caller_profile);
+
+    switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number);
 
     if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) {
         PTRACE(1, "mod_opal\tCould not launch session thread");
+        switch_core_session_destroy(&m_fsSession);
+        m_fsChannel = NULL;
         return false;
     }
 
@@ -656,57 +579,44 @@ bool FSConnection::OnIncoming()
 
 void FSConnection::OnReleased()
 {
-    opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession);
-    
-    /* so FS on_hangup will not try to deref a landmine */
-    tech_pvt->me = NULL;
-    
     m_rxAudioOpened.Signal();   // Just in case
     m_txAudioOpened.Signal();
-    H225_ReleaseCompleteReason dummy;
-    switch_channel_hangup(switch_core_session_get_channel(m_fsSession),
-                          (switch_call_cause_t)H323TranslateFromCallEndReason(GetCallEndReason(), dummy));    
-    OpalLocalConnection::OnReleased();
-}
 
+    if (m_fsChannel == NULL) {
+        PTRACE(3, "mod_opal\tHanging up FS side");
+        switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931);
+    }
 
-void FSConnection::OnAlerting()
-{
-    switch_channel_mark_ring_ready(m_fsChannel);
-    return OpalLocalConnection::OnAlerting();
+    OpalLocalConnection::OnReleased();
 }
 
+
 PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia)
 {
-    return OpalLocalConnection::SetAlerting(calleeName, withMedia);
-}
-
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return false;
 
-void FSConnection::OnEstablished()
-{
-    OpalLocalConnection::OnEstablished();
+    switch_channel_mark_ring_ready(m_fsChannel);
+    return OpalLocalConnection::SetAlerting(calleeName, withMedia);
 }
 
 
 PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration)
 {
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return false;
+
     switch_dtmf_t dtmf = { tone, duration };
     return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS;
 }
 
 
-PBoolean FSConnection::SendUserInputString(const PString & value)
-{
-  return OpalConnection::SendUserInputString(value);
-}
-
-
 OpalMediaFormatList FSConnection::GetMediaFormats() const
 {
     if (m_switchMediaFormats.IsEmpty()) {
         const_cast<FSConnection *>(this)->SetCodecs();
     }
-    
+
     return m_switchMediaFormats;
 }
 
@@ -714,39 +624,29 @@ OpalMediaFormatList FSConnection::GetMediaFormats() const
 void FSConnection::SetCodecs()
 {
     int numCodecs = 0;
-    const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];    
-    const char *codec_string = NULL, *abs, *ocodec;
-    char *tmp_codec_string = NULL;
-    char *codec_order[SWITCH_MAX_CODECS];
-    int codec_order_last;
-
-
-    if ((abs = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"))) {
-        codec_string = abs;
-    } else {
-        if ((abs = switch_channel_get_variable(m_fsChannel, "codec_string"))) {
-            codec_string = abs;
-        }
-
-        if ((ocodec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) {
-            codec_string = switch_core_session_sprintf(m_fsSession, "%s,%s", ocodec, codec_string);
+    const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
+
+    PString codec_string = switch_channel_get_variable(m_fsChannel, "absolute_codec_string");
+    if (codec_string.IsEmpty()) {
+        codec_string = switch_channel_get_variable(m_fsChannel, "codec_string");
+        const char *orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE);
+        if (orig_codec) {
+            codec_string.Splice(orig_codec, 0);
         }
     }
-    
-    if (!codec_string) {
-        codec_string = mod_opal_globals.codec_string;
+
+    if (codec_string.IsEmpty()) {
+        codec_string = m_endpoint.GetManager().GetCodecPrefs();
     }
 
-    if (codec_string) {
-        if ((tmp_codec_string = strdup(codec_string))) {
-            codec_order_last = switch_separate_string(tmp_codec_string, ',', codec_order, SWITCH_MAX_CODECS);
-            numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last);
-            
-        }
-    } else {
+    if (codec_string.IsEmpty()) {
         numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));
+    } else {
+        char *codec_order[SWITCH_MAX_CODECS];
+        int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS);
+        numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last);
     }
-    
+
     for (int i = 0; i < numCodecs; i++) {
         const switch_codec_implementation_t *codec = codecs[i];
 
@@ -762,12 +662,12 @@ void FSConnection::SetCodecs()
               continue;
             }
         }
-        
 
         // Did we match or create a new media format?
         if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) {
-            PTRACE(2, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);
+            PTRACE(3, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);
 
+#if IMPLEMENT_MULTI_FAME_AUDIO
             // Calculate frames per packet, do not use codec->codec_frames_per_packet as that field
             // has slightly different semantics when used in streamed codecs such as G.711
             int fpp = codec->samples_per_packet/switchFormat.GetFrameTime();
@@ -778,7 +678,7 @@ void FSConnection::SetCodecs()
                could end up with 60ms and the codec cannot be created. The "holes" are unlikely in
                all but streamed codecs such as G.711, where it is theoretically possible for OPAL to
                come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these
-               scenarios succifiently rare that we can safely ignore them ... for now. */
+               scenarios sufficiently rare that we can safely ignore them ... for now. */
 
             if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) {
                 switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp);
@@ -787,12 +687,11 @@ void FSConnection::SetCodecs()
             if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) {
                 switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp);
             }
+#endif // IMPLEMENT_MULTI_FAME_AUDIO
         }
 
         m_switchMediaFormats += switchFormat;
     }
-    
-    switch_safe_free(tmp_codec_string);
 }
 
 
@@ -802,110 +701,91 @@ OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFo
 }
 
 
-PBoolean FSConnection::OnOpenMediaStream(OpalMediaStream & stream)
+void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)
 {
-    if (!OpalConnection::OnOpenMediaStream(stream)) {
-        return false;
-    }
+    OpalConnection::OnPatchMediaStream(isSource, patch);
 
-    if (stream.GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) {
-        return true;
-    }
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return;
 
-    if (stream.IsSource()) {
-        m_rxAudioOpened.Signal();
-    } else {
-        m_txAudioOpened.Signal();
-    }
+    if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio())
+        return;
 
-    if (GetMediaStream(stream.GetSessionID(), stream.IsSink()) != NULL) {
+    if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
+      if (isSource)
+          m_rxAudioOpened.Signal();
+      else
+          m_txAudioOpened.Signal();
+    }
+    else if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) {
         // Have open media in both directions.
-        if (GetPhase() == AlertingPhase) {
-            switch_channel_mark_pre_answered(m_fsChannel);
-        } else if (GetPhase() < ReleasingPhase) {
+        if (IsEstablished())
             switch_channel_mark_answered(m_fsChannel);
-        }
+        else if (!IsReleased())
+            switch_channel_mark_pre_answered(m_fsChannel);
     }
-
-    return true;
 }
 
 
 switch_status_t FSConnection::on_init()
 {
-    switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
-    if (channel == NULL) {
+    if (PAssertNULL(m_fsChannel) == NULL)
         return SWITCH_STATUS_FALSE;
-    }
 
-    PTRACE(3, "mod_opal\tStarted routing for connection " << *this);
-    switch_channel_set_state(channel, CS_ROUTING);
+    PTRACE(4, "mod_opal\tStarted routing for connection " << *this);
+    switch_channel_set_state(m_fsChannel, CS_ROUTING);
     return SWITCH_STATUS_SUCCESS;
 }
 
 
 switch_status_t FSConnection::on_routing()
 {
-    PTRACE(3, "mod_opal\tRouting connection " << *this);
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return SWITCH_STATUS_FALSE;
+
+    PTRACE(4, "mod_opal\tRouting connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
 
 switch_status_t FSConnection::on_execute()
 {
-    PTRACE(3, "mod_opal\tExecuting connection " << *this);
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return SWITCH_STATUS_FALSE;
+
+    PTRACE(4, "mod_opal\tExecuting connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
-static switch_status_t on_destroy(switch_core_session_t *session)
-{
-    //switch_channel_t *channel = switch_core_session_get_channel(session);
-    opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session);
-    
-    if (tech_pvt) {
-    if (tech_pvt->read_codec.implementation) {
-        switch_core_codec_destroy(&tech_pvt->read_codec);
-    }
-
-    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);
-    }
+switch_status_t FSConnection::on_destroy()
+{
+    PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this);
 
-    if (tech_pvt->vid_write_codec.implementation) {
-        switch_core_codec_destroy(&tech_pvt->vid_write_codec);
-    }
+    m_fsChannel = NULL; // Will be destoyed by FS, so don't use it any more.
 
-    if (tech_pvt->read_timer.timer_interface) {
-        switch_core_timer_destroy(&tech_pvt->read_timer);
-    }
+    switch_core_codec_destroy(&m_read_codec);
+    switch_core_codec_destroy(&m_write_codec);
+    switch_core_codec_destroy(&m_vid_read_codec);
+    switch_core_codec_destroy(&m_vid_write_codec);
+    switch_core_timer_destroy(&m_read_timer);
+    switch_core_timer_destroy(&m_vid_read_timer);
 
-    if (tech_pvt->vid_read_timer.timer_interface) {
-        switch_core_timer_destroy(&tech_pvt->vid_read_timer);
-    }
-    }
+    switch_core_session_set_private(m_fsSession, NULL);
+    SafeDereference();
 
     return SWITCH_STATUS_SUCCESS;
 }
 
-/* this function has to be called with the original session beause the FSConnection might already be destroyed and we 
-   will can't have it be a method of a dead object
- */
-static switch_status_t on_hangup(switch_core_session_t *session)
+
+switch_status_t FSConnection::on_hangup()
 {
-    switch_channel_t *channel = switch_core_session_get_channel(session);
-    opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session);
-    
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return SWITCH_STATUS_FALSE;
+
     /* if this is still here it was our idea to hangup not opal's */
-    if (tech_pvt->me) {
-        Q931::CauseValues cause = (Q931::CauseValues)switch_channel_get_cause_q850(channel);
-        tech_pvt->me->SetQ931Cause(cause);
-        tech_pvt->me->ClearCallSynchronous(NULL, H323TranslateToCallEndReason(cause, UINT_MAX));
-        tech_pvt->me = NULL;
-    }
+    ClearCallSynchronous(NULL, H323TranslateToCallEndReason(
+              (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX));
 
     return SWITCH_STATUS_SUCCESS;
 }
@@ -913,30 +793,30 @@ static switch_status_t on_hangup(switch_core_session_t *session)
 
 switch_status_t FSConnection::on_exchange_media()
 {
-    PTRACE(3, "mod_opal\tLoopback on connection " << *this);
+    PTRACE(4, "mod_opal\tExchanging media on connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
 
 switch_status_t FSConnection::on_soft_execute()
 {
-    PTRACE(3, "mod_opal\tTransmit on connection " << *this);
+    PTRACE(4, "mod_opal\tTransmit on connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
 
 switch_status_t FSConnection::kill_channel(int sig)
 {
-    PTRACE(3, "mod_opal\tKill " << sig << " on connection " << *this);
-
     switch (sig) {
-    case SWITCH_SIG_BREAK:
-        break;
     case SWITCH_SIG_KILL:
         m_rxAudioOpened.Signal();
         m_txAudioOpened.Signal();
+        PTRACE(4, "mod_opal\tSignal channel KILL on connection " << *this);
         break;
+    case SWITCH_SIG_XFER:
+    case SWITCH_SIG_BREAK:
     default:
+        PTRACE(4, "mod_opal\tSignal channel " << sig << " on connection " << *this);
         break;
     }
 
@@ -953,41 +833,16 @@ switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf)
 
 switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg)
 {
-    switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
-
-
-    /*
-      SWITCH_MESSAGE_INDICATE_PROGRESS:  establish early media now and return SWITCH_STATUS_FALSE if you can't
-      SWITCH_MESSAGE_INDICATE_ANSWER:  answer and set up media now if it's not already and return SWITCH_STATUS_FALSE if you can't
-
-      Neither message means anything on an outbound call....
-
-      It would only happen if someone called switch_channel_answer() instead of switch_channel_mark_answered() on an outbound call.
-      it should not do anything if someone does it by accident somewhere hense this in both cases:
-
-      if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
-      return SWITCH_STATUS_FALSE;
-      }
-
-
-      When we get these messages the core will trust that you have triggered FSMediaStream::Open and are ready for media if we do not
-      have media we MUST return SWITCH_STATUS_FALSE or it will cause a CRASH.
-
-
+    if (PAssertNULL(m_fsChannel) == NULL)
+        return SWITCH_STATUS_FALSE;
 
-    */
     switch (msg->message_id) {
-    case SWITCH_MESSAGE_INDICATE_BRIDGE:
-    case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
-    case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
-        switch_channel_set_private_flag(channel, CF_NEED_FLUSH);
-        break;
-
     case SWITCH_MESSAGE_INDICATE_RINGING:
     case SWITCH_MESSAGE_INDICATE_PROGRESS:
     case SWITCH_MESSAGE_INDICATE_ANSWER:
-        {
-            switch_caller_profile_t * profile = switch_channel_get_caller_profile(channel);
+    case SWITCH_MESSAGE_INDICATE_DEFLECT:
+        if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
+            switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel);
             if (profile != NULL && profile->caller_extension != NULL)
             {
                 PSafePtr<OpalConnection> other = GetOtherPartyConnection();
@@ -999,6 +854,9 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg
                 SetDisplayName(profile->caller_extension->extension_name);
             }
         }
+        else {
+            return SWITCH_STATUS_FALSE;
+        }
         break;
 
     default:
@@ -1006,89 +864,78 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg
     }
 
     switch (msg->message_id) {
-    case SWITCH_MESSAGE_INDICATE_RINGING:
-        SetPhase(OpalConnection::AlertingPhase);
-        OnAlerting();
+    case SWITCH_MESSAGE_INDICATE_BRIDGE:
+    case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
+    case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
+        m_flushAudio = true;
         break;
 
-    case SWITCH_MESSAGE_INDICATE_DEFLECT:
-    {
-        PSafePtr<OpalConnection> other = GetOtherPartyConnection();
-        if (other != NULL)
-          other->TransferConnection(msg->string_arg);
+    case SWITCH_MESSAGE_INDICATE_RINGING:
+        AlertingIncoming();
         break;
-    }
 
     case SWITCH_MESSAGE_INDICATE_PROGRESS:
-    case SWITCH_MESSAGE_INDICATE_ANSWER:
-        {
-            int fixed = 0;
-            
-            if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
-                return SWITCH_STATUS_FALSE;
-            }
+        AutoStartMediaStreams();
+        AlertingIncoming();
 
-            if (msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) {
-                if (fixed) {
-                    /* this should send alerting + media and wait for it to be established and return SUCCESS or FAIL
-                       depending on if media was able to be established.  Need code to tell the other side we want early media here.
-                    */
-                    GetCall().OpenSourceMediaStreams(*this, OpalMediaType::Audio());
-                    SetPhase(OpalConnection::AlertingPhase);
-                    /* how do i say please establish early media ? */
-                    OnAlerting();
-                } else {
-                    /* hack to avoid getting stuck, pre_answer will imply answer */
-                    OnConnectedInternal();
-                }
-            } else {
-                OnConnectedInternal();
-            }
+        if (!WaitForMedia())
+            return SWITCH_STATUS_FALSE;
 
-            // Wait for media
-            PTRACE(2, "mod_opal\tAwaiting media start on connection " << *this);
-            m_rxAudioOpened.Wait();
-            m_txAudioOpened.Wait();
-            
-            if (GetPhase() >= ReleasingPhase) {
-                // Call got aborted
-                switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");
-                return SWITCH_STATUS_FALSE;
-            }
+        if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
+            switch_channel_mark_pre_answered(m_fsChannel);
+        }
+        break;
 
-            PTRACE(4, "mod_opal\tMedia started on connection " << *this);
+    case SWITCH_MESSAGE_INDICATE_ANSWER:
+        AcceptIncoming();
 
-            if (msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) {
-                if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
-                    switch_channel_mark_pre_answered(m_fsChannel);
-                }
-            } else {
-                if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
-                    switch_channel_mark_answered(m_fsChannel);
-                }
-            }
+        if (!WaitForMedia())
+            return SWITCH_STATUS_FALSE;
 
+        if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) {
+            switch_channel_mark_answered(m_fsChannel);
         }
         break;
 
+    case SWITCH_MESSAGE_INDICATE_DEFLECT:
+        ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection());
+        break;
+
     default:
-        PTRACE(3, "mod_opal\tReceived message " << msg->message_id << " on connection " << *this);
+        PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this);
     }
 
     return SWITCH_STATUS_SUCCESS;
 }
 
 
+bool FSConnection::WaitForMedia()
+{
+    PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this);
+    m_rxAudioOpened.Wait();
+    m_txAudioOpened.Wait();
+
+    if (IsReleased()) {
+        // Call got aborted
+        switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");
+        return false;
+    }
+
+    PTRACE(3, "mod_opal\tMedia started on connection " << *this);
+    return true;
+}
+
+
 switch_status_t FSConnection::receive_event(switch_event_t *event)
 {
-    PTRACE(3, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);
+    PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
 
 switch_status_t FSConnection::state_change()
 {
-    PTRACE(3, "mod_opal\tState changed on connection " << *this);
+    PTRACE(4, "mod_opal\tState changed on connection " << *this);
     return SWITCH_STATUS_SUCCESS;
 }
 
@@ -1119,14 +966,14 @@ switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io
 
 switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags)
 {
-    PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, false));
+    PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));
     return stream != NULL ? stream->read_frame(frame, flags) : SWITCH_STATUS_FALSE;
 }
 
 
 switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags)
 {
-    PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, true));
+    PSafePtr <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, true));
     return stream != NULL ? stream->write_frame(frame, flags) : SWITCH_STATUS_FALSE;
 }
 
@@ -1135,24 +982,24 @@ switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const
 
 FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)
     : OpalMediaStream(conn, mediaFormat, sessionID, isSource)
-    , m_fsSession(conn.GetSession())
-    , m_readRTP(0, 512)
-    , m_callOnStart(true)
+    , m_connection(conn)
+    , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE)
 {
     memset(&m_readFrame, 0, sizeof(m_readFrame));
-    m_readFrame.codec = m_switchCodec;
-    m_readFrame.flags = SFF_RAW_RTP;
 }
 
 
 PBoolean FSMediaStream::Open()
 {
-    opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession);
-
     if (IsOpen()) {
         return true;
     }
 
+    switch_core_session_t *fsSession = m_connection.GetSession();
+    switch_channel_t *fsChannel = m_connection.GetChannel();
+    if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL)
+        return false;
+
     bool isAudio;
     if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) {
         isAudio = true;
@@ -1162,86 +1009,76 @@ PBoolean FSMediaStream::Open()
         return OpalMediaStream::Open();
     }
 
-    m_fsChannel = switch_core_session_get_channel(m_fsSession);
-    
     int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits();
 
-
     if (IsSink()) {
-        m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec;
-        m_switchTimer = isAudio ? &tech_pvt->read_timer : &tech_pvt->vid_read_timer;
+        m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec;
+        m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer;
+        m_readFrame.codec = m_switchCodec;
+        m_readFrame.rate = mediaFormat.GetClockRate();
     } else {
-        m_switchCodec = isAudio ? &tech_pvt->write_codec : &tech_pvt->vid_write_codec;
+        m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec;
     }
 
     // The following is performed on two different instances of this object.
     if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP
                                mediaFormat.GetClockRate(), ptime, 1,  // Channels
                                SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,   // Settings
-                               switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
+                               switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
         // Could not select a codecs using negotiated frames/packet, so try using default.
         if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP
                                    mediaFormat.GetClockRate(), 0, 1,  // Channels
                                    SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL,   // Settings
-                                   switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
-            PTRACE(1, "mod_opal  " << switch_channel_get_name(m_fsChannel)<< " Cannot initialise " << (IsSink()? "read" : "write") << ' '
+                                   switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
+            PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
+                   << " cannot initialise " << (IsSink()? "read" : "write") << ' '
                    << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
-            switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
+            switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
             return false;
         }
-        PTRACE(2, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '
+        PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel)
+               << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '
                << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
     }
 
-    PTRACE(1, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " initialise " << 
-           switch_channel_get_name(m_fsChannel) << (IsSink()? "read" : "write") << ' '
-           << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
-
     if (IsSink()) {
-        m_readFrame.rate = mediaFormat.GetClockRate();
-
         if (isAudio) {
-            switch_core_session_set_read_codec(m_fsSession, m_switchCodec);
+            switch_core_session_set_read_codec(fsSession, m_switchCodec);
             if (switch_core_timer_init(m_switchTimer,
                                        "soft",
                                        m_switchCodec->implementation->microseconds_per_packet / 1000,
                                        m_switchCodec->implementation->samples_per_packet,
-                                       switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
+                                       switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
+                PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
+                       << " timer init failed on " << (IsSink()? "read" : "write") << ' '
+                       << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
                 switch_core_codec_destroy(m_switchCodec);
                 m_switchCodec = NULL;
                 return false;
             }
         } else {
-            switch_core_session_set_video_read_codec(m_fsSession, m_switchCodec);
-            switch_channel_set_flag(m_fsChannel, CF_VIDEO);
+            switch_core_session_set_video_read_codec(fsSession, m_switchCodec);
+            switch_channel_set_flag(fsChannel, CF_VIDEO);
         }
     } else {
         if (isAudio) {
-            switch_core_session_set_write_codec(m_fsSession, m_switchCodec);
+            switch_core_session_set_write_codec(fsSession, m_switchCodec);
         } else {
-            switch_core_session_set_video_write_codec(m_fsSession, m_switchCodec);
-            switch_channel_set_flag(m_fsChannel, CF_VIDEO);
+            switch_core_session_set_video_write_codec(fsSession, m_switchCodec);
+            switch_channel_set_flag(fsChannel, CF_VIDEO);
         }
     }
 
-    PTRACE(3, "mod_opal\tSet " << (IsSink()? "read" : "write") << ' '
-           << mediaFormat.GetMediaType() << " codec to << " << mediaFormat << " for connection " << *this);
+    PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel)
+           << " initialised " << (IsSink()? "read" : "write") << ' '
+           << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
 
     return OpalMediaStream::Open();
 }
 
 
-PBoolean FSMediaStream::Close()
+void FSMediaStream::InternalClose()
 {
-    if (!IsOpen())
-        return false;
-
-    /* forget these FS will properly destroy them for us */
-
-    m_switchTimer = NULL;
-    m_switchCodec = NULL;
-
-    return OpalMediaStream::Close();
 }
 
 
@@ -1256,149 +1093,88 @@ PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const
     return false;
 }
 
-bool FSMediaStream::CheckPatchAndLock()
-{
-    if (GetConnection().GetPhase() >= GetConnection().ReleasingPhase || !IsOpen())
-        return false;
 
-    if (LockReadWrite()) {
-        if (!GetPatch() || !IsOpen()) {
-            UnlockReadWrite();
-            return false;
-        }
-        return true;
-    } else {
-        return false;
-    }
-}
-
-switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)
+int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const
 {
+    if (!IsOpen()) {
+        PTRACE(2, "mod_opal\tNot open!");
+        return -1;
+    }
 
     if (!m_switchCodec) {
-        return SWITCH_STATUS_FALSE;
+        PTRACE(2, "mod_opal\tNo codec!");
+        return -1;
     }
 
-    if (m_callOnStart) {
-        /*
-          There is a race here... sometimes we make it here and GetPatch() is NULL
-          if we wait it shows up in 1ms, maybe there is a better way to wait.
-          
-        */
-        while(!GetPatch()) {
-            if (!m_fsChannel || !switch_channel_up(m_fsChannel)) {
-                return SWITCH_STATUS_FALSE;
-            }
-            switch_cond_next();
-        }
-        if (CheckPatchAndLock()) {
-            GetPatch()->OnStartMediaPatch();
-            m_callOnStart = false;
-            UnlockReadWrite();
-        } else {
-            return SWITCH_STATUS_FALSE; 
-        }
+    if (!m_connection.IsChannelReady()) {
+        PTRACE(2, "mod_opal\tChannel not ready!");
+        return -1;
     }
 
-    m_readFrame.flags = 0;
+    // We make referenced copy of pointer so can't be deleted out from under us
+    mediaPatch = m_mediaPatch;
+    if (mediaPatch == NULL) {
+        /*There is a race here... sometimes we make it here and m_mediaPatch is NULL
+          if we wait it shows up in 1ms, maybe there is a better way to wait. */
+        PTRACE(3, "mod_opal\tPatch not ready!");
+        return 1;
+    }
+
+    return 0;
+}
 
-    /*
-    while (switch_channel_ready(m_fsChannel)) {
-        if (CheckPatchAndLock()) {
-            if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) {
-                UnlockReadWrite();
-                return SWITCH_STATUS_FALSE;
-            }            
-            UnlockReadWrite();
-        } else {
-            return SWITCH_STATUS_FALSE; 
-        }
-        
-        if ((m_readFrame.datalen = m_readRTP.GetPayloadSize()) || switch_core_timer_check(&m_switchTimer, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
-            if (m_readFrame.datalen) {
-            } else {
-                m_readFrame.flags = SFF_CNG;
-            }
-            break;
-        }
 
-        switch_yield(1000);
+switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)
+{
+    PatchPtr mediaPatch;
+    switch (StartReadWrite(mediaPatch)) {
+      case -1 :
+        return SWITCH_STATUS_FALSE;
+      case 1 :
+        return SWITCH_STATUS_SUCCESS;
     }
-    */
-
-    if (switch_channel_test_private_flag(m_fsChannel, CF_NEED_FLUSH)) {
-        switch_channel_clear_private_flag(m_fsChannel, CF_NEED_FLUSH);
-        for(;;) {
-            if (CheckPatchAndLock()) {
-                if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) {
-                    UnlockReadWrite();
-                    return SWITCH_STATUS_FALSE;
-                }            
-                UnlockReadWrite();
-            } else {
-                return SWITCH_STATUS_FALSE; 
-            }
 
-            if (!m_readRTP.GetPayloadSize()) {
-                m_readFrame.flags = SFF_CNG;
-                break;
-            }
-        }
+    if (m_connection.NeedFlushAudio()) {
+        mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer
+        m_readRTP.SetPayloadSize(0);
     } else {
+        m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);
 
-        if (CheckPatchAndLock()) {
-            if (!m_switchTimer || !GetPatch()->GetSource().ReadPacket(m_readRTP)) {
-                UnlockReadWrite();
-                return SWITCH_STATUS_FALSE;
-            }            
-            UnlockReadWrite();
-        } else {
-            return SWITCH_STATUS_FALSE; 
-        }
-    
-        switch_core_timer_next(m_switchTimer);
-    
-        if (!(m_readFrame.datalen = m_readRTP.GetPayloadSize())) {
-            m_readFrame.flags = SFF_CNG;
+        if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) {
+            return SWITCH_STATUS_FALSE;
         }
     }
 
-    if (!switch_channel_ready(m_fsChannel)) {
+    if (!m_switchTimer) {
+        PTRACE(2, "mod_opal\tread_frame: no timer!");
         return SWITCH_STATUS_FALSE;
     }
+    switch_core_timer_next(m_switchTimer);
 
     if (!switch_core_codec_ready(m_switchCodec)) {
+        PTRACE(2, "mod_opal\tread_frame: codec not ready!");
         return SWITCH_STATUS_FALSE;
     }
 
-    //switch_core_timer_step(&m_switchTimer);
+#if IMPLEMENT_MULTI_FAME_AUDIO
+    // Repackage frames in incoming packet to agree with what FS expects.
+    // Not implmented yet!!!!!!!!!
+    // Cheating and only supporting one frame per packet
+#endif
 
-    if (m_readFrame.payload == RTP_DataFrame::CN || m_readFrame.payload == RTP_DataFrame::Cisco_CN) {
-        m_readFrame.flags = SFF_CNG;
-    }
-
-    if (m_readFrame.flags & SFF_CNG) {
-        m_readFrame.buflen = sizeof(m_buf);
-        m_readFrame.data = m_buf;
-        m_readFrame.packet = NULL;
-        m_readFrame.packetlen = 0;
-        m_readFrame.timestamp = 0;
-        m_readFrame.m = SWITCH_FALSE;
-        m_readFrame.seq = 0;
-        m_readFrame.ssrc = 0;
-        m_readFrame.codec = m_switchCodec;
-    } else {
-        m_readFrame.buflen = m_readRTP.GetSize();
-        m_readFrame.data = m_readRTP.GetPayloadPtr();
-        m_readFrame.packet = m_readRTP.GetPointer();
-        m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;
-        m_readFrame.payload = (switch_payload_t) m_readRTP.GetPayloadType();
-        m_readFrame.timestamp = m_readRTP.GetTimestamp();
-        m_readFrame.m = (switch_bool_t) m_readRTP.GetMarker();
-        m_readFrame.seq = m_readRTP.GetSequenceNumber();
-        m_readFrame.ssrc = m_readRTP.GetSyncSource();
-        m_readFrame.codec = m_switchCodec;
-    }
+    m_readFrame.buflen    = m_readRTP.GetSize();
+    m_readFrame.data      = m_readRTP.GetPayloadPtr();
+    m_readFrame.datalen   = m_readRTP.GetPayloadSize();
+    m_readFrame.packet    = m_readRTP.GetPointer();
+    m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;
+    m_readFrame.timestamp = m_readRTP.GetTimestamp();
+    m_readFrame.seq       = m_readRTP.GetSequenceNumber();
+    m_readFrame.ssrc      = m_readRTP.GetSyncSource();
+    m_readFrame.m         = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE;
+    m_readFrame.payload   = (switch_payload_t)m_readRTP.GetPayloadType();
+    m_readFrame.flags     = m_readFrame.datalen == 0 ||
+                            m_readFrame.payload == RTP_DataFrame::CN ||
+                            m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0;
 
     *frame = &m_readFrame;
 
@@ -1408,69 +1184,35 @@ switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag
 
 switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags)
 {
-    if (!switch_channel_ready(m_fsChannel)) {
+    PatchPtr mediaPatch;
+    switch (StartReadWrite(mediaPatch)) {
+      case -1 :
         return SWITCH_STATUS_FALSE;
-    }
-
-    if (m_callOnStart) {
-        if (CheckPatchAndLock()) {
-            GetPatch()->OnStartMediaPatch();
-            m_callOnStart = false;
-            UnlockReadWrite();
-        } else {
-            return SWITCH_STATUS_FALSE; 
-        }
-    }
-
-    if ((frame->flags & SFF_CNG)) {
+      case 1 :
         return SWITCH_STATUS_SUCCESS;
     }
 
     if ((frame->flags & SFF_RAW_RTP) != 0) {
-        RTP_DataFrame rtp((const BYTE *) frame->packet, frame->packetlen, false);
-
-        if (CheckPatchAndLock()) {
-            if (GetPatch()->PushFrame(rtp)) {
-                UnlockReadWrite();
-                return SWITCH_STATUS_SUCCESS;
-            }
-            UnlockReadWrite();
-        } else {
-            return SWITCH_STATUS_FALSE; 
-        }
-    } 
-    
-    /* If we reach this code it means a call to an ivr or something else that does not generate timestamps
-       Its possible that frame->timestamp is set but not guarenteed and is best ignored for the time being.
-       We are probably relying on the rtp stack to generate the timestamp and ssrc for us at this point.
-       As a quick hack I am going to keep a sample counter and increment it by frame->samples but it would be 
-       better if we could engage whatever it is in opal that makes it generate the timestamp.
-     */
+        RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false);
+        return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
+    }
 
     RTP_DataFrame rtp(frame->datalen);
-    rtp.SetPayloadType(mediaFormat.GetPayloadType());
-
-    m_timeStamp += frame->samples;
-    rtp.SetTimestamp(m_timeStamp);
-    
-    //rtp.SetTimestamp(frame->timestamp);
-    //rtp.SetSyncSource(frame->ssrc);
-    //rtp.SetMarker(frame->m);
-
     memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);
 
-    if (CheckPatchAndLock()) {
-        if (GetPatch()->PushFrame(rtp)) {
-            UnlockReadWrite();
-            return SWITCH_STATUS_SUCCESS;
-        }
-        UnlockReadWrite();
-    } else {
-        return SWITCH_STATUS_FALSE; 
-    }
-
+    rtp.SetPayloadType(mediaFormat.GetPayloadType());
 
-    return SWITCH_STATUS_FALSE;
+    /* Not sure what FS is going to give us!
+       Suspect it depends on the mod on the other side sending it. */
+    if (frame->timestamp != 0)
+      timestamp = frame->timestamp;
+    else if (frame->samples != 0)
+      timestamp += frame->samples;
+    else
+      timestamp += m_switchCodec->implementation->samples_per_packet;
+    rtp.SetTimestamp(timestamp);
+
+    return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
 }
 
 
index 1915d54b90da7b821a4865085d4deef6f285f998..07ba28a1f3f318637d0b0f7af6ace126dfcfaf5a 100644 (file)
@@ -4,6 +4,7 @@
  * Version: MPL 1.1
  *
  * Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)
+ * Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
@@ -46,6 +47,8 @@
 #define HAVE_APR
 #include <switch.h>
 #include <switch_version.h>
+
+
 #define MODNAME "mod_opal"
 
 
@@ -53,206 +56,239 @@ class FSEndPoint;
 class FSManager;
 
 
-struct mod_opal_globals {
-       int trace_level;
-       char *codec_string;
-       char *context;
-       char *dialplan;
-};
+class FSProcess : public PLibraryProcess
+{
+    PCLASSINFO(FSProcess, PLibraryProcess);
+  public:
+    FSProcess();
+    ~FSProcess();
 
-extern struct mod_opal_globals mod_opal_globals;
+    bool Initialise(switch_loadable_module_interface_t *iface);
 
+    FSManager & GetManager() const
+    {
+        return *m_manager;
+    }
 
-class FSProcess:public PLibraryProcess {
-       PCLASSINFO(FSProcess, PLibraryProcess);
+  protected:
+    FSManager * m_manager;
+};
 
-  public:
-       FSProcess();
-       ~FSProcess();
 
-       bool Initialise(switch_loadable_module_interface_t *iface);
+struct FSListener
+{
+    FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { }
 
-            FSManager & GetManager() const {
-               return *m_manager;
-  } protected:
-             FSManager * m_manager;
+    PString            m_name;
+    PIPSocket::Address m_address;
+    uint16_t           m_port;
 };
 
 
-struct FSListener {
-       FSListener() {
-       } PString name;
-       OpalTransportAddress listenAddress;
-       PString localUserName;
-       PString gatekeeper;
-};
+class FSManager : public OpalManager
+{
+    PCLASSINFO(FSManager, OpalManager);
 
-
-class FSCall:public OpalCall {
-       PCLASSINFO(FSCall, OpalCall);
   public:
-       FSCall(OpalManager & manager);
-       virtual PBoolean OnSetUp(OpalConnection & connection);
-};
+    FSManager();
 
+    bool Initialise(switch_loadable_module_interface_t *iface);
 
-class FSManager:public OpalManager {
-       PCLASSINFO(FSManager, OpalManager);
+    switch_status_t ReadConfig(int reload);
 
-  public:
-       FSManager();
+    switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; }
+    const PString & GetContext() const { return m_context; }
+    const PString & GetDialPlan() const { return m_dialplan; }
+    const PString & GetCodecPrefs() const { return m_codecPrefs; }
 
-       bool Initialise(switch_loadable_module_interface_t *iface);
+  private:
+    switch_endpoint_interface_t *m_FreeSwitch;
 
-       switch_status_t ReadConfig(int reload);
+    H323EndPoint *m_h323ep;
+    IAX2EndPoint *m_iaxep;
+    FSEndPoint   *m_fsep;
 
-       switch_endpoint_interface_t *GetSwitchInterface() const {
-               return m_FreeSwitch;
-       } virtual OpalCall *CreateCall(void *userData);
+    PString m_context;
+    PString m_dialplan;
+    PString m_codecPrefs;
+    PString m_gkAddress;
+    PString m_gkIdentifer;
+    PString m_gkInterface;
 
-  private:
-       switch_endpoint_interface_t *m_FreeSwitch;
+    list <FSListener> m_listeners;
+};
 
-       H323EndPoint *m_h323ep;
-       IAX2EndPoint *m_iaxep;
-       FSEndPoint *m_fsep;
 
-       PString m_gkAddress;
-       PString m_gkIdentifer;
-       PString m_gkInterface;
+class FSEndPoint : public OpalLocalEndPoint
+{
+    PCLASSINFO(FSEndPoint, OpalLocalEndPoint);
+  public:
+    FSEndPoint(FSManager & manager);
+
+    virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions);
 
-               list < FSListener > m_listeners;
+    FSManager & GetManager() const { return m_manager; }
+
+  protected:
+    FSManager & m_manager;
 };
 
 
 class FSConnection;
-typedef struct {
-       switch_timer_t read_timer;
-       switch_codec_t read_codec;
-       switch_codec_t write_codec;
-
-       switch_timer_t vid_read_timer;
-       switch_codec_t vid_read_codec;
-       switch_codec_t vid_write_codec;
-       FSConnection *me;
-} opal_private_t;
 
 
-class FSEndPoint:public OpalLocalEndPoint {
-       PCLASSINFO(FSEndPoint, OpalLocalEndPoint);
+class FSMediaStream : public OpalMediaStream
+{
+    PCLASSINFO(FSMediaStream, OpalMediaStream);
   public:
-       FSEndPoint(FSManager & manager);
+    FSMediaStream(
+      FSConnection & conn,
+      const OpalMediaFormat & mediaFormat,    ///<  Media format for stream
+      unsigned sessionID,    ///<  Session number for stream
+      bool isSource    ///<  Is a source stream
+    );
+
+    virtual PBoolean Open();
+    virtual PBoolean IsSynchronous() const;
+    virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;
+
+    switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);
+    switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);
+
+  protected:
+    virtual void InternalClose();
+    int StartReadWrite(PatchPtr & mediaPatch) const;
 
-       virtual bool OnIncomingCall(OpalLocalConnection &);
-       virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions);
+  private:
+    bool CheckPatchAndLock();
+
+    FSConnection          &m_connection;
+    switch_timer_t        *m_switchTimer;
+    switch_codec_t        *m_switchCodec;
+    switch_frame_t         m_readFrame;
+    RTP_DataFrame          m_readRTP;
 };
 
 
 #define DECLARE_CALLBACK0(name)                           \
     static switch_status_t name(switch_core_session_t *session) {       \
-        opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
-        return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name() : SWITCH_STATUS_FALSE; } \
-switch_status_t name()
+        FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
+        return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \
+    switch_status_t name()
 
 #define DECLARE_CALLBACK1(name, type1, name1)                           \
     static switch_status_t name(switch_core_session_t *session, type1 name1) { \
-        opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
-        return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1) : SWITCH_STATUS_FALSE; } \
-switch_status_t name(type1 name1)
+        FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
+        return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \
+    switch_status_t name(type1 name1)
 
 #define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \
     static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \
-        opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
-        return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \
-switch_status_t name(type1 name1, type2 name2, type3 name3)
+        FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
+        return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \
+    switch_status_t name(type1 name1, type2 name2, type3 name3)
 
 
 
 
-class FSConnection:public OpalLocalConnection {
-       PCLASSINFO(FSConnection, OpalLocalConnection)
+class FSConnection : public OpalLocalConnection
+{
+    PCLASSINFO(FSConnection, OpalLocalConnection)
 
   public:
-       FSConnection(OpalCall & call,
-                                FSEndPoint & endpoint,
-                                void *userData,
-                                unsigned options,
-                                OpalConnection::StringOptions * stringOptions,
-                                switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel);
-
-       virtual bool OnIncoming();
-       virtual void OnReleased();
-       virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia);
-       virtual void OnAlerting();
-       virtual void OnEstablished();
-       virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean);
-       virtual PBoolean OnOpenMediaStream(OpalMediaStream & stream);
-       virtual OpalMediaFormatList GetMediaFormats() const;
-       virtual PBoolean SendUserInputTone(char tone, unsigned duration);
-       virtual PBoolean SendUserInputString(const PString & value);
-
-       void SetCodecs();
-
-            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);
-
-       switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags);
-       switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags);
-
-       switch_core_session_t *GetSession() const {
-               return m_fsSession;
-  } private:
-             FSEndPoint & m_endpoint;
-       switch_core_session_t *m_fsSession;
-       switch_channel_t *m_fsChannel;
-       PSyncPoint m_rxAudioOpened;
-       PSyncPoint m_txAudioOpened;
-       OpalMediaFormatList m_switchMediaFormats;
-};
+    struct outgoing_params {
+      switch_event_t          *var_event;
+      switch_caller_profile_t *outbound_profile;
+      switch_core_session_t  **new_session;
+      switch_memory_pool_t   **pool;
+      switch_originate_flag_t  flags;
+      switch_call_cause_t     *cancel_cause;
+      switch_call_cause_t      fail_cause;
+    };
+
+    FSConnection(OpalCall & call,
+                 FSEndPoint & endpoint,
+                 unsigned options,
+                 OpalConnection::StringOptions * stringOptions,
+                 outgoing_params * params);
+
+    virtual bool OnOutgoingSetUp();
+    virtual bool OnIncoming();
+    virtual void OnReleased();
+    virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia);
+    virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean);
+    virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch);
+    virtual OpalMediaFormatList GetMediaFormats() const;
+    virtual PBoolean SendUserInputTone(char tone, unsigned duration);
+
+    void SetCodecs();
+    bool WaitForMedia();
+
+    DECLARE_CALLBACK0(on_init);
+    DECLARE_CALLBACK0(on_destroy);
+    DECLARE_CALLBACK0(on_routing);
+    DECLARE_CALLBACK0(on_execute);
+    DECLARE_CALLBACK0(on_hangup);
+
+    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);
+
+    switch_status_t read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags);
+    switch_status_t write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags);
+
+    __inline switch_core_session_t *GetSession() const
+    {
+        return m_fsSession;
+    }
+
+    __inline switch_channel_t *GetChannel() const
+    {
+        return m_fsChannel;
+    }
+
+    bool IsChannelReady() const
+    {
+        return m_fsChannel != NULL && switch_channel_ready(m_fsChannel);
+    }
+
+    bool NeedFlushAudio()
+    {
+        if (!m_flushAudio)
+          return false;
+        m_flushAudio = false;
+        return true;
+    }
 
+  private:
+    FSEndPoint            &m_endpoint;
+    switch_core_session_t *m_fsSession;
+    switch_channel_t      *m_fsChannel;
+    PSyncPoint             m_rxAudioOpened;
+    PSyncPoint             m_txAudioOpened;
+    OpalMediaFormatList    m_switchMediaFormats;
 
-class FSMediaStream:public OpalMediaStream {
-       PCLASSINFO(FSMediaStream, OpalMediaStream);
-  public:
-       FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, ///<  Media format for stream
-                                 unsigned sessionID,   ///<  Session number for stream
-                                 bool isSource ///<  Is a source stream
-               );
+    // If FS ever supports more than one audio and one video, this needs to change
+    switch_timer_t m_read_timer;
+    switch_codec_t m_read_codec;
+    switch_codec_t m_write_codec;
 
-       virtual PBoolean Open();
-       virtual PBoolean Close();
-       virtual PBoolean IsSynchronous() const;
-       virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;
+    switch_timer_t m_vid_read_timer;
+    switch_codec_t m_vid_read_codec;
+    switch_codec_t m_vid_write_codec;
 
-       switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);
-       switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);
+    bool m_flushAudio;
 
-  private:
-       switch_core_session_t *m_fsSession;
-       switch_channel_t *m_fsChannel;
-       switch_timer_t *m_switchTimer;
-       switch_codec_t *m_switchCodec;
-       switch_frame_t m_readFrame;
-       unsigned char m_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
-       RTP_DataFrame m_readRTP;
-       bool m_callOnStart;
-       uint32_t m_timeStamp;
-
-       bool CheckPatchAndLock();
+    friend PBoolean FSMediaStream::Open();
 };