9eeb24a34ba95c7d467826589fd5bd2459bcf0c3
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

1) /* Blinker
Stefan Schuermans update copyright header

Stefan Schuermans authored 5 years ago

2)    Copyright 2011-2019 Stefan Schuermans <stefan@blinkenarea.org>
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

3)    Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4)    a blinkenarea.org project */
5) 
6) #include <chrono>
7) #include <condition_variable>
8) #include <iostream>
9) #include <mutex>
10) #include <sstream>
11) #include <stdarg.h>
12) #include <stdio.h>
13) #include <string>
14) #include <string.h>
15) #include <thread>
Stefan Schuermans implement common sound dir,...

Stefan Schuermans authored 5 years ago

16) #include <vector>
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

17) 
18) #ifdef BLINKER_CFG_LINPHONE
19) #include <linphone/linphonecore.h>
20) #endif
21) 
22) #include "Directory.h"
23) #include "File.h"
24) #include "Module.h"
25) #include "NameFile.h"
26) #include "OpConn.h"
27) #include "OpConnIf.h"
28) #include "SipPhone.h"
29) #include "Time.h"
30) #include "TimeCallee.h"
31) 
32) namespace Blinker {
33) 
34) #ifdef BLINKER_CFG_LINPHONE
35) 
36) /**
37)  * everything using the linphone library happens in a separate thread,
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

38)  * because some calls to linphone take long (up to two seconds)
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

39)  */
40) namespace linphone {
41) 
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

42) /// interlock for liblinphone, higher in mutex hierarchy than shared data mutex
43) static std::mutex g_mtx;
44) 
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

45) static char const *loglevel2str(OrtpLogLevel lev)
46) {
47)   switch(lev) {
48)   case ORTP_DEBUG: return "DEBUG";
49)   case ORTP_MESSAGE: return "MESSAGE";
50)   case ORTP_WARNING: return "WARNING";
51)   case ORTP_ERROR: return "ERROR";
52)   case ORTP_FATAL: return "FATAL";
53)   case ORTP_TRACE: return "TRACE";
54)   case ORTP_LOGLEV_END: return "LOGLEV_END";
55)   default: return "?";
56)   }
57) }
58) 
59) static char const *gstate2str(LinphoneGlobalState gstate)
60) {
61)   switch (gstate) {
62)   case LinphoneGlobalOff: return "Off";
63)   case LinphoneGlobalStartup: return "Startup";
64)   case LinphoneGlobalOn: return "On";
65)   case LinphoneGlobalShutdown: return "Shutdown";
66)   default: return "?";
67)   }
68) }
69) 
70) static char const *rstate2str(LinphoneRegistrationState rstate)
71) {
72)   switch (rstate) {
73)   case LinphoneRegistrationNone: return "None";
74)   case LinphoneRegistrationProgress: return "Progress";
75)   case LinphoneRegistrationOk: return "Ok";
76)   case LinphoneRegistrationCleared: return "Cleared";
77)   case LinphoneRegistrationFailed: return "Failed";
78)   default: return "?";
79)   }
80) }
81) 
82) static char const *cstate2str(LinphoneCallState cstate)
83) {
84)   switch (cstate) {
85)   case LinphoneCallIdle: return "Idle";
86)   case LinphoneCallIncomingReceived: return "IncomingReceived";
87)   case LinphoneCallOutgoingInit: return "OutgoingInit";
88)   case LinphoneCallOutgoingProgress: return "OutgoingProgress";
89)   case LinphoneCallOutgoingRinging: return "OutgoingRinging";
90)   case LinphoneCallOutgoingEarlyMedia: return "OutgoingEarlyMedia";
91)   case LinphoneCallConnected: return "Connected";
92)   case LinphoneCallStreamsRunning: return "StreamsRunning";
93)   case LinphoneCallPausing: return "Pausing";
94)   case LinphoneCallPaused: return "Paused";
95)   case LinphoneCallResuming: return "Resuming";
96)   case LinphoneCallRefered: return "Refered";
97)   case LinphoneCallError: return "Error";
98)   case LinphoneCallEnd: return "End";
99)   case LinphoneCallPausedByRemote: return "PausedByRemote";
100)   case LinphoneCallUpdatedByRemote: return "UpdatedByRemote";
101)   case LinphoneCallIncomingEarlyMedia: return "IncomingEarlyMedia";
102)   case LinphoneCallUpdating: return "Updating";
103)   case LinphoneCallReleased: return "Released";
104)   default: return "?";
105)   }
106) }
107) 
108) std::string glLogFileName; ///< name of global log file
109) std::ofstream glLogStream; ///< stream to open global log file
110) unsigned int glLogLineCnt; ///< number of lines already logged
111) 
112) /**
113)  * @brief log information from linphone library
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

114)  * @param[in] domain log domain - ignored
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

115)  * @param[in] lev log level
116)  * @param[in] fmt format string
117)  * @param[in] args arguments to insert into format string
118)  */
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

