summary refs log tree commit diff
path: root/src/CallManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/CallManager.cpp')
-rw-r--r--src/CallManager.cpp569
1 files changed, 300 insertions, 269 deletions
diff --git a/src/CallManager.cpp b/src/CallManager.cpp

index cbfd5135..46781313 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp
@@ -1,13 +1,13 @@ #include <algorithm> #include <cctype> -#include <cstdint> #include <chrono> +#include <cstdint> #include <QMediaPlaylist> #include <QUrl> -#include "CallManager.h" #include "Cache.h" +#include "CallManager.h" #include "ChatPage.h" #include "Logging.h" #include "MainWindow.h" @@ -34,389 +34,420 @@ getTurnURIs(const mtx::responses::TurnServer &turnServer); } CallManager::CallManager(QSharedPointer<UserSettings> userSettings) - : QObject(), - session_(WebRTCSession::instance()), - turnServerTimer_(this), - settings_(userSettings) + : QObject() + , session_(WebRTCSession::instance()) + , turnServerTimer_(this) + , settings_(userSettings) { - qRegisterMetaType<std::vector<mtx::events::msg::CallCandidates::Candidate>>(); - qRegisterMetaType<mtx::events::msg::CallCandidates::Candidate>(); - qRegisterMetaType<mtx::responses::TurnServer>(); + qRegisterMetaType<std::vector<mtx::events::msg::CallCandidates::Candidate>>(); + qRegisterMetaType<mtx::events::msg::CallCandidates::Candidate>(); + qRegisterMetaType<mtx::responses::TurnServer>(); - connect(&session_, &WebRTCSession::offerCreated, this, - [this](const std::string &sdp, - const std::vector<CallCandidates::Candidate> &candidates) - { - nhlog::ui()->debug("WebRTC: call id: {} - sending offer", callid_); - emit newMessage(roomid_, CallInvite{callid_, sdp, 0, timeoutms_}); - emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); + connect( + &session_, + &WebRTCSession::offerCreated, + this, + [this](const std::string &sdp, const std::vector<CallCandidates::Candidate> &candidates) { + nhlog::ui()->debug("WebRTC: call id: {} - sending offer", callid_); + emit newMessage(roomid_, CallInvite{callid_, sdp, 0, timeoutms_}); + emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); + QTimer::singleShot(timeoutms_, this, [this]() { + if (session_.state() == WebRTCSession::State::OFFERSENT) { + hangUp(CallHangUp::Reason::InviteTimeOut); + emit ChatPage::instance()->showNotification( + "The remote side failed to pick up."); + } + }); + }); - QTimer::singleShot(timeoutms_, this, [this](){ - if (session_.state() == WebRTCSession::State::OFFERSENT) { - hangUp(CallHangUp::Reason::InviteTimeOut); - emit ChatPage::instance()->showNotification("The remote side failed to pick up."); - } - }); - }); - - connect(&session_, &WebRTCSession::answerCreated, this, - [this](const std::string &sdp, - const std::vector<CallCandidates::Candidate> &candidates) - { - nhlog::ui()->debug("WebRTC: call id: {} - sending answer", callid_); - emit newMessage(roomid_, CallAnswer{callid_, sdp, 0}); - emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); - }); + connect( + &session_, + &WebRTCSession::answerCreated, + this, + [this](const std::string &sdp, const std::vector<CallCandidates::Candidate> &candidates) { + nhlog::ui()->debug("WebRTC: call id: {} - sending answer", callid_); + emit newMessage(roomid_, CallAnswer{callid_, sdp, 0}); + emit newMessage(roomid_, CallCandidates{callid_, candidates, 0}); + }); - connect(&session_, &WebRTCSession::newICECandidate, this, - [this](const CallCandidates::Candidate &candidate) - { - nhlog::ui()->debug("WebRTC: call id: {} - sending ice candidate", callid_); - emit newMessage(roomid_, CallCandidates{callid_, {candidate}, 0}); - }); + connect(&session_, + &WebRTCSession::newICECandidate, + this, + [this](const CallCandidates::Candidate &candidate) { + nhlog::ui()->debug("WebRTC: call id: {} - sending ice candidate", callid_); + emit newMessage(roomid_, CallCandidates{callid_, {candidate}, 0}); + }); - connect(&turnServerTimer_, &QTimer::timeout, this, &CallManager::retrieveTurnServer); + connect(&turnServerTimer_, &QTimer::timeout, this, &CallManager::retrieveTurnServer); - connect(this, &CallManager::turnServerRetrieved, this, - [this](const mtx::responses::TurnServer &res) - { - nhlog::net()->info("TURN server(s) retrieved from homeserver:"); - nhlog::net()->info("username: {}", res.username); - nhlog::net()->info("ttl: {} seconds", res.ttl); - for (const auto &u : res.uris) - nhlog::net()->info("uri: {}", u); + connect(this, + &CallManager::turnServerRetrieved, + this, + [this](const mtx::responses::TurnServer &res) { + nhlog::net()->info("TURN server(s) retrieved from homeserver:"); + nhlog::net()->info("username: {}", res.username); + nhlog::net()->info("ttl: {} seconds", res.ttl); + for (const auto &u : res.uris) + nhlog::net()->info("uri: {}", u); - // Request new credentials close to expiry - // See https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 - turnURIs_ = getTurnURIs(res); - uint32_t ttl = std::max(res.ttl, UINT32_C(3600)); - if (res.ttl < 3600) - nhlog::net()->warn("Setting ttl to 1 hour"); - turnServerTimer_.setInterval(ttl * 1000 * 0.9); - }); + // Request new credentials close to expiry + // See https://tools.ietf.org/html/draft-uberti-behave-turn-rest-00 + turnURIs_ = getTurnURIs(res); + uint32_t ttl = std::max(res.ttl, UINT32_C(3600)); + if (res.ttl < 3600) + nhlog::net()->warn("Setting ttl to 1 hour"); + turnServerTimer_.setInterval(ttl * 1000 * 0.9); + }); - connect(&session_, &WebRTCSession::stateChanged, this, - [this](WebRTCSession::State state) { - if (state == WebRTCSession::State::DISCONNECTED) { - playRingtone("qrc:/media/media/callend.ogg", false); - } - else if (state == WebRTCSession::State::ICEFAILED) { - QString error("Call connection failed."); - if (turnURIs_.empty()) - error += " Your homeserver has no configured TURN server."; - emit ChatPage::instance()->showNotification(error); - hangUp(CallHangUp::Reason::ICEFailed); - } - }); + connect(&session_, &WebRTCSession::stateChanged, this, [this](WebRTCSession::State state) { + switch (state) { + case WebRTCSession::State::DISCONNECTED: + playRingtone("qrc:/media/media/callend.ogg", false); + clear(); + break; + case WebRTCSession::State::ICEFAILED: { + QString error("Call connection failed."); + if (turnURIs_.empty()) + error += " Your homeserver has no configured TURN server."; + emit ChatPage::instance()->showNotification(error); + hangUp(CallHangUp::Reason::ICEFailed); + break; + } + default: + break; + } + }); - connect(&player_, &QMediaPlayer::mediaStatusChanged, this, - [this](QMediaPlayer::MediaStatus status) { - if (status == QMediaPlayer::LoadedMedia) - player_.play(); - }); + connect(&player_, + &QMediaPlayer::mediaStatusChanged, + this, + [this](QMediaPlayer::MediaStatus status) { + if (status == QMediaPlayer::LoadedMedia) + player_.play(); + }); } void CallManager::sendInvite(const QString &roomid) { - if (onActiveCall()) - return; + if (onActiveCall()) + return; - auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); - if (roomInfo.member_count != 2) { - emit ChatPage::instance()->showNotification("Voice calls are limited to 1:1 rooms."); - return; - } + auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); + if (roomInfo.member_count != 2) { + emit ChatPage::instance()->showNotification( + "Voice calls are limited to 1:1 rooms."); + return; + } - std::string errorMessage; - if (!session_.init(&errorMessage)) { - emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); - return; - } + std::string errorMessage; + if (!session_.init(&errorMessage)) { + emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); + return; + } - roomid_ = roomid; - session_.setStunServer(settings_->useStunServer() ? STUN_SERVER : ""); - session_.setTurnServers(turnURIs_); + roomid_ = roomid; + session_.setStunServer(settings_->useStunServer() ? STUN_SERVER : ""); + session_.setTurnServers(turnURIs_); - generateCallID(); - nhlog::ui()->debug("WebRTC: call id: {} - creating invite", callid_); - 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, - 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."); - endCall(); - } + generateCallID(); + nhlog::ui()->debug("WebRTC: call id: {} - creating invite", callid_); + 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, + 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."); + endCall(); + } } namespace { -std::string callHangUpReasonString(CallHangUp::Reason reason) +std::string +callHangUpReasonString(CallHangUp::Reason reason) { - switch (reason) { - case CallHangUp::Reason::ICEFailed: - return "ICE failed"; - case CallHangUp::Reason::InviteTimeOut: - return "Invite time out"; - default: - return "User"; - } + switch (reason) { + case CallHangUp::Reason::ICEFailed: + return "ICE failed"; + case CallHangUp::Reason::InviteTimeOut: + return "Invite time out"; + default: + return "User"; + } } } void CallManager::hangUp(CallHangUp::Reason reason) { - if (!callid_.empty()) { - nhlog::ui()->debug("WebRTC: call id: {} - hanging up ({})", callid_, - callHangUpReasonString(reason)); - emit newMessage(roomid_, CallHangUp{callid_, 0, reason}); - endCall(); - } + if (!callid_.empty()) { + nhlog::ui()->debug( + "WebRTC: call id: {} - hanging up ({})", callid_, callHangUpReasonString(reason)); + emit newMessage(roomid_, CallHangUp{callid_, 0, reason}); + endCall(); + } } bool CallManager::onActiveCall() { - return session_.state() != WebRTCSession::State::DISCONNECTED; + return session_.state() != WebRTCSession::State::DISCONNECTED; } -void CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event) +void +CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event) { - if (handleEvent_<CallInvite>(event) || handleEvent_<CallCandidates>(event) - || handleEvent_<CallAnswer>(event) || handleEvent_<CallHangUp>(event)) - return; + if (handleEvent_<CallInvite>(event) || handleEvent_<CallCandidates>(event) || + handleEvent_<CallAnswer>(event) || handleEvent_<CallHangUp>(event)) + return; } template<typename T> bool CallManager::handleEvent_(const mtx::events::collections::TimelineEvents &event) { - if (std::holds_alternative<RoomEvent<T>>(event)) { - handleEvent(std::get<RoomEvent<T>>(event)); - return true; - } - return false; + if (std::holds_alternative<RoomEvent<T>>(event)) { + handleEvent(std::get<RoomEvent<T>>(event)); + return true; + } + return false; } void CallManager::handleEvent(const RoomEvent<CallInvite> &callInviteEvent) { - const char video[] = "m=video"; - const std::string &sdp = callInviteEvent.content.sdp; - bool isVideo = std::search(sdp.cbegin(), sdp.cend(), std::cbegin(video), std::cend(video) - 1, - [](unsigned char c1, unsigned char c2) {return std::tolower(c1) == std::tolower(c2);}) - != sdp.cend(); - - nhlog::ui()->debug(std::string("WebRTC: call id: {} - incoming ") + (isVideo ? "video" : "voice") + - " CallInvite from {}", callInviteEvent.content.call_id, callInviteEvent.sender); + const char video[] = "m=video"; + const std::string &sdp = callInviteEvent.content.sdp; + bool isVideo = std::search(sdp.cbegin(), + sdp.cend(), + std::cbegin(video), + std::cend(video) - 1, + [](unsigned char c1, unsigned char c2) { + return std::tolower(c1) == std::tolower(c2); + }) != sdp.cend(); - if (callInviteEvent.content.call_id.empty()) - return; + nhlog::ui()->debug(std::string("WebRTC: call id: {} - incoming ") + + (isVideo ? "video" : "voice") + " CallInvite from {}", + callInviteEvent.content.call_id, + callInviteEvent.sender); - if (isVideo) { - emit newMessage(QString::fromStdString(callInviteEvent.room_id), - CallHangUp{callInviteEvent.content.call_id, 0, CallHangUp::Reason::InviteTimeOut}); - return; - } + if (callInviteEvent.content.call_id.empty()) + return; - 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; - } + auto roomInfo = cache::singleRoomInfo(callInviteEvent.room_id); + if (onActiveCall() || roomInfo.member_count != 2 || isVideo) { + emit newMessage(QString::fromStdString(callInviteEvent.room_id), + CallHangUp{callInviteEvent.content.call_id, + 0, + CallHangUp::Reason::InviteTimeOut}); + return; + } - playRingtone("qrc:/media/media/ring.ogg", true); - roomid_ = QString::fromStdString(callInviteEvent.room_id); - callid_ = callInviteEvent.content.call_id; - remoteICECandidates_.clear(); + playRingtone("qrc:/media/media/ring.ogg", true); + roomid_ = QString::fromStdString(callInviteEvent.room_id); + callid_ = callInviteEvent.content.call_id; + remoteICECandidates_.clear(); - 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)); + 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(); - answerInvite(callInviteEvent.content);}); - connect(dialog, &dialogs::AcceptCall::reject, this, - [this](){ - MainWindow::instance()->hideOverlay(); - hangUp();}); - MainWindow::instance()->showSolidOverlayModal(dialog); + 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(); + answerInvite(callInviteEvent.content); + }); + connect(dialog, &dialogs::AcceptCall::reject, this, [this]() { + MainWindow::instance()->hideOverlay(); + hangUp(); + }); + MainWindow::instance()->showSolidOverlayModal(dialog); } void CallManager::answerInvite(const CallInvite &invite) { - stopRingtone(); - std::string errorMessage; - if (!session_.init(&errorMessage)) { - emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); - hangUp(); - return; - } + stopRingtone(); + std::string errorMessage; + if (!session_.init(&errorMessage)) { + emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); + hangUp(); + return; + } - session_.setStunServer(settings_->useStunServer() ? STUN_SERVER : ""); - session_.setTurnServers(turnURIs_); + session_.setStunServer(settings_->useStunServer() ? STUN_SERVER : ""); + session_.setTurnServers(turnURIs_); - if (!session_.acceptOffer(invite.sdp)) { - emit ChatPage::instance()->showNotification("Problem setting up call."); - hangUp(); - return; - } - session_.acceptICECandidates(remoteICECandidates_); - remoteICECandidates_.clear(); + if (!session_.acceptOffer(invite.sdp)) { + emit ChatPage::instance()->showNotification("Problem setting up call."); + hangUp(); + return; + } + session_.acceptICECandidates(remoteICECandidates_); + remoteICECandidates_.clear(); } void CallManager::handleEvent(const RoomEvent<CallCandidates> &callCandidatesEvent) { - if (callCandidatesEvent.sender == utils::localUser().toStdString()) - return; + if (callCandidatesEvent.sender == utils::localUser().toStdString()) + return; - nhlog::ui()->debug("WebRTC: call id: {} - incoming CallCandidates from {}", - callCandidatesEvent.content.call_id, callCandidatesEvent.sender); + nhlog::ui()->debug("WebRTC: call id: {} - incoming CallCandidates from {}", + callCandidatesEvent.content.call_id, + callCandidatesEvent.sender); - if (callid_ == callCandidatesEvent.content.call_id) { - if (onActiveCall()) - session_.acceptICECandidates(callCandidatesEvent.content.candidates); - else { - // CallInvite has been received and we're awaiting localUser to accept or reject the call - for (const auto &c : callCandidatesEvent.content.candidates) - remoteICECandidates_.push_back(c); - } - } + if (callid_ == callCandidatesEvent.content.call_id) { + if (onActiveCall()) + session_.acceptICECandidates(callCandidatesEvent.content.candidates); + else { + // CallInvite has been received and we're awaiting localUser to accept or + // reject the call + for (const auto &c : callCandidatesEvent.content.candidates) + remoteICECandidates_.push_back(c); + } + } } void CallManager::handleEvent(const RoomEvent<CallAnswer> &callAnswerEvent) { - nhlog::ui()->debug("WebRTC: call id: {} - incoming CallAnswer from {}", - callAnswerEvent.content.call_id, callAnswerEvent.sender); + nhlog::ui()->debug("WebRTC: call id: {} - incoming CallAnswer from {}", + callAnswerEvent.content.call_id, + callAnswerEvent.sender); - 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; - } + 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; + } - if (onActiveCall() && callid_ == callAnswerEvent.content.call_id) { - stopRingtone(); - if (!session_.acceptAnswer(callAnswerEvent.content.sdp)) { - emit ChatPage::instance()->showNotification("Problem setting up call."); - hangUp(); - } - } + if (onActiveCall() && callid_ == callAnswerEvent.content.call_id) { + stopRingtone(); + if (!session_.acceptAnswer(callAnswerEvent.content.sdp)) { + emit ChatPage::instance()->showNotification("Problem setting up call."); + hangUp(); + } + } } void CallManager::handleEvent(const RoomEvent<CallHangUp> &callHangUpEvent) { - nhlog::ui()->debug("WebRTC: call id: {} - incoming CallHangUp ({}) from {}", - callHangUpEvent.content.call_id, callHangUpReasonString(callHangUpEvent.content.reason), - callHangUpEvent.sender); + nhlog::ui()->debug("WebRTC: call id: {} - incoming CallHangUp ({}) from {}", + callHangUpEvent.content.call_id, + callHangUpReasonString(callHangUpEvent.content.reason), + callHangUpEvent.sender); - if (callid_ == callHangUpEvent.content.call_id) { - MainWindow::instance()->hideOverlay(); - endCall(); - } + if (callid_ == callHangUpEvent.content.call_id) { + MainWindow::instance()->hideOverlay(); + endCall(); + } } void CallManager::generateCallID() { - using namespace std::chrono; - uint64_t ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); - callid_ = "c" + std::to_string(ms); + using namespace std::chrono; + uint64_t ms = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count(); + callid_ = "c" + std::to_string(ms); +} + +void +CallManager::clear() +{ + roomid_.clear(); + callid_.clear(); + remoteICECandidates_.clear(); } void CallManager::endCall() { - stopRingtone(); - session_.end(); - roomid_.clear(); - callid_.clear(); - remoteICECandidates_.clear(); + stopRingtone(); + clear(); + session_.end(); } void CallManager::refreshTurnServer() { - turnURIs_.clear(); - turnServerTimer_.start(2000); + turnURIs_.clear(); + turnServerTimer_.start(2000); } void CallManager::retrieveTurnServer() { - http::client()->get_turn_server( - [this](const mtx::responses::TurnServer &res, mtx::http::RequestErr err) { - if (err) { - turnServerTimer_.setInterval(5000); - return; - } - emit turnServerRetrieved(res); - }); + http::client()->get_turn_server( + [this](const mtx::responses::TurnServer &res, mtx::http::RequestErr err) { + if (err) { + turnServerTimer_.setInterval(5000); + return; + } + emit turnServerRetrieved(res); + }); } void CallManager::playRingtone(const QString &ringtone, bool repeat) { - static QMediaPlaylist playlist; - playlist.clear(); - playlist.setPlaybackMode(repeat ? QMediaPlaylist::CurrentItemInLoop : QMediaPlaylist::CurrentItemOnce); - playlist.addMedia(QUrl(ringtone)); - player_.setVolume(100); - player_.setPlaylist(&playlist); + static QMediaPlaylist playlist; + playlist.clear(); + playlist.setPlaybackMode(repeat ? QMediaPlaylist::CurrentItemInLoop + : QMediaPlaylist::CurrentItemOnce); + playlist.addMedia(QUrl(ringtone)); + player_.setVolume(100); + player_.setPlaylist(&playlist); } void CallManager::stopRingtone() { - player_.setPlaylist(nullptr); + player_.setPlaylist(nullptr); } namespace { std::vector<std::string> getTurnURIs(const mtx::responses::TurnServer &turnServer) { - // gstreamer expects: turn(s)://username:password@host:port?transport=udp(tcp) - // where username and password are percent-encoded - std::vector<std::string> ret; - for (const auto &uri : turnServer.uris) { - if (auto c = uri.find(':'); c == std::string::npos) { - nhlog::ui()->error("Invalid TURN server uri: {}", uri); - continue; - } - else { - std::string scheme = std::string(uri, 0, c); - if (scheme != "turn" && scheme != "turns") { - nhlog::ui()->error("Invalid TURN server uri: {}", uri); - continue; - } + // gstreamer expects: turn(s)://username:password@host:port?transport=udp(tcp) + // where username and password are percent-encoded + std::vector<std::string> ret; + for (const auto &uri : turnServer.uris) { + if (auto c = uri.find(':'); c == std::string::npos) { + nhlog::ui()->error("Invalid TURN server uri: {}", uri); + continue; + } else { + std::string scheme = std::string(uri, 0, c); + if (scheme != "turn" && scheme != "turns") { + nhlog::ui()->error("Invalid TURN server uri: {}", uri); + continue; + } - QString encodedUri = QString::fromStdString(scheme) + "://" + - QUrl::toPercentEncoding(QString::fromStdString(turnServer.username)) + ":" + - QUrl::toPercentEncoding(QString::fromStdString(turnServer.password)) + "@" + - QString::fromStdString(std::string(uri, ++c)); - ret.push_back(encodedUri.toStdString()); - } - } - return ret; + QString encodedUri = + QString::fromStdString(scheme) + "://" + + QUrl::toPercentEncoding(QString::fromStdString(turnServer.username)) + + ":" + + QUrl::toPercentEncoding(QString::fromStdString(turnServer.password)) + + "@" + QString::fromStdString(std::string(uri, ++c)); + ret.push_back(encodedUri.toStdString()); + } + } + return ret; } } -