#include "mod_opal.h"\r
#include <opal/patch.h>\r
#include <rtp/rtp.h>\r
+#if PTLIB_CHECK_VERSION(2,11,1)\r
+#include <rtp/rtp_session.h>\r
+#endif\r
#include <h323/h323pdu.h>\r
#include <h323/gkclient.h>\r
\r
\r
static PConstString const ModuleName("opal");\r
static char const ConfigFile[] = "opal.conf";\r
+#define FS_PREFIX "fs"\r
\r
\r
static switch_io_routines_t opalfs_io_routines = {\r
}\r
}\r
\r
- AddRouteEntry("h323:.* = local:<da>"); // config option for direct routing\r
- AddRouteEntry("iax2:.* = local:<da>"); // config option for direct routing\r
- AddRouteEntry("local:.* = h323:<da>"); // config option for direct routing\r
+ AddRouteEntry("h323:.* = "FS_PREFIX":<da>"); // config option for direct routing\r
+ AddRouteEntry("iax2:.* = "FS_PREFIX":<da>"); // config option for direct routing\r
+ AddRouteEntry(FS_PREFIX":.* = h323:<da>"); // config option for direct routing\r
\r
// Make sure all known codecs are instantiated,\r
// these are ones we know how to translate into H.323 capabilities\r
}\r
#endif // IMPLEMENT_MULTI_FAME_AUDIO\r
\r
+ OpalMediaFormat t38 = OpalT38;\r
+ t38.SetOptionBoolean("UDPTL-Raw-Mode", true);\r
+ OpalMediaFormat::SetRegisteredMediaFormat(t38);\r
+\r
if (!m_gkAddress.IsEmpty()) {\r
if (m_h323ep->UseGatekeeper(m_gkAddress, m_gkIdentifer, m_gkInterface))\r
switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Started gatekeeper: %s\n",\r
params.cancel_cause = cancel_cause;\r
params.fail_cause = SWITCH_CAUSE_INVALID_NUMBER_FORMAT;\r
\r
- if (opal_process->GetManager().SetUpCall("local:", outbound_profile->destination_number, ¶ms) != NULL)\r
+ if (opal_process->GetManager().SetUpCall(FS_PREFIX":", outbound_profile->destination_number, ¶ms) != NULL)\r
return SWITCH_CAUSE_SUCCESS;\r
\r
if (*new_session != NULL)\r
///////////////////////////////////////////////////////////////////////\r
\r
FSEndPoint::FSEndPoint(FSManager & manager)\r
- : OpalLocalEndPoint(manager)\r
+ : OpalLocalEndPoint(manager, FS_PREFIX)\r
, m_manager(manager)\r
{\r
PTRACE(4, "mod_opal\tFSEndPoint created.");\r
, m_fsSession(NULL)\r
, m_fsChannel(NULL)\r
, m_flushAudio(false)\r
+ , m_udptl(false)\r
{\r
memset(&m_read_timer, 0, sizeof(m_read_timer));\r
memset(&m_read_codec, 0, sizeof(m_read_codec));\r
memset(&m_vid_read_timer, 0, sizeof(m_vid_read_timer));\r
memset(&m_vid_read_codec, 0, sizeof(m_vid_read_codec));\r
memset(&m_vid_write_codec, 0, sizeof(m_vid_write_codec));\r
+ memset(&m_dummy_frame, 0, sizeof(m_dummy_frame));\r
+ m_dummy_frame.flags = SFF_CNG;\r
\r
if (params != NULL) {\r
// If we fail, this is the cause\r
{\r
PTRACE(3, "mod_opal\tFS on_destroy for connection " << *this);\r
\r
- m_fsChannel = NULL; // Will be destoyed by FS, so don't use it any more.\r
+ m_fsChannel = NULL; // Will be destroyed by FS, so don't use it any more.\r
\r
switch_core_codec_destroy(&m_read_codec);\r
switch_core_codec_destroy(&m_write_codec);\r
\r
switch_status_t FSConnection::on_soft_execute()\r
{\r
- PTRACE(4, "mod_opal\tTransmit on connection " << *this);\r
+ PTRACE(4, "mod_opal\tSoft execute on connection " << *this);\r
return SWITCH_STATUS_SUCCESS;\r
}\r
\r
case SWITCH_SIG_KILL:\r
m_rxAudioOpened.Signal();\r
m_txAudioOpened.Signal();\r
- PTRACE(4, "mod_opal\tSignal channel KILL on connection " << *this);\r
+ PTRACE(4, "mod_opal\tSignal KILL received on connection " << *this);\r
break;\r
- case SWITCH_SIG_XFER:\r
+\r
case SWITCH_SIG_BREAK:\r
+ PTRACE(4, "mod_opal\tSignal BREAK received on connection " << *this);\r
+ break;\r
+\r
default:\r
- PTRACE(4, "mod_opal\tSignal channel " << sig << " on connection " << *this);\r
+ PTRACE(4, "mod_opal\tSignal " << sig << " received on connection " << *this);\r
break;\r
}\r
\r
\r
case SWITCH_MESSAGE_INDICATE_UDPTL_MODE:\r
PTRACE(2, "mod_opal\tSWITCH_MESSAGE_INDICATE_UDPTL_MODE");\r
+ m_udptl = true;\r
break;\r
#endif // HAVE_T38\r
\r
\r
void FSConnection::OnSwitchedT38(bool toT38, bool success)\r
{\r
- if (!toT38 || !success || !IndicateSwitchedT38())\r
- AbortT38();\r
+ if (toT38 && success && IndicateSwitchedT38()) {\r
+ PTRACE(3, "mod_opal\tMode change request to T.38 succeeded");\r
+ }\r
+ else {\r
+ AbortT38();\r
+ }\r
}\r
\r
\r
void FSConnection::OnSwitchingT38(bool toT38)\r
{\r
- if (!toT38 || !IndicateSwitchedT38())\r
+ if (toT38 && IndicateSwitchedT38()) {\r
+ PTRACE(3, "mod_opal\tMode change request to T.38 started");\r
+ }\r
+ else {\r
AbortT38();\r
+ }\r
}\r
\r
\r
bool FSConnection::IndicateSwitchedT38()\r
{\r
PSafePtr<OpalConnection> other = GetOtherPartyConnection();\r
- if (other == NULL)\r
+ if (other == NULL) {\r
+ PTRACE(3, "mod_opal\tCan't change to T.38, no other connection");\r
return false;\r
+ }\r
\r
OpalMediaFormatList otherFormats = other->GetMediaFormats();\r
OpalMediaFormatList::const_iterator t38 = otherFormats.FindFormat(OpalT38);\r
- if (t38 == otherFormats.end())\r
+ if (t38 == otherFormats.end()) {\r
+ PTRACE(3, "mod_opal\tCan't change to T.38, no remote capability");\r
return false;\r
+ }\r
\r
SetT38OptionsFromMediaFormat(*t38, "t38_options");\r
\r
\r
switch_channel_execute_on(m_fsChannel, "opal_execute_on_t38");\r
switch_channel_api_on(m_fsChannel, "opal_api_on_t38");\r
- PTRACE(3, "mod_opal\tMode change request to T.38 succeeded");\r
return true;\r
}\r
#endif // HAVE_T38\r
\r
switch_status_t FSConnection::read_audio_frame(switch_frame_t **frame, switch_io_flag_t flags, int stream_id)\r
{\r
- // Avoid all the channel closing and re-opening upsetting FS\r
- if (ownerCall.IsSwitchingT38())\r
- return SWITCH_STATUS_SUCCESS;\r
-\r
- return read_frame((flags&SFF_UDPTL_PACKET) ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
+ return read_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
}\r
\r
\r
switch_status_t FSConnection::write_audio_frame(switch_frame_t *frame, switch_io_flag_t flags, int stream_id)\r
{\r
- // Avoid all the channel closing and re-opening upsetting FS\r
- if (ownerCall.IsSwitchingT38())\r
- return SWITCH_STATUS_SUCCESS;\r
-\r
- return write_frame((flags&SFF_UDPTL_PACKET) ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
+ return write_frame(m_udptl ? OpalMediaType::Fax() : OpalMediaType::Audio(), frame, flags);\r
}\r
\r
\r
\r
switch_status_t FSConnection::read_frame(const OpalMediaType & mediaType, switch_frame_t **frame, switch_io_flag_t flags)\r
{\r
- PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));\r
- if (stream != NULL)\r
- return stream->read_frame(frame, flags);\r
+ if (!ownerCall.IsSwitchingT38()) {\r
+ PSafePtr <FSMediaStream> stream = PSafePtrCast <OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, false));\r
+ if (stream != NULL)\r
+ return stream->read_frame(frame, flags);\r
+\r
+ PTRACE(2, "mod_opal\tNo stream for read of " << mediaType);\r
+ }\r
\r
- PTRACE(2, "mod_opal\tNo stream for read of " << mediaType);\r
+ // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS\r
+ *frame = &m_dummy_frame;\r
return SWITCH_STATUS_SUCCESS;\r
}\r
\r
\r
switch_status_t FSConnection::write_frame(const OpalMediaType & mediaType, const switch_frame_t *frame, switch_io_flag_t flags)\r
{\r
+ // Avoid all the channel closing and re-opening, especially with faxa switching, upsetting FS\r
+ if (ownerCall.IsSwitchingT38())\r
+ return SWITCH_STATUS_SUCCESS;\r
+\r
PSafePtr <FSMediaStream> stream = PSafePtrCast<OpalMediaStream, FSMediaStream>(GetMediaStream(mediaType, true));\r
if (stream != NULL)\r
return stream->write_frame(frame, flags);\r
FSMediaStream::FSMediaStream(FSConnection & conn, const OpalMediaFormat & mediaFormat, unsigned sessionID, bool isSource)\r
: OpalMediaStream(conn, mediaFormat, sessionID, isSource)\r
, m_connection(conn)\r
+ , m_switchTimer(NULL)\r
+ , m_switchCodec(NULL)\r
, m_readRTP(0, SWITCH_RECOMMENDED_BUFFER_SIZE)\r
{\r
memset(&m_readFrame, 0, sizeof(m_readFrame));\r
isAudio = false;\r
#if HAVE_T38\r
else if (mediaType == OpalMediaType::Fax()) {\r
- m_readFrame.flags = SFF_UDPTL_PACKET;\r
- return true;\r
+ m_readFrame.flags = SFF_UDPTL_PACKET|SFF_PROXY_PACKET;\r
+ return OpalMediaStream::Open();\r
}\r
#endif\r
else {\r
return -1;\r
}\r
\r
- if (!m_switchCodec) {\r
- PTRACE(1, "mod_opal\tNo codec!");\r
- return -1;\r
- }\r
-\r
if (!m_connection.IsChannelReady()) {\r
PTRACE(1, "mod_opal\tChannel not ready!");\r
return -1;\r
\r
switch_status_t FSMediaStream::read_frame(switch_frame_t **frame, switch_io_flag_t flags)\r
{\r
+ *frame = &m_readFrame;\r
+ m_readFrame.flags |= SFF_CNG;\r
+\r
PatchPtr mediaPatch;\r
switch (StartReadWrite(mediaPatch)) {\r
case -1 :\r
mediaPatch->GetSource().EnableJitterBuffer(); // This flushes data and resets jitter buffer\r
m_readRTP.SetPayloadSize(0);\r
} else {\r
- m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);\r
+ if (m_switchCodec != NULL) {\r
+ m_readRTP.SetTimestamp(m_readFrame.timestamp + m_switchCodec->implementation->samples_per_packet);\r
+ }\r
\r
if (!mediaPatch->GetSource().ReadPacket(m_readRTP)) {\r
PTRACE(1, "mod_opal\tread_frame: no source data!");\r
}\r
}\r
\r
- *frame = &m_readFrame;\r
-\r
- m_readFrame.packet = m_readRTP.GetPointer();\r
- m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readFrame.datalen;\r
+ if (switch_test_flag(&m_readFrame, SFF_UDPTL_PACKET)) {\r
+ m_readFrame.flags &= ~SFF_CNG;\r
+ m_readFrame.packet = m_readRTP.GetPayloadPtr();\r
+ m_readFrame.packetlen = m_readRTP.GetPayloadSize();\r
+ return SWITCH_STATUS_SUCCESS;\r
+ }\r
\r
- if ((m_readFrame.flags & (SFF_UDPTL_PACKET|SFF_RAW_RTP)) != 0)\r
+ if (switch_test_flag(&m_readFrame, SFF_RAW_RTP)) {\r
+ m_readFrame.flags &= ~SFF_CNG;\r
+ m_readFrame.packet = m_readRTP.GetPointer();\r
+ m_readFrame.packetlen = m_readRTP.GetHeaderSize() + m_readRTP.GetPayloadSize();\r
return SWITCH_STATUS_SUCCESS;\r
+ }\r
\r
#if IMPLEMENT_MULTI_FAME_AUDIO\r
// Repackage frames in incoming packet to agree with what FS expects.\r
m_readFrame.ssrc = m_readRTP.GetSyncSource();\r
m_readFrame.m = m_readRTP.GetMarker() ? SWITCH_TRUE : SWITCH_FALSE;\r
m_readFrame.payload = (switch_payload_t)m_readRTP.GetPayloadType();\r
- m_readFrame.flags = m_readFrame.datalen == 0 ||\r
- m_readFrame.payload == RTP_DataFrame::CN ||\r
- m_readFrame.payload == RTP_DataFrame::Cisco_CN ? SFF_CNG : 0;\r
+\r
+ if (m_readFrame.datalen > 0 &&\r
+ m_readFrame.payload != RTP_DataFrame::CN &&\r
+ m_readFrame.payload != RTP_DataFrame::Cisco_CN) {\r
+ m_readFrame.flags &= ~SFF_CNG;\r
+ }\r
\r
return SWITCH_STATUS_SUCCESS;\r
}\r
return SWITCH_STATUS_SUCCESS;\r
}\r
\r
- if ((frame->flags & (SFF_UDPTL_PACKET|SFF_RAW_RTP)) != 0) {\r
- RTP_DataFrame rtp((const BYTE *)frame->packet, frame->packetlen, false);\r
- return mediaPatch->PushFrame(rtp) ? SWITCH_STATUS_SUCCESS : SWITCH_STATUS_FALSE;\r
+ RTP_DataFrame rtp;\r
+ if (switch_test_flag(frame, SFF_RAW_RTP)) {\r
+ rtp = RTP_DataFrame((const BYTE *)frame->packet, frame->packetlen, false);\r
+ }\r
+ else if (switch_test_flag(frame, SFF_UDPTL_PACKET)) {\r
+ rtp.SetPayloadSize(frame->packetlen);\r
+ memcpy(rtp.GetPayloadPtr(), frame->packet, frame->packetlen);\r
+ }\r
+ else {\r
+ rtp.SetPayloadSize(frame->datalen);\r
+ memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);\r
+\r
+ rtp.SetPayloadType(mediaFormat.GetPayloadType());\r
+\r
+ /* Not sure what FS is going to give us!\r
+ Suspect it depends on the mod on the other side sending it. */\r
+ if (frame->timestamp != 0)\r
+ timestamp = frame->timestamp;\r
+ else if (frame->samples != 0)\r
+ timestamp += frame->samples;\r
+ else if (m_switchCodec != NULL)\r
+ timestamp += m_switchCodec->implementation->samples_per_packet;\r
+ rtp.SetTimestamp(timestamp);\r
}\r
-\r
- RTP_DataFrame rtp(frame->datalen);\r
- memcpy(rtp.GetPayloadPtr(), frame->data, frame->datalen);\r
-\r
- rtp.SetPayloadType(mediaFormat.GetPayloadType());\r
-\r
- /* Not sure what FS is going to give us!\r
- Suspect it depends on the mod on the other side sending it. */\r
- if (frame->timestamp != 0)\r
- timestamp = frame->timestamp;\r
- else if (frame->samples != 0)\r
- timestamp += frame->samples;\r
- else\r
- timestamp += m_switchCodec->implementation->samples_per_packet;\r
- rtp.SetTimestamp(timestamp);\r
\r
if (mediaPatch->PushFrame(rtp))\r
return SWITCH_STATUS_SUCCESS;\r