MaartenBaert / ssr

SimpleScreenRecorder, a screen recorder for Linux
http://www.maartenbaert.be/simplescreenrecorder/
GNU General Public License v3.0
2.49k stars 288 forks source link

SSR fails to record QtWebEngine application on ubuntu 22.04 #1013

Open cout opened 1 year ago

cout commented 1 year ago

I have a simple python application using PyQt5 (version 5.15.6):

app = QtWidgets.QApplication(sys.argv)
webview = QtWebEngineWidgets.QWebEngineView()
webview.load(QtCore.QUrl('http://google.com/'))
layout = QtWidgets.QVBoxLayout()
layout.addWidget(webview)
widget = QtWidgets.QWidget()
widget.setLayout(layout)
widget.show()
app.exec()

I can enable "Graphics API Visual Indicator" in nvidia-settings and see the FPS indicating that there is a GL context.

I can run the program inside gdb and see it hit the breakpoint in glXSwapBuffers.

When I run this program using ssr-glinject, I see the directory created in /dev/shm, but it is empty. Other programs that use opengl (using glfw instead of Qt) capture just fine.

I am running under X11, not Wayland.

The above is a simplified version of a program that used to work with ssr-glinject under Ubuntu 20.04. AFAICT on 20.04 I had built from git at commit c934ae. Building from the same commit on 22.04, ssr-glinject segfaults.

I have tried building both with and without 32-bit glinject and get the same result either way.

It is possible this is related to #843, since QtWebEngine uses parts of chromium. Following the recommendations there, I set both QTWEBENGINE_CHROMIUM_FLAGS="--no-sandbox" and QTWEBENGINE_GL_TYPE=desktop but ssr-glinject still does not capture frames from the window.

To verify this is not a problem with QtWebEngine, I did try a simple PyQt5 gui that does not use QtWebEngine:

import sys
from PyQt5 import QtWidgets
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot

app = QtWidgets.QApplication(sys.argv)
widget = QtWidgets.QWidget()
widget.setGeometry(50,50,320,200)
button = QtWidgets.QPushButton('0', widget)
button.clicked.connect(lambda: button.setText(str(int(button.text()) + 1)))
layout = QtWidgets.QVBoxLayout(widget)
layout.addWidget(button)
widget.show()
app.exec()

ssr-glinject also does not capture from this window, but it is no surprise, because I do not think it is using opengl (I see no FPS indicator when I enable "Graphics API Visual Indicator" in nvidia-settings). This is despite seeing that Qt is using gl based on debug logging when I set QT_LOGGING_RULES="*.debug=true;qt.text.font.db=false;qt.qpa.events=false":

qt.qpa.gl: Choosing xcb gl-integration based on following priority
 ("xcb_glx", "xcb_egl")
qt.qpa.gl: Xcb GLX gl-integration created
qt.qpa.gl: Xcb GLX gl-integration successfully initialized

(I have been able to capture other Qt applications in the past with glinject; I do not know what the difference is with this application).

cout commented 1 year ago

FWIW I was able to capture the above QtWebEngine script using glinject 0.4.4.

The segfault was happening at the cerr, so I printed to a string instead:

diff --git a/glinject/Global.h b/glinject/Global.h
index 0d1f798..d5917a5 100644
--- a/glinject/Global.h
+++ b/glinject/Global.h
@@ -57,7 +57,9 @@ inline void atomic_thread_fence_replacement(memory_order) {
 #endif

 #define GLINJECT_PRINT(message) { \
-       std::cerr << "[SSR-GLInject] " << message << std::endl; \
+  std::stringstream strm; \
+       strm << "[SSR-GLInject] " << message << std::endl; \
+  int i = write(2, strm.str().c_str(), strm.str().length()); \
 }

Since 0.4.4 does not have the PLT fixes I changed Hook.cpp to fall back to libc instead:

