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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

147)   (void)domain;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

172)  * @param[in,out] data data of SIP client
173)  * @param[in] name name of sound file (without extension)
174)  */
175) void set_playback(Data &data, std::string const &name)
176) {
177)   Directory dir(data.configData->soundDir);
178)   data.playback = dir.getFile(name + ".wav").getPath();
179) }
180) 
181) void start_playback(Data &data)
182) {
183)   if (data.lc && ! data.playback.empty()) {
184)     linphone_core_set_play_file(data.lc, data.playback.c_str());
185)   }
186) }
187) 
188) /**
189)  * @brief write log message
190)  * @param[in,out] data data of SIP client
191)  * @param[in] line lie to log
192)  */
193) void log(Data &data, std::string const &line)
194) {
195)   // (re-)open logfile on first line
196)   if (data.logLineCnt == 0) {
197)     // close old file and clean up (just to be on the safe side)
198)     data.logStream.close();
199)     data.logStream.clear();
200)     // keep previous log file
201)     rename(data.configData->logFileName.c_str(),
202)            (data.configData->logFileName + ".prev").c_str());
203)     // open new file
204)     data.logStream.open(data.configData->logFileName.c_str(), std::ios::out);
205)   }
206)   // write log line
207)   data.logStream << Time::now().toStr() << " " << line << std::endl;
208)   // count lines
209)   ++data.logLineCnt;
210)   // close file if maximum line count reached
211)   if (data.logLineCnt >= 10000) {
212)     // close old file and clean up
213)     data.logStream.close();
214)     data.logStream.clear();
215)     // reset line count, so new file is opened on next line
216)     data.logLineCnt = 0;
217)   }
218) }
219) 
220) /**
221)  * @brief global SIP state changed
222)  * @param[in] lc linphone core object
223)  * @param[in] gstate new global SIP state
224)  * @param[in] message informational message
225)  */
226) void global_state_changed(struct _LinphoneCore *lc,
227)                           LinphoneGlobalState gstate,
228)                           const char *message)
229) {
230)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

231)   if (data == nullptr) {
232)     return; // still initializing -> ignore
233)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

234) 
235)   std::stringstream line;
236)   line << "global state changed to " << gstate2str(gstate) << ": " << message;
237)   log(*data, line.str());
238) }
239) 
240) /**
241)  * @brief registration state changed
242)  * @param[in] lc linphone core object
243)  * @param[in] cfg proxy configuration
244)  * @param[in] rstate new registration state
245)  * @param[in] message informational message
246)  */
247) void registration_state_changed(struct _LinphoneCore *lc,
248)                                 LinphoneProxyConfig *cfg,
249)                                 LinphoneRegistrationState rstate,
250)                                 const char *message)
251) {
252)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

253)   if (data == nullptr) {
254)     return; // still initializing -> ignore
255)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

256)   (void)cfg;
257) 
258)   std::stringstream line;
259)   line << "registration state changed to " << rstate2str(rstate) << ": "
260)        << message;
261)   log(*data, line.str());
262) }
263) 
264) /**
265)  * @brief call state changed
266)  * @param[in] lc linphone core object
267)  * @param[in] call call object
268)  * @param[in] cstate new call state
269)  * @param[in] message informational message
270)  */
271) void call_state_changed(struct _LinphoneCore *lc, LinphoneCall *call,
272)                         LinphoneCallState cstate, const char *message)
273) {
274)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

275)   if (data == nullptr) {
276)     return; // still initializing -> ignore
277)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

278) 
279)   std::stringstream line;
280)   line << "call state changed to " << cstate2str(cstate) << ": "
281)        << message;
282)   log(*data, line.str());
283) 
284)   // current call -> handle state changes
285)   if (data->call == call) {
286)     switch (cstate) {
287)     case LinphoneCallStreamsRunning:
288)       start_playback(*data);
289)       break;
290)     case LinphoneCallRefered:
291)     case LinphoneCallError:
292)     case LinphoneCallEnd:
293)     case LinphoneCallReleased:
294)       data->call = nullptr;
295)       {
296)         std::lock_guard<std::mutex> lock(data->sharedData->mtx);
297)         data->sharedData->terminated = true;
298)       }
299)       break;
300)     default:
301)       break;
302)     }
303)   }
304)   // other call, but current call active -> reject call
305)   else if (data->call) {
306)     switch (cstate) {
307)     case LinphoneCallIncomingReceived:
308)       log(*data, "rejecting call (busy)");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