119) void log_handler(const char *domain, OrtpLogLevel lev, const char *fmt,
120)                  va_list args)
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

121) {
122)   // (re-)open logfile on first line
123)   if (glLogLineCnt == 0) {
124)     // close old file and clean up (just to be on the safe side)
125)     glLogStream.close();
126)     glLogStream.clear();
127)     // keep previous log file
128)     rename(glLogFileName.c_str(), (glLogFileName + ".prev").c_str());
129)     // open new file
130)     glLogStream.open(glLogFileName.c_str(), std::ios::out);
131)   }
132)   // write log line
133)   char buffer[4096];
134)   vsnprintf(buffer, sizeof(buffer) - 1, fmt, args);
135)   buffer[sizeof(buffer) - 1] = 0;
136)   glLogStream << Time::now().toStr() << " "
137)               << loglevel2str(lev) << " " << buffer << std::endl;
138)   // count lines
139)   ++glLogLineCnt;
140)   // close file if maximum line count reached
141)   if (glLogLineCnt >= 10000) {
142)     // close old file and clean up
143)     glLogStream.close();
144)     glLogStream.clear();
145)     // reset line count, so new file is opened on next line
146)     glLogLineCnt = 0;
147)   }
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

148)   (void)domain;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

149) }
150) 
151) /// global init
152) void init(std::string const &logFileName)
153) {
154)   glLogFileName = logFileName;
155)   glLogLineCnt = 0;
156)   linphone_core_set_log_handler(log_handler);
157) }
158) 
159) /// data of SIP client worker thread
160) struct Data {
161)   SipPhone::ConfigData const *configData; ///< config data for worker thread
162)   SipPhone::SharedData *sharedData; ///< data shared with main Blinker thread
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

163)   LinphoneCoreCbs *callbacks; ///< callbacks object, if active
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

164)   LinphoneCore *lc; ///< linphone core object, if active
165)   LinphoneCall *call; ///< current call, if available
166)   std::string playback; ///< name of playback file
167)   std::ofstream logStream; ///< stream to open log file
168)   unsigned int logLineCnt; ///< number of lines already logged to file
169) };
170) 
171) /**
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

172)  * @brief set playback file
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

173)  * @param[in,out] data data of SIP client
174)  * @param[in] name name of sound file (without extension)
175)  */
176) void set_playback(Data &data, std::string const &name)
177) {
Stefan Schuermans implement common sound dir,...

Stefan Schuermans authored 5 years ago

178)   // search sound directories for file with mathcing name
179)   for (Directory const & soundDir : data.configData->soundDirs) {
180)     File soundFile(soundDir.getFile(name + ".wav"));
181)     if (soundFile.exists()) {
182)       data.playback = soundFile.getPath();
183)       return; // first existing file found is set for playback
184)     }
185)   }
186)   data.playback.clear(); // no file found -> no playback
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

187) }
188) 
Stefan Schuermans fix comments

Stefan Schuermans authored 5 years ago

189) /**
190)  * @brief start playback of sound
191)  * @param[in,out] data data of SIP client
192)  */
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

