diff options
author | checkraisefold <devsmash1@gmail.com> | 2024-05-10 12:22:58 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-10 15:22:58 -0400 |
commit | e7d28b96da929754d3f65f78ab88db76e6ca98b7 (patch) | |
tree | cfdd3769b65e4bca80f27f8d24dcb33fb5f8d3d7 /src/voip/WebRTCSession.cpp | |
parent | Update flat-manager-client (diff) | |
download | nheko-e7d28b96da929754d3f65f78ab88db76e6ca98b7.tar.xz |
Windows screenshare/video call support, general call improvements (#1725)
* Initial support for d3d11 qml video item * Windows screenshare support, D3D11 * misc fixes * further window visibility checks * preview updates * fix qml preview * fix compositor * add libnice plugin dep * re-run formatter * final formatter fix [skip ci] * fix tumbleweed build --------- Co-authored-by: Joseph Donofry <rubberduckie3554@gmail.com>
Diffstat (limited to 'src/voip/WebRTCSession.cpp')
-rw-r--r-- | src/voip/WebRTCSession.cpp | 191 |
1 files changed, 147 insertions, 44 deletions
diff --git a/src/voip/WebRTCSession.cpp b/src/voip/WebRTCSession.cpp index a10e24ef..ba1d5424 100644 --- a/src/voip/WebRTCSession.cpp +++ b/src/voip/WebRTCSession.cpp @@ -23,6 +23,7 @@ #include "voip/ScreenCastPortal.h" #ifdef GSTREAMER_AVAILABLE +#include "MainWindow.h" extern "C" { #include "gst/gl/gstgldisplay.h" @@ -330,33 +331,66 @@ GstElement * newVideoSinkChain(GstElement *pipe) { // use compositor for now; acceleration needs investigation - GstElement *queue = gst_element_factory_make("queue", nullptr); - GstElement *compositor = gst_element_factory_make("compositor", "compositor"); - GstElement *glupload = gst_element_factory_make("glupload", nullptr); - GstElement *qmlglsink = gst_element_factory_make("qml6glsink", nullptr); - GstElement *glsinkbin = gst_element_factory_make("glsinkbin", nullptr); + GstElement *queue = gst_element_factory_make("queue", nullptr); + + auto graphicsApi = MainWindow::instance()->graphicsApi(); + GstElement *compositor = gst_element_factory_make( + graphicsApi == QSGRendererInterface::OpenGL ? "compositor" : "d3d11compositor", "compositor"); g_object_set(compositor, "background", 1, nullptr); - g_object_set(qmlglsink, "widget", WebRTCSession::instance().getVideoItem(), nullptr); - g_object_set(glsinkbin, "sink", qmlglsink, nullptr); - gst_bin_add_many(GST_BIN(pipe), queue, compositor, glupload, glsinkbin, nullptr); - gst_element_link_many(queue, compositor, glupload, glsinkbin, nullptr); - gst_element_sync_state_with_parent(queue); - gst_element_sync_state_with_parent(compositor); - gst_element_sync_state_with_parent(glupload); - gst_element_sync_state_with_parent(glsinkbin); - - // to propagate context (hopefully) - gst_element_set_state(qmlglsink, GST_STATE_READY); - - // Workaround: On wayland, when egl is used, gstreamer might terminate the display connection. - // Prevent that by "leaking" a reference to the display. See - // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3743 - if (QGuiApplication::platformName() == QStringLiteral("wayland")) { - auto context = gst_element_get_context(qmlglsink, "gst.gl.GLDisplay"); - if (context) { - GstGLDisplay *display; - gst_context_get_gl_display(context, &display); + switch (graphicsApi) { + case QSGRendererInterface::OpenGL: { + GstElement *glupload = gst_element_factory_make("glupload", nullptr); + GstElement *glcolorconvert = gst_element_factory_make("glcolorconvert", nullptr); + GstElement *qmlglsink = gst_element_factory_make("qml6glsink", nullptr); + GstElement *glsinkbin = gst_element_factory_make("glsinkbin", nullptr); + + g_object_set(qmlglsink, "widget", WebRTCSession::instance().getVideoItem(), nullptr); + g_object_set(glsinkbin, "sink", qmlglsink, nullptr); + gst_bin_add_many( + GST_BIN(pipe), queue, compositor, glupload, glcolorconvert, glsinkbin, nullptr); + gst_element_link_many(queue, compositor, glupload, glcolorconvert, glsinkbin, nullptr); + + gst_element_sync_state_with_parent(queue); + gst_element_sync_state_with_parent(compositor); + gst_element_sync_state_with_parent(glupload); + gst_element_sync_state_with_parent(glcolorconvert); + gst_element_sync_state_with_parent(glsinkbin); + + // to propagate context (hopefully) + gst_element_set_state(qmlglsink, GST_STATE_READY); + + // Workaround: On wayland, when egl is used, gstreamer might terminate the display + // connection. Prevent that by "leaking" a reference to the display. See + // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3743 + if (QGuiApplication::platformName() == QStringLiteral("wayland")) { + auto context = gst_element_get_context(qmlglsink, "gst.gl.GLDisplay"); + if (context) { + GstGLDisplay *display; + gst_context_get_gl_display(context, &display); + } } + } break; + case QSGRendererInterface::Direct3D11: { + GstElement *d3d11upload = gst_element_factory_make("d3d11upload", nullptr); + GstElement *d3d11colorconvert = gst_element_factory_make("d3d11colorconvert", nullptr); + GstElement *qmld3d11sink = gst_element_factory_make("qml6d3d11sink", nullptr); + + g_object_set(qmld3d11sink, "widget", WebRTCSession::instance().getVideoItem(), nullptr); + gst_bin_add_many( + GST_BIN(pipe), queue, d3d11upload, compositor, d3d11colorconvert, qmld3d11sink, nullptr); + gst_element_link_many( + queue, d3d11upload, compositor, d3d11colorconvert, qmld3d11sink, nullptr); + + gst_element_sync_state_with_parent(queue); + gst_element_sync_state_with_parent(compositor); + gst_element_sync_state_with_parent(d3d11upload); + gst_element_sync_state_with_parent(d3d11colorconvert); + + // to propagate context (hopefully) + gst_element_set_state(qmld3d11sink, GST_STATE_READY); + } break; + default: + break; } return queue; @@ -606,20 +640,20 @@ WebRTCSession::havePlugins(bool isVideo, if (!initialised_ && !init(errorMessage)) return false; - static constexpr std::initializer_list<const char *> audio_elements = { - "audioconvert", - "audioresample", - "autoaudiosink", - "capsfilter", - "decodebin", - "opusenc", - "queue", - "rtpopuspay", - "volume", - "webrtcbin", - }; - - static constexpr std::initializer_list<const char *> video_elements = { + static constexpr std::initializer_list<const char *> audio_elements = {"audioconvert", + "audioresample", + "autoaudiosink", + "capsfilter", + "decodebin", + "opusenc", + "queue", + "rtpopuspay", + "volume", + "webrtcbin", + "nicesrc", + "nicesink"}; + + static constexpr std::initializer_list<const char *> gl_video_elements = { "compositor", "glsinkbin", "glupload", @@ -631,6 +665,19 @@ WebRTCSession::havePlugins(bool isVideo, "vp8enc", }; + static constexpr std::initializer_list<const char *> d3d11_video_elements = { + "compositor", + "d3d11colorconvert", + "d3d11videosink", + "d3d11upload", + "qml6d3d11sink", + "rtpvp8pay", + "tee", + "videoconvert", + "videoscale", + "vp8enc", + }; + std::string strError("Missing GStreamer elements: "); GstRegistry *registry = gst_registry_get(); @@ -654,8 +701,19 @@ WebRTCSession::havePlugins(bool isVideo, haveVoicePlugins_ = check_plugins(audio_elements); // check both elements at once - if (isVideo) - haveVideoPlugins_ = check_plugins(video_elements); + if (isVideo) { + switch (MainWindow::instance()->graphicsApi()) { + case QSGRendererInterface::OpenGL: + haveVideoPlugins_ = check_plugins(gl_video_elements); + break; + case QSGRendererInterface::Direct3D11: + haveVideoPlugins_ = check_plugins(d3d11_video_elements); + break; + default: + haveVideoPlugins_ = false; + break; + } + } bool haveScreensharePlugins = false; if (isScreenshare) { @@ -663,6 +721,8 @@ WebRTCSession::havePlugins(bool isVideo, if (haveScreensharePlugins) { if (QGuiApplication::platformName() == QStringLiteral("wayland")) { haveScreensharePlugins = check_plugins({"waylandsink"}); + } else if (QGuiApplication::platformName() == QStringLiteral("windows")) { + haveScreensharePlugins = check_plugins({"d3d11videosink"}); } else { haveScreensharePlugins = check_plugins({"ximagesink"}); } @@ -670,6 +730,9 @@ WebRTCSession::havePlugins(bool isVideo, if (haveScreensharePlugins) { if (screenShareType == ScreenShareType::X11) { haveScreensharePlugins = check_plugins({"ximagesrc"}); + } else if (screenShareType == ScreenShareType::D3D11) { + haveScreensharePlugins = + check_plugins({"d3d11screencapturesrc", "d3d11download", "d3d11convert"}); } else { haveScreensharePlugins = check_plugins({"pipewiresrc"}); } @@ -685,9 +748,19 @@ WebRTCSession::havePlugins(bool isVideo, } if (isVideo || isScreenshare) { - // load qmlglsink to register GStreamer's GstGLVideoItem QML type - GstElement *qmlglsink = gst_element_factory_make("qml6glsink", nullptr); - gst_object_unref(qmlglsink); + switch (MainWindow::instance()->graphicsApi()) { + case QSGRendererInterface::OpenGL: { + // load qmlglsink to register GStreamer's GstGLVideoItem QML type + GstElement *qmlglsink = gst_element_factory_make("qml6glsink", nullptr); + gst_object_unref(qmlglsink); + } break; + case QSGRendererInterface::Direct3D11: { + GstElement *qmld3d11sink = gst_element_factory_make("qml6d3d11sink", nullptr); + gst_object_unref(qmld3d11sink); + } break; + default: + break; + } } return true; } @@ -1027,6 +1100,36 @@ WebRTCSession::addVideoPipeline(int vp8PayloadType) gst_bin_add(GST_BIN(pipe_), ximagesrc); screencastsrc = ximagesrc; + } else if (screenShareType_ == ScreenShareType::D3D11) { + GstElement *d3d11screensrc = + gst_element_factory_make("d3d11screencapturesrc", "screenshare"); + if (!d3d11screensrc) { + nhlog::ui()->error("WebRTC: failed to create d3d11screencapturesrc"); + gst_object_unref(pipe_); + pipe_ = nullptr; + return false; + } + g_object_set( + d3d11screensrc, "window-handle", static_cast<guint64>(shareWindowId_), nullptr); + g_object_set( + d3d11screensrc, "show-cursor", !settings->screenShareHideCursor(), nullptr); + g_object_set(d3d11screensrc, "do-timestamp", (gboolean)1, nullptr); + gst_bin_add(GST_BIN(pipe_), d3d11screensrc); + + GstElement *d3d11convert = gst_element_factory_make("d3d11convert", nullptr); + gst_bin_add(GST_BIN(pipe_), d3d11convert); + if (!gst_element_link(d3d11screensrc, d3d11convert)) { + nhlog::ui()->error("WebRTC: failed to link d3d11screencapturesrc -> d3d11convert"); + return false; + } + + GstElement *d3d11download = gst_element_factory_make("d3d11download", nullptr); + gst_bin_add(GST_BIN(pipe_), d3d11download); + if (!gst_element_link(d3d11convert, d3d11download)) { + nhlog::ui()->error("WebRTC: failed to link d3d11convert -> d3d11download"); + return false; + } + screencastsrc = d3d11download; } else { ScreenCastPortal &sc_portal = ScreenCastPortal::instance(); GstElement *pipewiresrc = gst_element_factory_make("pipewiresrc", "screenshare"); |