1 /*
2 * main.cxx
3 *
4 * OPAL application source file for sending/receiving faxes via T.38
5 *
6 * Copyright (c) 2008 Vox Lucida Pty. Ltd.
7 *
8 * The contents of this file are subject to the Mozilla Public License
9 * Version 1.0 (the "License"); you may not use this file except in
10 * compliance with the License. You may obtain a copy of the License at
11 * http://www.mozilla.org/MPL/
12 *
13 * Software distributed under the License is distributed on an "AS IS"
14 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
15 * the License for the specific language governing rights and limitations
16 * under the License.
17 *
18 * The Original Code is Open Phone Abstraction Library.
19 *
20 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
21 *
22 * Contributor(s): ______________________________________.
23 *
24 * $Revision: 28445 $
25 * $Author: rjongbloed $
26 * $Date: 2012-10-02 20:11:02 -0500 (Tue, 02 Oct 2012) $
27 */
28
29 #include "precompile.h"
30 #include "main.h"
31
32
33 // -ttttt c:\temp\testfax.tif sip:fax@10.0.1.11
34 // -ttttt c:\temp\incoming.tif
35
36 PCREATE_PROCESS(FaxOPAL);
37
38
FaxOPAL()39 FaxOPAL::FaxOPAL()
40 : PProcess("OPAL T.38 Fax", "FaxOPAL", OPAL_MAJOR, OPAL_MINOR, ReleaseCode, OPAL_BUILD)
41 , m_manager(NULL)
42 {
43 }
44
45
~FaxOPAL()46 FaxOPAL::~FaxOPAL()
47 {
48 delete m_manager;
49 }
50
51
Main()52 void FaxOPAL::Main()
53 {
54 SetTerminationValue(1);
55
56 m_manager = new MyManager();
57
58 PArgList & args = GetArguments();
59
60 if (!args.Parse(m_manager->GetArgumentSpec() +
61 "a-audio."
62 "A-no-audio."
63 "d-directory:"
64 "-station-id:"
65 "-header-info:"
66 "e-switch-on-ced."
67 "F-no-fallback."
68 "q-quiet."
69 "T-timeout:"
70 "v-verbose."
71 "X-switch-time:") ||
72 args.HasOption('h') ||
73 args.GetCount() == 0) {
74 PString name = GetFile().GetTitle();
75 cerr << "usage: " << name << " [ options ] filename [ url ]\n"
76 "\n"
77 "Available options are:\n"
78 " -d or --directory dir : Set default directory for fax receive.\n"
79 " --station-id id : Set T.30 Station Identifier string.\n"
80 " --header-info msg : Set transmitted fax page header string.\n"
81 " -a or --audio : Send fax as G.711 audio.\n"
82 " -A or --no-audio : No audio phase at all, starts T.38 immediately.\n"
83 " -F or --no-fallback n : Do not fall back to audio it T.38 switch fails.\n"
84 " -e or --switch-on-ced : Switch to T.38 on receipt of CED tone as caller.\n"
85 " -X or --switch-time n : Set fail safe T.38 switch time in seconds.\n"
86 " -T or --timeout n : Set timeout to wait for fax rx/tx to complete in seconds.\n"
87 " -q or --quiet : Only output error conditions.\n"
88 " -v or --verbose : Output more information on call progress.\n"
89 "\n"
90 << m_manager->GetArgumentUsage()
91 << "\n"
92 "Specific T.38 format options (using -O/--option):\n"
93 " Station-Identifier string (\"-\"\n"
94 " Header-Info string (\"\")\n"
95 " Use-ECM bool (1)\n"
96 " T38FaxVersion integer (0)\n"
97 " T38FaxRateManagement localTCF or transferredTCF (transferredTCF) \n"
98 " T38MaxBitRate integer (14400)\n"
99 " T38FaxMaxBuffer integer (2000\n"
100 " T38FaxMaxDatagram integer (528)\n"
101 " T38FaxUdpEC t38UDPFEC or t38UDPRedundancy (t38UDPRedundancy)\n"
102 " T38FaxFillBitRemoval bool (0)\n"
103 " T38FaxTranscodingMMR bool (0)\n"
104 " T38FaxTranscodingJBIG bool (0)\n"
105 "\n"
106 "e.g. " << name << " --option 'T.38:Header-Info=My custom header line' send_fax.tif sip:fred@bloggs.com\n"
107 "\n"
108 " " << name << " received_fax.tif\n\n";
109 return;
110 }
111
112 static char const * FormatMask[] = { "!G.711*", "!@fax" };
113 m_manager->SetMediaFormatMask(PStringArray(PARRAYSIZE(FormatMask), FormatMask));
114
115 PString prefix;
116
117 if (args.HasOption('q'))
118 cout.rdbuf(NULL);
119
120 cout << "Fax Mode: ";
121 if (args.HasOption('A')) {
122 OpalMediaType::Fax().GetDefinition()->SetAutoStart(OpalMediaType::ReceiveTransmit);
123 OpalMediaType::Audio().GetDefinition()->SetAutoStart(OpalMediaType::DontOffer);
124 cout << "Offer T.38 only";
125 prefix = "t38";
126 }
127 else if (args.HasOption('a')) {
128 cout << "Audio Only";
129 prefix = "fax";
130 }
131 else {
132 cout << "Switch to T.38";
133 prefix = "t38";
134 }
135 cout << '\n';
136
137 if (!m_manager->Initialise(args, !args.HasOption('q'), prefix + ":" + args[0] + ";receive"))
138 return;
139
140 // Create audio or T.38 fax endpoint.
141 MyFaxEndPoint * fax = new MyFaxEndPoint(*m_manager);
142 if (args.HasOption('d'))
143 fax->SetDefaultDirectory(args.GetOptionString('d'));
144
145 if (!fax->IsAvailable()) {
146 cerr << "No fax codecs, SpanDSP plug-in probably not installed." << endl;
147 return;
148 }
149
150 OpalConnection::StringOptions stringOptions;
151
152 if (args.HasOption("station-id")) {
153 PString str = args.GetOptionString("station-id");
154 cout << "Station Identifier: " << str << '\n';
155 stringOptions.SetAt(OPAL_OPT_STATION_ID, str);
156 }
157
158 if (args.HasOption("header-info")) {
159 PString str = args.GetOptionString("header-info");
160 cout << "Transmit Header Info: " << str << '\n';
161 stringOptions.SetAt(OPAL_OPT_HEADER_INFO, str);
162 }
163
164 if (args.HasOption('F')) {
165 stringOptions.SetBoolean(OPAL_NO_G111_FAX, true);
166 cout << "Disabled fallback to audio (G.711) mode on T.38 switch failure\n";
167 }
168
169 if (args.HasOption('e')) {
170 stringOptions.SetBoolean(OPAL_SWITCH_ON_CED, true);
171 cout << "Enabled switch to T.38 on receipt of CED\n";
172 }
173
174 if (args.HasOption('X')) {
175 unsigned seconds = args.GetOptionString('X').AsUnsigned();
176 stringOptions.SetInteger(OPAL_T38_SWITCH_TIME, seconds);
177 cout << "Switch to T.38 after " << seconds << "seconds\n";
178 }
179 else
180 cout << "No T.38 switch timeout set\n";
181
182 // Wait for call to come in and finish (default one year)
183 PSimpleTimer timeout(args.GetOptionString('T', "365:0:0:0"));
184 cout << "Completion timeout is " << timeout.AsString(0, PTimeInterval::IncludeDays) << '\n';
185
186 if (args.GetCount() == 1)
187 cout << "Receive directory: " << fax->GetDefaultDirectory() << "\n"
188 "\n"
189 "Awaiting incoming fax, saving as " << args[0];
190 else {
191 PString token;
192 if (!m_manager->SetUpCall(prefix + ":" + args[0], args[1], token, NULL, 0, &stringOptions)) {
193 cerr << "Could not start call to \"" << args[1] << '"' << endl;
194 return;
195 }
196 cout << "\n"
197 "Sending " << args[0] << " to " << args[1];
198 }
199 cout << " ..." << endl;
200
201 SetTerminationValue(2);
202
203 map<PString, OpalMediaStatistics> lastStatisticsByToken;
204
205 while (!m_manager->m_completed.Wait(1000)) {
206 if (timeout.HasExpired()) {
207 cout << " no call";
208 break;
209 }
210
211 if (args.HasOption('v')) {
212 PArray<PString> tokens = m_manager->GetAllCalls();
213 for (PINDEX i = 0; i < tokens.GetSize(); ++i) {
214 PSafePtr<OpalCall> call = m_manager->FindCallWithLock(tokens[i], PSafeReadOnly);
215 if (call != NULL) {
216 PSafePtr<OpalFaxConnection> connection = call->GetConnectionAs<OpalFaxConnection>();
217 if (connection != NULL) {
218 PSafePtr<OpalMediaStream> stream = connection->GetMediaStream(OpalMediaType::Fax(), true);
219 if (stream != NULL) {
220 OpalMediaStatistics & lastStatistics = lastStatisticsByToken[tokens[i]];
221 OpalMediaStatistics statistics;
222 stream->GetStatistics(statistics);
223
224 #define SHOW_STAT(message, member) \
225 if (lastStatistics.m_fax.member != statistics.m_fax.member) \
226 cout << message << ": " << statistics.m_fax.member << endl;
227 SHOW_STAT("Phase", m_phase);
228 SHOW_STAT("Station identifier", m_stationId);
229 SHOW_STAT("Tx pages", m_txPages);
230 SHOW_STAT("Rx pages", m_rxPages);
231 SHOW_STAT("Bit rate", m_bitRate);
232 SHOW_STAT("Compression", m_compression);
233 SHOW_STAT("Page width", m_pageWidth);
234 SHOW_STAT("Page height", m_pageHeight);
235
236 lastStatistics = statistics;
237 }
238 }
239 }
240 }
241 }
242 }
243
244 cout << "\nCompleted." << endl;
245 }
246
247
OnInterrupt(bool)248 bool FaxOPAL::OnInterrupt(bool)
249 {
250 if (m_manager == NULL)
251 return false;
252
253 m_manager->m_completed.Signal();
254 return true;
255 }
256
257
OnClearedCall(OpalCall & call)258 void MyManager::OnClearedCall(OpalCall & call)
259 {
260 switch (call.GetCallEndReason()) {
261 case OpalConnection::EndedByLocalUser :
262 case OpalConnection::EndedByRemoteUser :
263 break;
264
265 default :
266 cerr << "Call error: " << OpalConnection::GetCallEndReasonText(call.GetCallEndReason());
267 }
268
269 m_completed.Signal();
270 }
271
272
OnFaxCompleted(OpalFaxConnection & connection,bool failed)273 void MyFaxEndPoint::OnFaxCompleted(OpalFaxConnection & connection, bool failed)
274 {
275 OpalMediaStatistics stats;
276 connection.GetStatistics(stats);
277 switch (stats.m_fax.m_result) {
278 case -2 :
279 cerr << "Failed to establish T.30";
280 break;
281 case 0 :
282 cout << "Success, "
283 << (connection.IsReceive() ? stats.m_fax.m_rxPages : stats.m_fax.m_txPages)
284 << " of " << stats.m_fax.m_totalPages << " pages";
285 PProcess::Current().SetTerminationValue(0); // Indicate success
286 break;
287 case 41 :
288 cerr << "Failed to open TIFF file";
289 break;
290 case 42 :
291 case 43 :
292 case 44 :
293 case 45 :
294 case 46 :
295 cerr << "Illegal TIFF file";
296 break;
297 default :
298 cerr << "T.30 error " << stats.m_fax.m_result;
299 if (!stats.m_fax.m_errorText.IsEmpty())
300 cerr << " (" << stats.m_fax.m_errorText << ')';
301 }
302 OpalFaxEndPoint::OnFaxCompleted(connection, failed);
303 }
304
305
306 // End of File ///////////////////////////////////////////////////////////////
307