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.cpp918
1 files changed, 452 insertions, 466 deletions
diff --git a/src/CallManager.cpp b/src/CallManager.cpp

index 601c9d6b..0f701b0d 100644 --- a/src/CallManager.cpp +++ b/src/CallManager.cpp
@@ -54,206 +54,197 @@ CallManager::CallManager(QObject *parent) , session_(WebRTCSession::instance()) , turnServerTimer_(this) { - 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"}); - std::string callid(callid_); - QTimer::singleShot(timeoutms_, this, [this, callid]() { - if (session_.state() == webrtc::State::OFFERSENT && callid == callid_) { - hangUp(CallHangUp::Reason::InviteTimeOut); - emit ChatPage::instance()->showNotification( - "The remote side failed to pick up."); - } - }); + 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"}); + std::string callid(callid_); + QTimer::singleShot(timeoutms_, this, [this, callid]() { + if (session_.state() == webrtc::State::OFFERSENT && callid == callid_) { + 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::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( + 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); + }); + + connect(&session_, &WebRTCSession::stateChanged, this, [this](webrtc::State state) { + switch (state) { + case webrtc::State::DISCONNECTED: + playRingtone(QUrl("qrc:/media/media/callend.ogg"), false); + clear(); + break; + case webrtc::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; + } + emit newCallState(); + }); - 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( + &CallDevices::instance(), &CallDevices::devicesChanged, this, &CallManager::devicesChanged); - 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(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); - }); - - connect(&session_, &WebRTCSession::stateChanged, this, [this](webrtc::State state) { - switch (state) { - case webrtc::State::DISCONNECTED: - playRingtone(QUrl("qrc:/media/media/callend.ogg"), false); - clear(); - break; - case webrtc::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; - } + connect( + &player_, &QMediaPlayer::mediaStatusChanged, this, [this](QMediaPlayer::MediaStatus status) { + if (status == QMediaPlayer::LoadedMedia) + player_.play(); + }); + + connect(&player_, + QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error), + [this](QMediaPlayer::Error error) { + stopRingtone(); + switch (error) { + case QMediaPlayer::FormatError: + case QMediaPlayer::ResourceError: + nhlog::ui()->error("WebRTC: valid ringtone file not found"); + break; + case QMediaPlayer::AccessDeniedError: + nhlog::ui()->error("WebRTC: access to ringtone file denied"); + break; default: - break; + nhlog::ui()->error("WebRTC: unable to play ringtone"); + break; } - emit newCallState(); - }); - - connect(&CallDevices::instance(), - &CallDevices::devicesChanged, - this, - &CallManager::devicesChanged); - - connect(&player_, - &QMediaPlayer::mediaStatusChanged, - this, - [this](QMediaPlayer::MediaStatus status) { - if (status == QMediaPlayer::LoadedMedia) - player_.play(); - }); - - connect(&player_, - QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error), - [this](QMediaPlayer::Error error) { - stopRingtone(); - switch (error) { - case QMediaPlayer::FormatError: - case QMediaPlayer::ResourceError: - nhlog::ui()->error("WebRTC: valid ringtone file not found"); - break; - case QMediaPlayer::AccessDeniedError: - nhlog::ui()->error("WebRTC: access to ringtone file denied"); - break; - default: - nhlog::ui()->error("WebRTC: unable to play ringtone"); - break; - } - }); + }); } void CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int windowIndex) { - if (isOnCall()) - return; - if (callType == CallType::SCREEN) { - if (!screenShareSupported()) - return; - if (windows_.empty() || windowIndex >= windows_.size()) { - nhlog::ui()->error("WebRTC: window index out of range"); - return; - } - } - - auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); - if (roomInfo.member_count != 2) { - emit ChatPage::instance()->showNotification("Calls are limited to 1:1 rooms."); - return; - } - - std::string errorMessage; - if (!session_.havePlugins(false, &errorMessage) || - ((callType == CallType::VIDEO || callType == CallType::SCREEN) && - !session_.havePlugins(true, &errorMessage))) { - emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); - return; - } - - callType_ = callType; - roomid_ = roomid; - session_.setTurnServers(turnURIs_); - generateCallID(); - std::string strCallType = callType_ == CallType::VOICE - ? "voice" - : (callType_ == CallType::VIDEO ? "video" : "screen"); - nhlog::ui()->debug("WebRTC: call id: {} - creating {} invite", callid_, strCallType); - std::vector<RoomMember> members(cache::getMembers(roomid.toStdString())); - const RoomMember &callee = - members.front().user_id == utils::localUser() ? members.back() : members.front(); - callParty_ = callee.user_id; - callPartyDisplayName_ = - callee.display_name.isEmpty() ? callee.user_id : callee.display_name; - callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); - emit newInviteState(); - playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true); - if (!session_.createOffer( - callType, callType == CallType::SCREEN ? windows_[windowIndex].second : 0)) { - emit ChatPage::instance()->showNotification("Problem setting up call."); - endCall(); + if (isOnCall()) + return; + if (callType == CallType::SCREEN) { + if (!screenShareSupported()) + return; + if (windows_.empty() || windowIndex >= windows_.size()) { + nhlog::ui()->error("WebRTC: window index out of range"); + return; } + } + + auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); + if (roomInfo.member_count != 2) { + emit ChatPage::instance()->showNotification("Calls are limited to 1:1 rooms."); + return; + } + + std::string errorMessage; + if (!session_.havePlugins(false, &errorMessage) || + ((callType == CallType::VIDEO || callType == CallType::SCREEN) && + !session_.havePlugins(true, &errorMessage))) { + emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); + return; + } + + callType_ = callType; + roomid_ = roomid; + session_.setTurnServers(turnURIs_); + generateCallID(); + std::string strCallType = + callType_ == CallType::VOICE ? "voice" : (callType_ == CallType::VIDEO ? "video" : "screen"); + nhlog::ui()->debug("WebRTC: call id: {} - creating {} invite", callid_, strCallType); + std::vector<RoomMember> members(cache::getMembers(roomid.toStdString())); + const RoomMember &callee = + members.front().user_id == utils::localUser() ? members.back() : members.front(); + callParty_ = callee.user_id; + callPartyDisplayName_ = callee.display_name.isEmpty() ? callee.user_id : callee.display_name; + callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); + emit newInviteState(); + playRingtone(QUrl("qrc:/media/media/ringback.ogg"), true); + if (!session_.createOffer(callType, + callType == CallType::SCREEN ? windows_[windowIndex].second : 0)) { + emit ChatPage::instance()->showNotification("Problem setting up call."); + endCall(); + } } namespace { 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(); + } } void CallManager::syncEvent(const mtx::events::collections::TimelineEvents &event) { #ifdef GSTREAMER_AVAILABLE - 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; #else - (void)event; + (void)event; #endif } @@ -261,325 +252,321 @@ 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("WebRTC: call id: {} - incoming {} CallInvite from {}", - callInviteEvent.content.call_id, - (isVideo ? "video" : "voice"), - callInviteEvent.sender); - - if (callInviteEvent.content.call_id.empty()) - return; - - auto roomInfo = cache::singleRoomInfo(callInviteEvent.room_id); - if (isOnCall() || roomInfo.member_count != 2) { - emit newMessage(QString::fromStdString(callInviteEvent.room_id), - CallHangUp{callInviteEvent.content.call_id, - "0", - CallHangUp::Reason::InviteTimeOut}); - return; - } - - const QString &ringtone = ChatPage::instance()->userSettings()->ringtone(); - if (ringtone != "Mute") - playRingtone(ringtone == "Default" ? QUrl("qrc:/media/media/ring.ogg") - : QUrl::fromLocalFile(ringtone), - 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(); - callParty_ = caller.user_id; - callPartyDisplayName_ = - caller.display_name.isEmpty() ? caller.user_id : caller.display_name; - callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); - - haveCallInvite_ = true; - callType_ = isVideo ? CallType::VIDEO : CallType::VOICE; - inviteSDP_ = callInviteEvent.content.sdp; - emit newInviteState(); + 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("WebRTC: call id: {} - incoming {} CallInvite from {}", + callInviteEvent.content.call_id, + (isVideo ? "video" : "voice"), + callInviteEvent.sender); + + if (callInviteEvent.content.call_id.empty()) + return; + + auto roomInfo = cache::singleRoomInfo(callInviteEvent.room_id); + if (isOnCall() || roomInfo.member_count != 2) { + emit newMessage( + QString::fromStdString(callInviteEvent.room_id), + CallHangUp{callInviteEvent.content.call_id, "0", CallHangUp::Reason::InviteTimeOut}); + return; + } + + const QString &ringtone = ChatPage::instance()->userSettings()->ringtone(); + if (ringtone != "Mute") + playRingtone(ringtone == "Default" ? QUrl("qrc:/media/media/ring.ogg") + : QUrl::fromLocalFile(ringtone), + 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(); + callParty_ = caller.user_id; + callPartyDisplayName_ = caller.display_name.isEmpty() ? caller.user_id : caller.display_name; + callPartyAvatarUrl_ = QString::fromStdString(roomInfo.avatar_url); + + haveCallInvite_ = true; + callType_ = isVideo ? CallType::VIDEO : CallType::VOICE; + inviteSDP_ = callInviteEvent.content.sdp; + emit newInviteState(); } void CallManager::acceptInvite() { - if (!haveCallInvite_) - return; - - stopRingtone(); - std::string errorMessage; - if (!session_.havePlugins(false, &errorMessage) || - (callType_ == CallType::VIDEO && !session_.havePlugins(true, &errorMessage))) { - emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); - hangUp(); - return; - } - - session_.setTurnServers(turnURIs_); - if (!session_.acceptOffer(inviteSDP_)) { - emit ChatPage::instance()->showNotification("Problem setting up call."); - hangUp(); - return; - } - session_.acceptICECandidates(remoteICECandidates_); - remoteICECandidates_.clear(); - haveCallInvite_ = false; - emit newInviteState(); + if (!haveCallInvite_) + return; + + stopRingtone(); + std::string errorMessage; + if (!session_.havePlugins(false, &errorMessage) || + (callType_ == CallType::VIDEO && !session_.havePlugins(true, &errorMessage))) { + emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); + hangUp(); + return; + } + + session_.setTurnServers(turnURIs_); + if (!session_.acceptOffer(inviteSDP_)) { + emit ChatPage::instance()->showNotification("Problem setting up call."); + hangUp(); + return; + } + session_.acceptICECandidates(remoteICECandidates_); + remoteICECandidates_.clear(); + haveCallInvite_ = false; + emit newInviteState(); } void CallManager::handleEvent(const RoomEvent<CallCandidates> &callCandidatesEvent) { - if (callCandidatesEvent.sender == utils::localUser().toStdString()) - return; - - nhlog::ui()->debug("WebRTC: call id: {} - incoming CallCandidates from {}", - callCandidatesEvent.content.call_id, - callCandidatesEvent.sender); - - if (callid_ == callCandidatesEvent.content.call_id) { - if (isOnCall()) - 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 (callCandidatesEvent.sender == utils::localUser().toStdString()) + return; + + nhlog::ui()->debug("WebRTC: call id: {} - incoming CallCandidates from {}", + callCandidatesEvent.content.call_id, + callCandidatesEvent.sender); + + if (callid_ == callCandidatesEvent.content.call_id) { + if (isOnCall()) + 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); - - if (callAnswerEvent.sender == utils::localUser().toStdString() && - callid_ == callAnswerEvent.content.call_id) { - if (!isOnCall()) { - emit ChatPage::instance()->showNotification( - "Call answered on another device."); - stopRingtone(); - haveCallInvite_ = false; - emit newInviteState(); - } - return; + nhlog::ui()->debug("WebRTC: call id: {} - incoming CallAnswer from {}", + callAnswerEvent.content.call_id, + callAnswerEvent.sender); + + if (callAnswerEvent.sender == utils::localUser().toStdString() && + callid_ == callAnswerEvent.content.call_id) { + if (!isOnCall()) { + emit ChatPage::instance()->showNotification("Call answered on another device."); + stopRingtone(); + haveCallInvite_ = false; + emit newInviteState(); } + return; + } - if (isOnCall() && callid_ == callAnswerEvent.content.call_id) { - stopRingtone(); - if (!session_.acceptAnswer(callAnswerEvent.content.sdp)) { - emit ChatPage::instance()->showNotification("Problem setting up call."); - hangUp(); - } + if (isOnCall() && 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) - endCall(); + if (callid_ == callHangUpEvent.content.call_id) + endCall(); } void CallManager::toggleMicMute() { - session_.toggleMicMute(); - emit micMuteChanged(); + session_.toggleMicMute(); + emit micMuteChanged(); } bool CallManager::callsSupported() { #ifdef GSTREAMER_AVAILABLE - return true; + return true; #else - return false; + return false; #endif } bool CallManager::screenShareSupported() { - return std::getenv("DISPLAY") && !std::getenv("WAYLAND_DISPLAY"); + return std::getenv("DISPLAY") && !std::getenv("WAYLAND_DISPLAY"); } QStringList CallManager::devices(bool isVideo) const { - QStringList ret; - const QString &defaultDevice = isVideo ? ChatPage::instance()->userSettings()->camera() - : ChatPage::instance()->userSettings()->microphone(); - std::vector<std::string> devices = - CallDevices::instance().names(isVideo, defaultDevice.toStdString()); - ret.reserve(devices.size()); - std::transform(devices.cbegin(), - devices.cend(), - std::back_inserter(ret), - [](const auto &d) { return QString::fromStdString(d); }); - - return ret; + QStringList ret; + const QString &defaultDevice = isVideo ? ChatPage::instance()->userSettings()->camera() + : ChatPage::instance()->userSettings()->microphone(); + std::vector<std::string> devices = + CallDevices::instance().names(isVideo, defaultDevice.toStdString()); + ret.reserve(devices.size()); + std::transform(devices.cbegin(), devices.cend(), std::back_inserter(ret), [](const auto &d) { + return QString::fromStdString(d); + }); + + return ret; } 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(); - callParty_.clear(); - callPartyDisplayName_.clear(); - callPartyAvatarUrl_.clear(); - callid_.clear(); - callType_ = CallType::VOICE; - haveCallInvite_ = false; - emit newInviteState(); - inviteSDP_.clear(); - remoteICECandidates_.clear(); + roomid_.clear(); + callParty_.clear(); + callPartyDisplayName_.clear(); + callPartyAvatarUrl_.clear(); + callid_.clear(); + callType_ = CallType::VOICE; + haveCallInvite_ = false; + emit newInviteState(); + inviteSDP_.clear(); + remoteICECandidates_.clear(); } void CallManager::endCall() { - stopRingtone(); - session_.end(); - clear(); + stopRingtone(); + session_.end(); + clear(); } 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 QUrl &ringtone, bool repeat) { - static QMediaPlaylist playlist; - playlist.clear(); - playlist.setPlaybackMode(repeat ? QMediaPlaylist::CurrentItemInLoop - : QMediaPlaylist::CurrentItemOnce); - playlist.addMedia(ringtone); - player_.setVolume(100); - player_.setPlaylist(&playlist); + static QMediaPlaylist playlist; + playlist.clear(); + playlist.setPlaybackMode(repeat ? QMediaPlaylist::CurrentItemInLoop + : QMediaPlaylist::CurrentItemOnce); + playlist.addMedia(ringtone); + player_.setVolume(100); + player_.setPlaylist(&playlist); } void CallManager::stopRingtone() { - player_.setPlaylist(nullptr); + player_.setPlaylist(nullptr); } QStringList CallManager::windowList() { - windows_.clear(); - windows_.push_back({tr("Entire screen"), 0}); + windows_.clear(); + windows_.push_back({tr("Entire screen"), 0}); #ifdef XCB_AVAILABLE - std::unique_ptr<xcb_connection_t, std::function<void(xcb_connection_t *)>> connection( - xcb_connect(nullptr, nullptr), [](xcb_connection_t *c) { xcb_disconnect(c); }); - if (xcb_connection_has_error(connection.get())) { - nhlog::ui()->error("Failed to connect to X server"); - return {}; - } - - xcb_ewmh_connection_t ewmh; - if (!xcb_ewmh_init_atoms_replies( - &ewmh, xcb_ewmh_init_atoms(connection.get(), &ewmh), nullptr)) { - nhlog::ui()->error("Failed to connect to EWMH server"); - return {}; + std::unique_ptr<xcb_connection_t, std::function<void(xcb_connection_t *)>> connection( + xcb_connect(nullptr, nullptr), [](xcb_connection_t *c) { xcb_disconnect(c); }); + if (xcb_connection_has_error(connection.get())) { + nhlog::ui()->error("Failed to connect to X server"); + return {}; + } + + xcb_ewmh_connection_t ewmh; + if (!xcb_ewmh_init_atoms_replies( + &ewmh, xcb_ewmh_init_atoms(connection.get(), &ewmh), nullptr)) { + nhlog::ui()->error("Failed to connect to EWMH server"); + return {}; + } + std::unique_ptr<xcb_ewmh_connection_t, std::function<void(xcb_ewmh_connection_t *)>> + ewmhconnection(&ewmh, [](xcb_ewmh_connection_t *c) { xcb_ewmh_connection_wipe(c); }); + + for (int i = 0; i < ewmh.nb_screens; i++) { + xcb_ewmh_get_windows_reply_t clients; + if (!xcb_ewmh_get_client_list_reply( + &ewmh, xcb_ewmh_get_client_list(&ewmh, i), &clients, nullptr)) { + nhlog::ui()->error("Failed to request window list"); + return {}; } - std::unique_ptr<xcb_ewmh_connection_t, std::function<void(xcb_ewmh_connection_t *)>> - ewmhconnection(&ewmh, [](xcb_ewmh_connection_t *c) { xcb_ewmh_connection_wipe(c); }); - - for (int i = 0; i < ewmh.nb_screens; i++) { - xcb_ewmh_get_windows_reply_t clients; - if (!xcb_ewmh_get_client_list_reply( - &ewmh, xcb_ewmh_get_client_list(&ewmh, i), &clients, nullptr)) { - nhlog::ui()->error("Failed to request window list"); - return {}; - } - for (uint32_t w = 0; w < clients.windows_len; w++) { - xcb_window_t window = clients.windows[w]; + for (uint32_t w = 0; w < clients.windows_len; w++) { + xcb_window_t window = clients.windows[w]; - std::string name; - xcb_ewmh_get_utf8_strings_reply_t data; - auto getName = [](xcb_ewmh_get_utf8_strings_reply_t *r) { - std::string name(r->strings, r->strings_len); - xcb_ewmh_get_utf8_strings_reply_wipe(r); - return name; - }; + std::string name; + xcb_ewmh_get_utf8_strings_reply_t data; + auto getName = [](xcb_ewmh_get_utf8_strings_reply_t *r) { + std::string name(r->strings, r->strings_len); + xcb_ewmh_get_utf8_strings_reply_wipe(r); + return name; + }; - xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(&ewmh, window); - if (xcb_ewmh_get_wm_name_reply(&ewmh, cookie, &data, nullptr)) - name = getName(&data); + xcb_get_property_cookie_t cookie = xcb_ewmh_get_wm_name(&ewmh, window); + if (xcb_ewmh_get_wm_name_reply(&ewmh, cookie, &data, nullptr)) + name = getName(&data); - cookie = xcb_ewmh_get_wm_visible_name(&ewmh, window); - if (xcb_ewmh_get_wm_visible_name_reply(&ewmh, cookie, &data, nullptr)) - name = getName(&data); + cookie = xcb_ewmh_get_wm_visible_name(&ewmh, window); + if (xcb_ewmh_get_wm_visible_name_reply(&ewmh, cookie, &data, nullptr)) + name = getName(&data); - windows_.push_back({QString::fromStdString(name), window}); - } - xcb_ewmh_get_windows_reply_wipe(&clients); + windows_.push_back({QString::fromStdString(name), window}); } + xcb_ewmh_get_windows_reply_wipe(&clients); + } #endif - QStringList ret; - ret.reserve(windows_.size()); - for (const auto &w : windows_) - ret.append(w.first); + QStringList ret; + ret.reserve(windows_.size()); + for (const auto &w : windows_) + ret.append(w.first); - return ret; + return ret; } #ifdef GSTREAMER_AVAILABLE @@ -591,22 +578,22 @@ unsigned int busWatchId_ = 0; gboolean newBusMessage(GstBus *bus G_GNUC_UNUSED, GstMessage *msg, gpointer G_GNUC_UNUSED) { - switch (GST_MESSAGE_TYPE(msg)) { - case GST_MESSAGE_EOS: - if (pipe_) { - gst_element_set_state(GST_ELEMENT(pipe_), GST_STATE_NULL); - gst_object_unref(pipe_); - pipe_ = nullptr; - } - if (busWatchId_) { - g_source_remove(busWatchId_); - busWatchId_ = 0; - } - break; - default: - break; + switch (GST_MESSAGE_TYPE(msg)) { + case GST_MESSAGE_EOS: + if (pipe_) { + gst_element_set_state(GST_ELEMENT(pipe_), GST_STATE_NULL); + gst_object_unref(pipe_); + pipe_ = nullptr; + } + if (busWatchId_) { + g_source_remove(busWatchId_); + busWatchId_ = 0; } - return TRUE; + break; + default: + break; + } + return TRUE; } } #endif @@ -615,50 +602,50 @@ void CallManager::previewWindow(unsigned int index) const { #ifdef GSTREAMER_AVAILABLE - if (windows_.empty() || index >= windows_.size() || !gst_is_initialized()) - return; - - GstElement *ximagesrc = gst_element_factory_make("ximagesrc", nullptr); - if (!ximagesrc) { - nhlog::ui()->error("Failed to create ximagesrc"); - return; - } - GstElement *videoconvert = gst_element_factory_make("videoconvert", nullptr); - GstElement *videoscale = gst_element_factory_make("videoscale", nullptr); - GstElement *capsfilter = gst_element_factory_make("capsfilter", nullptr); - GstElement *ximagesink = gst_element_factory_make("ximagesink", nullptr); - - g_object_set(ximagesrc, "use-damage", FALSE, nullptr); - g_object_set(ximagesrc, "show-pointer", FALSE, nullptr); - g_object_set(ximagesrc, "xid", windows_[index].second, nullptr); - - GstCaps *caps = gst_caps_new_simple( - "video/x-raw", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, nullptr); - g_object_set(capsfilter, "caps", caps, nullptr); - gst_caps_unref(caps); - - pipe_ = gst_pipeline_new(nullptr); - gst_bin_add_many( - GST_BIN(pipe_), ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr); - if (!gst_element_link_many( - ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr)) { - nhlog::ui()->error("Failed to link preview window elements"); - gst_object_unref(pipe_); - pipe_ = nullptr; - return; - } - if (gst_element_set_state(pipe_, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { - nhlog::ui()->error("Unable to start preview pipeline"); - gst_object_unref(pipe_); - pipe_ = nullptr; - return; - } - - GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_)); - busWatchId_ = gst_bus_add_watch(bus, newBusMessage, nullptr); - gst_object_unref(bus); + if (windows_.empty() || index >= windows_.size() || !gst_is_initialized()) + return; + + GstElement *ximagesrc = gst_element_factory_make("ximagesrc", nullptr); + if (!ximagesrc) { + nhlog::ui()->error("Failed to create ximagesrc"); + return; + } + GstElement *videoconvert = gst_element_factory_make("videoconvert", nullptr); + GstElement *videoscale = gst_element_factory_make("videoscale", nullptr); + GstElement *capsfilter = gst_element_factory_make("capsfilter", nullptr); + GstElement *ximagesink = gst_element_factory_make("ximagesink", nullptr); + + g_object_set(ximagesrc, "use-damage", FALSE, nullptr); + g_object_set(ximagesrc, "show-pointer", FALSE, nullptr); + g_object_set(ximagesrc, "xid", windows_[index].second, nullptr); + + GstCaps *caps = gst_caps_new_simple( + "video/x-raw", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, nullptr); + g_object_set(capsfilter, "caps", caps, nullptr); + gst_caps_unref(caps); + + pipe_ = gst_pipeline_new(nullptr); + gst_bin_add_many( + GST_BIN(pipe_), ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr); + if (!gst_element_link_many( + ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr)) { + nhlog::ui()->error("Failed to link preview window elements"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + if (gst_element_set_state(pipe_, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + nhlog::ui()->error("Unable to start preview pipeline"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + + GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(pipe_)); + busWatchId_ = gst_bus_add_watch(bus, newBusMessage, nullptr); + gst_object_unref(bus); #else - (void)index; + (void)index; #endif } @@ -666,29 +653,28 @@ 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; - } - - 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()); - } + // 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; + } + return ret; } }