blob: 4b11bdae409a4a3156b50e47fe735ccbc2c33f3d [file] [log] [blame]
Alexandre Lision0e143012014-01-22 11:02:46 -05001/* $Id: endpoint.cpp 4704 2014-01-16 05:30:46Z ming $ */
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
26using namespace pj;
27using namespace std;
28
29#include <pjsua2/account.hpp>
30#include <pjsua2/call.hpp>
31
32#define THIS_FILE "endpoint.cpp"
33#define MAX_STUN_SERVERS 32
34#define TIMER_SIGNATURE 0x600D878A
35#define MAX_CODEC_NUM 64
36
37struct UserTimer
38{
39 pj_uint32_t signature;
40 OnTimerParam prm;
41 pj_timer_entry entry;
42};
43
44Endpoint *Endpoint::instance_;
45
46///////////////////////////////////////////////////////////////////////////////
47
48UaConfig::UaConfig()
49{
50 pjsua_config ua_cfg;
51
52 pjsua_config_default(&ua_cfg);
53 fromPj(ua_cfg);
54}
55
56void UaConfig::fromPj(const pjsua_config &ua_cfg)
57{
58 unsigned i;
59
60 this->maxCalls = ua_cfg.max_calls;
61 this->threadCnt = ua_cfg.thread_cnt;
62 this->userAgent = pj2Str(ua_cfg.user_agent);
63
64 for (i=0; i<ua_cfg.nameserver_count; ++i) {
65 this->nameserver.push_back(pj2Str(ua_cfg.nameserver[i]));
66 }
67
68 for (i=0; i<ua_cfg.stun_srv_cnt; ++i) {
69 this->stunServer.push_back(pj2Str(ua_cfg.stun_srv[i]));
70 }
71
72 this->stunIgnoreFailure = PJ2BOOL(ua_cfg.stun_ignore_failure);
73 this->natTypeInSdp = ua_cfg.nat_type_in_sdp;
74 this->mwiUnsolicitedEnabled = PJ2BOOL(ua_cfg.enable_unsolicited_mwi);
75}
76
77pjsua_config UaConfig::toPj() const
78{
79 unsigned i;
80 pjsua_config pua_cfg;
81
82 pjsua_config_default(&pua_cfg);
83
84 pua_cfg.max_calls = this->maxCalls;
85 pua_cfg.thread_cnt = this->threadCnt;
86 pua_cfg.user_agent = str2Pj(this->userAgent);
87
88 for (i=0; i<this->nameserver.size() && i<PJ_ARRAY_SIZE(pua_cfg.nameserver);
89 ++i)
90 {
91 pua_cfg.nameserver[i] = str2Pj(this->nameserver[i]);
92 }
93 pua_cfg.nameserver_count = i;
94
95 for (i=0; i<this->stunServer.size() && i<PJ_ARRAY_SIZE(pua_cfg.stun_srv);
96 ++i)
97 {
98 pua_cfg.stun_srv[i] = str2Pj(this->stunServer[i]);
99 }
100 pua_cfg.stun_srv_cnt = i;
101
102 pua_cfg.nat_type_in_sdp = this->natTypeInSdp;
103 pua_cfg.enable_unsolicited_mwi = this->mwiUnsolicitedEnabled;
104
105 return pua_cfg;
106}
107
108void UaConfig::readObject(const ContainerNode &node) throw(Error)
109{
110 ContainerNode this_node = node.readContainer("UaConfig");
111
112 NODE_READ_UNSIGNED( this_node, maxCalls);
113 NODE_READ_UNSIGNED( this_node, threadCnt);
114 NODE_READ_BOOL ( this_node, mainThreadOnly);
115 NODE_READ_STRINGV ( this_node, nameserver);
116 NODE_READ_STRING ( this_node, userAgent);
117 NODE_READ_STRINGV ( this_node, stunServer);
118 NODE_READ_BOOL ( this_node, stunIgnoreFailure);
119 NODE_READ_INT ( this_node, natTypeInSdp);
120 NODE_READ_BOOL ( this_node, mwiUnsolicitedEnabled);
121}
122
123void UaConfig::writeObject(ContainerNode &node) const throw(Error)
124{
125 ContainerNode this_node = node.writeNewContainer("UaConfig");
126
127 NODE_WRITE_UNSIGNED( this_node, maxCalls);
128 NODE_WRITE_UNSIGNED( this_node, threadCnt);
129 NODE_WRITE_BOOL ( this_node, mainThreadOnly);
130 NODE_WRITE_STRINGV ( this_node, nameserver);
131 NODE_WRITE_STRING ( this_node, userAgent);
132 NODE_WRITE_STRINGV ( this_node, stunServer);
133 NODE_WRITE_BOOL ( this_node, stunIgnoreFailure);
134 NODE_WRITE_INT ( this_node, natTypeInSdp);
135 NODE_WRITE_BOOL ( this_node, mwiUnsolicitedEnabled);
136}
137
138///////////////////////////////////////////////////////////////////////////////
139
140LogConfig::LogConfig()
141{
142 pjsua_logging_config lc;
143
144 pjsua_logging_config_default(&lc);
145 fromPj(lc);
146}
147
148void LogConfig::fromPj(const pjsua_logging_config &lc)
149{
150 this->msgLogging = lc.msg_logging;
151 this->level = lc.level;
152 this->consoleLevel = lc.console_level;
153 this->decor = lc.decor;
154 this->filename = pj2Str(lc.log_filename);
155 this->fileFlags = lc.log_file_flags;
156 this->writer = NULL;
157}
158
159pjsua_logging_config LogConfig::toPj() const
160{
161 pjsua_logging_config lc;
162
163 pjsua_logging_config_default(&lc);
164
165 lc.msg_logging = this->msgLogging;
166 lc.level = this->level;
167 lc.console_level = this->consoleLevel;
168 lc.decor = this->decor;
169 lc.log_file_flags = this->fileFlags;
170 lc.log_filename = str2Pj(this->filename);
171
172 return lc;
173}
174
175void LogConfig::readObject(const ContainerNode &node) throw(Error)
176{
177 ContainerNode this_node = node.readContainer("LogConfig");
178
179 NODE_READ_UNSIGNED( this_node, msgLogging);
180 NODE_READ_UNSIGNED( this_node, level);
181 NODE_READ_UNSIGNED( this_node, consoleLevel);
182 NODE_READ_UNSIGNED( this_node, decor);
183 NODE_READ_STRING ( this_node, filename);
184 NODE_READ_UNSIGNED( this_node, fileFlags);
185}
186
187void LogConfig::writeObject(ContainerNode &node) const throw(Error)
188{
189 ContainerNode this_node = node.writeNewContainer("LogConfig");
190
191 NODE_WRITE_UNSIGNED( this_node, msgLogging);
192 NODE_WRITE_UNSIGNED( this_node, level);
193 NODE_WRITE_UNSIGNED( this_node, consoleLevel);
194 NODE_WRITE_UNSIGNED( this_node, decor);
195 NODE_WRITE_STRING ( this_node, filename);
196 NODE_WRITE_UNSIGNED( this_node, fileFlags);
197}
198
199///////////////////////////////////////////////////////////////////////////////
200
201MediaConfig::MediaConfig()
202{
203 pjsua_media_config mc;
204
205 pjsua_media_config_default(&mc);
206 fromPj(mc);
207}
208
209void MediaConfig::fromPj(const pjsua_media_config &mc)
210{
211 this->clockRate = mc.clock_rate;
212 this->sndClockRate = mc.snd_clock_rate;
213 this->channelCount = mc.channel_count;
214 this->audioFramePtime = mc.audio_frame_ptime;
215 this->maxMediaPorts = mc.max_media_ports;
216 this->hasIoqueue = PJ2BOOL(mc.has_ioqueue);
217 this->threadCnt = mc.thread_cnt;
218 this->quality = mc.quality;
219 this->ptime = mc.ptime;
220 this->noVad = PJ2BOOL(mc.no_vad);
221 this->ilbcMode = mc.ilbc_mode;
222 this->txDropPct = mc.tx_drop_pct;
223 this->rxDropPct = mc.rx_drop_pct;
224 this->ecOptions = mc.ec_options;
225 this->ecTailLen = mc.ec_tail_len;
226 this->sndRecLatency = mc.snd_rec_latency;
227 this->sndPlayLatency = mc.snd_play_latency;
228 this->jbInit = mc.jb_init;
229 this->jbMinPre = mc.jb_min_pre;
230 this->jbMaxPre = mc.jb_max_pre;
231 this->jbMax = mc.jb_max;
232 this->sndAutoCloseTime = mc.snd_auto_close_time;
233 this->vidPreviewEnableNative = PJ2BOOL(mc.vid_preview_enable_native);
234}
235
236pjsua_media_config MediaConfig::toPj() const
237{
238 pjsua_media_config mcfg;
239
240 pjsua_media_config_default(&mcfg);
241
242 mcfg.clock_rate = this->clockRate;
243 mcfg.snd_clock_rate = this->sndClockRate;
244 mcfg.channel_count = this->channelCount;
245 mcfg.audio_frame_ptime = this->audioFramePtime;
246 mcfg.max_media_ports = this->maxMediaPorts;
247 mcfg.has_ioqueue = this->hasIoqueue;
248 mcfg.thread_cnt = this->threadCnt;
249 mcfg.quality = this->quality;
250 mcfg.ptime = this->ptime;
251 mcfg.no_vad = this->noVad;
252 mcfg.ilbc_mode = this->ilbcMode;
253 mcfg.tx_drop_pct = this->txDropPct;
254 mcfg.rx_drop_pct = this->rxDropPct;
255 mcfg.ec_options = this->ecOptions;
256 mcfg.ec_tail_len = this->ecTailLen;
257 mcfg.snd_rec_latency = this->sndRecLatency;
258 mcfg.snd_play_latency = this->sndPlayLatency;
259 mcfg.jb_init = this->jbInit;
260 mcfg.jb_min_pre = this->jbMinPre;
261 mcfg.jb_max_pre = this->jbMaxPre;
262 mcfg.jb_max = this->jbMax;
263 mcfg.snd_auto_close_time = this->sndAutoCloseTime;
264 mcfg.vid_preview_enable_native = this->vidPreviewEnableNative;
265
266 return mcfg;
267}
268
269void MediaConfig::readObject(const ContainerNode &node) throw(Error)
270{
271 ContainerNode this_node = node.readContainer("MediaConfig");
272
273 NODE_READ_UNSIGNED( this_node, clockRate);
274 NODE_READ_UNSIGNED( this_node, sndClockRate);
275 NODE_READ_UNSIGNED( this_node, channelCount);
276 NODE_READ_UNSIGNED( this_node, audioFramePtime);
277 NODE_READ_UNSIGNED( this_node, maxMediaPorts);
278 NODE_READ_BOOL ( this_node, hasIoqueue);
279 NODE_READ_UNSIGNED( this_node, threadCnt);
280 NODE_READ_UNSIGNED( this_node, quality);
281 NODE_READ_UNSIGNED( this_node, ptime);
282 NODE_READ_BOOL ( this_node, noVad);
283 NODE_READ_UNSIGNED( this_node, ilbcMode);
284 NODE_READ_UNSIGNED( this_node, txDropPct);
285 NODE_READ_UNSIGNED( this_node, rxDropPct);
286 NODE_READ_UNSIGNED( this_node, ecOptions);
287 NODE_READ_UNSIGNED( this_node, ecTailLen);
288 NODE_READ_UNSIGNED( this_node, sndRecLatency);
289 NODE_READ_UNSIGNED( this_node, sndPlayLatency);
290 NODE_READ_INT ( this_node, jbInit);
291 NODE_READ_INT ( this_node, jbMinPre);
292 NODE_READ_INT ( this_node, jbMaxPre);
293 NODE_READ_INT ( this_node, jbMax);
294 NODE_READ_INT ( this_node, sndAutoCloseTime);
295 NODE_READ_BOOL ( this_node, vidPreviewEnableNative);
296}
297
298void MediaConfig::writeObject(ContainerNode &node) const throw(Error)
299{
300 ContainerNode this_node = node.writeNewContainer("MediaConfig");
301
302 NODE_WRITE_UNSIGNED( this_node, clockRate);
303 NODE_WRITE_UNSIGNED( this_node, sndClockRate);
304 NODE_WRITE_UNSIGNED( this_node, channelCount);
305 NODE_WRITE_UNSIGNED( this_node, audioFramePtime);
306 NODE_WRITE_UNSIGNED( this_node, maxMediaPorts);
307 NODE_WRITE_BOOL ( this_node, hasIoqueue);
308 NODE_WRITE_UNSIGNED( this_node, threadCnt);
309 NODE_WRITE_UNSIGNED( this_node, quality);
310 NODE_WRITE_UNSIGNED( this_node, ptime);
311 NODE_WRITE_BOOL ( this_node, noVad);
312 NODE_WRITE_UNSIGNED( this_node, ilbcMode);
313 NODE_WRITE_UNSIGNED( this_node, txDropPct);
314 NODE_WRITE_UNSIGNED( this_node, rxDropPct);
315 NODE_WRITE_UNSIGNED( this_node, ecOptions);
316 NODE_WRITE_UNSIGNED( this_node, ecTailLen);
317 NODE_WRITE_UNSIGNED( this_node, sndRecLatency);
318 NODE_WRITE_UNSIGNED( this_node, sndPlayLatency);
319 NODE_WRITE_INT ( this_node, jbInit);
320 NODE_WRITE_INT ( this_node, jbMinPre);
321 NODE_WRITE_INT ( this_node, jbMaxPre);
322 NODE_WRITE_INT ( this_node, jbMax);
323 NODE_WRITE_INT ( this_node, sndAutoCloseTime);
324 NODE_WRITE_BOOL ( this_node, vidPreviewEnableNative);
325}
326
327///////////////////////////////////////////////////////////////////////////////
328
329void EpConfig::readObject(const ContainerNode &node) throw(Error)
330{
331 ContainerNode this_node = node.readContainer("EpConfig");
332 NODE_READ_OBJ( this_node, uaConfig);
333 NODE_READ_OBJ( this_node, logConfig);
334 NODE_READ_OBJ( this_node, medConfig);
335}
336
337void EpConfig::writeObject(ContainerNode &node) const throw(Error)
338{
339 ContainerNode this_node = node.writeNewContainer("EpConfig");
340 NODE_WRITE_OBJ( this_node, uaConfig);
341 NODE_WRITE_OBJ( this_node, logConfig);
342 NODE_WRITE_OBJ( this_node, medConfig);
343}
344
345///////////////////////////////////////////////////////////////////////////////
346/* Class to post log to main thread */
347struct PendingLog : public PendingJob
348{
349 LogEntry entry;
350 virtual void execute(bool is_pending)
351 {
352 PJ_UNUSED_ARG(is_pending);
353 Endpoint::instance().utilLogWrite(entry);
354 }
355};
356
357///////////////////////////////////////////////////////////////////////////////
358/*
359 * Endpoint instance
360 */
361Endpoint::Endpoint()
362: writer(NULL), mainThreadOnly(false), mainThread(NULL), pendingJobSize(0)
363{
364 if (instance_) {
365 PJSUA2_RAISE_ERROR(PJ_EEXISTS);
366 }
367
368 instance_ = this;
369}
370
371Endpoint& Endpoint::instance() throw(Error)
372{
373 if (!instance_) {
374 PJSUA2_RAISE_ERROR(PJ_ENOTFOUND);
375 }
376 return *instance_;
377}
378
379Endpoint::~Endpoint()
380{
381 while (!pendingJobs.empty()) {
382 delete pendingJobs.front();
383 pendingJobs.pop_front();
384 }
385
386 while(mediaList.size() > 0) {
387 AudioMedia *cur_media = mediaList[0];
388 delete cur_media; /* this will remove itself from the list */
389 }
390
391 clearCodecInfoList();
392
393 try {
394 libDestroy();
395 } catch (Error &err) {
396 // Ignore
397 PJ_UNUSED_ARG(err);
398 }
399
400 instance_ = NULL;
401}
402
403void Endpoint::utilAddPendingJob(PendingJob *job)
404{
405 enum {
406 MAX_PENDING_JOBS = 1024
407 };
408
409 /* See if we can execute immediately */
410 if (!mainThreadOnly || pj_thread_this()==mainThread) {
411 job->execute(false);
412 delete job;
413 return;
414 }
415
416 if (pendingJobSize > MAX_PENDING_JOBS) {
417 enum { NUMBER_TO_DISCARD = 5 };
418
419 pj_enter_critical_section();
420 for (unsigned i=0; i<NUMBER_TO_DISCARD; ++i) {
421 delete pendingJobs.back();
422 pendingJobs.pop_back();
423 }
424
425 pendingJobSize -= NUMBER_TO_DISCARD;
426 pj_leave_critical_section();
427
428 utilLogWrite(1, THIS_FILE,
429 "*** ERROR: Job queue full!! Jobs discarded!!! ***");
430 }
431
432 pj_enter_critical_section();
433 pendingJobs.push_back(job);
434 pendingJobSize++;
435 pj_leave_critical_section();
436}
437
438/* Handle log callback */
439void Endpoint::utilLogWrite(LogEntry &entry)
440{
441 if (mainThreadOnly && pj_thread_this() != mainThread) {
442 PendingLog *job = new PendingLog;
443 job->entry = entry;
444 utilAddPendingJob(job);
445 } else {
446 writer->write(entry);
447 }
448}
449
450/* Run pending jobs only in main thread */
451void Endpoint::performPendingJobs()
452{
453 if (pj_thread_this() != mainThread)
454 return;
455
456 if (pendingJobSize == 0)
457 return;
458
459 for (;;) {
460 PendingJob *job = NULL;
461
462 pj_enter_critical_section();
463 if (pendingJobSize != 0) {
464 job = pendingJobs.front();
465 pendingJobs.pop_front();
466 pendingJobSize--;
467 }
468 pj_leave_critical_section();
469
470 if (job) {
471 job->execute(true);
472 delete job;
473 } else
474 break;
475 }
476}
477
478///////////////////////////////////////////////////////////////////////////////
479/*
480 * Endpoint static callbacks
481 */
482void Endpoint::logFunc(int level, const char *data, int len)
483{
484 Endpoint &ep = Endpoint::instance();
485
486 if (!ep.writer)
487 return;
488
489 LogEntry entry;
490 entry.level = level;
491 entry.msg = string(data, len);
492 entry.threadId = (long)pj_thread_this();
493 entry.threadName = string(pj_thread_get_name(pj_thread_this()));
494
495 ep.utilLogWrite(entry);
496}
497
498void Endpoint::stun_resolve_cb(const pj_stun_resolve_result *res)
499{
500 Endpoint &ep = Endpoint::instance();
501
502 if (!res)
503 return;
504
505 OnNatCheckStunServersCompleteParam prm;
506
507 prm.userData = res->token;
508 prm.status = res->status;
509 if (res->status == PJ_SUCCESS) {
510 char straddr[PJ_INET6_ADDRSTRLEN+10];
511
512 prm.name = string(res->name.ptr, res->name.slen);
513 pj_sockaddr_print(&res->addr, straddr, sizeof(straddr), 3);
514 prm.addr = straddr;
515 }
516
517 ep.onNatCheckStunServersComplete(prm);
518}
519
520void Endpoint::on_timer(pj_timer_heap_t *timer_heap,
521 pj_timer_entry *entry)
522{
523 PJ_UNUSED_ARG(timer_heap);
524
525 Endpoint &ep = Endpoint::instance();
526 UserTimer *ut = (UserTimer*) entry->user_data;
527
528 if (ut->signature != TIMER_SIGNATURE)
529 return;
530
531 ep.onTimer(ut->prm);
532}
533
534void Endpoint::on_nat_detect(const pj_stun_nat_detect_result *res)
535{
536 Endpoint &ep = Endpoint::instance();
537
538 if (!res)
539 return;
540
541 OnNatDetectionCompleteParam prm;
542
543 prm.status = res->status;
544 prm.reason = res->status_text;
545 prm.natType = res->nat_type;
546 prm.natTypeName = res->nat_type_name;
547
548 ep.onNatDetectionComplete(prm);
549}
550
551void Endpoint::on_transport_state( pjsip_transport *tp,
552 pjsip_transport_state state,
553 const pjsip_transport_state_info *info)
554{
555 Endpoint &ep = Endpoint::instance();
556
557 OnTransportStateParam prm;
558
559 prm.hnd = (TransportHandle)tp;
560 prm.state = state;
561 prm.lastError = info ? info->status : PJ_SUCCESS;
562
563 ep.onTransportState(prm);
564}
565
566///////////////////////////////////////////////////////////////////////////////
567/*
568 * Account static callbacks
569 */
570
571Account *Endpoint::lookupAcc(int acc_id, const char *op)
572{
573 Account *acc = Account::lookup(acc_id);
574 if (!acc) {
575 PJ_LOG(1,(THIS_FILE,
576 "Error: cannot find Account instance for account id %d in "
577 "%s", acc_id, op));
578 }
579
580 return acc;
581}
582
583Call *Endpoint::lookupCall(int call_id, const char *op)
584{
585 Call *call = Call::lookup(call_id);
586 if (!call) {
587 PJ_LOG(1,(THIS_FILE,
588 "Error: cannot find Call instance for call id %d in "
589 "%s", call_id, op));
590 }
591
592 return call;
593}
594
595void Endpoint::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
596 pjsip_rx_data *rdata)
597{
598 Account *acc = lookupAcc(acc_id, "on_incoming_call()");
599 if (!acc) {
600 pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
601 return;
602 }
603
604 /* call callback */
605 OnIncomingCallParam prm;
606 prm.callId = call_id;
607 prm.rdata.fromPj(*rdata);
608
609 acc->onIncomingCall(prm);
610
611 /* disconnect if callback doesn't handle the call */
612 pjsua_call_info ci;
613
614 pjsua_call_get_info(call_id, &ci);
615 if (!pjsua_call_get_user_data(call_id) &&
616 ci.state != PJSIP_INV_STATE_DISCONNECTED)
617 {
618 pjsua_call_hangup(call_id, PJSIP_SC_INTERNAL_SERVER_ERROR, NULL, NULL);
619 }
620}
621
622void Endpoint::on_reg_started(pjsua_acc_id acc_id, pj_bool_t renew)
623{
624 Account *acc = lookupAcc(acc_id, "on_reg_started()");
625 if (!acc) {
626 return;
627 }
628
629 OnRegStartedParam prm;
630 prm.renew = PJ2BOOL(renew);
631 acc->onRegStarted(prm);
632}
633
634void Endpoint::on_reg_state2(pjsua_acc_id acc_id, pjsua_reg_info *info)
635{
636 Account *acc = lookupAcc(acc_id, "on_reg_state2()");
637 if (!acc) {
638 return;
639 }
640
641 OnRegStateParam prm;
642 prm.status = info->cbparam->status;
643 prm.code = (pjsip_status_code) info->cbparam->code;
644 prm.reason = pj2Str(info->cbparam->reason);
645 if (info->cbparam->rdata)
646 prm.rdata.fromPj(*info->cbparam->rdata);
647 prm.expiration = info->cbparam->expiration;
648
649 acc->onRegState(prm);
650}
651
652void Endpoint::on_incoming_subscribe(pjsua_acc_id acc_id,
653 pjsua_srv_pres *srv_pres,
654 pjsua_buddy_id buddy_id,
655 const pj_str_t *from,
656 pjsip_rx_data *rdata,
657 pjsip_status_code *code,
658 pj_str_t *reason,
659 pjsua_msg_data *msg_data)
660{
661 PJ_UNUSED_ARG(buddy_id);
662 PJ_UNUSED_ARG(srv_pres);
663
664 Account *acc = lookupAcc(acc_id, "on_incoming_subscribe()");
665 if (!acc) {
666 /* default behavior should apply */
667 return;
668 }
669
670 OnIncomingSubscribeParam prm;
671 prm.srvPres = srv_pres;
672 prm.fromUri = pj2Str(*from);
673 prm.rdata.fromPj(*rdata);
674 prm.code = *code;
675 prm.reason = pj2Str(*reason);
676 prm.txOption.fromPj(*msg_data);
677
678 acc->onIncomingSubscribe(prm);
679
680 *code = prm.code;
681 acc->tmpReason = prm.reason;
682 *reason = str2Pj(acc->tmpReason);
683 prm.txOption.toPj(*msg_data);
684}
685
686void Endpoint::on_pager2(pjsua_call_id call_id,
687 const pj_str_t *from,
688 const pj_str_t *to,
689 const pj_str_t *contact,
690 const pj_str_t *mime_type,
691 const pj_str_t *body,
692 pjsip_rx_data *rdata,
693 pjsua_acc_id acc_id)
694{
695 OnInstantMessageParam prm;
696 prm.fromUri = pj2Str(*from);
697 prm.toUri = pj2Str(*to);
698 prm.contactUri = pj2Str(*contact);
699 prm.contentType = pj2Str(*mime_type);
700 prm.msgBody = pj2Str(*body);
701 prm.rdata.fromPj(*rdata);
702
703 if (call_id != PJSUA_INVALID_ID) {
704 Call *call = lookupCall(call_id, "on_pager2()");
705 if (!call) {
706 /* Ignored */
707 return;
708 }
709
710 call->onInstantMessage(prm);
711 } else {
712 Account *acc = lookupAcc(acc_id, "on_pager2()");
713 if (!acc) {
714 /* Ignored */
715 return;
716 }
717
718 acc->onInstantMessage(prm);
719 }
720}
721
722void Endpoint::on_pager_status2( pjsua_call_id call_id,
723 const pj_str_t *to,
724 const pj_str_t *body,
725 void *user_data,
726 pjsip_status_code status,
727 const pj_str_t *reason,
728 pjsip_tx_data *tdata,
729 pjsip_rx_data *rdata,
730 pjsua_acc_id acc_id)
731{
732 PJ_UNUSED_ARG(tdata);
733
734 OnInstantMessageStatusParam prm;
735 prm.userData = user_data;
736 prm.toUri = pj2Str(*to);
737 prm.msgBody = pj2Str(*body);
738 prm.code = status;
739 prm.reason = pj2Str(*reason);
740 if (rdata)
741 prm.rdata.fromPj(*rdata);
742
743 if (call_id != PJSUA_INVALID_ID) {
744 Call *call = lookupCall(call_id, "on_pager_status2()");
745 if (!call) {
746 /* Ignored */
747 return;
748 }
749
750 call->onInstantMessageStatus(prm);
751 } else {
752 Account *acc = lookupAcc(acc_id, "on_pager_status2()");
753 if (!acc) {
754 /* Ignored */
755 return;
756 }
757
758 acc->onInstantMessageStatus(prm);
759 }
760}
761
762void Endpoint::on_typing2( pjsua_call_id call_id,
763 const pj_str_t *from,
764 const pj_str_t *to,
765 const pj_str_t *contact,
766 pj_bool_t is_typing,
767 pjsip_rx_data *rdata,
768 pjsua_acc_id acc_id)
769{
770 OnTypingIndicationParam prm;
771 prm.fromUri = pj2Str(*from);
772 prm.toUri = pj2Str(*to);
773 prm.contactUri = pj2Str(*contact);
774 prm.isTyping = is_typing != 0;
775 prm.rdata.fromPj(*rdata);
776
777 if (call_id != PJSUA_INVALID_ID) {
778 Call *call = lookupCall(call_id, "on_typing2()");
779 if (!call) {
780 /* Ignored */
781 return;
782 }
783
784 call->onTypingIndication(prm);
785 } else {
786 Account *acc = lookupAcc(acc_id, "on_typing2()");
787 if (!acc) {
788 /* Ignored */
789 return;
790 }
791
792 acc->onTypingIndication(prm);
793 }
794}
795
796void Endpoint::on_mwi_info(pjsua_acc_id acc_id,
797 pjsua_mwi_info *mwi_info)
798{
799 OnMwiInfoParam prm;
800 prm.state = pjsip_evsub_get_state(mwi_info->evsub);
801 prm.rdata.fromPj(*mwi_info->rdata);
802
803 Account *acc = lookupAcc(acc_id, "on_mwi_info()");
804 if (!acc) {
805 /* Ignored */
806 return;
807 }
808
809 acc->onMwiInfo(prm);
810}
811
812void Endpoint::on_buddy_state(pjsua_buddy_id buddy_id)
813{
814 Buddy *buddy = (Buddy*)pjsua_buddy_get_user_data(buddy_id);
815 if (!buddy || !buddy->isValid()) {
816 /* Ignored */
817 return;
818 }
819
820 buddy->onBuddyState();
821}
822
823// Call callbacks
824void Endpoint::on_call_state(pjsua_call_id call_id, pjsip_event *e)
825{
826 Call *call = Call::lookup(call_id);
827 if (!call) {
828 return;
829 }
830
831 OnCallStateParam prm;
832 prm.e.fromPj(*e);
833
834 call->processStateChange(prm);
835 /* If the state is DISCONNECTED, call may have already been deleted
836 * by the application in the callback, so do not access it anymore here.
837 */
838}
839
840void Endpoint::on_call_tsx_state(pjsua_call_id call_id,
841 pjsip_transaction *tsx,
842 pjsip_event *e)
843{
844 PJ_UNUSED_ARG(tsx);
845
846 Call *call = Call::lookup(call_id);
847 if (!call) {
848 return;
849 }
850
851 OnCallTsxStateParam prm;
852 prm.e.fromPj(*e);
853
854 call->onCallTsxState(prm);
855}
856
857void Endpoint::on_call_media_state(pjsua_call_id call_id)
858{
859 Call *call = Call::lookup(call_id);
860 if (!call) {
861 return;
862 }
863
864 OnCallMediaStateParam prm;
865 call->processMediaUpdate(prm);
866}
867
868void Endpoint::on_call_sdp_created(pjsua_call_id call_id,
869 pjmedia_sdp_session *sdp,
870 pj_pool_t *pool,
871 const pjmedia_sdp_session *rem_sdp)
872{
873 Call *call = Call::lookup(call_id);
874 if (!call) {
875 return;
876 }
877
878 OnCallSdpCreatedParam prm;
879 string orig_sdp;
880
881 prm.sdp.fromPj(*sdp);
882 orig_sdp = prm.sdp.wholeSdp;
883 if (rem_sdp)
884 prm.remSdp.fromPj(*rem_sdp);
885
886 call->onCallSdpCreated(prm);
887
888 /* Check if application modifies the SDP */
889 if (orig_sdp != prm.sdp.wholeSdp) {
890 pjmedia_sdp_parse(pool, (char*)prm.sdp.wholeSdp.c_str(),
891 prm.sdp.wholeSdp.size(), &sdp);
892 }
893}
894
895void Endpoint::on_stream_created(pjsua_call_id call_id,
896 pjmedia_stream *strm,
897 unsigned stream_idx,
898 pjmedia_port **p_port)
899{
900 Call *call = Call::lookup(call_id);
901 if (!call) {
902 return;
903 }
904
905 OnStreamCreatedParam prm;
906 prm.stream = strm;
907 prm.streamIdx = stream_idx;
908 prm.pPort = (void *)*p_port;
909
910 call->onStreamCreated(prm);
911
912 if (prm.pPort != (void *)*p_port)
913 *p_port = (pjmedia_port *)prm.pPort;
914}
915
916void Endpoint::on_stream_destroyed(pjsua_call_id call_id,
917 pjmedia_stream *strm,
918 unsigned stream_idx)
919{
920 Call *call = Call::lookup(call_id);
921 if (!call) {
922 return;
923 }
924
925 OnStreamDestroyedParam prm;
926 prm.stream = strm;
927 prm.streamIdx = stream_idx;
928
929 call->onStreamDestroyed(prm);
930}
931
932struct PendingOnDtmfDigitCallback : public PendingJob
933{
934 int call_id;
935 OnDtmfDigitParam prm;
936
937 virtual void execute(bool is_pending)
938 {
939 PJ_UNUSED_ARG(is_pending);
940
941 Call *call = Call::lookup(call_id);
942 if (!call)
943 return;
944
945 call->onDtmfDigit(prm);
946 }
947};
948
949void Endpoint::on_dtmf_digit(pjsua_call_id call_id, int digit)
950{
951 Call *call = Call::lookup(call_id);
952 if (!call) {
953 return;
954 }
955
956 PendingOnDtmfDigitCallback *job = new PendingOnDtmfDigitCallback;
957 job->call_id = call_id;
958 char buf[10];
959 pj_ansi_sprintf(buf, "%c", digit);
960 job->prm.digit = (string)buf;
961
962 Endpoint::instance().utilAddPendingJob(job);
963}
964
965void Endpoint::on_call_transfer_request2(pjsua_call_id call_id,
966 const pj_str_t *dst,
967 pjsip_status_code *code,
968 pjsua_call_setting *opt)
969{
970 Call *call = Call::lookup(call_id);
971 if (!call) {
972 return;
973 }
974
975 OnCallTransferRequestParam prm;
976 prm.dstUri = pj2Str(*dst);
977 prm.statusCode = *code;
978 prm.opt.fromPj(*opt);
979
980 call->onCallTransferRequest(prm);
981
982 *code = prm.statusCode;
983 *opt = prm.opt.toPj();
984}
985
986void Endpoint::on_call_transfer_status(pjsua_call_id call_id,
987 int st_code,
988 const pj_str_t *st_text,
989 pj_bool_t final,
990 pj_bool_t *p_cont)
991{
992 Call *call = Call::lookup(call_id);
993 if (!call) {
994 return;
995 }
996
997 OnCallTransferStatusParam prm;
998 prm.statusCode = (pjsip_status_code)st_code;
999 prm.reason = pj2Str(*st_text);
1000 prm.finalNotify = PJ2BOOL(final);
1001 prm.cont = PJ2BOOL(*p_cont);
1002
1003 call->onCallTransferStatus(prm);
1004
1005 *p_cont = prm.cont;
1006}
1007
1008void Endpoint::on_call_replace_request2(pjsua_call_id call_id,
1009 pjsip_rx_data *rdata,
1010 int *st_code,
1011 pj_str_t *st_text,
1012 pjsua_call_setting *opt)
1013{
1014 Call *call = Call::lookup(call_id);
1015 if (!call) {
1016 return;
1017 }
1018
1019 OnCallReplaceRequestParam prm;
1020 prm.rdata.fromPj(*rdata);
1021 prm.statusCode = (pjsip_status_code)*st_code;
1022 prm.reason = pj2Str(*st_text);
1023 prm.opt.fromPj(*opt);
1024
1025 call->onCallReplaceRequest(prm);
1026
1027 *st_code = prm.statusCode;
1028 *st_text = str2Pj(prm.reason);
1029 *opt = prm.opt.toPj();
1030}
1031
1032void Endpoint::on_call_replaced(pjsua_call_id old_call_id,
1033 pjsua_call_id new_call_id)
1034{
1035 Call *call = Call::lookup(old_call_id);
1036 if (!call) {
1037 return;
1038 }
1039
1040 OnCallReplacedParam prm;
1041 prm.newCallId = new_call_id;
1042
1043 call->onCallReplaced(prm);
1044}
1045
1046void Endpoint::on_call_rx_offer(pjsua_call_id call_id,
1047 const pjmedia_sdp_session *offer,
1048 void *reserved,
1049 pjsip_status_code *code,
1050 pjsua_call_setting *opt)
1051{
1052 PJ_UNUSED_ARG(reserved);
1053
1054 Call *call = Call::lookup(call_id);
1055 if (!call) {
1056 return;
1057 }
1058
1059 OnCallRxOfferParam prm;
1060 prm.offer.fromPj(*offer);
1061 prm.statusCode = *code;
1062 prm.opt.fromPj(*opt);
1063
1064 call->onCallRxOffer(prm);
1065
1066 *code = prm.statusCode;
1067 *opt = prm.opt.toPj();
1068}
1069
1070pjsip_redirect_op Endpoint::on_call_redirected(pjsua_call_id call_id,
1071 const pjsip_uri *target,
1072 const pjsip_event *e)
1073{
1074 Call *call = Call::lookup(call_id);
1075 if (!call) {
1076 return PJSIP_REDIRECT_STOP;
1077 }
1078
1079 OnCallRedirectedParam prm;
1080 char uristr[PJSIP_MAX_URL_SIZE];
1081 int len = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, target, uristr,
1082 sizeof(uristr));
1083 if (len < 1) {
1084 pj_ansi_strcpy(uristr, "--URI too long--");
1085 }
1086 prm.targetUri = string(uristr);
1087 if (e)
1088 prm.e.fromPj(*e);
1089 else
1090 prm.e.type = PJSIP_EVENT_UNKNOWN;
1091
1092 return call->onCallRedirected(prm);
1093}
1094
1095
1096struct PendingOnMediaTransportCallback : public PendingJob
1097{
1098 int call_id;
1099 OnCallMediaTransportStateParam prm;
1100
1101 virtual void execute(bool is_pending)
1102 {
1103 PJ_UNUSED_ARG(is_pending);
1104
1105 Call *call = Call::lookup(call_id);
1106 if (!call)
1107 return;
1108
1109 call->onCallMediaTransportState(prm);
1110 }
1111};
1112
1113pj_status_t
1114Endpoint::on_call_media_transport_state(pjsua_call_id call_id,
1115 const pjsua_med_tp_state_info *info)
1116{
1117 Call *call = Call::lookup(call_id);
1118 if (!call) {
1119 return PJ_SUCCESS;
1120 }
1121
1122 PendingOnMediaTransportCallback *job = new PendingOnMediaTransportCallback;
1123
1124 job->call_id = call_id;
1125 job->prm.medIdx = info->med_idx;
1126 job->prm.state = info->state;
1127 job->prm.status = info->status;
1128 job->prm.sipErrorCode = info->sip_err_code;
1129
1130 Endpoint::instance().utilAddPendingJob(job);
1131
1132 return PJ_SUCCESS;
1133}
1134
1135struct PendingOnMediaEventCallback : public PendingJob
1136{
1137 int call_id;
1138 OnCallMediaEventParam prm;
1139
1140 virtual void execute(bool is_pending)
1141 {
1142 Call *call = Call::lookup(call_id);
1143 if (!call)
1144 return;
1145
1146 if (is_pending) {
1147 /* Can't do this anymore, pointer is invalid */
1148 prm.ev.pjMediaEvent = NULL;
1149 }
1150
1151 call->onCallMediaEvent(prm);
1152 }
1153};
1154
1155void Endpoint::on_call_media_event(pjsua_call_id call_id,
1156 unsigned med_idx,
1157 pjmedia_event *event)
1158{
1159 Call *call = Call::lookup(call_id);
1160 if (!call) {
1161 return;
1162 }
1163
1164 PendingOnMediaEventCallback *job = new PendingOnMediaEventCallback;
1165
1166 job->call_id = call_id;
1167 job->prm.medIdx = med_idx;
1168 job->prm.ev.fromPj(*event);
1169
1170 Endpoint::instance().utilAddPendingJob(job);
1171}
1172
1173pjmedia_transport*
1174Endpoint::on_create_media_transport(pjsua_call_id call_id,
1175 unsigned media_idx,
1176 pjmedia_transport *base_tp,
1177 unsigned flags)
1178{
1179 Call *call = Call::lookup(call_id);
1180 if (!call) {
1181 return base_tp;
1182 }
1183
1184 OnCreateMediaTransportParam prm;
1185 prm.mediaIdx = media_idx;
1186 prm.mediaTp = base_tp;
1187 prm.flags = flags;
1188
1189 call->onCreateMediaTransport(prm);
1190
1191 return (pjmedia_transport *)prm.mediaTp;
1192}
1193
1194///////////////////////////////////////////////////////////////////////////////
1195/*
1196 * Endpoint library operations
1197 */
1198Version Endpoint::libVersion() const
1199{
1200 Version ver;
1201 ver.major = PJ_VERSION_NUM_MAJOR;
1202 ver.minor = PJ_VERSION_NUM_MINOR;
1203 ver.rev = PJ_VERSION_NUM_REV;
1204 ver.suffix = PJ_VERSION_NUM_EXTRA;
1205 ver.full = pj_get_version();
1206 ver.numeric = PJ_VERSION_NUM;
1207 return ver;
1208}
1209
1210void Endpoint::libCreate() throw(Error)
1211{
1212 PJSUA2_CHECK_EXPR( pjsua_create() );
1213 mainThread = pj_thread_this();
1214}
1215
1216pjsua_state Endpoint::libGetState() const
1217{
1218 return pjsua_get_state();
1219}
1220
1221void Endpoint::libInit(const EpConfig &prmEpConfig) throw(Error)
1222{
1223 pjsua_config ua_cfg;
1224 pjsua_logging_config log_cfg;
1225 pjsua_media_config med_cfg;
1226
1227 ua_cfg = prmEpConfig.uaConfig.toPj();
1228 log_cfg = prmEpConfig.logConfig.toPj();
1229 med_cfg = prmEpConfig.medConfig.toPj();
1230
1231 /* Setup log callback */
1232 if (prmEpConfig.logConfig.writer) {
1233 this->writer = prmEpConfig.logConfig.writer;
1234 log_cfg.cb = &Endpoint::logFunc;
1235 }
1236 mainThreadOnly = prmEpConfig.uaConfig.mainThreadOnly;
1237
1238 /* Setup UA callbacks */
1239 pj_bzero(&ua_cfg.cb, sizeof(ua_cfg.cb));
1240 ua_cfg.cb.on_nat_detect = &Endpoint::on_nat_detect;
1241 ua_cfg.cb.on_transport_state = &Endpoint::on_transport_state;
1242
1243 ua_cfg.cb.on_incoming_call = &Endpoint::on_incoming_call;
1244 ua_cfg.cb.on_reg_started = &Endpoint::on_reg_started;
1245 ua_cfg.cb.on_reg_state2 = &Endpoint::on_reg_state2;
1246 ua_cfg.cb.on_incoming_subscribe = &Endpoint::on_incoming_subscribe;
1247 ua_cfg.cb.on_pager2 = &Endpoint::on_pager2;
1248 ua_cfg.cb.on_pager_status2 = &Endpoint::on_pager_status2;
1249 ua_cfg.cb.on_typing2 = &Endpoint::on_typing2;
1250 ua_cfg.cb.on_mwi_info = &Endpoint::on_mwi_info;
1251 ua_cfg.cb.on_buddy_state = &Endpoint::on_buddy_state;
1252
1253 /* Call callbacks */
1254 ua_cfg.cb.on_call_state = &Endpoint::on_call_state;
1255 ua_cfg.cb.on_call_tsx_state = &Endpoint::on_call_tsx_state;
1256 ua_cfg.cb.on_call_media_state = &Endpoint::on_call_media_state;
1257 ua_cfg.cb.on_call_sdp_created = &Endpoint::on_call_sdp_created;
1258 ua_cfg.cb.on_stream_created = &Endpoint::on_stream_created;
1259 ua_cfg.cb.on_stream_destroyed = &Endpoint::on_stream_destroyed;
1260 ua_cfg.cb.on_dtmf_digit = &Endpoint::on_dtmf_digit;
1261 ua_cfg.cb.on_call_transfer_request2 = &Endpoint::on_call_transfer_request2;
1262 ua_cfg.cb.on_call_transfer_status = &Endpoint::on_call_transfer_status;
1263 ua_cfg.cb.on_call_replace_request2 = &Endpoint::on_call_replace_request2;
1264 ua_cfg.cb.on_call_replaced = &Endpoint::on_call_replaced;
1265 ua_cfg.cb.on_call_rx_offer = &Endpoint::on_call_rx_offer;
1266 ua_cfg.cb.on_call_redirected = &Endpoint::on_call_redirected;
1267 ua_cfg.cb.on_call_media_transport_state =
1268 &Endpoint::on_call_media_transport_state;
1269 ua_cfg.cb.on_call_media_event = &Endpoint::on_call_media_event;
1270 ua_cfg.cb.on_create_media_transport = &Endpoint::on_create_media_transport;
1271
1272 /* Init! */
1273 PJSUA2_CHECK_EXPR( pjsua_init(&ua_cfg, &log_cfg, &med_cfg) );
1274}
1275
1276void Endpoint::libStart() throw(Error)
1277{
1278 PJSUA2_CHECK_EXPR(pjsua_start());
1279}
1280
1281void Endpoint::libRegisterWorkerThread(const string &name) throw(Error)
1282{
1283 PJSUA2_CHECK_EXPR(pjsua_register_worker_thread(name.c_str()));
1284}
1285
1286void Endpoint::libStopWorkerThreads()
1287{
1288 pjsua_stop_worker_threads();
1289}
1290
1291int Endpoint::libHandleEvents(unsigned msec_timeout)
1292{
1293 performPendingJobs();
1294 return pjsua_handle_events(msec_timeout);
1295}
1296
1297void Endpoint::libDestroy(unsigned flags) throw(Error)
1298{
1299 pj_status_t status;
1300
1301 status = pjsua_destroy2(flags);
1302
1303 delete this->writer;
1304 this->writer = NULL;
1305
1306 if (pj_log_get_log_func() == &Endpoint::logFunc) {
1307 pj_log_set_log_func(NULL);
1308 }
1309
1310 PJSUA2_CHECK_RAISE_ERROR(status);
1311}
1312
1313///////////////////////////////////////////////////////////////////////////////
1314/*
1315 * Endpoint Utilities
1316 */
1317string Endpoint::utilStrError(pj_status_t prmErr)
1318{
1319 char errmsg[PJ_ERR_MSG_SIZE];
1320 pj_strerror(prmErr, errmsg, sizeof(errmsg));
1321 return errmsg;
1322}
1323
1324static void ept_log_write(int level, const char *sender,
1325 const char *format, ...)
1326{
1327 va_list arg;
1328 va_start(arg, format);
1329 pj_log(sender, level, format, arg );
1330 va_end(arg);
1331}
1332
1333void Endpoint::utilLogWrite(int prmLevel,
1334 const string &prmSender,
1335 const string &prmMsg)
1336{
1337 ept_log_write(prmLevel, prmSender.c_str(), "%s", prmMsg.c_str());
1338}
1339
1340pj_status_t Endpoint::utilVerifySipUri(const string &prmUri)
1341{
1342 return pjsua_verify_sip_url(prmUri.c_str());
1343}
1344
1345pj_status_t Endpoint::utilVerifyUri(const string &prmUri)
1346{
1347 return pjsua_verify_url(prmUri.c_str());
1348}
1349
1350Token Endpoint::utilTimerSchedule(unsigned prmMsecDelay,
1351 Token prmUserData) throw (Error)
1352{
1353 UserTimer *ut;
1354 pj_time_val delay;
1355 pj_status_t status;
1356
1357 ut = new UserTimer;
1358 ut->signature = TIMER_SIGNATURE;
1359 ut->prm.msecDelay = prmMsecDelay;
1360 ut->prm.userData = prmUserData;
1361 pj_timer_entry_init(&ut->entry, 1, ut, &Endpoint::on_timer);
1362
1363 delay.sec = 0;
1364 delay.msec = prmMsecDelay;
1365 pj_time_val_normalize(&delay);
1366
1367 status = pjsua_schedule_timer(&ut->entry, &delay);
1368 if (status != PJ_SUCCESS) {
1369 delete ut;
1370 PJSUA2_CHECK_RAISE_ERROR(status);
1371 }
1372
1373 return (Token)ut;
1374}
1375
1376void Endpoint::utilTimerCancel(Token prmTimerToken)
1377{
1378 UserTimer *ut = (UserTimer*)(void*)prmTimerToken;
1379
1380 if (ut->signature != TIMER_SIGNATURE) {
1381 PJ_LOG(1,(THIS_FILE,
1382 "Invalid timer token in Endpoint::utilTimerCancel()"));
1383 return;
1384 }
1385
1386 ut->entry.id = 0;
1387 ut->signature = 0xFFFFFFFE;
1388 pjsua_cancel_timer(&ut->entry);
1389
1390 delete ut;
1391}
1392
1393IntVector Endpoint::utilSslGetAvailableCiphers() throw (Error)
1394{
1395#if PJ_HAS_SSL_SOCK
1396 pj_ssl_cipher ciphers[64];
1397 unsigned count = PJ_ARRAY_SIZE(ciphers);
1398
1399 PJSUA2_CHECK_EXPR( pj_ssl_cipher_get_availables(ciphers, &count) );
1400
1401 return IntVector(ciphers, ciphers + count);
1402#else
1403 return IntVector();
1404#endif
1405}
1406
1407///////////////////////////////////////////////////////////////////////////////
1408/*
1409 * Endpoint NAT operations
1410 */
1411void Endpoint::natDetectType(void) throw(Error)
1412{
1413 PJSUA2_CHECK_EXPR( pjsua_detect_nat_type() );
1414}
1415
1416pj_stun_nat_type Endpoint::natGetType() throw(Error)
1417{
1418 pj_stun_nat_type type;
1419
1420 PJSUA2_CHECK_EXPR( pjsua_get_nat_type(&type) );
1421
1422 return type;
1423}
1424
1425void Endpoint::natCheckStunServers(const StringVector &servers,
1426 bool wait,
1427 Token token) throw(Error)
1428{
1429 pj_str_t srv[MAX_STUN_SERVERS];
1430 unsigned i, count = 0;
1431
1432 for (i=0; i<servers.size() && i<MAX_STUN_SERVERS; ++i) {
1433 srv[count].ptr = (char*)servers[i].c_str();
1434 srv[count].slen = servers[i].size();
1435 ++count;
1436 }
1437
1438 PJSUA2_CHECK_EXPR(pjsua_resolve_stun_servers(count, srv, wait, token,
1439 &Endpoint::stun_resolve_cb) );
1440}
1441
1442void Endpoint::natCancelCheckStunServers(Token token,
1443 bool notify_cb) throw(Error)
1444{
1445 PJSUA2_CHECK_EXPR( pjsua_cancel_stun_resolution(token, notify_cb) );
1446}
1447
1448///////////////////////////////////////////////////////////////////////////////
1449/*
1450 * Transport API
1451 */
1452TransportId Endpoint::transportCreate(pjsip_transport_type_e type,
1453 const TransportConfig &cfg) throw(Error)
1454{
1455 pjsua_transport_config tcfg;
1456 pjsua_transport_id tid;
1457
1458 tcfg = cfg.toPj();
1459 PJSUA2_CHECK_EXPR( pjsua_transport_create(type,
1460 &tcfg, &tid) );
1461
1462 return tid;
1463}
1464
1465IntVector Endpoint::transportEnum() throw(Error)
1466{
1467 pjsua_transport_id tids[32];
1468 unsigned count = PJ_ARRAY_SIZE(tids);
1469
1470 PJSUA2_CHECK_EXPR( pjsua_enum_transports(tids, &count) );
1471
1472 return IntVector(tids, tids+count);
1473}
1474
1475TransportInfo Endpoint::transportGetInfo(TransportId id) throw(Error)
1476{
1477 pjsua_transport_info pj_tinfo;
1478 TransportInfo tinfo;
1479
1480 PJSUA2_CHECK_EXPR( pjsua_transport_get_info(id, &pj_tinfo) );
1481 tinfo.fromPj(pj_tinfo);
1482
1483 return tinfo;
1484}
1485
1486void Endpoint::transportSetEnable(TransportId id, bool enabled) throw(Error)
1487{
1488 PJSUA2_CHECK_EXPR( pjsua_transport_set_enable(id, enabled) );
1489}
1490
1491void Endpoint::transportClose(TransportId id) throw(Error)
1492{
1493 PJSUA2_CHECK_EXPR( pjsua_transport_close(id, PJ_FALSE) );
1494}
1495
1496///////////////////////////////////////////////////////////////////////////////
1497/*
1498 * Call operations
1499 */
1500
1501void Endpoint::hangupAllCalls(void)
1502{
1503 pjsua_call_hangup_all();
1504}
1505
1506///////////////////////////////////////////////////////////////////////////////
1507/*
1508 * Media API
1509 */
1510unsigned Endpoint::mediaMaxPorts() const
1511{
1512 return pjsua_conf_get_max_ports();
1513}
1514
1515unsigned Endpoint::mediaActivePorts() const
1516{
1517 return pjsua_conf_get_active_ports();
1518}
1519
1520const AudioMediaVector &Endpoint::mediaEnumPorts() const throw(Error)
1521{
1522 return mediaList;
1523}
1524
1525void Endpoint::mediaAdd(AudioMedia &media)
1526{
1527 if (mediaExists(media))
1528 return;
1529
1530 mediaList.push_back(&media);
1531}
1532
1533void Endpoint::mediaRemove(AudioMedia &media)
1534{
1535 AudioMediaVector::iterator it = std::find(mediaList.begin(),
1536 mediaList.end(),
1537 &media);
1538
1539 if (it != mediaList.end())
1540 mediaList.erase(it);
1541
1542}
1543
1544bool Endpoint::mediaExists(const AudioMedia &media) const
1545{
1546 AudioMediaVector::const_iterator it = std::find(mediaList.begin(),
1547 mediaList.end(),
1548 &media);
1549
1550 return (it != mediaList.end());
1551}
1552
1553AudDevManager &Endpoint::audDevManager()
1554{
1555 return audioDevMgr;
1556}
1557
1558/*
1559 * Codec operations.
1560 */
1561const CodecInfoVector &Endpoint::codecEnum() throw(Error)
1562{
1563 pjsua_codec_info pj_codec[MAX_CODEC_NUM];
1564 unsigned count = 0;
1565
1566 PJSUA2_CHECK_EXPR( pjsua_enum_codecs(pj_codec, &count) );
1567
1568 pj_enter_critical_section();
1569 clearCodecInfoList();
1570 for (unsigned i=0;(i<count && i<MAX_CODEC_NUM);++i) {
1571 CodecInfo *codec_info = new CodecInfo;
1572
1573 codec_info->fromPj(pj_codec[i]);
1574 codecInfoList.push_back(codec_info);
1575 }
1576 pj_leave_critical_section();
1577 return codecInfoList;
1578}
1579
1580void Endpoint::codecSetPriority(const string &codec_id,
1581 pj_uint8_t priority) throw(Error)
1582{
1583 pj_str_t codec_str = str2Pj(codec_id);
1584 PJSUA2_CHECK_EXPR( pjsua_codec_set_priority(&codec_str, priority) );
1585}
1586
1587CodecParam Endpoint::codecGetParam(const string &codec_id) const throw(Error)
1588{
1589 pjmedia_codec_param *pj_param = NULL;
1590 pj_str_t codec_str = str2Pj(codec_id);
1591
1592 PJSUA2_CHECK_EXPR( pjsua_codec_get_param(&codec_str, pj_param) );
1593
1594 return pj_param;
1595}
1596
1597void Endpoint::codecSetParam(const string &codec_id,
1598 const CodecParam param) throw(Error)
1599{
1600 pj_str_t codec_str = str2Pj(codec_id);
1601 pjmedia_codec_param *pj_param = (pjmedia_codec_param*)param;
1602
1603 PJSUA2_CHECK_EXPR( pjsua_codec_set_param(&codec_str, pj_param) );
1604}
1605
1606void Endpoint::clearCodecInfoList()
1607{
1608 for (unsigned i=0;i<codecInfoList.size();++i) {
1609 delete codecInfoList[i];
1610 }
1611 codecInfoList.clear();
1612}