310)       break;
311)     default:
312)       break;
313)     }
314)   }
315)   // no call active -> accept call
316)   else {
317)     switch (cstate) {
318)     case LinphoneCallIncomingReceived:
319)       data->call = call;
320)       {
321)         std::lock_guard<std::mutex> lock(data->sharedData->mtx);
322)         data->sharedData->accepted = true;
323)       }
324)       data->playback.clear();
325)       log(*data, "accepting call");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

326)       linphone_call_accept(call);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

327)       break;
328)     default:
329)       break;
330)     }
331)   }
332) }
333) 
334) /**
335)  * @brief DTMF received
336)  * @param[in] lc linphone core object
337)  * @param[in] call call object
338)  * @param[in] dtmf DTMF as ASCII character
339)  */
340) void dtmf_received(struct _LinphoneCore* lc, LinphoneCall *call, int dtmf)
341) {
342)   Data *data = (Data*)linphone_core_get_user_data(lc);
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

343)   if (data == nullptr) {
344)     return; // still initializing -> ignore
345)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

396)   LinphoneFactory *factory = linphone_factory_get();
397) 
398)   // create linphone callback object
399)   data.callbacks = linphone_factory_create_core_cbs(factory);
400)   if (! data.callbacks) {
401)     log(data, "failed to create linphone callback object");
402)     return;
403)   }
404) 
405)   // set callbacks
406)   linphone_core_cbs_set_global_state_changed(data.callbacks,
407)                                              global_state_changed);
408)   linphone_core_cbs_set_registration_state_changed(data.callbacks,
409)                                                    registration_state_changed);
410)   linphone_core_cbs_set_call_state_changed(data.callbacks, call_state_changed);
411)   linphone_core_cbs_set_dtmf_received(data.callbacks, dtmf_received);
412) 
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

417)     linphone_core_cbs_unref(data.callbacks);
418)     data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

419)     return;
420)   }
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

424)   // set ports to random (to allow multiple SIP endpoints)
425)   LinphoneTransports *transports = linphone_factory_create_transports(factory);
426)   if (transports != nullptr) {
427)     linphone_transports_set_udp_port(transports, LC_SIP_TRANSPORT_RANDOM);
428)     linphone_transports_set_tcp_port(transports, LC_SIP_TRANSPORT_RANDOM);
429)     linphone_transports_set_tls_port(transports, LC_SIP_TRANSPORT_RANDOM);
430)     linphone_transports_set_dtls_port(transports, LC_SIP_TRANSPORT_RANDOM);
431)     linphone_core_set_transports(data.lc, transports);
432)     linphone_transports_unref(transports);
433)     log(data, "linphone core: transports set to random");
434)   } else {
435)     log(data, "linphone core: cannot set transports");
436)   }
437) 
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

438)   // set auth data
439)   if (! username.empty() && ! password.empty()) {
440)     LinphoneAuthInfo *auth_info =
441)         linphone_auth_info_new(username.c_str(), username.c_str(),
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

447)       linphone_core_cbs_unref(data.callbacks);
448)       data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

449)       return;
450)     }
451)     linphone_core_add_auth_info(data.lc, auth_info);
452)     log(data, "auth info set");
453)   } else {
454)     log(data, "no auth data available");
455)   }
456) 
457)   // configure proxy
458)   std::string sipserver = "sip:" + server;
459)   std::string identity = "sip:" + username + "@" + server;
460)   LinphoneProxyConfig *proxy = linphone_core_create_proxy_config(data.lc);
461)   if (! proxy) {
462)     log(data, "failed to create proxy config");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

465)     linphone_core_cbs_unref(data.callbacks);
466)     data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