193) void start_playback(Data &data)
194) {
195)   if (data.lc && ! data.playback.empty()) {
196)     linphone_core_set_play_file(data.lc, data.playback.c_str());
197)   }
198) }
199) 
200) /**
201)  * @brief write log message
202)  * @param[in,out] data data of SIP client
203)  * @param[in] line lie to log
204)  */
205) void log(Data &data, std::string const &line)
206) {
207)   // (re-)open logfile on first line
208)   if (data.logLineCnt == 0) {
209)     // close old file and clean up (just to be on the safe side)
210)     data.logStream.close();
211)     data.logStream.clear();
212)     // keep previous log file
213)     rename(data.configData->logFileName.c_str(),
214)            (data.configData->logFileName + ".prev").c_str());
215)     // open new file
216)     data.logStream.open(data.configData->logFileName.c_str(), std::ios::out);
217)   }
218)   // write log line
219)   data.logStream << Time::now().toStr() << " " << line << std::endl;
220)   // count lines
221)   ++data.logLineCnt;
222)   // close file if maximum line count reached
223)   if (data.logLineCnt >= 10000) {
224)     // close old file and clean up
225)     data.logStream.close();
226)     data.logStream.clear();
227)     // reset line count, so new file is opened on next line
228)     data.logLineCnt = 0;
229)   }
230) }
231) 
232) /**
233)  * @brief global SIP state changed
234)  * @param[in] lc linphone core object
235)  * @param[in] gstate new global SIP state
236)  * @param[in] message informational message
237)  */
238) void global_state_changed(struct _LinphoneCore *lc,
239)                           LinphoneGlobalState gstate,
240)                           const char *message)
241) {
242)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

243)   if (data == nullptr) {
244)     return; // still initializing -> ignore
245)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

246) 
247)   std::stringstream line;
248)   line << "global state changed to " << gstate2str(gstate) << ": " << message;
249)   log(*data, line.str());
250) }
251) 
252) /**
253)  * @brief registration state changed
254)  * @param[in] lc linphone core object
255)  * @param[in] cfg proxy configuration
256)  * @param[in] rstate new registration state
257)  * @param[in] message informational message
258)  */
259) void registration_state_changed(struct _LinphoneCore *lc,
260)                                 LinphoneProxyConfig *cfg,
261)                                 LinphoneRegistrationState rstate,
262)                                 const char *message)
263) {
264)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

265)   if (data == nullptr) {
266)     return; // still initializing -> ignore
267)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

268)   (void)cfg;
269) 
270)   std::stringstream line;
271)   line << "registration state changed to " << rstate2str(rstate) << ": "
272)        << message;
273)   log(*data, line.str());
274) }
275) 
276) /**
277)  * @brief call state changed
278)  * @param[in] lc linphone core object
279)  * @param[in] call call object
280)  * @param[in] cstate new call state
281)  * @param[in] message informational message
282)  */
283) void call_state_changed(struct _LinphoneCore *lc, LinphoneCall *call,
284)                         LinphoneCallState cstate, const char *message)
285) {
286)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

287)   if (data == nullptr) {
288)     return; // still initializing -> ignore
289)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

290) 
291)   std::stringstream line;
292)   line << "call state changed to " << cstate2str(cstate) << ": "
293)        << message;
294)   log(*data, line.str());
295) 
296)   // current call -> handle state changes
297)   if (data->call == call) {
298)     switch (cstate) {
299)     case LinphoneCallStreamsRunning:
300)       start_playback(*data);
301)       break;
302)     case LinphoneCallRefered:
303)     case LinphoneCallError:
304)     case LinphoneCallEnd:
305)     case LinphoneCallReleased:
306)       data->call = nullptr;
307)       {
308)         std::lock_guard<std::mutex> lock(data->sharedData->mtx);
309)         data->sharedData->terminated = true;
310)       }
311)       break;
312)     default:
313)       break;
314)     }
315)   }
316)   // other call, but current call active -> reject call
317)   else if (data->call) {
318)     switch (cstate) {
319)     case LinphoneCallIncomingReceived:
320)       log(*data, "rejecting call (busy)");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

