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(&param);
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(&param);
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 &param)
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, &param));
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 							  &param));
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 &param)
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 &param) 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