467)     return;
468)   }
469)   linphone_proxy_config_set_server_addr(proxy, sipserver.c_str());
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

470)   LinphoneAddress *addr =
471)       linphone_proxy_config_normalize_sip_uri(proxy, identity.c_str());
472)   if (addr != NULL) {
473)     linphone_proxy_config_set_identity_address(proxy, addr);
474)   }
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

475)   linphone_proxy_config_enable_register(proxy, TRUE);
476)   linphone_core_add_proxy_config(data.lc, proxy);
477)   log(data, "proxy config set");
478) 
479)   // tell linphone not to rind and to use files instead of sound card
480)   linphone_core_set_ring(data.lc, nullptr);
481)   linphone_core_use_files(data.lc, TRUE);
482)   linphone_core_set_play_file(data.lc, nullptr);
483) }
484) 
485) /**
486)  * @brief deregister if active
487)  * @param[in,out] data data of SIP client
488)  */
489) void do_deregister(Data &data)
490) {
491)   if (! data.lc) {
492)     return;
493)   }
494) 
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

495)   log(data, "unreferencing...");
496)   linphone_core_unref(data.lc);
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

498)   linphone_core_cbs_unref(data.callbacks);
499)   data.callbacks = nullptr;
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

502) }
503) 
504) /**
505)  * @brief play sound on call if active
506)  * @param[in,out] data data of SIP client
507)  * @param[in] name name of sound
508)  */
509) void do_play(Data &data, std::string const &name)
510) {
511)   if (! data.lc) {
512)     return;
513)   }
514)   if (! data.call) {
515)     return;
516)   }
517) 
518)   std::stringstream line;
519)   line << "playing sound \"" << name << "\"";
520)   log(data, line.str());
521)   set_playback(data, name);
522)   start_playback(data);
523) }
524) 
525) /**
526)  * @brief hangup call if active
527)  * @param[in,out] data data of SIP client
528)  */
529) void do_hangup(Data &data)
530) {
531)   if (! data.lc) {
532)     return;
533)   }
534)   if (! data.call) {
535)     return;
536)   }
537) 
538)   log(data, "terminating call...");
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

540)   data.call = nullptr;
541)   log(data, "call terminated");
542) }
543) 
544) /**
545)  * @brief linphone worker thread
546)  * @param[in] soundDir directory of sound files
547)  * @param[in,out] data shared data for communication with main Blinker thread
548)  */
549) void worker(SipPhone::ConfigData const &configData,
550)             SipPhone::SharedData &sharedData)
551) {
552)   // set up data of SIP client
553)   Data data;
554)   data.configData = &configData;
555)   data.sharedData = &sharedData;
Stefan Schuermans update to liblinphone 3.12

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

557)   data.lc = nullptr;
558)   data.call = nullptr;
559)   data.logLineCnt = 0;
560) 
561)   // main loop of worker - with shared data locked
562)   std::chrono::milliseconds timeout(10);
Stefan Schuermans fix liblinphone threading i...

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

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

Stefan Schuermans authored 5 years ago

616)     // nothing to do, wait for signal (with both locks released)
617)     lock.unlock();
618)     sharedData.cond.wait_for(g_lock, timeout);
619)     lock.lock();
Stefan Schuermans implement SIP

Stefan Schuermans authored 5 years ago