321)       linphone_call_decline(call, LinphoneReasonBusy);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

322)       break;
323)     default:
324)       break;
325)     }
326)   }
327)   // no call active -> accept call
328)   else {
329)     switch (cstate) {
330)     case LinphoneCallIncomingReceived:
331)       data->call = call;
332)       {
333)         std::lock_guard<std::mutex> lock(data->sharedData->mtx);
334)         data->sharedData->accepted = true;
335)       }
336)       data->playback.clear();
337)       log(*data, "accepting call");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

338)       linphone_call_accept(call);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

339)       break;
340)     default:
341)       break;
342)     }
343)   }
344) }
345) 
346) /**
347)  * @brief DTMF received
348)  * @param[in] lc linphone core object
349)  * @param[in] call call object
350)  * @param[in] dtmf DTMF as ASCII character
351)  */
352) void dtmf_received(struct _LinphoneCore* lc, LinphoneCall *call, int dtmf)
353) {
354)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

355)   if (data == nullptr) {
356)     return; // still initializing -> ignore
357)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

358) 
359)   // check if current call
360)   bool current = data->call == call;
361) 
362)   // check DTMF
363)   char c;
364)   if (dtmf >= '0' && dtmf <= '9') {
365)     c = dtmf;
366)   } else if (dtmf == '*') {
367)     c = '*';
368)   } else if (dtmf == '#') {
369)     c = '#';
370)   } else {
371)     c = '?';
372)   }
373) 
374)   std::stringstream line;
375)   line << "dtmf received " << (current ? "(current call)" : "(other call)")
376)        << ": " << c;
377)   log(*data, line.str());
378) 
379)   // ignore DTMF from other calls
380)   if (! current) {
381)     return;
382)   }
383) 
384)   // report DTMF keys
385)   {
386)     std::lock_guard<std::mutex> lock(data->sharedData->mtx);
387)     data->sharedData->dtmf += c;
388)   }
389) }
390) 
391) /**
392)  * @brief register if not active and login data available
393)  * @param[in,out] data data of SIP client
394)  * @param[in] server SIP server
395)  * @param[in] username SIP username
396)  * @param[in] password SIP password
397)  */
398) void do_register(Data &data, std::string const &server,
399)                  std::string const &username, std::string &password)
400) {
401)   if (data.lc) {
402)     return;
403)   }
404)   if (server.empty()) {
405)     return;
406)   }
407) 
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

408)   LinphoneFactory *factory = linphone_factory_get();
409) 
410)   // create linphone callback object
411)   data.callbacks = linphone_factory_create_core_cbs(factory);
412)   if (! data.callbacks) {
413)     log(data, "failed to create linphone callback object");
414)     return;
415)   }
416) 
417)   // set callbacks
418)   linphone_core_cbs_set_global_state_changed(data.callbacks,
419)                                              global_state_changed);
420)   linphone_core_cbs_set_registration_state_changed(data.callbacks,
421)                                                    registration_state_changed);
422)   linphone_core_cbs_set_call_state_changed(data.callbacks, call_state_changed);
423)   linphone_core_cbs_set_dtmf_received(data.callbacks, dtmf_received);
424) 
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

425)   // create linphone core object
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

426)   data.lc = linphone_factory_create_core(factory, data.callbacks, NULL, NULL);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

427)   if (! data.lc) {
428)     log(data, "failed to create linphone core");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

429)     linphone_core_cbs_unref(data.callbacks);
430)     data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

431)     return;
432)   }
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

433)   linphone_core_set_user_data(data.lc, &data);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

434)   log(data, "linphone core created");
435) 
Stefan Schuermans randomize SIP ports for mul...

Stefan Schuermans authored 5 years ago

