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

index 02fc3100..4471a0c2 100644 --- a/src/voip/CallManager.cpp +++ b/src/voip/CallManager.cpp
@@ -9,6 +9,7 @@ #include <cstdlib> #include <memory> +#include <QGuiApplication> #include <QMediaPlaylist> #include <QUrl> @@ -22,6 +23,8 @@ #include "Utils.h" #include "mtx/responses/turn_server.hpp" +#include "voip/ScreenCastPortal.h" +#include "voip/WebRTCSession.h" /* * Select Answer when one instance of the client supports v0 @@ -47,6 +50,7 @@ using namespace mtx::events; using namespace mtx::events::voip; using webrtc::CallType; +using webrtc::ScreenShareType; //! Session Description Object typedef RTCSessionDescriptionInit SDO; @@ -64,6 +68,12 @@ CallManager::CallManager(QObject *parent) qRegisterMetaType<mtx::events::voip::CallCandidates::Candidate>(); qRegisterMetaType<mtx::responses::TurnServer>(); + if (screenShareX11Available()) { + screenShareType_ = ScreenShareType::X11; + } else { + screenShareType_ = ScreenShareType::XDP; + } + connect( &session_, &WebRTCSession::offerCreated, @@ -176,6 +186,13 @@ CallManager::CallManager(QObject *parent) break; } }); + +#ifdef GSTREAMER_AVAILABLE + connect(&ScreenCastPortal::instance(), + &ScreenCastPortal::readyChanged, + this, + &CallManager::screenShareChanged); +#endif } void @@ -191,8 +208,10 @@ CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int w auto roomInfo = cache::singleRoomInfo(roomid.toStdString()); std::string errorMessage; - if (!session_.havePlugins( - callType != CallType::VOICE, callType == CallType::SCREEN, &errorMessage)) { + if (!session_.havePlugins(callType != CallType::VOICE, + callType == CallType::SCREEN, + screenShareType_, + &errorMessage)) { emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); return; } @@ -212,14 +231,22 @@ CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int w return; } +#ifdef GSTREAMER_AVAILABLE if (callType == CallType::SCREEN) { - if (!screenShareSupported()) - return; - if (windows_.empty() || windowIndex >= windows_.size()) { - nhlog::ui()->error("WebRTC: window index out of range"); - return; + if (screenShareType_ == ScreenShareType::X11) { + if (windows_.empty() || windowIndex >= windows_.size()) { + nhlog::ui()->error("WebRTC: window index out of range"); + return; + } + } else { + ScreenCastPortal &sc_portal = ScreenCastPortal::instance(); + if (sc_portal.getStream() == nullptr) { + nhlog::ui()->error("xdg-desktop-portal stream not started"); + return; + } } } +#endif if (haveCallInvite_) { nhlog::ui()->debug( @@ -255,8 +282,12 @@ CallManager::sendInvite(const QString &roomid, CallType callType, unsigned int w invitee_ = callParty_.toStdString(); emit newInviteState(); playRingtone(QUrl(QStringLiteral("qrc:/media/media/ringback.ogg")), true); - if (!session_.createOffer(callType, - callType == CallType::SCREEN ? windows_[windowIndex].second : 0)) { + + uint32_t shareWindowId = + callType == CallType::SCREEN && screenShareType_ == ScreenShareType::X11 + ? windows_[windowIndex].second + : 0; + if (!session_.createOffer(callType, screenShareType_, shareWindowId)) { emit ChatPage::instance()->showNotification(QStringLiteral("Problem setting up call.")); endCall(); } @@ -466,8 +497,10 @@ CallManager::acceptInvite() stopRingtone(); std::string errorMessage; - if (!session_.havePlugins( - callType_ != CallType::VOICE, callType_ == CallType::SCREEN, &errorMessage)) { + if (!session_.havePlugins(callType_ != CallType::VOICE, + callType_ == CallType::SCREEN, + screenShareType_, + &errorMessage)) { emit ChatPage::instance()->showNotification(QString::fromStdString(errorMessage)); hangUp(CallHangUp::Reason::UserMediaFailed); return; @@ -713,9 +746,13 @@ CallManager::callsSupported() } bool -CallManager::screenShareSupported() +CallManager::screenShareX11Available() { - return std::getenv("DISPLAY") && !std::getenv("WAYLAND_DISPLAY"); +#ifdef GSTREAMER_AVAILABLE + return std::getenv("DISPLAY"); +#else + return false; +#endif } QStringList @@ -746,6 +783,7 @@ CallManager::generateCallID() void CallManager::clear(bool endAllCalls) { + closeScreenShare(); roomid_.clear(); callParty_.clear(); callPartyDisplayName_.clear(); @@ -810,9 +848,27 @@ CallManager::stopRingtone() player_.setPlaylist(nullptr); } +bool +CallManager::screenShareReady() const +{ +#ifdef GSTREAMER_AVAILABLE + if (screenShareType_ == ScreenShareType::X11) { + return true; + } else { + return ScreenCastPortal::instance().ready(); + } +#else + return false; +#endif +} + QStringList CallManager::windowList() { + if (!screenShareX11Available()) { + return {}; + } + windows_.clear(); windows_.push_back({tr("Entire screen"), 0}); @@ -880,26 +936,52 @@ namespace { GstElement *pipe_ = nullptr; unsigned int busWatchId_ = 0; +void +close_preview_stream() +{ + 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; + } +} + 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; - } + close_preview_stream(); + break; + case GST_MESSAGE_ERROR: { + GError *err = nullptr; + gchar *dbg_info = nullptr; + gst_message_parse_error(msg, &err, &dbg_info); + nhlog::ui()->error("GST error: {}", dbg_info); + g_error_free(err); + g_free(dbg_info); + close_preview_stream(); break; + } default: break; } return TRUE; } + +static GstElement * +make_preview_sink() +{ + if (QGuiApplication::platformName() == QStringLiteral("wayland")) { + return gst_element_factory_make("waylandsink", nullptr); + } else { + return gst_element_factory_make("ximagesink", nullptr); + } +} } #endif @@ -907,38 +989,81 @@ void CallManager::previewWindow(unsigned int index) const { #ifdef GSTREAMER_AVAILABLE - if (windows_.empty() || index >= windows_.size() || !gst_is_initialized()) + if (!gst_is_initialized()) return; - GstElement *ximagesrc = gst_element_factory_make("ximagesrc", nullptr); - if (!ximagesrc) { - nhlog::ui()->error("Failed to create ximagesrc"); + if (pipe_ != nullptr) { + nhlog::ui()->warn("Preview already started"); return; } + + if (screenShareType_ == ScreenShareType::X11 && + (!screenShareX11Available() || windows_.empty() || index >= windows_.size())) { + nhlog::ui()->error("X11 screencast not available"); + return; + } + + auto settings = ChatPage::instance()->userSettings(); + + pipe_ = gst_pipeline_new(nullptr); + 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); + GstElement *preview_sink = make_preview_sink(); + GstElement *videorate = gst_element_factory_make("videorate", 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); + gst_bin_add_many( + GST_BIN(pipe_), videorate, videoconvert, videoscale, capsfilter, preview_sink, nullptr); GstCaps *caps = gst_caps_new_simple( - "video/x-raw", "width", G_TYPE_INT, 480, "height", G_TYPE_INT, 360, nullptr); + "video/x-raw", "framerate", GST_TYPE_FRACTION, settings->screenShareFrameRate(), 1, 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); + GstElement *screencastsrc = nullptr; + if (screenShareType_ == ScreenShareType::X11) { + GstElement *ximagesrc = gst_element_factory_make("ximagesrc", nullptr); + if (!ximagesrc) { + nhlog::ui()->error("Failed to create ximagesrc"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + g_object_set(ximagesrc, "use-damage", FALSE, nullptr); + g_object_set(ximagesrc, "xid", windows_[index].second, nullptr); + g_object_set(ximagesrc, "show-pointer", !settings->screenShareHideCursor(), nullptr); + g_object_set(ximagesrc, "do-timestamp", (gboolean)1, nullptr); + + gst_bin_add(GST_BIN(pipe_), ximagesrc); + screencastsrc = ximagesrc; + } else { + ScreenCastPortal &sc_portal = ScreenCastPortal::instance(); + const ScreenCastPortal::Stream *stream = sc_portal.getStream(); + if (stream == nullptr) { + nhlog::ui()->error("xdg-desktop-portal stream not started"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return; + } + GstElement *pipewiresrc = gst_element_factory_make("pipewiresrc", nullptr); + g_object_set(pipewiresrc, "fd", (gint)stream->fd, nullptr); + std::string path = std::to_string(stream->nodeId); + g_object_set(pipewiresrc, "path", path.c_str(), nullptr); + g_object_set(pipewiresrc, "do-timestamp", (gboolean)1, nullptr); + + gst_bin_add(GST_BIN(pipe_), pipewiresrc); + screencastsrc = pipewiresrc; + } + if (!gst_element_link_many( - ximagesrc, videoconvert, videoscale, capsfilter, ximagesink, nullptr)) { + screencastsrc, videorate, videoconvert, videoscale, capsfilter, preview_sink, 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_); @@ -954,6 +1079,40 @@ CallManager::previewWindow(unsigned int index) const #endif } +void +CallManager::setupScreenShareXDP() +{ +#ifdef GSTREAMER_AVAILABLE + ScreenCastPortal &sc_portal = ScreenCastPortal::instance(); + sc_portal.init(); + screenShareType_ = ScreenShareType::XDP; +#endif +} + +void +CallManager::setScreenShareType(webrtc::ScreenShareType screenShareType) +{ +#ifdef GSTREAMER_AVAILABLE + closeScreenShare(); + screenShareType_ = screenShareType; + emit screenShareChanged(); +#else + (void)screenShareType; +#endif +} + +void +CallManager::closeScreenShare() +{ +#ifdef GSTREAMER_AVAILABLE + close_preview_stream(); + if (!isOnCall()) { + ScreenCastPortal &sc_portal = ScreenCastPortal::instance(); + sc_portal.close(); + } +#endif +} + namespace { std::vector<std::string> getTurnURIs(const mtx::responses::TurnServer &turnServer)