summaryrefslogtreecommitdiff
path: root/pjsip-apps/src/vidgui/vidgui.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'pjsip-apps/src/vidgui/vidgui.cpp')
-rw-r--r--pjsip-apps/src/vidgui/vidgui.cpp735
1 files changed, 735 insertions, 0 deletions
diff --git a/pjsip-apps/src/vidgui/vidgui.cpp b/pjsip-apps/src/vidgui/vidgui.cpp
new file mode 100644
index 0000000..9e1d540
--- /dev/null
+++ b/pjsip-apps/src/vidgui/vidgui.cpp
@@ -0,0 +1,735 @@
+/* $Id: vidgui.cpp 4060 2012-04-17 09:55:30Z ming $ */
+/*
+ * Copyright (C) 2011-2011 Teluu Inc. (http://www.teluu.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "vidgui.h"
+#include "vidwin.h"
+
+#if defined(PJ_WIN32)
+# define SDL_MAIN_HANDLED
+#endif
+
+#include <SDL.h>
+#include <assert.h>
+#include <QMessageBox>
+
+#define LOG_FILE "vidgui.log"
+#define THIS_FILE "vidgui.cpp"
+
+///////////////////////////////////////////////////////////////////////////
+//
+// SETTINGS
+//
+
+//
+// These configure SIP registration
+//
+#define USE_REGISTRATION 0
+#define SIP_DOMAIN "pjsip.org"
+#define SIP_USERNAME "vidgui"
+#define SIP_PASSWORD "secret"
+#define SIP_PORT 5080
+#define SIP_TCP 1
+
+//
+// NAT helper settings
+//
+#define USE_ICE 1
+#define USE_STUN 0
+#define STUN_SRV "stun.pjsip.org"
+
+//
+// Devices settings
+//
+#define DEFAULT_CAP_DEV PJMEDIA_VID_DEFAULT_CAPTURE_DEV
+//#define DEFAULT_CAP_DEV 1
+#define DEFAULT_REND_DEV PJMEDIA_VID_DEFAULT_RENDER_DEV
+
+
+//
+// End of Settings
+///////////////////////////////////////////////////////////////////////////
+
+
+MainWin *MainWin::theInstance_;
+
+MainWin::MainWin(QWidget *parent)
+: QWidget(parent), accountId_(-1), currentCall_(-1),
+ preview_on(false), video_(NULL), video_prev_(NULL)
+{
+ theInstance_ = this;
+
+ initLayout();
+ emit signalCallReleased();
+}
+
+MainWin::~MainWin()
+{
+ quit();
+ theInstance_ = NULL;
+}
+
+MainWin *MainWin::instance()
+{
+ return theInstance_;
+}
+
+void MainWin::initLayout()
+{
+ //statusBar_ = new QStatusBar(this);
+
+ /* main layout */
+ QHBoxLayout *hbox_main = new QHBoxLayout;
+ //QVBoxLayout *vbox_left = new QVBoxLayout;
+ vbox_left = new QVBoxLayout;
+ QVBoxLayout *vbox_right = new QVBoxLayout;
+ hbox_main->addLayout(vbox_left);
+ hbox_main->addLayout(vbox_right);
+
+ /* Left pane */
+ QHBoxLayout *hbox_url = new QHBoxLayout;
+ hbox_url->addWidget(new QLabel(tr("Url:")));
+ hbox_url->addWidget(url_=new QLineEdit(tr("sip:")), 1);
+ vbox_left->addLayout(hbox_url);
+
+ /* Right pane */
+ vbox_right->addWidget((localUri_ = new QLabel));
+ vbox_right->addWidget((vidEnabled_ = new QCheckBox(tr("Enable &video"))));
+ vbox_right->addWidget((previewButton_=new QPushButton(tr("Start &Preview"))));
+ vbox_right->addWidget((callButton_=new QPushButton(tr("Call"))));
+ vbox_right->addWidget((hangupButton_=new QPushButton(tr("Hangup"))));
+ vbox_right->addWidget((quitButton_=new QPushButton(tr("Quit"))));
+
+#if PJMEDIA_HAS_VIDEO
+ vidEnabled_->setCheckState(Qt::Checked);
+#else
+ vidEnabled_->setCheckState(Qt::Unchecked);
+ vidEnabled_->setEnabled(false);
+#endif
+
+ /* Outest layout */
+ QVBoxLayout *vbox_outest = new QVBoxLayout;
+ vbox_outest->addLayout(hbox_main);
+ vbox_outest->addWidget((statusBar_ = new QLabel));
+
+ setLayout(vbox_outest);
+
+ connect(previewButton_, SIGNAL(clicked()), this, SLOT(preview()));
+ connect(callButton_, SIGNAL(clicked()), this, SLOT(call()));
+ connect(hangupButton_, SIGNAL(clicked()), this, SLOT(hangup()));
+ connect(quitButton_, SIGNAL(clicked()), this, SLOT(quit()));
+ //connect(this, SIGNAL(close()), this, SLOT(quit()));
+ connect(vidEnabled_, SIGNAL(stateChanged(int)), this, SLOT(onVidEnabledChanged(int)));
+
+ // UI updates must be done in the UI thread!
+ connect(this, SIGNAL(signalNewCall(int, bool)),
+ this, SLOT(onNewCall(int, bool)));
+ connect(this, SIGNAL(signalCallReleased()),
+ this, SLOT(onCallReleased()));
+ connect(this, SIGNAL(signalInitVideoWindow()),
+ this, SLOT(initVideoWindow()));
+ connect(this, SIGNAL(signalShowStatus(const QString&)),
+ this, SLOT(doShowStatus(const QString&)));
+}
+
+void MainWin::quit()
+{
+ delete video_prev_;
+ video_prev_ = NULL;
+ delete video_;
+ video_ = NULL;
+
+ pjsua_destroy();
+ qApp->quit();
+}
+
+void MainWin::showStatus(const char *msg)
+{
+ PJ_LOG(3,(THIS_FILE, "%s", msg));
+
+ QString msg_ = QString::fromUtf8(msg);
+ emit signalShowStatus(msg_);
+}
+
+void MainWin::doShowStatus(const QString& msg)
+{
+ //statusBar_->showMessage(msg);
+ statusBar_->setText(msg);
+}
+
+void MainWin::showError(const char *title, pj_status_t status)
+{
+ char errmsg[PJ_ERR_MSG_SIZE];
+ char errline[120];
+
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ snprintf(errline, sizeof(errline), "%s error: %s", title, errmsg);
+ showStatus(errline);
+}
+
+void MainWin::onVidEnabledChanged(int state)
+{
+ pjsua_call_setting call_setting;
+
+ if (currentCall_ == -1)
+ return;
+
+ pjsua_call_setting_default(&call_setting);
+ call_setting.vid_cnt = (state == Qt::Checked);
+
+ pjsua_call_reinvite2(currentCall_, &call_setting, NULL);
+}
+
+void MainWin::onNewCall(int cid, bool incoming)
+{
+ pjsua_call_info ci;
+
+ pj_assert(currentCall_ == -1);
+ currentCall_ = cid;
+
+ pjsua_call_get_info(cid, &ci);
+ url_->setText(ci.remote_info.ptr);
+ url_->setEnabled(false);
+ hangupButton_->setEnabled(true);
+
+ if (incoming) {
+ callButton_->setText(tr("Answer"));
+ callButton_->setEnabled(true);
+ } else {
+ callButton_->setEnabled(false);
+ }
+
+ //video_->setText(ci.remote_contact.ptr);
+ //video_->setWindowTitle(ci.remote_contact.ptr);
+}
+
+void MainWin::onCallReleased()
+{
+ url_->setEnabled(true);
+ callButton_->setEnabled(true);
+ callButton_->setText(tr("Call"));
+ hangupButton_->setEnabled(false);
+ currentCall_ = -1;
+
+ delete video_;
+ video_ = NULL;
+}
+
+void MainWin::preview()
+{
+ if (preview_on) {
+ delete video_prev_;
+ video_prev_ = NULL;
+
+ pjsua_vid_preview_stop(DEFAULT_CAP_DEV);
+
+ showStatus("Preview stopped");
+ previewButton_->setText(tr("Start &Preview"));
+ } else {
+ pjsua_vid_win_id wid;
+ pjsua_vid_win_info wi;
+ pjsua_vid_preview_param pre_param;
+ pj_status_t status;
+
+ pjsua_vid_preview_param_default(&pre_param);
+ pre_param.rend_id = DEFAULT_REND_DEV;
+ pre_param.show = PJ_FALSE;
+
+ status = pjsua_vid_preview_start(DEFAULT_CAP_DEV, &pre_param);
+ if (status != PJ_SUCCESS) {
+ char errmsg[PJ_ERR_MSG_SIZE];
+ pj_strerror(status, errmsg, sizeof(errmsg));
+ QMessageBox::critical(0, "Error creating preview", errmsg);
+ return;
+ }
+ wid = pjsua_vid_preview_get_win(DEFAULT_CAP_DEV);
+ pjsua_vid_win_get_info(wid, &wi);
+
+ video_prev_ = new VidWin(&wi.hwnd);
+ video_prev_->putIntoLayout(vbox_left);
+ //Using this will cause SDL window to display blank
+ //screen sometimes, probably because it's using different
+ //X11 Display
+ //status = pjsua_vid_win_set_show(wid, PJ_TRUE);
+ //This is handled by VidWin now
+ //video_prev_->show_sdl();
+ showStatus("Preview started");
+
+ previewButton_->setText(tr("Stop &Preview"));
+ }
+ preview_on = !preview_on;
+}
+
+
+void MainWin::call()
+{
+ if (callButton_->text() == "Answer") {
+ pjsua_call_setting call_setting;
+
+ pj_assert(currentCall_ != -1);
+
+ pjsua_call_setting_default(&call_setting);
+ call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked);
+
+ pjsua_call_answer2(currentCall_, &call_setting, 200, NULL, NULL);
+ callButton_->setEnabled(false);
+ } else {
+ pj_status_t status;
+ QString dst = url_->text();
+ char uri[256];
+ pjsua_call_setting call_setting;
+
+ pj_ansi_strncpy(uri, dst.toAscii().data(), sizeof(uri));
+ pj_str_t uri2 = pj_str((char*)uri);
+
+ pj_assert(currentCall_ == -1);
+
+ pjsua_call_setting_default(&call_setting);
+ call_setting.vid_cnt = (vidEnabled_->checkState()==Qt::Checked);
+
+ status = pjsua_call_make_call(accountId_, &uri2, &call_setting,
+ NULL, NULL, &currentCall_);
+ if (status != PJ_SUCCESS) {
+ showError("make call", status);
+ return;
+ }
+ }
+}
+
+void MainWin::hangup()
+{
+ pj_assert(currentCall_ != -1);
+ //pjsua_call_hangup(currentCall_, PJSIP_SC_BUSY_HERE, NULL, NULL);
+ pjsua_call_hangup_all();
+ emit signalCallReleased();
+}
+
+
+void MainWin::initVideoWindow()
+{
+ pjsua_call_info ci;
+ unsigned i;
+
+ if (currentCall_ == -1)
+ return;
+
+ delete video_;
+ video_ = NULL;
+
+ pjsua_call_get_info(currentCall_, &ci);
+ for (i = 0; i < ci.media_cnt; ++i) {
+ if ((ci.media[i].type == PJMEDIA_TYPE_VIDEO) &&
+ (ci.media[i].dir & PJMEDIA_DIR_DECODING))
+ {
+ pjsua_vid_win_info wi;
+ pjsua_vid_win_get_info(ci.media[i].stream.vid.win_in, &wi);
+
+ video_= new VidWin(&wi.hwnd);
+ video_->putIntoLayout(vbox_left);
+
+ break;
+ }
+ }
+}
+
+void MainWin::on_reg_state(pjsua_acc_id acc_id)
+{
+ pjsua_acc_info info;
+
+ pjsua_acc_get_info(acc_id, &info);
+
+ char reg_status[80];
+ char status[120];
+
+ if (!info.has_registration) {
+ pj_ansi_snprintf(reg_status, sizeof(reg_status), "%.*s",
+ (int)info.status_text.slen,
+ info.status_text.ptr);
+
+ } else {
+ pj_ansi_snprintf(reg_status, sizeof(reg_status),
+ "%d/%.*s (expires=%d)",
+ info.status,
+ (int)info.status_text.slen,
+ info.status_text.ptr,
+ info.expires);
+
+ }
+
+ snprintf(status, sizeof(status),
+ "%.*s: %s\n",
+ (int)info.acc_uri.slen, info.acc_uri.ptr,
+ reg_status);
+ showStatus(status);
+}
+
+void MainWin::on_call_state(pjsua_call_id call_id, pjsip_event *e)
+{
+ pjsua_call_info ci;
+
+ PJ_UNUSED_ARG(e);
+
+ pjsua_call_get_info(call_id, &ci);
+
+ if (currentCall_ == -1 && ci.state < PJSIP_INV_STATE_DISCONNECTED &&
+ ci.role == PJSIP_ROLE_UAC)
+ {
+ emit signalNewCall(call_id, false);
+ }
+
+ char status[80];
+ if (ci.state == PJSIP_INV_STATE_DISCONNECTED) {
+ snprintf(status, sizeof(status), "Call is %s (%s)",
+ ci.state_text.ptr,
+ ci.last_status_text.ptr);
+ showStatus(status);
+ emit signalCallReleased();
+ } else {
+ snprintf(status, sizeof(status), "Call is %s", pjsip_inv_state_name(ci.state));
+ showStatus(status);
+ }
+}
+
+void MainWin::on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
+ pjsip_rx_data *rdata)
+{
+ PJ_UNUSED_ARG(acc_id);
+ PJ_UNUSED_ARG(rdata);
+
+ if (currentCall_ != -1) {
+ pjsua_call_answer(call_id, PJSIP_SC_BUSY_HERE, NULL, NULL);
+ return;
+ }
+
+ emit signalNewCall(call_id, true);
+
+ pjsua_call_info ci;
+ char status[80];
+
+ pjsua_call_get_info(call_id, &ci);
+ snprintf(status, sizeof(status), "Incoming call from %.*s",
+ (int)ci.remote_info.slen, ci.remote_info.ptr);
+ showStatus(status);
+}
+
+void MainWin::on_call_media_state(pjsua_call_id call_id)
+{
+ pjsua_call_info ci;
+
+ pjsua_call_get_info(call_id, &ci);
+
+ for (unsigned i=0; i<ci.media_cnt; ++i) {
+ if (ci.media[i].type == PJMEDIA_TYPE_AUDIO) {
+ switch (ci.media[i].status) {
+ case PJSUA_CALL_MEDIA_ACTIVE:
+ pjsua_conf_connect(ci.media[i].stream.aud.conf_slot, 0);
+ pjsua_conf_connect(0, ci.media[i].stream.aud.conf_slot);
+ break;
+ default:
+ break;
+ }
+ } else if (ci.media[i].type == PJMEDIA_TYPE_VIDEO) {
+ emit signalInitVideoWindow();
+ }
+ }
+}
+
+//
+// pjsua callbacks
+//
+static void on_reg_state(pjsua_acc_id acc_id)
+{
+ MainWin::instance()->on_reg_state(acc_id);
+}
+
+static void on_call_state(pjsua_call_id call_id, pjsip_event *e)
+{
+ MainWin::instance()->on_call_state(call_id, e);
+}
+
+static void on_incoming_call(pjsua_acc_id acc_id, pjsua_call_id call_id,
+ pjsip_rx_data *rdata)
+{
+ MainWin::instance()->on_incoming_call(acc_id, call_id, rdata);
+}
+
+static void on_call_media_state(pjsua_call_id call_id)
+{
+ MainWin::instance()->on_call_media_state(call_id);
+}
+
+//
+// initStack()
+//
+bool MainWin::initStack()
+{
+ pj_status_t status;
+
+ //showStatus("Creating stack..");
+ status = pjsua_create();
+ if (status != PJ_SUCCESS) {
+ showError("pjsua_create", status);
+ return false;
+ }
+
+ showStatus("Initializing stack..");
+
+ pjsua_config ua_cfg;
+ pjsua_config_default(&ua_cfg);
+ pjsua_callback ua_cb;
+ pj_bzero(&ua_cb, sizeof(ua_cb));
+ ua_cfg.cb.on_reg_state = &::on_reg_state;
+ ua_cfg.cb.on_call_state = &::on_call_state;
+ ua_cfg.cb.on_incoming_call = &::on_incoming_call;
+ ua_cfg.cb.on_call_media_state = &::on_call_media_state;
+#if USE_STUN
+ ua_cfg.stun_srv_cnt = 1;
+ ua_cfg.stun_srv[0] = pj_str((char*)STUN_SRV);
+#endif
+
+ pjsua_logging_config log_cfg;
+ pjsua_logging_config_default(&log_cfg);
+ log_cfg.log_filename = pj_str((char*)LOG_FILE);
+
+ pjsua_media_config med_cfg;
+ pjsua_media_config_default(&med_cfg);
+ med_cfg.enable_ice = USE_ICE;
+
+ status = pjsua_init(&ua_cfg, &log_cfg, &med_cfg);
+ if (status != PJ_SUCCESS) {
+ showError("pjsua_init", status);
+ goto on_error;
+ }
+
+ //
+ // Create UDP and TCP transports
+ //
+ pjsua_transport_config udp_cfg;
+ pjsua_transport_id udp_id;
+ pjsua_transport_config_default(&udp_cfg);
+ udp_cfg.port = SIP_PORT;
+
+ status = pjsua_transport_create(PJSIP_TRANSPORT_UDP,
+ &udp_cfg, &udp_id);
+ if (status != PJ_SUCCESS) {
+ showError("UDP transport creation", status);
+ goto on_error;
+ }
+
+ pjsua_transport_info udp_info;
+ status = pjsua_transport_get_info(udp_id, &udp_info);
+ if (status != PJ_SUCCESS) {
+ showError("UDP transport info", status);
+ goto on_error;
+ }
+
+#if SIP_TCP
+ pjsua_transport_config tcp_cfg;
+ pjsua_transport_config_default(&tcp_cfg);
+ tcp_cfg.port = 0;
+
+ status = pjsua_transport_create(PJSIP_TRANSPORT_TCP,
+ &tcp_cfg, NULL);
+ if (status != PJ_SUCCESS) {
+ showError("TCP transport creation", status);
+ goto on_error;
+ }
+#endif
+
+ //
+ // Create account
+ //
+ pjsua_acc_config acc_cfg;
+ pjsua_acc_config_default(&acc_cfg);
+#if USE_REGISTRATION
+ acc_cfg.id = pj_str( (char*)"<sip:" SIP_USERNAME "@" SIP_DOMAIN ">");
+ acc_cfg.reg_uri = pj_str((char*) ("sip:" SIP_DOMAIN));
+ acc_cfg.cred_count = 1;
+ acc_cfg.cred_info[0].realm = pj_str((char*)"*");
+ acc_cfg.cred_info[0].scheme = pj_str((char*)"digest");
+ acc_cfg.cred_info[0].username = pj_str((char*)SIP_USERNAME);
+ acc_cfg.cred_info[0].data = pj_str((char*)SIP_PASSWORD);
+
+# if SIP_TCP
+ acc_cfg.proxy[acc_cfg.proxy_cnt++] = pj_str((char*) "<sip:" SIP_DOMAIN ";transport=tcp>");
+# endif
+
+#else
+ char sip_id[80];
+ snprintf(sip_id, sizeof(sip_id),
+ "sip:%s@%.*s:%u", SIP_USERNAME,
+ (int)udp_info.local_name.host.slen,
+ udp_info.local_name.host.ptr,
+ udp_info.local_name.port);
+ acc_cfg.id = pj_str(sip_id);
+#endif
+
+ acc_cfg.vid_cap_dev = DEFAULT_CAP_DEV;
+ acc_cfg.vid_rend_dev = DEFAULT_REND_DEV;
+ acc_cfg.vid_in_auto_show = PJ_TRUE;
+ acc_cfg.vid_out_auto_transmit = PJ_TRUE;
+
+ status = pjsua_acc_add(&acc_cfg, PJ_TRUE, &accountId_);
+ if (status != PJ_SUCCESS) {
+ showError("Account creation", status);
+ goto on_error;
+ }
+
+ localUri_->setText(acc_cfg.id.ptr);
+
+ //
+ // Start pjsua!
+ //
+ showStatus("Starting stack..");
+ status = pjsua_start();
+ if (status != PJ_SUCCESS) {
+ showError("pjsua_start", status);
+ goto on_error;
+ }
+
+ showStatus("Ready");
+
+ return true;
+
+on_error:
+ pjsua_destroy();
+ return false;
+}
+
+/*
+ * A simple registrar, invoked by default_mod_on_rx_request()
+ */
+static void simple_registrar(pjsip_rx_data *rdata)
+{
+ pjsip_tx_data *tdata;
+ const pjsip_expires_hdr *exp;
+ const pjsip_hdr *h;
+ unsigned cnt = 0;
+ pjsip_generic_string_hdr *srv;
+ pj_status_t status;
+
+ status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
+ rdata, 200, NULL, &tdata);
+ if (status != PJ_SUCCESS)
+ return;
+
+ exp = (pjsip_expires_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+
+ h = rdata->msg_info.msg->hdr.next;
+ while (h != &rdata->msg_info.msg->hdr) {
+ if (h->type == PJSIP_H_CONTACT) {
+ const pjsip_contact_hdr *c = (const pjsip_contact_hdr*)h;
+ int e = c->expires;
+
+ if (e < 0) {
+ if (exp)
+ e = exp->ivalue;
+ else
+ e = 3600;
+ }
+
+ if (e > 0) {
+ pjsip_contact_hdr *nc = (pjsip_contact_hdr*)
+ pjsip_hdr_clone(tdata->pool, h);
+ nc->expires = e;
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)nc);
+ ++cnt;
+ }
+ }
+ h = h->next;
+ }
+
+ srv = pjsip_generic_string_hdr_create(tdata->pool, NULL, NULL);
+ srv->name = pj_str((char*)"Server");
+ srv->hvalue = pj_str((char*)"pjsua simple registrar");
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)srv);
+
+ pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(),
+ rdata, tdata, NULL, NULL);
+}
+
+/* Notification on incoming request */
+static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
+{
+ /* Simple registrar */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_register_method) == 0)
+ {
+ simple_registrar(rdata);
+ return PJ_TRUE;
+ }
+
+ return PJ_FALSE;
+}
+
+/* The module instance. */
+static pjsip_module mod_default_handler =
+{
+ NULL, NULL, /* prev, next. */
+ { (char*)"mod-default-handler", 19 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION+99, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &default_mod_on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+int main(int argc, char *argv[])
+{
+ /* At least on Linux, we have to initialize SDL video subsystem prior to
+ * creating/initializing QApplication, otherwise we'll segfault miserably
+ * in SDL_CreateWindow(). Here's a stack trace if you're interested:
+
+ Thread [7] (Suspended: Signal 'SIGSEGV' received. Description: Segmentation fault.)
+ 13 XCreateIC()
+ 12 SetupWindowData()
+ 11 X11_CreateWindow()
+ 10 SDL_CreateWindow()
+ ..
+ */
+ if ( SDL_InitSubSystem(SDL_INIT_VIDEO) < 0 ) {
+ printf("Unable to init SDL: %s\n", SDL_GetError());
+ return 1;
+ }
+
+ QApplication app(argc, argv);
+
+ MainWin win;
+ win.show();
+
+ if (!win.initStack()) {
+ win.quit();
+ return 1;
+ }
+
+ /* We want to be registrar too! */
+ if (pjsua_get_pjsip_endpt()) {
+ pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
+ &mod_default_handler);
+ }
+
+ return app.exec();
+}
+