436)   // set ports to random (to allow multiple SIP endpoints)
437)   LinphoneTransports *transports = linphone_factory_create_transports(factory);
438)   if (transports != nullptr) {
439)     linphone_transports_set_udp_port(transports, LC_SIP_TRANSPORT_RANDOM);
440)     linphone_transports_set_tcp_port(transports, LC_SIP_TRANSPORT_RANDOM);
441)     linphone_transports_set_tls_port(transports, LC_SIP_TRANSPORT_RANDOM);
442)     linphone_transports_set_dtls_port(transports, LC_SIP_TRANSPORT_RANDOM);
443)     linphone_core_set_transports(data.lc, transports);
444)     linphone_transports_unref(transports);
445)     log(data, "linphone core: transports set to random");
446)   } else {
447)     log(data, "linphone core: cannot set transports");
448)   }
449) 
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

450)   // set auth data
451)   if (! username.empty() && ! password.empty()) {
452)     LinphoneAuthInfo *auth_info =
453)         linphone_auth_info_new(username.c_str(), username.c_str(),
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

454)                                password.c_str(), nullptr, "", "");
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

455)     if (! auth_info) {
456)       log(data, "failed to create auth info");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

457)       linphone_core_unref(data.lc);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

458)       data.lc = nullptr;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

459)       linphone_core_cbs_unref(data.callbacks);
460)       data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

461)       return;
462)     }
463)     linphone_core_add_auth_info(data.lc, auth_info);
464)     log(data, "auth info set");
465)   } else {
466)     log(data, "no auth data available");
467)   }
468) 
469)   // configure proxy
470)   std::string sipserver = "sip:" + server;
471)   std::string identity = "sip:" + username + "@" + server;
472)   LinphoneProxyConfig *proxy = linphone_core_create_proxy_config(data.lc);
473)   if (! proxy) {
474)     log(data, "failed to create proxy config");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

475)     linphone_core_unref(data.lc);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

476)     data.lc = nullptr;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

477)     linphone_core_cbs_unref(data.callbacks);
478)     data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

479)     return;
480)   }
481)   linphone_proxy_config_set_server_addr(proxy, sipserver.c_str());
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

482)   LinphoneAddress *addr =
483)       linphone_proxy_config_normalize_sip_uri(proxy, identity.c_str());
484)   if (addr != NULL) {
485)     linphone_proxy_config_set_identity_address(proxy, addr);
486)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

487)   linphone_proxy_config_enable_register(proxy, TRUE);
488)   linphone_core_add_proxy_config(data.lc, proxy);
489)   log(data, "proxy config set");
490) 
491)   // tell linphone not to rind and to use files instead of sound card
492)   linphone_core_set_ring(data.lc, nullptr);
493)   linphone_core_use_files(data.lc, TRUE);
494)   linphone_core_set_play_file(data.lc, nullptr);
495) }
496) 
497) /**
498)  * @brief deregister if active
499)  * @param[in,out] data data of SIP client
500)  */
501) void do_deregister(Data &data)
502) {
503)   if (! data.lc) {
504)     return;
505)   }
506) 
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

507)   log(data, "unreferencing...");
508)   linphone_core_unref(data.lc);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

509)   data.lc = nullptr;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

510)   linphone_core_cbs_unref(data.callbacks);
511)   data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

512)   data.call = nullptr;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

513)   log(data, "unreferenced");
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

514) }
515) 
516) /**
517)  * @brief play sound on call if active
518)  * @param[in,out] data data of SIP client
519)  * @param[in] name name of sound
520)  */
521) void do_play(Data &data, std::string const &name)
522) {
523)   if (! data.lc) {
524)     return;
525)   }
526)   if (! data.call) {
527)     return;
528)   }
529) 
530)   std::stringstream line;
531)   line << "playing sound \"" << name << "\"";
532)   log(data, line.str());
533)   set_playback(data, name);
534)   start_playback(data);
535) }
536) 
537) /**
538)  * @brief hangup call if active
539)  * @param[in,out] data data of SIP client
540)  */
541) void do_hangup(Data &data)
542) {
543)   if (! data.lc) {
544)     return;
545)   }
546)   if (! data.call) {
547)     return;
548)   }
549) 
550)   log(data, "terminating call...");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

551)   linphone_call_terminate(data.call);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

