--- channels/h323/caps_h323.cxx.orig 2008-04-03 22:28:37.000000000 +0400 +++ channels/h323/caps_h323.cxx 2008-04-04 01:03:54.000000000 +0400 @@ -1,8 +1,14 @@ #include #include #include -#include "ast_h323.h" +#ifdef T38_SUPPORT +#include +#include +#include "ast_t38.h" +#endif #include "caps_h323.h" +#include "ast_h323.h" +#include "asterisk/logger.h" #define DEFINE_G711_CAPABILITY(cls, code, capName) \ class cls : public AST_G711Capability { \ @@ -237,3 +243,55 @@ { return NULL; } + + +#ifdef T38_SUPPORT +/* T.38 Caps */ +AST_T38Capability::AST_T38Capability(TransportMode mode) + : H323_T38Capability(mode) +{ +} + +PObject *AST_T38Capability::Clone() const +{ + return new AST_T38Capability(*this); +} + +H323Channel *AST_T38Capability::CreateChannel(H323Connection &connection, + H323Channel::Directions direction, unsigned sessionID, + const H245_H2250LogicalChannelParameters *param) const +{ + PTRACE(1, "AST_T38\tCreateChannel, sessionID=" << sessionID << " direction =" << direction); + return new AST_T38Channel(connection, *this, direction, sessionID, mode); +} + +BOOL AST_T38Capability::OnSendingPDU(H245_DataProtocolCapability &proto, + H245_T38FaxProfile &profile) const +{ + PTRACE(1, "AST_T38\tOnSendingPDU(proto, profile)"); + if (mode == e_UDP) { + proto.SetTag(H245_DataProtocolCapability::e_udp); + profile.m_t38FaxRateManagement.SetTag(H245_T38FaxRateManagement::e_transferredTCF); + profile.IncludeOptionalField(H245_T38FaxProfile::e_t38FaxUdpOptions); + profile.m_t38FaxUdpOptions.IncludeOptionalField(H245_T38FaxUdpOptions::e_t38FaxMaxBuffer); + profile.m_t38FaxUdpOptions.m_t38FaxMaxBuffer = 200; + profile.m_t38FaxUdpOptions.IncludeOptionalField(H245_T38FaxUdpOptions::e_t38FaxMaxDatagram); + profile.m_t38FaxUdpOptions.m_t38FaxMaxDatagram = 72; + profile.m_t38FaxUdpOptions.m_t38FaxUdpEC.SetTag(H245_T38FaxUdpOptions_t38FaxUdpEC::e_t38UDPRedundancy); + } else { + proto.SetTag(H245_DataProtocolCapability::e_tcp); + profile.IncludeOptionalField(H245_T38FaxProfile::e_t38FaxTcpOptions); + profile.m_t38FaxTcpOptions.m_t38TCPBidirectionalMode = mode == e_SingleTCP; + } + + return TRUE; +} + +OpalT38Protocol *MyH323EndPoint::CreateT38ProtocolHandler(const H323Connection & conn) const +{ + OpalT38Protocol *proto = new OpalT38Protocol(); + ast_log(LOG_DEBUG, "OpalT38Protocol is %p\n", proto); + return proto; +} +#endif + --- channels/h323/ast_h323.cxx.orig 2008-04-03 22:28:24.000000000 +0400 +++ channels/h323/ast_h323.cxx 2008-04-07 02:19:06.000000000 +0400 @@ -46,6 +46,11 @@ #include "h45011.h" #include "h450pdu.h" #endif +#ifdef T38_SUPPORT +#include +#include +#include "ast_t38.h" +#endif #ifdef __cplusplus extern "C" { @@ -58,9 +63,9 @@ #endif #include "chan_h323.h" -#include "ast_h323.h" #include "cisco-h225.h" #include "caps_h323.h" +#include "ast_h323.h" /* PWlib Required Components */ #define MAJOR_VERSION 1 @@ -229,6 +234,10 @@ /* Reset call setup timeout to some more reasonable value than 1 minute */ signallingChannelCallTimeout = PTimeInterval(0, 0, 10); /* 10 minutes */ + SetSilenceDetectionMode(H323AudioCodec::NoSilenceDetection); + clearCallOnRoundTripFail = TRUE; + roundTripDelayRate = PTimeInterval(0,0,120); + registrationTimeToLive = PTimeInterval(0, 600); } /** The fullAddress parameter is used directly in the MakeCall method so @@ -550,6 +559,10 @@ #ifdef TUNNELLING tunnelOptions = remoteTunnelOptions = 0; #endif +#ifdef T38_SUPPORT + t38mode = FALSE; +#endif + doH245inSETUP = FALSE; if (h323debug) { cout << " == New H.323 Connection created." << endl; } @@ -1843,6 +1856,10 @@ localCapabilities.RemoveAll(); +#ifdef T38_SUPPORT + lastcap = localCapabilities.SetCapability(0, 0, t38Cap = new AST_T38Capability(H323_T38Capability::e_UDP)); +#endif + if (h323debug) { cout << "Setting capabilities to " << ast_getformatname_multiple(caps_str, sizeof(caps_str), cap) << endl; ast_codec_pref_string(prefs, caps_str, sizeof(caps_str)); @@ -2068,6 +2085,38 @@ return FALSE; } +#ifdef T38_SUPPORT +void MyH323Connection::OnModeChanged(const H245_ModeDescription & newMode) +{ + t38mode = FALSE; + for (PINDEX i = 0; i < newMode.GetSize(); i++) { + if (localCapabilities.FindCapability(newMode[i]) == t38Cap) + t38mode = TRUE; + } + + if (on_modechange) + on_modechange(GetCallReference(), (const char *)callToken, + t38mode); + H323Connection::OnModeChanged(newMode); +} +void MyH323Connection::OnAcceptModeChange(const H245_RequestModeAck & pdu) +{ + if (!t38ModeChangeCapabilities.IsEmpty() && on_modechange) { + t38mode = TRUE; + on_modechange(GetCallReference(), (const char *)callToken, t38mode); + } + H323Connection::OnAcceptModeChange(pdu); + +} + +void MyH323Connection::OnSelectLogicalChannels() +{ + if (h323debug) cout << "My OnSelectLogicalChan\n" << endl; + if (!t38mode) H323Connection::OnSelectLogicalChannels(); +} + +#endif + /** IMPLEMENTATION OF C FUNCTIONS */ @@ -2126,7 +2175,13 @@ rfc2833_cb dtmffunc, hangup_cb hangupfunc, setcapabilities_cb capabilityfunc, - setpeercapabilities_cb peercapabilityfunc) + setpeercapabilities_cb peercapabilityfunc +#ifdef T38_SUPPORT + , on_udptl_cb udptlfunc, + start_udptl_cb udptlstartfunc, + chmode_cb changemodefunc +#endif +) { on_incoming_call = ifunc; on_outgoing_call = sfunc; @@ -2142,8 +2197,26 @@ on_hangup = hangupfunc; on_setcapabilities = capabilityfunc; on_setpeercapabilities = peercapabilityfunc; +#ifdef T38_SUPPORT + on_external_udptl_create = udptlfunc; + on_start_udptl_channel = udptlstartfunc; + on_modechange = changemodefunc; +#endif } +#ifdef T38_SUPPORT +void h323_mode_change(const char* call_token) { + const PString currentToken(call_token); + MyH323Connection *connection; + if (!h323_end_point_exist()) return; + connection = (MyH323Connection *)endPoint->FindConnectionWithLock(currentToken); + if (connection) { + connection->RequestModeChangeT38(); + connection->Unlock(); + } +} +#endif + /** * Add capability to the capability table of the end point. */ @@ -2334,6 +2407,10 @@ if (!h323_end_point_exist()) { return 1; } + + if (h323debug) { + cout << "\tClearCall " << call_token << " cause " << cause << endl; + } if (cause) { r = H323TranslateToCallEndReason((Q931::CauseValues)(cause), dummy); --- channels/h323/chan_h323.h.orig 2008-04-03 21:31:19.000000000 +0400 +++ channels/h323/chan_h323.h 2008-04-05 04:10:27.000000000 +0400 @@ -188,6 +188,15 @@ typedef void (*setpeercapabilities_cb)(unsigned, const char *, int, struct ast_codec_pref *); extern setpeercapabilities_cb on_setpeercapabilities; +#ifdef T38_SUPPORT +typedef void (*chmode_cb)(unsigned, const char *, int); +extern chmode_cb on_modechange; +typedef rtp_info_t *(*on_udptl_cb)(unsigned, const char *); +extern on_udptl_cb on_external_udptl_create; +typedef void (*start_udptl_cb)(unsigned int, const char *, int, const char *, int); +extern start_udptl_cb on_start_udptl_channel; +#endif + /* debug flag */ extern int h323debug; @@ -223,7 +232,16 @@ rfc2833_cb, hangup_cb, setcapabilities_cb, - setpeercapabilities_cb); + setpeercapabilities_cb +#ifdef T38_SUPPORT + ,on_udptl_cb, + start_udptl_cb, + chmode_cb +#endif +); +#ifdef T38_SUPPORT + void h323_mode_change(const char*); +#endif int h323_set_capabilities(const char *, int, int, struct ast_codec_pref *, int); int h323_set_alias(struct oh323_alias *); int h323_set_gk(int, char *, char *); --- channels/h323/ast_h323.h.orig 2008-04-03 21:31:19.000000000 +0400 +++ channels/h323/ast_h323.h 2008-04-04 00:09:48.000000000 +0400 @@ -51,6 +51,9 @@ void SetEndpointTypeInfo( H225_EndpointType & info ) const; void SetGateway(void); PStringArray SupportedPrefixes; +#ifdef T38_SUPPORT + OpalT38Protocol* CreateT38ProtocolHandler(const H323Connection & conn) const; +#endif }; class MyH323Connection : public H323Connection @@ -95,6 +98,11 @@ virtual BOOL HandleSignalPDU(H323SignalPDU &pdu); BOOL EmbedTunneledInfo(H323SignalPDU &pdu); #endif +#ifdef T38_SUPPORT + void OnModeChanged(const H245_ModeDescription & newMode); + void OnAcceptModeChange(const H245_RequestModeAck & pdu); + void OnSelectLogicalChannels(); +#endif PString sourceAliases; PString destAliases; @@ -119,6 +127,10 @@ RTP_DataFrame::PayloadTypes dtmfCodec; int dtmfMode; +#ifdef T38_SUPPORT + int t38mode; + AST_T38Capability *t38Cap; +#endif }; class MyH323_ExternalRTPChannel : public H323_ExternalRTPChannel --- channels/h323/ast_t38.cxx.orig 2008-04-08 03:52:53.000000000 +0400 +++ channels/h323/ast_t38.cxx 2008-04-04 01:03:57.000000000 +0400 @@ -0,0 +1,317 @@ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif +#include "asterisk/compat.h" +#include "asterisk/logger.h" +#include "asterisk/channel.h" +#include "asterisk/astobj.h" +#ifdef __cplusplus +} +#endif + +#include "chan_h323.h" +#include "ast_t38.h" + +/* extern iostream *logstream; + +#define cout (*logstream) */ + +BOOL AST_T38Channel::DetermineLocalMediaAddress(H323Connection &connection) +{ + struct rtp_info *info; + + info = on_external_udptl_create(connection.GetCallReference(), (const char *)connection.GetCallToken()); + if (!info) { + cout << "\tERROR: on_external_udptl_create failure" << endl; + return FALSE; + } + localIpAddr = info->addr; + localPort = info->port; + /* clean up allocated memory */ + free(info); + + return TRUE; +} + +AST_T38Channel::AST_T38Channel(H323Connection & conn, + const H323Capability & cap, + Directions dir, + unsigned id, + H323_T38Capability::TransportMode mode) +#ifdef T38_RTPCHANNEL + : H323_ExternalRTPChannel(conn, cap, dir, id) +#else + : H323UnidirectionalChannel(conn, cap, dir) +#endif +{ + if (DetermineLocalMediaAddress(connection)) { + externalMediaAddress = H323TransportAddress(localIpAddr, localPort); + externalMediaControlAddress = H323TransportAddress(localIpAddr, localPort+1); + PTRACE(1, "AST_T38\tExternal Media Address is set to " << externalMediaAddress); + } +#ifndef T38_RTPCHANNEL + sessionID = id; + separateReverseChannel = TRUE; +#endif + t38mode = mode; +} + + +AST_T38Channel::~AST_T38Channel() +{ +} + + +void AST_T38Channel::CleanUpOnTermination() +{ + if (terminating) + return; + + PTRACE(3, "LogChan\tCleaning up data channel " << number); + +#ifdef T38_RTPCHANNEL + H323_ExternalRTPChannel::CleanUpOnTermination(); +#else + H323UnidirectionalChannel::CleanUpOnTermination(); +#endif +} + + +#ifndef T38_RTPCHANNEL +unsigned AST_T38Channel::GetSessionID() const +{ + return sessionID; +} + + +BOOL AST_T38Channel::OnSendingPDU(H245_OpenLogicalChannel & open) const +{ + PTRACE(3, "LogChan\tOnSendingPDU for channel: " << number); + + open.m_forwardLogicalChannelNumber = (unsigned)number; + + open.m_forwardLogicalChannelParameters.m_multiplexParameters.SetTag( + H245_OpenLogicalChannel_forwardLogicalChannelParameters_multiplexParameters + ::e_h2250LogicalChannelParameters); + H245_H2250LogicalChannelParameters & fparam = open.m_forwardLogicalChannelParameters.m_multiplexParameters; + fparam.m_sessionID = GetSessionID(); + fparam.IncludeOptionalField(H245_H2250LogicalChannelParameters::e_mediaControlChannel); + externalMediaControlAddress.SetPDU(fparam.m_mediaControlChannel); + + if (separateReverseChannel) + return TRUE; + + open.IncludeOptionalField(H245_OpenLogicalChannel::e_reverseLogicalChannelParameters); + open.m_reverseLogicalChannelParameters.IncludeOptionalField( + H245_OpenLogicalChannel_reverseLogicalChannelParameters::e_multiplexParameters); + open.m_reverseLogicalChannelParameters.m_multiplexParameters.SetTag( + H245_OpenLogicalChannel_reverseLogicalChannelParameters_multiplexParameters + ::e_h2250LogicalChannelParameters); + H245_H2250LogicalChannelParameters & rparam = open.m_reverseLogicalChannelParameters.m_multiplexParameters; + rparam.m_sessionID = GetSessionID(); + + return capability->OnSendingPDU(open.m_reverseLogicalChannelParameters.m_dataType); +} + + +void AST_T38Channel::OnSendOpenAck(const H245_OpenLogicalChannel & /*open*/, + H245_OpenLogicalChannelAck & ack) const +{ + PTRACE(3, "LogChan\tOnSendOpenAck for channel: " << number); + + H245_H2250LogicalChannelAckParameters * param; + + if (separateReverseChannel) { + ack.IncludeOptionalField(H245_OpenLogicalChannelAck::e_forwardMultiplexAckParameters); + ack.m_forwardMultiplexAckParameters.SetTag( + H245_OpenLogicalChannelAck_forwardMultiplexAckParameters::e_h2250LogicalChannelAckParameters); + param = (H245_H2250LogicalChannelAckParameters*)&ack.m_forwardMultiplexAckParameters.GetObject(); + } + else { + ack.IncludeOptionalField(H245_OpenLogicalChannelAck::e_reverseLogicalChannelParameters); + ack.m_reverseLogicalChannelParameters.m_multiplexParameters.SetTag( + H245_OpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters + ::e_h2250LogicalChannelParameters); + param = (H245_H2250LogicalChannelAckParameters*) + &ack.m_reverseLogicalChannelParameters.m_multiplexParameters.GetObject(); + } + + unsigned session = GetSessionID(); + if (session != 0) { + param->IncludeOptionalField(H245_H2250LogicalChannelAckParameters::e_sessionID); + param->m_sessionID = GetSessionID(); + } + + param->IncludeOptionalField(H245_H2250LogicalChannelAckParameters::e_mediaControlChannel); + PTRACE(1, "AST_T38\tUpdating PDU with external media control address " << externalMediaControlAddress); + externalMediaControlAddress.SetPDU(param->m_mediaControlChannel); + + param->IncludeOptionalField(H245_H2250LogicalChannelAckParameters::e_mediaChannel); + PTRACE(1, "AST_T38\tUpdating PDU with external media address " << externalMediaAddress); + externalMediaAddress.SetPDU(param->m_mediaChannel); +} + + +BOOL AST_T38Channel::OnReceivedPDU(const H245_OpenLogicalChannel & open, + unsigned & errorCode) +{ + number = H323ChannelNumber(open.m_forwardLogicalChannelNumber, TRUE); + + PTRACE(3, "LogChan\tOnReceivedPDU for data channel: " << number); + + if (separateReverseChannel && + open.HasOptionalField(H245_OpenLogicalChannel::e_reverseLogicalChannelParameters)) { + errorCode = H245_OpenLogicalChannelReject_cause::e_unsuitableReverseParameters; + PTRACE(2, "LogChan\tOnReceivedPDU has unexpected reverse parameters"); + return FALSE; + } + + if (!capability->OnReceivedPDU(open.m_forwardLogicalChannelParameters.m_dataType, receiver)) { + PTRACE(1, "H323RTP\tData type not supported"); + errorCode = H245_OpenLogicalChannelReject_cause::e_dataTypeNotSupported; + return FALSE; + } + + return TRUE; +} + + +BOOL AST_T38Channel::OnReceivedAckPDU(const H245_OpenLogicalChannelAck & ack) +{ + PTRACE(3, "LogChan\tOnReceivedAckPDU"); + + const H245_TransportAddress *address; + const H245_TransportAddress *controlAddress = NULL; + + if (separateReverseChannel) { + PTRACE(3, "LogChan\tseparateReverseChannels"); + if (!ack.HasOptionalField(H245_OpenLogicalChannelAck::e_forwardMultiplexAckParameters)) { + PTRACE(1, "LogChan\tNo forwardMultiplexAckParameters"); + return FALSE; + } + + if (ack.m_forwardMultiplexAckParameters.GetTag() != + H245_OpenLogicalChannelAck_forwardMultiplexAckParameters::e_h2250LogicalChannelAckParameters) { + PTRACE(1, "LogChan\tOnly H.225.0 multiplex supported"); + return FALSE; + } + + const H245_H2250LogicalChannelAckParameters & param = ack.m_forwardMultiplexAckParameters; + + if (!param.HasOptionalField(H245_H2250LogicalChannelAckParameters::e_mediaChannel)) { + PTRACE(1, "LogChan\tNo media channel address provided"); + return FALSE; + } + + address = ¶m.m_mediaChannel; + + if (param.HasOptionalField(H245_H2250LogicalChannelAckParameters::e_mediaControlChannel)) + controlAddress = ¶m.m_mediaControlChannel; + + if (ack.HasOptionalField(H245_OpenLogicalChannelAck::e_reverseLogicalChannelParameters)) { + PTRACE(3, "LogChan\treverseLogicalChannelParameters set"); + reverseChannel = H323ChannelNumber(ack.m_reverseLogicalChannelParameters.m_reverseLogicalChannelNumber, TRUE); + } + } + else { + if (!ack.HasOptionalField(H245_OpenLogicalChannelAck::e_reverseLogicalChannelParameters)) { + PTRACE(1, "LogChan\tNo reverseLogicalChannelParameters"); + return FALSE; + } + + if (ack.m_reverseLogicalChannelParameters.m_multiplexParameters.GetTag() != + H245_OpenLogicalChannelAck_reverseLogicalChannelParameters_multiplexParameters + ::e_h2250LogicalChannelParameters) { + PTRACE(1, "LogChan\tOnly H.225.0 multiplex supported"); + return FALSE; + } + + const H245_H2250LogicalChannelParameters & param = ack.m_reverseLogicalChannelParameters.m_multiplexParameters; + + if (!param.HasOptionalField(H245_H2250LogicalChannelParameters::e_mediaChannel)) { + PTRACE(1, "LogChan\tNo media channel address provided"); + return FALSE; + } + + address = ¶m.m_mediaChannel; + + if (param.HasOptionalField(H245_H2250LogicalChannelAckParameters::e_mediaControlChannel)) + controlAddress = ¶m.m_mediaControlChannel; + } + + remoteMediaAddress = *address; + if (remoteMediaAddress.IsEmpty()) { + PTRACE(1, "LogChan\tDummy media channel address provided"); + return FALSE; + } + + if (controlAddress) { + PTRACE(1, "LogChan\tMedia control address is " << *controlAddress); + remoteMediaControlAddress = *controlAddress; + } + + return TRUE; +} + +BOOL AST_T38Channel::GetRemoteAddress(PIPSocket::Address &ip, WORD &dataPort) const +{ + if (!remoteMediaAddress) + return remoteMediaAddress.GetIpAndPort(ip, dataPort); + + if (!remoteMediaControlAddress) { + if (remoteMediaControlAddress.GetIpAndPort(ip, dataPort)) { + dataPort--; + return TRUE; + } + } + + return FALSE; +} + +#endif + +BOOL AST_T38Channel::Open() +{ + if (opened) + return TRUE; + + opened = TRUE; + + return TRUE; +} + + +BOOL AST_T38Channel::Start() +{ + PTRACE(1, "AST_T38\tStarting channel " << number); + + if (!Open()) + return FALSE; + + if (h323debug) { + PTRACE(1,"AST_T38\tExternal UDPTL Session Starting"); + PTRACE(1,"AST_T38\tUDPTL channel id " << sessionID << " parameters: "); + } + if (!DetermineLocalMediaAddress(connection)) { + return FALSE; + } + GetRemoteAddress(remoteIpAddr, remotePort); + if (h323debug) { + PTRACE(1, "AST_T38\t-- remoteIpAddress: " << remoteIpAddr); + PTRACE(1, "AST_T38\t-- remotePort: " << remotePort); + PTRACE(1, "AST_T38\t-- ExternalIpAddress: " << localIpAddr); + PTRACE(1, "AST_T38\t-- ExternalPort: " << localPort); + } + PTRACE(1, "AST_T38\tStarting UDPTL channel"); + on_start_udptl_channel(connection.GetCallReference(), (const char *)remoteIpAddr.AsString(), remotePort, + (const char *)connection.GetCallToken(), GetDirection() == H323Channel::IsTransmitter ? 1 : 0); + return TRUE; +} --- channels/h323/ast_t38.h.orig 2008-04-08 03:52:57.000000000 +0400 +++ channels/h323/ast_t38.h 2008-04-04 00:56:16.000000000 +0400 @@ -0,0 +1,90 @@ +class AST_T38Capability : public H323_T38Capability +{ + PCLASSINFO(AST_T38Capability, H323_T38Capability); + public: + /* Create a new capability */ + AST_T38Capability(TransportMode mode); + virtual PObject *Clone() const; + + /* Create the channel instance, allocating resources as required. */ + virtual H323Channel *CreateChannel(H323Connection &connection, + H323Channel::Directions, unsigned sessionID, + const H245_H2250LogicalChannelParameters *param) const; + virtual BOOL OnSendingPDU(H245_DataProtocolCapability &proto, + H245_T38FaxProfile & profile) const; +}; + +#ifdef T38_RTPCHANNEL +class AST_T38Channel : public H323_ExternalRTPChannel +{ + PCLASSINFO(AST_T38Channel, H323_ExternalRTPChannel); +#else +class AST_T38Channel : public H323UnidirectionalChannel +{ + PCLASSINFO(AST_T38Channel, H323UnidirectionalChannel); +#endif + + public: + /**Create a new channel. + */ + AST_T38Channel( + H323Connection & connection, ///< Connection to endpoint for channel + const H323Capability & capability, ///< Capability channel is using + Directions direction, ///< Direction of channel + unsigned sessionID, ///< Session ID for channel + H323_T38Capability::TransportMode mode + ); + + /**Destroy the channel. + */ + ~AST_T38Channel(); + + virtual void CleanUpOnTermination(); + +#ifndef T38_RTPCHANNEL + virtual unsigned GetSessionID() const; + + virtual BOOL OnSendingPDU( + H245_OpenLogicalChannel & openPDU ///< Open PDU to send. + ) const; + + virtual void OnSendOpenAck( + const H245_OpenLogicalChannel & open, ///< Open PDU + H245_OpenLogicalChannelAck & ack ///< Acknowledgement PDU + ) const; + + virtual BOOL OnReceivedPDU( + const H245_OpenLogicalChannel & pdu, ///< Open PDU + unsigned & errorCode ///< Error code on failure + ); + + virtual BOOL OnReceivedAckPDU( + const H245_OpenLogicalChannelAck & pdu ///< Acknowledgement PDU + ); + + BOOL GetRemoteAddress(PIPSocket::Address & ip, WORD & dataPort) const; +#endif + + virtual BOOL Start(); + virtual BOOL Open(); + + virtual void Receive() { }; + virtual void Transmit() { }; + + protected: +#ifndef T38_RTPCHANNEL + unsigned sessionID; + BOOL separateReverseChannel; + H323TransportAddress externalMediaAddress; + H323TransportAddress externalMediaControlAddress; + H323TransportAddress remoteMediaAddress; + H323TransportAddress remoteMediaControlAddress; +#endif + PIPSocket::Address localIpAddr; + PIPSocket::Address remoteIpAddr; + WORD localPort; + WORD remotePort; + H323_T38Capability::TransportMode t38mode; + + BOOL DetermineLocalMediaAddress(H323Connection &connection); +}; --- channels/chan_h323.c.orig 2008-04-04 00:01:36.000000000 +0400 +++ channels/chan_h323.c 2008-04-07 02:03:03.000000000 +0400 @@ -97,6 +97,14 @@ #include "asterisk/stringfields.h" #include "asterisk/abstract_jb.h" #include "asterisk/astobj.h" +#define T38_SUPPORT +#ifdef T38_SUPPORT +#include "asterisk/udptl.h" +#include +#include "asterisk/alaw.h" +#include "asterisk/ulaw.h" +#include "spandsp.h" +#endif #ifdef __cplusplus } @@ -118,6 +126,11 @@ hangup_cb on_hangup; setcapabilities_cb on_setcapabilities; setpeercapabilities_cb on_setpeercapabilities; +#ifdef T38_SUPPORT +on_udptl_cb on_external_udptl_create; +start_udptl_cb on_start_udptl_channel; +chmode_cb on_modechange; +#endif /* global debug flag */ int h323debug; @@ -174,6 +187,16 @@ int amaflags; /* AMA Flags */ struct ast_rtp *rtp; /* RTP Session */ struct ast_dsp *vad; /* Used for in-band DTMF detection */ + +#ifdef T38_SUPPORT + struct ast_udptl *udptl; + int faxmode; /* current mode */ + int t38_tx_enable; + int t38_init; + t38_gateway_state_t t38r_state; /* t38gw instance for receive */ + struct sockaddr_in udptlredirip; +#endif + int nativeformats; /* Codec formats supported by a channel */ int needhangup; /* Send hangup when Asterisk is ready */ int hangupcause; /* Hangup cause from OpenH323 layer */ @@ -347,7 +370,8 @@ } if (pvt->needhangup) { if (h323debug) - ast_log(LOG_DEBUG, "Process pending hangup for %s\n", c->name); + ast_log(LOG_DEBUG, "Process pending hangup for %s %d %d\n", c->name, + pvt->needhangup, pvt->hangupcause); c->_softhangup |= AST_SOFTHANGUP_DEV; c->hangupcause = pvt->hangupcause; ast_queue_hangup(c); @@ -395,6 +419,12 @@ c->fds[1] = ast_rtcp_fd(pvt->rtp); ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ } +#ifdef T38_SUPPORT + if (pvt->udptl) { + c->fds[5] = ast_udptl_fd(pvt->udptl); + ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ + } +#endif pvt->update_rtp_info = -1; } } @@ -456,6 +486,11 @@ if (pvt->rtp) { ast_rtp_destroy(pvt->rtp); } +#ifdef T38_SUPPORT + if (pvt->udptl) { + ast_udptl_destroy(pvt->udptl); + } +#endif /* Free dsp used for in-band DTMF detection */ if (pvt->vad) { @@ -508,6 +543,18 @@ struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; char *token; +#ifdef T38_SUPPORT + if (digit == 'f' && !pvt->faxmode) { + ast_mutex_lock(&pvt->lock); + token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL; + ast_log(LOG_DEBUG,"request to change %s to t.38 because fax cng\n", token); + h323_mode_change(token); + ast_mutex_unlock(&pvt->lock); + if (token) free(token); + return 0; + } +#endif + if (!pvt) { ast_log(LOG_ERROR, "No private structure?! This is bad\n"); return -1; @@ -768,6 +815,9 @@ if (pvt->owner) { /* We already hold the channel lock */ if (f->frametype == AST_FRAME_VOICE) { +#ifdef T38_SUPPORT + if (!pvt->faxmode) +#endif if (f->subclass != pvt->owner->nativeformats) { /* Try to avoid deadlock */ if (ast_channel_trylock(pvt->owner)) { @@ -821,6 +871,19 @@ else fr = &ast_null_frame; break; +#ifdef T38_SUPPORT + case 5: + fr = ast_udptl_read(pvt->udptl); + ast_log(LOG_DEBUG, "Got UDPTL %d/%d len %d for %s\n", + fr->frametype, fr->subclass, fr->datalen, pvt->cd.call_token); + if (!pvt->faxmode) pvt->faxmode = 1; + if (pvt->t38_init) { + t38_core_rx_ifp_packet(&pvt->t38r_state.t38, fr->data, + fr->datalen, 0); + fr = &ast_null_frame; + } + break; +#endif default: ast_log(LOG_ERROR, "Unable to handle fd %d on channel %s\n", c->fdno, c->name); fr = &ast_null_frame; @@ -834,6 +897,29 @@ { struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt; int res = 0; +#ifdef T38_SUPPORT +#define MAXT30 240 + int16_t *t38data; + int t38samples; + int16_t t30data[MAXT30]; + unsigned char* t30alaw; + int len; + struct ast_frame* t30frame; + + if (frame->frametype == AST_FRAME_MODEM) { + ast_log(LOG_DEBUG, "Send UDPTL %d/%d len %d for %s\n", + frame->frametype, frame->subclass, frame->datalen, pvt->cd.call_token); + if (pvt) { + ast_mutex_lock(&pvt->lock); + if (pvt->udptl && !pvt->recvonly) + res = ast_udptl_write(pvt->udptl, frame); + __oh323_update_info(c, pvt); + ast_mutex_unlock(&pvt->lock); + } + return res; + } + +#endif if (frame->frametype != AST_FRAME_VOICE) { if (frame->frametype == AST_FRAME_IMAGE) { return 0; @@ -850,7 +936,55 @@ } if (pvt) { ast_mutex_lock(&pvt->lock); - if (pvt->rtp && !pvt->recvonly) +#ifdef T38_SUPPORT + if (pvt->faxmode && pvt->t38_tx_enable && pvt->t38_init) { + int i; + unsigned char* odata; + odata = NULL; + + t38samples = 0; + if (frame->subclass == AST_FORMAT_ALAW) { + t38data = malloc(sizeof(unsigned int)*frame->datalen*2); + odata = frame->data; + for (i=0;idatalen;i++) + t38data[i] = AST_ALAW(odata[i]); + t38samples = i; + } else if (frame->subclass == AST_FORMAT_ULAW) { + t38data = malloc(sizeof(unsigned int)*frame->datalen*2); + odata = frame->data; + for (i=0;idatalen;i++) + t38data[i] = AST_MULAW(odata[i]); + t38samples = i; + } else if (frame->subclass == AST_FORMAT_SLINEAR) { + t38data = frame->data; + t38samples = frame->samples; + } else t38samples = 0; + if (t38samples) + t38_gateway_rx(&pvt->t38r_state, t38data, t38samples); + /* ast_log(LOG_DEBUG,"t38gw_rx %d\n", t38samples); */ + res = frame->samples; + + if ((t30alaw=malloc(2*MAXT30*sizeof(unsigned char))) != NULL && + ((t30frame = malloc(sizeof(struct ast_frame))) != NULL) && + (len = t38_gateway_tx(&pvt->t38r_state, + (int16_t *) t30data, MAXT30)) ) { + memset(t30frame, 0, sizeof(struct ast_frame)); + for (i=0;iframetype = AST_FRAME_VOICE; + t30frame->subclass = AST_FORMAT_ALAW; + t30frame->samples = len; + t30frame->datalen = len; + t30frame->data = &t30alaw[AST_FRIENDLY_OFFSET]; + t30frame->mallocd = AST_MALLOCD_DATA; + ast_queue_frame(pvt->owner, t30frame); + + } + if (odata != NULL) free(t38data); + } else +#endif + if (pvt->rtp && !pvt->recvonly && !pvt->faxmode) res = ast_rtp_write(pvt->rtp, frame); __oh323_update_info(c, pvt); ast_mutex_unlock(&pvt->lock); @@ -977,6 +1111,19 @@ ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", pvt->options.nat); ast_rtp_setnat(pvt->rtp, pvt->options.nat); +#ifdef T38_SUPPORT + pvt->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, our_addr); + if (!pvt->udptl) { + ast_mutex_unlock(&pvt->lock); + ast_log(LOG_WARNING, "Unable to create UDPTL session: %s\n", strerror(errno)); + } + if (h323debug) + ast_log(LOG_DEBUG, "Created UDPTL channel\n"); + ast_udptl_setnat(pvt->udptl, pvt->options.nat); + ast_udptl_set_error_correction_scheme(pvt->udptl, UDPTL_ERROR_CORRECTION_NONE); + pvt->faxmode = 0; +#endif + if (pvt->dtmf_pt > 0) ast_rtp_set_rtpmap_type(pvt->rtp, pvt->dtmf_pt, "audio", "telephone-event", 0); @@ -987,6 +1134,9 @@ ast_jb_configure(pvt->owner, &global_jbconf); pvt->owner->fds[0] = ast_rtp_fd(pvt->rtp); pvt->owner->fds[1] = ast_rtcp_fd(pvt->rtp); +#ifdef T38_SUPPORT + pvt->owner->fds[5] = ast_udptl_fd(pvt->udptl); +#endif ast_queue_frame(pvt->owner, &ast_null_frame); /* Tell Asterisk to apply changes */ ast_channel_unlock(pvt->owner); } else @@ -1041,7 +1191,7 @@ #endif #ifdef T38_SUPPORT if (pvt->udptl) { - ch->fds[4] = ast_udptl_fd(pvt->udptl); + ch->fds[5] = ast_udptl_fd(pvt->udptl); } #endif if (state == AST_STATE_RING) { @@ -2434,6 +2584,102 @@ ast_log(LOG_DEBUG, "Capabilities for connection %s is set\n", token); } +#ifdef T38_SUPPORT + +static int t38_tx_packet_handler(t38_core_state_t *s, void *data, + unsigned int* buf, int len, int cout) +{ + struct ast_frame f = { AST_FRAME_MODEM, AST_MODEM_T38 }; + struct oh323_pvt* pvt = (struct oh323_pvt*) data; + int res; + f.datalen = len; + f.data = buf; + f.offset = 0; + ast_log(LOG_DEBUG, "Sending UDPTL packet of %d bytes\n", len); + if (pvt) { + ast_mutex_lock(&pvt->lock); + if (pvt->udptl) + res = ast_udptl_write(pvt->udptl, &f); + if (pvt->owner) + __oh323_update_info(pvt->owner, pvt); + ast_mutex_unlock(&pvt->lock); + } + return 0; + +} +static void span_message(int level, const char* msg) { + ast_log(LOG_DEBUG, msg); +} +static void changemode(unsigned call_reference, const char *token, int t38mode) +{ + struct oh323_pvt *pvt; + struct ast_frame f; + pvt = find_call_locked(call_reference, token); + ast_log(LOG_DEBUG,"change mode to %d for %s\n", t38mode, token); + if (!pvt) return; +#if 0 + f.frametype = AST_FRAME_MODEM; + f.subclass = 'c'; + f.datalen = 0; + f.samples = 0; + f.offset = 0; + f.data = NULL; + f.mallocd = 0; + f.src = "CHANGE_MODE"; + if (pvt->owner && !ast_channel_trylock(pvt->owner)) { + ast_queue_frame(pvt->owner, &f); + ast_mutex_unlock(&pvt->owner->lock); + } +#endif + + if (t38mode) { + + if (t38_gateway_init(&pvt->t38r_state, t38_tx_packet_handler, pvt) + != NULL) { + + pvt->t38_init = 1; + t38_gateway_set_transmit_on_idle(&pvt->t38r_state, TRUE); + + span_log_set_message_handler(&pvt->t38r_state.logging, span_message); + span_log_set_message_handler(&pvt->t38r_state.t38.logging, span_message); + + + span_log_set_level(&pvt->t38r_state.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + span_log_set_level(&pvt->t38r_state.t38.logging, SPAN_LOG_SHOW_SEVERITY | SPAN_LOG_SHOW_PROTOCOL | SPAN_LOG_FLOW); + + t38_set_t38_version(&pvt->t38r_state.t38, 0); + + t38_set_sequence_number_handling(&pvt->t38r_state.t38,0); + + t38_gateway_set_ecm_capability(&pvt->t38r_state, 0); + + /* t38_gateway_set_supported_modems(&pvt->t38r_state, T30_SUPPORT_V27TER); + t38_gateway_set_supported_modems(&pvt->t38t_state, T30_SUPPORT_V27TER); */ + + t38_gateway_set_supported_modems(&pvt->t38r_state, T30_SUPPORT_V17); + + ast_log(LOG_DEBUG,"Going to change native format for %s\n", token); + pvt->nativeformats = AST_FORMAT_ALAW; + pvt->faxmode = 1; + if (pvt->owner && !ast_channel_trylock(pvt->owner)) { + pvt->owner->nativeformats = pvt->nativeformats; + ast_set_read_format(pvt->owner, pvt->owner->readformat); + ast_set_write_format(pvt->owner, pvt->owner->writeformat); + ast_channel_unlock(pvt->owner); + } + ast_log(LOG_DEBUG,"native format for %s is %d\n", token, pvt->owner->nativeformats); + } + + } else { +/* else if t38mode */ + pvt->faxmode = 0; + pvt->t38_init = 0; + } + + ast_mutex_unlock(&pvt->lock); +} +#endif + static void *do_monitor(void *data) { int res; @@ -2799,7 +3045,7 @@ global_options.dtmfcodec = 101; global_options.dtmfmode = H323_DTMF_RFC2833; global_options.capability = GLOBAL_CAPABILITY; - global_options.bridge = 1; /* Do native bridging by default */ + global_options.bridge = 0; /* Do native bridging by default */ strcpy(default_context, "default"); h323_signalling_port = 1720; gatekeeper_disable = 1; @@ -3091,6 +3337,137 @@ .get_vrtp_info = oh323_get_vrtp_peer, .set_rtp_peer = oh323_set_rtp_peer, }; +#ifdef T38_SUPPORT + +static struct ast_udptl *oh323_get_udptl_peer(struct ast_channel *chan) +{ + struct oh323_pvt *pvt; + struct ast_udptl *udptl = NULL; + + pvt = chan->tech_pvt; + if (!pvt) + return NULL; + + ast_mutex_lock(&pvt->lock); + if (pvt->udptl) + udptl = pvt->udptl; + ast_mutex_unlock(&pvt->lock); + return udptl; +} + +static int oh323_set_udptl_peer(struct ast_channel *chan, struct ast_udptl *udptl) +{ + struct oh323_pvt *pvt; + + pvt = chan->tech_pvt; + if (!pvt) + return -1; + ast_mutex_lock(&pvt->lock); + if (udptl) + ast_udptl_get_peer(udptl, &pvt->udptlredirip); + else + memset(&pvt->udptlredirip, 0, sizeof(pvt->udptlredirip)); + + ast_mutex_unlock(&pvt->lock); + return 0; +} + +struct rtp_info *external_udptl_create(unsigned call_reference, const char * token) +{ + struct oh323_pvt *pvt; + struct sockaddr_in us; + struct rtp_info *info; + + info = (struct rtp_info *)malloc(sizeof(struct rtp_info)); + if (!info) { + ast_log(LOG_ERROR, "Unable to allocated info structure, this is very bad"); + return NULL; + } + + pvt = find_call_locked(call_reference, token); + if (!pvt) { + free(info); + ast_log(LOG_ERROR,"Unable to find call %s(%d)\n", token, + call_reference); + return NULL; + } + /* figure out our local RTP port and tell the H.323 stack about it */ + ast_udptl_get_us(pvt->udptl, &us); + ast_mutex_unlock(&pvt->lock); + + strncpy(info->addr,ast_inet_ntoa(us.sin_addr),sizeof(info->addr)); + info->port = ntohs(us.sin_port); + if (h323debug) + ast_log(LOG_DEBUG, "Sending UDPTL 'US' %s:%d\n", info->addr, info->port); + return info; +} + +void setup_udptl_connection(unsigned call_reference, const char *remoteIp, int +remotePort, const char *token, int direction) +{ + struct oh323_pvt *pvt; + struct sockaddr_in them; + struct rtp_info* info; + + if (h323debug) + ast_log(LOG_DEBUG, "Setting up UDPTL connection/%d for %s\n", + direction, token); + + /* Find the call or allocate a private structure if call not found */ + pvt = find_call_locked(call_reference, token); + if (!pvt) { + ast_log(LOG_ERROR, "Something is wrong: udptl\n"); + return; + } + if (pvt->alreadygone) { + ast_mutex_unlock(&pvt->lock); + return; + } + + them.sin_family = AF_INET; + them.sin_addr.s_addr = inet_addr(remoteIp); + them.sin_port = htons(remotePort); + if (direction) { + ast_udptl_set_peer(pvt->udptl, &them); + pvt->t38_tx_enable = 1; + info = (struct rtp_info *)malloc(sizeof(struct rtp_info)); + strncpy(info->addr,ast_inet_ntoa(them.sin_addr), sizeof(info->addr)); + info->port = ntohs(them.sin_port); + ast_log(LOG_DEBUG, "Receiving UDPTL %s:%d\n", info->addr, info->port); + free(info); + } + ast_mutex_unlock(&pvt->lock); + if (h323debug) + ast_log(LOG_DEBUG, "UDPTL connection prepared for %s\n", token); + return; +} + +void shutdown_udptl_connection(unsigned call_reference, const char *token, int direction) +{ + struct oh323_pvt *pvt; + if (h323debug) + ast_log(LOG_DEBUG, "Shutting down UDPTL connection for %s\n", token); + + pvt = find_call_locked(call_reference, token); + if (!pvt) { + ast_log(LOG_ERROR, "Something is wrong: rtp\n"); + return; + } + pvt->t38_tx_enable = 0; + if (pvt->alreadygone) { + ast_mutex_unlock(&pvt->lock); + return; + } + ast_mutex_unlock(&pvt->lock); +} + +static struct ast_udptl_protocol oh323_udptl = { + type: "H323", + get_udptl_info: oh323_get_udptl_peer, + set_udptl_peer: oh323_set_udptl_peer, +}; + +#endif static enum ast_module_load_result load_module(void) { @@ -3145,6 +3522,9 @@ ast_cli_register_multiple(cli_h323, sizeof(cli_h323) / sizeof(struct ast_cli_entry)); ast_rtp_proto_register(&oh323_rtp); +#ifdef T38_SUPPORT + ast_udptl_proto_register(&oh323_udptl); +#endif /* Register our callback functions */ h323_callback_register(setup_incoming_call, @@ -3160,7 +3540,13 @@ set_dtmf_payload, hangup_connection, set_local_capabilities, - set_peer_capabilities); + set_peer_capabilities +#ifdef T38_SUPPORT + ,external_udptl_create, + setup_udptl_connection, + changemode +#endif +); /* start the h.323 listener */ if (h323_start_listener(h323_signalling_port, bindaddr)) { ast_log(LOG_ERROR, "Unable to create H323 listener.\n"); @@ -3204,6 +3590,9 @@ ast_channel_unregister(&oh323_tech); ast_rtp_proto_unregister(&oh323_rtp); +#ifdef T38_SUPPORT + ast_udptl_proto_unregister(&oh323_udptl); +#endif if (!ast_mutex_lock(&iflock)) { /* hangup all interfaces if they have an owner */