1
/* $Id: vidgui.cpp 4060 2012-04-17 09:55:30Z ming $ */
3
* Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
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.
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.
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
23
# define SDL_MAIN_HANDLED
28
#include <QMessageBox>
30
#define LOG_FILE "vidgui.log"
31
#define THIS_FILE "vidgui.cpp"
33
///////////////////////////////////////////////////////////////////////////
39
// These configure SIP registration
41
#define USE_REGISTRATION 0
42
#define SIP_DOMAIN "pjsip.org"
43
#define SIP_USERNAME "vidgui"
44
#define SIP_PASSWORD "secret"
49
// NAT helper settings
53
#define STUN_SRV "stun.pjsip.org"
58
#define DEFAULT_CAP_DEV PJMEDIA_VID_DEFAULT_CAPTURE_DEV
59
//#define DEFAULT_CAP_DEV 1
60
#define DEFAULT_REND_DEV PJMEDIA_VID_DEFAULT_RENDER_DEV
65
///////////////////////////////////////////////////////////////////////////
68
MainWin *MainWin::theInstance_;
70
MainWin::MainWin(QWidget *parent)
71
: QWidget(parent), accountId_(-1), currentCall_(-1),
72
preview_on(false), video_(NULL), video_prev_(NULL)
77
emit signalCallReleased();
86
MainWin *MainWin::instance()
91
void MainWin::initLayout()
93
//statusBar_ = new QStatusBar(this);
96
QHBoxLayout *hbox_main = new QHBoxLayout;
97
//QVBoxLayout *vbox_left = new QVBoxLayout;
98
vbox_left = new QVBoxLayout;
99
QVBoxLayout *vbox_right = new QVBoxLayout;
100
hbox_main->addLayout(vbox_left);
101
hbox_main->addLayout(vbox_right);
104
QHBoxLayout *hbox_url = new QHBoxLayout;
105
hbox_url->addWidget(new QLabel(tr("Url:")));
106
hbox_url->addWidget(url_=new QLineEdit(tr("sip:")), 1);
107
vbox_left->addLayout(hbox_url);
110
vbox_right->addWidget((localUri_ = new QLabel));
111
vbox_right->addWidget((vidEnabled_ = new QCheckBox(tr("Enable &video"))));
112
vbox_right->addWidget((previewButton_=new QPushButton(tr("Start &Preview"))));
113
vbox_right->addWidget((callButton_=new QPushButton(tr("Call"))));
114
vbox_right->addWidget((hangupButton_=new QPushButton(tr("Hangup"))));
115
vbox_right->addWidget((quitButton_=new QPushButton(tr("Quit"))));
117
#if PJMEDIA_HAS_VIDEO
118
vidEnabled_->setCheckState(Qt::Checked);
120
vidEnabled_->setCheckState(Qt::Unchecked);
121
vidEnabled_->setEnabled(false);
125
QVBoxLayout *vbox_outest = new QVBoxLayout;
126
vbox_outest->addLayout(hbox_main);
127
vbox_outest->addWidget((statusBar_ = new QLabel));
129
setLayout(vbox_outest);
131
connect(previewButton_, SIGNAL(clicked()), this, SLOT(preview()));
132
connect(callButton_, SIGNAL(clicked()), this, SLOT(call()));
133
connect(hangupButton_, SIGNAL(clicked()), this, SLOT(hangup()));
134
connect(quitButton_, SIGNAL(clicked()), this, SLOT(quit()));
135
//connect(this, SIGNAL(close()), this, SLOT(quit()));
136
connect(vidEnabled_, SIGNAL(stateChanged(int)), this, SLOT(onVidEnabledChanged(int)));
138
// UI updates must be done in the UI thread!
139
connect(this, SIGNAL(signalNewCall(int, bool)),
140
this, SLOT(onNewCall(int, bool)));
141
connect(this, SIGNAL(signalCallReleased()),
142
this, SLOT(onCallReleased()));
143
connect(this, SIGNAL(signalInitVideoWindow()),
144
this, SLOT(initVideoWindow()));
145
connect(this, SIGNAL(signalShowStatus(const QString&)),
146
this, SLOT(doShowStatus(const QString&)));
160
void MainWin::showStatus(const char *msg)
162
PJ_LOG(3,(THIS_FILE, "%s", msg));
164
QString msg_ = QString::fromUtf8(msg);
165
emit signalShowStatus(msg_);
168
void MainWin::doShowStatus(const QString& msg)
170
//statusBar_->showMessage(msg);
171
statusBar_->setText(msg);
174
void MainWin::showError(const char *title, pj_status_t status)
176
char errmsg[PJ_ERR_MSG_SIZE];
179
pj_strerror(status, errmsg, sizeof(errmsg));
180
snprintf(errline, sizeof(errline), "%s error: %s", title, errmsg);
184
void MainWin::onVidEnabledChanged(int state)
186
pjsua_call_setting call_setting;
188
if (currentCall_ == -1)
191
pjsua_call_setting_default(&call_setting);
192
call_setting.vid_cnt = (state == Qt::Checked);
194
pjsua_call_reinvite2(currentCall_, &call_setting, NULL);
197
void MainWin::onNewCall(int cid, bool incoming)
201
pj_assert(currentCall_ == -1);
204
pjsua_call_get_info(cid, &ci);
205
url_->setText(ci.remote_info.ptr);
206
url_->setEnabled(false);
207
hangupButton_->setEnabled(true);
210
callButton_->setText(tr("Answer"));
211
callButton_->setEnabled(true);
213
callButton_->setEnabled(false);
216
//video_->setText(ci.remote_contact.ptr);
217
//video_->setWindowTitle(ci.remote_contact.ptr);
220
void MainWin::onCallReleased()
222
url_->setEnabled(true);
223
callButton_->setEnabled(true);
224
callButton_->setText(tr("Call"));
225
hangupButton_->setEnabled(false);
232
void MainWin::preview()
238
pjsua_vid_preview_stop(DEFAULT_CAP_DEV);
240
showStatus("Preview stopped");
241
previewButton_->setText(tr("Start &Preview"));
243
pjsua_vid_win_id wid;
244
pjsua_vid_win_info wi;
245
pjsua_vid_preview_param pre_param;
248
pjsua_vid_preview_param_default(&pre_param);
249
pre_param.rend_id = DEFAULT_REND_DEV;
250
pre_param.show = PJ_FALSE;
252
status = pjsua_vid_preview_start(DEFAULT_CAP_DEV, &pre_param);
253
if (status != PJ_SUCCESS) {
254
char errmsg[PJ_ERR_MSG_SIZE];
255
pj_strerror(status, errmsg, sizeof(errmsg));
256
QMessageBox::critical(0, "Error creating preview", errmsg);
259
wid = pjsua_vid_preview_get_win(DEFAULT_CAP_DEV);
260
pjsua_vid_win_get_info(wid, &wi);
262
video_prev_ = new VidWin(&wi.hwnd);
263
video_prev_->putIntoLayout(vbox_left);
264
//Using this will cause SDL window to display blank
265
//screen sometimes, probably because it's using different
267
//status = pjsua_vid_win_set_show(wid, PJ_TRUE);
268
//This is handled by VidWin now
269
//video_prev_->show_sdl();
270
showStatus("Preview started");
272
previewButton_->setText(tr("Stop &Preview"));
274
preview_on = !preview_on;
280
if (callButton_->text() == "Answer") {
281
pjsua_call_setting call_setting;
283
pj_assert(currentCall_ != -1);
285
pjsua_call_setting_default(&call_setting);
286
call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked);
288
pjsua_call_answer2(currentCall_, &call_setting, 200, NULL, NULL);
289
callButton_->setEnabled(false);
292
QString dst = url_->text();
294
pjsua_call_setting call_setting;
296
pj_ansi_strncpy(uri, dst.toAscii().data(), sizeof(uri));
297
pj_str_t uri2 = pj_str((char*)uri);
299
pj_assert(currentCall_ == -1);
301
pjsua_call_setting_default(&call_setting);
302
call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked);
304
status = pjsua_call_make_call(accountId_, &uri2, &call_setting,
305
NULL, NULL, ¤tCall_);
306
if (status != PJ_SUCCESS) {
307
showError("make call", status);
313
void MainWin::hangup()
315
pj_assert(currentCall_ != -1);
316
//pjsua_call_hangup(currentCall_, PJSIP_SC_BUSY_HERE, NULL, NULL);
317
pjsua_call_hangup_all();
318
emit signalCallReleased();
322
void MainWin::initVideoWindow()
327
if (currentCall_ == -1)
333
pjsua_call_get_info(currentCall_, &ci);
334
for (i = 0; i < ci.media_cnt; ++i) {
335
if ((ci.media[i].type == PJMEDIA_TYPE_VIDEO) &&
336
(ci.media[i].dir & PJMEDIA_DIR_DECODING))
338
pjsua_vid_win_info wi;
339
pjsua_vid_win_get_info(ci.media[i].stream.vid.win_in, &wi);
341
video_= new VidWin(&wi.hwnd);
342
video_->putIntoLayout(vbox_left);
349
void MainWin::on_reg_state(pjsua_acc_id acc_id)
353
pjsua_acc_get_info(acc_id, &info);
358
if (!info.has_registration) {
359
pj_ansi_snprintf(reg_status, sizeof(reg_status), "%.*s",
360
(int)info.status_text.slen,
361
info.status_text.ptr);
364
pj_ansi_snprintf(reg_status, sizeof(reg_status),
365
"%d/%.*s (expires=%d)",
367
(int)info.status_text.slen,
368
info.status_text.ptr,
373
snprintf(status, sizeof(status),
375
(int)info.acc_uri.slen, info.acc_uri.ptr,
380
void MainWin::on_call_state(pjsua_call_id call_id, pjsip_event *e)
386
pjsua_call_get_info(call_id, &ci);
388
if (currentCall_ == -1 && ci.state < PJSIP_INV_STATE_DISCONNECTED &&
389
ci.role == PJSIP_ROLE_UAC)
391
emit signalNewCall(call_id, false);
395
if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
396
snprintf(status, sizeof(status), "Call is %s (%s)",
398
ci.last_status_text.ptr);
400
emit signalCallReleased();
402
snprintf(status, sizeof(status), "Call is %s", pjsip_inv_state_name(ci.state));
407
void MainWin::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
408
pjsip_rx_data *rdata)
410
PJ_UNUSED_ARG(acc_id);
411
PJ_UNUSED_ARG(rdata);
413
if (currentCall_ != -1) {
414
pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, NULL, NULL);
418
emit signalNewCall(call_id, true);
423
pjsua_call_get_info(call_id, &ci);
424
snprintf(status, sizeof(status), "Incoming call from %.*s",
425
(int)ci.remote_info.slen, ci.remote_info.ptr);
429
void MainWin::on_call_media_state(pjsua_call_id call_id)
433
pjsua_call_get_info(call_id, &ci);
435
for (unsigned i=0; i<ci.media_cnt; ++i) {
436
if (ci.media[i].type == PJMEDIA_TYPE_AUDIO) {
437
switch (ci.media[i].status) {
438
case PJSUA_CALL_MEDIA_ACTIVE:
439
pjsua_conf_connect(ci.media[i].stream.aud.conf_slot, 0);
440
pjsua_conf_connect(0, ci.media[i].stream.aud.conf_slot);
445
} else if (ci.media[i].type == PJMEDIA_TYPE_VIDEO) {
446
emit signalInitVideoWindow();
454
static void on_reg_state(pjsua_acc_id acc_id)
456
MainWin::instance()->on_reg_state(acc_id);
459
static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
461
MainWin::instance()->on_call_state(call_id, e);
464
static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
465
pjsip_rx_data *rdata)
467
MainWin::instance()->on_incoming_call(acc_id, call_id, rdata);
470
static void on_call_media_state(pjsua_call_id call_id)
472
MainWin::instance()->on_call_media_state(call_id);
478
bool MainWin::initStack()
482
//showStatus("Creating stack..");
483
status = pjsua_create();
484
if (status != PJ_SUCCESS) {
485
showError("pjsua_create", status);
489
showStatus("Initializing stack..");
492
pjsua_config_default(&ua_cfg);
493
pjsua_callback ua_cb;
494
pj_bzero(&ua_cb, sizeof(ua_cb));
495
ua_cfg.cb.on_reg_state = &::on_reg_state;
496
ua_cfg.cb.on_call_state = &::on_call_state;
497
ua_cfg.cb.on_incoming_call = &::on_incoming_call;
498
ua_cfg.cb.on_call_media_state = &::on_call_media_state;
500
ua_cfg.stun_srv_cnt = 1;
501
ua_cfg.stun_srv[0] = pj_str((char*)STUN_SRV);
504
pjsua_logging_config log_cfg;
505
pjsua_logging_config_default(&log_cfg);
506
log_cfg.log_filename = pj_str((char*)LOG_FILE);
508
pjsua_media_config med_cfg;
509
pjsua_media_config_default(&med_cfg);
510
med_cfg.enable_ice = USE_ICE;
512
status = pjsua_init(&ua_cfg, &log_cfg, &med_cfg);
513
if (status != PJ_SUCCESS) {
514
showError("pjsua_init", status);
519
// Create UDP and TCP transports
521
pjsua_transport_config udp_cfg;
522
pjsua_transport_id udp_id;
523
pjsua_transport_config_default(&udp_cfg);
524
udp_cfg.port = SIP_PORT;
526
status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
528
if (status != PJ_SUCCESS) {
529
showError("UDP transport creation", status);
533
pjsua_transport_info udp_info;
534
status = pjsua_transport_get_info(udp_id, &udp_info);
535
if (status != PJ_SUCCESS) {
536
showError("UDP transport info", status);
541
pjsua_transport_config tcp_cfg;
542
pjsua_transport_config_default(&tcp_cfg);
545
status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
547
if (status != PJ_SUCCESS) {
548
showError("TCP transport creation", status);
556
pjsua_acc_config acc_cfg;
557
pjsua_acc_config_default(&acc_cfg);
559
acc_cfg.id = pj_str( (char*)"<sip:" SIP_USERNAME "@" SIP_DOMAIN ">");
560
acc_cfg.reg_uri = pj_str((char*) ("sip:" SIP_DOMAIN));
561
acc_cfg.cred_count = 1;
562
acc_cfg.cred_info[0].realm = pj_str((char*)"*");
563
acc_cfg.cred_info[0].scheme = pj_str((char*)"digest");
564
acc_cfg.cred_info[0].username = pj_str((char*)SIP_USERNAME);
565
acc_cfg.cred_info[0].data = pj_str((char*)SIP_PASSWORD);
568
acc_cfg.proxy[acc_cfg.proxy_cnt++] = pj_str((char*) "<sip:" SIP_DOMAIN ";transport=tcp>");
573
snprintf(sip_id, sizeof(sip_id),
574
"sip:%s@%.*s:%u", SIP_USERNAME,
575
(int)udp_info.local_name.host.slen,
576
udp_info.local_name.host.ptr,
577
udp_info.local_name.port);
578
acc_cfg.id = pj_str(sip_id);
581
acc_cfg.vid_cap_dev = DEFAULT_CAP_DEV;
582
acc_cfg.vid_rend_dev = DEFAULT_REND_DEV;
583
acc_cfg.vid_in_auto_show = PJ_TRUE;
584
acc_cfg.vid_out_auto_transmit = PJ_TRUE;
586
status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &accountId_);
587
if (status != PJ_SUCCESS) {
588
showError("Account creation", status);
592
localUri_->setText(acc_cfg.id.ptr);
597
showStatus("Starting stack..");
598
status = pjsua_start();
599
if (status != PJ_SUCCESS) {
600
showError("pjsua_start", status);
614
* A simple registrar, invoked by default_mod_on_rx_request()
616
static void simple_registrar(pjsip_rx_data *rdata)
618
pjsip_tx_data *tdata;
619
const pjsip_expires_hdr *exp;
622
pjsip_generic_string_hdr *srv;
625
status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
626
rdata, 200, NULL, &tdata);
627
if (status != PJ_SUCCESS)
630
exp = (pjsip_expires_hdr*)
631
pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
633
h = rdata->msg_info.msg->hdr.next;
634
while (h != &rdata->msg_info.msg->hdr) {
635
if (h->type == PJSIP_H_CONTACT) {
636
const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h;
647
pjsip_contact_hdr *nc = (pjsip_contact_hdr*)
648
pjsip_hdr_clone(tdata->pool, h);
650
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc);
657
srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL);
658
srv->name = pj_str((char*)"Server");
659
srv->hvalue = pj_str((char*)"pjsua simple registrar");
660
pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv);
662
pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(),
663
rdata, tdata, NULL, NULL);
666
/* Notification on incoming request */
667
static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
669
/* Simple registrar */
670
if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
671
&pjsip_register_method) == 0)
673
simple_registrar(rdata);
680
/* The module instance. */
681
static pjsip_module mod_default_handler =
683
NULL, NULL, /* prev, next. */
684
{ (char*)"mod-default-handler", 19 }, /* Name. */
686
PJSIP_MOD_PRIORITY_APPLICATION+99, /* Priority */
691
&default_mod_on_rx_request, /* on_rx_request() */
692
NULL, /* on_rx_response() */
693
NULL, /* on_tx_request. */
694
NULL, /* on_tx_response() */
695
NULL, /* on_tsx_state() */
699
int main(int argc, char *argv[])
701
/* At least on Linux, we have to initialize SDL video subsystem prior to
702
* creating/initializing QApplication, otherwise we'll segfault miserably
703
* in SDL_CreateWindow(). Here's a stack trace if you're interested:
705
Thread [7] (Suspended: Signal 'SIGSEGV' received. Description: Segmentation fault.)
708
11 X11_CreateWindow()
709
10 SDL_CreateWindow()
712
if ( SDL_InitSubSystem(SDL_INIT_VIDEO) < 0 ) {
713
printf("Unable to init SDL: %s\n", SDL_GetError());
717
QApplication app(argc, argv);
722
if (!win.initStack()) {
727
/* We want to be registrar too! */
728
if (pjsua_get_pjsip_endpt()) {
729
pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
730
&mod_default_handler);