4 * T.38 protocol handler
6 * Open Phone Abstraction Library
8 * Copyright (c) 1998-2002 Equivalence Pty. Ltd.
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
20 * The Original Code is Open H323 Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): Vyacheslav Frolov.
27 * Revision 2.8 2005/02/21 12:20:08 rjongbloed
28 * Added new "options list" to the OpalMediaFormat class.
30 * Revision 2.7 2003/01/07 04:39:53 robertj
31 * Updated to OpenH323 v1.11.2
33 * Revision 2.6 2002/11/10 11:33:20 robertj
34 * Updated to OpenH323 v1.10.3
36 * Revision 2.5 2002/09/04 06:01:49 robertj
37 * Updated to OpenH323 v1.9.6
39 * Revision 2.4 2002/02/11 09:32:13 robertj
40 * Updated to openH323 v1.8.0
42 * Revision 2.3 2002/01/22 05:21:54 robertj
43 * Added RTP encoding name string to media format database.
44 * Changed time units to clock rate in Hz.
46 * Revision 2.2 2002/01/14 06:35:59 robertj
47 * Updated to OpenH323 v1.7.9
49 * Revision 2.1 2001/08/01 05:05:26 robertj
50 * Major changes to H.323 capabilities, uses OpalMediaFormat for base name.
52 * Revision 2.0 2001/07/27 15:48:25 robertj
53 * Conversion of OpenH323 to Open Phone Abstraction Library (OPAL)
55 * Revision 1.17 2002/12/19 01:49:08 robertj
56 * Fixed incorrect setting of optional fields in pre-corrigendum packet
57 * translation function, thanks Vyacheslav Frolov
59 * Revision 1.16 2002/12/06 04:18:02 robertj
62 * Revision 1.15 2002/12/02 04:08:02 robertj
63 * Turned T.38 Originate inside out, so now has WriteXXX() functions that can
64 * be call ed in different thread contexts.
66 * Revision 1.14 2002/12/02 00:37:19 robertj
67 * More implementation of T38 base library code, some taken from the t38modem
68 * application by Vyacheslav Frolov, eg redundent frames.
70 * Revision 1.13 2002/11/21 06:40:00 robertj
71 * Changed promiscuous mode to be three way. Fixes race condition in gkserver
72 * which can cause crashes or more PDUs to be sent to the wrong place.
74 * Revision 1.12 2002/09/25 05:20:40 robertj
75 * Fixed warning on no trace version.
77 * Revision 1.11 2002/08/05 10:03:48 robertj
78 * Cosmetic changes to normalise the usage of pragma interface/implementation.
80 * Revision 1.10 2002/02/09 04:39:05 robertj
81 * Changes to allow T.38 logical channels to use single transport which is
82 * now owned by the OpalT38Protocol object instead of H323Channel.
84 * Revision 1.9 2002/01/01 23:27:50 craigs
85 * Added CleanupOnTermination functions
86 * Thanks to Vyacheslav Frolov
88 * Revision 1.8 2001/12/22 22:18:07 craigs
89 * Canged to ignore subsequent PDUs with identical sequence numbers
91 * Revision 1.7 2001/12/22 01:56:51 robertj
92 * Cleaned up code and allowed for repeated sequence numbers.
94 * Revision 1.6 2001/12/19 09:15:43 craigs
95 * Added changes from Vyacheslav Frolov
97 * Revision 1.5 2001/12/14 08:36:36 robertj
98 * More implementation of T.38, thanks Adam Lazur
100 * Revision 1.4 2001/11/11 23:18:53 robertj
101 * MSVC warnings removed.
103 * Revision 1.3 2001/11/11 23:07:52 robertj
104 * Some clean ups after T.38 commit, thanks Adam Lazur
106 * Revision 1.2 2001/11/09 05:39:54 craigs
107 * Added initial T.38 support thanks to Adam Lazur
109 * Revision 1.1 2001/07/17 04:44:32 robertj
110 * Partial implementation of T.120 and T.38 logical channels.
117 #pragma implementation "t38proto.h"
120 #include <t38/t38proto.h>
122 #include <h323/transaddr.h>
129 const OpalMediaFormat
OpalT38(
131 OpalMediaFormat::DefaultDataSessionID
,
132 RTP_DataFrame::IllegalPayloadType
,
134 FALSE
, // No jitter for data
135 1440, // 100's bits/sec
141 /////////////////////////////////////////////////////////////////////////////
143 OpalT38Protocol::OpalT38Protocol()
146 autoDeleteTransport
= FALSE
;
147 corrigendumASN
= TRUE
;
148 indicatorRedundancy
= 0;
149 lowSpeedRedundancy
= 0;
150 highSpeedRedundancy
= 0;
151 lastSentSequenceNumber
= -1;
155 OpalT38Protocol::~OpalT38Protocol()
157 if (autoDeleteTransport
)
161 void OpalT38Protocol::Close()
167 void OpalT38Protocol::SetTransport(H323Transport
* t
, BOOL autoDelete
)
169 if (transport
!= t
) {
170 if (autoDeleteTransport
)
176 autoDeleteTransport
= autoDelete
;
180 BOOL
OpalT38Protocol::Originate()
182 PTRACE(3, "T38\tOriginate, transport=" << *transport
);
184 // Application would normally override this. The default just sends
186 while (WriteIndicator(T38_Type_of_msg_t30_indicator::e_no_signal
))
193 BOOL
OpalT38Protocol::WritePacket(const T38_IFPPacket
& ifp
)
195 T38_UDPTLPacket udptl
;
197 // If there are redundant frames saved from last time, put them in
198 if (!redundantIFPs
.IsEmpty()) {
199 udptl
.m_error_recovery
.SetTag(T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets
);
200 T38_UDPTLPacket_error_recovery_secondary_ifp_packets
& secondary
= udptl
.m_error_recovery
;
201 secondary
.SetSize(redundantIFPs
.GetSize());
202 for (PINDEX i
= 0; i
< redundantIFPs
.GetSize(); i
++)
203 secondary
[i
].SetValue(redundantIFPs
[i
]);
206 // Encode the current ifp, but need to do stupid things as there are two
207 // versions of the ASN out there, completely incompatible.
208 if (corrigendumASN
|| !ifp
.HasOptionalField(T38_IFPPacket::e_data_field
))
209 udptl
.m_primary_ifp_packet
.EncodeSubType(ifp
);
211 T38_PreCorrigendum_IFPPacket old_ifp
;
213 old_ifp
.m_type_of_msg
= ifp
.m_type_of_msg
;
215 old_ifp
.IncludeOptionalField(T38_IFPPacket::e_data_field
);
217 PINDEX count
= ifp
.m_data_field
.GetSize();
218 old_ifp
.m_data_field
.SetSize(count
);
220 for (PINDEX i
= 0 ; i
< count
; i
++) {
221 old_ifp
.m_data_field
[i
].m_field_type
= ifp
.m_data_field
[i
].m_field_type
;
222 if (ifp
.m_data_field
[i
].HasOptionalField(T38_Data_Field_subtype::e_field_data
)) {
223 old_ifp
.m_data_field
[i
].IncludeOptionalField(T38_Data_Field_subtype::e_field_data
);
224 old_ifp
.m_data_field
[i
].m_field_data
= ifp
.m_data_field
[i
].m_field_data
;
228 udptl
.m_primary_ifp_packet
.PASN_OctetString::EncodeSubType(old_ifp
);
231 lastSentSequenceNumber
= (lastSentSequenceNumber
+ 1) & 0xffff;
232 udptl
.m_seq_number
= lastSentSequenceNumber
;
235 udptl
.Encode(rawData
);
238 if (PTrace::CanTrace(4)) {
239 PTRACE(4, "T38\tSending PDU:\n "
240 << setprecision(2) << ifp
<< "\n "
241 << setprecision(2) << udptl
<< "\n "
242 << setprecision(2) << rawData
);
245 PTRACE(3, "T38\tSending PDU:"
246 " seq=" << lastSentSequenceNumber
<<
247 " type=" << ifp
.m_type_of_msg
.GetTagName());
251 if (!transport
->WritePDU(rawData
)) {
252 PTRACE(1, "T38\tWritePacket error: " << transport
->GetErrorText());
256 // Calculate the level of redundency for this data phase
257 PINDEX maxRedundancy
;
258 if (ifp
.m_type_of_msg
.GetTag() == T38_Type_of_msg::e_t30_indicator
)
259 maxRedundancy
= indicatorRedundancy
;
260 else if ((T38_Type_of_msg_data
)ifp
.m_type_of_msg
== T38_Type_of_msg_data::e_v21
)
261 maxRedundancy
= lowSpeedRedundancy
;
263 maxRedundancy
= highSpeedRedundancy
;
265 // Push down the current ifp into redundant data
266 if (maxRedundancy
> 0)
267 redundantIFPs
.InsertAt(0, new PBYTEArray(udptl
.m_primary_ifp_packet
.GetValue()));
269 // Remove redundant data that are surplus to requirements
270 while (redundantIFPs
.GetSize() > maxRedundancy
)
271 redundantIFPs
.RemoveAt(maxRedundancy
);
277 BOOL
OpalT38Protocol::WriteIndicator(unsigned indicator
)
281 ifp
.SetTag(T38_Type_of_msg::e_t30_indicator
);
282 T38_Type_of_msg_t30_indicator
& ind
= ifp
.m_type_of_msg
;
283 ind
.SetValue(indicator
);
285 return WritePacket(ifp
);
289 BOOL
OpalT38Protocol::WriteMultipleData(unsigned mode
,
292 const PBYTEArray
* data
)
296 ifp
.SetTag(T38_Type_of_msg::e_data
);
297 T38_Type_of_msg_data
& datamode
= ifp
.m_type_of_msg
;
298 datamode
.SetValue(mode
);
300 ifp
.IncludeOptionalField(T38_IFPPacket::e_data_field
);
301 ifp
.m_data_field
.SetSize(count
);
302 for (PINDEX i
= 0; i
< count
; i
++) {
303 ifp
.m_data_field
[i
].m_field_type
.SetValue(type
[i
]);
304 ifp
.m_data_field
[i
].m_field_data
.SetValue(data
[i
]);
307 return WritePacket(ifp
);
311 BOOL
OpalT38Protocol::WriteData(unsigned mode
, unsigned type
, const PBYTEArray
& data
)
313 return WriteMultipleData(mode
, 1, &type
, &data
);
317 BOOL
OpalT38Protocol::Answer()
319 PTRACE(3, "T38\tAnswer, transport=" << *transport
);
321 // Should probably get this from the channel open negotiation, but for
322 // the time being just accept from whoever sends us something first
323 transport
->SetPromiscuous(H323Transport::AcceptFromAnyAutoSet
);
325 int consecutiveBadPackets
= 0;
326 int expectedSequenceNumber
= 0; // 16 bit
327 BOOL firstPacket
= TRUE
;
331 if (!transport
->ReadPDU(rawData
)) {
332 PTRACE(1, "T38\tError reading PDU: " << transport
->GetErrorText(PChannel::LastReadError
));
336 /* when we get the first packet, set the RemoteAddress and then turn off
337 * promiscuous listening */
339 PTRACE(3, "T38\tReceived first packet, remote=" << transport
->GetRemoteAddress());
340 transport
->SetPromiscuous(H323Transport::AcceptFromRemoteOnly
);
345 T38_UDPTLPacket udptl
;
346 if (udptl
.Decode(rawData
))
347 consecutiveBadPackets
= 0;
349 consecutiveBadPackets
++;
350 PTRACE(2, "T38\tRaw data decode failure:\n "
351 << setprecision(2) << rawData
<< "\n UDPTL = "
352 << setprecision(2) << udptl
);
353 if (consecutiveBadPackets
> 3) {
354 PTRACE(1, "T38\tRaw data decode failed multiple times, aborting!");
361 if (!udptl
.m_primary_ifp_packet
.DecodeSubType(ifp
)) {
362 PTRACE(2, "T38\tUDPTLPacket decode failure:\n "
363 << setprecision(2) << rawData
<< "\n UDPTL = "
364 << setprecision(2) << udptl
<< "\n ifp = "
365 << setprecision(2) << ifp
);
369 unsigned receivedSequenceNumber
= udptl
.m_seq_number
;
372 if (PTrace::CanTrace(5)) {
373 PTRACE(4, "T38\tReceived UDPTL packet:\n "
374 << setprecision(2) << rawData
<< "\n "
375 << setprecision(2) << udptl
);
377 if (PTrace::CanTrace(4)) {
378 PTRACE(4, "T38\tReceived UDPTL packet:\n " << setprecision(2) << udptl
);
381 PTRACE(3, "T38\tReceived UDPTL packet: seq=" << receivedSequenceNumber
);
385 // Calculate the number of lost packets, if the number lost is really
386 // really big then it means it is actually a packet arriving out of order
387 int lostPackets
= (receivedSequenceNumber
- expectedSequenceNumber
)&0xffff;
388 if (lostPackets
> 32767) {
389 PTRACE(3, "T38\tIgnoring out of order packet");
393 expectedSequenceNumber
= (WORD
)(receivedSequenceNumber
+1);
395 // See if this is the expected packet
396 if (lostPackets
> 0) {
397 // Not what was expected, see if we have enough redundant data
398 if (udptl
.m_error_recovery
.GetTag() == T38_UDPTLPacket_error_recovery::e_secondary_ifp_packets
) {
399 T38_UDPTLPacket_error_recovery_secondary_ifp_packets
& secondary
= udptl
.m_error_recovery
;
400 int nRedundancy
= secondary
.GetSize();
401 if (lostPackets
>= nRedundancy
) {
402 if (!HandlePacketLost(lostPackets
- nRedundancy
)) {
403 PTRACE(1, "T38\tHandle packet failed, aborting answer");
406 lostPackets
= nRedundancy
;
408 while (lostPackets
> 0) {
409 if (!HandleRawIFP(secondary
[lostPackets
++])) {
410 PTRACE(1, "T38\tHandle packet failed, aborting answer");
416 if (!HandlePacketLost(lostPackets
)) {
417 PTRACE(1, "T38\tHandle lost packet, aborting answer");
423 if (!HandleRawIFP(udptl
.m_primary_ifp_packet
)) {
424 PTRACE(1, "T38\tHandle packet failed, aborting answer");
431 BOOL
OpalT38Protocol::HandleRawIFP(const PASN_OctetString
& pdu
)
435 if (corrigendumASN
) {
436 if (pdu
.DecodeSubType(ifp
))
437 return HandlePacket(ifp
);
439 PTRACE(2, "T38\tIFP decode failure:\n " << setprecision(2) << ifp
);
443 T38_PreCorrigendum_IFPPacket old_ifp
;
444 if (!pdu
.DecodeSubType(old_ifp
)) {
445 PTRACE(2, "T38\tPre-corrigendum IFP decode failure:\n " << setprecision(2) << old_ifp
);
449 ifp
.m_type_of_msg
= old_ifp
.m_type_of_msg
;
451 if (old_ifp
.HasOptionalField(T38_IFPPacket::e_data_field
)) {
452 ifp
.IncludeOptionalField(T38_IFPPacket::e_data_field
);
453 PINDEX count
= old_ifp
.m_data_field
.GetSize();
454 ifp
.m_data_field
.SetSize(count
);
455 for (PINDEX i
= 0 ; i
< count
; i
++) {
456 ifp
.m_data_field
[i
].m_field_type
= old_ifp
.m_data_field
[i
].m_field_type
;
457 if (old_ifp
.m_data_field
[i
].HasOptionalField(T38_Data_Field_subtype::e_field_data
)) {
458 ifp
.m_data_field
[i
].IncludeOptionalField(T38_Data_Field_subtype::e_field_data
);
459 ifp
.m_data_field
[i
].m_field_data
= old_ifp
.m_data_field
[i
].m_field_data
;
464 return HandlePacket(ifp
);
468 BOOL
OpalT38Protocol::HandlePacket(const T38_IFPPacket
& ifp
)
470 if (ifp
.m_type_of_msg
.GetTag() == T38_Type_of_msg::e_t30_indicator
)
471 return OnIndicator((T38_Type_of_msg_t30_indicator
)ifp
.m_type_of_msg
);
473 for (PINDEX i
= 0; i
< ifp
.m_data_field
.GetSize(); i
++) {
474 if (!OnData((T38_Type_of_msg_data
)ifp
.m_type_of_msg
,
475 ifp
.m_data_field
[i
].m_field_type
,
476 ifp
.m_data_field
[i
].m_field_data
.GetValue()))
483 BOOL
OpalT38Protocol::OnIndicator(unsigned indicator
)
486 case T38_Type_of_msg_t30_indicator::e_no_signal
:
489 case T38_Type_of_msg_t30_indicator::e_cng
:
492 case T38_Type_of_msg_t30_indicator::e_ced
:
495 case T38_Type_of_msg_t30_indicator::e_v21_preamble
:
498 case T38_Type_of_msg_t30_indicator::e_v27_2400_training
:
499 case T38_Type_of_msg_t30_indicator::e_v27_4800_training
:
500 case T38_Type_of_msg_t30_indicator::e_v29_7200_training
:
501 case T38_Type_of_msg_t30_indicator::e_v29_9600_training
:
502 case T38_Type_of_msg_t30_indicator::e_v17_7200_short_training
:
503 case T38_Type_of_msg_t30_indicator::e_v17_7200_long_training
:
504 case T38_Type_of_msg_t30_indicator::e_v17_9600_short_training
:
505 case T38_Type_of_msg_t30_indicator::e_v17_9600_long_training
:
506 case T38_Type_of_msg_t30_indicator::e_v17_12000_short_training
:
507 case T38_Type_of_msg_t30_indicator::e_v17_12000_long_training
:
508 case T38_Type_of_msg_t30_indicator::e_v17_14400_short_training
:
509 case T38_Type_of_msg_t30_indicator::e_v17_14400_long_training
:
510 return OnTraining(indicator
);
520 BOOL
OpalT38Protocol::OnCNG()
526 BOOL
OpalT38Protocol::OnCED()
532 BOOL
OpalT38Protocol::OnPreamble()
538 BOOL
OpalT38Protocol::OnTraining(unsigned /*indicator*/)
544 BOOL
OpalT38Protocol::OnData(unsigned /*mode*/,
546 const PBYTEArray
& /*data*/)
553 #define PTRACE_nLost nLost
558 BOOL
OpalT38Protocol::HandlePacketLost(unsigned PTRACE_nLost
)
560 PTRACE(2, "T38\tHandlePacketLost, n=" << PTRACE_nLost
);
561 /* don't handle lost packets yet */
566 /////////////////////////////////////////////////////////////////////////////