Skip to content
Snippets Groups Projects
Commit 88cfa3a8 authored by trilene's avatar trilene
Browse files

Polish voice call UI

parent da9995fc
No related branches found
No related tags found
No related merge requests found
Showing
with 371 additions and 146 deletions
resources/icons/ui/end-call.png

643 B

resources/icons/ui/microphone-mute.png

1.13 KiB

resources/icons/ui/microphone-unmute.png

1.07 KiB

resources/icons/ui/place-call.png

759 B

......@@ -70,6 +70,11 @@
<file>icons/ui/mail-reply.png</file>
<file>icons/ui/place-call.png</file>
<file>icons/ui/end-call.png</file>
<file>icons/ui/microphone-mute.png</file>
<file>icons/ui/microphone-unmute.png</file>
<file>icons/emoji-categories/people.png</file>
<file>icons/emoji-categories/people@2x.png</file>
<file>icons/emoji-categories/nature.png</file>
......
#include <cstdio>
#include <QDateTime>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel>
#include <QString>
#include <QTimer>
#include "ActiveCallBar.h"
#include "ChatPage.h"
#include "Utils.h"
#include "WebRTCSession.h"
#include "ui/Avatar.h"
#include "ui/FlatButton.h"
ActiveCallBar::ActiveCallBar(QWidget *parent)
......@@ -12,7 +19,7 @@ ActiveCallBar::ActiveCallBar(QWidget *parent)
{
setAutoFillBackground(true);
auto p = palette();
p.setColor(backgroundRole(), Qt::green);
p.setColor(backgroundRole(), QColorConstants::Svg::limegreen);
setPalette(p);
QFont f;
......@@ -24,51 +31,126 @@ ActiveCallBar::ActiveCallBar(QWidget *parent)
setFixedHeight(contentHeight + widgetMargin);
topLayout_ = new QHBoxLayout(this);
topLayout_->setSpacing(widgetMargin);
topLayout_->setContentsMargins(
layout_ = new QHBoxLayout(this);
layout_->setSpacing(widgetMargin);
layout_->setContentsMargins(
2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin);
topLayout_->setSizeConstraint(QLayout::SetMinimumSize);
QFont labelFont;
labelFont.setPointSizeF(labelFont.pointSizeF() * 1.2);
labelFont.setPointSizeF(labelFont.pointSizeF() * 1.1);
labelFont.setWeight(QFont::Medium);
avatar_ = new Avatar(this, QFontMetrics(f).height() * 2.5);
callPartyLabel_ = new QLabel(this);
callPartyLabel_->setFont(labelFont);
// TODO microphone mute/unmute icons
stateLabel_ = new QLabel(this);
stateLabel_->setFont(labelFont);
durationLabel_ = new QLabel(this);
durationLabel_->setFont(labelFont);
durationLabel_->hide();
muteBtn_ = new FlatButton(this);
QIcon muteIcon;
muteIcon.addFile(":/icons/icons/ui/do-not-disturb-rounded-sign.png");
muteBtn_->setIcon(muteIcon);
muteBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));
muteBtn_->setToolTip(tr("Mute Mic"));
setMuteIcon(false);
muteBtn_->setFixedSize(buttonSize_, buttonSize_);
muteBtn_->setCornerRadius(buttonSize_ / 2);
connect(muteBtn_, &FlatButton::clicked, this, [this]() {
if (WebRTCSession::instance().toggleMuteAudioSrc(muted_)) {
QIcon icon;
if (muted_) {
muteBtn_->setToolTip("Unmute Mic");
icon.addFile(":/icons/icons/ui/round-remove-button.png");
} else {
muteBtn_->setToolTip("Mute Mic");
icon.addFile(":/icons/icons/ui/do-not-disturb-rounded-sign.png");
}
muteBtn_->setIcon(icon);
}
connect(muteBtn_, &FlatButton::clicked, this, [this](){
if (WebRTCSession::instance().toggleMuteAudioSrc(muted_))
setMuteIcon(muted_);
});
topLayout_->addWidget(callPartyLabel_, 0, Qt::AlignLeft);
topLayout_->addWidget(muteBtn_, 0, Qt::AlignRight);
layout_->addWidget(avatar_, 0, Qt::AlignLeft);
layout_->addWidget(callPartyLabel_, 0, Qt::AlignLeft);
layout_->addWidget(stateLabel_, 0, Qt::AlignLeft);
layout_->addWidget(durationLabel_, 0, Qt::AlignLeft);
layout_->addStretch();
layout_->addWidget(muteBtn_, 0, Qt::AlignCenter);
layout_->addSpacing(18);
timer_ = new QTimer(this);
connect(timer_, &QTimer::timeout, this,
[this](){
auto seconds = QDateTime::currentSecsSinceEpoch() - callStartTime_;
int s = seconds % 60;
int m = (seconds / 60) % 60;
int h = seconds / 3600;
char buf[12];
if (h)
snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d", h, m, s);
else
snprintf(buf, sizeof(buf), "%.2d:%.2d", m, s);
durationLabel_->setText(buf);
});
connect(&WebRTCSession::instance(), &WebRTCSession::stateChanged, this, &ActiveCallBar::update);
}
void
ActiveCallBar::setMuteIcon(bool muted)
{
QIcon icon;
if (muted) {
muteBtn_->setToolTip("Unmute Mic");
icon.addFile(":/icons/icons/ui/microphone-unmute.png");
} else {
muteBtn_->setToolTip("Mute Mic");
icon.addFile(":/icons/icons/ui/microphone-mute.png");
}
muteBtn_->setIcon(icon);
muteBtn_->setIconSize(QSize(buttonSize_, buttonSize_));
}
void
ActiveCallBar::setCallParty(const QString &userid, const QString &displayName)
ActiveCallBar::setCallParty(
const QString &userid,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl)
{
if (!displayName.isEmpty() && displayName != userid)
callPartyLabel_->setText("Active Call: " + displayName + " (" + userid + ")");
callPartyLabel_->setText(
(displayName.isEmpty() ? userid : displayName) + " -");
if (!avatarUrl.isEmpty())
avatar_->setImage(avatarUrl);
else
callPartyLabel_->setText("Active Call: " + userid);
avatar_->setLetter(utils::firstChar(roomName));
}
void
ActiveCallBar::update(WebRTCSession::State state)
{
switch (state) {
case WebRTCSession::State::INITIATING:
stateLabel_->setText("Initiating call...");
break;
case WebRTCSession::State::INITIATED:
stateLabel_->setText("Call initiated...");
break;
case WebRTCSession::State::OFFERSENT:
stateLabel_->setText("Calling...");
break;
case WebRTCSession::State::CONNECTING:
stateLabel_->setText("Connecting...");
break;
case WebRTCSession::State::CONNECTED:
callStartTime_ = QDateTime::currentSecsSinceEpoch();
timer_->start(1000);
stateLabel_->setText("Active call:");
durationLabel_->setText("00:00");
durationLabel_->show();
muteBtn_->show();
break;
case WebRTCSession::State::DISCONNECTED:
timer_->stop();
callPartyLabel_->setText(QString());
stateLabel_->setText(QString());
durationLabel_->setText(QString());
durationLabel_->hide();
setMuteIcon(false);
break;
default:
break;
}
}
......@@ -2,9 +2,12 @@
#include <QWidget>
#include "WebRTCSession.h"
class QHBoxLayout;
class QLabel;
class QString;
class QTimer;
class Avatar;
class FlatButton;
class ActiveCallBar : public QWidget
......@@ -15,12 +18,24 @@ public:
ActiveCallBar(QWidget *parent = nullptr);
public slots:
void setCallParty(const QString &userid, const QString &displayName);
void update(WebRTCSession::State);
void setCallParty(
const QString &userid,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl);
private:
QHBoxLayout *topLayout_ = nullptr;
QHBoxLayout *layout_ = nullptr;
Avatar *avatar_ = nullptr;
QLabel *callPartyLabel_ = nullptr;
QLabel *stateLabel_ = nullptr;
QLabel *durationLabel_ = nullptr;
FlatButton *muteBtn_ = nullptr;
int buttonSize_ = 32;
int buttonSize_ = 22;
bool muted_ = false;
qint64 callStartTime_ = 0;
QTimer *timer_ = nullptr;
void setMuteIcon(bool muted);
};
......@@ -68,9 +68,9 @@ CallManager::CallManager(QSharedPointer<UserSettings> userSettings)
turnServerTimer_.setInterval(res.ttl * 1000 * 0.9);
});
connect(&session_, &WebRTCSession::pipelineChanged, this,
[this](bool started) {
if (!started)
connect(&session_, &WebRTCSession::stateChanged, this,
[this](WebRTCSession::State state) {
if (state == WebRTCSession::State::DISCONNECTED)
playRingtone("qrc:/media/media/callend.ogg", false);
});
......@@ -87,9 +87,9 @@ CallManager::sendInvite(const QString &roomid)
if (onActiveCall())
return;
std::vector<RoomMember> members(cache::getMembers(roomid.toStdString()));
if (members.size() != 2) {
emit ChatPage::instance()->showNotification("Voice/Video calls are limited to 1:1 rooms");
auto roomInfo = cache::singleRoomInfo(roomid.toStdString());
if (roomInfo.member_count != 2) {
emit ChatPage::instance()->showNotification("Voice calls are limited to 1:1 rooms.");
return;
}
......@@ -105,11 +105,13 @@ CallManager::sendInvite(const QString &roomid)
// TODO Add invite timeout
generateCallID();
std::vector<RoomMember> members(cache::getMembers(roomid.toStdString()));
const RoomMember &callee = members.front().user_id == utils::localUser() ? members.back() : members.front();
emit newCallParty(callee.user_id, callee.display_name);
emit newCallParty(callee.user_id, callee.display_name,
QString::fromStdString(roomInfo.name), QString::fromStdString(roomInfo.avatar_url));
playRingtone("qrc:/media/media/ringback.ogg", true);
if (!session_.createOffer()) {
emit ChatPage::instance()->showNotification("Problem setting up call");
emit ChatPage::instance()->showNotification("Problem setting up call.");
endCall();
}
}
......@@ -127,7 +129,7 @@ CallManager::hangUp()
bool
CallManager::onActiveCall()
{
return session_.isActive();
return session_.state() != WebRTCSession::State::DISCONNECTED;
}
void CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event)
......@@ -156,8 +158,8 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent)
if (callInviteEvent.content.call_id.empty())
return;
std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id));
if (onActiveCall() || members.size() != 2) {
auto roomInfo = cache::singleRoomInfo(callInviteEvent.room_id);
if (onActiveCall() || roomInfo.member_count != 2) {
emit newMessage(QString::fromStdString(callInviteEvent.room_id),
CallHangUp{callInviteEvent.content.call_id, 0, CallHangUp::Reason::InviteTimeOut});
return;
......@@ -168,10 +170,18 @@ CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent)
callid_ = callInviteEvent.content.call_id;
remoteICECandidates_.clear();
const RoomMember &caller = members.front().user_id == utils::localUser() ? members.back() : members.front();
emit newCallParty(caller.user_id, caller.display_name);
auto dialog = new dialogs::AcceptCall(caller.user_id, caller.display_name, MainWindow::instance());
std::vector<RoomMember> members(cache::getMembers(callInviteEvent.room_id));
const RoomMember &caller =
members.front().user_id == utils::localUser() ? members.back() : members.front();
emit newCallParty(caller.user_id, caller.display_name,
QString::fromStdString(roomInfo.name), QString::fromStdString(roomInfo.avatar_url));
auto dialog = new dialogs::AcceptCall(
caller.user_id,
caller.display_name,
QString::fromStdString(roomInfo.name),
QString::fromStdString(roomInfo.avatar_url),
MainWindow::instance());
connect(dialog, &dialogs::AcceptCall::accept, this,
[this, callInviteEvent](){
MainWindow::instance()->hideOverlay();
......@@ -198,7 +208,7 @@ CallManager::answerInvite(const CallInvite &invite)
session_.setStunServer(settings_->useStunServer() ? STUN_SERVER : "");
if (!session_.acceptOffer(invite.sdp)) {
emit ChatPage::instance()->showNotification("Problem setting up call");
emit ChatPage::instance()->showNotification("Problem setting up call.");
hangUp();
return;
}
......@@ -232,6 +242,7 @@ CallManager::handleEvent(const RoomEvent<CallAnswer> &callAnswerEvent)
if (!onActiveCall() && callAnswerEvent.sender == utils::localUser().toStdString() &&
callid_ == callAnswerEvent.content.call_id) {
emit ChatPage::instance()->showNotification("Call answered on another device.");
stopRingtone();
MainWindow::instance()->hideOverlay();
return;
......@@ -240,7 +251,7 @@ CallManager::handleEvent(const RoomEvent<CallAnswer> &callAnswerEvent)
if (onActiveCall() && callid_ == callAnswerEvent.content.call_id) {
stopRingtone();
if (!session_.acceptAnswer(callAnswerEvent.content.sdp)) {
emit ChatPage::instance()->showNotification("Problem setting up call");
emit ChatPage::instance()->showNotification("Problem setting up call.");
hangUp();
}
}
......
......@@ -36,7 +36,11 @@ signals:
void newMessage(const QString &roomid, const mtx::events::msg::CallAnswer&);
void newMessage(const QString &roomid, const mtx::events::msg::CallHangUp&);
void turnServerRetrieved(const mtx::responses::TurnServer&);
void newCallParty(const QString &userid, const QString& displayName);
void newCallParty(
const QString &userid,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl);
private slots:
void retrieveTurnServer();
......
......@@ -138,13 +138,13 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect(
&callManager_, &CallManager::newCallParty, activeCallBar_, &ActiveCallBar::setCallParty);
connect(&WebRTCSession::instance(),
&WebRTCSession::pipelineChanged,
&WebRTCSession::stateChanged,
this,
[this](bool callStarted) {
if (callStarted)
activeCallBar_->show();
else
[this](WebRTCSession::State state) {
if (state == WebRTCSession::State::DISCONNECTED)
activeCallBar_->hide();
else
activeCallBar_->show();
});
// Splitter
......@@ -469,22 +469,28 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
if (callManager_.onActiveCall()) {
callManager_.hangUp();
} else {
if (cache::singleRoomInfo(current_room_.toStdString()).member_count != 2) {
showNotification("Voice/Video calls are limited to 1:1 rooms");
if (auto roomInfo =
cache::singleRoomInfo(current_room_.toStdString());
roomInfo.member_count != 2) {
showNotification("Voice calls are limited to 1:1 rooms.");
} else {
std::vector<RoomMember> members(
cache::getMembers(current_room_.toStdString()));
const RoomMember &callee =
members.front().user_id == utils::localUser() ? members.back()
: members.front();
auto dialog =
new dialogs::PlaceCall(callee.user_id, callee.display_name, MainWindow::instance());
auto dialog = new dialogs::PlaceCall(
callee.user_id,
callee.display_name,
QString::fromStdString(roomInfo.name),
QString::fromStdString(roomInfo.avatar_url),
MainWindow::instance());
connect(dialog, &dialogs::PlaceCall::voice, this, [this]() {
callManager_.sendInvite(current_room_);
});
connect(dialog, &dialogs::PlaceCall::video, this, [this]() {
showNotification("Video calls not yet implemented");
});
/*connect(dialog, &dialogs::PlaceCall::video, this, [this]() {
showNotification("Video calls not yet implemented.");
});*/
utils::centerWidget(dialog, MainWindow::instance());
dialog->show();
}
......
......@@ -31,7 +31,6 @@
#include "Logging.h"
#include "TextInputWidget.h"
#include "Utils.h"
#include "WebRTCSession.h"
#include "ui/FlatButton.h"
#include "ui/LoadingIndicator.h"
......@@ -455,9 +454,9 @@ TextInputWidget::TextInputWidget(QWidget *parent)
topLayout_->setContentsMargins(13, 1, 13, 0);
callBtn_ = new FlatButton(this);
changeCallButtonState(false);
changeCallButtonState(WebRTCSession::State::DISCONNECTED);
connect(&WebRTCSession::instance(),
&WebRTCSession::pipelineChanged,
&WebRTCSession::stateChanged,
this,
&TextInputWidget::changeCallButtonState);
......@@ -664,17 +663,16 @@ TextInputWidget::paintEvent(QPaintEvent *)
}
void
TextInputWidget::changeCallButtonState(bool callStarted)
TextInputWidget::changeCallButtonState(WebRTCSession::State state)
{
// TODO Telephone and HangUp icons - co-opt the ones below for now
QIcon icon;
if (callStarted) {
callBtn_->setToolTip(tr("Hang up"));
icon.addFile(":/icons/icons/ui/remove-symbol.png");
} else {
if (state == WebRTCSession::State::DISCONNECTED) {
callBtn_->setToolTip(tr("Place a call"));
icon.addFile(":/icons/icons/ui/speech-bubbles-comment-option.png");
icon.addFile(":/icons/icons/ui/place-call.png");
} else {
callBtn_->setToolTip(tr("Hang up"));
icon.addFile(":/icons/icons/ui/end-call.png");
}
callBtn_->setIcon(icon);
callBtn_->setIconSize(QSize(ButtonHeight, ButtonHeight));
callBtn_->setIconSize(QSize(ButtonHeight * 1.1, ButtonHeight * 1.1));
}
......@@ -26,6 +26,7 @@
#include <QTextEdit>
#include <QWidget>
#include "WebRTCSession.h"
#include "dialogs/PreviewUploadOverlay.h"
#include "emoji/PickButton.h"
#include "popups/SuggestionsPopup.h"
......@@ -149,7 +150,7 @@ public slots:
void openFileSelection();
void hideUploadSpinner();
void focusLineEdit() { input_->setFocus(); }
void changeCallButtonState(bool callStarted);
void changeCallButtonState(WebRTCSession::State);
private slots:
void addSelectedEmoji(const QString &emoji);
......
......@@ -11,6 +11,8 @@ extern "C" {
#include "gst/webrtc/webrtc.h"
}
Q_DECLARE_METATYPE(WebRTCSession::State)
namespace {
bool gisoffer;
std::string glocalsdp;
......@@ -29,6 +31,12 @@ std::string::const_iterator findName(const std::string &sdp, const std::string
int getPayloadType(const std::string &sdp, const std::string &name);
}
WebRTCSession::WebRTCSession() : QObject()
{
qRegisterMetaType<WebRTCSession::State>();
connect(this, &WebRTCSession::stateChanged, this, &WebRTCSession::setState);
}
bool
WebRTCSession::init(std::string *errorMessage)
{
......@@ -54,14 +62,14 @@ WebRTCSession::init(std::string *errorMessage)
nhlog::ui()->info("Initialised " + gstVersion);
// GStreamer Plugins:
// Base: audioconvert, audioresample, opus, playback, videoconvert, volume
// Base: audioconvert, audioresample, opus, playback, volume
// Good: autodetect, rtpmanager, vpx
// Bad: dtls, srtp, webrtc
// libnice [GLib]: nice
initialised_ = true;
std::string strError = gstVersion + ": Missing plugins: ";
const gchar *needed[] = {"audioconvert", "audioresample", "autodetect", "dtls", "nice",
"opus", "playback", "rtpmanager", "srtp", "videoconvert", "vpx", "volume", "webrtc", nullptr};
"opus", "playback", "rtpmanager", "srtp", "vpx", "volume", "webrtc", nullptr};
GstRegistry *registry = gst_registry_get();
for (guint i = 0; i < g_strv_length((gchar**)needed); i++) {
GstPlugin *plugin = gst_registry_find_plugin(registry, needed[i]);
......@@ -91,17 +99,19 @@ WebRTCSession::createOffer()
}
bool
WebRTCSession::acceptOffer(const std::string& sdp)
WebRTCSession::acceptOffer(const std::string &sdp)
{
nhlog::ui()->debug("Received offer:\n{}", sdp);
if (state_ != State::DISCONNECTED)
return false;
gisoffer = false;
glocalsdp.clear();
gcandidates.clear();
int opusPayloadType = getPayloadType(sdp, "opus");
if (opusPayloadType == -1) {
if (opusPayloadType == -1)
return false;
}
GstWebRTCSessionDescription *offer = parseSDP(sdp, GST_WEBRTC_SDP_TYPE_OFFER);
if (!offer)
......@@ -120,9 +130,11 @@ WebRTCSession::acceptOffer(const std::string& sdp)
bool
WebRTCSession::startPipeline(int opusPayloadType)
{
if (isActive())
if (state_ != State::DISCONNECTED)
return false;
emit stateChanged(State::INITIATING);
if (!createPipeline(opusPayloadType))
return false;
......@@ -132,7 +144,12 @@ WebRTCSession::startPipeline(int opusPayloadType)
nhlog::ui()->info("WebRTC: Setting STUN server: {}", stunServer_);
g_object_set(webrtc_, "stun-server", stunServer_.c_str(), nullptr);
}
addTurnServers();
for (const auto &uri : turnServers_) {
nhlog::ui()->info("WebRTC: Setting TURN server: {}", uri);
gboolean udata;
g_signal_emit_by_name(webrtc_, "add-turn-server", uri.c_str(), (gpointer)(&udata));
}
// generate the offer when the pipeline goes to PLAYING
if (gisoffer)
......@@ -152,16 +169,14 @@ WebRTCSession::startPipeline(int opusPayloadType)
GstStateChangeReturn ret = gst_element_set_state(pipe_, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
nhlog::ui()->error("WebRTC: unable to start pipeline");
gst_object_unref(pipe_);
pipe_ = nullptr;
webrtc_ = nullptr;
end();
return false;
}
GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_));
gst_bus_add_watch(bus, newBusMessage, this);
gst_object_unref(bus);
emit pipelineChanged(true);
emit stateChanged(State::INITIATED);
return true;
}
......@@ -180,10 +195,7 @@ WebRTCSession::createPipeline(int opusPayloadType)
if (error) {
nhlog::ui()->error("WebRTC: Failed to parse pipeline: {}", error->message);
g_error_free(error);
if (pipe_) {
gst_object_unref(pipe_);
pipe_ = nullptr;
}
end();
return false;
}
return true;
......@@ -193,7 +205,7 @@ bool
WebRTCSession::acceptAnswer(const std::string &sdp)
{
nhlog::ui()->debug("WebRTC: Received sdp:\n{}", sdp);
if (!isActive())
if (state_ != State::OFFERSENT)
return false;
GstWebRTCSessionDescription *answer = parseSDP(sdp, GST_WEBRTC_SDP_TYPE_ANSWER);
......@@ -206,18 +218,20 @@ WebRTCSession::acceptAnswer(const std::string &sdp)
}
void
WebRTCSession::acceptICECandidates(const std::vector<mtx::events::msg::CallCandidates::Candidate>& candidates)
WebRTCSession::acceptICECandidates(const std::vector<mtx::events::msg::CallCandidates::Candidate> &candidates)
{
if (isActive()) {
for (const auto& c : candidates)
if (state_ >= State::INITIATED) {
for (const auto &c : candidates)
g_signal_emit_by_name(webrtc_, "add-ice-candidate", c.sdpMLineIndex, c.candidate.c_str());
}
if (state_ < State::CONNECTED)
emit stateChanged(State::CONNECTING);
}
bool
WebRTCSession::toggleMuteAudioSrc(bool &isMuted)
{
if (!isActive())
if (state_ < State::INITIATED)
return false;
GstElement *srclevel = gst_bin_get_by_name(GST_BIN(pipe_), "srclevel");
......@@ -241,20 +255,7 @@ WebRTCSession::end()
pipe_ = nullptr;
}
webrtc_ = nullptr;
emit pipelineChanged(false);
}
void
WebRTCSession::addTurnServers()
{
if (!webrtc_)
return;
for (const auto &uri : turnServers_) {
nhlog::ui()->info("WebRTC: Setting TURN server: {}", uri);
gboolean udata;
g_signal_emit_by_name(webrtc_, "add-turn-server", uri.c_str(), (gpointer)(&udata));
}
emit stateChanged(State::DISCONNECTED);
}
namespace {
......@@ -373,8 +374,10 @@ gboolean
onICEGatheringCompletion(gpointer timerid)
{
*(guint*)(timerid) = 0;
if (gisoffer)
if (gisoffer) {
emit WebRTCSession::instance().offerCreated(glocalsdp, gcandidates);
emit WebRTCSession::instance().stateChanged(WebRTCSession::State::OFFERSENT);
}
else
emit WebRTCSession::instance().answerCreated(glocalsdp, gcandidates);
......@@ -445,6 +448,9 @@ linkNewPad(GstElement *decodebin G_GNUC_UNUSED, GstPad *newpad, GstElement *pipe
if (queuepad) {
if (GST_PAD_LINK_FAILED(gst_pad_link(newpad, queuepad)))
nhlog::ui()->error("WebRTC: Unable to link new pad");
else {
emit WebRTCSession::instance().stateChanged(WebRTCSession::State::CONNECTED);
}
gst_object_unref(queuepad);
}
}
......
......@@ -14,6 +14,15 @@ class WebRTCSession : public QObject
Q_OBJECT
public:
enum class State {
DISCONNECTED,
INITIATING,
INITIATED,
OFFERSENT,
CONNECTING,
CONNECTED
};
static WebRTCSession& instance()
{
static WebRTCSession instance;
......@@ -27,7 +36,7 @@ public:
bool acceptAnswer(const std::string &sdp);
void acceptICECandidates(const std::vector<mtx::events::msg::CallCandidates::Candidate>&);
bool isActive() { return pipe_ != nullptr; }
State state() const {return state_;}
bool toggleMuteAudioSrc(bool &isMuted);
void end();
......@@ -37,12 +46,16 @@ public:
signals:
void offerCreated(const std::string &sdp, const std::vector<mtx::events::msg::CallCandidates::Candidate>&);
void answerCreated(const std::string &sdp, const std::vector<mtx::events::msg::CallCandidates::Candidate>&);
void pipelineChanged(bool started);
void stateChanged(WebRTCSession::State); // explicit qualifier necessary for Qt
private slots:
void setState(State state) {state_ = state;}
private:
WebRTCSession() : QObject() {}
WebRTCSession();
bool initialised_ = false;
State state_ = State::DISCONNECTED;
GstElement *pipe_ = nullptr;
GstElement *webrtc_ = nullptr;
std::string stunServer_;
......@@ -50,7 +63,6 @@ private:
bool startPipeline(int opusPayloadType);
bool createPipeline(int opusPayloadType);
void addTurnServers();
public:
WebRTCSession(WebRTCSession const&) = delete;
......
#include <QLabel>
#include <QPushButton>
#include <QString>
#include <QVBoxLayout>
#include "Config.h"
#include "Utils.h"
#include "dialogs/AcceptCall.h"
#include "ui/Avatar.h"
namespace dialogs {
AcceptCall::AcceptCall(const QString &caller, const QString &displayName, QWidget *parent)
: QWidget(parent)
AcceptCall::AcceptCall(
const QString &caller,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl,
QWidget *parent) : QWidget(parent)
{
setAutoFillBackground(true);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
setWindowModality(Qt::WindowModal);
setAttribute(Qt::WA_DeleteOnClose, true);
setMinimumWidth(conf::modals::MIN_WIDGET_WIDTH);
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
auto layout = new QVBoxLayout(this);
layout->setSpacing(conf::modals::WIDGET_SPACING);
layout->setMargin(conf::modals::WIDGET_MARGIN);
auto buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(15);
buttonLayout->setMargin(0);
QFont f;
f.setPointSizeF(f.pointSizeF());
QFont labelFont;
labelFont.setWeight(QFont::Medium);
QLabel *displayNameLabel = nullptr;
if (!displayName.isEmpty() && displayName != caller) {
displayNameLabel = new QLabel(displayName, this);
labelFont.setPointSizeF(f.pointSizeF() * 2);
displayNameLabel ->setFont(labelFont);
displayNameLabel ->setAlignment(Qt::AlignCenter);
}
QLabel *callerLabel = new QLabel(caller, this);
labelFont.setPointSizeF(f.pointSizeF() * 1.2);
callerLabel->setFont(labelFont);
callerLabel->setAlignment(Qt::AlignCenter);
QLabel *voiceCallLabel = new QLabel("Voice Call", this);
labelFont.setPointSizeF(f.pointSizeF() * 1.1);
voiceCallLabel->setFont(labelFont);
voiceCallLabel->setAlignment(Qt::AlignCenter);
auto avatar = new Avatar(this, QFontMetrics(f).height() * 6);
if (!avatarUrl.isEmpty())
avatar->setImage(avatarUrl);
else
avatar->setLetter(utils::firstChar(roomName));
const int iconSize = 24;
auto buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(20);
acceptBtn_ = new QPushButton(tr("Accept"), this);
acceptBtn_->setDefault(true);
rejectBtn_ = new QPushButton(tr("Reject"), this);
acceptBtn_->setIcon(QIcon(":/icons/icons/ui/place-call.png"));
acceptBtn_->setIconSize(QSize(iconSize, iconSize));
buttonLayout->addStretch(1);
rejectBtn_ = new QPushButton(tr("Reject"), this);
rejectBtn_->setIcon(QIcon(":/icons/icons/ui/end-call.png"));
rejectBtn_->setIconSize(QSize(iconSize, iconSize));
buttonLayout->addWidget(acceptBtn_);
buttonLayout->addWidget(rejectBtn_);
QLabel *label;
if (!displayName.isEmpty() && displayName != caller)
label = new QLabel("Accept call from " + displayName + " (" + caller + ")?", this);
else
label = new QLabel("Accept call from " + caller + "?", this);
layout->addWidget(label);
if (displayNameLabel)
layout->addWidget(displayNameLabel, 0, Qt::AlignCenter);
layout->addWidget(callerLabel, 0, Qt::AlignCenter);
layout->addWidget(voiceCallLabel, 0, Qt::AlignCenter);
layout->addWidget(avatar, 0, Qt::AlignCenter);
layout->addLayout(buttonLayout);
connect(acceptBtn_, &QPushButton::clicked, this, [this]() {
......
#pragma once
#include <QString>
#include <QWidget>
class QPushButton;
class QString;
namespace dialogs {
......@@ -12,7 +12,12 @@ class AcceptCall : public QWidget
Q_OBJECT
public:
AcceptCall(const QString &caller, const QString &displayName, QWidget *parent = nullptr);
AcceptCall(
const QString &caller,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl,
QWidget *parent = nullptr);
signals:
void accept();
......
......@@ -4,12 +4,18 @@
#include <QVBoxLayout>
#include "Config.h"
#include "Utils.h"
#include "dialogs/PlaceCall.h"
#include "ui/Avatar.h"
namespace dialogs {
PlaceCall::PlaceCall(const QString &callee, const QString &displayName, QWidget *parent)
: QWidget(parent)
PlaceCall::PlaceCall(
const QString &callee,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl,
QWidget *parent) : QWidget(parent)
{
setAutoFillBackground(true);
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
......@@ -20,25 +26,31 @@ PlaceCall::PlaceCall(const QString &callee, const QString &displayName, QWidget
layout->setSpacing(conf::modals::WIDGET_SPACING);
layout->setMargin(conf::modals::WIDGET_MARGIN);
auto buttonLayout = new QHBoxLayout();
auto buttonLayout = new QHBoxLayout(this);
buttonLayout->setSpacing(15);
buttonLayout->setMargin(0);
QFont f;
f.setPointSizeF(f.pointSizeF());
auto avatar = new Avatar(this, QFontMetrics(f).height() * 3);
if (!avatarUrl.isEmpty())
avatar->setImage(avatarUrl);
else
avatar->setLetter(utils::firstChar(roomName));
voiceBtn_ = new QPushButton(tr("Voice Call"), this);
voiceBtn_->setDefault(true);
videoBtn_ = new QPushButton(tr("Video Call"), this);
//videoBtn_ = new QPushButton(tr("Video Call"), this);
cancelBtn_ = new QPushButton(tr("Cancel"), this);
buttonLayout->addStretch(1);
buttonLayout->addWidget(avatar);
buttonLayout->addWidget(voiceBtn_);
buttonLayout->addWidget(videoBtn_);
//buttonLayout->addWidget(videoBtn_);
buttonLayout->addWidget(cancelBtn_);
QLabel *label;
if (!displayName.isEmpty() && displayName != callee)
label = new QLabel("Place a call to " + displayName + " (" + callee + ")?", this);
else
label = new QLabel("Place a call to " + callee + "?", this);
QString name = displayName.isEmpty() ? callee : displayName;
QLabel *label = new QLabel("Place a call to " + name + "?", this);
layout->addWidget(label);
layout->addLayout(buttonLayout);
......@@ -47,10 +59,10 @@ PlaceCall::PlaceCall(const QString &callee, const QString &displayName, QWidget
emit voice();
emit close();
});
connect(videoBtn_, &QPushButton::clicked, this, [this]() {
/*connect(videoBtn_, &QPushButton::clicked, this, [this]() {
emit video();
emit close();
});
});*/
connect(cancelBtn_, &QPushButton::clicked, this, [this]() {
emit cancel();
emit close();
......
......@@ -12,16 +12,21 @@ class PlaceCall : public QWidget
Q_OBJECT
public:
PlaceCall(const QString &callee, const QString &displayName, QWidget *parent = nullptr);
PlaceCall(
const QString &callee,
const QString &displayName,
const QString &roomName,
const QString &avatarUrl,
QWidget *parent = nullptr);
signals:
void voice();
void video();
// void video();
void cancel();
private:
QPushButton *voiceBtn_;
QPushButton *videoBtn_;
// QPushButton *videoBtn_;
QPushButton *cancelBtn_;
};
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment