1 /* $Id$ */
2 /*
3 * Copyright (C) 2013 Teluu Inc. (http://www.teluu.com)
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include <pjsua2/endpoint.hpp>
20 #include <pjsua2/account.hpp>
21 #include <pjsua2/call.hpp>
22 #include <pjsua2/presence.hpp>
23 #include <algorithm>
24 #include "util.hpp"
25
26 using namespace pj;
27 using namespace std;
28
29 #include <pjsua-lib/pjsua_internal.h> /* For retrieving pjsua threads */
30
31 #define THIS_FILE "endpoint.cpp"
32 #define MAX_STUN_SERVERS 32
33 #define TIMER_SIGNATURE 0x600D878A
34 #define MAX_CODEC_NUM 64
35
36 struct UserTimer
37 {
38 pj_uint32_t signature;
39 OnTimerParam prm;
40 pj_timer_entry entry;
41 };
42
43 Endpoint *Endpoint::instance_;
44
45 ///////////////////////////////////////////////////////////////////////////////
46
TlsInfo()47 TlsInfo::TlsInfo() : cipher(PJ_TLS_UNKNOWN_CIPHER),
48 empty(true)
49 {
50 }
51
isEmpty() const52 bool TlsInfo::isEmpty() const
53 {
54 return empty;
55 }
56
fromPj(const pjsip_tls_state_info & info)57 void TlsInfo::fromPj(const pjsip_tls_state_info &info)
58 {
59 #if defined(PJ_HAS_SSL_SOCK) && PJ_HAS_SSL_SOCK != 0
60 pj_ssl_sock_info *ssock_info = info.ssl_sock_info;
61 char straddr[PJ_INET6_ADDRSTRLEN+10];
62 const char *verif_msgs[32];
63 unsigned verif_msg_cnt;
64
65 empty = false;
66 established = PJ2BOOL(ssock_info->established);
67 protocol = ssock_info->proto;
68 cipher = ssock_info->cipher;
69 cipherName = pj_ssl_cipher_name(ssock_info->cipher);
70 pj_sockaddr_print(&ssock_info->local_addr, straddr, sizeof(straddr), 3);
71 localAddr = straddr;
72 pj_sockaddr_print(&ssock_info->remote_addr, straddr, sizeof(straddr),3);
73 remoteAddr = straddr;
74 verifyStatus = ssock_info->verify_status;
75 if (ssock_info->local_cert_info)
76 localCertInfo.fromPj(*ssock_info->local_cert_info);
77 if (ssock_info->remote_cert_info)
78 remoteCertInfo.fromPj(*ssock_info->remote_cert_info);
79
80 /* Dump server TLS certificate verification result */
81 verif_msg_cnt = PJ_ARRAY_SIZE(verif_msgs);
82 pj_ssl_cert_get_verify_status_strings(ssock_info->verify_status,
83 verif_msgs, &verif_msg_cnt);
84 for (unsigned i = 0; i < verif_msg_cnt; ++i) {
85 verifyMsgs.push_back(verif_msgs[i]);
86 }
87 #else
88 PJ_UNUSED_ARG(info);
89 #endif
90 }
91
SslCertInfo()92 SslCertInfo::SslCertInfo()
93 : empty(true)
94 {
95 }
96
isEmpty() const97 bool SslCertInfo::isEmpty() const
98 {
99 return empty;
100 }
101
fromPj(const pj_ssl_cert_info & info)102 void SslCertInfo::fromPj(const pj_ssl_cert_info &info)
103 {
104 empty = false;
105 version = info.version;
106 pj_memcpy(serialNo, info.serial_no, sizeof(info.serial_no));
107 subjectCn = pj2Str(info.subject.cn);
108 subjectInfo = pj2Str(info.subject.info);
109 issuerCn = pj2Str(info.issuer.cn);
110 issuerInfo = pj2Str(info.issuer.info);
111 validityStart.fromPj(info.validity.start);
112 validityEnd.fromPj(info.validity.end);
113 validityGmt = PJ2BOOL(info.validity.gmt);
114 raw = pj2Str(info.raw);
115
116 for (unsigned i = 0; i < info.subj_alt_name.cnt; i++) {
117 SslCertName cname;
118 cname.type = info.subj_alt_name.entry[i].type;
119 cname.name = pj2Str(info.subj_alt_name.entry[i].name);
120 subjectAltName.push_back(cname);
121 }
122 }
123
124 ///////////////////////////////////////////////////////////////////////////////
IpChangeParam()125 IpChangeParam::IpChangeParam()
126 {
127 pjsua_ip_change_param param;
128 pjsua_ip_change_param_default(¶m);
129 fromPj(param);
130 }
131
132
toPj() const133 pjsua_ip_change_param IpChangeParam::toPj() const
134 {
135 pjsua_ip_change_param param;
136 pjsua_ip_change_param_default(¶m);
137
138 param.restart_listener = restartListener;
139 param.restart_lis_delay = restartLisDelay;
140
141 return param;
142 }
143
144
fromPj(const pjsua_ip_change_param & param)145 void IpChangeParam::fromPj(const pjsua_ip_change_param ¶m)
146 {
147 restartListener = PJ2BOOL(param.restart_listener);
148 restartLisDelay = param.restart_lis_delay;
149 }
150
151 ///////////////////////////////////////////////////////////////////////////////
UaConfig()152 UaConfig::UaConfig()
153 : mainThreadOnly(false)
154 {
155 pjsua_config ua_cfg;
156
157 pjsua_config_default(&ua_cfg);
158 fromPj(ua_cfg);
159 }
160
fromPj(const pjsua_config & ua_cfg)161 void UaConfig::fromPj(const pjsua_config &ua_cfg)
162 {
163 unsigned i;
164
165 this->maxCalls = ua_cfg.max_calls;
166 this->threadCnt = ua_cfg.thread_cnt;
167 this->userAgent = pj2Str(ua_cfg.user_agent);
168
169 for (i=0; i<ua_cfg.nameserver_count; ++i) {
170 this->nameserver.push_back(pj2Str(ua_cfg.nameserver[i]));
171 }
172
173 for (i=0; i<ua_cfg.stun_srv_cnt; ++i) {
174 this->stunServer.push_back(pj2Str(ua_cfg.stun_srv[i]));
175 }
176 for (i=0; i<ua_cfg.outbound_proxy_cnt; ++i) {
177 this->outboundProxies.push_back(pj2Str(ua_cfg.outbound_proxy[i]));
178 }
179
180 this->stunTryIpv6 = PJ2BOOL(ua_cfg.stun_try_ipv6);
181 this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
182 this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
183 this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
184 }
185
toPj() const186 pjsua_config UaConfig::toPj() const
187 {
188 unsigned i;
189 pjsua_config pua_cfg;
190
191 pjsua_config_default(&pua_cfg);
192
193 pua_cfg.max_calls = this->maxCalls;
194 pua_cfg.thread_cnt = this->threadCnt;
195 pua_cfg.user_agent = str2Pj(this->userAgent);
196
197 for (i=0; i<this->nameserver.size() && i<PJ_ARRAY_SIZE(pua_cfg.nameserver);
198 ++i)
199 {
200 pua_cfg.nameserver[i] = str2Pj(this->nameserver[i]);
201 }
202 pua_cfg.nameserver_count = i;
203
204 for (i=0; i<this->stunServer.size() && i<PJ_ARRAY_SIZE(pua_cfg.stun_srv);
205 ++i)
206 {
207 pua_cfg.stun_srv[i] = str2Pj(this->stunServer[i]);
208 }
209 pua_cfg.stun_srv_cnt = i;
210
211 for (i=0; i<this->outboundProxies.size() &&
212 i<PJ_ARRAY_SIZE(pua_cfg.outbound_proxy); ++i)
213 {
214 pua_cfg.outbound_proxy[i] = str2Pj(this->outboundProxies[i]);
215 }
216 pua_cfg.outbound_proxy_cnt= i;
217
218 pua_cfg.nat_type_in_sdp = this->natTypeInSdp;
219 pua_cfg.enable_unsolicited_mwi = this->mwiUnsolicitedEnabled;
220 pua_cfg.stun_try_ipv6 = this->stunTryIpv6;
221 pua_cfg.stun_ignore_failure = this->stunIgnoreFailure;
222
223 return pua_cfg;
224 }
225
readObject(const ContainerNode & node)226 void UaConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error)
227 {
228 ContainerNode this_node = node.readContainer("UaConfig");
229
230 NODE_READ_UNSIGNED( this_node, maxCalls);
231 NODE_READ_UNSIGNED( this_node, threadCnt);
232 NODE_READ_BOOL ( this_node, mainThreadOnly);
233 NODE_READ_STRINGV ( this_node, nameserver);
234 NODE_READ_STRING ( this_node, userAgent);
235 NODE_READ_STRINGV ( this_node, stunServer);
236 NODE_READ_BOOL ( this_node, stunTryIpv6);
237 NODE_READ_BOOL ( this_node, stunIgnoreFailure);
238 NODE_READ_INT ( this_node, natTypeInSdp);
239 NODE_READ_BOOL ( this_node, mwiUnsolicitedEnabled);
240 }
241
writeObject(ContainerNode & node) const242 void UaConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
243 {
244 ContainerNode this_node = node.writeNewContainer("UaConfig");
245
246 NODE_WRITE_UNSIGNED( this_node, maxCalls);
247 NODE_WRITE_UNSIGNED( this_node, threadCnt);
248 NODE_WRITE_BOOL ( this_node, mainThreadOnly);
249 NODE_WRITE_STRINGV ( this_node, nameserver);
250 NODE_WRITE_STRING ( this_node, userAgent);
251 NODE_WRITE_STRINGV ( this_node, stunServer);
252 NODE_WRITE_BOOL ( this_node, stunTryIpv6);
253 NODE_WRITE_BOOL ( this_node, stunIgnoreFailure);
254 NODE_WRITE_INT ( this_node, natTypeInSdp);
255 NODE_WRITE_BOOL ( this_node, mwiUnsolicitedEnabled);
256 }
257
258 ///////////////////////////////////////////////////////////////////////////////
259
LogConfig()260 LogConfig::LogConfig()
261 {
262 pjsua_logging_config lc;
263
264 pjsua_logging_config_default(&lc);
265 fromPj(lc);
266 }
267
fromPj(const pjsua_logging_config & lc)268 void LogConfig::fromPj(const pjsua_logging_config &lc)
269 {
270 this->msgLogging = lc.msg_logging;
271 this->level = lc.level;
272 this->consoleLevel = lc.console_level;
273 this->decor = lc.decor;
274 this->filename = pj2Str(lc.log_filename);
275 this->fileFlags = lc.log_file_flags;
276 this->writer = NULL;
277 }
278
toPj() const279 pjsua_logging_config LogConfig::toPj() const
280 {
281 pjsua_logging_config lc;
282
283 pjsua_logging_config_default(&lc);
284
285 lc.msg_logging = this->msgLogging;
286 lc.level = this->level;
287 lc.console_level = this->consoleLevel;
288 lc.decor = this->decor;
289 lc.log_file_flags = this->fileFlags;
290 lc.log_filename = str2Pj(this->filename);
291
292 return lc;
293 }
294
readObject(const ContainerNode & node)295 void LogConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error)
296 {
297 ContainerNode this_node = node.readContainer("LogConfig");
298
299 NODE_READ_UNSIGNED( this_node, msgLogging);
300 NODE_READ_UNSIGNED( this_node, level);
301 NODE_READ_UNSIGNED( this_node, consoleLevel);
302 NODE_READ_UNSIGNED( this_node, decor);
303 NODE_READ_STRING ( this_node, filename);
304 NODE_READ_UNSIGNED( this_node, fileFlags);
305 }
306
writeObject(ContainerNode & node) const307 void LogConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
308 {
309 ContainerNode this_node = node.writeNewContainer("LogConfig");
310
311 NODE_WRITE_UNSIGNED( this_node, msgLogging);
312 NODE_WRITE_UNSIGNED( this_node, level);
313 NODE_WRITE_UNSIGNED( this_node, consoleLevel);
314 NODE_WRITE_UNSIGNED( this_node, decor);
315 NODE_WRITE_STRING ( this_node, filename);
316 NODE_WRITE_UNSIGNED( this_node, fileFlags);
317 }
318
319 ///////////////////////////////////////////////////////////////////////////////
320
MediaConfig()321 MediaConfig::MediaConfig()
322 {
323 pjsua_media_config mc;
324
325 pjsua_media_config_default(&mc);
326 fromPj(mc);
327 }
328
fromPj(const pjsua_media_config & mc)329 void MediaConfig::fromPj(const pjsua_media_config &mc)
330 {
331 this->clockRate = mc.clock_rate;
332 this->sndClockRate = mc.snd_clock_rate;
333 this->channelCount = mc.channel_count;
334 this->audioFramePtime = mc.audio_frame_ptime;
335 this->maxMediaPorts = mc.max_media_ports;
336 this->hasIoqueue = PJ2BOOL(mc.has_ioqueue);
337 this->threadCnt = mc.thread_cnt;
338 this->quality = mc.quality;
339 this->ptime = mc.ptime;
340 this->noVad = PJ2BOOL(mc.no_vad);
341 this->ilbcMode = mc.ilbc_mode;
342 this->txDropPct = mc.tx_drop_pct;
343 this->rxDropPct = mc.rx_drop_pct;
344 this->ecOptions = mc.ec_options;
345 this->ecTailLen = mc.ec_tail_len;
346 this->sndRecLatency = mc.snd_rec_latency;
347 this->sndPlayLatency = mc.snd_play_latency;
348 this->jbInit = mc.jb_init;
349 this->jbMinPre = mc.jb_min_pre;
350 this->jbMaxPre = mc.jb_max_pre;
351 this->jbMax = mc.jb_max;
352 this->jbDiscardAlgo = mc.jb_discard_algo;
353 this->sndAutoCloseTime = mc.snd_auto_close_time;
354 this->vidPreviewEnableNative = PJ2BOOL(mc.vid_preview_enable_native);
355 }
356
toPj() const357 pjsua_media_config MediaConfig::toPj() const
358 {
359 pjsua_media_config mcfg;
360
361 pjsua_media_config_default(&mcfg);
362
363 mcfg.clock_rate = this->clockRate;
364 mcfg.snd_clock_rate = this->sndClockRate;
365 mcfg.channel_count = this->channelCount;
366 mcfg.audio_frame_ptime = this->audioFramePtime;
367 mcfg.max_media_ports = this->maxMediaPorts;
368 mcfg.has_ioqueue = this->hasIoqueue;
369 mcfg.thread_cnt = this->threadCnt;
370 mcfg.quality = this->quality;
371 mcfg.ptime = this->ptime;
372 mcfg.no_vad = this->noVad;
373 mcfg.ilbc_mode = this->ilbcMode;
374 mcfg.tx_drop_pct = this->txDropPct;
375 mcfg.rx_drop_pct = this->rxDropPct;
376 mcfg.ec_options = this->ecOptions;
377 mcfg.ec_tail_len = this->ecTailLen;
378 mcfg.snd_rec_latency = this->sndRecLatency;
379 mcfg.snd_play_latency = this->sndPlayLatency;
380 mcfg.jb_init = this->jbInit;
381 mcfg.jb_min_pre = this->jbMinPre;
382 mcfg.jb_max_pre = this->jbMaxPre;
383 mcfg.jb_max = this->jbMax;
384 mcfg.jb_discard_algo = this->jbDiscardAlgo;
385 mcfg.snd_auto_close_time = this->sndAutoCloseTime;
386 mcfg.vid_preview_enable_native = this->vidPreviewEnableNative;
387
388 return mcfg;
389 }
390
readObject(const ContainerNode & node)391 void MediaConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error)
392 {
393 ContainerNode this_node = node.readContainer("MediaConfig");
394
395 NODE_READ_UNSIGNED( this_node, clockRate);
396 NODE_READ_UNSIGNED( this_node, sndClockRate);
397 NODE_READ_UNSIGNED( this_node, channelCount);
398 NODE_READ_UNSIGNED( this_node, audioFramePtime);
399 NODE_READ_UNSIGNED( this_node, maxMediaPorts);
400 NODE_READ_BOOL ( this_node, hasIoqueue);
401 NODE_READ_UNSIGNED( this_node, threadCnt);
402 NODE_READ_UNSIGNED( this_node, quality);
403 NODE_READ_UNSIGNED( this_node, ptime);
404 NODE_READ_BOOL ( this_node, noVad);
405 NODE_READ_UNSIGNED( this_node, ilbcMode);
406 NODE_READ_UNSIGNED( this_node, txDropPct);
407 NODE_READ_UNSIGNED( this_node, rxDropPct);
408 NODE_READ_UNSIGNED( this_node, ecOptions);
409 NODE_READ_UNSIGNED( this_node, ecTailLen);
410 NODE_READ_UNSIGNED( this_node, sndRecLatency);
411 NODE_READ_UNSIGNED( this_node, sndPlayLatency);
412 NODE_READ_INT ( this_node, jbInit);
413 NODE_READ_INT ( this_node, jbMinPre);
414 NODE_READ_INT ( this_node, jbMaxPre);
415 NODE_READ_INT ( this_node, jbMax);
416 NODE_READ_NUM_T ( this_node, pjmedia_jb_discard_algo, jbDiscardAlgo);
417 NODE_READ_INT ( this_node, sndAutoCloseTime);
418 NODE_READ_BOOL ( this_node, vidPreviewEnableNative);
419 }
420
writeObject(ContainerNode & node) const421 void MediaConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
422 {
423 ContainerNode this_node = node.writeNewContainer("MediaConfig");
424
425 NODE_WRITE_UNSIGNED( this_node, clockRate);
426 NODE_WRITE_UNSIGNED( this_node, sndClockRate);
427 NODE_WRITE_UNSIGNED( this_node, channelCount);
428 NODE_WRITE_UNSIGNED( this_node, audioFramePtime);
429 NODE_WRITE_UNSIGNED( this_node, maxMediaPorts);
430 NODE_WRITE_BOOL ( this_node, hasIoqueue);
431 NODE_WRITE_UNSIGNED( this_node, threadCnt);
432 NODE_WRITE_UNSIGNED( this_node, quality);
433 NODE_WRITE_UNSIGNED( this_node, ptime);
434 NODE_WRITE_BOOL ( this_node, noVad);
435 NODE_WRITE_UNSIGNED( this_node, ilbcMode);
436 NODE_WRITE_UNSIGNED( this_node, txDropPct);
437 NODE_WRITE_UNSIGNED( this_node, rxDropPct);
438 NODE_WRITE_UNSIGNED( this_node, ecOptions);
439 NODE_WRITE_UNSIGNED( this_node, ecTailLen);
440 NODE_WRITE_UNSIGNED( this_node, sndRecLatency);
441 NODE_WRITE_UNSIGNED( this_node, sndPlayLatency);
442 NODE_WRITE_INT ( this_node, jbInit);
443 NODE_WRITE_INT ( this_node, jbMinPre);
444 NODE_WRITE_INT ( this_node, jbMaxPre);
445 NODE_WRITE_INT ( this_node, jbMax);
446 NODE_WRITE_NUM_T ( this_node, pjmedia_jb_discard_algo, jbDiscardAlgo);
447 NODE_WRITE_INT ( this_node, sndAutoCloseTime);
448 NODE_WRITE_BOOL ( this_node, vidPreviewEnableNative);
449 }
450
451 ///////////////////////////////////////////////////////////////////////////////
452
readObject(const ContainerNode & node)453 void EpConfig::readObject(const ContainerNode &node) PJSUA2_THROW(Error)
454 {
455 ContainerNode this_node = node.readContainer("EpConfig");
456 NODE_READ_OBJ( this_node, uaConfig);
457 NODE_READ_OBJ( this_node, logConfig);
458 NODE_READ_OBJ( this_node, medConfig);
459 }
460
writeObject(ContainerNode & node) const461 void EpConfig::writeObject(ContainerNode &node) const PJSUA2_THROW(Error)
462 {
463 ContainerNode this_node = node.writeNewContainer("EpConfig");
464 NODE_WRITE_OBJ( this_node, uaConfig);
465 NODE_WRITE_OBJ( this_node, logConfig);
466 NODE_WRITE_OBJ( this_node, medConfig);
467 }
468
469 ///////////////////////////////////////////////////////////////////////////////
470 /* Class to post log to main thread */
471 struct PendingLog : public PendingJob
472 {
473 LogEntry entry;
executePendingLog474 virtual void execute(bool is_pending)
475 {
476 PJ_UNUSED_ARG(is_pending);
477 Endpoint::instance().utilLogWrite(entry);
478 }
479 };
480
481 ///////////////////////////////////////////////////////////////////////////////
482 /*
483 * Endpoint instance
484 */
Endpoint()485 Endpoint::Endpoint():
486 #if !DEPRECATED_FOR_TICKET_2232
487 mediaListMutex(NULL),
488 #endif
489 writer(NULL), threadDescMutex(NULL), mainThreadOnly(false),
490 mainThread(NULL), pendingJobSize(0)
491 {
492 if (instance_) {
493 PJSUA2_RAISE_ERROR(PJ_EEXISTS);
494 }
495
496 instance_ = this;
497 }
498
instance()499 Endpoint& Endpoint::instance() PJSUA2_THROW(Error)
500 {
501 if (!instance_) {
502 PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
503 }
504 return *instance_;
505 }
506
~Endpoint()507 Endpoint::~Endpoint()
508 {
509 while (!pendingJobs.empty()) {
510 delete pendingJobs.front();
511 pendingJobs.pop_front();
512 }
513
514 #if !DEPRECATED_FOR_TICKET_2232
515 clearCodecInfoList(codecInfoList);
516 clearCodecInfoList(videoCodecInfoList);
517 #endif
518
519 try {
520 libDestroy();
521 } catch (Error &err) {
522 // Ignore
523 PJ_UNUSED_ARG(err);
524 }
525
526 instance_ = NULL;
527 }
528
utilAddPendingJob(PendingJob * job)529 void Endpoint::utilAddPendingJob(PendingJob *job)
530 {
531 enum {
532 MAX_PENDING_JOBS = 1024
533 };
534
535 /* See if we can execute immediately */
536 if (!mainThreadOnly || pj_thread_this()==mainThread) {
537 job->execute(false);
538 delete job;
539 return;
540 }
541
542 if (pendingJobSize > MAX_PENDING_JOBS) {
543 enum { NUMBER_TO_DISCARD = 5 };
544
545 pj_enter_critical_section();
546 for (unsigned i=0; i<NUMBER_TO_DISCARD; ++i) {
547 delete pendingJobs.back();
548 pendingJobs.pop_back();
549 }
550
551 pendingJobSize -= NUMBER_TO_DISCARD;
552 pj_leave_critical_section();
553
554 utilLogWrite(1, THIS_FILE,
555 "*** ERROR: Job queue full!! Jobs discarded!!! ***");
556 }
557
558 pj_enter_critical_section();
559 pendingJobs.push_back(job);
560 pendingJobSize++;
561 pj_leave_critical_section();
562 }
563
564 /* Handle log callback */
utilLogWrite(LogEntry & entry)565 void Endpoint::utilLogWrite(LogEntry &entry)
566 {
567 if (!writer) return;
568
569 if (mainThreadOnly && pj_thread_this() != mainThread) {
570 PendingLog *job = new PendingLog;
571 job->entry = entry;
572 utilAddPendingJob(job);
573 } else {
574 writer->write(entry);
575 }
576 }
577
578 /* Run pending jobs only in main thread */
performPendingJobs()579 void Endpoint::performPendingJobs()
580 {
581 if (pj_thread_this() != mainThread)
582 return;
583
584 if (pendingJobSize == 0)
585 return;
586
587 for (;;) {
588 PendingJob *job = NULL;
589
590 pj_enter_critical_section();
591 if (pendingJobSize != 0) {
592 job = pendingJobs.front();
593 pendingJobs.pop_front();
594 pendingJobSize--;
595 }
596 pj_leave_critical_section();
597
598 if (job) {
599 job->execute(true);
600 delete job;
601 } else
602 break;
603 }
604 }
605
606 ///////////////////////////////////////////////////////////////////////////////
607 /*
608 * Endpoint static callbacks
609 */
logFunc(int level,const char * data,int len)610 void Endpoint::logFunc(int level, const char *data, int len)
611 {
612 Endpoint &ep = Endpoint::instance();
613
614 if (!ep.writer)
615 return;
616
617 LogEntry entry;
618 entry.level = level;
619 entry.msg = string(data, len);
620 entry.threadId = (long)(size_t)pj_thread_this();
621 entry.threadName = string(pj_thread_get_name(pj_thread_this()));
622
623 ep.utilLogWrite(entry);
624 }
625
stun_resolve_cb(const pj_stun_resolve_result * res)626 void Endpoint::stun_resolve_cb(const pj_stun_resolve_result *res)
627 {
628 Endpoint &ep = Endpoint::instance();
629
630 if (!res)
631 return;
632
633 OnNatCheckStunServersCompleteParam prm;
634
635 prm.userData = res->token;
636 prm.status = res->status;
637 if (res->status == PJ_SUCCESS) {
638 char straddr[PJ_INET6_ADDRSTRLEN+10];
639
640 prm.name = string(res->name.ptr, res->name.slen);
641 pj_sockaddr_print(&res->addr, straddr, sizeof(straddr), 3);
642 prm.addr = straddr;
643 }
644
645 ep.onNatCheckStunServersComplete(prm);
646 }
647
on_timer(pj_timer_heap_t * timer_heap,pj_timer_entry * entry)648 void Endpoint::on_timer(pj_timer_heap_t *timer_heap,
649 pj_timer_entry *entry)
650 {
651 PJ_UNUSED_ARG(timer_heap);
652
653 Endpoint &ep = Endpoint::instance();
654 UserTimer *ut = (UserTimer*) entry->user_data;
655
656 if (ut->signature != TIMER_SIGNATURE)
657 return;
658
659 ep.onTimer(ut->prm);
660 }
661
on_nat_detect(const pj_stun_nat_detect_result * res)662 void Endpoint::on_nat_detect(const pj_stun_nat_detect_result *res)
663 {
664 Endpoint &ep = Endpoint::instance();
665
666 if (!res)
667 return;
668
669 OnNatDetectionCompleteParam prm;
670
671 prm.status = res->status;
672 prm.reason = res->status_text;
673 prm.natType = res->nat_type;
674 prm.natTypeName = res->nat_type_name;
675
676 ep.onNatDetectionComplete(prm);
677 }
678
on_transport_state(pjsip_transport * tp,pjsip_transport_state state,const pjsip_transport_state_info * info)679 void Endpoint::on_transport_state( pjsip_transport *tp,
680 pjsip_transport_state state,
681 const pjsip_transport_state_info *info)
682 {
683 Endpoint &ep = Endpoint::instance();
684
685 OnTransportStateParam prm;
686
687 prm.hnd = (TransportHandle)tp;
688 prm.type = tp->type_name;
689 prm.state = state;
690 prm.lastError = info ? info->status : PJ_SUCCESS;
691
692 #if defined(PJSIP_HAS_TLS_TRANSPORT) && PJSIP_HAS_TLS_TRANSPORT!=0
693 if (!pj_ansi_stricmp(tp->type_name, "tls") && info->ext_info &&
694 (state == PJSIP_TP_STATE_CONNECTED ||
695 ((pjsip_tls_state_info*)info->ext_info)->
696 ssl_sock_info->verify_status != PJ_SUCCESS))
697 {
698 prm.tlsInfo.fromPj(*((pjsip_tls_state_info*)info->ext_info));
699 }
700 #endif
701
702 ep.onTransportState(prm);
703 }
704
705 ///////////////////////////////////////////////////////////////////////////////
706 /*
707 * Account static callbacks
708 */
709
lookupAcc(int acc_id,const char * op)710 Account *Endpoint::lookupAcc(int acc_id, const char *op)
711 {
712 Account *acc = Account::lookup(acc_id);
713 if (!acc) {
714 PJ_LOG(1,(THIS_FILE,
715 "Error: cannot find Account instance for account id %d in "
716 "%s", acc_id, op));
717 }
718
719 return acc;
720 }
721
lookupCall(int call_id,const char * op)722 Call *Endpoint::lookupCall(int call_id, const char *op)
723 {
724 Call *call = Call::lookup(call_id);
725 if (!call) {
726 PJ_LOG(1,(THIS_FILE,
727 "Error: cannot find Call instance for call id %d in "
728 "%s", call_id, op));
729 }
730
731 return call;
732 }
733
on_incoming_call(pjsua_acc_id acc_id,pjsua_call_id call_id,pjsip_rx_data * rdata)734 void Endpoint::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
735 pjsip_rx_data *rdata)
736 {
737 Account *acc = lookupAcc(acc_id, "on_incoming_call()");
738 if (!acc) {
739 pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
740 return;
741 }
742
743 pjsua_call *call = &pjsua_var.calls[call_id];
744 if (!call->incoming_data) {
745 /* This happens when the incoming call callback has been called from
746 * inside the on_create_media_transport() callback. So we simply
747 * return here to avoid calling the callback twice.
748 */
749 return;
750 }
751
752 /* call callback */
753 OnIncomingCallParam prm;
754 prm.callId = call_id;
755 prm.rdata.fromPj(*rdata);
756
757 acc->onIncomingCall(prm);
758
759 /* Free cloned rdata. */
760 pjsip_rx_data_free_cloned(call->incoming_data);
761 call->incoming_data = NULL;
762
763 /* disconnect if callback doesn't handle the call */
764 pjsua_call_info ci;
765
766 pjsua_call_get_info(call_id, &ci);
767 if (!pjsua_call_get_user_data(call_id) &&
768 ci.state != PJSIP_INV_STATE_DISCONNECTED)
769 {
770 pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
771 }
772 }
773
on_reg_started(pjsua_acc_id acc_id,pj_bool_t renew)774 void Endpoint::on_reg_started(pjsua_acc_id acc_id, pj_bool_t renew)
775 {
776 Account *acc = lookupAcc(acc_id, "on_reg_started()");
777 if (!acc) {
778 return;
779 }
780
781 OnRegStartedParam prm;
782 prm.renew = PJ2BOOL(renew);
783 acc->onRegStarted(prm);
784 }
785
on_reg_state2(pjsua_acc_id acc_id,pjsua_reg_info * info)786 void Endpoint::on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info)
787 {
788 Account *acc = lookupAcc(acc_id, "on_reg_state2()");
789 if (!acc) {
790 return;
791 }
792
793 OnRegStateParam prm;
794 prm.status = info->cbparam->status;
795 prm.code = (pjsip_status_code) info->cbparam->code;
796 prm.reason = pj2Str(info->cbparam->reason);
797 if (info->cbparam->rdata)
798 prm.rdata.fromPj(*info->cbparam->rdata);
799 prm.expiration = info->cbparam->expiration;
800
801 acc->onRegState(prm);
802 }
803
on_incoming_subscribe(pjsua_acc_id acc_id,pjsua_srv_pres * srv_pres,pjsua_buddy_id buddy_id,const pj_str_t * from,pjsip_rx_data * rdata,pjsip_status_code * code,pj_str_t * reason,pjsua_msg_data * msg_data)804 void Endpoint::on_incoming_subscribe(pjsua_acc_id acc_id,
805 pjsua_srv_pres *srv_pres,
806 pjsua_buddy_id buddy_id,
807 const pj_str_t *from,
808 pjsip_rx_data *rdata,
809 pjsip_status_code *code,
810 pj_str_t *reason,
811 pjsua_msg_data *msg_data)
812 {
813 PJ_UNUSED_ARG(buddy_id);
814 PJ_UNUSED_ARG(srv_pres);
815
816 Account *acc = lookupAcc(acc_id, "on_incoming_subscribe()");
817 if (!acc) {
818 /* default behavior should apply */
819 return;
820 }
821
822 OnIncomingSubscribeParam prm;
823 prm.srvPres = srv_pres;
824 prm.fromUri = pj2Str(*from);
825 prm.rdata.fromPj(*rdata);
826 prm.code = *code;
827 prm.reason = pj2Str(*reason);
828 prm.txOption.fromPj(*msg_data);
829
830 acc->onIncomingSubscribe(prm);
831
832 *code = prm.code;
833 acc->tmpReason = prm.reason;
834 *reason = str2Pj(acc->tmpReason);
835 prm.txOption.toPj(*msg_data);
836 }
837
on_pager2(pjsua_call_id call_id,const pj_str_t * from,const pj_str_t * to,const pj_str_t * contact,const pj_str_t * mime_type,const pj_str_t * body,pjsip_rx_data * rdata,pjsua_acc_id acc_id)838 void Endpoint::on_pager2(pjsua_call_id call_id,
839 const pj_str_t *from,
840 const pj_str_t *to,
841 const pj_str_t *contact,
842 const pj_str_t *mime_type,
843 const pj_str_t *body,
844 pjsip_rx_data *rdata,
845 pjsua_acc_id acc_id)
846 {
847 OnInstantMessageParam prm;
848 prm.fromUri = pj2Str(*from);
849 prm.toUri = pj2Str(*to);
850 prm.contactUri = pj2Str(*contact);
851 prm.contentType = pj2Str(*mime_type);
852 prm.msgBody = pj2Str(*body);
853 prm.rdata.fromPj(*rdata);
854
855 if (call_id != PJSUA_INVALID_ID) {
856 Call *call = lookupCall(call_id, "on_pager2()");
857 if (!call) {
858 /* Ignored */
859 return;
860 }
861
862 call->onInstantMessage(prm);
863 } else {
864 Account *acc = lookupAcc(acc_id, "on_pager2()");
865 if (!acc) {
866 /* Ignored */
867 return;
868 }
869
870 acc->onInstantMessage(prm);
871 }
872 }
873
on_pager_status2(pjsua_call_id call_id,const pj_str_t * to,const pj_str_t * body,void * user_data,pjsip_status_code status,const pj_str_t * reason,pjsip_tx_data * tdata,pjsip_rx_data * rdata,pjsua_acc_id acc_id)874 void Endpoint::on_pager_status2( pjsua_call_id call_id,
875 const pj_str_t *to,
876 const pj_str_t *body,
877 void *user_data,
878 pjsip_status_code status,
879 const pj_str_t *reason,
880 pjsip_tx_data *tdata,
881 pjsip_rx_data *rdata,
882 pjsua_acc_id acc_id)
883 {
884 PJ_UNUSED_ARG(tdata);
885
886 OnInstantMessageStatusParam prm;
887 prm.userData = user_data;
888 prm.toUri = pj2Str(*to);
889 prm.msgBody = pj2Str(*body);
890 prm.code = status;
891 prm.reason = pj2Str(*reason);
892 if (rdata)
893 prm.rdata.fromPj(*rdata);
894
895 if (call_id != PJSUA_INVALID_ID) {
896 Call *call = lookupCall(call_id, "on_pager_status2()");
897 if (!call) {
898 /* Ignored */
899 return;
900 }
901
902 call->onInstantMessageStatus(prm);
903 } else {
904 Account *acc = lookupAcc(acc_id, "on_pager_status2()");
905 if (!acc) {
906 /* Ignored */
907 return;
908 }
909
910 acc->onInstantMessageStatus(prm);
911 }
912 }
913
on_typing2(pjsua_call_id call_id,const pj_str_t * from,const pj_str_t * to,const pj_str_t * contact,pj_bool_t is_typing,pjsip_rx_data * rdata,pjsua_acc_id acc_id)914 void Endpoint::on_typing2( pjsua_call_id call_id,
915 const pj_str_t *from,
916 const pj_str_t *to,
917 const pj_str_t *contact,
918 pj_bool_t is_typing,
919 pjsip_rx_data *rdata,
920 pjsua_acc_id acc_id)
921 {
922 OnTypingIndicationParam prm;
923 prm.fromUri = pj2Str(*from);
924 prm.toUri = pj2Str(*to);
925 prm.contactUri = pj2Str(*contact);
926 prm.isTyping = is_typing != 0;
927 prm.rdata.fromPj(*rdata);
928
929 if (call_id != PJSUA_INVALID_ID) {
930 Call *call = lookupCall(call_id, "on_typing2()");
931 if (!call) {
932 /* Ignored */
933 return;
934 }
935
936 call->onTypingIndication(prm);
937 } else {
938 Account *acc = lookupAcc(acc_id, "on_typing2()");
939 if (!acc) {
940 /* Ignored */
941 return;
942 }
943
944 acc->onTypingIndication(prm);
945 }
946 }
947
on_mwi_info(pjsua_acc_id acc_id,pjsua_mwi_info * mwi_info)948 void Endpoint::on_mwi_info(pjsua_acc_id acc_id,
949 pjsua_mwi_info *mwi_info)
950 {
951 OnMwiInfoParam prm;
952
953 if (mwi_info->evsub) {
954 prm.state = pjsip_evsub_get_state(mwi_info->evsub);
955 } else {
956 /* Unsolicited MWI */
957 prm.state = PJSIP_EVSUB_STATE_NULL;
958 }
959 prm.rdata.fromPj(*mwi_info->rdata);
960
961 Account *acc = lookupAcc(acc_id, "on_mwi_info()");
962 if (!acc) {
963 /* Ignored */
964 return;
965 }
966
967 acc->onMwiInfo(prm);
968 }
969
on_acc_find_for_incoming(const pjsip_rx_data * rdata,pjsua_acc_id * acc_id)970 void Endpoint::on_acc_find_for_incoming(const pjsip_rx_data *rdata,
971 pjsua_acc_id* acc_id)
972 {
973 OnSelectAccountParam prm;
974
975 pj_assert(rdata && acc_id);
976 prm.rdata.fromPj(*((pjsip_rx_data *)rdata));
977 prm.accountIndex = *acc_id;
978
979 instance_->onSelectAccount(prm);
980
981 *acc_id = prm.accountIndex;
982 }
983
on_buddy_state(pjsua_buddy_id buddy_id)984 void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id)
985 {
986 Buddy b(buddy_id);
987 Buddy *buddy = b.getOriginalInstance();
988 if (!buddy || !buddy->isValid()) {
989 /* Ignored */
990 return;
991 }
992
993 buddy->onBuddyState();
994 }
995
on_buddy_evsub_state(pjsua_buddy_id buddy_id,pjsip_evsub * sub,pjsip_event * event)996 void Endpoint::on_buddy_evsub_state(pjsua_buddy_id buddy_id,
997 pjsip_evsub *sub,
998 pjsip_event *event)
999 {
1000 PJ_UNUSED_ARG(sub);
1001
1002 Buddy b(buddy_id);
1003 Buddy *buddy = b.getOriginalInstance();
1004 if (!buddy || !buddy->isValid()) {
1005 /* Ignored */
1006 return;
1007 }
1008
1009 OnBuddyEvSubStateParam prm;
1010 prm.e.fromPj(*event);
1011
1012 buddy->onBuddyEvSubState(prm);
1013 }
1014
1015 // Call callbacks
on_call_state(pjsua_call_id call_id,pjsip_event * e)1016 void Endpoint::on_call_state(pjsua_call_id call_id, pjsip_event *e)
1017 {
1018 Call *call = Call::lookup(call_id);
1019 if (!call) {
1020 return;
1021 }
1022
1023 OnCallStateParam prm;
1024 prm.e.fromPj(*e);
1025
1026 call->processStateChange(prm);
1027 /* If the state is DISCONNECTED, call may have already been deleted
1028 * by the application in the callback, so do not access it anymore here.
1029 */
1030 }
1031
on_call_tsx_state(pjsua_call_id call_id,pjsip_transaction * tsx,pjsip_event * e)1032 void Endpoint::on_call_tsx_state(pjsua_call_id call_id,
1033 pjsip_transaction *tsx,
1034 pjsip_event *e)
1035 {
1036 PJ_UNUSED_ARG(tsx);
1037
1038 Call *call = Call::lookup(call_id);
1039 if (!call) {
1040 return;
1041 }
1042
1043 OnCallTsxStateParam prm;
1044 prm.e.fromPj(*e);
1045
1046 call->onCallTsxState(prm);
1047 }
1048
on_call_media_state(pjsua_call_id call_id)1049 void Endpoint::on_call_media_state(pjsua_call_id call_id)
1050 {
1051 Call *call = Call::lookup(call_id);
1052 if (!call) {
1053 return;
1054 }
1055
1056 OnCallMediaStateParam prm;
1057 call->processMediaUpdate(prm);
1058 }
1059
on_call_sdp_created(pjsua_call_id call_id,pjmedia_sdp_session * sdp,pj_pool_t * pool,const pjmedia_sdp_session * rem_sdp)1060 void Endpoint::on_call_sdp_created(pjsua_call_id call_id,
1061 pjmedia_sdp_session *sdp,
1062 pj_pool_t *pool,
1063 const pjmedia_sdp_session *rem_sdp)
1064 {
1065 Call *call = Call::lookup(call_id);
1066 if (!call) {
1067 return;
1068 }
1069
1070 OnCallSdpCreatedParam prm;
1071 string orig_sdp;
1072
1073 prm.sdp.fromPj(*sdp);
1074 orig_sdp = prm.sdp.wholeSdp;
1075 if (rem_sdp)
1076 prm.remSdp.fromPj(*rem_sdp);
1077
1078 call->sdp_pool = pool;
1079 call->onCallSdpCreated(prm);
1080
1081 /* Check if application modifies the SDP */
1082 if (orig_sdp != prm.sdp.wholeSdp) {
1083 pjmedia_sdp_session *new_sdp;
1084 pj_str_t dup_new_sdp;
1085 pj_str_t new_sdp_str = {(char*)prm.sdp.wholeSdp.c_str(),
1086 (pj_ssize_t)prm.sdp.wholeSdp.size()};
1087 pj_status_t status;
1088
1089 pj_strdup(pool, &dup_new_sdp, &new_sdp_str);
1090 status = pjmedia_sdp_parse(pool, dup_new_sdp.ptr,
1091 dup_new_sdp.slen, &new_sdp);
1092 if (status != PJ_SUCCESS) {
1093 PJ_PERROR(4,(THIS_FILE, status,
1094 "Failed to parse the modified SDP"));
1095 } else {
1096 pj_memcpy(sdp, new_sdp, sizeof(*sdp));
1097 }
1098 }
1099 }
1100
on_stream_precreate(pjsua_call_id call_id,pjsua_on_stream_precreate_param * param)1101 void Endpoint::on_stream_precreate(pjsua_call_id call_id,
1102 pjsua_on_stream_precreate_param *param)
1103 {
1104 Call *call = Call::lookup(call_id);
1105 if (!call) {
1106 return;
1107 }
1108
1109 OnStreamPreCreateParam prm;
1110 prm.streamIdx = param->stream_idx;
1111 prm.streamInfo.fromPj(param->stream_info);
1112
1113 call->onStreamPreCreate(prm);
1114
1115 /* Copy back only the fields which are allowed to be changed. */
1116 param->stream_info.info.aud.jb_init = prm.streamInfo.jbInit;
1117 param->stream_info.info.aud.jb_min_pre = prm.streamInfo.jbMinPre;
1118 param->stream_info.info.aud.jb_max_pre = prm.streamInfo.jbMaxPre;
1119 param->stream_info.info.aud.jb_max = prm.streamInfo.jbMax;
1120 param->stream_info.info.aud.jb_discard_algo = prm.streamInfo.jbDiscardAlgo;
1121 #if defined(PJMEDIA_STREAM_ENABLE_KA) && (PJMEDIA_STREAM_ENABLE_KA != 0)
1122 param->stream_info.info.aud.use_ka = prm.streamInfo.useKa;
1123 #endif
1124 param->stream_info.info.aud.rtcp_sdes_bye_disabled = prm.streamInfo.rtcpSdesByeDisabled;
1125 }
1126
on_stream_created2(pjsua_call_id call_id,pjsua_on_stream_created_param * param)1127 void Endpoint::on_stream_created2(pjsua_call_id call_id,
1128 pjsua_on_stream_created_param *param)
1129 {
1130 Call *call = Call::lookup(call_id);
1131 if (!call) {
1132 return;
1133 }
1134
1135 OnStreamCreatedParam prm;
1136 prm.stream = param->stream;
1137 prm.streamIdx = param->stream_idx;
1138 prm.destroyPort = (param->destroy_port != PJ_FALSE);
1139 prm.pPort = (MediaPort)param->port;
1140
1141 call->onStreamCreated(prm);
1142
1143 param->destroy_port = prm.destroyPort;
1144 param->port = (pjmedia_port *)prm.pPort;
1145 }
1146
on_stream_destroyed(pjsua_call_id call_id,pjmedia_stream * strm,unsigned stream_idx)1147 void Endpoint::on_stream_destroyed(pjsua_call_id call_id,
1148 pjmedia_stream *strm,
1149 unsigned stream_idx)
1150 {
1151 Call *call = Call::lookup(call_id);
1152 if (!call) {
1153 /* This can happen for call disconnection case. The callback
1154 * should have been called from on_call_state() instead.
1155 */
1156 return;
1157 }
1158
1159 OnStreamDestroyedParam prm;
1160 prm.stream = strm;
1161 prm.streamIdx = stream_idx;
1162
1163 call->onStreamDestroyed(prm);
1164 }
1165
1166 struct PendingOnDtmfDigitCallback : public PendingJob
1167 {
1168 int call_id;
1169 OnDtmfDigitParam prm;
1170
executePendingOnDtmfDigitCallback1171 virtual void execute(bool is_pending)
1172 {
1173 PJ_UNUSED_ARG(is_pending);
1174
1175 Call *call = Call::lookup(call_id);
1176 if (!call)
1177 return;
1178
1179 call->onDtmfDigit(prm);
1180 }
1181 };
1182
on_dtmf_digit(pjsua_call_id call_id,int digit)1183 void Endpoint::on_dtmf_digit(pjsua_call_id call_id, int digit)
1184 {
1185 Call *call = Call::lookup(call_id);
1186 if (!call) {
1187 return;
1188 }
1189
1190 PendingOnDtmfDigitCallback *job = new PendingOnDtmfDigitCallback;
1191 job->call_id = call_id;
1192 char buf[10];
1193 pj_ansi_sprintf(buf, "%c", digit);
1194 job->prm.digit = string(buf);
1195
1196 Endpoint::instance().utilAddPendingJob(job);
1197 }
1198
on_dtmf_digit2(pjsua_call_id call_id,const pjsua_dtmf_info * info)1199 void Endpoint::on_dtmf_digit2(pjsua_call_id call_id,
1200 const pjsua_dtmf_info *info)
1201 {
1202 Call *call = Call::lookup(call_id);
1203 if (!call) {
1204 return;
1205 }
1206
1207 PendingOnDtmfDigitCallback *job = new PendingOnDtmfDigitCallback;
1208 job->call_id = call_id;
1209 char buf[10];
1210 pj_ansi_sprintf(buf, "%c", info->digit);
1211 job->prm.digit = string(buf);
1212 job->prm.method = info->method;
1213 job->prm.duration = info->duration;
1214
1215 Endpoint::instance().utilAddPendingJob(job);
1216 }
1217
1218 struct PendingOnDtmfEventCallback : public PendingJob
1219 {
1220 int call_id;
1221 OnDtmfEventParam prm;
1222
executePendingOnDtmfEventCallback1223 virtual void execute(bool is_pending)
1224 {
1225 PJ_UNUSED_ARG(is_pending);
1226
1227 Call *call = Call::lookup(call_id);
1228 if (!call)
1229 return;
1230
1231 call->onDtmfEvent(prm);
1232
1233 /* If this event indicates a new DTMF digit, invoke onDtmfDigit
1234 * as well.
1235 * Note that the duration is pretty much useless in this context as it
1236 * will most likely equal the ptime of one frame received via RTP in
1237 * milliseconds. Since the application can't receive updates to the
1238 * duration via this interface and the total duration of the event is
1239 * not known yet, just indicate an unknown duration.
1240 */
1241 if (!(prm.flags & PJMEDIA_STREAM_DTMF_IS_UPDATE)) {
1242 OnDtmfDigitParam prmBasic;
1243 prmBasic.method = prm.method;
1244 prmBasic.digit = prm.digit;
1245 prmBasic.duration = PJSUA_UNKNOWN_DTMF_DURATION;
1246 call->onDtmfDigit(prmBasic);
1247 }
1248 }
1249 };
1250
on_dtmf_event(pjsua_call_id call_id,const pjsua_dtmf_event * event)1251 void Endpoint::on_dtmf_event(pjsua_call_id call_id,
1252 const pjsua_dtmf_event* event)
1253 {
1254 Call *call = Call::lookup(call_id);
1255 if (!call) {
1256 return;
1257 }
1258
1259 PendingOnDtmfEventCallback *job = new PendingOnDtmfEventCallback;
1260 job->call_id = call_id;
1261 char buf[10];
1262 pj_ansi_sprintf(buf, "%c", event->digit);
1263 job->prm.method = event->method;
1264 job->prm.timestamp = event->timestamp;
1265 job->prm.digit = string(buf);
1266 job->prm.duration = event->duration;
1267 job->prm.flags = event->flags;
1268
1269 Endpoint::instance().utilAddPendingJob(job);
1270 }
1271
on_call_transfer_request2(pjsua_call_id call_id,const pj_str_t * dst,pjsip_status_code * code,pjsua_call_setting * opt)1272 void Endpoint::on_call_transfer_request2(pjsua_call_id call_id,
1273 const pj_str_t *dst,
1274 pjsip_status_code *code,
1275 pjsua_call_setting *opt)
1276 {
1277 Call *call = Call::lookup(call_id);
1278 if (!call) {
1279 return;
1280 }
1281
1282 OnCallTransferRequestParam prm;
1283 prm.dstUri = pj2Str(*dst);
1284 prm.statusCode = *code;
1285 prm.opt.fromPj(*opt);
1286 prm.newCall = NULL;
1287
1288 call->onCallTransferRequest(prm);
1289
1290 *code = prm.statusCode;
1291 *opt = prm.opt.toPj();
1292 if (*code/100 <= 2) {
1293 if (prm.newCall) {
1294 /* We don't manage (e.g: create, delete) the call child,
1295 * so let's just override any existing child.
1296 */
1297 call->child = prm.newCall;
1298 call->child->id = PJSUA_INVALID_ID;
1299 } else {
1300 PJ_LOG(4,(THIS_FILE,
1301 "Warning: application reuses Call instance in "
1302 "call transfer (call ID:%d)", call_id));
1303 }
1304 }
1305 }
1306
on_call_transfer_status(pjsua_call_id call_id,int st_code,const pj_str_t * st_text,pj_bool_t final,pj_bool_t * p_cont)1307 void Endpoint::on_call_transfer_status(pjsua_call_id call_id,
1308 int st_code,
1309 const pj_str_t *st_text,
1310 pj_bool_t final,
1311 pj_bool_t *p_cont)
1312 {
1313 Call *call = Call::lookup(call_id);
1314 if (!call) {
1315 return;
1316 }
1317
1318 OnCallTransferStatusParam prm;
1319 prm.statusCode = (pjsip_status_code)st_code;
1320 prm.reason = pj2Str(*st_text);
1321 prm.finalNotify = PJ2BOOL(final);
1322 prm.cont = PJ2BOOL(*p_cont);
1323
1324 call->onCallTransferStatus(prm);
1325
1326 *p_cont = prm.cont;
1327 }
1328
on_call_replace_request2(pjsua_call_id call_id,pjsip_rx_data * rdata,int * st_code,pj_str_t * st_text,pjsua_call_setting * opt)1329 void Endpoint::on_call_replace_request2(pjsua_call_id call_id,
1330 pjsip_rx_data *rdata,
1331 int *st_code,
1332 pj_str_t *st_text,
1333 pjsua_call_setting *opt)
1334 {
1335 Call *call = Call::lookup(call_id);
1336 if (!call) {
1337 return;
1338 }
1339
1340 OnCallReplaceRequestParam prm;
1341 prm.rdata.fromPj(*rdata);
1342 prm.statusCode = (pjsip_status_code)*st_code;
1343 prm.reason = pj2Str(*st_text);
1344 prm.opt.fromPj(*opt);
1345
1346 call->onCallReplaceRequest(prm);
1347
1348 *st_code = prm.statusCode;
1349 *st_text = str2Pj(prm.reason);
1350 *opt = prm.opt.toPj();
1351 }
1352
on_call_replaced(pjsua_call_id old_call_id,pjsua_call_id new_call_id)1353 void Endpoint::on_call_replaced(pjsua_call_id old_call_id,
1354 pjsua_call_id new_call_id)
1355 {
1356 Call *call = Call::lookup(old_call_id);
1357 if (!call) {
1358 return;
1359 }
1360
1361 OnCallReplacedParam prm;
1362 prm.newCallId = new_call_id;
1363 prm.newCall = NULL;
1364
1365 call->onCallReplaced(prm);
1366
1367 if (prm.newCall) {
1368 /* Sanity checks */
1369 pj_assert(prm.newCall->id == new_call_id);
1370 pj_assert(prm.newCall->acc.getId() == call->acc.getId());
1371 pj_assert(pjsua_call_get_user_data(new_call_id) == prm.newCall);
1372 } else {
1373 PJ_LOG(4,(THIS_FILE,
1374 "Warning: application has not created new Call instance "
1375 "for call replace (old call ID:%d, new call ID: %d)",
1376 old_call_id, new_call_id));
1377 }
1378 }
1379
on_call_rx_offer(pjsua_call_id call_id,const pjmedia_sdp_session * offer,void * reserved,pjsip_status_code * code,pjsua_call_setting * opt)1380 void Endpoint::on_call_rx_offer(pjsua_call_id call_id,
1381 const pjmedia_sdp_session *offer,
1382 void *reserved,
1383 pjsip_status_code *code,
1384 pjsua_call_setting *opt)
1385 {
1386 PJ_UNUSED_ARG(reserved);
1387
1388 Call *call = Call::lookup(call_id);
1389 if (!call) {
1390 return;
1391 }
1392
1393 OnCallRxOfferParam prm;
1394 prm.offer.fromPj(*offer);
1395 prm.statusCode = *code;
1396 prm.opt.fromPj(*opt);
1397
1398 call->onCallRxOffer(prm);
1399
1400 *code = prm.statusCode;
1401 *opt = prm.opt.toPj();
1402 }
1403
on_call_rx_reinvite(pjsua_call_id call_id,const pjmedia_sdp_session * offer,pjsip_rx_data * rdata,void * reserved,pj_bool_t * async,pjsip_status_code * code,pjsua_call_setting * opt)1404 void Endpoint::on_call_rx_reinvite(pjsua_call_id call_id,
1405 const pjmedia_sdp_session *offer,
1406 pjsip_rx_data *rdata,
1407 void *reserved,
1408 pj_bool_t *async,
1409 pjsip_status_code *code,
1410 pjsua_call_setting *opt)
1411 {
1412 PJ_UNUSED_ARG(reserved);
1413
1414 Call *call = Call::lookup(call_id);
1415 if (!call) {
1416 return;
1417 }
1418
1419 OnCallRxReinviteParam prm;
1420 prm.offer.fromPj(*offer);
1421 prm.rdata.fromPj(*rdata);
1422 prm.isAsync = PJ2BOOL(*async);
1423 prm.statusCode = *code;
1424 prm.opt.fromPj(*opt);
1425
1426 call->onCallRxReinvite(prm);
1427
1428 *async = prm.isAsync;
1429 *code = prm.statusCode;
1430 *opt = prm.opt.toPj();
1431 }
1432
on_call_tx_offer(pjsua_call_id call_id,void * reserved,pjsua_call_setting * opt)1433 void Endpoint::on_call_tx_offer(pjsua_call_id call_id,
1434 void *reserved,
1435 pjsua_call_setting *opt)
1436 {
1437 PJ_UNUSED_ARG(reserved);
1438
1439 Call *call = Call::lookup(call_id);
1440 if (!call) {
1441 return;
1442 }
1443
1444 OnCallTxOfferParam prm;
1445 prm.opt.fromPj(*opt);
1446
1447 call->onCallTxOffer(prm);
1448
1449 *opt = prm.opt.toPj();
1450 }
1451
on_call_redirected(pjsua_call_id call_id,const pjsip_uri * target,const pjsip_event * e)1452 pjsip_redirect_op Endpoint::on_call_redirected(pjsua_call_id call_id,
1453 const pjsip_uri *target,
1454 const pjsip_event *e)
1455 {
1456 Call *call = Call::lookup(call_id);
1457 if (!call) {
1458 return PJSIP_REDIRECT_STOP;
1459 }
1460
1461 OnCallRedirectedParam prm;
1462 char uristr[PJSIP_MAX_URL_SIZE];
1463 int len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
1464 sizeof(uristr));
1465 if (len < 1) {
1466 pj_ansi_strcpy(uristr, "--URI too long--");
1467 }
1468 prm.targetUri = string(uristr);
1469 if (e)
1470 prm.e.fromPj(*e);
1471 else
1472 prm.e.type = PJSIP_EVENT_UNKNOWN;
1473
1474 return call->onCallRedirected(prm);
1475 }
1476
1477
1478 struct PendingOnMediaTransportCallback : public PendingJob
1479 {
1480 int call_id;
1481 OnCallMediaTransportStateParam prm;
1482
executePendingOnMediaTransportCallback1483 virtual void execute(bool is_pending)
1484 {
1485 PJ_UNUSED_ARG(is_pending);
1486
1487 Call *call = Call::lookup(call_id);
1488 if (!call)
1489 return;
1490
1491 call->onCallMediaTransportState(prm);
1492 }
1493 };
1494
1495 pj_status_t
on_call_media_transport_state(pjsua_call_id call_id,const pjsua_med_tp_state_info * info)1496 Endpoint::on_call_media_transport_state(pjsua_call_id call_id,
1497 const pjsua_med_tp_state_info *info)
1498 {
1499 Call *call = Call::lookup(call_id);
1500 if (!call) {
1501 return PJ_SUCCESS;
1502 }
1503
1504 PendingOnMediaTransportCallback *job = new PendingOnMediaTransportCallback;
1505
1506 job->call_id = call_id;
1507 job->prm.medIdx = info->med_idx;
1508 job->prm.state = info->state;
1509 job->prm.status = info->status;
1510 job->prm.sipErrorCode = info->sip_err_code;
1511
1512 Endpoint::instance().utilAddPendingJob(job);
1513
1514 return PJ_SUCCESS;
1515 }
1516
1517 struct PendingOnMediaEventCallback : public PendingJob
1518 {
1519 int call_id;
1520 OnCallMediaEventParam prm;
1521
executePendingOnMediaEventCallback1522 virtual void execute(bool is_pending)
1523 {
1524 if (is_pending) {
1525 /* Can't do this anymore, pointer is invalid */
1526 prm.ev.pjMediaEvent = NULL;
1527 }
1528
1529 if (call_id == PJSUA_INVALID_ID) {
1530 OnMediaEventParam prm2;
1531 prm2.ev = prm.ev;
1532 Endpoint::instance().onMediaEvent(prm2);
1533 } else {
1534 Call *call = Call::lookup(call_id);
1535
1536 if (call)
1537 call->onCallMediaEvent(prm);
1538 }
1539 }
1540 };
1541
on_media_event(pjmedia_event * event)1542 void Endpoint::on_media_event(pjmedia_event *event)
1543 {
1544 PendingOnMediaEventCallback *job = new PendingOnMediaEventCallback;
1545
1546 job->call_id = PJSUA_INVALID_ID;
1547 job->prm.medIdx = 0;
1548 job->prm.ev.fromPj(*event);
1549
1550 Endpoint::instance().utilAddPendingJob(job);
1551 }
1552
on_call_media_event(pjsua_call_id call_id,unsigned med_idx,pjmedia_event * event)1553 void Endpoint::on_call_media_event(pjsua_call_id call_id,
1554 unsigned med_idx,
1555 pjmedia_event *event)
1556 {
1557 PendingOnMediaEventCallback *job = new PendingOnMediaEventCallback;
1558
1559 job->call_id = call_id;
1560 job->prm.medIdx = med_idx;
1561 job->prm.ev.fromPj(*event);
1562
1563 Endpoint::instance().utilAddPendingJob(job);
1564 }
1565
1566 pjmedia_transport*
on_create_media_transport(pjsua_call_id call_id,unsigned media_idx,pjmedia_transport * base_tp,unsigned flags)1567 Endpoint::on_create_media_transport(pjsua_call_id call_id,
1568 unsigned media_idx,
1569 pjmedia_transport *base_tp,
1570 unsigned flags)
1571 {
1572 Call *call = Call::lookup(call_id);
1573 if (!call) {
1574 pjsua_call *in_call = &pjsua_var.calls[call_id];
1575 if (in_call->incoming_data) {
1576 /* This can happen when there is an incoming call but the
1577 * on_incoming_call() callback hasn't been called. So we need to
1578 * call the callback here.
1579 */
1580 on_incoming_call(in_call->acc_id, call_id, in_call->incoming_data);
1581
1582 /* New call should already be created by app. */
1583 call = Call::lookup(call_id);
1584 if (!call) {
1585 return base_tp;
1586 }
1587 if (in_call->inv->dlg->mod_data[pjsua_var.mod.id] == NULL) {
1588 /* This will enabled notification for fail events related to
1589 * the call via on_call_state() and on_call_tsx_state().
1590 */
1591 in_call->inv->dlg->mod_data[pjsua_var.mod.id] = in_call;
1592 in_call->inv->mod_data[pjsua_var.mod.id] = in_call;
1593 ++pjsua_var.call_cnt;
1594 }
1595 } else {
1596 return base_tp;
1597 }
1598 }
1599
1600 OnCreateMediaTransportParam prm;
1601 prm.mediaIdx = media_idx;
1602 prm.mediaTp = base_tp;
1603 prm.flags = flags;
1604
1605 call->onCreateMediaTransport(prm);
1606
1607 return (pjmedia_transport *)prm.mediaTp;
1608 }
1609
on_create_media_transport_srtp(pjsua_call_id call_id,unsigned media_idx,pjmedia_srtp_setting * srtp_opt)1610 void Endpoint::on_create_media_transport_srtp(pjsua_call_id call_id,
1611 unsigned media_idx,
1612 pjmedia_srtp_setting *srtp_opt)
1613 {
1614 Call *call = Call::lookup(call_id);
1615 if (!call) {
1616 pjsua_call *in_call = &pjsua_var.calls[call_id];
1617 if (in_call->incoming_data) {
1618 /* This can happen when there is an incoming call but the
1619 * on_incoming_call() callback hasn't been called. So we need to
1620 * call the callback here.
1621 */
1622 on_incoming_call(in_call->acc_id, call_id, in_call->incoming_data);
1623
1624 /* New call should already be created by app. */
1625 call = Call::lookup(call_id);
1626 if (!call) {
1627 return;
1628 }
1629 } else {
1630 return;
1631 }
1632 }
1633
1634 OnCreateMediaTransportSrtpParam prm;
1635 prm.mediaIdx = media_idx;
1636 prm.srtpUse = srtp_opt->use;
1637 for (unsigned i = 0; i < srtp_opt->crypto_count; i++) {
1638 SrtpCrypto crypto;
1639
1640 crypto.key = pj2Str(srtp_opt->crypto[i].key);
1641 crypto.name = pj2Str(srtp_opt->crypto[i].name);
1642 crypto.flags = srtp_opt->crypto[i].flags;
1643 prm.cryptos.push_back(crypto);
1644 }
1645
1646 call->onCreateMediaTransportSrtp(prm);
1647
1648 srtp_opt->use = prm.srtpUse;
1649 srtp_opt->crypto_count = (unsigned)prm.cryptos.size();
1650 for (unsigned i = 0; i < srtp_opt->crypto_count; i++) {
1651 srtp_opt->crypto[i].key = str2Pj(prm.cryptos[i].key);
1652 srtp_opt->crypto[i].name = str2Pj(prm.cryptos[i].name);
1653 srtp_opt->crypto[i].flags = prm.cryptos[i].flags;
1654 }
1655 }
1656
on_ip_change_progress(pjsua_ip_change_op op,pj_status_t status,const pjsua_ip_change_op_info * info)1657 void Endpoint::on_ip_change_progress(pjsua_ip_change_op op,
1658 pj_status_t status,
1659 const pjsua_ip_change_op_info *info)
1660 {
1661 Endpoint &ep = Endpoint::instance();
1662 OnIpChangeProgressParam param;
1663
1664 param.op = op;
1665 param.status = status;
1666 switch (op) {
1667 case PJSUA_IP_CHANGE_OP_RESTART_LIS:
1668 param.transportId = info->lis_restart.transport_id;
1669 break;
1670 case PJSUA_IP_CHANGE_OP_ACC_SHUTDOWN_TP:
1671 param.accId = info->acc_shutdown_tp.acc_id;
1672 break;
1673 case PJSUA_IP_CHANGE_OP_ACC_UPDATE_CONTACT:
1674 param.accId = info->acc_update_contact.acc_id;
1675 param.regInfo.code = info->acc_update_contact.code;
1676 param.regInfo.isRegister =
1677 PJ2BOOL(info->acc_update_contact.is_register);
1678 break;
1679 case PJSUA_IP_CHANGE_OP_ACC_HANGUP_CALLS:
1680 param.accId = info->acc_hangup_calls.acc_id;
1681 param.callId = info->acc_hangup_calls.call_id;
1682 break;
1683 case PJSUA_IP_CHANGE_OP_ACC_REINVITE_CALLS:
1684 param.accId = info->acc_reinvite_calls.acc_id;
1685 param.callId = info->acc_reinvite_calls.call_id;
1686 break;
1687 default:
1688 param.accId = PJSUA_INVALID_ID;
1689 break;
1690 }
1691 ep.onIpChangeProgress(param);
1692 }
1693
1694 ///////////////////////////////////////////////////////////////////////////////
1695 /*
1696 * Endpoint library operations
1697 */
libVersion() const1698 Version Endpoint::libVersion() const
1699 {
1700 Version ver;
1701 ver.major = PJ_VERSION_NUM_MAJOR;
1702 ver.minor = PJ_VERSION_NUM_MINOR;
1703 ver.rev = PJ_VERSION_NUM_REV;
1704 ver.suffix = PJ_VERSION_NUM_EXTRA;
1705 ver.full = pj_get_version();
1706 ver.numeric = PJ_VERSION_NUM;
1707 return ver;
1708 }
1709
libCreate()1710 void Endpoint::libCreate() PJSUA2_THROW(Error)
1711 {
1712 PJSUA2_CHECK_EXPR( pjsua_create() );
1713 mainThread = pj_thread_this();
1714
1715 /* Register library main thread */
1716 threadDescMap[pj_thread_this()] = NULL;
1717 }
1718
libGetState() const1719 pjsua_state Endpoint::libGetState() const
1720 {
1721 return pjsua_get_state();
1722 }
1723
libInit(const EpConfig & prmEpConfig)1724 void Endpoint::libInit(const EpConfig &prmEpConfig) PJSUA2_THROW(Error)
1725 {
1726 pjsua_config ua_cfg;
1727 pjsua_logging_config log_cfg;
1728 pjsua_media_config med_cfg;
1729
1730 ua_cfg = prmEpConfig.uaConfig.toPj();
1731 log_cfg = prmEpConfig.logConfig.toPj();
1732 med_cfg = prmEpConfig.medConfig.toPj();
1733
1734 /* Setup log callback */
1735 if (prmEpConfig.logConfig.writer) {
1736 this->writer = prmEpConfig.logConfig.writer;
1737 log_cfg.cb = &Endpoint::logFunc;
1738 }
1739 mainThreadOnly = prmEpConfig.uaConfig.mainThreadOnly;
1740
1741 /* Setup UA callbacks */
1742 pj_bzero(&ua_cfg.cb, sizeof(ua_cfg.cb));
1743 ua_cfg.cb.on_nat_detect = &Endpoint::on_nat_detect;
1744 ua_cfg.cb.on_transport_state = &Endpoint::on_transport_state;
1745
1746 ua_cfg.cb.on_incoming_call = &Endpoint::on_incoming_call;
1747 ua_cfg.cb.on_reg_started = &Endpoint::on_reg_started;
1748 ua_cfg.cb.on_reg_state2 = &Endpoint::on_reg_state2;
1749 ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe;
1750 ua_cfg.cb.on_pager2 = &Endpoint::on_pager2;
1751 ua_cfg.cb.on_pager_status2 = &Endpoint::on_pager_status2;
1752 ua_cfg.cb.on_typing2 = &Endpoint::on_typing2;
1753 ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info;
1754 ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state;
1755 ua_cfg.cb.on_buddy_evsub_state = &Endpoint::on_buddy_evsub_state;
1756 ua_cfg.cb.on_acc_find_for_incoming = &Endpoint::on_acc_find_for_incoming;
1757 ua_cfg.cb.on_ip_change_progress = &Endpoint::on_ip_change_progress;
1758
1759 /* Call callbacks */
1760 ua_cfg.cb.on_call_state = &Endpoint::on_call_state;
1761 ua_cfg.cb.on_call_tsx_state = &Endpoint::on_call_tsx_state;
1762 ua_cfg.cb.on_call_media_state = &Endpoint::on_call_media_state;
1763 ua_cfg.cb.on_call_sdp_created = &Endpoint::on_call_sdp_created;
1764 ua_cfg.cb.on_stream_precreate = &Endpoint::on_stream_precreate;
1765 ua_cfg.cb.on_stream_created2 = &Endpoint::on_stream_created2;
1766 ua_cfg.cb.on_stream_destroyed = &Endpoint::on_stream_destroyed;
1767 //ua_cfg.cb.on_dtmf_digit = &Endpoint::on_dtmf_digit;
1768 //ua_cfg.cb.on_dtmf_digit2 = &Endpoint::on_dtmf_digit2;
1769 ua_cfg.cb.on_dtmf_event = &Endpoint::on_dtmf_event;
1770 ua_cfg.cb.on_call_transfer_request2 = &Endpoint::on_call_transfer_request2;
1771 ua_cfg.cb.on_call_transfer_status = &Endpoint::on_call_transfer_status;
1772 ua_cfg.cb.on_call_replace_request2 = &Endpoint::on_call_replace_request2;
1773 ua_cfg.cb.on_call_replaced = &Endpoint::on_call_replaced;
1774 ua_cfg.cb.on_call_rx_offer = &Endpoint::on_call_rx_offer;
1775 ua_cfg.cb.on_call_rx_reinvite = &Endpoint::on_call_rx_reinvite;
1776 ua_cfg.cb.on_call_tx_offer = &Endpoint::on_call_tx_offer;
1777 ua_cfg.cb.on_call_redirected = &Endpoint::on_call_redirected;
1778 ua_cfg.cb.on_call_media_transport_state =
1779 &Endpoint::on_call_media_transport_state;
1780 ua_cfg.cb.on_media_event = &Endpoint::on_media_event;
1781 ua_cfg.cb.on_call_media_event = &Endpoint::on_call_media_event;
1782 ua_cfg.cb.on_create_media_transport = &Endpoint::on_create_media_transport;
1783 ua_cfg.cb.on_stun_resolution_complete =
1784 &Endpoint::stun_resolve_cb;
1785
1786 /* Init! */
1787 PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) );
1788
1789 /* Register worker threads */
1790 int i = pjsua_var.ua_cfg.thread_cnt;
1791 while (i) {
1792 pj_thread_t *t = pjsua_var.thread[--i];
1793 if (t)
1794 threadDescMap[t] = NULL;
1795 }
1796
1797 /* Register media endpoint worker thread */
1798 pjmedia_endpt *medept = pjsua_get_pjmedia_endpt();
1799 i = pjmedia_endpt_get_thread_count(medept);
1800 while (i) {
1801 pj_thread_t *t = pjmedia_endpt_get_thread(medept, --i);
1802 if (t)
1803 threadDescMap[t] = NULL;
1804 }
1805
1806 PJSUA2_CHECK_EXPR( pj_mutex_create_simple(pjsua_var.pool, "threadDesc",
1807 &threadDescMutex) );
1808
1809 #if !DEPRECATED_FOR_TICKET_2232
1810 PJSUA2_CHECK_EXPR( pj_mutex_create_recursive(pjsua_var.pool, "mediaList",
1811 &mediaListMutex) );
1812 #endif
1813 }
1814
libStart()1815 void Endpoint::libStart() PJSUA2_THROW(Error)
1816 {
1817 PJSUA2_CHECK_EXPR(pjsua_start());
1818 }
1819
libRegisterThread(const string & name)1820 void Endpoint::libRegisterThread(const string &name) PJSUA2_THROW(Error)
1821 {
1822 pj_thread_t *thread;
1823 pj_thread_desc *desc;
1824 pj_status_t status;
1825
1826 desc = (pj_thread_desc*)malloc(sizeof(pj_thread_desc));
1827 if (!desc) {
1828 PJSUA2_RAISE_ERROR(PJ_ENOMEM);
1829 }
1830
1831 pj_bzero(desc, sizeof(pj_thread_desc));
1832
1833 status = pj_thread_register(name.c_str(), *desc, &thread);
1834 if (status == PJ_SUCCESS) {
1835 pj_mutex_lock(threadDescMutex);
1836 threadDescMap[thread] = desc;
1837 pj_mutex_unlock(threadDescMutex);
1838 } else {
1839 free(desc);
1840 PJSUA2_RAISE_ERROR(status);
1841 }
1842 }
1843
libIsThreadRegistered()1844 bool Endpoint::libIsThreadRegistered()
1845 {
1846 if (pj_thread_is_registered()) {
1847 bool found;
1848
1849 pj_mutex_lock(threadDescMutex);
1850 /* Recheck again if it exists in the thread description map */
1851 found = (threadDescMap.find(pj_thread_this()) != threadDescMap.end());
1852 pj_mutex_unlock(threadDescMutex);
1853
1854 return found;
1855 }
1856
1857 return false;
1858 }
1859
libStopWorkerThreads()1860 void Endpoint::libStopWorkerThreads()
1861 {
1862 pjsua_stop_worker_threads();
1863 }
1864
libHandleEvents(unsigned msec_timeout)1865 int Endpoint::libHandleEvents(unsigned msec_timeout)
1866 {
1867 performPendingJobs();
1868 return pjsua_handle_events(msec_timeout);
1869 }
1870
libDestroy(unsigned flags)1871 void Endpoint::libDestroy(unsigned flags) PJSUA2_THROW(Error)
1872 {
1873 pj_status_t status;
1874
1875 if (threadDescMutex) {
1876 pj_mutex_destroy(threadDescMutex);
1877 threadDescMutex = NULL;
1878 }
1879
1880 #if !DEPRECATED_FOR_TICKET_2232
1881 while(mediaList.size() > 0) {
1882 AudioMedia *cur_media = mediaList[0];
1883 delete cur_media; /* this will remove itself from the list */
1884 }
1885
1886 if (mediaListMutex) {
1887 pj_mutex_destroy(mediaListMutex);
1888 mediaListMutex = NULL;
1889 }
1890 #endif
1891
1892 status = pjsua_destroy2(flags);
1893
1894 delete this->writer;
1895 this->writer = NULL;
1896
1897 #if PJ_LOG_MAX_LEVEL >= 1
1898 if (pj_log_get_log_func() == &Endpoint::logFunc) {
1899 pj_log_set_log_func(NULL);
1900 }
1901 #endif
1902
1903 /* Clean up thread descriptors */
1904 std::map<pj_thread_t*, pj_thread_desc*>::iterator i;
1905 for (i = threadDescMap.begin(); i != threadDescMap.end(); ++i) {
1906 pj_thread_desc* d = (*i).second;
1907 if (d != NULL)
1908 free(d);
1909 }
1910 threadDescMap.clear();
1911
1912 PJSUA2_CHECK_RAISE_ERROR(status);
1913 }
1914
1915 ///////////////////////////////////////////////////////////////////////////////
1916 /*
1917 * Endpoint Utilities
1918 */
utilStrError(pj_status_t prmErr)1919 string Endpoint::utilStrError(pj_status_t prmErr)
1920 {
1921 char errmsg[PJ_ERR_MSG_SIZE];
1922 pj_strerror(prmErr, errmsg, sizeof(errmsg));
1923 return errmsg;
1924 }
1925
ept_log_write(int level,const char * sender,const char * format,...)1926 static void ept_log_write(int level, const char *sender,
1927 const char *format, ...)
1928 {
1929 #if PJ_LOG_MAX_LEVEL >= 1
1930 va_list arg;
1931 va_start(arg, format);
1932 pj_log(sender, level, format, arg );
1933 va_end(arg);
1934 #endif
1935 }
1936
utilLogWrite(int prmLevel,const string & prmSender,const string & prmMsg)1937 void Endpoint::utilLogWrite(int prmLevel,
1938 const string &prmSender,
1939 const string &prmMsg)
1940 {
1941 ept_log_write(prmLevel, prmSender.c_str(), "%s", prmMsg.c_str());
1942 }
1943
utilVerifySipUri(const string & prmUri)1944 pj_status_t Endpoint::utilVerifySipUri(const string &prmUri)
1945 {
1946 return pjsua_verify_sip_url(prmUri.c_str());
1947 }
1948
utilVerifyUri(const string & prmUri)1949 pj_status_t Endpoint::utilVerifyUri(const string &prmUri)
1950 {
1951 return pjsua_verify_url(prmUri.c_str());
1952 }
1953
utilTimerSchedule(unsigned prmMsecDelay,Token prmUserData)1954 Token Endpoint::utilTimerSchedule(unsigned prmMsecDelay,
1955 Token prmUserData) PJSUA2_THROW(Error)
1956 {
1957 UserTimer *ut;
1958 pj_time_val delay;
1959 pj_status_t status;
1960
1961 ut = new UserTimer;
1962 ut->signature = TIMER_SIGNATURE;
1963 ut->prm.msecDelay = prmMsecDelay;
1964 ut->prm.userData = prmUserData;
1965 pj_timer_entry_init(&ut->entry, 1, ut, &Endpoint::on_timer);
1966
1967 delay.sec = 0;
1968 delay.msec = prmMsecDelay;
1969 pj_time_val_normalize(&delay);
1970
1971 status = pjsua_schedule_timer(&ut->entry, &delay);
1972 if (status != PJ_SUCCESS) {
1973 delete ut;
1974 PJSUA2_CHECK_RAISE_ERROR(status);
1975 }
1976
1977 return (Token)ut;
1978 }
1979
utilTimerCancel(Token prmTimerToken)1980 void Endpoint::utilTimerCancel(Token prmTimerToken)
1981 {
1982 UserTimer *ut = (UserTimer*)(void*)prmTimerToken;
1983
1984 if (ut->signature != TIMER_SIGNATURE) {
1985 PJ_LOG(1,(THIS_FILE,
1986 "Invalid timer token in Endpoint::utilTimerCancel()"));
1987 return;
1988 }
1989
1990 ut->entry.id = 0;
1991 ut->signature = 0xFFFFFFFE;
1992 pjsua_cancel_timer(&ut->entry);
1993
1994 delete ut;
1995 }
1996
utilSslGetAvailableCiphers()1997 IntVector Endpoint::utilSslGetAvailableCiphers() PJSUA2_THROW(Error)
1998 {
1999 #if PJ_HAS_SSL_SOCK
2000 pj_ssl_cipher ciphers[PJ_SSL_SOCK_MAX_CIPHERS];
2001 unsigned count = PJ_ARRAY_SIZE(ciphers);
2002
2003 PJSUA2_CHECK_EXPR( pj_ssl_cipher_get_availables(ciphers, &count) );
2004
2005 return IntVector(ciphers, ciphers + count);
2006 #else
2007 return IntVector();
2008 #endif
2009 }
2010
2011 ///////////////////////////////////////////////////////////////////////////////
2012 /*
2013 * Endpoint NAT operations
2014 */
natDetectType(void)2015 void Endpoint::natDetectType(void) PJSUA2_THROW(Error)
2016 {
2017 PJSUA2_CHECK_EXPR( pjsua_detect_nat_type() );
2018 }
2019
natGetType()2020 pj_stun_nat_type Endpoint::natGetType() PJSUA2_THROW(Error)
2021 {
2022 pj_stun_nat_type type;
2023
2024 PJSUA2_CHECK_EXPR( pjsua_get_nat_type(&type) );
2025
2026 return type;
2027 }
2028
natUpdateStunServers(const StringVector & servers,bool wait)2029 void Endpoint::natUpdateStunServers(const StringVector &servers,
2030 bool wait) PJSUA2_THROW(Error)
2031 {
2032 pj_str_t srv[MAX_STUN_SERVERS];
2033 unsigned i, count = 0;
2034
2035 for (i=0; i<servers.size() && i<MAX_STUN_SERVERS; ++i) {
2036 srv[count].ptr = (char*)servers[i].c_str();
2037 srv[count].slen = servers[i].size();
2038 ++count;
2039 }
2040
2041 PJSUA2_CHECK_EXPR(pjsua_update_stun_servers(count, srv, wait) );
2042 }
2043
natCheckStunServers(const StringVector & servers,bool wait,Token token)2044 void Endpoint::natCheckStunServers(const StringVector &servers,
2045 bool wait,
2046 Token token) PJSUA2_THROW(Error)
2047 {
2048 pj_str_t srv[MAX_STUN_SERVERS];
2049 unsigned i, count = 0;
2050
2051 for (i=0; i<servers.size() && i<MAX_STUN_SERVERS; ++i) {
2052 srv[count].ptr = (char*)servers[i].c_str();
2053 srv[count].slen = servers[i].size();
2054 ++count;
2055 }
2056
2057 PJSUA2_CHECK_EXPR(pjsua_resolve_stun_servers(count, srv, wait, token,
2058 &Endpoint::stun_resolve_cb) );
2059 }
2060
natCancelCheckStunServers(Token token,bool notify_cb)2061 void Endpoint::natCancelCheckStunServers(Token token,
2062 bool notify_cb) PJSUA2_THROW(Error)
2063 {
2064 PJSUA2_CHECK_EXPR( pjsua_cancel_stun_resolution(token, notify_cb) );
2065 }
2066
2067 ///////////////////////////////////////////////////////////////////////////////
2068 /*
2069 * Transport API
2070 */
transportCreate(pjsip_transport_type_e type,const TransportConfig & cfg)2071 TransportId Endpoint::transportCreate(pjsip_transport_type_e type,
2072 const TransportConfig &cfg)
2073 PJSUA2_THROW(Error)
2074 {
2075 pjsua_transport_config tcfg;
2076 pjsua_transport_id tid;
2077
2078 tcfg = cfg.toPj();
2079 PJSUA2_CHECK_EXPR( pjsua_transport_create(type,
2080 &tcfg, &tid) );
2081
2082 return tid;
2083 }
2084
transportEnum()2085 IntVector Endpoint::transportEnum() PJSUA2_THROW(Error)
2086 {
2087 pjsua_transport_id tids[32];
2088 unsigned count = PJ_ARRAY_SIZE(tids);
2089
2090 PJSUA2_CHECK_EXPR( pjsua_enum_transports(tids, &count) );
2091
2092 return IntVector(tids, tids+count);
2093 }
2094
transportGetInfo(TransportId id)2095 TransportInfo Endpoint::transportGetInfo(TransportId id) PJSUA2_THROW(Error)
2096 {
2097 pjsua_transport_info pj_tinfo;
2098 TransportInfo tinfo;
2099
2100 PJSUA2_CHECK_EXPR( pjsua_transport_get_info(id, &pj_tinfo) );
2101 tinfo.fromPj(pj_tinfo);
2102
2103 return tinfo;
2104 }
2105
transportSetEnable(TransportId id,bool enabled)2106 void Endpoint::transportSetEnable(TransportId id, bool enabled)
2107 PJSUA2_THROW(Error)
2108 {
2109 PJSUA2_CHECK_EXPR( pjsua_transport_set_enable(id, enabled) );
2110 }
2111
transportClose(TransportId id)2112 void Endpoint::transportClose(TransportId id) PJSUA2_THROW(Error)
2113 {
2114 PJSUA2_CHECK_EXPR( pjsua_transport_close(id, PJ_FALSE) );
2115 }
2116
transportShutdown(TransportHandle tp)2117 void Endpoint::transportShutdown(TransportHandle tp) PJSUA2_THROW(Error)
2118 {
2119 PJSUA2_CHECK_EXPR( pjsip_transport_shutdown((pjsip_transport *)tp) );
2120 }
2121
2122 ///////////////////////////////////////////////////////////////////////////////
2123 /*
2124 * Call operations
2125 */
2126
hangupAllCalls(void)2127 void Endpoint::hangupAllCalls(void)
2128 {
2129 pjsua_call_hangup_all();
2130 }
2131
2132 ///////////////////////////////////////////////////////////////////////////////
2133 /*
2134 * Media API
2135 */
mediaMaxPorts() const2136 unsigned Endpoint::mediaMaxPorts() const
2137 {
2138 return pjsua_conf_get_max_ports();
2139 }
2140
mediaActivePorts() const2141 unsigned Endpoint::mediaActivePorts() const
2142 {
2143 return pjsua_conf_get_active_ports();
2144 }
2145
2146 #if !DEPRECATED_FOR_TICKET_2232
mediaEnumPorts() const2147 const AudioMediaVector &Endpoint::mediaEnumPorts() const PJSUA2_THROW(Error)
2148 {
2149 return mediaList;
2150 }
2151 #endif
2152
mediaEnumPorts2() const2153 AudioMediaVector2 Endpoint::mediaEnumPorts2() const PJSUA2_THROW(Error)
2154 {
2155 AudioMediaVector2 amv2;
2156 pjsua_conf_port_id ids[PJSUA_MAX_CONF_PORTS];
2157 unsigned i, count = PJSUA_MAX_CONF_PORTS;
2158
2159 PJSUA2_CHECK_EXPR( pjsua_enum_conf_ports(ids, &count) );
2160 for (i = 0; i < count; ++i) {
2161 AudioMediaHelper am;
2162 am.setPortId(ids[i]);
2163 amv2.push_back(am);
2164 }
2165
2166 return amv2;
2167 }
2168
mediaEnumVidPorts() const2169 VideoMediaVector Endpoint::mediaEnumVidPorts() const PJSUA2_THROW(Error)
2170 {
2171 #if PJSUA_HAS_VIDEO
2172 VideoMediaVector vmv;
2173 pjsua_conf_port_id ids[PJSUA_MAX_CONF_PORTS];
2174 unsigned i, count = PJSUA_MAX_CONF_PORTS;
2175
2176 PJSUA2_CHECK_EXPR( pjsua_vid_conf_enum_ports(ids, &count) );
2177 for (i = 0; i < count; ++i) {
2178 VideoMediaHelper vm;
2179 vm.setPortId(ids[i]);
2180 vmv.push_back(vm);
2181 }
2182
2183 return vmv;
2184 #else
2185 PJSUA2_RAISE_ERROR(PJ_EINVALIDOP);
2186 #endif
2187 }
2188
mediaAdd(AudioMedia & media)2189 void Endpoint::mediaAdd(AudioMedia &media)
2190 {
2191 #if !DEPRECATED_FOR_TICKET_2232
2192 /* mediaList serves mediaEnumPorts() only, once mediaEnumPorts()
2193 * is removed, this function implementation should be no-op.
2194 */
2195 pj_mutex_lock(mediaListMutex);
2196
2197 AudioMediaVector::iterator it = std::find(mediaList.begin(),
2198 mediaList.end(),
2199 &media);
2200
2201 if (it == mediaList.end())
2202 mediaList.push_back(&media);
2203 pj_mutex_unlock(mediaListMutex);
2204 #else
2205 PJ_UNUSED_ARG(media);
2206 #endif
2207 }
2208
mediaRemove(AudioMedia & media)2209 void Endpoint::mediaRemove(AudioMedia &media)
2210 {
2211 #if !DEPRECATED_FOR_TICKET_2232
2212 /* mediaList serves mediaEnumPorts() only, once mediaEnumPorts()
2213 * is removed, this function implementation should be no-op.
2214 */
2215 pj_mutex_lock(mediaListMutex);
2216 AudioMediaVector::iterator it = std::find(mediaList.begin(),
2217 mediaList.end(),
2218 &media);
2219
2220 if (it != mediaList.end())
2221 mediaList.erase(it);
2222 pj_mutex_unlock(mediaListMutex);
2223 #else
2224 PJ_UNUSED_ARG(media);
2225 #endif
2226 }
2227
mediaExists(const AudioMedia & media) const2228 bool Endpoint::mediaExists(const AudioMedia &media) const
2229 {
2230 pjsua_conf_port_id id = media.getPortId();
2231 if (id == PJSUA_INVALID_ID || id >= (int)mediaMaxPorts())
2232 return false;
2233
2234 pjsua_conf_port_info pi;
2235 return (pjsua_conf_get_port_info(id, &pi) == PJ_SUCCESS);
2236 }
2237
audDevManager()2238 AudDevManager &Endpoint::audDevManager()
2239 {
2240 return audioDevMgr;
2241 }
2242
vidDevManager()2243 VidDevManager &Endpoint::vidDevManager()
2244 {
2245 return videoDevMgr;
2246 }
2247
2248 /*
2249 * Codec operations.
2250 */
2251
2252 #if !DEPRECATED_FOR_TICKET_2232
codecEnum()2253 const CodecInfoVector &Endpoint::codecEnum() PJSUA2_THROW(Error)
2254 {
2255 pjsua_codec_info pj_codec[MAX_CODEC_NUM];
2256 unsigned count = MAX_CODEC_NUM;
2257
2258 PJSUA2_CHECK_EXPR( pjsua_enum_codecs(pj_codec, &count) );
2259
2260 updateCodecInfoList(pj_codec, count, codecInfoList);
2261 return codecInfoList;
2262 }
2263 #endif
2264
codecEnum2() const2265 CodecInfoVector2 Endpoint::codecEnum2() const PJSUA2_THROW(Error)
2266 {
2267 CodecInfoVector2 civ2;
2268 pjsua_codec_info pj_codec[MAX_CODEC_NUM];
2269 unsigned count = MAX_CODEC_NUM;
2270
2271 PJSUA2_CHECK_EXPR( pjsua_enum_codecs(pj_codec, &count) );
2272 for (unsigned i = 0; i<count; ++i) {
2273 CodecInfo codec_info;
2274 codec_info.fromPj(pj_codec[i]);
2275 civ2.push_back(codec_info);
2276 }
2277 return civ2;
2278 }
2279
codecSetPriority(const string & codec_id,pj_uint8_t priority)2280 void Endpoint::codecSetPriority(const string &codec_id,
2281 pj_uint8_t priority) PJSUA2_THROW(Error)
2282 {
2283 pj_str_t codec_str = str2Pj(codec_id);
2284 PJSUA2_CHECK_EXPR( pjsua_codec_set_priority(&codec_str, priority) );
2285 }
2286
codecGetParam(const string & codec_id) const2287 CodecParam Endpoint::codecGetParam(const string &codec_id) const
2288 PJSUA2_THROW(Error)
2289 {
2290 CodecParam param;
2291 pjmedia_codec_param pj_param;
2292 pj_str_t codec_str = str2Pj(codec_id);
2293
2294 PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, &pj_param) );
2295
2296 param.fromPj(pj_param);
2297 return param;
2298 }
2299
codecSetParam(const string & codec_id,const CodecParam param)2300 void Endpoint::codecSetParam(const string &codec_id,
2301 const CodecParam param) PJSUA2_THROW(Error)
2302 {
2303 pj_str_t codec_str = str2Pj(codec_id);
2304 pjmedia_codec_param pj_param = param.toPj();
2305
2306 PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, &pj_param) );
2307 }
2308
2309 #if defined(PJMEDIA_HAS_OPUS_CODEC) && (PJMEDIA_HAS_OPUS_CODEC!=0)
2310
getCodecOpusConfig() const2311 CodecOpusConfig Endpoint::getCodecOpusConfig() const PJSUA2_THROW(Error)
2312 {
2313 pjmedia_codec_opus_config opus_cfg;
2314 CodecOpusConfig config;
2315
2316 PJSUA2_CHECK_EXPR(pjmedia_codec_opus_get_config(&opus_cfg));
2317 config.fromPj(opus_cfg);
2318
2319 return config;
2320 }
2321
setCodecOpusConfig(const CodecOpusConfig & opus_cfg)2322 void Endpoint::setCodecOpusConfig(const CodecOpusConfig &opus_cfg)
2323 PJSUA2_THROW(Error)
2324 {
2325 const pj_str_t codec_id = {(char *)"opus", 4};
2326 pjmedia_codec_param param;
2327 pjmedia_codec_opus_config new_opus_cfg;
2328
2329 PJSUA2_CHECK_EXPR(pjsua_codec_get_param(&codec_id, ¶m));
2330
2331 PJSUA2_CHECK_EXPR(pjmedia_codec_opus_get_config(&new_opus_cfg));
2332
2333 new_opus_cfg = opus_cfg.toPj();
2334
2335 PJSUA2_CHECK_EXPR(pjmedia_codec_opus_set_default_param(&new_opus_cfg,
2336 ¶m));
2337 }
2338
2339 #endif
2340
clearCodecInfoList(CodecInfoVector & codec_list)2341 void Endpoint::clearCodecInfoList(CodecInfoVector &codec_list)
2342 {
2343 for (unsigned i=0;i<codec_list.size();++i) {
2344 delete codec_list[i];
2345 }
2346 codec_list.clear();
2347 }
2348
updateCodecInfoList(pjsua_codec_info pj_codec[],unsigned count,CodecInfoVector & codec_list)2349 void Endpoint::updateCodecInfoList(pjsua_codec_info pj_codec[],
2350 unsigned count,
2351 CodecInfoVector &codec_list)
2352 {
2353 pj_enter_critical_section();
2354 clearCodecInfoList(codec_list);
2355 for (unsigned i = 0; i<count; ++i) {
2356 CodecInfo *codec_info = new CodecInfo;
2357
2358 codec_info->fromPj(pj_codec[i]);
2359 codec_list.push_back(codec_info);
2360 }
2361 pj_leave_critical_section();
2362 }
2363
2364 #if !DEPRECATED_FOR_TICKET_2232
videoCodecEnum()2365 const CodecInfoVector &Endpoint::videoCodecEnum() PJSUA2_THROW(Error)
2366 {
2367 #if PJSUA_HAS_VIDEO
2368 pjsua_codec_info pj_codec[MAX_CODEC_NUM];
2369 unsigned count = MAX_CODEC_NUM;
2370
2371 PJSUA2_CHECK_EXPR(pjsua_vid_enum_codecs(pj_codec, &count));
2372
2373 updateCodecInfoList(pj_codec, count, videoCodecInfoList);
2374 #endif
2375 return videoCodecInfoList;
2376 }
2377 #endif
2378
videoCodecEnum2() const2379 CodecInfoVector2 Endpoint::videoCodecEnum2() const PJSUA2_THROW(Error)
2380 {
2381 CodecInfoVector2 civ2;
2382 #if PJSUA_HAS_VIDEO
2383 pjsua_codec_info pj_codec[MAX_CODEC_NUM];
2384 unsigned count = MAX_CODEC_NUM;
2385
2386 PJSUA2_CHECK_EXPR(pjsua_vid_enum_codecs(pj_codec, &count));
2387 for (unsigned i = 0; i<count; ++i) {
2388 CodecInfo codec_info;
2389 codec_info.fromPj(pj_codec[i]);
2390 civ2.push_back(codec_info);
2391 }
2392 #endif
2393 return civ2;
2394 }
2395
videoCodecSetPriority(const string & codec_id,pj_uint8_t priority)2396 void Endpoint::videoCodecSetPriority(const string &codec_id,
2397 pj_uint8_t priority) PJSUA2_THROW(Error)
2398 {
2399 #if PJSUA_HAS_VIDEO
2400 pj_str_t codec_str = str2Pj(codec_id);
2401 PJSUA2_CHECK_EXPR(pjsua_vid_codec_set_priority(&codec_str, priority));
2402 #else
2403 PJ_UNUSED_ARG(codec_id);
2404 PJ_UNUSED_ARG(priority);
2405 #endif
2406 }
2407
getVideoCodecParam(const string & codec_id) const2408 VidCodecParam Endpoint::getVideoCodecParam(const string &codec_id) const
2409 PJSUA2_THROW(Error)
2410 {
2411 VidCodecParam codec_param;
2412 #if PJSUA_HAS_VIDEO
2413 pjmedia_vid_codec_param pj_param;
2414 pj_str_t codec_str = str2Pj(codec_id);
2415
2416 PJSUA2_CHECK_EXPR(pjsua_vid_codec_get_param(&codec_str, &pj_param));
2417 codec_param.fromPj(pj_param);
2418 #else
2419 PJ_UNUSED_ARG(codec_id);
2420 #endif
2421 return codec_param;
2422 }
2423
setVideoCodecParam(const string & codec_id,const VidCodecParam & param)2424 void Endpoint::setVideoCodecParam(const string &codec_id,
2425 const VidCodecParam ¶m)
2426 PJSUA2_THROW(Error)
2427 {
2428 #if PJSUA_HAS_VIDEO
2429 pj_str_t codec_str = str2Pj(codec_id);
2430 pjmedia_vid_codec_param pj_param = param.toPj();
2431
2432 PJSUA2_CHECK_EXPR(pjsua_vid_codec_set_param(&codec_str, &pj_param));
2433 #else
2434 PJ_UNUSED_ARG(codec_id);
2435 PJ_UNUSED_ARG(param);
2436 #endif
2437 }
2438
resetVideoCodecParam(const string & codec_id)2439 void Endpoint::resetVideoCodecParam(const string &codec_id)
2440 PJSUA2_THROW(Error)
2441 {
2442 #if PJSUA_HAS_VIDEO
2443 pj_str_t codec_str = str2Pj(codec_id);
2444
2445 PJSUA2_CHECK_EXPR(pjsua_vid_codec_set_param(&codec_str, NULL));
2446 #else
2447 PJ_UNUSED_ARG(codec_id);
2448 #endif
2449 }
2450
2451 /*
2452 * Enumerate all SRTP crypto-suite names.
2453 */
srtpCryptoEnum()2454 StringVector Endpoint::srtpCryptoEnum() PJSUA2_THROW(Error)
2455 {
2456 StringVector result;
2457 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
2458 unsigned cnt = PJMEDIA_SRTP_MAX_CRYPTOS;
2459 pjmedia_srtp_crypto cryptos[PJMEDIA_SRTP_MAX_CRYPTOS];
2460
2461 PJSUA2_CHECK_EXPR(pjmedia_srtp_enum_crypto(&cnt, cryptos));
2462
2463 for (unsigned i = 0; i < cnt; ++i)
2464 result.push_back(pj2Str(cryptos[i].name));
2465 #endif
2466
2467 return result;
2468 }
2469
handleIpChange(const IpChangeParam & param)2470 void Endpoint::handleIpChange(const IpChangeParam ¶m) PJSUA2_THROW(Error)
2471 {
2472 pjsua_ip_change_param ip_change_param = param.toPj();
2473 PJSUA2_CHECK_EXPR(pjsua_handle_ip_change(&ip_change_param));
2474 }
2475