552)   data.call = nullptr;
553)   log(data, "call terminated");
554) }
555) 
556) /**
557)  * @brief linphone worker thread
Stefan Schuermans fix comments

Stefan Schuermans authored 5 years ago

558)  * @param[in] configData configuration data
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

559)  * @param[in,out] data shared data for communication with main Blinker thread
560)  */
561) void worker(SipPhone::ConfigData const &configData,
562)             SipPhone::SharedData &sharedData)
563) {
564)   // set up data of SIP client
565)   Data data;
566)   data.configData = &configData;
567)   data.sharedData = &sharedData;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

568)   data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

569)   data.lc = nullptr;
570)   data.call = nullptr;
571)   data.logLineCnt = 0;
572) 
573)   // main loop of worker - with shared data locked
574)   std::chrono::milliseconds timeout(10);
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

575)   std::unique_lock<std::mutex> g_lock(g_mtx);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

576)   std::unique_lock<std::mutex> lock(sharedData.mtx);
577)   while (sharedData.run) {
578) 
579)     // hangup
580)     if (sharedData.hangup) {
581)       sharedData.hangup = false;
582)       sharedData.reqPlay = false;
583)       sharedData.dtmf.clear();
584)       sharedData.terminated = true;
585)       // execute hangup with unlocked shared data (parallelism!)
586)       lock.unlock();
587)       do_hangup(data);
588)       lock.lock();
589)       continue; // re-check everything after re-locking mutex
590)     }
591) 
592)     // re-register (including initial registration and derigstration)
593)     if (sharedData.reregister) {
594)       sharedData.reregister = false;
595)       sharedData.hangup = false;
596)       sharedData.reqPlay = false;
597)       sharedData.dtmf.clear();
598)       sharedData.terminated = true;
599)       std::string server = sharedData.server;
600)       std::string username = sharedData.username;
601)       std::string password = sharedData.password;
602)       // execute re-registration with unlocked shared data (parallelism!)
603)       lock.unlock();
604)       do_deregister(data);
605)       do_register(data, server, username, password);
606)       lock.lock();
607)       continue; // re-check everything after re-locking mutex
608)     }
609) 
610)     // play sound
611)     if (sharedData.reqPlay) {
612)       sharedData.reqPlay = false;
613)       std::string name = sharedData.reqPlayName;
614)       // execute hangup with unlocked shared data (parallelism!)
615)       lock.unlock();
616)       do_play(data, name);
617)       lock.lock();
618)       continue; // re-check everything after re-locking mutex
619)     }
620) 
621)     // background processing by linphone if active
622)     if (data.lc) {
623)       lock.unlock();
624)       linphone_core_iterate(data.lc);
625)       lock.lock();
626)     }
627) 
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

628)     // nothing to do, wait for signal (with both locks released)
629)     lock.unlock();
630)     sharedData.cond.wait_for(g_lock, timeout);
631)     lock.lock();
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

632) 
633)   } // while (sharedData.run)
634) }
635) 
636) } // namespace linphone
637) 
638) #endif // #ifdef BLINKER_CFG_LINPHONE
639) 
640) /**
641)  * @brief global initialization (call once before using this class)
642)  * @param[in] globalLogDir directory for global SIP log files
643)  */
644) void SipPhone::init(const Directory &globalLogDir)
645) {
646) #ifdef BLINKER_CFG_LINPHONE
647)   linphone::init(globalLogDir.getFile("sip.log").getPath());
648) #endif
649) }
650) 
651) /**
652)  * @brief constructor
653)  * @param[in] name module name
654)  * @param[in] mgrs managers
655)  * @param[in] dirBase base directory
656)  */
657) SipPhone::SipPhone(const std::string &name, Mgrs &mgrs,
658)                    const Directory &dirBase):
659)   Module(name, mgrs, dirBase),
660)   m_fileServer(dirBase.getFile("server")),
661)   m_fileUsername(dirBase.getFile("username")),
662)   m_filePassword(dirBase.getFile("password")),
663)   m_fileTarget(dirBase.getFile("target")),
664)   m_configData(),
665)   m_worker(),
666)   m_sharedData(),
667)   m_curConn(nullptr)
668) {
Stefan Schuermans implement common sound dir,...

Stefan Schuermans authored 5 years ago

669)   m_configData.soundDirs.clear();
670)   m_configData.soundDirs.push_back(dirBase.getSubdir("sounds"));
671)   m_configData.soundDirs.push_back(dirBase.getParent().getParent()
672)                                           .getSubdir("sounds"));
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

