提交 bfe4c177 authored 作者: Robert Jongbloed's avatar Robert Jongbloed 提交者: Ken Rice

Updated mod_opal to latest stable OPAL version.

Enhancements to trace logging, include threads and context ID.

Changed default opal_conf.xml to allow more than just G.711 uLaw and not to clutter log file with debug logs.

Added to opal_conf.xml item for "disable-transcoding".

Updated build/buildopal.sh to use correct ./configure items for PTLib, allow for something other than standard install directory for PTLib/OPAL and be able to easily bind to a specific release of PTLib/OPAL.
上级 a7fadffc
#!/bin/sh #!/bin/sh
if [ -z "$1" ]; then
INSTALLDIR=/usr/local
else
INSTALLDIR="$1"
fi
if [ -z `which svn` ]; then
echo "Need SVN installed!"
exit 1
fi
uname -a | grep -qi bsd && MAKE=gmake || MAKE=make uname -a | grep -qi bsd && MAKE=gmake || MAKE=make
#Locate our script, then go up one directory to be in FreeSWITCH root
cd `dirname $0`
cd ..
FS_DIR=`pwd` FS_DIR=`pwd`
cd /root export PKG_CONFIG_PATH=$INSTALLDIR/lib/pkgconfig
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/ptlib/trunk ptlib
cd ptlib
./configure --prefix=/usr
${MAKE}
${MAKE} install
cd ..
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/opal/branches/v3_6 opal # Version and patch for PTLib and OPAL. These are almost always in lock
cd opal # step so shoud be the same unless you really know ehat you are doing!
export PKG_CONFIG_PATH=/usr/lib/pkgconfig # The PATCH should be set to a specific"snapshot release" when things
./configure --prefix=/usr # are nice and stable.
VERSION=10
#PATCH=6
if [ -z "$PATCH" ]; then
PTLIB_VERSION=branches/v2_$VERSION
OPAL_VERSION=branches/v3_$VERSION
else
PTLIB_VERSION=tags/v2_${VERSION}_$PATCH
OPAL_VERSION=tags/v3_${VERSION}_$PATCH
fi
cd $FS_DIR/libs
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/ptlib/$PTLIB_VERSION ptlib
cd $FS_DIR/libs/ptlib
# LDAP disabled due to conflict wit libs in spidermonkey
./configure --disable-plugins --disable-openldap --prefix=$INSTALLDIR
${MAKE} ${MAKE}
${MAKE} install sudo ${MAKE} install
cd ${FS_DIR}
${MAKE} mod_opal-install cd $FS_DIR/libs
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/opal/$OPAL_VERSION opal
cd $FS_DIR/libs/opal
./configure --disable-plugins --prefix=$INSTALLDIR
$MAKE
sudo $MAKE install
echo "======================================"
echo "PTLib/OPAL build and install completed"
echo "======================================"
cd $FS_DIR
$MAKE mod_opal-install
<configuration name="opal.conf" description="Opal Endpoints"> <configuration name="opal.conf" description="Opal Endpoints">
<settings> <settings>
<param name="trace-level" value="4"/> <param name="trace-level" value="3"/>
<param name="context" value="default"/> <param name="context" value="default"/>
<param name="dialplan" value="XML"/> <param name="dialplan" value="XML"/>
<param name="codec-prefs" value="PCMU"/> <param name="jitter-size" value="40,100"/> <!-- Jitter buffer min/max size, milliseconds -->
<param name="gk-address" value=""/> <!-- empty to disable, "*" to search LAN --> <!-- <param name="codec-prefs" value="PCMU,PCMA"/> --> <!-- list, and preferecnce order, of codecs -->
<param name="gk-identifer" value=""/> <!-- optional name of gk --> <!-- <param name="disable-transcoding" value="true"/> --> <!-- do not transcode, use source channel codec only -->
<param name="gk-interface" value=""/> <!-- optional listener interface name --> <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="$${local_ip_v4}"/> <!-- optional listener interface name -->
</settings> </settings>
<listeners> <listeners>
<listener name="default"> <listener name="default">
......
...@@ -13,6 +13,9 @@ libtool ...@@ -13,6 +13,9 @@ libtool
ltmain.sh ltmain.sh
missing missing
ptlib
opal
*_manifest.rc *_manifest.rc
*.pc *.pc
......
BASE=../../../.. BASE=../../../..
LOCAL_INSERT_CFLAGS= pkg-config opal --cflags
LOCAL_CFLAGS+=-g -ggdb -I. PKG_DIR:=/usr/local/lib/pkgconfig
LOCAL_INSERT_LDFLAGS= pkg-config opal --libs ifeq ($(PKG_CONFIG_PATH),)
export PKG_CONFIG_PATH:=$(PKG_DIR)
else
ifeq ($(findstring $(PKG_DIR),$(PKG_CONFIG_PATH)),)
export PKG_CONFIG_PATH:=$(PKG_CONFIG_PATH):$(PKG_DIR)
endif
endif
#DEBUG_SUFFIX:=--define-variable=suffix=_d
LOCAL_INSERT_CFLAGS= pkg-config opal $(DEBUG_SUFFIX) --cflags
LOCAL_CFLAGS+=-g -ggdb
LOCAL_INSERT_LDFLAGS= pkg-config opal $(DEBUG_SUFFIX) --libs
include $(BASE)/build/modmake.rules include $(BASE)/build/modmake.rules
......
/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / /* Opal endpoint interface for Freeswitch Modular Media Switching Software Library /
* Soft-Switch Application * Soft-Switch Application
* *
* Version: MPL 1.1 * Version: MPL 1.1
* *
* Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) * 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 * The contents of this file are subject to the Mozilla Public License Version
* the License. You may obtain a copy of the License at * 1.1 (the "License"); you may not use this file except in compliance with
* http://www.mozilla.org/MPL/ * the License. You may obtain a copy of the License at
* * http://www.mozilla.org/MPL/
* Software distributed under the License is distributed on an "AS IS" basis, *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * Software distributed under the License is distributed on an "AS IS" basis,
* for the specific language governing rights and limitations under the * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* License. * for the specific language governing rights and limitations under the
* * License.
* Contributor(s): *
* Tuyan Ozipek (tuyanozipek@gmail.com) * Contributor(s):
* Lukasz Zwierko (lzwierko@gmail.com) * Tuyan Ozipek (tuyanozipek@gmail.com)
* Robert Jongbloed (robertj@voxlucida.com.au) * Lukasz Zwierko (lzwierko@gmail.com)
* * Robert Jongbloed (robertj@voxlucida.com.au)
*/ *
*/
#include "mod_opal.h"
#include <opal/patch.h> #include "mod_opal.h"
#include <rtp/rtp.h> #include <opal/patch.h>
#include <h323/h323pdu.h> #include <rtp/rtp.h>
#include <h323/gkclient.h> #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
#define CF_NEED_FLUSH (1 << 1) 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
struct mod_opal_globals mod_opal_globals = { 0 }; in FS.
Eventually we will get around to bundling the packets, but not yet. This
static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event, compile flag will just force one frame/packet for all audio codecs.
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); #define IMPLEMENT_MULTI_FAME_AUDIO 0
static FSProcess *opal_process = NULL; static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
static const char ModuleName[] = "opal"; switch_core_session_t **new_session,
switch_memory_pool_t **pool,
switch_originate_flag_t flags,
static switch_status_t on_hangup(switch_core_session_t *session); switch_call_cause_t *cancel_cause);
static switch_status_t on_destroy(switch_core_session_t *session);
static FSProcess *opal_process = NULL;
static switch_io_routines_t opalfs_io_routines = {
/*.outgoing_channel */ create_outgoing_channel,
/*.read_frame */ FSConnection::read_audio_frame, static PConstString const ModuleName("opal");
/*.write_frame */ FSConnection::write_audio_frame, static char const ConfigFile[] = "opal.conf";
/*.kill_channel */ FSConnection::kill_channel,
/*.send_dtmf */ FSConnection::send_dtmf,
/*.receive_message */ FSConnection::receive_message, static switch_io_routines_t opalfs_io_routines = {
/*.receive_event */ FSConnection::receive_event, /*.outgoing_channel */ create_outgoing_channel,
/*.state_change */ FSConnection::state_change, /*.read_frame */ FSConnection::read_audio_frame,
/*.read_video_frame */ FSConnection::read_video_frame, /*.write_frame */ FSConnection::write_audio_frame,
/*.write_video_frame */ FSConnection::write_video_frame /*.kill_channel */ FSConnection::kill_channel,
}; /*.send_dtmf */ FSConnection::send_dtmf,
/*.receive_message */ FSConnection::receive_message,
static switch_state_handler_table_t opalfs_event_handlers = { /*.receive_event */ FSConnection::receive_event,
/*.on_init */ FSConnection::on_init, /*.state_change */ FSConnection::state_change,
/*.on_routing */ FSConnection::on_routing, /*.read_video_frame */ FSConnection::read_video_frame,
/*.on_execute */ FSConnection::on_execute, /*.write_video_frame */ FSConnection::write_video_frame
/*.on_hangup */ on_hangup, };
/*.on_exchange_media */ FSConnection::on_exchange_media,
/*.on_soft_execute */ FSConnection::on_soft_execute, static switch_state_handler_table_t opalfs_event_handlers = {
/*.on_consume_media*/ NULL, /*.on_init */ FSConnection::on_init,
/*.on_hibernate*/ NULL, /*.on_routing */ FSConnection::on_routing,
/*.on_reset*/ NULL, /*.on_execute */ FSConnection::on_execute,
/*.on_park*/ NULL, /*.on_hangup */ FSConnection::on_hangup,
/*.on_reporting*/ NULL, /*.on_exchange_media */ FSConnection::on_exchange_media,
/*.on_destroy*/ on_destroy /*.on_soft_execute */ FSConnection::on_soft_execute,
}; /*.on_consume_media*/ NULL,
/*.on_hibernate*/ NULL,
/*.on_reset*/ NULL,
SWITCH_BEGIN_EXTERN_C /*.on_park*/ NULL,
/*******************************************************************************/ /*.on_reporting*/ NULL,
/*.on_destroy*/ FSConnection::on_destroy
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_BEGIN_EXTERN_C
SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) { /*******************************************************************************/
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n");
SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load);
/* Prevent the loading of OPAL codecs via "plug ins", this is a directory SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown);
full of DLLs that will be loaded automatically. */ SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL);
putenv((char *)"PTLIBPLUGINDIR=/no/thanks");
SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load)
{
*module_interface = switch_loadable_module_create_module_interface(pool, modname); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n");
if (!*module_interface) {
return SWITCH_STATUS_MEMERR; /* Prevent the loading of OPAL codecs via "plug ins", this is a directory
} full of DLLs that will be loaded automatically. */
putenv((char *)"PTLIBPLUGINDIR=/no/thanks");
opal_process = new FSProcess();
if (opal_process == NULL) {
return SWITCH_STATUS_MEMERR; *module_interface = switch_loadable_module_create_module_interface(pool, modname);
} if (!*module_interface) {
return SWITCH_STATUS_MEMERR;
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 opal_process = new FSProcess();
return SWITCH_STATUS_NOUNLOAD; if (opal_process == NULL) {
//return SWITCH_STATUS_SUCCESS; return SWITCH_STATUS_MEMERR;
} }
delete opal_process; if (opal_process->Initialise(*module_interface)) {
opal_process = NULL; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n");
return SWITCH_STATUS_FALSE; //unloading causes a seg in linux
} //return SWITCH_STATUS_UNLOAD;
return SWITCH_STATUS_SUCCESS;
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) {
delete opal_process;
switch_safe_free(mod_opal_globals.context); opal_process = NULL;
switch_safe_free(mod_opal_globals.dialplan); return SWITCH_STATUS_FALSE;
switch_safe_free(mod_opal_globals.codec_string); }
delete opal_process;
opal_process = NULL;
return SWITCH_STATUS_SUCCESS; SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown)
} {
delete opal_process;
SWITCH_END_EXTERN_C opal_process = NULL;
/*******************************************************************************/ return SWITCH_STATUS_SUCCESS;
}
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 PTRACING
if (opal_process == NULL) {
return SWITCH_CAUSE_CRASH; class FSTrace : public std::ostream
} {
private:
PString token; class Buffer : public std::stringbuf
{
FSManager & manager = opal_process->GetManager(); virtual int sync()
if (!manager.SetUpCall("local:", outbound_profile->destination_number, token, outbound_profile)) { {
return SWITCH_CAUSE_INVALID_NUMBER_FORMAT; std::string s = str();
} if (s.empty())
return 0;
PSafePtr < OpalCall > call = manager.FindCallWithLock(token);
//Due to explicit setting of flags we know exactly what we are getting
if (call == NULL) { #define THREAD_ID_INDEX 2
return SWITCH_CAUSE_PROTOCOL_ERROR; #define FILE_NAME_INDEX 3
} #define FILE_LINE_INDEX 4
#if PTLIB_CHECK_VERSION(2,11,1)
PSafePtr < FSConnection > connection = call->GetConnectionAs < FSConnection > (0); #define CONTEXT_ID_REGEX "([0-9]+|- - - - - - -)\t"
#define LOG_PRINTF_FORMAT "{%s,%s} %s"
if (connection == NULL) { #define FULL_TEXT_INDEX 6
return SWITCH_CAUSE_PROTOCOL_ERROR; #else
} #define CONTEXT_ID_REGEX
#define LOG_PRINTF_FORMAT "{%s} %s"
*new_session = connection->GetSession(); #define FULL_TEXT_INDEX 5
#endif
return SWITCH_CAUSE_SUCCESS; PStringArray fields(7);
} static PRegularExpression logRE("^([0-9]+)\t *(.+)\t *([^(]+)\\(([0-9]+)\\)\t"CONTEXT_ID_REGEX"(.*)",
PRegularExpression::Extended);
if (!logRE.Execute(s.c_str(), fields)) {
/////////////////////////////////////////////////////////////////////// fields[1] = "4";
fields[THREAD_ID_INDEX] = "unknown";
#if PTRACING fields[FILE_NAME_INDEX] = __FILE__;
fields[FILE_LINE_INDEX] = __LINE__;
class FSTrace : public ostream { fields[FULL_TEXT_INDEX] = s;
public: }
FSTrace()
: ostream(&buffer) switch_log_level_t level;
{ switch (fields[1].AsUnsigned()) {
} case 0 :
level = SWITCH_LOG_ALERT;
private: break;
class Buffer : public streambuf { case 1 :
char buffer[250]; level = SWITCH_LOG_ERROR;
break;
public: case 2 :
Buffer() level = SWITCH_LOG_WARNING;
{ break;
setg(buffer, buffer, &buffer[sizeof(buffer)-2]); case 3 :
setp(buffer, &buffer[sizeof(buffer)-2]); level = SWITCH_LOG_INFO;
} break;
default :
virtual int sync() level = SWITCH_LOG_DEBUG;
{ break;
return overflow(EOF); }
}
fields[4].Replace("\t", " ", true);
virtual int underflow() #if PTLIB_CHECK_VERSION(2,11,1)
{ fields[5].Replace("- - - - - - -", "-"),
return EOF; #endif
} switch_log_printf(SWITCH_CHANNEL_ID_LOG,
fields[FILE_NAME_INDEX],
virtual int overflow(int c) "PTLib-OPAL",
{ fields[FILE_LINE_INDEX].AsUnsigned(),
const char *fmt = "%s"; NULL,
char *func = NULL; level,
LOG_PRINTF_FORMAT,
int bufSize = pptr() - pbase(); fields[THREAD_ID_INDEX].GetPointer(),
#if PTLIB_CHECK_VERSION(2,11,1)
if (c != EOF) { fields[5].GetPointer(),
*pptr() = (char)c; #endif
bufSize++; fields[FULL_TEXT_INDEX].GetPointer());
}
// Reset string
if (bufSize != 0) { str(std::string());
char *bufPtr = pbase(); return 0;
char *bufEndPtr = NULL; }
setp(bufPtr, epptr()); } buffer;
bufPtr[bufSize] = '\0';
int line = 0; public:
char *p; FSTrace()
: ostream(&buffer)
char *file = NULL; {
switch_log_level_t level; }
};
switch (strtoul(bufPtr, &file, 10)) { #endif // PTRACING
case 1 :
level = SWITCH_LOG_INFO;
break; ///////////////////////////////////////////////////////////////////////
default :
level = SWITCH_LOG_DEBUG; FSProcess::FSProcess()
break; : PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1)
} , m_manager(NULL)
{
if (file) { }
while (isspace(*file)) file++;
if (file && (bufPtr = strchr(file, '(')) && (bufEndPtr = strchr(bufPtr, ')'))) { FSProcess::~FSProcess()
char *e; {
delete m_manager;
for(p = bufPtr; p && *p; p++) { #if PTRACING
if (*p == '\t') { PTrace::SetStream(NULL); // This will delete the FSTrace object
*p = ' '; #endif
} }
}
*bufPtr++ = '\0'; bool FSProcess::Initialise(switch_loadable_module_interface_t *iface)
line = atoi(bufPtr); {
while (bufEndPtr && isspace(*(++bufEndPtr))); m_manager = new FSManager();
bufPtr = bufEndPtr; return m_manager != NULL && m_manager->Initialise(iface);
if (bufPtr && ((e = strchr(bufPtr, ' ')) || (e = strchr(bufPtr, '\t')))) { }
func = bufPtr;
bufPtr = e;
*bufPtr++ = '\0'; ///////////////////////////////////////////////////////////////////////
}
} FSManager::FSManager()
} : m_context("default")
, m_dialplan("XML")
switch_text_channel_t tchannel = SWITCH_CHANNEL_ID_LOG; {
// These are deleted by the OpalManager class, no need to have destructor
if (!bufPtr) { m_h323ep = new H323EndPoint(*this);
bufPtr = pbase(); m_iaxep = new IAX2EndPoint(*this);
level = SWITCH_LOG_DEBUG; m_fsep = new FSEndPoint(*this);
} }
if (bufPtr) {
if (end_of(bufPtr) != '\n') { bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
fmt = "%s\n"; {
} ReadConfig(false);
if (!(file && func && line)) tchannel = SWITCH_CHANNEL_ID_LOG_CLEAN;
m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE);
switch_log_printf(tchannel, file, func, line, NULL, level, fmt, bufPtr); m_FreeSwitch->interface_name = ModuleName;
} m_FreeSwitch->io_routines = &opalfs_io_routines;
m_FreeSwitch->state_handler = &opalfs_event_handlers;
}
silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection;
return 0;
} if (m_listeners.empty()) {
} buffer; m_h323ep->StartListener("");
}; } else {
for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) {
#endif if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) {
PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name);
}
/////////////////////////////////////////////////////////////////////// }
}
FSProcess::FSProcess()
: PLibraryProcess("Vox Lucida Pty. Ltd.", "mod_opal", 1, 0, AlphaCode, 1) AddRouteEntry("h323:.* = local:<da>"); // config option for direct routing
, m_manager(NULL) AddRouteEntry("iax2:.* = local:<da>"); // config option for direct routing
{ AddRouteEntry("local:.* = h323:<da>"); // config option for direct routing
}
// Make sure all known codecs are instantiated,
// these are ones we know how to translate into H.323 capabilities
FSProcess::~FSProcess() GetOpalG728();
{ GetOpalG729();
delete m_manager; GetOpalG729A();
} GetOpalG729B();
GetOpalG729AB();
GetOpalG7231_6k3();
bool FSProcess::Initialise(switch_loadable_module_interface_t *iface) GetOpalG7231_5k3();
{ GetOpalG7231A_6k3();
m_manager = new FSManager(); GetOpalG7231A_5k3();
return m_manager != NULL && m_manager->Initialise(iface); GetOpalGSM0610();
} GetOpalGSMAMR();
GetOpaliLBC();
/////////////////////////////////////////////////////////////////////// #if !IMPLEMENT_MULTI_FAME_AUDIO
OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats();
FSManager::FSManager() for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) {
{ if (it->GetMediaType() == OpalMediaType::Audio()) {
// These are deleted by the OpalManager class, no need to have destructor it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), 1);
m_h323ep = new H323EndPoint(*this); it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), 1);
m_iaxep = new IAX2EndPoint(*this); OpalMediaFormat::SetRegisteredMediaFormat(*it);
m_fsep = new FSEndPoint(*this); }
} }
#endif // IMPLEMENT_MULTI_FAME_AUDIO
bool FSManager::Initialise(switch_loadable_module_interface_t *iface) if (!m_gkAddress.IsEmpty()) {
{ if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface))
ReadConfig(false); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n",
(const char *)m_h323ep->GetGatekeeper()->GetName());
#if PTRACING else
PTrace::SetLevel(mod_opal_globals.trace_level); //just for fun and eyecandy ;) switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR,
PTrace::SetOptions(PTrace::TraceLevel); "Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n",
PTrace::SetStream(new FSTrace); (const char *)m_gkAddress,
#endif (const char *)m_gkIdentifer,
(const char *)m_gkInterface);
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; return TRUE;
m_FreeSwitch->state_handler = &opalfs_event_handlers; }
silenceDetectParams.m_mode = OpalSilenceDetector::NoSilenceDetection;
switch_status_t FSManager::ReadConfig(int reload)
if (m_listeners.empty()) { {
m_h323ep->StartListener(""); switch_event_t *request_params = NULL;
} else { switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS);
for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) { switch_assert(request_params);
if (!m_h323ep->StartListener(it->listenAddress)) { switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil(""));
PTRACE(3, "mod_opal\tCannot start listener for " << it->name);
} switch_xml_t cfg;
} 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", ConfigFile);
AddRouteEntry("h323:.* = local:<da>"); // config option for direct routing return SWITCH_STATUS_FALSE;
AddRouteEntry("iax2:.* = local:<da>"); // config option for direct routing }
AddRouteEntry("local:.* = h323:<da>"); // config option for direct routing
switch_xml_t xmlSettings = switch_xml_child(cfg, "settings");
// Make sure all known codecs are instantiated, if (xmlSettings) {
// these are ones we know how to translate into H.323 capabilities for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
GetOpalG728(); PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
GetOpalG729(); PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
GetOpalG729A();
GetOpalG729B(); if (var == "context") {
GetOpalG729AB(); m_context = val;
GetOpalG7231_6k3(); } else if (var == "dialplan") {
GetOpalG7231_5k3(); m_dialplan = val;
GetOpalG7231A_6k3(); } else if (var == "codec-prefs") {
GetOpalG7231A_5k3(); m_codecPrefs = val;
GetOpalGSM0610(); } else if (var == "disable-transcoding") {
GetOpalGSMAMR(); m_disableTranscoding = switch_true(val);
GetOpaliLBC(); } else if (var == "jitter-size") {
SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds
/* For compatibility with the algorithm in FSConnection::SetCodecs() we need } else if (var == "gk-address") {
to set all audio media formats to be 1 frame per packet */ m_gkAddress = val;
OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats(); } else if (var == "gk-identifer") {
for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) { m_gkIdentifer = val;
if (it->GetMediaType() == OpalMediaType::Audio()) { } else if (var == "gk-interface") {
it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), 1); m_gkInterface = val;
it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), 1); #if PTRACING
} } else if (var == "trace-level") {
} unsigned level = val.AsUnsigned();
if (level > 0) {
if (!m_gkAddress.IsEmpty()) { PTrace::SetLevel(level);
if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface)) PTrace::ClearOptions(0xffffffff); // Everything off
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n", PTrace::SetOptions( // Except these
(const char *)m_h323ep->GetGatekeeper()->GetName()); PTrace::TraceLevel|PTrace::FileAndLine|PTrace::Thread
else #if PTLIB_CHECK_VERSION(2,11,1)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, |PTrace::ContextIdentifier
"Could not start gatekeeper: addr=\"%s\", id=\"%s\", if=\"%s\"\n", #endif
(const char *)m_gkAddress, );
(const char *)m_gkIdentifer, PTrace::SetStream(new FSTrace);
(const char *)m_gkInterface); }
} #endif
}
return TRUE; }
} }
switch_xml_t xmlListeners = switch_xml_child(cfg, "listeners");
switch_status_t FSManager::ReadConfig(int reload) if (xmlListeners != NULL) {
{ for (switch_xml_t xmlListener = switch_xml_child(xmlListeners, "listener"); xmlListener != NULL; xmlListener = xmlListener->next) {
const char *cf = "opal.conf";
switch_status_t status = SWITCH_STATUS_SUCCESS; m_listeners.push_back(FSListener());
FSListener & listener = m_listeners.back();
switch_memory_pool_t *pool = NULL;
if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) { listener.m_name = switch_xml_attr_soft(xmlListener, "name");
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n"); if (listener.m_name.IsEmpty())
return status; listener.m_name = "unnamed";
}
for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
set_global_context("default"); PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
set_global_dialplan("XML"); PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
if (var == "h323-ip")
switch_event_t *params = NULL; listener.m_address = val;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS); else if (var == "h323-port")
switch_assert(params); listener.m_port = (uint16_t)val.AsUnsigned();
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_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name);
if (xml == NULL) { }
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf); }
return SWITCH_STATUS_FALSE;
} switch_event_destroy(&request_params);
switch_xml_t xmlSettings = switch_xml_child(cfg, "settings"); if (xml)
if (xmlSettings) { switch_xml_free(xml);
for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
const char *var = switch_xml_attr_soft(xmlParam, "name"); return SWITCH_STATUS_SUCCESS;
const char *val = switch_xml_attr_soft(xmlParam, "value"); }
if (!strcasecmp(var, "trace-level")) {
int level = atoi(val); static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
if (level > 0) { switch_event_t *var_event,
mod_opal_globals.trace_level = level; switch_caller_profile_t *outbound_profile,
} switch_core_session_t **new_session,
} else if (!strcasecmp(var, "context")) { switch_memory_pool_t **pool,
set_global_context(val); switch_originate_flag_t flags,
} else if (!strcasecmp(var, "dialplan")) { switch_call_cause_t *cancel_cause)
set_global_dialplan(val); {
} else if (!strcasecmp(var, "codec-prefs")) { if (opal_process == NULL)
set_global_codec_string(val); return SWITCH_CAUSE_CRASH;
} else if (!strcasecmp(var, "jitter-size")) {
char * next; FSConnection::outgoing_params params;
unsigned minJitter = strtoul(val, &next, 10); params.var_event = var_event;
if (minJitter >= 10) { params.outbound_profile = outbound_profile;
unsigned maxJitter = minJitter; params.new_session = new_session;
if (*next == ',') params.pool = pool;
maxJitter = atoi(next+1); params.flags = flags;
SetAudioJitterDelay(minJitter, maxJitter); // In milliseconds params.cancel_cause = cancel_cause;
} params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
} else if (!strcasecmp(var, "gk-address")) {
m_gkAddress = val; if (opal_process->GetManager().SetUpCall("local:", outbound_profile->destination_number, &params) != NULL)
} else if (!strcasecmp(var, "gk-identifer")) { return SWITCH_CAUSE_SUCCESS;
m_gkIdentifer = val;
} else if (!strcasecmp(var, "gk-interface")) { if (*new_session != NULL)
m_gkInterface = val; switch_core_session_destroy(new_session);
} return params.fail_cause;
} }
}
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) { FSEndPoint::FSEndPoint(FSManager & manager)
: OpalLocalEndPoint(manager)
m_listeners.push_back(FSListener()); , m_manager(manager)
FSListener & listener = m_listeners.back(); {
PTRACE(4, "mod_opal\tFSEndPoint created.");
listener.name = switch_xml_attr_soft(xmlListener, "name"); }
if (listener.name.IsEmpty())
listener.name = "unnamed";
OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions)
PIPSocket::Address ip; {
WORD port = 1720; return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData);
}
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")) FSConnection::FSConnection(OpalCall & call,
ip = val; FSEndPoint & endpoint,
else if (!strcasecmp(var, "h323-port")) unsigned options,
port = (WORD) atoi(val); OpalConnection::StringOptions* stringOptions,
} outgoing_params * params)
: OpalLocalConnection(call, endpoint, NULL, options, stringOptions)
listener.listenAddress = OpalTransportAddress(ip, port); , m_endpoint(endpoint)
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.name); , m_fsSession(NULL)
} , m_fsChannel(NULL)
} , m_flushAudio(false)
{
switch_event_destroy(&params); memset(&m_read_timer, 0, sizeof(m_read_timer));
memset(&m_read_codec, 0, sizeof(m_read_codec));
if (xml) memset(&m_write_codec, 0, sizeof(m_write_codec));
switch_xml_free(xml); memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer));
memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec));
return status; memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec));
}
if (params != NULL) {
// If we fail, this is the cause
OpalCall * FSManager::CreateCall(void * /*userData*/) params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
{
return new FSCall(*this); 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;
/////////////////////////////////////////////////////////////////////// }
}
FSEndPoint::FSEndPoint(FSManager & manager) else {
: OpalLocalEndPoint(manager) if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),
{ SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) {
PTRACE(3, "mod_opal\t FSEndPoint Created!"); PTRACE(1, "mod_opal\tCannot create session for incoming call.");
} return;
}
}
bool FSEndPoint::OnIncomingCall(OpalLocalConnection & connection)
{ if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) {
return ((FSConnection &) connection).OnIncoming(); switch_core_session_destroy(&m_fsSession);
} return;
}
OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions) switch_core_session_set_private(m_fsSession, this);
{ SafeReference(); // Make sure cannot be deleted until on_destroy()
FSManager & mgr = (FSManager &) GetManager();
switch_core_session_t *fsSession = switch_core_session_request(mgr.GetSwitchInterface(), if (params != NULL) {
(switch_caller_profile_t *)userData ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL); switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile);
if (fsSession == NULL) switch_channel_set_caller_profile(m_fsChannel, caller_profile);
return NULL; SetLocalPartyName(caller_profile->caller_id_number);
SetDisplayName(caller_profile->caller_id_name);
switch_channel_t *fsChannel = switch_core_session_get_channel(fsSession);
*params->new_session = m_fsSession;
if (fsChannel == NULL) { }
switch_core_session_destroy(&fsSession);
return NULL; switch_channel_set_state(m_fsChannel, CS_INIT);
} }
return new FSConnection(call, *this, userData, options, stringOptions, (switch_caller_profile_t *)userData, fsSession, fsChannel);
} bool FSConnection::OnOutgoingSetUp()
{
if (m_fsSession == NULL || m_fsChannel == NULL) {
/////////////////////////////////////////////////////////////////////// PTRACE(1, "mod_opal\tSession request failed.");
return false;
FSCall::FSCall(OpalManager & manager) }
: OpalCall(manager)
{ // 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();
PBoolean FSCall::OnSetUp(OpalConnection & connection) if (proto == NULL) {
{ PTRACE(1, "mod_opal\tNo protocol connection in call.");
// Transfer FS caller_id_number & caller_id_name from the FSConnection return false;
// to the protocol connectionm (e.g. H.323) so gets sent correctly }
// in outgoing packets
PSafePtr<FSConnection> local = GetConnectionAs<FSConnection>(); proto->SetLocalPartyName(GetLocalPartyName());
if (local != NULL) { proto->SetDisplayName(GetDisplayName());
PSafePtr<OpalConnection> proto = local->GetOtherPartyConnection();
if (proto != NULL) { switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL());
proto->SetLocalPartyName(local->GetLocalPartyName()); return true;
proto->SetDisplayName(local->GetDisplayName()); }
}
}
bool FSConnection::OnIncoming()
return OpalCall::OnSetUp(connection); {
} if (m_fsSession == NULL || m_fsChannel == NULL) {
PTRACE(1, "mod_opal\tSession request failed.");
return false;
/////////////////////////////////////////////////////////////////////// }
switch_core_session_add_stream(m_fsSession, NULL);
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) PURL url = GetRemotePartyURL();
, m_endpoint(endpoint) switch_caller_profile_t *caller_profile = switch_caller_profile_new(
, m_fsSession(fsSession) switch_core_session_get_pool(m_fsSession),
, m_fsChannel(fsChannel) url.GetUserName(), /** username */
{ m_endpoint.GetManager().GetDialPlan(), /** dial plan */
opal_private_t *tech_pvt; GetRemotePartyName(), /** caller_id_name */
GetRemotePartyNumber(), /** caller_id_number */
tech_pvt = (opal_private_t *) switch_core_session_alloc(m_fsSession, sizeof(*tech_pvt)); url.GetHostName(), /** network addr */
tech_pvt->me = this; NULL, /** ANI */
switch_core_session_set_private(m_fsSession, tech_pvt); NULL, /** ANI II */
NULL, /** RDNIS */
if (outbound_profile != NULL) { ModuleName, /** source */
SetLocalPartyName(outbound_profile->caller_id_number); m_endpoint.GetManager().GetContext(), /** set context */
SetDisplayName(outbound_profile->caller_id_name); GetCalledPartyNumber() /** destination_number */
);
switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, outbound_profile); if (caller_profile == NULL) {
switch_channel_set_caller_profile(m_fsChannel, caller_profile); PTRACE(1, "mod_opal\tCould not create caller profile");
return false;
PString name = "opal/"; }
name += outbound_profile->destination_number;
switch_channel_set_name(m_fsChannel, name); PTRACE(4, "mod_opal\tCreated switch caller profile:\n"
" username = " << caller_profile->username << "\n"
switch_channel_set_state(m_fsChannel, CS_INIT); " 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"
bool FSConnection::OnIncoming() " context = " << caller_profile->context << "\n"
{ " destination_number= " << caller_profile->destination_number);
if (m_fsSession == NULL) { switch_channel_set_caller_profile(m_fsChannel, caller_profile);
PTRACE(1, "mod_opal\tSession request failed.");
return false; switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number);
}
if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) {
switch_core_session_add_stream(m_fsSession, NULL); PTRACE(1, "mod_opal\tCould not launch session thread");
switch_core_session_destroy(&m_fsSession);
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); m_fsChannel = NULL;
if (channel == NULL) { return false;
PTRACE(1, "mod_opal\tSession does not have a channel"); }
return false;
} return true;
}
PURL url = GetRemotePartyURL();
switch_caller_profile_t *caller_profile = switch_caller_profile_new(switch_core_session_get_pool(m_fsSession),
url.GetUserName(), void FSConnection::OnReleased()
/** username */ {
mod_opal_globals.dialplan, m_rxAudioOpened.Signal(); // Just in case
/** dial plan */ m_txAudioOpened.Signal();
GetRemotePartyName(),
/** caller_id_name */ if (m_fsChannel == NULL) {
GetRemotePartyNumber(), PTRACE(3, "mod_opal\tHanging up FS side");
/** caller_id_number */ switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931);
url.GetHostName(), }
/** network addr */
NULL, OpalLocalConnection::OnReleased();
/** ANI */ }
NULL,
/** ANI II */
NULL, PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia)
/** RDNIS */ {
ModuleName, if (PAssertNULL(m_fsChannel) == NULL)
/** source */ return false;
mod_opal_globals.context,
/** set context */ switch_channel_mark_ring_ready(m_fsChannel);
GetCalledPartyNumber() return OpalLocalConnection::SetAlerting(calleeName, withMedia);
/** destination_number */ }
);
if (caller_profile == NULL) {
PTRACE(1, "mod_opal\tCould not create caller profile"); PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration)
return false; {
} if (PAssertNULL(m_fsChannel) == NULL)
return false;
PTRACE(4, "mod_opal\tCreated switch caller profile:\n"
" username = " << caller_profile->username << "\n" switch_dtmf_t dtmf = { tone, duration };
" dialplan = " << caller_profile->dialplan << "\n" return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS;
" 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" OpalMediaFormatList FSConnection::GetMediaFormats() const
" context = " << caller_profile->context << "\n" " destination_number= " << caller_profile->destination_number); {
switch_channel_set_caller_profile(channel, caller_profile); if (m_switchMediaFormats.IsEmpty()) {
const_cast<FSConnection *>(this)->SetCodecs();
char name[256] = "opal/in:"; }
switch_copy_string(name + 8, caller_profile->destination_number, sizeof(name)-8);
switch_channel_set_name(channel, name); return m_switchMediaFormats;
switch_channel_set_state(channel, CS_INIT); }
if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) {
PTRACE(1, "mod_opal\tCould not launch session thread"); void FSConnection::SetCodecs()
return false; {
} int numCodecs = 0;
const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
return true;
} 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");
void FSConnection::OnReleased() if (codec_string.IsEmpty()) {
{ codec_string = m_endpoint.GetManager().GetCodecPrefs();
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession); if (codec_string.IsEmpty()) {
numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));
/* so FS on_hangup will not try to deref a landmine */ for (int i = 0; i < numCodecs; i++) {
tech_pvt->me = NULL; if (i > 0)
codec_string += ',';
m_rxAudioOpened.Signal(); // Just in case codec_string += codecs[i]->iananame;
m_txAudioOpened.Signal(); }
H225_ReleaseCompleteReason dummy; PTRACE(4, "mod_opal\tDefault to all loaded codecs=" << codec_string);
switch_channel_hangup(switch_core_session_get_channel(m_fsSession), }
(switch_call_cause_t)H323TranslateFromCallEndReason(GetCallEndReason(), dummy)); else {
OpalLocalConnection::OnReleased(); PTRACE(4, "mod_opal\tSettings codec-prefs=" << codec_string);
} }
}
else {
void FSConnection::OnAlerting() PTRACE(4, "mod_opal\tChannel codec_string=" << codec_string);
{ }
switch_channel_mark_ring_ready(m_fsChannel);
return OpalLocalConnection::OnAlerting(); PString orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE);
} if (!orig_codec.IsEmpty()) {
if (m_endpoint.GetManager().GetDisableTranscoding()) {
PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia) codec_string = orig_codec;
{ PTRACE(4, "mod_opal\tNo transcoding, forced to originator codec=" << orig_codec);
return OpalLocalConnection::SetAlerting(calleeName, withMedia); }
} else {
codec_string.Splice(orig_codec+',', 0);
PTRACE(4, "mod_opal\tSetting preference to originator codec=" << orig_codec);
void FSConnection::OnEstablished() }
{ }
OpalLocalConnection::OnEstablished(); }
} else {
PTRACE(4, "mod_opal\tChannel absolute_codec_string=" << codec_string);
}
PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration)
{ {
switch_dtmf_t dtmf = { tone, duration }; char *codec_order[SWITCH_MAX_CODECS];
return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS; 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);
}
PBoolean FSConnection::SendUserInputString(const PString & value) for (int i = 0; i < numCodecs; i++) {
{ const switch_codec_implementation_t *codec = codecs[i];
return OpalConnection::SendUserInputString(value);
} // See if we have a match by PayloadType/rate/name
OpalMediaFormat switchFormat((RTP_DataFrame::PayloadTypes)codec->ianacode,
codec->samples_per_second,
OpalMediaFormatList FSConnection::GetMediaFormats() const codec->iananame);
{ if (!switchFormat.IsValid()) {
if (m_switchMediaFormats.IsEmpty()) { // See if we have a match by name alone
const_cast<FSConnection *>(this)->SetCodecs(); switchFormat = codec->iananame;
} if (!switchFormat.IsValid()) {
PTRACE(2, "mod_opal\tCould not match FS codec "
return m_switchMediaFormats; << codec->iananame << '@' << codec->samples_per_second
} << " (pt=" << (unsigned)codec->ianacode << ")"
" to an OPAL media format.");
continue;
void FSConnection::SetCodecs() }
{ }
int numCodecs = 0;
const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS]; PTRACE(4, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);
const char *codec_string = NULL, *abs, *ocodec;
char *tmp_codec_string = NULL; #if IMPLEMENT_MULTI_FAME_AUDIO
char *codec_order[SWITCH_MAX_CODECS]; // Did we match or create a new media format?
int codec_order_last; if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_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
if ((abs = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"))) { int fpp = codec->samples_per_packet/switchFormat.GetFrameTime();
codec_string = abs;
} else { /* Set the frames/packet to maximum of what is in the FS table. The OPAL negotiations will
if ((abs = switch_channel_get_variable(m_fsChannel, "codec_string"))) { drop the value from there. This might fail if there are "holes" in the FS table, e.g.
codec_string = abs; if for some reason G.723.1 has 30ms and 90ms but not 60ms, then the OPAL negotiations
} 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
if ((ocodec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) { come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these
codec_string = switch_core_session_sprintf(m_fsSession, "%s,%s", ocodec, codec_string); scenarios sufficiently rare that we can safely ignore them ... for now. */
}
} if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) {
switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp);
if (!codec_string) { }
codec_string = mod_opal_globals.codec_string;
} if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) {
switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp);
if (codec_string) { }
if ((tmp_codec_string = strdup(codec_string))) { }
codec_order_last = switch_separate_string(tmp_codec_string, ',', codec_order, SWITCH_MAX_CODECS); #endif // IMPLEMENT_MULTI_FAME_AUDIO
numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last);
m_switchMediaFormats += switchFormat;
} }
} else { }
numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));
}
OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource)
for (int i = 0; i < numCodecs; i++) { {
const switch_codec_implementation_t *codec = codecs[i]; return new FSMediaStream(*this, mediaFormat, sessionID, isSource);
}
// See if we have a match by PayloadType/rate/name
OpalMediaFormat switchFormat((RTP_DataFrame::PayloadTypes)codec->ianacode,
codec->samples_per_second, void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)
codec->iananame); {
if (!switchFormat.IsValid()) { OpalConnection::OnPatchMediaStream(isSource, patch);
// See if we have a match by name alone
switchFormat = codec->iananame; if (PAssertNULL(m_fsChannel) == NULL)
if (!switchFormat.IsValid()) { return;
PTRACE(2, "mod_opal\tCould not match FS codec " << codec->iananame << " to OPAL media format.");
continue; if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio())
} return;
}
if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
if (isSource)
// Did we match or create a new media format? m_rxAudioOpened.Signal();
if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) { else
PTRACE(2, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat); m_txAudioOpened.Signal();
}
// Calculate frames per packet, do not use codec->codec_frames_per_packet as that field else if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) {
// has slightly different semantics when used in streamed codecs such as G.711 // Have open media in both directions.
int fpp = codec->samples_per_packet/switchFormat.GetFrameTime(); if (IsEstablished())
switch_channel_mark_answered(m_fsChannel);
/* Set the frames/packet to maximum of what is in the FS table. The OPAL negotiations will else if (!IsReleased())
drop the value from there. This might fail if there are "holes" in the FS table, e.g. switch_channel_mark_pre_answered(m_fsChannel);
if for some reason G.723.1 has 30ms and 90ms but not 60ms, then the OPAL negotiations }
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. */ switch_status_t FSConnection::on_init()
{
if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) { if (PAssertNULL(m_fsChannel) == NULL)
switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp); return SWITCH_STATUS_FALSE;
}
PTRACE(4, "mod_opal\tStarted routing for connection " << *this);
if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption())) { switch_channel_set_state(m_fsChannel, CS_ROUTING);
switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp); return SWITCH_STATUS_SUCCESS;
} }
}
m_switchMediaFormats += switchFormat; switch_status_t FSConnection::on_routing()
} {
if (PAssertNULL(m_fsChannel) == NULL)
switch_safe_free(tmp_codec_string); return SWITCH_STATUS_FALSE;
}
PTRACE(4, "mod_opal\tRouting connection " << *this);
return SWITCH_STATUS_SUCCESS;
OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFormat, unsigned sessionID, PBoolean isSource) }
{
return new FSMediaStream(*this, mediaFormat, sessionID, isSource);
} switch_status_t FSConnection::on_execute()
{
if (PAssertNULL(m_fsChannel) == NULL)
PBoolean FSConnection::OnOpenMediaStream(OpalMediaStream & stream) return SWITCH_STATUS_FALSE;
{
if (!OpalConnection::OnOpenMediaStream(stream)) { PTRACE(4, "mod_opal\tExecuting connection " << *this);
return false; return SWITCH_STATUS_SUCCESS;
} }
if (stream.GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) {
return true; switch_status_t FSConnection::on_destroy()
} {
PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this);
if (stream.IsSource()) {
m_rxAudioOpened.Signal(); m_fsChannel = NULL; // Will be destoyed by FS, so don't use it any more.
} else {
m_txAudioOpened.Signal(); switch_core_codec_destroy(&m_read_codec);
} switch_core_codec_destroy(&m_write_codec);
switch_core_codec_destroy(&m_vid_read_codec);
if (GetMediaStream(stream.GetSessionID(), stream.IsSink()) != NULL) { switch_core_codec_destroy(&m_vid_write_codec);
// Have open media in both directions. switch_core_timer_destroy(&m_read_timer);
if (GetPhase() == AlertingPhase) { switch_core_timer_destroy(&m_vid_read_timer);
switch_channel_mark_pre_answered(m_fsChannel);
} else if (GetPhase() < ReleasingPhase) { switch_core_session_set_private(m_fsSession, NULL);
switch_channel_mark_answered(m_fsChannel); SafeDereference();
}
} return SWITCH_STATUS_SUCCESS;
}
return true;
}
switch_status_t FSConnection::on_hangup()
{
switch_status_t FSConnection::on_init() if (PAssertNULL(m_fsChannel) == NULL)
{ return SWITCH_STATUS_FALSE;
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
if (channel == NULL) { /* if this is still here it was our idea to hangup not opal's */
return SWITCH_STATUS_FALSE; ClearCallSynchronous(NULL, H323TranslateToCallEndReason(
} (Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX));
PTRACE(3, "mod_opal\tStarted routing for connection " << *this); return SWITCH_STATUS_SUCCESS;
switch_channel_set_state(channel, CS_ROUTING); }
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::on_exchange_media()
{
switch_status_t FSConnection::on_routing() PTRACE(4, "mod_opal\tExchanging media on connection " << *this);
{ return SWITCH_STATUS_SUCCESS;
PTRACE(3, "mod_opal\tRouting connection " << *this); }
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::on_soft_execute()
{
switch_status_t FSConnection::on_execute() PTRACE(4, "mod_opal\tTransmit on connection " << *this);
{ return SWITCH_STATUS_SUCCESS;
PTRACE(3, "mod_opal\tExecuting connection " << *this); }
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::kill_channel(int sig)
static switch_status_t on_destroy(switch_core_session_t *session) {
{ switch (sig) {
//switch_channel_t *channel = switch_core_session_get_channel(session); case SWITCH_SIG_KILL:
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); m_rxAudioOpened.Signal();
m_txAudioOpened.Signal();
if (tech_pvt) { PTRACE(4, "mod_opal\tSignal channel KILL on connection " << *this);
if (tech_pvt->read_codec.implementation) { break;
switch_core_codec_destroy(&tech_pvt->read_codec); case SWITCH_SIG_XFER:
} case SWITCH_SIG_BREAK:
default:
if (tech_pvt->write_codec.implementation) { PTRACE(4, "mod_opal\tSignal channel " << sig << " on connection " << *this);
switch_core_codec_destroy(&tech_pvt->write_codec); break;
} }
if (tech_pvt->vid_read_codec.implementation) { return SWITCH_STATUS_SUCCESS;
switch_core_codec_destroy(&tech_pvt->vid_read_codec); }
}
if (tech_pvt->vid_write_codec.implementation) { switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf)
switch_core_codec_destroy(&tech_pvt->vid_write_codec); {
} OnUserInputTone(dtmf->digit, dtmf->duration);
return SWITCH_STATUS_SUCCESS;
if (tech_pvt->read_timer.timer_interface) { }
switch_core_timer_destroy(&tech_pvt->read_timer);
}
switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg)
if (tech_pvt->vid_read_timer.timer_interface) { {
switch_core_timer_destroy(&tech_pvt->vid_read_timer); if (PAssertNULL(m_fsChannel) == NULL)
} return SWITCH_STATUS_FALSE;
}
switch (msg->message_id) {
return SWITCH_STATUS_SUCCESS; case SWITCH_MESSAGE_INDICATE_RINGING:
} case SWITCH_MESSAGE_INDICATE_PROGRESS:
case SWITCH_MESSAGE_INDICATE_ANSWER:
/* this function has to be called with the original session beause the FSConnection might already be destroyed and we case SWITCH_MESSAGE_INDICATE_DEFLECT:
will can't have it be a method of a dead object if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
*/ switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel);
static switch_status_t on_hangup(switch_core_session_t *session) if (profile != NULL && profile->caller_extension != NULL)
{ {
switch_channel_t *channel = switch_core_session_get_channel(session); PSafePtr<OpalConnection> other = GetOtherPartyConnection();
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); if (other != NULL) {
other->SetLocalPartyName(profile->caller_extension->extension_number);
/* if this is still here it was our idea to hangup not opal's */ other->SetDisplayName(profile->caller_extension->extension_name);
if (tech_pvt->me) { }
Q931::CauseValues cause = (Q931::CauseValues)switch_channel_get_cause_q850(channel); SetLocalPartyName(profile->caller_extension->extension_number);
tech_pvt->me->SetQ931Cause(cause); SetDisplayName(profile->caller_extension->extension_name);
tech_pvt->me->ClearCallSynchronous(NULL, H323TranslateToCallEndReason(cause, UINT_MAX)); }
tech_pvt->me = NULL; }
} else {
return SWITCH_STATUS_FALSE;
return SWITCH_STATUS_SUCCESS; }
} break;
default:
switch_status_t FSConnection::on_exchange_media() break;
{ }
PTRACE(3, "mod_opal\tLoopback on connection " << *this);
return SWITCH_STATUS_SUCCESS; switch (msg->message_id) {
} case SWITCH_MESSAGE_INDICATE_BRIDGE:
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
switch_status_t FSConnection::on_soft_execute() m_flushAudio = true;
{ break;
PTRACE(3, "mod_opal\tTransmit on connection " << *this);
return SWITCH_STATUS_SUCCESS; case SWITCH_MESSAGE_INDICATE_RINGING:
} AlertingIncoming();
break;
switch_status_t FSConnection::kill_channel(int sig) case SWITCH_MESSAGE_INDICATE_PROGRESS:
{ AutoStartMediaStreams();
PTRACE(3, "mod_opal\tKill " << sig << " on connection " << *this); AlertingIncoming();
switch (sig) { if (!WaitForMedia())
case SWITCH_SIG_BREAK: return SWITCH_STATUS_FALSE;
break;
case SWITCH_SIG_KILL: if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
m_rxAudioOpened.Signal(); switch_channel_mark_pre_answered(m_fsChannel);
m_txAudioOpened.Signal(); }
break; break;
default:
break; case SWITCH_MESSAGE_INDICATE_ANSWER:
} AcceptIncoming();
return SWITCH_STATUS_SUCCESS; if (!WaitForMedia())
} return SWITCH_STATUS_FALSE;
if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) {
switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf) switch_channel_mark_answered(m_fsChannel);
{ }
OnUserInputTone(dtmf->digit, dtmf->duration); break;
return SWITCH_STATUS_SUCCESS;
} case SWITCH_MESSAGE_INDICATE_DEFLECT:
ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection());
break;
switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg)
{ default:
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession); PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this);
}
/* return SWITCH_STATUS_SUCCESS;
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.... bool FSConnection::WaitForMedia()
{
It would only happen if someone called switch_channel_answer() instead of switch_channel_mark_answered() on an outbound call. PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this);
it should not do anything if someone does it by accident somewhere hense this in both cases: m_rxAudioOpened.Wait();
m_txAudioOpened.Wait();
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
return SWITCH_STATUS_FALSE; if (IsReleased()) {
} // Call got aborted
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");
return 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.
PTRACE(3, "mod_opal\tMedia started on connection " << *this);
return true;
}
*/
switch (msg->message_id) {
case SWITCH_MESSAGE_INDICATE_BRIDGE: switch_status_t FSConnection::receive_event(switch_event_t *event)
case SWITCH_MESSAGE_INDICATE_UNBRIDGE: {
case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC: PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);
switch_channel_set_private_flag(channel, CF_NEED_FLUSH); return SWITCH_STATUS_SUCCESS;
break; }
case SWITCH_MESSAGE_INDICATE_RINGING:
case SWITCH_MESSAGE_INDICATE_PROGRESS: switch_status_t FSConnection::state_change()
case SWITCH_MESSAGE_INDICATE_ANSWER: {
{ PTRACE(4, "mod_opal\tState changed on connection " << *this);
switch_caller_profile_t * profile = switch_channel_get_caller_profile(channel); return SWITCH_STATUS_SUCCESS;
if (profile != NULL && profile->caller_extension != NULL) }
{
PSafePtr<OpalConnection> other = GetOtherPartyConnection();
if (other != NULL) { switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id)
other->SetLocalPartyName(profile->caller_extension->extension_number); {
other->SetDisplayName(profile->caller_extension->extension_name); return read_frame(OpalMediaType::Audio(), frame, flags);
} }
SetLocalPartyName(profile->caller_extension->extension_number);
SetDisplayName(profile->caller_extension->extension_name);
} switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id)
} {
break; return write_frame(OpalMediaType::Audio(), frame, flags);
}
default:
break;
} switch_status_t FSConnection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id)
{
switch (msg->message_id) { return read_frame(OpalMediaType::Video(), frame, flag);
case SWITCH_MESSAGE_INDICATE_RINGING: }
SetPhase(OpalConnection::AlertingPhase);
OnAlerting();
break; switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id)
{
case SWITCH_MESSAGE_INDICATE_DEFLECT: return write_frame(OpalMediaType::Video(), frame, flag);
{ }
PSafePtr<OpalConnection> other = GetOtherPartyConnection();
if (other != NULL)
other->TransferConnection(msg->string_arg); switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags)
break; {
} PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));
return stream != NULL ? stream->read_frame(frame, flags) : SWITCH_STATUS_FALSE;
case SWITCH_MESSAGE_INDICATE_PROGRESS: }
case SWITCH_MESSAGE_INDICATE_ANSWER:
{
int fixed = 0; switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags)
{
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) { PSafePtr <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, true));
return SWITCH_STATUS_FALSE; return stream != NULL ? stream->write_frame(frame, flags) : SWITCH_STATUS_FALSE;
} }
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. FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)
*/ : OpalMediaStream(conn, mediaFormat, sessionID, isSource)
GetCall().OpenSourceMediaStreams(*this, OpalMediaType::Audio()); , m_connection(conn)
SetPhase(OpalConnection::AlertingPhase); , m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE)
/* how do i say please establish early media ? */ {
OnAlerting(); memset(&m_readFrame, 0, sizeof(m_readFrame));
} else { }
/* hack to avoid getting stuck, pre_answer will imply answer */
OnConnectedInternal();
} PBoolean FSMediaStream::Open()
} else { {
OnConnectedInternal(); if (IsOpen()) {
} return true;
}
// Wait for media
PTRACE(2, "mod_opal\tAwaiting media start on connection " << *this); switch_core_session_t *fsSession = m_connection.GetSession();
m_rxAudioOpened.Wait(); switch_channel_t *fsChannel = m_connection.GetChannel();
m_txAudioOpened.Wait(); if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL)
return false;
if (GetPhase() >= ReleasingPhase) {
// Call got aborted bool isAudio;
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n"); if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) {
return SWITCH_STATUS_FALSE; isAudio = true;
} } else if (mediaFormat.GetMediaType() == OpalMediaType::Video()) {
isAudio = false;
PTRACE(4, "mod_opal\tMedia started on connection " << *this); } else {
return false;
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); int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits();
}
} else { if (IsSink()) {
if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) { m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec;
switch_channel_mark_answered(m_fsChannel); 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 ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec;
break; }
default: // The following is performed on two different instances of this object.
PTRACE(3, "mod_opal\tReceived message " << msg->message_id << " on connection " << *this); 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
return 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_status_t FSConnection::receive_event(switch_event_t *event) SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings
{ switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
PTRACE(3, "mod_opal\tReceived event " << event->event_id << " on connection " << *this); PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
return SWITCH_STATUS_SUCCESS; << " cannot initialise " << (IsSink()? "read" : "write") << ' '
} << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
return false;
switch_status_t FSConnection::state_change() }
{ PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel)
PTRACE(3, "mod_opal\tState changed on connection " << *this); << " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '
return SWITCH_STATUS_SUCCESS; << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
} }
if (IsSink()) {
switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id) if (isAudio) {
{ switch_core_session_set_read_codec(fsSession, m_switchCodec);
return read_frame(OpalMediaType::Audio(), frame, flags); if (switch_core_timer_init(m_switchTimer,
} "soft",
m_switchCodec->implementation->microseconds_per_packet / 1000,
m_switchCodec->implementation->samples_per_packet,
switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id) switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
{ PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
return write_frame(OpalMediaType::Audio(), frame, flags); << " timer init failed on " << (IsSink()? "read" : "write") << ' '
} << mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
switch_core_codec_destroy(m_switchCodec);
m_switchCodec = NULL;
switch_status_t FSConnection::read_video_frame(switch_frame_t **frame, switch_io_flag_t flag, int stream_id) return false;
{ }
return read_frame(OpalMediaType::Video(), frame, flag); } else {
} switch_core_session_set_video_read_codec(fsSession, m_switchCodec);
switch_channel_set_flag(fsChannel, CF_VIDEO);
}
switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io_flag_t flag, int stream_id) } else {
{ if (isAudio) {
return write_frame(OpalMediaType::Video(), frame, flag); switch_core_session_set_write_codec(fsSession, m_switchCodec);
} } else {
switch_core_session_set_video_write_codec(fsSession, m_switchCodec);
switch_channel_set_flag(fsChannel, CF_VIDEO);
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));
return stream != NULL ? stream->read_frame(frame, flags) : SWITCH_STATUS_FALSE; PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel)
} << " initialised " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags) return OpalMediaStream::Open();
{ }
PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, true));
return stream != NULL ? stream->write_frame(frame, flags) : SWITCH_STATUS_FALSE;
} void FSMediaStream::InternalClose()
{
}
///////////////////////////////////////////////////////////////////////
FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource) PBoolean FSMediaStream::IsSynchronous() const
: OpalMediaStream(conn, mediaFormat, sessionID, isSource) {
, m_fsSession(conn.GetSession()) return true;
, m_readRTP(0, 512) }
, m_callOnStart(true)
{
memset(&m_readFrame, 0, sizeof(m_readFrame)); PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const
m_readFrame.codec = m_switchCodec; {
m_readFrame.flags = SFF_RAW_RTP; return false;
} }
PBoolean FSMediaStream::Open() int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const
{ {
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession); if (!IsOpen()) {
PTRACE(2, "mod_opal\tNot open!");
if (IsOpen()) { return -1;
return true; }
}
if (!m_switchCodec) {
bool isAudio; PTRACE(2, "mod_opal\tNo codec!");
if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) { return -1;
isAudio = true; }
} else if (mediaFormat.GetMediaType() == OpalMediaType::Video()) {
isAudio = false; if (!m_connection.IsChannelReady()) {
} else { PTRACE(2, "mod_opal\tChannel not ready!");
return OpalMediaStream::Open(); return -1;
} }
m_fsChannel = switch_core_session_get_channel(m_fsSession); // We make referenced copy of pointer so can't be deleted out from under us
mediaPatch = m_mediaPatch;
int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits(); 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. */
if (IsSink()) { PTRACE(3, "mod_opal\tPatch not ready!");
m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec; return 1;
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; return 0;
} }
// The following is performed on two different instances of this object.
if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)
mediaFormat.GetClockRate(), ptime, 1, // Channels {
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings PatchPtr mediaPatch;
switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) { switch (StartReadWrite(mediaPatch)) {
// Could not select a codecs using negotiated frames/packet, so try using default. case -1 :
if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP return SWITCH_STATUS_FALSE;
mediaFormat.GetClockRate(), 0, 1, // Channels case 1 :
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings return SWITCH_STATUS_SUCCESS;
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") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this); if (m_connection.NeedFlushAudio()) {
switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer
return false; m_readRTP.SetPayloadSize(0);
} } else {
PTRACE(2, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' ' m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
} if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) {
return SWITCH_STATUS_FALSE;
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 (m_switchTimer != NULL) {
if (IsSink()) { switch_core_timer_next(m_switchTimer);
m_readFrame.rate = mediaFormat.GetClockRate(); }
if (isAudio) { if (m_switchCodec != NULL) {
switch_core_session_set_read_codec(m_fsSession, m_switchCodec); if (!switch_core_codec_ready(m_switchCodec)) {
if (switch_core_timer_init(m_switchTimer, PTRACE(2, "mod_opal\tread_frame: codec not ready!");
"soft", return SWITCH_STATUS_FALSE;
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_readFrame.packet = m_readRTP.GetPointer();
m_switchCodec = NULL; m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;
return false;
} #if IMPLEMENT_MULTI_FAME_AUDIO
} else { // Repackage frames in incoming packet to agree with what FS expects.
switch_core_session_set_video_read_codec(m_fsSession, m_switchCodec); // Not implmented yet!!!!!!!!!
switch_channel_set_flag(m_fsChannel, CF_VIDEO); // Cheating and only supporting one frame per packet
} #endif
} else {
if (isAudio) { m_readFrame.buflen = m_readRTP.GetSize();
switch_core_session_set_write_codec(m_fsSession, m_switchCodec); m_readFrame.data = m_readRTP.GetPayloadPtr();
} else { m_readFrame.datalen = m_readRTP.GetPayloadSize();
switch_core_session_set_video_write_codec(m_fsSession, m_switchCodec); m_readFrame.timestamp = m_readRTP.GetTimestamp();
switch_channel_set_flag(m_fsChannel, CF_VIDEO); 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();
PTRACE(3, "mod_opal\tSet " << (IsSink()? "read" : "write") << ' ' m_readFrame.flags = m_readFrame.datalen == 0 ||
<< mediaFormat.GetMediaType() << " codec to << " << mediaFormat << " for connection " << *this); m_readFrame.payload == RTP_DataFrame::CN ||
m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0;
return OpalMediaStream::Open();
} *frame = &m_readFrame;
return SWITCH_STATUS_SUCCESS;
PBoolean FSMediaStream::Close() }
{
if (!IsOpen())
return false; switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags)
{
/* forget these FS will properly destroy them for us */ PatchPtr mediaPatch;
switch (StartReadWrite(mediaPatch)) {
m_switchTimer = NULL; case -1 :
m_switchCodec = NULL; return SWITCH_STATUS_FALSE;
case 1 :
return OpalMediaStream::Close(); return SWITCH_STATUS_SUCCESS;
} }
if ((frame->flags & SFF_RAW_RTP) != 0) {
PBoolean FSMediaStream::IsSynchronous() const RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false);
{ return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
return true; }
}
RTP_DataFrame rtp(frame->datalen);
memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);
PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const
{ rtp.SetPayloadType(mediaFormat.GetPayloadType());
return false;
} /* Not sure what FS is going to give us!
Suspect it depends on the mod on the other side sending it. */
bool FSMediaStream::CheckPatchAndLock() if (frame->timestamp != 0)
{ timestamp = frame->timestamp;
if (GetConnection().GetPhase() >= GetConnection().ReleasingPhase || !IsOpen()) else if (frame->samples != 0)
return false; timestamp += frame->samples;
else
if (LockReadWrite()) { timestamp += m_switchCodec->implementation->samples_per_packet;
if (!GetPatch() || !IsOpen()) { rtp.SetTimestamp(timestamp);
UnlockReadWrite();
return false; return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
} }
return true;
} else {
return false; /* For Emacs:
} * Local Variables:
} * mode:c
* indent-tabs-mode:nil
switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags) * tab-width:4
{ * c-basic-offset:4
* End:
if (!m_switchCodec) { * For VIM:
return SWITCH_STATUS_FALSE; * vim:set softtabstop=4 shiftwidth=4 tabstop=4:s:
} */
if (m_callOnStart) {
/*
There is a race here... sometimes we make it here and GetPatch() is NULL
if we wait it shows up in 1ms, maybe there is a better way to wait.
*/
while(!GetPatch()) {
if (!m_fsChannel || !switch_channel_up(m_fsChannel)) {
return SWITCH_STATUS_FALSE;
}
switch_cond_next();
}
if (CheckPatchAndLock()) {
GetPatch()->OnStartMediaPatch();
m_callOnStart = false;
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
}
m_readFrame.flags = 0;
/*
while (switch_channel_ready(m_fsChannel)) {
if (CheckPatchAndLock()) {
if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) {
UnlockReadWrite();
return SWITCH_STATUS_FALSE;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
if ((m_readFrame.datalen = m_readRTP.GetPayloadSize()) || switch_core_timer_check(&m_switchTimer, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) {
if (m_readFrame.datalen) {
} else {
m_readFrame.flags = SFF_CNG;
}
break;
}
switch_yield(1000);
}
*/
if (switch_channel_test_private_flag(m_fsChannel, CF_NEED_FLUSH)) {
switch_channel_clear_private_flag(m_fsChannel, CF_NEED_FLUSH);
for(;;) {
if (CheckPatchAndLock()) {
if (!GetPatch()->GetSource().ReadPacket(m_readRTP)) {
UnlockReadWrite();
return SWITCH_STATUS_FALSE;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
if (!m_readRTP.GetPayloadSize()) {
m_readFrame.flags = SFF_CNG;
break;
}
}
} else {
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 (!switch_channel_ready(m_fsChannel)) {
return SWITCH_STATUS_FALSE;
}
if (!switch_core_codec_ready(m_switchCodec)) {
return SWITCH_STATUS_FALSE;
}
//switch_core_timer_step(&m_switchTimer);
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;
}
*frame = &m_readFrame;
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags)
{
if (!switch_channel_ready(m_fsChannel)) {
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)) {
return SWITCH_STATUS_SUCCESS;
}
if ((frame->flags & SFF_RAW_RTP) != 0) {
RTP_DataFrame rtp((const BYTE *) frame->packet, frame->packetlen, false);
if (CheckPatchAndLock()) {
if (GetPatch()->PushFrame(rtp)) {
UnlockReadWrite();
return SWITCH_STATUS_SUCCESS;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
}
/* If we reach this code it means a call to an ivr or something else that does not generate timestamps
Its possible that frame->timestamp is set but not guarenteed and is best ignored for the time being.
We are probably relying on the rtp stack to generate the timestamp and ssrc for us at this point.
As a quick hack I am going to keep a sample counter and increment it by frame->samples but it would be
better if we could engage whatever it is in opal that makes it generate the timestamp.
*/
RTP_DataFrame rtp(frame->datalen);
rtp.SetPayloadType(mediaFormat.GetPayloadType());
m_timeStamp += frame->samples;
rtp.SetTimestamp(m_timeStamp);
//rtp.SetTimestamp(frame->timestamp);
//rtp.SetSyncSource(frame->ssrc);
//rtp.SetMarker(frame->m);
memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);
if (CheckPatchAndLock()) {
if (GetPatch()->PushFrame(rtp)) {
UnlockReadWrite();
return SWITCH_STATUS_SUCCESS;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
return SWITCH_STATUS_FALSE;
}
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:s:
*/
/* Opal endpoint interface for Freeswitch Modular Media Switching Software Library / /* Opal endpoint interface for Freeswitch Modular Media Switching Software Library /
* Soft-Switch Application * Soft-Switch Application
* *
* Version: MPL 1.1 * Version: MPL 1.1
* *
* Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com) * 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 * The contents of this file are subject to the Mozilla Public License Version
* the License. You may obtain a copy of the License at * 1.1 (the "License"); you may not use this file except in compliance with
* http://www.mozilla.org/MPL/ * the License. You may obtain a copy of the License at
* * http://www.mozilla.org/MPL/
* Software distributed under the License is distributed on an "AS IS" basis, *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * Software distributed under the License is distributed on an "AS IS" basis,
* for the specific language governing rights and limitations under the * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* License. * for the specific language governing rights and limitations under the
* * License.
* Contributor(s): *
* Tuyan Ozipek (tuyanozipek@gmail.com) * Contributor(s):
* Lukasz Zwierko (lzwierko@gmail.com) * Tuyan Ozipek (tuyanozipek@gmail.com)
* Robert Jongbloed (robertj@voxlucida.com.au) * Lukasz Zwierko (lzwierko@gmail.com)
* * Robert Jongbloed (robertj@voxlucida.com.au)
*/ *
*/
#ifndef __FREESWITCH_MOD_OPAL__
#define __FREESWITCH_MOD_OPAL__ #ifndef __FREESWITCH_MOD_OPAL__
#define __FREESWITCH_MOD_OPAL__
#if defined(__GNUC__) && defined(HAVE_VISIBILITY)
#pragma GCC visibility push(default) #if defined(__GNUC__) && defined(HAVE_VISIBILITY)
#endif #pragma GCC visibility push(default)
#endif
#include <ptlib.h>
#include <opal/manager.h> #include <ptlib.h>
#include <opal/localep.h> #include <opal/manager.h>
#include <h323/h323ep.h> #include <opal/localep.h>
#include <iax2/iax2ep.h> #include <h323/h323ep.h>
#include <iax2/iax2ep.h>
#if defined(__GNUC__) && defined(HAVE_VISIBILITY)
#pragma GCC visibility pop #if defined(__GNUC__) && defined(HAVE_VISIBILITY)
#endif #pragma GCC visibility pop
#endif
#undef strcasecmp
#undef strncasecmp #undef strcasecmp
#undef strncasecmp
#define HAVE_APR
#include <switch.h>
#include <switch_version.h> #if _MSC_VER < 1600
#define MODNAME "mod_opal" /*The following insanity is because libteletone_generate.h defines int8_t in
a slightly different manner to most other cases (SDL, PCAP, Java V8, stdint.h
etc) and does not provide a mechanism to prevent it's inclusion. Then, to
class FSEndPoint; cap it off, VS2008 barfs on the difference. VS2010 seems OK with it.
class FSManager;
Sigh.
*/
struct mod_opal_globals { #pragma include_alias(<libteletone.h>, <../../libs/libteletone/src/libteletone.h>)
int trace_level; #pragma include_alias(<libteletone_generate.h>, <../../libs/libteletone/src/libteletone_generate.h>)
char *codec_string; #pragma include_alias(<libteletone_detect.h>, <../../libs/libteletone/src/libteletone_detect.h>)
char *context; #define int8_t signed int8_t
char *dialplan; #include <libteletone_generate.h>
}; #undef int8_t
#endif // End of insanity
extern struct mod_opal_globals mod_opal_globals;
#define HAVE_APR
class FSProcess:public PLibraryProcess { #define uint32_t uint32_t // Avoid conflict in stdint definitions
PCLASSINFO(FSProcess, PLibraryProcess); #include <switch.h>
#undef uint32_t
public:
FSProcess(); #include <switch_version.h>
~FSProcess();
bool Initialise(switch_loadable_module_interface_t *iface); #define MODNAME "mod_opal"
FSManager & GetManager() const {
return *m_manager; class FSEndPoint;
} protected: class FSManager;
FSManager * m_manager;
};
class FSProcess : public PLibraryProcess
{
struct FSListener { PCLASSINFO(FSProcess, PLibraryProcess);
FSListener() { public:
} PString name; FSProcess();
OpalTransportAddress listenAddress; ~FSProcess();
PString localUserName;
PString gatekeeper; bool Initialise(switch_loadable_module_interface_t *iface);
};
FSManager & GetManager() const
{
class FSCall:public OpalCall { return *m_manager;
PCLASSINFO(FSCall, OpalCall); }
public:
FSCall(OpalManager & manager); protected:
virtual PBoolean OnSetUp(OpalConnection & connection); FSManager * m_manager;
}; };
class FSManager:public OpalManager { struct FSListener
PCLASSINFO(FSManager, OpalManager); {
FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { }
public:
FSManager(); PString m_name;
PIPSocket::Address m_address;
bool Initialise(switch_loadable_module_interface_t *iface); uint16_t m_port;
};
switch_status_t ReadConfig(int reload);
switch_endpoint_interface_t *GetSwitchInterface() const { class FSManager : public OpalManager
return m_FreeSwitch; {
} virtual OpalCall *CreateCall(void *userData); PCLASSINFO(FSManager, OpalManager);
private: public:
switch_endpoint_interface_t *m_FreeSwitch; FSManager();
H323EndPoint *m_h323ep; bool Initialise(switch_loadable_module_interface_t *iface);
IAX2EndPoint *m_iaxep;
FSEndPoint *m_fsep; switch_status_t ReadConfig(int reload);
PString m_gkAddress; switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; }
PString m_gkIdentifer; const PString & GetContext() const { return m_context; }
PString m_gkInterface; const PString & GetDialPlan() const { return m_dialplan; }
const PString & GetCodecPrefs() const { return m_codecPrefs; }
list < FSListener > m_listeners; bool GetDisableTranscoding() const { return m_disableTranscoding; }
};
private:
switch_endpoint_interface_t *m_FreeSwitch;
class FSConnection;
typedef struct { H323EndPoint *m_h323ep;
switch_timer_t read_timer; IAX2EndPoint *m_iaxep;
switch_codec_t read_codec; FSEndPoint *m_fsep;
switch_codec_t write_codec;
PString m_context;
switch_timer_t vid_read_timer; PString m_dialplan;
switch_codec_t vid_read_codec; PString m_codecPrefs;
switch_codec_t vid_write_codec; bool m_disableTranscoding;
FSConnection *me; PString m_gkAddress;
} opal_private_t; PString m_gkIdentifer;
PString m_gkInterface;
class FSEndPoint:public OpalLocalEndPoint { list <FSListener> m_listeners;
PCLASSINFO(FSEndPoint, OpalLocalEndPoint); };
public:
FSEndPoint(FSManager & manager);
class FSEndPoint : public OpalLocalEndPoint
virtual bool OnIncomingCall(OpalLocalConnection &); {
virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions); PCLASSINFO(FSEndPoint, OpalLocalEndPoint);
}; public:
FSEndPoint(FSManager & manager);
#define DECLARE_CALLBACK0(name) \ virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions);
static switch_status_t name(switch_core_session_t *session) { \
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \ FSManager & GetManager() const { return m_manager; }
return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name() : SWITCH_STATUS_FALSE; } \
switch_status_t name() protected:
FSManager & m_manager;
#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; } \ class FSConnection;
switch_status_t name(type1 name1)
#define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \ class FSMediaStream : public OpalMediaStream
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); \ PCLASSINFO(FSMediaStream, OpalMediaStream);
return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \ public:
switch_status_t name(type1 name1, type2 name2, type3 name3) FSMediaStream(
FSConnection & conn,
const OpalMediaFormat & mediaFormat, ///< Media format for stream
unsigned sessionID, ///< Session number for stream
bool isSource ///< Is a source stream
class FSConnection:public OpalLocalConnection { );
PCLASSINFO(FSConnection, OpalLocalConnection)
virtual PBoolean Open();
public: virtual PBoolean IsSynchronous() const;
FSConnection(OpalCall & call, virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;
FSEndPoint & endpoint,
void *userData, switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);
unsigned options, switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);
OpalConnection::StringOptions * stringOptions,
switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel); protected:
virtual void InternalClose();
virtual bool OnIncoming(); int StartReadWrite(PatchPtr & mediaPatch) const;
virtual void OnReleased();
virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia); private:
virtual void OnAlerting(); bool CheckPatchAndLock();
virtual void OnEstablished();
virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean); FSConnection &m_connection;
virtual PBoolean OnOpenMediaStream(OpalMediaStream & stream); switch_timer_t *m_switchTimer;
virtual OpalMediaFormatList GetMediaFormats() const; switch_codec_t *m_switchCodec;
virtual PBoolean SendUserInputTone(char tone, unsigned duration); switch_frame_t m_readFrame;
virtual PBoolean SendUserInputString(const PString & value); RTP_DataFrame m_readRTP;
};
void SetCodecs();
DECLARE_CALLBACK0(on_init); #define DECLARE_CALLBACK0(name) \
DECLARE_CALLBACK0(on_routing); static switch_status_t name(switch_core_session_t *session) { \
DECLARE_CALLBACK0(on_execute); FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \
DECLARE_CALLBACK0(on_exchange_media); switch_status_t name()
DECLARE_CALLBACK0(on_soft_execute);
#define DECLARE_CALLBACK1(name, type1, name1) \
DECLARE_CALLBACK1(kill_channel, int, sig); static switch_status_t name(switch_core_session_t *session, type1 name1) { \
DECLARE_CALLBACK1(send_dtmf, const switch_dtmf_t *, dtmf); FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
DECLARE_CALLBACK1(receive_message, switch_core_session_message_t *, msg); return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \
DECLARE_CALLBACK1(receive_event, switch_event_t *, event); switch_status_t name(type1 name1)
DECLARE_CALLBACK0(state_change);
DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id); #define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \
DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id); static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \
DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id); FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id); return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \
switch_status_t name(type1 name1, type2 name2, type3 name3)
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; class FSConnection : public OpalLocalConnection
} private: {
FSEndPoint & m_endpoint; PCLASSINFO(FSConnection, OpalLocalConnection)
switch_core_session_t *m_fsSession;
switch_channel_t *m_fsChannel; public:
PSyncPoint m_rxAudioOpened; struct outgoing_params {
PSyncPoint m_txAudioOpened; switch_event_t *var_event;
OpalMediaFormatList m_switchMediaFormats; switch_caller_profile_t *outbound_profile;
}; switch_core_session_t **new_session;
switch_memory_pool_t **pool;
switch_originate_flag_t flags;
class FSMediaStream:public OpalMediaStream { switch_call_cause_t *cancel_cause;
PCLASSINFO(FSMediaStream, OpalMediaStream); switch_call_cause_t fail_cause;
public: };
FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, ///< Media format for stream
unsigned sessionID, ///< Session number for stream FSConnection(OpalCall & call,
bool isSource ///< Is a source stream FSEndPoint & endpoint,
); unsigned options,
OpalConnection::StringOptions * stringOptions,
virtual PBoolean Open(); outgoing_params * params);
virtual PBoolean Close();
virtual PBoolean IsSynchronous() const; virtual bool OnOutgoingSetUp();
virtual PBoolean RequiresPatchThread(OpalMediaStream *) const; virtual bool OnIncoming();
virtual void OnReleased();
switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags); virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia);
switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags); virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean);
virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch);
private: virtual OpalMediaFormatList GetMediaFormats() const;
switch_core_session_t *m_fsSession; virtual PBoolean SendUserInputTone(char tone, unsigned duration);
switch_channel_t *m_fsChannel;
switch_timer_t *m_switchTimer; DECLARE_CALLBACK0(on_init);
switch_codec_t *m_switchCodec; DECLARE_CALLBACK0(on_destroy);
switch_frame_t m_readFrame; DECLARE_CALLBACK0(on_routing);
unsigned char m_buf[SWITCH_RECOMMENDED_BUFFER_SIZE]; DECLARE_CALLBACK0(on_execute);
RTP_DataFrame m_readRTP; DECLARE_CALLBACK0(on_hangup);
bool m_callOnStart;
uint32_t m_timeStamp; DECLARE_CALLBACK0(on_exchange_media);
DECLARE_CALLBACK0(on_soft_execute);
bool CheckPatchAndLock();
}; 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);
#endif /* __FREESWITCH_MOD_OPAL__ */ DECLARE_CALLBACK1(receive_event, switch_event_t *, event);
DECLARE_CALLBACK0(state_change);
/* For Emacs: DECLARE_CALLBACK3(read_audio_frame, switch_frame_t **, frame, switch_io_flag_t, flags, int, stream_id);
* Local Variables: DECLARE_CALLBACK3(write_audio_frame, switch_frame_t *, frame, switch_io_flag_t, flags, int, stream_id);
* mode:c DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id);
* indent-tabs-mode:nil DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id);
* tab-width:4
* c-basic-offset:4 __inline switch_core_session_t *GetSession() const
* End: {
* For VIM: return m_fsSession;
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:s: }
*/
__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;
}
protected:
void SetCodecs();
bool WaitForMedia();
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);
private:
FSEndPoint &m_endpoint;
switch_core_session_t *m_fsSession;
switch_channel_t *m_fsChannel;
PSyncPoint m_rxAudioOpened;
PSyncPoint m_txAudioOpened;
OpalMediaFormatList m_switchMediaFormats;
// If FS ever supports more than one audio and one video, this needs to change
switch_timer_t m_read_timer;
switch_codec_t m_read_codec;
switch_codec_t m_write_codec;
switch_timer_t m_vid_read_timer;
switch_codec_t m_vid_read_codec;
switch_codec_t m_vid_write_codec;
bool m_flushAudio;
friend PBoolean FSMediaStream::Open();
};
#endif /* __FREESWITCH_MOD_OPAL__ */
/* For Emacs:
* Local Variables:
* mode:c
* indent-tabs-mode:nil
* tab-width:4
* c-basic-offset:4
* End:
* For VIM:
* vim:set softtabstop=4 shiftwidth=4 tabstop=4:s:
*/
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论