diff --git a/glinject/Hook.cpp b/glinject/Hook.cpp
index 766889a..a30000c 100644
--- a/glinject/Hook.cpp
+++ b/glinject/Hook.cpp
@@ -54,9 +54,11 @@ void InitGLInject() {
        // part 1: get dlsym and dlvsym
        eh_obj_t libdl;
        if(eh_find_obj(&libdl, "*/libdl.so*")) {
+       if(eh_find_obj(&libdl, "*/libc.so*")) {
                GLINJECT_PRINT("Error: Can't open libdl.so!");
                exit(1);
        }
+       }
        if(eh_find_sym(&libdl, "dlsym", (void**) &g_glinject_real_dlsym)) {
                GLINJECT_PRINT("Error: Can't get dlsym address!");
                eh_destroy_obj(&libdl);

Lastly libssr-glinject.so was not linked to libGLX (according to ldd), which this addresses:

diff --git a/glinject/CMakeLists.txt b/glinject/CMakeLists.txt
index 52660db..09889fa 100644
--- a/glinject/CMakeLists.txt
+++ b/glinject/CMakeLists.txt
@@ -25,6 +25,7 @@ set(include_directories
 )

 set(link_libraries
+  -Wl,--no-as-needed
        ${CMAKE_THREAD_LIBS_INIT}
        ${X11_X11_LIB}
        ${X11_Xfixes_LIB}
cout commented 1 year ago

In the master branch libssr-glinject.so also does not link to libGLX.so. I tried making the same change there to add -Wl,--no-as-needed but it still does not capture anything. So there is some other change between 0.4.4 and HEAD that is the cause.

cout commented 11 months ago

One other limitation: the above changes are enough for 0.4.4 to work with Ubuntu's python3 package (3.10.6). Trying to use glinject with conda's python (3.10.9) fails, because g_glinject gets reset to null after glinject has been initialized. When glinject initialized a second time, it fails, because it cannot find dlsym.

SirNate0 commented 8 months ago

I believe the error I am experiencing is caused by the same issue you just discussed, so I'll include this here. I have been having SSR fail to run any OpenGL application recently (my own, but also external projects like glxgears). I managed to get a backtrace using gdb -iex "set exec-wrapper env 'LD_PRELOAD=:/usr/lib/x86_64-linux-gnu/simplescreenrecorder/libssr-glinject.so'" glxgears, which seems to be an issue with an ostream, as seems to be the case above with std::cerr:

(gdb) r
Starting program: /usr/bin/glxgears 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7931f8a in std::ostream::sentry::sentry(std::ostream&) () from /lib/x86_64-linux-gnu/libstdc++.so.6
(gdb) bt
#0  0x00007ffff7931f8a in std::ostream::sentry::sentry(std::ostream&) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#1  0x00007ffff7932a0c in std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, long) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7932ebb in std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*) () from /lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7fb0cd7 in InitGLInject() () from /usr/lib/x86_64-linux-gnu/simplescreenrecorder/libssr-glinject.so
#4  0x00007ffff7fb0ff3 in dlsym () from /usr/lib/x86_64-linux-gnu/simplescreenrecorder/libssr-glinject.so
#5  0x00007ffff775e28e in ?? () from /lib/x86_64-linux-gnu/libGLdispatch.so.0
#6  0x00007ffff7fc947e in call_init (l=<optimized out>, argc=argc@entry=1, argv=argv@entry=0x7fffffffdc38, env=env@entry=0x7fffffffdc48) at ./elf/dl-init.c:70
#7  0x00007ffff7fc9568 in call_init (env=0x7fffffffdc48, argv=0x7fffffffdc38, argc=1, l=<optimized out>) at ./elf/dl-init.c:33
#8  _dl_init (main_map=0x7ffff7ffe2e0, argc=1, argv=0x7fffffffdc38, env=0x7fffffffdc48) at ./elf/dl-init.c:117
#9  0x00007ffff7fe32ca in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#10 0x0000000000000001 in ?? ()
#11 0x00007fffffffe002 in ?? ()
#12 0x0000000000000000 in ?? ()
petterreinholdtsen commented 4 months ago

I have been told the latest git edition solve the segfault, and suspect the change in https://github.com/MaartenBaert/ssr/commit/83b8f9f5d9c9ab06152657e57f85b6f71954a6b9 . Perhaps give it a try?

petterreinholdtsen commented 1 month ago

See also https://github.com/MaartenBaert/ssr/issues/947 .