673)   m_configData.logFileName = dirBase.getFile("sip.log").getPath();
674)   m_sharedData.run = true;
675)   m_sharedData.reregister = false;
676)   m_sharedData.reqPlay = false;
677)   m_sharedData.hangup = false;
678)   m_sharedData.accepted = false;
679)   m_sharedData.terminated = false;
680) 
681)   m_mgrs.m_callMgr.requestTimeCall(this, Time::now());
682) 
683) #ifdef BLINKER_CFG_LINPHONE
684)   m_worker = std::thread(linphone::worker, std::ref(m_configData),
685)                          std::ref(m_sharedData));
686) #endif
687) 
688)   sipRegister();
689) }
690) 
691) /// virtual destructor
692) SipPhone::~SipPhone()
693) {
694)   if (m_curConn) {
695)     m_curConn->close();
696)     m_curConn = nullptr;
697)   }
698) 
699)   sipDeregister();
700) 
701)   {
702)     std::lock_guard<std::mutex> lock(m_sharedData.mtx);
703)     m_sharedData.run = false;
704)   }
705)   m_sharedData.cond.notify_one();
706) #ifdef BLINKER_CFG_LINPHONE
707)   m_worker.join();
708) #endif
709) 
710)   m_mgrs.m_callMgr.cancelTimeCall(this);
711) }
712) 
713) /// check for update of configuration
714) void SipPhone::updateConfig()
715) {
716)   // server, username or password file modified -> re-connect
717)   if (m_fileServer.checkModified() || m_fileUsername.checkModified() ||
718)                                       m_filePassword.checkModified()) {
719)     // re-register
720)     sipDeregister();
721)     sipRegister();
722)   }
723) 
724)   // target file was modified -> re-get target operator interface to connect to
725)   if (m_fileTarget.checkModified()) {
726)     m_fileTarget.update();
727)   }
728) }
729) 
730) /// callback when requested time reached
731) void SipPhone::timeCall()
732) {
733)   Time now = Time::now();
734) 
735)   // get information from SIP phone worker
736)   bool accepted;
737)   std::string dtmf;
738)   bool terminated;
739)   {
740)     std::lock_guard<std::mutex> lock(m_sharedData.mtx);
741)     accepted = m_sharedData.accepted;
742)     m_sharedData.accepted = false;
743)     dtmf = m_sharedData.dtmf;
744)     m_sharedData.dtmf.clear();
745)     terminated = m_sharedData.terminated;
746)     m_sharedData.terminated = false;
747)   }
748) 
749)   // phone call has terminated or new one has been accepted
750)   if (terminated || accepted) {
751)     // close connection if any
752)     if (m_curConn) {
753)       // request hang up of phone
754)       {
755)         std::lock_guard<std::mutex> lock(m_sharedData.mtx);
756)         m_sharedData.hangup = true;
757)       }
758)       // close and forget connection
759)       m_curConn->close();
760)       m_curConn = nullptr;
761)     }
762)   }
763) 
764)   // phone call has been accepted
765)   if (accepted) {
766)     // try to open new connection to target operator interface
767)     if (m_fileTarget.m_valid) {
768)       m_curConn = m_mgrs.m_opMgr.connect(m_fileTarget.m_obj.m_str, this);
769)     }
Stefan Schuermans sip: hang up after connect...

Stefan Schuermans authored 5 years ago

770)     // operator connection failed -> request hang up of phone
771)     if (! m_curConn) {
772)       std::lock_guard<std::mutex> lock(m_sharedData.mtx);
773)       m_sharedData.hangup = true;
774)     }