620) 
621)   } // while (sharedData.run)
622) }
623) 
624) } // namespace linphone
625) 
626) #endif // #ifdef BLINKER_CFG_LINPHONE
627) 
628) /**
629)  * @brief global initialization (call once before using this class)
630)  * @param[in] globalLogDir directory for global SIP log files
631)  */
632) void SipPhone::init(const Directory &globalLogDir)
633) {
634) #ifdef BLINKER_CFG_LINPHONE
635)   linphone::init(globalLogDir.getFile("sip.log").getPath());
636) #endif
637) }
638) 
639) /**
640)  * @brief constructor
641)  * @param[in] name module name
642)  * @param[in] mgrs managers
643)  * @param[in] dirBase base directory
644)  */
645) SipPhone::SipPhone(const std::string &name, Mgrs &mgrs,
646)                    const Directory &dirBase):
647)   Module(name, mgrs, dirBase),
648)   m_fileServer(dirBase.getFile("server")),
649)   m_fileUsername(dirBase.getFile("username")),
650)   m_filePassword(dirBase.getFile("password")),
651)   m_fileTarget(dirBase.getFile("target")),
652)   m_configData(),
653)   m_worker(),
654)   m_sharedData(),
655)   m_curConn(nullptr)
656) {
657)   m_configData.soundDir = dirBase.getSubdir("sounds").getPath();
658)   m_configData.logFileName = dirBase.getFile("sip.log").getPath();
659)   m_sharedData.run = true;
660)   m_sharedData.reregister = false;
661)   m_sharedData.reqPlay = false;
662)   m_sharedData.hangup = false;
663)   m_sharedData.accepted = false;
664)   m_sharedData.terminated = false;
665) 
666)   m_mgrs.m_callMgr.requestTimeCall(this, Time::now());
667) 
668) #ifdef BLINKER_CFG_LINPHONE
669)   m_worker = std::thread(linphone::worker, std::ref(m_configData),
670)                          std::ref(m_sharedData));
671) #endif
672) 
673)   sipRegister();
674) }
675) 
676) /// virtual destructor
677) SipPhone::~SipPhone()
678) {
679)   if (m_curConn) {
680)     m_curConn->close();
681)     m_curConn = nullptr;
682)   }
683) 
684)   sipDeregister();
685) 
686)   {
687)     std::lock_guard<std::mutex> lock(m_sharedData.mtx);
688)     m_sharedData.run = false;
689)   }
690)   m_sharedData.cond.notify_one();
691) #ifdef BLINKER_CFG_LINPHONE
692)   m_worker.join();
693) #endif
694) 
695)   m_mgrs.m_callMgr.cancelTimeCall(this);
696) }
697) 
698) /// check for update of configuration
699) void SipPhone::updateConfig()
700) {
701)   // server, username or password file modified -> re-connect
702)   if (m_fileServer.checkModified() || m_fileUsername.checkModified() ||
703)                                       m_filePassword.checkModified()) {
704)     // re-register
705)     sipDeregister();
706)     sipRegister();
707)   }
708) 
709)   // target file was modified -> re-get target operator interface to connect to
710)   if (m_fileTarget.checkModified()) {
711)     m_fileTarget.update();
712)   }
713) }
714) 
715) /// callback when requested time reached
716) void SipPhone::timeCall()
717) {
718)   Time now = Time::now();
719) 
720)   // get information from SIP phone worker
721)   bool accepted;
722)   std::string dtmf;
723)   bool terminated;
724)   {
725)     std::lock_guard<std::mutex> lock(m_sharedData.mtx);
726)     accepted = m_sharedData.accepted;
727)     m_sharedData.accepted = false;
728)     dtmf = m_sharedData.dtmf;
729)     m_sharedData.dtmf.clear();
730)     terminated = m_sharedData.terminated;
731)     m_sharedData.terminated = false;
732)   }
733) 
734)   // phone call has terminated or new one has been accepted
735)   if (terminated || accepted) {
736)     // close connection if any
737)     if (m_curConn) {
738)       // request hang up of phone
739)       {
740)         std::lock_guard<std::mutex> lock(m_sharedData.mtx);
741)         m_sharedData.hangup = true;
742)       }
743)       // close and forget connection
744)       m_curConn->close();
745)       m_curConn = nullptr;
746)     }
747)   }
748) 
749)   // phone call has been accepted
750)   if (accepted) {
751)     // try to open new connection to target operator interface
752)     if (m_fileTarget.m_valid) {
753)       m_curConn = m_mgrs.m_opMgr.connect(m_fileTarget.m_obj.m_str, this);
754)     }
Stefan Schuermans sip: hang up after connect...

Stefan Schuermans authored 5 years ago

755)     // operator connection failed -> request hang up of phone
756)     if (! m_curConn) {
757)       std::lock_guard<std::mutex> lock(m_sharedData.mtx);
758)       m_sharedData.hangup = true;
759)     }