提交 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
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
#Locate our script, then go up one directory to be in FreeSWITCH root
cd `dirname $0`
cd ..
FS_DIR=`pwd`
cd /root
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/ptlib/trunk ptlib
cd ptlib
./configure --prefix=/usr
${MAKE}
${MAKE} install
export PKG_CONFIG_PATH=$INSTALLDIR/lib/pkgconfig
cd ..
svn co https://opalvoip.svn.sourceforge.net/svnroot/opalvoip/opal/branches/v3_6 opal
cd opal
export PKG_CONFIG_PATH=/usr/lib/pkgconfig
./configure --prefix=/usr
# Version and patch for PTLib and OPAL. These are almost always in lock
# step so shoud be the same unless you really know ehat you are doing!
# The PATCH should be set to a specific"snapshot release" when things
# 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} install
cd ${FS_DIR}
${MAKE} mod_opal-install
sudo ${MAKE} 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">
<settings>
<param name="trace-level" value="4"/>
<param name="trace-level" value="3"/>
<param name="context" value="default"/>
<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="codec-prefs" value="PCMU,PCMA"/> --> <!-- list, and preferecnce order, of codecs -->
<!-- <param name="disable-transcoding" value="true"/> --> <!-- do not transcode, use source channel codec only -->
<param name="gk-address" value=""/> <!-- empty to disable, "*" to search LAN -->
<param name="gk-identifer" value=""/> <!-- optional name of gk -->
<param name="gk-interface" value=""/> <!-- optional listener interface name -->
<param name="gk-interface" value="$${local_ip_v4}"/> <!-- optional listener interface name -->
</settings>
<listeners>
<listener name="default">
......
......@@ -13,6 +13,9 @@ libtool
ltmain.sh
missing
ptlib
opal
*_manifest.rc
*.pc
......
BASE=../../../..
LOCAL_INSERT_CFLAGS= pkg-config opal --cflags
LOCAL_CFLAGS+=-g -ggdb -I.
LOCAL_INSERT_LDFLAGS= pkg-config opal --libs
PKG_DIR:=/usr/local/lib/pkgconfig
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
......
......@@ -4,6 +4,7 @@
* Version: MPL 1.1
*
* Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)
* Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
......@@ -28,29 +29,34 @@
#include <h323/h323pdu.h>
#include <h323/gkclient.h>
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_codec_string, mod_opal_globals.codec_string);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_context, mod_opal_globals.context);
SWITCH_DECLARE_GLOBAL_STRING_FUNC(set_global_dialplan, mod_opal_globals.dialplan);
/* FreeSWITCH does not correctly handle an H.323 subtely, that is that a
MAXIMUM audio frames per packet is nototiated, and there is no
requirement for the remote to actually send that many. So, in say GSM, we
negotiate up to 3 frames or 60ms of data and the remote actually sends one
(20ms) frame per packet. Perfectly legal but blows up the media handling
in FS.
#define CF_NEED_FLUSH (1 << 1)
struct mod_opal_globals mod_opal_globals = { 0 };
Eventually we will get around to bundling the packets, but not yet. This
compile flag will just force one frame/packet for all audio codecs.
*/
#define IMPLEMENT_MULTI_FAME_AUDIO 0
static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session, switch_event_t *var_event,
switch_caller_profile_t *outbound_profile, switch_core_session_t **new_session,
switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause);
static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
switch_core_session_t **new_session,
switch_memory_pool_t **pool,
switch_originate_flag_t flags,
switch_call_cause_t *cancel_cause);
static FSProcess *opal_process = NULL;
static const char ModuleName[] = "opal";
static switch_status_t on_hangup(switch_core_session_t *session);
static switch_status_t on_destroy(switch_core_session_t *session);
static PConstString const ModuleName("opal");
static char const ConfigFile[] = "opal.conf";
static switch_io_routines_t opalfs_io_routines = {
......@@ -70,7 +76,7 @@ static switch_state_handler_table_t opalfs_event_handlers = {
/*.on_init */ FSConnection::on_init,
/*.on_routing */ FSConnection::on_routing,
/*.on_execute */ FSConnection::on_execute,
/*.on_hangup */ on_hangup,
/*.on_hangup */ FSConnection::on_hangup,
/*.on_exchange_media */ FSConnection::on_exchange_media,
/*.on_soft_execute */ FSConnection::on_soft_execute,
/*.on_consume_media*/ NULL,
......@@ -78,7 +84,7 @@ static switch_state_handler_table_t opalfs_event_handlers = {
/*.on_reset*/ NULL,
/*.on_park*/ NULL,
/*.on_reporting*/ NULL,
/*.on_destroy*/ on_destroy
/*.on_destroy*/ FSConnection::on_destroy
};
......@@ -89,7 +95,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown);
SWITCH_MODULE_DEFINITION(mod_opal, mod_opal_load, mod_opal_shutdown, NULL);
SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load)
{
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Starting loading mod_opal\n");
/* Prevent the loading of OPAL codecs via "plug ins", this is a directory
......@@ -110,8 +117,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
if (opal_process->Initialise(*module_interface)) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Opal manager initialized and running\n");
//unloading causes a seg in linux
return SWITCH_STATUS_NOUNLOAD;
//return SWITCH_STATUS_SUCCESS;
//return SWITCH_STATUS_UNLOAD;
return SWITCH_STATUS_SUCCESS;
}
delete opal_process;
......@@ -120,11 +127,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_opal_load) {
}
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) {
switch_safe_free(mod_opal_globals.context);
switch_safe_free(mod_opal_globals.dialplan);
switch_safe_free(mod_opal_globals.codec_string);
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown)
{
delete opal_process;
opal_process = NULL;
return SWITCH_STATUS_SUCCESS;
......@@ -133,100 +137,57 @@ SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_opal_shutdown) {
SWITCH_END_EXTERN_C
/*******************************************************************************/
static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
switch_core_session_t **new_session, switch_memory_pool_t **pool, switch_originate_flag_t flags, switch_call_cause_t *cancel_cause)
{
if (opal_process == NULL) {
return SWITCH_CAUSE_CRASH;
}
PString token;
FSManager & manager = opal_process->GetManager();
if (!manager.SetUpCall("local:", outbound_profile->destination_number, token, outbound_profile)) {
return SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
}
PSafePtr < OpalCall > call = manager.FindCallWithLock(token);
if (call == NULL) {
return SWITCH_CAUSE_PROTOCOL_ERROR;
}
PSafePtr < FSConnection > connection = call->GetConnectionAs < FSConnection > (0);
if (connection == NULL) {
return SWITCH_CAUSE_PROTOCOL_ERROR;
}
*new_session = connection->GetSession();
return SWITCH_CAUSE_SUCCESS;
}
///////////////////////////////////////////////////////////////////////
#if PTRACING
class FSTrace : public ostream {
public:
FSTrace()
: ostream(&buffer)
{
}
private:
class Buffer : public streambuf {
char buffer[250];
public:
Buffer()
class FSTrace : public std::ostream
{
private:
class Buffer : public std::stringbuf
{
setg(buffer, buffer, &buffer[sizeof(buffer)-2]);
setp(buffer, &buffer[sizeof(buffer)-2]);
}
virtual int sync()
{
return overflow(EOF);
}
virtual int underflow()
{
return EOF;
}
virtual int overflow(int c)
{
const char *fmt = "%s";
char *func = NULL;
int bufSize = pptr() - pbase();
std::string s = str();
if (s.empty())
return 0;
if (c != EOF) {
*pptr() = (char)c;
bufSize++;
//Due to explicit setting of flags we know exactly what we are getting
#define THREAD_ID_INDEX 2
#define FILE_NAME_INDEX 3
#define FILE_LINE_INDEX 4
#if PTLIB_CHECK_VERSION(2,11,1)
#define CONTEXT_ID_REGEX "([0-9]+|- - - - - - -)\t"
#define LOG_PRINTF_FORMAT "{%s,%s} %s"
#define FULL_TEXT_INDEX 6
#else
#define CONTEXT_ID_REGEX
#define LOG_PRINTF_FORMAT "{%s} %s"
#define FULL_TEXT_INDEX 5
#endif
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";
fields[FILE_NAME_INDEX] = __FILE__;
fields[FILE_LINE_INDEX] = __LINE__;
fields[FULL_TEXT_INDEX] = s;
}
if (bufSize != 0) {
char *bufPtr = pbase();
char *bufEndPtr = NULL;
setp(bufPtr, epptr());
bufPtr[bufSize] = '\0';
int line = 0;
char *p;
char *file = NULL;
switch_log_level_t level;
switch (strtoul(bufPtr, &file, 10)) {
switch (fields[1].AsUnsigned()) {
case 0 :
level = SWITCH_LOG_ALERT;
break;
case 1 :
level = SWITCH_LOG_ERROR;
break;
case 2 :
level = SWITCH_LOG_WARNING;
break;
case 3 :
level = SWITCH_LOG_INFO;
break;
default :
......@@ -234,60 +195,43 @@ class FSTrace : public ostream {
break;
}
if (file) {
while (isspace(*file)) file++;
if (file && (bufPtr = strchr(file, '(')) && (bufEndPtr = strchr(bufPtr, ')'))) {
char *e;
for(p = bufPtr; p && *p; p++) {
if (*p == '\t') {
*p = ' ';
}
}
*bufPtr++ = '\0';
line = atoi(bufPtr);
while (bufEndPtr && isspace(*(++bufEndPtr)));
bufPtr = bufEndPtr;
if (bufPtr && ((e = strchr(bufPtr, ' ')) || (e = strchr(bufPtr, '\t')))) {
func = bufPtr;
bufPtr = e;
*bufPtr++ = '\0';
}
}
}
switch_text_channel_t tchannel = SWITCH_CHANNEL_ID_LOG;
if (!bufPtr) {
bufPtr = pbase();
level = SWITCH_LOG_DEBUG;
}
if (bufPtr) {
if (end_of(bufPtr) != '\n') {
fmt = "%s\n";
}
if (!(file && func && line)) tchannel = SWITCH_CHANNEL_ID_LOG_CLEAN;
switch_log_printf(tchannel, file, func, line, NULL, level, fmt, bufPtr);
}
}
fields[4].Replace("\t", " ", true);
#if PTLIB_CHECK_VERSION(2,11,1)
fields[5].Replace("- - - - - - -", "-"),
#endif
switch_log_printf(SWITCH_CHANNEL_ID_LOG,
fields[FILE_NAME_INDEX],
"PTLib-OPAL",
fields[FILE_LINE_INDEX].AsUnsigned(),
NULL,
level,
LOG_PRINTF_FORMAT,
fields[THREAD_ID_INDEX].GetPointer(),
#if PTLIB_CHECK_VERSION(2,11,1)
fields[5].GetPointer(),
#endif
fields[FULL_TEXT_INDEX].GetPointer());
// Reset string
str(std::string());
return 0;
}
} buffer;
public:
FSTrace()
: ostream(&buffer)
{
}
};
#endif
#endif // PTRACING
///////////////////////////////////////////////////////////////////////
FSProcess::FSProcess()
: PLibraryProcess("Vox Lucida Pty. Ltd.", "mod_opal", 1, 0, AlphaCode, 1)
: PLibraryProcess("Vox Lucida Pty. Ltd.", MODNAME, 1, 1, BetaCode, 1)
, m_manager(NULL)
{
}
......@@ -296,6 +240,9 @@ FSProcess::FSProcess()
FSProcess::~FSProcess()
{
delete m_manager;
#if PTRACING
PTrace::SetStream(NULL); // This will delete the FSTrace object
#endif
}
......@@ -309,6 +256,8 @@ bool FSProcess::Initialise(switch_loadable_module_interface_t *iface)
///////////////////////////////////////////////////////////////////////
FSManager::FSManager()
: m_context("default")
, m_dialplan("XML")
{
// These are deleted by the OpalManager class, no need to have destructor
m_h323ep = new H323EndPoint(*this);
......@@ -321,12 +270,6 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
{
ReadConfig(false);
#if PTRACING
PTrace::SetLevel(mod_opal_globals.trace_level); //just for fun and eyecandy ;)
PTrace::SetOptions(PTrace::TraceLevel);
PTrace::SetStream(new FSTrace);
#endif
m_FreeSwitch = (switch_endpoint_interface_t *) switch_loadable_module_create_interface(iface, SWITCH_ENDPOINT_INTERFACE);
m_FreeSwitch->interface_name = ModuleName;
m_FreeSwitch->io_routines = &opalfs_io_routines;
......@@ -338,8 +281,8 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
m_h323ep->StartListener("");
} else {
for (std::list < FSListener >::iterator it = m_listeners.begin(); it != m_listeners.end(); ++it) {
if (!m_h323ep->StartListener(it->listenAddress)) {
PTRACE(3, "mod_opal\tCannot start listener for " << it->name);
if (!m_h323ep->StartListener(OpalTransportAddress(it->m_address, it->m_port))) {
PTRACE(2, "mod_opal\tCannot start listener for " << it->m_name);
}
}
}
......@@ -363,15 +306,16 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
GetOpalGSMAMR();
GetOpaliLBC();
/* For compatibility with the algorithm in FSConnection::SetCodecs() we need
to set all audio media formats to be 1 frame per packet */
#if !IMPLEMENT_MULTI_FAME_AUDIO
OpalMediaFormatList allCodecs = OpalMediaFormat::GetAllRegisteredMediaFormats();
for (OpalMediaFormatList::iterator it = allCodecs.begin(); it != allCodecs.end(); ++it) {
if (it->GetMediaType() == OpalMediaType::Audio()) {
it->SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), 1);
it->SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), 1);
OpalMediaFormat::SetRegisteredMediaFormat(*it);
}
}
#endif // IMPLEMENT_MULTI_FAME_AUDIO
if (!m_gkAddress.IsEmpty()) {
if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface))
......@@ -391,61 +335,55 @@ bool FSManager::Initialise(switch_loadable_module_interface_t *iface)
switch_status_t FSManager::ReadConfig(int reload)
{
const char *cf = "opal.conf";
switch_status_t status = SWITCH_STATUS_SUCCESS;
switch_memory_pool_t *pool = NULL;
if ((status = switch_core_new_memory_pool(&pool)) != SWITCH_STATUS_SUCCESS) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CRIT, "Memory Error!\n");
return status;
}
set_global_context("default");
set_global_dialplan("XML");
switch_event_t *request_params = NULL;
switch_event_create(&request_params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(request_params);
switch_event_add_header_string(request_params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil(""));
switch_event_t *params = NULL;
switch_event_create(&params, SWITCH_EVENT_REQUEST_PARAMS);
switch_assert(params);
switch_event_add_header_string(params, SWITCH_STACK_BOTTOM, "profile", switch_str_nil(""));
switch_xml_t cfg;
switch_xml_t xml = switch_xml_open_cfg(cf, &cfg, params);
switch_xml_t xml = switch_xml_open_cfg(ConfigFile, &cfg, request_params);
if (xml == NULL) {
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", cf);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "open of %s failed\n", ConfigFile);
return SWITCH_STATUS_FALSE;
}
switch_xml_t xmlSettings = switch_xml_child(cfg, "settings");
if (xmlSettings) {
for (switch_xml_t xmlParam = switch_xml_child(xmlSettings, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
const char *var = switch_xml_attr_soft(xmlParam, "name");
const char *val = switch_xml_attr_soft(xmlParam, "value");
if (!strcasecmp(var, "trace-level")) {
int level = atoi(val);
if (level > 0) {
mod_opal_globals.trace_level = level;
}
} else if (!strcasecmp(var, "context")) {
set_global_context(val);
} else if (!strcasecmp(var, "dialplan")) {
set_global_dialplan(val);
} else if (!strcasecmp(var, "codec-prefs")) {
set_global_codec_string(val);
} else if (!strcasecmp(var, "jitter-size")) {
char * next;
unsigned minJitter = strtoul(val, &next, 10);
if (minJitter >= 10) {
unsigned maxJitter = minJitter;
if (*next == ',')
maxJitter = atoi(next+1);
SetAudioJitterDelay(minJitter, maxJitter); // In milliseconds
}
} else if (!strcasecmp(var, "gk-address")) {
PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
if (var == "context") {
m_context = val;
} else if (var == "dialplan") {
m_dialplan = val;
} else if (var == "codec-prefs") {
m_codecPrefs = val;
} else if (var == "disable-transcoding") {
m_disableTranscoding = switch_true(val);
} else if (var == "jitter-size") {
SetAudioJitterDelay(val.AsUnsigned(), val.Mid(val.Find(',')+1).AsUnsigned()); // In milliseconds
} else if (var == "gk-address") {
m_gkAddress = val;
} else if (!strcasecmp(var, "gk-identifer")) {
} else if (var == "gk-identifer") {
m_gkIdentifer = val;
} else if (!strcasecmp(var, "gk-interface")) {
} else if (var == "gk-interface") {
m_gkInterface = val;
#if PTRACING
} else if (var == "trace-level") {
unsigned level = val.AsUnsigned();
if (level > 0) {
PTrace::SetLevel(level);
PTrace::ClearOptions(0xffffffff); // Everything off
PTrace::SetOptions( // Except these
PTrace::TraceLevel|PTrace::FileAndLine|PTrace::Thread
#if PTLIB_CHECK_VERSION(2,11,1)
|PTrace::ContextIdentifier
#endif
);
PTrace::SetStream(new FSTrace);
}
#endif
}
}
}
......@@ -457,173 +395,183 @@ switch_status_t FSManager::ReadConfig(int reload)
m_listeners.push_back(FSListener());
FSListener & listener = m_listeners.back();
listener.name = switch_xml_attr_soft(xmlListener, "name");
if (listener.name.IsEmpty())
listener.name = "unnamed";
PIPSocket::Address ip;
WORD port = 1720;
listener.m_name = switch_xml_attr_soft(xmlListener, "name");
if (listener.m_name.IsEmpty())
listener.m_name = "unnamed";
for (switch_xml_t xmlParam = switch_xml_child(xmlListener, "param"); xmlParam != NULL; xmlParam = xmlParam->next) {
const char *var = switch_xml_attr_soft(xmlParam, "name");
const char *val = switch_xml_attr_soft(xmlParam, "value");
//switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Var - '%s' and Val - '%s' \n", var, val);
if (!strcasecmp(var, "h323-ip"))
ip = val;
else if (!strcasecmp(var, "h323-port"))
port = (WORD) atoi(val);
PConstCaselessString const var(switch_xml_attr_soft(xmlParam, "name"));
PConstString const val(switch_xml_attr_soft(xmlParam, "value"));
if (var == "h323-ip")
listener.m_address = val;
else if (var == "h323-port")
listener.m_port = (uint16_t)val.AsUnsigned();
}
listener.listenAddress = OpalTransportAddress(ip, port);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.name);
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Created Listener '%s'\n", (const char *) listener.m_name);
}
}
switch_event_destroy(&params);
switch_event_destroy(&request_params);
if (xml)
switch_xml_free(xml);
return status;
return SWITCH_STATUS_SUCCESS;
}
OpalCall * FSManager::CreateCall(void * /*userData*/)
static switch_call_cause_t create_outgoing_channel(switch_core_session_t *session,
switch_event_t *var_event,
switch_caller_profile_t *outbound_profile,
switch_core_session_t **new_session,
switch_memory_pool_t **pool,
switch_originate_flag_t flags,
switch_call_cause_t *cancel_cause)
{
return new FSCall(*this);
}
if (opal_process == NULL)
return SWITCH_CAUSE_CRASH;
FSConnection::outgoing_params params;
params.var_event = var_event;
params.outbound_profile = outbound_profile;
params.new_session = new_session;
params.pool = pool;
params.flags = flags;
params.cancel_cause = cancel_cause;
params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
///////////////////////////////////////////////////////////////////////
if (opal_process->GetManager().SetUpCall("local:", outbound_profile->destination_number, &params) != NULL)
return SWITCH_CAUSE_SUCCESS;
FSEndPoint::FSEndPoint(FSManager & manager)
: OpalLocalEndPoint(manager)
{
PTRACE(3, "mod_opal\t FSEndPoint Created!");
if (*new_session != NULL)
switch_core_session_destroy(new_session);
return params.fail_cause;
}
bool FSEndPoint::OnIncomingCall(OpalLocalConnection & connection)
///////////////////////////////////////////////////////////////////////
FSEndPoint::FSEndPoint(FSManager & manager)
: OpalLocalEndPoint(manager)
, m_manager(manager)
{
return ((FSConnection &) connection).OnIncoming();
PTRACE(4, "mod_opal\tFSEndPoint created.");
}
OpalLocalConnection *FSEndPoint::CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions* stringOptions)
{
FSManager & mgr = (FSManager &) GetManager();
switch_core_session_t *fsSession = switch_core_session_request(mgr.GetSwitchInterface(),
(switch_caller_profile_t *)userData ? SWITCH_CALL_DIRECTION_OUTBOUND : SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL);
if (fsSession == NULL)
return NULL;
switch_channel_t *fsChannel = switch_core_session_get_channel(fsSession);
if (fsChannel == NULL) {
switch_core_session_destroy(&fsSession);
return NULL;
}
return new FSConnection(call, *this, userData, options, stringOptions, (switch_caller_profile_t *)userData, fsSession, fsChannel);
return new FSConnection(call, *this, options, stringOptions, (FSConnection::outgoing_params *)userData);
}
///////////////////////////////////////////////////////////////////////
FSCall::FSCall(OpalManager & manager)
: OpalCall(manager)
FSConnection::FSConnection(OpalCall & call,
FSEndPoint & endpoint,
unsigned options,
OpalConnection::StringOptions* stringOptions,
outgoing_params * params)
: OpalLocalConnection(call, endpoint, NULL, options, stringOptions)
, m_endpoint(endpoint)
, m_fsSession(NULL)
, m_fsChannel(NULL)
, m_flushAudio(false)
{
}
memset(&m_read_timer, 0, sizeof(m_read_timer));
memset(&m_read_codec, 0, sizeof(m_read_codec));
memset(&m_write_codec, 0, sizeof(m_write_codec));
memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer));
memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec));
memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec));
if (params != NULL) {
// If we fail, this is the cause
params->fail_cause = SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER;
PBoolean FSCall::OnSetUp(OpalConnection & connection)
{
// Transfer FS caller_id_number & caller_id_name from the FSConnection
// to the protocol connectionm (e.g. H.323) so gets sent correctly
// in outgoing packets
PSafePtr<FSConnection> local = GetConnectionAs<FSConnection>();
if (local != NULL) {
PSafePtr<OpalConnection> proto = local->GetOtherPartyConnection();
if (proto != NULL) {
proto->SetLocalPartyName(local->GetLocalPartyName());
proto->SetDisplayName(local->GetDisplayName());
if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),
SWITCH_CALL_DIRECTION_INBOUND, params->flags, params->pool)) == NULL) {
PTRACE(1, "mod_opal\tCannot create session for outgoing call.");
return;
}
}
else {
if ((m_fsSession = switch_core_session_request(endpoint.GetManager().GetSwitchInterface(),
SWITCH_CALL_DIRECTION_INBOUND, SOF_NONE, NULL)) == NULL) {
PTRACE(1, "mod_opal\tCannot create session for incoming call.");
return;
}
}
return OpalCall::OnSetUp(connection);
}
if ((m_fsChannel = switch_core_session_get_channel(m_fsSession)) == NULL) {
switch_core_session_destroy(&m_fsSession);
return;
}
switch_core_session_set_private(m_fsSession, this);
SafeReference(); // Make sure cannot be deleted until on_destroy()
///////////////////////////////////////////////////////////////////////
if (params != NULL) {
switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, params->outbound_profile);
switch_channel_set_caller_profile(m_fsChannel, caller_profile);
SetLocalPartyName(caller_profile->caller_id_number);
SetDisplayName(caller_profile->caller_id_name);
*params->new_session = m_fsSession;
}
FSConnection::FSConnection(OpalCall & call, FSEndPoint & endpoint, void* userData, unsigned options, OpalConnection::StringOptions* stringOptions, switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel)
: OpalLocalConnection(call, endpoint, userData, options, stringOptions)
, m_endpoint(endpoint)
, m_fsSession(fsSession)
, m_fsChannel(fsChannel)
{
opal_private_t *tech_pvt;
switch_channel_set_state(m_fsChannel, CS_INIT);
}
tech_pvt = (opal_private_t *) switch_core_session_alloc(m_fsSession, sizeof(*tech_pvt));
tech_pvt->me = this;
switch_core_session_set_private(m_fsSession, tech_pvt);
if (outbound_profile != NULL) {
SetLocalPartyName(outbound_profile->caller_id_number);
SetDisplayName(outbound_profile->caller_id_name);
bool FSConnection::OnOutgoingSetUp()
{
if (m_fsSession == NULL || m_fsChannel == NULL) {
PTRACE(1, "mod_opal\tSession request failed.");
return false;
}
switch_caller_profile_t *caller_profile = switch_caller_profile_clone(m_fsSession, outbound_profile);
switch_channel_set_caller_profile(m_fsChannel, caller_profile);
// Transfer FS caller_id_number & caller_id_name from the FSConnection
// to the protocol connection (e.g. H.323) so gets sent correctly
// in outgoing packets
PSafePtr<OpalConnection> proto = GetOtherPartyConnection();
if (proto == NULL) {
PTRACE(1, "mod_opal\tNo protocol connection in call.");
return false;
}
PString name = "opal/";
name += outbound_profile->destination_number;
switch_channel_set_name(m_fsChannel, name);
proto->SetLocalPartyName(GetLocalPartyName());
proto->SetDisplayName(GetDisplayName());
switch_channel_set_state(m_fsChannel, CS_INIT);
}
switch_channel_set_name(m_fsChannel, ModuleName + '/' + GetRemotePartyURL());
return true;
}
bool FSConnection::OnIncoming()
{
if (m_fsSession == NULL) {
if (m_fsSession == NULL || m_fsChannel == NULL) {
PTRACE(1, "mod_opal\tSession request failed.");
return false;
}
switch_core_session_add_stream(m_fsSession, NULL);
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
if (channel == NULL) {
PTRACE(1, "mod_opal\tSession does not have a channel");
return false;
}
PURL url = GetRemotePartyURL();
switch_caller_profile_t *caller_profile = switch_caller_profile_new(switch_core_session_get_pool(m_fsSession),
url.GetUserName(),
/** username */
mod_opal_globals.dialplan,
/** dial plan */
GetRemotePartyName(),
/** caller_id_name */
GetRemotePartyNumber(),
/** caller_id_number */
url.GetHostName(),
/** network addr */
NULL,
/** ANI */
NULL,
/** ANI II */
NULL,
/** RDNIS */
ModuleName,
/** source */
mod_opal_globals.context,
/** set context */
GetCalledPartyNumber()
/** destination_number */
switch_caller_profile_t *caller_profile = switch_caller_profile_new(
switch_core_session_get_pool(m_fsSession),
url.GetUserName(), /** username */
m_endpoint.GetManager().GetDialPlan(), /** dial plan */
GetRemotePartyName(), /** caller_id_name */
GetRemotePartyNumber(), /** caller_id_number */
url.GetHostName(), /** network addr */
NULL, /** ANI */
NULL, /** ANI II */
NULL, /** RDNIS */
ModuleName, /** source */
m_endpoint.GetManager().GetContext(), /** set context */
GetCalledPartyNumber() /** destination_number */
);
if (caller_profile == NULL) {
PTRACE(1, "mod_opal\tCould not create caller profile");
......@@ -637,16 +585,16 @@ bool FSConnection::OnIncoming()
" caller_id_number = " << caller_profile->caller_id_number << "\n"
" network_addr = " << caller_profile->network_addr << "\n"
" source = " << caller_profile->source << "\n"
" context = " << caller_profile->context << "\n" " destination_number= " << caller_profile->destination_number);
switch_channel_set_caller_profile(channel, caller_profile);
" context = " << caller_profile->context << "\n"
" destination_number= " << caller_profile->destination_number);
switch_channel_set_caller_profile(m_fsChannel, caller_profile);
char name[256] = "opal/in:";
switch_copy_string(name + 8, caller_profile->destination_number, sizeof(name)-8);
switch_channel_set_name(channel, name);
switch_channel_set_state(channel, CS_INIT);
switch_channel_set_name(m_fsChannel, ModuleName + '/' + url.GetScheme() + ':' + caller_profile->destination_number);
if (switch_core_session_thread_launch(m_fsSession) != SWITCH_STATUS_SUCCESS) {
PTRACE(1, "mod_opal\tCould not launch session thread");
switch_core_session_destroy(&m_fsSession);
m_fsChannel = NULL;
return false;
}
......@@ -656,51 +604,38 @@ bool FSConnection::OnIncoming()
void FSConnection::OnReleased()
{
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession);
/* so FS on_hangup will not try to deref a landmine */
tech_pvt->me = NULL;
m_rxAudioOpened.Signal(); // Just in case
m_txAudioOpened.Signal();
H225_ReleaseCompleteReason dummy;
switch_channel_hangup(switch_core_session_get_channel(m_fsSession),
(switch_call_cause_t)H323TranslateFromCallEndReason(GetCallEndReason(), dummy));
OpalLocalConnection::OnReleased();
}
if (m_fsChannel == NULL) {
PTRACE(3, "mod_opal\tHanging up FS side");
switch_channel_hangup(m_fsChannel, (switch_call_cause_t)callEndReason.q931);
}
void FSConnection::OnAlerting()
{
switch_channel_mark_ring_ready(m_fsChannel);
return OpalLocalConnection::OnAlerting();
OpalLocalConnection::OnReleased();
}
PBoolean FSConnection::SetAlerting(const PString & calleeName, PBoolean withMedia)
{
return OpalLocalConnection::SetAlerting(calleeName, withMedia);
}
if (PAssertNULL(m_fsChannel) == NULL)
return false;
void FSConnection::OnEstablished()
{
OpalLocalConnection::OnEstablished();
switch_channel_mark_ring_ready(m_fsChannel);
return OpalLocalConnection::SetAlerting(calleeName, withMedia);
}
PBoolean FSConnection::SendUserInputTone(char tone, unsigned duration)
{
if (PAssertNULL(m_fsChannel) == NULL)
return false;
switch_dtmf_t dtmf = { tone, duration };
return switch_channel_queue_dtmf(m_fsChannel, &dtmf) == SWITCH_STATUS_SUCCESS;
}
PBoolean FSConnection::SendUserInputString(const PString & value)
{
return OpalConnection::SendUserInputString(value);
}
OpalMediaFormatList FSConnection::GetMediaFormats() const
{
if (m_switchMediaFormats.IsEmpty()) {
......@@ -715,36 +650,49 @@ void FSConnection::SetCodecs()
{
int numCodecs = 0;
const switch_codec_implementation_t *codecs[SWITCH_MAX_CODECS];
const char *codec_string = NULL, *abs, *ocodec;
char *tmp_codec_string = NULL;
char *codec_order[SWITCH_MAX_CODECS];
int codec_order_last;
if ((abs = switch_channel_get_variable(m_fsChannel, "absolute_codec_string"))) {
codec_string = abs;
} else {
if ((abs = switch_channel_get_variable(m_fsChannel, "codec_string"))) {
codec_string = abs;
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");
if (codec_string.IsEmpty()) {
codec_string = m_endpoint.GetManager().GetCodecPrefs();
if (codec_string.IsEmpty()) {
numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));
for (int i = 0; i < numCodecs; i++) {
if (i > 0)
codec_string += ',';
codec_string += codecs[i]->iananame;
}
if ((ocodec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE))) {
codec_string = switch_core_session_sprintf(m_fsSession, "%s,%s", ocodec, codec_string);
PTRACE(4, "mod_opal\tDefault to all loaded codecs=" << codec_string);
}
else {
PTRACE(4, "mod_opal\tSettings codec-prefs=" << codec_string);
}
}
else {
PTRACE(4, "mod_opal\tChannel codec_string=" << codec_string);
}
if (!codec_string) {
codec_string = mod_opal_globals.codec_string;
PString orig_codec = switch_channel_get_variable(m_fsChannel, SWITCH_ORIGINATOR_CODEC_VARIABLE);
if (!orig_codec.IsEmpty()) {
if (m_endpoint.GetManager().GetDisableTranscoding()) {
codec_string = orig_codec;
PTRACE(4, "mod_opal\tNo transcoding, forced to originator codec=" << orig_codec);
}
else {
codec_string.Splice(orig_codec+',', 0);
PTRACE(4, "mod_opal\tSetting preference to originator codec=" << orig_codec);
}
}
}
else {
PTRACE(4, "mod_opal\tChannel absolute_codec_string=" << codec_string);
}
if (codec_string) {
if ((tmp_codec_string = strdup(codec_string))) {
codec_order_last = switch_separate_string(tmp_codec_string, ',', codec_order, SWITCH_MAX_CODECS);
{
char *codec_order[SWITCH_MAX_CODECS];
int codec_order_last = switch_separate_string((char *)codec_string.GetPointer(), ',', codec_order, SWITCH_MAX_CODECS);
numCodecs = switch_loadable_module_get_codecs_sorted(codecs, SWITCH_MAX_CODECS, codec_order, codec_order_last);
}
} else {
numCodecs = switch_loadable_module_get_codecs(codecs, sizeof(codecs) / sizeof(codecs[0]));
}
for (int i = 0; i < numCodecs; i++) {
......@@ -758,16 +706,19 @@ void FSConnection::SetCodecs()
// See if we have a match by name alone
switchFormat = codec->iananame;
if (!switchFormat.IsValid()) {
PTRACE(2, "mod_opal\tCould not match FS codec " << codec->iananame << " to OPAL media format.");
PTRACE(2, "mod_opal\tCould not match FS codec "
<< codec->iananame << '@' << codec->samples_per_second
<< " (pt=" << (unsigned)codec->ianacode << ")"
" to an OPAL media format.");
continue;
}
}
PTRACE(4, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);
#if IMPLEMENT_MULTI_FAME_AUDIO
// Did we match or create a new media format?
if (switchFormat.IsValid() && codec->codec_type == SWITCH_CODEC_TYPE_AUDIO) {
PTRACE(2, "mod_opal\tMatched FS codec " << codec->iananame << " to OPAL media format " << switchFormat);
// Calculate frames per packet, do not use codec->codec_frames_per_packet as that field
// has slightly different semantics when used in streamed codecs such as G.711
int fpp = codec->samples_per_packet/switchFormat.GetFrameTime();
......@@ -778,7 +729,7 @@ void FSConnection::SetCodecs()
could end up with 60ms and the codec cannot be created. The "holes" are unlikely in
all but streamed codecs such as G.711, where it is theoretically possible for OPAL to
come up with 32ms and there is only 30ms and 40ms in the FS table. We deem these
scenarios succifiently rare that we can safely ignore them ... for now. */
scenarios sufficiently rare that we can safely ignore them ... for now. */
if (fpp > switchFormat.GetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption())) {
switchFormat.SetOptionInteger(OpalAudioFormat::RxFramesPerPacketOption(), fpp);
......@@ -788,11 +739,10 @@ void FSConnection::SetCodecs()
switchFormat.SetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption(), fpp);
}
}
#endif // IMPLEMENT_MULTI_FAME_AUDIO
m_switchMediaFormats += switchFormat;
}
switch_safe_free(tmp_codec_string);
}
......@@ -802,110 +752,91 @@ OpalMediaStream *FSConnection::CreateMediaStream(const OpalMediaFormat & mediaFo
}
PBoolean FSConnection::OnOpenMediaStream(OpalMediaStream & stream)
void FSConnection::OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch)
{
if (!OpalConnection::OnOpenMediaStream(stream)) {
return false;
}
OpalConnection::OnPatchMediaStream(isSource, patch);
if (stream.GetMediaFormat().GetMediaType() != OpalMediaType::Audio()) {
return true;
}
if (PAssertNULL(m_fsChannel) == NULL)
return;
if (patch.GetSource().GetMediaFormat().GetMediaType() != OpalMediaType::Audio())
return;
if (stream.IsSource()) {
if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
if (isSource)
m_rxAudioOpened.Signal();
} else {
else
m_txAudioOpened.Signal();
}
if (GetMediaStream(stream.GetSessionID(), stream.IsSink()) != NULL) {
else if (GetMediaStream(OpalMediaType::Audio(), !isSource) != NULL) {
// Have open media in both directions.
if (GetPhase() == AlertingPhase) {
switch_channel_mark_pre_answered(m_fsChannel);
} else if (GetPhase() < ReleasingPhase) {
if (IsEstablished())
switch_channel_mark_answered(m_fsChannel);
else if (!IsReleased())
switch_channel_mark_pre_answered(m_fsChannel);
}
}
return true;
}
switch_status_t FSConnection::on_init()
{
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
if (channel == NULL) {
if (PAssertNULL(m_fsChannel) == NULL)
return SWITCH_STATUS_FALSE;
}
PTRACE(3, "mod_opal\tStarted routing for connection " << *this);
switch_channel_set_state(channel, CS_ROUTING);
PTRACE(4, "mod_opal\tStarted routing for connection " << *this);
switch_channel_set_state(m_fsChannel, CS_ROUTING);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::on_routing()
{
PTRACE(3, "mod_opal\tRouting connection " << *this);
if (PAssertNULL(m_fsChannel) == NULL)
return SWITCH_STATUS_FALSE;
PTRACE(4, "mod_opal\tRouting connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::on_execute()
{
PTRACE(3, "mod_opal\tExecuting connection " << *this);
if (PAssertNULL(m_fsChannel) == NULL)
return SWITCH_STATUS_FALSE;
PTRACE(4, "mod_opal\tExecuting connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
static switch_status_t on_destroy(switch_core_session_t *session)
{
//switch_channel_t *channel = switch_core_session_get_channel(session);
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session);
if (tech_pvt) {
if (tech_pvt->read_codec.implementation) {
switch_core_codec_destroy(&tech_pvt->read_codec);
}
if (tech_pvt->write_codec.implementation) {
switch_core_codec_destroy(&tech_pvt->write_codec);
}
if (tech_pvt->vid_read_codec.implementation) {
switch_core_codec_destroy(&tech_pvt->vid_read_codec);
}
switch_status_t FSConnection::on_destroy()
{
PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this);
if (tech_pvt->vid_write_codec.implementation) {
switch_core_codec_destroy(&tech_pvt->vid_write_codec);
}
m_fsChannel = NULL; // Will be destoyed by FS, so don't use it any more.
if (tech_pvt->read_timer.timer_interface) {
switch_core_timer_destroy(&tech_pvt->read_timer);
}
switch_core_codec_destroy(&m_read_codec);
switch_core_codec_destroy(&m_write_codec);
switch_core_codec_destroy(&m_vid_read_codec);
switch_core_codec_destroy(&m_vid_write_codec);
switch_core_timer_destroy(&m_read_timer);
switch_core_timer_destroy(&m_vid_read_timer);
if (tech_pvt->vid_read_timer.timer_interface) {
switch_core_timer_destroy(&tech_pvt->vid_read_timer);
}
}
switch_core_session_set_private(m_fsSession, NULL);
SafeDereference();
return SWITCH_STATUS_SUCCESS;
}
/* this function has to be called with the original session beause the FSConnection might already be destroyed and we
will can't have it be a method of a dead object
*/
static switch_status_t on_hangup(switch_core_session_t *session)
switch_status_t FSConnection::on_hangup()
{
switch_channel_t *channel = switch_core_session_get_channel(session);
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session);
if (PAssertNULL(m_fsChannel) == NULL)
return SWITCH_STATUS_FALSE;
/* if this is still here it was our idea to hangup not opal's */
if (tech_pvt->me) {
Q931::CauseValues cause = (Q931::CauseValues)switch_channel_get_cause_q850(channel);
tech_pvt->me->SetQ931Cause(cause);
tech_pvt->me->ClearCallSynchronous(NULL, H323TranslateToCallEndReason(cause, UINT_MAX));
tech_pvt->me = NULL;
}
ClearCallSynchronous(NULL, H323TranslateToCallEndReason(
(Q931::CauseValues)switch_channel_get_cause_q850(m_fsChannel), UINT_MAX));
return SWITCH_STATUS_SUCCESS;
}
......@@ -913,30 +844,30 @@ static switch_status_t on_hangup(switch_core_session_t *session)
switch_status_t FSConnection::on_exchange_media()
{
PTRACE(3, "mod_opal\tLoopback on connection " << *this);
PTRACE(4, "mod_opal\tExchanging media on connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::on_soft_execute()
{
PTRACE(3, "mod_opal\tTransmit on connection " << *this);
PTRACE(4, "mod_opal\tTransmit on connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::kill_channel(int sig)
{
PTRACE(3, "mod_opal\tKill " << sig << " on connection " << *this);
switch (sig) {
case SWITCH_SIG_BREAK:
break;
case SWITCH_SIG_KILL:
m_rxAudioOpened.Signal();
m_txAudioOpened.Signal();
PTRACE(4, "mod_opal\tSignal channel KILL on connection " << *this);
break;
case SWITCH_SIG_XFER:
case SWITCH_SIG_BREAK:
default:
PTRACE(4, "mod_opal\tSignal channel " << sig << " on connection " << *this);
break;
}
......@@ -953,41 +884,16 @@ switch_status_t FSConnection::send_dtmf(const switch_dtmf_t *dtmf)
switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg)
{
switch_channel_t *channel = switch_core_session_get_channel(m_fsSession);
/*
SWITCH_MESSAGE_INDICATE_PROGRESS: establish early media now and return SWITCH_STATUS_FALSE if you can't
SWITCH_MESSAGE_INDICATE_ANSWER: answer and set up media now if it's not already and return SWITCH_STATUS_FALSE if you can't
Neither message means anything on an outbound call....
It would only happen if someone called switch_channel_answer() instead of switch_channel_mark_answered() on an outbound call.
it should not do anything if someone does it by accident somewhere hense this in both cases:
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
if (PAssertNULL(m_fsChannel) == NULL)
return SWITCH_STATUS_FALSE;
}
When we get these messages the core will trust that you have triggered FSMediaStream::Open and are ready for media if we do not
have media we MUST return SWITCH_STATUS_FALSE or it will cause a CRASH.
*/
switch (msg->message_id) {
case SWITCH_MESSAGE_INDICATE_BRIDGE:
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
switch_channel_set_private_flag(channel, CF_NEED_FLUSH);
break;
case SWITCH_MESSAGE_INDICATE_RINGING:
case SWITCH_MESSAGE_INDICATE_PROGRESS:
case SWITCH_MESSAGE_INDICATE_ANSWER:
{
switch_caller_profile_t * profile = switch_channel_get_caller_profile(channel);
case SWITCH_MESSAGE_INDICATE_DEFLECT:
if (switch_channel_direction(m_fsChannel) == SWITCH_CALL_DIRECTION_INBOUND) {
switch_caller_profile_t * profile = switch_channel_get_caller_profile(m_fsChannel);
if (profile != NULL && profile->caller_extension != NULL)
{
PSafePtr<OpalConnection> other = GetOtherPartyConnection();
......@@ -999,6 +905,9 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg
SetDisplayName(profile->caller_extension->extension_name);
}
}
else {
return SWITCH_STATUS_FALSE;
}
break;
default:
......@@ -1006,89 +915,78 @@ switch_status_t FSConnection::receive_message(switch_core_session_message_t *msg
}
switch (msg->message_id) {
case SWITCH_MESSAGE_INDICATE_RINGING:
SetPhase(OpalConnection::AlertingPhase);
OnAlerting();
case SWITCH_MESSAGE_INDICATE_BRIDGE:
case SWITCH_MESSAGE_INDICATE_UNBRIDGE:
case SWITCH_MESSAGE_INDICATE_AUDIO_SYNC:
m_flushAudio = true;
break;
case SWITCH_MESSAGE_INDICATE_DEFLECT:
{
PSafePtr<OpalConnection> other = GetOtherPartyConnection();
if (other != NULL)
other->TransferConnection(msg->string_arg);
case SWITCH_MESSAGE_INDICATE_RINGING:
AlertingIncoming();
break;
}
case SWITCH_MESSAGE_INDICATE_PROGRESS:
case SWITCH_MESSAGE_INDICATE_ANSWER:
{
int fixed = 0;
AutoStartMediaStreams();
AlertingIncoming();
if (switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND) {
if (!WaitForMedia())
return 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.
*/
GetCall().OpenSourceMediaStreams(*this, OpalMediaType::Audio());
SetPhase(OpalConnection::AlertingPhase);
/* how do i say please establish early media ? */
OnAlerting();
} else {
/* hack to avoid getting stuck, pre_answer will imply answer */
OnConnectedInternal();
}
} else {
OnConnectedInternal();
if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
switch_channel_mark_pre_answered(m_fsChannel);
}
break;
// Wait for media
PTRACE(2, "mod_opal\tAwaiting media start on connection " << *this);
m_rxAudioOpened.Wait();
m_txAudioOpened.Wait();
case SWITCH_MESSAGE_INDICATE_ANSWER:
AcceptIncoming();
if (GetPhase() >= ReleasingPhase) {
// Call got aborted
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");
if (!WaitForMedia())
return SWITCH_STATUS_FALSE;
}
PTRACE(4, "mod_opal\tMedia started on connection " << *this);
if (msg->message_id == SWITCH_MESSAGE_INDICATE_PROGRESS) {
if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
switch_channel_mark_pre_answered(m_fsChannel);
}
} else {
if (!switch_channel_test_flag(m_fsChannel, CF_EARLY_MEDIA)) {
if (!switch_channel_test_flag(m_fsChannel, CF_ANSWERED)) {
switch_channel_mark_answered(m_fsChannel);
}
}
break;
}
case SWITCH_MESSAGE_INDICATE_DEFLECT:
ownerCall.Transfer(msg->string_arg, GetOtherPartyConnection());
break;
default:
PTRACE(3, "mod_opal\tReceived message " << msg->message_id << " on connection " << *this);
PTRACE(3, "mod_opal\tReceived unhandled message " << msg->message_id << " on connection " << *this);
}
return SWITCH_STATUS_SUCCESS;
}
bool FSConnection::WaitForMedia()
{
PTRACE(4, "mod_opal\tAwaiting media start on connection " << *this);
m_rxAudioOpened.Wait();
m_txAudioOpened.Wait();
if (IsReleased()) {
// Call got aborted
switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(m_fsSession), SWITCH_LOG_ERROR, "Call abandoned!\n");
return false;
}
PTRACE(3, "mod_opal\tMedia started on connection " << *this);
return true;
}
switch_status_t FSConnection::receive_event(switch_event_t *event)
{
PTRACE(3, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);
PTRACE(4, "mod_opal\tReceived event " << event->event_id << " on connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
switch_status_t FSConnection::state_change()
{
PTRACE(3, "mod_opal\tState changed on connection " << *this);
PTRACE(4, "mod_opal\tState changed on connection " << *this);
return SWITCH_STATUS_SUCCESS;
}
......@@ -1119,14 +1017,14 @@ switch_status_t FSConnection::write_video_frame(switch_frame_t *frame, switch_io
switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags)
{
PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, false));
PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));
return stream != NULL ? stream->read_frame(frame, flags) : SWITCH_STATUS_FALSE;
}
switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags)
{
PSafePtr < FSMediaStream > stream = PSafePtrCast < OpalMediaStream, FSMediaStream > (GetMediaStream(mediaType, true));
PSafePtr <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, true));
return stream != NULL ? stream->write_frame(frame, flags) : SWITCH_STATUS_FALSE;
}
......@@ -1135,113 +1033,103 @@ switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const
FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)
: OpalMediaStream(conn, mediaFormat, sessionID, isSource)
, m_fsSession(conn.GetSession())
, m_readRTP(0, 512)
, m_callOnStart(true)
, m_connection(conn)
, m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE)
{
memset(&m_readFrame, 0, sizeof(m_readFrame));
m_readFrame.codec = m_switchCodec;
m_readFrame.flags = SFF_RAW_RTP;
}
PBoolean FSMediaStream::Open()
{
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(m_fsSession);
if (IsOpen()) {
return true;
}
switch_core_session_t *fsSession = m_connection.GetSession();
switch_channel_t *fsChannel = m_connection.GetChannel();
if (PAssertNULL(fsSession) == NULL || PAssertNULL(fsChannel) == NULL)
return false;
bool isAudio;
if (mediaFormat.GetMediaType() == OpalMediaType::Audio()) {
isAudio = true;
} else if (mediaFormat.GetMediaType() == OpalMediaType::Video()) {
isAudio = false;
} else {
return OpalMediaStream::Open();
return false;
}
m_fsChannel = switch_core_session_get_channel(m_fsSession);
int ptime = mediaFormat.GetOptionInteger(OpalAudioFormat::TxFramesPerPacketOption()) * mediaFormat.GetFrameTime() / mediaFormat.GetTimeUnits();
if (IsSink()) {
m_switchCodec = isAudio ? &tech_pvt->read_codec : &tech_pvt->vid_read_codec;
m_switchTimer = isAudio ? &tech_pvt->read_timer : &tech_pvt->vid_read_timer;
m_switchCodec = isAudio ? &m_connection.m_read_codec : &m_connection.m_vid_read_codec;
m_switchTimer = isAudio ? &m_connection.m_read_timer : &m_connection.m_vid_read_timer;
m_readFrame.codec = m_switchCodec;
m_readFrame.rate = mediaFormat.GetClockRate();
} else {
m_switchCodec = isAudio ? &tech_pvt->write_codec : &tech_pvt->vid_write_codec;
m_switchCodec = isAudio ? &m_connection.m_write_codec : &m_connection.m_vid_write_codec;
}
// The following is performed on two different instances of this object.
if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP
mediaFormat.GetClockRate(), ptime, 1, // Channels
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings
switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
// Could not select a codecs using negotiated frames/packet, so try using default.
if (switch_core_codec_init(m_switchCodec, mediaFormat.GetEncodingName(), NULL, // FMTP
mediaFormat.GetClockRate(), 0, 1, // Channels
SWITCH_CODEC_FLAG_ENCODE | SWITCH_CODEC_FLAG_DECODE, NULL, // Settings
switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
PTRACE(1, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Cannot initialise " << (IsSink()? "read" : "write") << ' '
switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
<< " cannot initialise " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
switch_channel_hangup(m_fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
switch_channel_hangup(fsChannel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION);
return false;
}
PTRACE(2, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " Unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '
PTRACE(2, "mod_opal\t" << switch_channel_get_name(fsChannel)
<< " unsupported ptime of " << ptime << " on " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
}
PTRACE(1, "mod_opal " << switch_channel_get_name(m_fsChannel)<< " initialise " <<
switch_channel_get_name(m_fsChannel) << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
if (IsSink()) {
m_readFrame.rate = mediaFormat.GetClockRate();
if (isAudio) {
switch_core_session_set_read_codec(m_fsSession, m_switchCodec);
switch_core_session_set_read_codec(fsSession, m_switchCodec);
if (switch_core_timer_init(m_switchTimer,
"soft",
m_switchCodec->implementation->microseconds_per_packet / 1000,
m_switchCodec->implementation->samples_per_packet,
switch_core_session_get_pool(m_fsSession)) != SWITCH_STATUS_SUCCESS) {
switch_core_session_get_pool(fsSession)) != SWITCH_STATUS_SUCCESS) {
PTRACE(1, "mod_opal\t" << switch_channel_get_name(fsChannel)
<< " timer init failed on " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
switch_core_codec_destroy(m_switchCodec);
m_switchCodec = NULL;
return false;
}
} else {
switch_core_session_set_video_read_codec(m_fsSession, m_switchCodec);
switch_channel_set_flag(m_fsChannel, CF_VIDEO);
switch_core_session_set_video_read_codec(fsSession, m_switchCodec);
switch_channel_set_flag(fsChannel, CF_VIDEO);
}
} else {
if (isAudio) {
switch_core_session_set_write_codec(m_fsSession, m_switchCodec);
switch_core_session_set_write_codec(fsSession, m_switchCodec);
} else {
switch_core_session_set_video_write_codec(m_fsSession, m_switchCodec);
switch_channel_set_flag(m_fsChannel, CF_VIDEO);
switch_core_session_set_video_write_codec(fsSession, m_switchCodec);
switch_channel_set_flag(fsChannel, CF_VIDEO);
}
}
PTRACE(3, "mod_opal\tSet " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec to << " << mediaFormat << " for connection " << *this);
PTRACE(3, "mod_opal\t" << switch_channel_get_name(fsChannel)
<< " initialised " << (IsSink()? "read" : "write") << ' '
<< mediaFormat.GetMediaType() << " codec " << mediaFormat << " for connection " << *this);
return OpalMediaStream::Open();
}
PBoolean FSMediaStream::Close()
void FSMediaStream::InternalClose()
{
if (!IsOpen())
return false;
/* forget these FS will properly destroy them for us */
m_switchTimer = NULL;
m_switchCodec = NULL;
return OpalMediaStream::Close();
}
......@@ -1256,149 +1144,89 @@ PBoolean FSMediaStream::RequiresPatchThread(OpalMediaStream *) const
return false;
}
bool FSMediaStream::CheckPatchAndLock()
{
if (GetConnection().GetPhase() >= GetConnection().ReleasingPhase || !IsOpen())
return false;
if (LockReadWrite()) {
if (!GetPatch() || !IsOpen()) {
UnlockReadWrite();
return false;
}
return true;
} else {
return false;
}
}
switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)
int FSMediaStream::StartReadWrite(PatchPtr & mediaPatch) const
{
if (!IsOpen()) {
PTRACE(2, "mod_opal\tNot open!");
return -1;
}
if (!m_switchCodec) {
return SWITCH_STATUS_FALSE;
PTRACE(2, "mod_opal\tNo codec!");
return -1;
}
if (m_callOnStart) {
/*
There is a race here... sometimes we make it here and GetPatch() is NULL
if we wait it shows up in 1ms, maybe there is a better way to wait.
*/
while(!GetPatch()) {
if (!m_fsChannel || !switch_channel_up(m_fsChannel)) {
return SWITCH_STATUS_FALSE;
}
switch_cond_next();
}
if (CheckPatchAndLock()) {
GetPatch()->OnStartMediaPatch();
m_callOnStart = false;
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
if (!m_connection.IsChannelReady()) {
PTRACE(2, "mod_opal\tChannel not ready!");
return -1;
}
}
m_readFrame.flags = 0;
/*
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;
// We make referenced copy of pointer so can't be deleted out from under us
mediaPatch = m_mediaPatch;
if (mediaPatch == NULL) {
/*There is a race here... sometimes we make it here and m_mediaPatch is NULL
if we wait it shows up in 1ms, maybe there is a better way to wait. */
PTRACE(3, "mod_opal\tPatch not ready!");
return 1;
}
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;
}
return 0;
}
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 {
switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)
{
PatchPtr mediaPatch;
switch (StartReadWrite(mediaPatch)) {
case -1 :
return SWITCH_STATUS_FALSE;
case 1 :
return SWITCH_STATUS_SUCCESS;
}
if (!m_readRTP.GetPayloadSize()) {
m_readFrame.flags = SFF_CNG;
break;
}
}
if (m_connection.NeedFlushAudio()) {
mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer
m_readRTP.SetPayloadSize(0);
} else {
m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);
if (CheckPatchAndLock()) {
if (!m_switchTimer || !GetPatch()->GetSource().ReadPacket(m_readRTP)) {
UnlockReadWrite();
if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) {
return SWITCH_STATUS_FALSE;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
if (m_switchTimer != NULL) {
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 (m_switchCodec != NULL) {
if (!switch_core_codec_ready(m_switchCodec)) {
PTRACE(2, "mod_opal\tread_frame: codec not ready!");
return SWITCH_STATUS_FALSE;
}
}
//switch_core_timer_step(&m_switchTimer);
m_readFrame.packet = m_readRTP.GetPointer();
m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;
if (m_readFrame.payload == RTP_DataFrame::CN || m_readFrame.payload == RTP_DataFrame::Cisco_CN) {
m_readFrame.flags = SFF_CNG;
}
#if IMPLEMENT_MULTI_FAME_AUDIO
// Repackage frames in incoming packet to agree with what FS expects.
// Not implmented yet!!!!!!!!!
// Cheating and only supporting one frame per packet
#endif
if (m_readFrame.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.datalen = m_readRTP.GetPayloadSize();
m_readFrame.timestamp = m_readRTP.GetTimestamp();
m_readFrame.m = (switch_bool_t) m_readRTP.GetMarker();
m_readFrame.seq = m_readRTP.GetSequenceNumber();
m_readFrame.ssrc = m_readRTP.GetSyncSource();
m_readFrame.codec = m_switchCodec;
}
m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE;
m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType();
m_readFrame.flags = m_readFrame.datalen == 0 ||
m_readFrame.payload == RTP_DataFrame::CN ||
m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0;
*frame = &m_readFrame;
......@@ -1408,69 +1236,35 @@ switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag
switch_status_t FSMediaStream::write_frame(const switch_frame_t *frame, switch_io_flag_t flags)
{
if (!switch_channel_ready(m_fsChannel)) {
PatchPtr mediaPatch;
switch (StartReadWrite(mediaPatch)) {
case -1 :
return SWITCH_STATUS_FALSE;
}
if (m_callOnStart) {
if (CheckPatchAndLock()) {
GetPatch()->OnStartMediaPatch();
m_callOnStart = false;
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
}
if ((frame->flags & SFF_CNG)) {
case 1 :
return SWITCH_STATUS_SUCCESS;
}
if ((frame->flags & SFF_RAW_RTP) != 0) {
RTP_DataFrame rtp((const BYTE *) frame->packet, frame->packetlen, false);
if (CheckPatchAndLock()) {
if (GetPatch()->PushFrame(rtp)) {
UnlockReadWrite();
return SWITCH_STATUS_SUCCESS;
RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false);
return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
}
/* If we reach this code it means a call to an ivr or something else that does not generate timestamps
Its possible that frame->timestamp is set but not guarenteed and is best ignored for the time being.
We are probably relying on the rtp stack to generate the timestamp and ssrc for us at this point.
As a quick hack I am going to keep a sample counter and increment it by frame->samples but it would be
better if we could engage whatever it is in opal that makes it generate the timestamp.
*/
RTP_DataFrame rtp(frame->datalen);
rtp.SetPayloadType(mediaFormat.GetPayloadType());
m_timeStamp += frame->samples;
rtp.SetTimestamp(m_timeStamp);
//rtp.SetTimestamp(frame->timestamp);
//rtp.SetSyncSource(frame->ssrc);
//rtp.SetMarker(frame->m);
memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);
if (CheckPatchAndLock()) {
if (GetPatch()->PushFrame(rtp)) {
UnlockReadWrite();
return SWITCH_STATUS_SUCCESS;
}
UnlockReadWrite();
} else {
return SWITCH_STATUS_FALSE;
}
rtp.SetPayloadType(mediaFormat.GetPayloadType());
/* Not sure what FS is going to give us!
Suspect it depends on the mod on the other side sending it. */
if (frame->timestamp != 0)
timestamp = frame->timestamp;
else if (frame->samples != 0)
timestamp += frame->samples;
else
timestamp += m_switchCodec->implementation->samples_per_packet;
rtp.SetTimestamp(timestamp);
return SWITCH_STATUS_FALSE;
return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;
}
......
......@@ -4,6 +4,7 @@
* Version: MPL 1.1
*
* Copyright (c) 2007 Tuyan Ozipek (tuyanozipek@gmail.com)
* Copyright (c) 2008-2012 Vox Lucida Pty. Ltd. (robertj@voxlucida.com.au)
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
......@@ -43,9 +44,32 @@
#undef strcasecmp
#undef strncasecmp
#if _MSC_VER < 1600
/*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
cap it off, VS2008 barfs on the difference. VS2010 seems OK with it.
Sigh.
*/
#pragma include_alias(<libteletone.h>, <../../libs/libteletone/src/libteletone.h>)
#pragma include_alias(<libteletone_generate.h>, <../../libs/libteletone/src/libteletone_generate.h>)
#pragma include_alias(<libteletone_detect.h>, <../../libs/libteletone/src/libteletone_detect.h>)
#define int8_t signed int8_t
#include <libteletone_generate.h>
#undef int8_t
#endif // End of insanity
#define HAVE_APR
#define uint32_t uint32_t // Avoid conflict in stdint definitions
#include <switch.h>
#undef uint32_t
#include <switch_version.h>
#define MODNAME "mod_opal"
......@@ -53,50 +77,37 @@ class FSEndPoint;
class FSManager;
struct mod_opal_globals {
int trace_level;
char *codec_string;
char *context;
char *dialplan;
};
extern struct mod_opal_globals mod_opal_globals;
class FSProcess:public PLibraryProcess {
class FSProcess : public PLibraryProcess
{
PCLASSINFO(FSProcess, PLibraryProcess);
public:
FSProcess();
~FSProcess();
bool Initialise(switch_loadable_module_interface_t *iface);
FSManager & GetManager() const {
FSManager & GetManager() const
{
return *m_manager;
} protected:
}
protected:
FSManager * m_manager;
};
struct FSListener {
FSListener() {
} PString name;
OpalTransportAddress listenAddress;
PString localUserName;
PString gatekeeper;
};
struct FSListener
{
FSListener() : m_port(H323EndPoint::DefaultTcpSignalPort) { }
class FSCall:public OpalCall {
PCLASSINFO(FSCall, OpalCall);
public:
FSCall(OpalManager & manager);
virtual PBoolean OnSetUp(OpalConnection & connection);
PString m_name;
PIPSocket::Address m_address;
uint16_t m_port;
};
class FSManager:public OpalManager {
class FSManager : public OpalManager
{
PCLASSINFO(FSManager, OpalManager);
public:
......@@ -106,9 +117,11 @@ class FSManager:public OpalManager {
switch_status_t ReadConfig(int reload);
switch_endpoint_interface_t *GetSwitchInterface() const {
return m_FreeSwitch;
} virtual OpalCall *CreateCall(void *userData);
switch_endpoint_interface_t *GetSwitchInterface() const { return m_FreeSwitch; }
const PString & GetContext() const { return m_context; }
const PString & GetDialPlan() const { return m_dialplan; }
const PString & GetCodecPrefs() const { return m_codecPrefs; }
bool GetDisableTranscoding() const { return m_disableTranscoding; }
private:
switch_endpoint_interface_t *m_FreeSwitch;
......@@ -117,85 +130,125 @@ class FSManager:public OpalManager {
IAX2EndPoint *m_iaxep;
FSEndPoint *m_fsep;
PString m_context;
PString m_dialplan;
PString m_codecPrefs;
bool m_disableTranscoding;
PString m_gkAddress;
PString m_gkIdentifer;
PString m_gkInterface;
list < FSListener > m_listeners;
list <FSListener> m_listeners;
};
class FSConnection;
typedef struct {
switch_timer_t read_timer;
switch_codec_t read_codec;
switch_codec_t write_codec;
switch_timer_t vid_read_timer;
switch_codec_t vid_read_codec;
switch_codec_t vid_write_codec;
FSConnection *me;
} opal_private_t;
class FSEndPoint:public OpalLocalEndPoint {
class FSEndPoint : public OpalLocalEndPoint
{
PCLASSINFO(FSEndPoint, OpalLocalEndPoint);
public:
FSEndPoint(FSManager & manager);
virtual bool OnIncomingCall(OpalLocalConnection &);
virtual OpalLocalConnection *CreateConnection(OpalCall & call, void *userData, unsigned options, OpalConnection::StringOptions * stringOptions);
FSManager & GetManager() const { return m_manager; }
protected:
FSManager & m_manager;
};
class FSConnection;
class FSMediaStream : public OpalMediaStream
{
PCLASSINFO(FSMediaStream, OpalMediaStream);
public:
FSMediaStream(
FSConnection & conn,
const OpalMediaFormat & mediaFormat, ///< Media format for stream
unsigned sessionID, ///< Session number for stream
bool isSource ///< Is a source stream
);
virtual PBoolean Open();
virtual PBoolean IsSynchronous() const;
virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;
switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);
switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);
protected:
virtual void InternalClose();
int StartReadWrite(PatchPtr & mediaPatch) const;
private:
bool CheckPatchAndLock();
FSConnection &m_connection;
switch_timer_t *m_switchTimer;
switch_codec_t *m_switchCodec;
switch_frame_t m_readFrame;
RTP_DataFrame m_readRTP;
};
#define DECLARE_CALLBACK0(name) \
static switch_status_t name(switch_core_session_t *session) { \
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name() : SWITCH_STATUS_FALSE; } \
switch_status_t name()
FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
return tech_pvt != NULL ? tech_pvt->name() : SWITCH_STATUS_FALSE; } \
switch_status_t name()
#define DECLARE_CALLBACK1(name, type1, name1) \
static switch_status_t name(switch_core_session_t *session, type1 name1) { \
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1) : SWITCH_STATUS_FALSE; } \
switch_status_t name(type1 name1)
FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
return tech_pvt != NULL ? tech_pvt->name(name1) : SWITCH_STATUS_FALSE; } \
switch_status_t name(type1 name1)
#define DECLARE_CALLBACK3(name, type1, name1, type2, name2, type3, name3) \
static switch_status_t name(switch_core_session_t *session, type1 name1, type2 name2, type3 name3) { \
opal_private_t *tech_pvt = (opal_private_t *) switch_core_session_get_private(session); \
return tech_pvt && tech_pvt->me != NULL ? tech_pvt->me->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \
switch_status_t name(type1 name1, type2 name2, type3 name3)
FSConnection *tech_pvt = (FSConnection *) switch_core_session_get_private(session); \
return tech_pvt != NULL ? tech_pvt->name(name1, name2, name3) : SWITCH_STATUS_FALSE; } \
switch_status_t name(type1 name1, type2 name2, type3 name3)
class FSConnection:public OpalLocalConnection {
class FSConnection : public OpalLocalConnection
{
PCLASSINFO(FSConnection, OpalLocalConnection)
public:
struct outgoing_params {
switch_event_t *var_event;
switch_caller_profile_t *outbound_profile;
switch_core_session_t **new_session;
switch_memory_pool_t **pool;
switch_originate_flag_t flags;
switch_call_cause_t *cancel_cause;
switch_call_cause_t fail_cause;
};
FSConnection(OpalCall & call,
FSEndPoint & endpoint,
void *userData,
unsigned options,
OpalConnection::StringOptions * stringOptions,
switch_caller_profile_t *outbound_profile, switch_core_session_t *fsSession, switch_channel_t *fsChannel);
outgoing_params * params);
virtual bool OnOutgoingSetUp();
virtual bool OnIncoming();
virtual void OnReleased();
virtual PBoolean SetAlerting(const PString & calleeName, PBoolean withMedia);
virtual void OnAlerting();
virtual void OnEstablished();
virtual OpalMediaStream *CreateMediaStream(const OpalMediaFormat &, unsigned, PBoolean);
virtual PBoolean OnOpenMediaStream(OpalMediaStream & stream);
virtual void OnPatchMediaStream(PBoolean isSource, OpalMediaPatch & patch);
virtual OpalMediaFormatList GetMediaFormats() const;
virtual PBoolean SendUserInputTone(char tone, unsigned duration);
virtual PBoolean SendUserInputString(const PString & value);
void SetCodecs();
DECLARE_CALLBACK0(on_init);
DECLARE_CALLBACK0(on_destroy);
DECLARE_CALLBACK0(on_routing);
DECLARE_CALLBACK0(on_execute);
DECLARE_CALLBACK0(on_hangup);
DECLARE_CALLBACK0(on_exchange_media);
DECLARE_CALLBACK0(on_soft_execute);
......@@ -210,49 +263,56 @@ class FSConnection:public OpalLocalConnection {
DECLARE_CALLBACK3(read_video_frame, switch_frame_t **, frame, switch_io_flag_t, flag, int, stream_id);
DECLARE_CALLBACK3(write_video_frame, switch_frame_t *, frame, switch_io_flag_t, flag, int, stream_id);
__inline switch_core_session_t *GetSession() const
{
return m_fsSession;
}
__inline switch_channel_t *GetChannel() const
{
return m_fsChannel;
}
bool IsChannelReady() const
{
return m_fsChannel != NULL && switch_channel_ready(m_fsChannel);
}
bool NeedFlushAudio()
{
if (!m_flushAudio)
return false;
m_flushAudio = false;
return true;
}
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);
switch_core_session_t *GetSession() const {
return m_fsSession;
} private:
FSEndPoint & m_endpoint;
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;
class FSMediaStream:public OpalMediaStream {
PCLASSINFO(FSMediaStream, OpalMediaStream);
public:
FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, ///< Media format for stream
unsigned sessionID, ///< Session number for stream
bool isSource ///< Is a source stream
);
virtual PBoolean Open();
virtual PBoolean Close();
virtual PBoolean IsSynchronous() const;
virtual PBoolean RequiresPatchThread(OpalMediaStream *) const;
switch_timer_t m_vid_read_timer;
switch_codec_t m_vid_read_codec;
switch_codec_t m_vid_write_codec;
switch_status_t read_frame(switch_frame_t **frame, switch_io_flag_t flags);
switch_status_t write_frame(const switch_frame_t *frame, switch_io_flag_t flags);
bool m_flushAudio;
private:
switch_core_session_t *m_fsSession;
switch_channel_t *m_fsChannel;
switch_timer_t *m_switchTimer;
switch_codec_t *m_switchCodec;
switch_frame_t m_readFrame;
unsigned char m_buf[SWITCH_RECOMMENDED_BUFFER_SIZE];
RTP_DataFrame m_readRTP;
bool m_callOnStart;
uint32_t m_timeStamp;
bool CheckPatchAndLock();
friend PBoolean FSMediaStream::Open();
};
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论