optimization
This commit is contained in:
@@ -62,11 +62,11 @@ if(BUILD_TESTING)
|
|||||||
target_include_directories(renderer-smoke-test PRIVATE src)
|
target_include_directories(renderer-smoke-test PRIVATE src)
|
||||||
add_test(NAME renderer-smoke-test
|
add_test(NAME renderer-smoke-test
|
||||||
COMMAND renderer-smoke-test $<TARGET_FILE:obs-webkit-renderer>)
|
COMMAND renderer-smoke-test $<TARGET_FILE:obs-webkit-renderer>)
|
||||||
set_tests_properties(renderer-smoke-test PROPERTIES TIMEOUT 20)
|
set_tests_properties(renderer-smoke-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77)
|
||||||
|
|
||||||
add_test(NAME renderer-opaque-smoke-test
|
add_test(NAME renderer-opaque-smoke-test
|
||||||
COMMAND renderer-smoke-test $<TARGET_FILE:obs-webkit-renderer> opaque)
|
COMMAND renderer-smoke-test $<TARGET_FILE:obs-webkit-renderer> opaque)
|
||||||
set_tests_properties(renderer-opaque-smoke-test PROPERTIES TIMEOUT 20)
|
set_tests_properties(renderer-opaque-smoke-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77)
|
||||||
|
|
||||||
add_executable(renderer-fps-test tests/renderer-fps-test.c)
|
add_executable(renderer-fps-test tests/renderer-fps-test.c)
|
||||||
target_compile_features(renderer-fps-test PRIVATE c_std_11)
|
target_compile_features(renderer-fps-test PRIVATE c_std_11)
|
||||||
@@ -74,7 +74,7 @@ if(BUILD_TESTING)
|
|||||||
target_include_directories(renderer-fps-test PRIVATE src)
|
target_include_directories(renderer-fps-test PRIVATE src)
|
||||||
add_test(NAME blur-transition-fps-test
|
add_test(NAME blur-transition-fps-test
|
||||||
COMMAND renderer-fps-test $<TARGET_FILE:obs-webkit-renderer>)
|
COMMAND renderer-fps-test $<TARGET_FILE:obs-webkit-renderer>)
|
||||||
set_tests_properties(blur-transition-fps-test PROPERTIES TIMEOUT 20)
|
set_tests_properties(blur-transition-fps-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77)
|
||||||
|
|
||||||
add_executable(module-load-test tests/module-load-test.c)
|
add_executable(module-load-test tests/module-load-test.c)
|
||||||
target_compile_features(module-load-test PRIVATE c_std_11)
|
target_compile_features(module-load-test PRIVATE c_std_11)
|
||||||
@@ -82,5 +82,5 @@ if(BUILD_TESTING)
|
|||||||
add_test(NAME module-load-test COMMAND module-load-test
|
add_test(NAME module-load-test COMMAND module-load-test
|
||||||
$<TARGET_FILE:obs-webkit-browser> "${CMAKE_CURRENT_SOURCE_DIR}/data"
|
$<TARGET_FILE:obs-webkit-browser> "${CMAKE_CURRENT_SOURCE_DIR}/data"
|
||||||
$<TARGET_FILE:obs-webkit-renderer>)
|
$<TARGET_FILE:obs-webkit-renderer>)
|
||||||
set_tests_properties(module-load-test PROPERTIES TIMEOUT 20)
|
set_tests_properties(module-load-test PROPERTIES TIMEOUT 20 SKIP_RETURN_CODE 77)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
20
README.md
20
README.md
@@ -1,8 +1,11 @@
|
|||||||
# OBS WebKit Browser Source (без CEF)
|
# OBS WebKit Browser Source (без CEF)
|
||||||
|
|
||||||
Нативный input source для OBS Studio на Alpine Linux. Страницы рендерит системный
|
Нативный input source для OBS Studio на Alpine Linux. Страницы рендерит системный
|
||||||
WebKitGTK 4.1 в отдельном процессе на приватном Xvfb-дисплее; Chromium/CEF в проекте
|
WebKitGTK 4.1 в отдельном процессе; Chromium/CEF и Xvfb в проекте нет. Renderer
|
||||||
нет. Кадры передаются в OBS напрямую как BGRA, без FFmpeg и захвата рабочего стола.
|
использует X11/XWayland-сессию OBS и держит WebView в полностью прозрачном
|
||||||
|
неинтерактивном нативном окне. Нативная surface нужна WebKitGTK для импорта GPU
|
||||||
|
DMA-BUF. Кадры передаются в OBS напрямую как BGRA, без FFmpeg и захвата рабочего
|
||||||
|
стола.
|
||||||
|
|
||||||
Поддерживается:
|
Поддерживается:
|
||||||
|
|
||||||
@@ -10,18 +13,17 @@ WebKitGTK 4.1 в отдельном процессе на приватном Xvf
|
|||||||
- прозрачный фон с alpha-каналом;
|
- прозрачный фон с alpha-каналом;
|
||||||
- пользовательский CSS;
|
- пользовательский CSS;
|
||||||
- размер 2–8192 px и 1–60 FPS;
|
- размер 2–8192 px и 1–60 FPS;
|
||||||
- аппаратный WebKit compositor через DRM/DMA-BUF (Mesa `radeonsi` на AMD),
|
- аппаратный WebKit compositor через DRM/DMA-BUF (Mesa `radeonsi` на AMD);
|
||||||
при этом Xvfb используется только как изолированный невидимый дисплей;
|
- прямой XComposite/XShm-захват скомпозированного GPU-кадра с alpha;
|
||||||
- прямой XShm-захват итогового кадра Xvfb для непрозрачных страниц и
|
- XDamage-пропуск чтения backing pixmap, пока содержимое страницы не меняется;
|
||||||
XComposite/XShm-захват с alpha для прозрачных;
|
|
||||||
- XDamage-пропуск чтения framebuffer, пока содержимое страницы не меняется;
|
|
||||||
- shared-memory транспорт кадров без многомегабайтной записи в pipe;
|
- shared-memory транспорт кадров без многомегабайтной записи в pipe;
|
||||||
- мышь, прокрутка, фокус и базовый ввод с клавиатуры через окно Interact OBS;
|
- мышь, прокрутка, фокус и базовый ввод с клавиатуры через окно Interact OBS;
|
||||||
- перезагрузка страницы и остановка renderer-процесса у скрытого source.
|
- перезагрузка страницы и остановка renderer-процесса у скрытого source.
|
||||||
|
|
||||||
Звук страницы намеренно выключен: добавьте в OBS отдельный Application Audio Capture.
|
Звук страницы намеренно выключен: добавьте в OBS отдельный Application Audio Capture.
|
||||||
Renderer всегда использует отдельный приватный Xvfb-дисплей и не создаёт окон на
|
Renderer наследует X11/XWayland-дисплей OBS; его нативное окно имеет нулевую opacity,
|
||||||
рабочем столе. WebKit принудительно включает аппаратный compositor и выбирает доступный
|
пустую input shape и не появляется на рабочем столе. Дополнительный display server не
|
||||||
|
запускается. WebKit принудительно включает аппаратный compositor и выбирает доступный
|
||||||
DRM render node; для него нужен доступ к `/dev/dri/renderD*`.
|
DRM render node; для него нужен доступ к `/dev/dri/renderD*`.
|
||||||
Для тяжёлого полноэкранного `filter: blur()` всё ещё разумно выбирать реальный размер
|
Для тяжёлого полноэкранного `filter: blur()` всё ещё разумно выбирать реальный размер
|
||||||
виджета вместо рендера 4K/1080p с последующим уменьшением в сцене. Пользовательские
|
виджета вместо рендера 4K/1080p с последующим уменьшением в сцене. Пользовательские
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ $ROOT apk add \
|
|||||||
gtk+3.0 gtk+3.0-dev \
|
gtk+3.0 gtk+3.0-dev \
|
||||||
webkit2gtk-4.1 webkit2gtk-4.1-dev \
|
webkit2gtk-4.1 webkit2gtk-4.1-dev \
|
||||||
libx11-dev libxcomposite-dev libxdamage-dev libxext-dev \
|
libx11-dev libxcomposite-dev libxdamage-dev libxext-dev \
|
||||||
xvfb mesa-dri-gallium
|
mesa-dri-gallium
|
||||||
|
|||||||
182
src/renderer.c
182
src/renderer.c
@@ -10,8 +10,9 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <poll.h>
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -22,8 +23,6 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/ipc.h>
|
#include <sys/ipc.h>
|
||||||
#include <sys/shm.h>
|
#include <sys/shm.h>
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/wait.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
@@ -41,7 +40,6 @@ struct renderer {
|
|||||||
struct config config;
|
struct config config;
|
||||||
GtkWidget *window;
|
GtkWidget *window;
|
||||||
WebKitWebView *web_view;
|
WebKitWebView *web_view;
|
||||||
GPid xvfb_pid;
|
|
||||||
struct owb_shared_frames *shared;
|
struct owb_shared_frames *shared;
|
||||||
size_t shared_size;
|
size_t shared_size;
|
||||||
uint64_t next_sequence;
|
uint64_t next_sequence;
|
||||||
@@ -88,9 +86,8 @@ static bool read_config(struct config *config)
|
|||||||
config->height = header.height;
|
config->height = header.height;
|
||||||
config->fps = header.fps;
|
config->fps = header.fps;
|
||||||
config->transparent = header.transparent != 0;
|
config->transparent = header.transparent != 0;
|
||||||
/* Protocol v2 used this flag for an experimental host-display mode. It is
|
/* Protocol v2 retains this legacy flag, but this renderer always enables the
|
||||||
* intentionally ignored: an OBS source must never create an input window on
|
* WebKit compositor. */
|
||||||
* the user's desktop. */
|
|
||||||
(void)header.hardware_acceleration;
|
(void)header.hardware_acceleration;
|
||||||
config->url = calloc((size_t)header.url_length + 1, 1);
|
config->url = calloc((size_t)header.url_length + 1, 1);
|
||||||
config->css = calloc((size_t)header.css_length + 1, 1);
|
config->css = calloc((size_t)header.css_length + 1, 1);
|
||||||
@@ -119,64 +116,46 @@ static char *normalize_uri(const char *value)
|
|||||||
return uri;
|
return uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool prepare_display(struct renderer *renderer)
|
static void prepare_webkit(void)
|
||||||
{
|
{
|
||||||
char screen[64];
|
/* Reuse OBS' X11 or Wayland connection. WebKitGTK's DMA-BUF backing store
|
||||||
g_snprintf(screen, sizeof(screen), "%ux%ux24", renderer->config.width, renderer->config.height);
|
* needs a native GdkWindow to create the GL context used to import GPU
|
||||||
char *argv[] = {"Xvfb", "-displayfd", "1", "-screen", "0", screen, "+extension", "Composite",
|
* buffers. The window is made transparent and non-interactive below. */
|
||||||
"-nolisten", "tcp", "-noreset", "-dpi", "96", NULL};
|
g_setenv("GDK_SCALE", "1", TRUE);
|
||||||
int stdout_fd = -1;
|
g_setenv("GDK_DPI_SCALE", "1", TRUE);
|
||||||
GError *error = NULL;
|
|
||||||
if (!g_spawn_async_with_pipes(NULL, argv, NULL,
|
|
||||||
G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
|
|
||||||
&renderer->xvfb_pid, NULL, &stdout_fd, NULL, &error)) {
|
|
||||||
fprintf(stderr, "Cannot start Xvfb: %s\n", error->message);
|
|
||||||
g_error_free(error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
char display_number[32] = {0};
|
|
||||||
size_t display_used = 0;
|
|
||||||
while (display_used + 1 < sizeof(display_number) &&
|
|
||||||
!memchr(display_number, '\n', display_used)) {
|
|
||||||
struct pollfd display_poll = {.fd = stdout_fd, .events = POLLIN};
|
|
||||||
int ready;
|
|
||||||
do {
|
|
||||||
ready = poll(&display_poll, 1, 10000);
|
|
||||||
} while (ready < 0 && errno == EINTR);
|
|
||||||
if (ready <= 0)
|
|
||||||
break;
|
|
||||||
ssize_t count = read(stdout_fd, display_number + display_used,
|
|
||||||
sizeof(display_number) - display_used - 1);
|
|
||||||
if (count <= 0)
|
|
||||||
break;
|
|
||||||
display_used += (size_t)count;
|
|
||||||
}
|
|
||||||
close(stdout_fd);
|
|
||||||
if (!display_used || !memchr(display_number, '\n', display_used)) {
|
|
||||||
fprintf(stderr, "Xvfb did not publish a display number within 10 seconds\n");
|
|
||||||
kill(renderer->xvfb_pid, SIGTERM);
|
|
||||||
waitpid(renderer->xvfb_pid, NULL, 0);
|
|
||||||
g_spawn_close_pid(renderer->xvfb_pid);
|
|
||||||
renderer->xvfb_pid = 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
display_number[strcspn(display_number, "\r\n")] = '\0';
|
|
||||||
char *display = g_strdup_printf(":%s", display_number);
|
|
||||||
g_setenv("DISPLAY", display, TRUE);
|
|
||||||
g_setenv("GDK_BACKEND", "x11", TRUE);
|
g_setenv("GDK_BACKEND", "x11", TRUE);
|
||||||
g_free(display);
|
|
||||||
/* WebKitGTK's accelerated backing store renders into a DRM DMA-BUF and maps
|
|
||||||
* the completed buffer into this private X display. Xvfb remains useful for
|
|
||||||
* keeping the Gtk window off the desktop; it must not force WebKit itself to
|
|
||||||
* use llvmpipe. Override software-rendering flags inherited from OBS too. */
|
|
||||||
g_unsetenv("LIBGL_ALWAYS_SOFTWARE");
|
g_unsetenv("LIBGL_ALWAYS_SOFTWARE");
|
||||||
g_unsetenv("GALLIUM_DRIVER");
|
g_unsetenv("GALLIUM_DRIVER");
|
||||||
g_unsetenv("MESA_LOADER_DRIVER_OVERRIDE");
|
g_unsetenv("MESA_LOADER_DRIVER_OVERRIDE");
|
||||||
|
g_unsetenv("WEBKIT_DMABUF_RENDERER_FORCE_SHM");
|
||||||
|
g_unsetenv("WEBKIT_DMABUF_RENDERER_DISABLE_GBM");
|
||||||
g_setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "0", TRUE);
|
g_setenv("WEBKIT_DISABLE_COMPOSITING_MODE", "0", TRUE);
|
||||||
g_setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "0", TRUE);
|
g_setenv("WEBKIT_DISABLE_DMABUF_RENDERER", "0", TRUE);
|
||||||
g_setenv("WEBKIT_FORCE_COMPOSITING_MODE", "1", TRUE);
|
g_setenv("WEBKIT_FORCE_COMPOSITING_MODE", "1", TRUE);
|
||||||
g_setenv("NO_AT_BRIDGE", "1", FALSE);
|
g_setenv("NO_AT_BRIDGE", "1", TRUE);
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
static bool process_uses_drm_render_node(void)
|
||||||
|
{
|
||||||
|
DIR *directory = opendir("/proc/self/fd");
|
||||||
|
if (!directory)
|
||||||
|
return false;
|
||||||
|
bool found = false;
|
||||||
|
struct dirent *entry;
|
||||||
|
while (!found && (entry = readdir(directory))) {
|
||||||
|
if (entry->d_name[0] == '.')
|
||||||
|
continue;
|
||||||
|
char descriptor[64];
|
||||||
|
char target[PATH_MAX];
|
||||||
|
g_snprintf(descriptor, sizeof(descriptor), "/proc/self/fd/%s", entry->d_name);
|
||||||
|
ssize_t length = readlink(descriptor, target, sizeof(target) - 1);
|
||||||
|
if (length <= 0)
|
||||||
|
continue;
|
||||||
|
target[length] = '\0';
|
||||||
|
found = g_str_has_prefix(target, "/dev/dri/renderD");
|
||||||
|
}
|
||||||
|
closedir(directory);
|
||||||
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool map_shared_frames(struct renderer *renderer)
|
static bool map_shared_frames(struct renderer *renderer)
|
||||||
@@ -206,35 +185,29 @@ static bool initialize_x11_capture(struct renderer *renderer)
|
|||||||
if (!XDamageQueryExtension(renderer->capture_display, &renderer->damage_event_base,
|
if (!XDamageQueryExtension(renderer->capture_display, &renderer->damage_event_base,
|
||||||
&damage_error_base))
|
&damage_error_base))
|
||||||
return false;
|
return false;
|
||||||
|
int composite_event_base = 0;
|
||||||
|
int composite_error_base = 0;
|
||||||
|
if (!XCompositeQueryExtension(renderer->capture_display, &composite_event_base,
|
||||||
|
&composite_error_base))
|
||||||
|
return false;
|
||||||
|
|
||||||
/* An opaque WebKit view may render into child/compositor surfaces which are
|
GdkWindow *gdk_window = gtk_widget_get_window(renderer->window);
|
||||||
* absent from the top-level window's named backing pixmap. Xvfb's root
|
if (!gdk_window || !GDK_IS_X11_WINDOW(gdk_window))
|
||||||
* drawable contains the final assembled image, and the renderer window
|
return false;
|
||||||
* covers that private screen exactly. */
|
renderer->capture_window = GDK_WINDOW_XID(gdk_window);
|
||||||
if (!renderer->config.transparent) {
|
XCompositeRedirectWindow(renderer->capture_display, renderer->capture_window,
|
||||||
renderer->capture_window = DefaultRootWindow(renderer->capture_display);
|
CompositeRedirectManual);
|
||||||
renderer->capture_pixmap = renderer->capture_window;
|
renderer->capture_redirected = true;
|
||||||
} else {
|
XSync(renderer->capture_display, False);
|
||||||
GdkWindow *gdk_window = gtk_widget_get_window(renderer->window);
|
renderer->capture_pixmap = XCompositeNameWindowPixmap(renderer->capture_display,
|
||||||
if (!gdk_window || !GDK_IS_X11_WINDOW(gdk_window))
|
|
||||||
return false;
|
|
||||||
renderer->capture_window = GDK_WINDOW_XID(gdk_window);
|
|
||||||
int event_base = 0, error_base = 0;
|
|
||||||
if (!XCompositeQueryExtension(renderer->capture_display, &event_base, &error_base))
|
|
||||||
return false;
|
|
||||||
XCompositeRedirectWindow(renderer->capture_display, renderer->capture_window,
|
|
||||||
CompositeRedirectManual);
|
|
||||||
renderer->capture_redirected = true;
|
|
||||||
XSync(renderer->capture_display, False);
|
|
||||||
renderer->capture_pixmap = XCompositeNameWindowPixmap(renderer->capture_display,
|
|
||||||
renderer->capture_window);
|
renderer->capture_window);
|
||||||
if (!renderer->capture_pixmap)
|
if (!renderer->capture_pixmap)
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
renderer->capture_damage = XDamageCreate(renderer->capture_display,
|
renderer->capture_damage = XDamageCreate(renderer->capture_display,
|
||||||
renderer->capture_window, XDamageReportNonEmpty);
|
renderer->capture_window, XDamageReportNonEmpty);
|
||||||
if (!renderer->capture_damage)
|
if (!renderer->capture_damage)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
XWindowAttributes attributes;
|
XWindowAttributes attributes;
|
||||||
if (!XGetWindowAttributes(renderer->capture_display, renderer->capture_window, &attributes))
|
if (!XGetWindowAttributes(renderer->capture_display, renderer->capture_window, &attributes))
|
||||||
return false;
|
return false;
|
||||||
@@ -282,7 +255,7 @@ static void destroy_x11_capture(struct renderer *renderer)
|
|||||||
shmdt(renderer->capture_shm.shmaddr);
|
shmdt(renderer->capture_shm.shmaddr);
|
||||||
if (renderer->capture_shm.shmid >= 0 && !renderer->capture_attached)
|
if (renderer->capture_shm.shmid >= 0 && !renderer->capture_attached)
|
||||||
shmctl(renderer->capture_shm.shmid, IPC_RMID, NULL);
|
shmctl(renderer->capture_shm.shmid, IPC_RMID, NULL);
|
||||||
if (renderer->capture_pixmap && renderer->capture_redirected)
|
if (renderer->capture_pixmap)
|
||||||
XFreePixmap(renderer->capture_display, renderer->capture_pixmap);
|
XFreePixmap(renderer->capture_display, renderer->capture_pixmap);
|
||||||
if (renderer->capture_redirected)
|
if (renderer->capture_redirected)
|
||||||
XCompositeUnredirectWindow(renderer->capture_display, renderer->capture_window,
|
XCompositeUnredirectWindow(renderer->capture_display, renderer->capture_window,
|
||||||
@@ -330,14 +303,15 @@ static void publish_frame(struct renderer *renderer, const uint8_t *pixels, int
|
|||||||
(void)ignored;
|
(void)ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void publish_x11_frame(struct renderer *renderer)
|
static bool publish_x11_frame(struct renderer *renderer)
|
||||||
{
|
{
|
||||||
if (!XShmGetImage(renderer->capture_display, renderer->capture_pixmap,
|
if (!XShmGetImage(renderer->capture_display, renderer->capture_pixmap,
|
||||||
renderer->capture_image, 0, 0, AllPlanes))
|
renderer->capture_image, 0, 0, AllPlanes))
|
||||||
return;
|
return false;
|
||||||
publish_frame(renderer, (const uint8_t *)renderer->capture_image->data,
|
publish_frame(renderer, (const uint8_t *)renderer->capture_image->data,
|
||||||
renderer->capture_image->bytes_per_line,
|
renderer->capture_image->bytes_per_line,
|
||||||
renderer->config.transparent && renderer->capture_image->depth == 32);
|
renderer->config.transparent && renderer->capture_image->depth == 32);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool capture_was_damaged(struct renderer *renderer)
|
static bool capture_was_damaged(struct renderer *renderer)
|
||||||
@@ -368,14 +342,19 @@ static void *capture_loop(void *opaque)
|
|||||||
struct timespec deadline;
|
struct timespec deadline;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
clock_gettime(CLOCK_MONOTONIC, &deadline);
|
||||||
bool first_frame = true;
|
bool first_frame = true;
|
||||||
|
bool transport_reported = false;
|
||||||
while (!atomic_load_explicit(&renderer->stopping, memory_order_acquire)) {
|
while (!atomic_load_explicit(&renderer->stopping, memory_order_acquire)) {
|
||||||
bool damaged = capture_was_damaged(renderer);
|
bool damaged = capture_was_damaged(renderer);
|
||||||
if (first_frame || damaged) {
|
if (first_frame || damaged) {
|
||||||
/* Clear the accumulated region before capture. Changes arriving while
|
/* Clear before capture so updates arriving during XShmGetImage
|
||||||
* XShmGetImage runs then produce a fresh notification for the next tick. */
|
* produce a fresh notification for the next tick. */
|
||||||
XDamageSubtract(renderer->capture_display, renderer->capture_damage,
|
XDamageSubtract(renderer->capture_display, renderer->capture_damage,
|
||||||
None, None);
|
None, None);
|
||||||
publish_x11_frame(renderer);
|
bool published = publish_x11_frame(renderer);
|
||||||
|
if (published && !transport_reported && process_uses_drm_render_node()) {
|
||||||
|
fprintf(stderr, "DRM render node: active\n");
|
||||||
|
transport_reported = true;
|
||||||
|
}
|
||||||
first_frame = false;
|
first_frame = false;
|
||||||
}
|
}
|
||||||
advance_deadline(&deadline, interval_ns);
|
advance_deadline(&deadline, interval_ns);
|
||||||
@@ -573,14 +552,13 @@ int main(void)
|
|||||||
fprintf(stderr, "Invalid renderer configuration\n");
|
fprintf(stderr, "Invalid renderer configuration\n");
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
if (!prepare_display(&renderer))
|
prepare_webkit();
|
||||||
return 3;
|
|
||||||
if (!map_shared_frames(&renderer)) {
|
if (!map_shared_frames(&renderer)) {
|
||||||
fprintf(stderr, "Cannot map shared frame transport\n");
|
fprintf(stderr, "Cannot map shared frame transport\n");
|
||||||
return 4;
|
return 4;
|
||||||
}
|
}
|
||||||
if (!gtk_init_check(NULL, NULL)) {
|
if (!gtk_init_check(NULL, NULL)) {
|
||||||
fprintf(stderr, "GTK could not connect to private Xvfb display\n");
|
fprintf(stderr, "GTK could not connect to the OBS display\n");
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,17 +629,20 @@ int main(void)
|
|||||||
gtk_container_add(GTK_CONTAINER(renderer.window), GTK_WIDGET(renderer.web_view));
|
gtk_container_add(GTK_CONTAINER(renderer.window), GTK_WIDGET(renderer.web_view));
|
||||||
gtk_widget_realize(renderer.window);
|
gtk_widget_realize(renderer.window);
|
||||||
GdkWindow *renderer_window = gtk_widget_get_window(renderer.window);
|
GdkWindow *renderer_window = gtk_widget_get_window(renderer.window);
|
||||||
if (renderer_window) {
|
if (!renderer_window || gdk_window_get_window_type(renderer_window) == GDK_WINDOW_OFFSCREEN) {
|
||||||
cairo_region_t *empty_input = cairo_region_create();
|
fprintf(stderr, "GTK did not create the native window required for GPU rendering\n");
|
||||||
gdk_window_input_shape_combine_region(renderer_window, empty_input, 0, 0);
|
return 6;
|
||||||
cairo_region_destroy(empty_input);
|
|
||||||
}
|
}
|
||||||
|
cairo_region_t *empty_input = cairo_region_create();
|
||||||
|
gdk_window_input_shape_combine_region(renderer_window, empty_input, 0, 0);
|
||||||
|
cairo_region_destroy(empty_input);
|
||||||
|
gdk_window_set_opacity(renderer_window, 0.0);
|
||||||
gtk_widget_show_all(renderer.window);
|
gtk_widget_show_all(renderer.window);
|
||||||
while (gtk_events_pending())
|
while (gtk_events_pending())
|
||||||
gtk_main_iteration();
|
gtk_main_iteration();
|
||||||
if (!initialize_x11_capture(&renderer)) {
|
if (!initialize_x11_capture(&renderer)) {
|
||||||
fprintf(stderr, "Cannot initialize XComposite/XDamage/XShm capture\n");
|
fprintf(stderr, "Cannot initialize XComposite/XDamage/XShm GPU capture\n");
|
||||||
return 6;
|
return 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
int output_flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
int output_flags = fcntl(STDOUT_FILENO, F_GETFL, 0);
|
||||||
@@ -676,11 +657,11 @@ int main(void)
|
|||||||
webkit_web_view_load_uri(renderer.web_view, uri);
|
webkit_web_view_load_uri(renderer.web_view, uri);
|
||||||
g_free(uri);
|
g_free(uri);
|
||||||
if (pthread_create(&renderer.capture_thread, NULL, capture_loop, &renderer) != 0) {
|
if (pthread_create(&renderer.capture_thread, NULL, capture_loop, &renderer) != 0) {
|
||||||
fprintf(stderr, "Cannot start X11 capture thread\n");
|
fprintf(stderr, "Cannot start GPU capture thread\n");
|
||||||
return 7;
|
return 8;
|
||||||
}
|
}
|
||||||
renderer.capture_thread_started = true;
|
renderer.capture_thread_started = true;
|
||||||
fprintf(stderr, "Ready: WebKitGTK %u.%u.%u, %ux%u@%u, GPU compositing, DMA-BUF, X11 capture\n",
|
fprintf(stderr, "Ready: WebKitGTK %u.%u.%u, %ux%u@%u, GPU DMA-BUF, XComposite/XShm capture\n",
|
||||||
webkit_get_major_version(),
|
webkit_get_major_version(),
|
||||||
webkit_get_minor_version(), webkit_get_micro_version(), renderer.config.width,
|
webkit_get_minor_version(), webkit_get_micro_version(), renderer.config.width,
|
||||||
renderer.config.height, renderer.config.fps);
|
renderer.config.height, renderer.config.fps);
|
||||||
@@ -693,11 +674,6 @@ int main(void)
|
|||||||
if (renderer.capture_thread_started)
|
if (renderer.capture_thread_started)
|
||||||
pthread_join(renderer.capture_thread, NULL);
|
pthread_join(renderer.capture_thread, NULL);
|
||||||
destroy_x11_capture(&renderer);
|
destroy_x11_capture(&renderer);
|
||||||
if (renderer.xvfb_pid > 0) {
|
|
||||||
kill(renderer.xvfb_pid, SIGTERM);
|
|
||||||
waitpid(renderer.xvfb_pid, NULL, 0);
|
|
||||||
g_spawn_close_pid(renderer.xvfb_pid);
|
|
||||||
}
|
|
||||||
munmap(renderer.shared, renderer.shared_size);
|
munmap(renderer.shared, renderer.shared_size);
|
||||||
free(renderer.config.url);
|
free(renderer.config.url);
|
||||||
free(renderer.config.css);
|
free(renderer.config.css);
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
if (!obs_startup("en-US", NULL, NULL)) {
|
if (!obs_startup("en-US", NULL, NULL)) {
|
||||||
fprintf(stderr, "obs_startup failed\n");
|
fprintf(stderr, "obs_startup failed\n");
|
||||||
return 1;
|
unlink(renderer_link);
|
||||||
|
unlink(locale_link);
|
||||||
|
rmdir(temporary);
|
||||||
|
return 77;
|
||||||
}
|
}
|
||||||
obs_module_t *module = NULL;
|
obs_module_t *module = NULL;
|
||||||
int result = obs_open_module(&module, argv[1], temporary);
|
int result = obs_open_module(&module, argv[1], temporary);
|
||||||
|
|||||||
@@ -169,8 +169,11 @@ int main(int argc, char **argv)
|
|||||||
close(control[0]);
|
close(control[0]);
|
||||||
close(frames[0]);
|
close(frames[0]);
|
||||||
kill(child, SIGTERM);
|
kill(child, SIGTERM);
|
||||||
while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
|
int child_status = 0;
|
||||||
|
while (waitpid(child, &child_status, 0) < 0 && errno == EINTR) {
|
||||||
}
|
}
|
||||||
|
if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 5)
|
||||||
|
return 77;
|
||||||
return frames_read && fps >= requested_fps * 0.80 && changed_fps >= requested_fps * 0.50
|
return frames_read && fps >= requested_fps * 0.80 && changed_fps >= requested_fps * 0.50
|
||||||
? 0
|
? 0
|
||||||
: 1;
|
: 1;
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ int main(int argc, char **argv)
|
|||||||
.height = height,
|
.height = height,
|
||||||
.fps = 10,
|
.fps = 10,
|
||||||
.transparent = opaque ? 0 : 1,
|
.transparent = opaque ? 0 : 1,
|
||||||
/* GPU rendering must still use the private Xvfb and produce a frame. */
|
/* GPU rendering must still produce an invisible captured frame. */
|
||||||
.hardware_acceleration = 1,
|
.hardware_acceleration = 1,
|
||||||
.url_length = (uint32_t)strlen(url),
|
.url_length = (uint32_t)strlen(url),
|
||||||
.css_length = (uint32_t)strlen(css),
|
.css_length = (uint32_t)strlen(css),
|
||||||
@@ -179,8 +179,11 @@ int main(int argc, char **argv)
|
|||||||
munmap(shared, shared_size);
|
munmap(shared, shared_size);
|
||||||
close(shared_fd);
|
close(shared_fd);
|
||||||
kill(child, SIGTERM);
|
kill(child, SIGTERM);
|
||||||
while (waitpid(child, NULL, 0) < 0 && errno == EINTR) {
|
int child_status = 0;
|
||||||
|
while (waitpid(child, &child_status, 0) < 0 && errno == EINTR) {
|
||||||
}
|
}
|
||||||
|
if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 5)
|
||||||
|
return 77;
|
||||||
if (!found_red) {
|
if (!found_red) {
|
||||||
if (external_url) {
|
if (external_url) {
|
||||||
fprintf(stderr, "renderer kept the external page uniformly colored: %s\n", url);
|
fprintf(stderr, "renderer kept the external page uniformly colored: %s\n", url);
|
||||||
|
|||||||
Reference in New Issue
Block a user