aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-06-28 04:25:20 +0200
committerdec05eba <dec05eba@protonmail.com>2025-06-28 04:25:26 +0200
commitb9537941c9eba041b7afc8b0f4256c73e35c2387 (patch)
tree84b650502b0a54ae173acfa9b3cf45d5089beb0e
parent2290f0641c91317c4fd05f35d2c0cfc25fba2181 (diff)
Restart portal capture if it's paused for 3 seconds or more (can happen if returning from lock screen)
Remove dbus server, no longer needed. Just run dbus code directly.
-rw-r--r--dbus/client/dbus_client.c269
-rw-r--r--dbus/client/dbus_client.h36
-rw-r--r--dbus/portal.h17
-rw-r--r--dbus/protocol.h86
-rw-r--r--dbus/server/dbus_server.c175
-rw-r--r--include/capture/capture.h5
-rw-r--r--include/dbus.h (renamed from dbus/dbus_impl.h)14
-rw-r--r--include/pipewire_video.h4
-rw-r--r--meson.build7
-rw-r--r--project.conf2
-rw-r--r--src/capture/portal.c95
-rw-r--r--src/dbus.c (renamed from dbus/dbus_impl.c)2
-rw-r--r--src/main.cpp32
-rw-r--r--src/pipewire_video.c33
14 files changed, 147 insertions, 630 deletions
diff --git a/dbus/client/dbus_client.c b/dbus/client/dbus_client.c
deleted file mode 100644
index de2df62..0000000
--- a/dbus/client/dbus_client.c
+++ /dev/null
@@ -1,269 +0,0 @@
-#include "dbus_client.h"
-#include "../protocol.h"
-
-#include <sys/socket.h>
-#include <sys/wait.h>
-#include <sys/prctl.h>
-#include <unistd.h>
-#include <poll.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <errno.h>
-
-// TODO: Error checking for write/read
-
-static bool gsr_dbus_client_wait_for_startup(gsr_dbus_client *self) {
- struct pollfd poll_fd = {
- .fd = self->socket_pair[0],
- .events = POLLIN,
- .revents = 0
- };
- for(;;) {
- int poll_res = poll(&poll_fd, 1, 100);
- if(poll_res > 0 && (poll_fd.revents & POLLIN)) {
- char msg;
- read(self->socket_pair[0], &msg, 1);
- return true;
- } else {
- int status = 0;
- int wait_result = waitpid(self->pid, &status, WNOHANG);
- if(wait_result != 0) {
- int exit_code = -1;
- if(WIFEXITED(status))
- exit_code = WEXITSTATUS(status);
- fprintf(stderr, "gsr error: gsr_dbus_client_init: server died or never started, exit code: %d\n", exit_code);
- self->pid = 0;
- return false;
- }
- }
- }
-}
-
-bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token) {
- memset(self, 0, sizeof(*self));
-
- if(socketpair(AF_UNIX, SOCK_STREAM, 0, self->socket_pair) == -1) {
- fprintf(stderr, "gsr error: gsr_dbus_client_init: socketpair failed, error: %s\n", strerror(errno));
- return false;
- }
-
- if(screencast_restore_token) {
- self->screencast_restore_token = strdup(screencast_restore_token);
- if(!self->screencast_restore_token) {
- fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to clone restore token\n");
- gsr_dbus_client_deinit(self);
- return false;
- }
- }
-
- self->pid = fork();
- if(self->pid == -1) {
- fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to fork process\n");
- gsr_dbus_client_deinit(self);
- return false;
- } else if(self->pid == 0) { /* child */
- char socket_pair_server_str[32];
- snprintf(socket_pair_server_str, sizeof(socket_pair_server_str), "%d", self->socket_pair[1]);
-
- /* Needed for NixOS for example, to make sure gsr-dbus-server doesn't inherit cap_sys_nice */
- prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0, 0, 0);
-
- const char *args[] = { "gsr-dbus-server", socket_pair_server_str, self->screencast_restore_token ? self->screencast_restore_token : "", NULL };
- execvp(args[0], (char *const*)args);
-
- fprintf(stderr, "gsr error: gsr_dbus_client_init: failed to launch \"gsr-dbus-server\", error: %s\n", strerror(errno));
- _exit(127);
- } else { /* parent */
- if(!gsr_dbus_client_wait_for_startup(self)) {
- gsr_dbus_client_deinit(self);
- return false;
- }
- }
-
- return true;
-}
-
-void gsr_dbus_client_deinit(gsr_dbus_client *self) {
- for(int i = 0; i < 2; ++i) {
- if(self->socket_pair[i] > 0) {
- close(self->socket_pair[i]);
- self->socket_pair[i] = -1;
- }
- }
-
- if(self->screencast_restore_token) {
- free(self->screencast_restore_token);
- self->screencast_restore_token = NULL;
- }
-
- if(self->pid > 0) {
- kill(self->pid, SIGKILL);
- int status = 0;
- waitpid(self->pid, &status, 0);
- self->pid = 0;
- }
-}
-
-int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size) {
- const gsr_dbus_request_message request = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_REQ_CREATE_SESSION,
- .create_session = (gsr_dbus_message_req_create_session) {}
- };
- write(self->socket_pair[0], &request, sizeof(request));
-
- gsr_dbus_response_message response = {0};
- read(self->socket_pair[0], &response, sizeof(response));
-
- if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
- return -1;
- }
-
- if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
- return response.error.error_code;
- }
-
- if(response.type != GSR_DBUS_MESSAGE_RESP_CREATE_SESSION) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_create_session: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_CREATE_SESSION, response.type);
- return -1;
- }
-
- snprintf(session_handle, session_handle_size, "%s", response.create_session.session_handle);
- return 0;
-}
-
-int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode) {
- gsr_dbus_request_message request = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES,
- .select_sources = (gsr_dbus_message_req_select_sources) {
- .capture_type = capture_type,
- .cursor_mode = cursor_mode
- }
- };
- snprintf(request.select_sources.session_handle, sizeof(request.select_sources.session_handle), "%s", session_handle);
- write(self->socket_pair[0], &request, sizeof(request));
-
- gsr_dbus_response_message response = {0};
- read(self->socket_pair[0], &response, sizeof(response));
-
- if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
- return -1;
- }
-
- if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
- return response.error.error_code;
- }
-
- if(response.type != GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_select_sources: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES, response.type);
- return -1;
- }
-
- return 0;
-}
-
-int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node) {
- *pipewire_node = 0;
-
- gsr_dbus_request_message request = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_REQ_START,
- .start = (gsr_dbus_message_req_start) {}
- };
- snprintf(request.start.session_handle, sizeof(request.start.session_handle), "%s", session_handle);
- write(self->socket_pair[0], &request, sizeof(request));
-
- gsr_dbus_response_message response = {0};
- read(self->socket_pair[0], &response, sizeof(response));
-
- if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
- return -1;
- }
-
- if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
- return response.error.error_code;
- }
-
- if(response.type != GSR_DBUS_MESSAGE_RESP_START) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_start: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_START, response.type);
- return -1;
- }
-
- if(self->screencast_restore_token) {
- free(self->screencast_restore_token);
- if(response.start.restore_token[0] == '\0')
- self->screencast_restore_token = NULL;
- else
- self->screencast_restore_token = strdup(response.start.restore_token);
- }
-
- *pipewire_node = response.start.pipewire_node;
- return 0;
-}
-
-bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd) {
- *pipewire_fd = 0;
-
- gsr_dbus_request_message request = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE,
- .open_pipewire_remote = (gsr_dbus_message_req_open_pipewire_remote) {}
- };
- snprintf(request.open_pipewire_remote.session_handle, sizeof(request.open_pipewire_remote.session_handle), "%s", session_handle);
- write(self->socket_pair[0], &request, sizeof(request));
-
- gsr_dbus_response_message response = {0};
- struct iovec iov = {
- .iov_base = &response,
- .iov_len = sizeof(response)
- };
-
- char msg_control[CMSG_SPACE(sizeof(int))];
-
- struct msghdr message = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = msg_control,
- .msg_controllen = sizeof(msg_control)
- };
-
- const int bla = recvmsg(self->socket_pair[0], &message, MSG_WAITALL);
- (void)bla;
-
- if(response.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server uses protocol version %d while the client is using protocol version %d", response.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
- return false;
- }
-
- if(response.type == GSR_DBUS_MESSAGE_RESP_ERROR) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: server return error: %s (%d)\n", response.error.message, (int)response.error.error_code);
- return false;
- }
-
- if(response.type != GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: received incorrect response type. Expected %d got %d\n", GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE, response.type);
- return false;
- }
-
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
- if(!cmsg || cmsg->cmsg_type != SCM_RIGHTS) {
- fprintf(stderr, "gsr error: gsr_dbus_client_screencast_open_pipewire_remote: returned message data is missing file descriptor\n");
- return false;
- }
-
- memcpy(pipewire_fd, CMSG_DATA(cmsg), sizeof(*pipewire_fd));
- return true;
-}
-
-const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self) {
- return self->screencast_restore_token;
-}
diff --git a/dbus/client/dbus_client.h b/dbus/client/dbus_client.h
deleted file mode 100644
index 98a1ecf..0000000
--- a/dbus/client/dbus_client.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef GSR_DBUS_CLIENT_H
-#define GSR_DBUS_CLIENT_H
-
-/*
- Using a client-server architecture is needed for dbus because cap_sys_nice breaks desktop portal.
- The main binary has cap_sys_nice and we launch a new child-process without it which uses uses desktop portal.
-*/
-
-#include "../portal.h"
-#include <stdbool.h>
-#include <stdint.h>
-#include <signal.h>
-
-typedef struct {
- int socket_pair[2];
- char *screencast_restore_token;
- pid_t pid;
-} gsr_dbus_client;
-
-/* Blocking. TODO: Make non-blocking */
-bool gsr_dbus_client_init(gsr_dbus_client *self, const char *screencast_restore_token);
-void gsr_dbus_client_deinit(gsr_dbus_client *self);
-
-/* The follow functions should be called in order to setup ScreenCast properly */
-/* These functions that return an int return the response status code */
-int gsr_dbus_client_screencast_create_session(gsr_dbus_client *self, char *session_handle, size_t session_handle_size);
-/*
- |capture_type| is a bitmask of gsr_portal_capture_type values. gsr_portal_capture_type values that are not supported by the desktop portal will be ignored.
- |gsr_portal_cursor_mode| is a bitmask of gsr_portal_cursor_mode values. gsr_portal_cursor_mode values that are not supported will be ignored.
-*/
-int gsr_dbus_client_screencast_select_sources(gsr_dbus_client *self, const char *session_handle, uint32_t capture_type, uint32_t cursor_mode);
-int gsr_dbus_client_screencast_start(gsr_dbus_client *self, const char *session_handle, uint32_t *pipewire_node);
-bool gsr_dbus_client_screencast_open_pipewire_remote(gsr_dbus_client *self, const char *session_handle, int *pipewire_fd);
-const char* gsr_dbus_client_screencast_get_restore_token(gsr_dbus_client *self);
-
-#endif /* GSR_DBUS_CLIENT_H */
diff --git a/dbus/portal.h b/dbus/portal.h
deleted file mode 100644
index 6b93aa6..0000000
--- a/dbus/portal.h
+++ /dev/null
@@ -1,17 +0,0 @@
-#ifndef GSR_PORTAL_H
-#define GSR_PORTAL_H
-
-typedef enum {
- GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
- GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
- GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
- GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
-} gsr_portal_capture_type;
-
-typedef enum {
- GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
- GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
- GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
-} gsr_portal_cursor_mode;
-
-#endif /* GSR_PORTAL_H */
diff --git a/dbus/protocol.h b/dbus/protocol.h
deleted file mode 100644
index 212358d..0000000
--- a/dbus/protocol.h
+++ /dev/null
@@ -1,86 +0,0 @@
-#ifndef GSR_DBUS_PROTOCOL_H
-#define GSR_DBUS_PROTOCOL_H
-
-#include <stdint.h>
-
-#define GSR_DBUS_PROTOCOL_VERSION 1
-
-typedef enum {
- GSR_DBUS_MESSAGE_REQ_CREATE_SESSION,
- GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES,
- GSR_DBUS_MESSAGE_REQ_START,
- GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE
-} gsr_dbus_message_req_type;
-
-typedef struct {
-
-} gsr_dbus_message_req_create_session;
-
-typedef struct {
- char session_handle[128];
- uint32_t capture_type;
- uint32_t cursor_mode;
-} gsr_dbus_message_req_select_sources;
-
-typedef struct {
- char session_handle[128];
-} gsr_dbus_message_req_start;
-
-typedef struct {
- char session_handle[128];
-} gsr_dbus_message_req_open_pipewire_remote;
-
-typedef struct {
- uint8_t protocol_version;
- gsr_dbus_message_req_type type;
- union {
- gsr_dbus_message_req_create_session create_session;
- gsr_dbus_message_req_select_sources select_sources;
- gsr_dbus_message_req_start start;
- gsr_dbus_message_req_open_pipewire_remote open_pipewire_remote;
- };
-} gsr_dbus_request_message;
-
-typedef enum {
- GSR_DBUS_MESSAGE_RESP_ERROR,
- GSR_DBUS_MESSAGE_RESP_CREATE_SESSION,
- GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES,
- GSR_DBUS_MESSAGE_RESP_START,
- GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE
-} gsr_dbus_message_resp_type;
-
-typedef struct {
- uint32_t error_code;
- char message[128];
-} gsr_dbus_message_resp_error;
-
-typedef struct {
- char session_handle[128];
-} gsr_dbus_message_resp_create_session;
-
-typedef struct {
-
-} gsr_dbus_message_resp_select_sources;
-
-typedef struct {
- char restore_token[128];
- uint32_t pipewire_node;
-} gsr_dbus_message_resp_start;
-
-typedef struct {
-
-} gsr_dbus_message_resp_open_pipewire_remote;
-
-typedef struct {
- uint8_t protocol_version;
- gsr_dbus_message_resp_type type;
- union {
- gsr_dbus_message_resp_error error;
- gsr_dbus_message_resp_create_session create_session;
- gsr_dbus_message_resp_select_sources select_sources;
- gsr_dbus_message_resp_start start;
- gsr_dbus_message_resp_open_pipewire_remote open_pipewire_remote;
- };
-} gsr_dbus_response_message;
-
-#endif /* GSR_DBUS_PROTOCOL_H */
diff --git a/dbus/server/dbus_server.c b/dbus/server/dbus_server.c
deleted file mode 100644
index bde6acb..0000000
--- a/dbus/server/dbus_server.c
+++ /dev/null
@@ -1,175 +0,0 @@
-#include "../dbus_impl.h"
-#include "../protocol.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <unistd.h>
-#include <sys/socket.h>
-
-/* TODO: Error check write/read */
-
-static int handle_create_session(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_create_session *create_session) {
- (void)create_session;
- char *session_handle = NULL;
- const int status = gsr_dbus_screencast_create_session(dbus, &session_handle);
- if(status == 0) {
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_CREATE_SESSION,
- .create_session = (gsr_dbus_message_resp_create_session) {}
- };
- snprintf(response.create_session.session_handle, sizeof(response.create_session.session_handle), "%s", session_handle);
- free(session_handle);
- write(rpc_fd, &response, sizeof(response));
- }
- return status;
-}
-
-static int handle_select_sources(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_select_sources *select_sources) {
- const int status = gsr_dbus_screencast_select_sources(dbus, select_sources->session_handle, select_sources->capture_type, select_sources->cursor_mode);
- if(status == 0) {
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_SELECT_SOURCES,
- .select_sources = (gsr_dbus_message_resp_select_sources) {}
- };
- write(rpc_fd, &response, sizeof(response));
- }
- return status;
-}
-
-static int handle_start(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_start *start) {
- uint32_t pipewire_node = 0;
- const int status = gsr_dbus_screencast_start(dbus, start->session_handle, &pipewire_node);
- if(status == 0) {
- const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(dbus);
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_START,
- .start = (gsr_dbus_message_resp_start) {
- .pipewire_node = pipewire_node
- }
- };
- snprintf(response.start.restore_token, sizeof(response.start.restore_token), "%s", screencast_restore_token ? screencast_restore_token : "");
- write(rpc_fd, &response, sizeof(response));
- }
- return status;
-}
-
-static bool handle_open_pipewire_remote(gsr_dbus *dbus, int rpc_fd, const gsr_dbus_message_req_open_pipewire_remote *open_pipewire_remote) {
- int pipewire_fd = 0;
- const bool success = gsr_dbus_screencast_open_pipewire_remote(dbus, open_pipewire_remote->session_handle, &pipewire_fd);
- if(success) {
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_OPEN_PIPEWIRE_REMOTE,
- .open_pipewire_remote = (gsr_dbus_message_resp_open_pipewire_remote) {}
- };
-
- struct iovec iov = {
- .iov_base = &response,
- .iov_len = sizeof(response)
- };
-
- char msg_control[CMSG_SPACE(sizeof(int))];
-
- struct msghdr message = {
- .msg_iov = &iov,
- .msg_iovlen = 1,
- .msg_control = msg_control,
- .msg_controllen = sizeof(msg_control)
- };
-
- struct cmsghdr *cmsg = CMSG_FIRSTHDR(&message);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(sizeof(int));
- int *fds = (int*)CMSG_DATA(cmsg);
- fds[0] = pipewire_fd;
- message.msg_controllen = cmsg->cmsg_len;
- sendmsg(rpc_fd, &message, 0);
- }
- return success;
-}
-
-int main(int argc, char **argv) {
- if(argc != 3) {
- fprintf(stderr, "usage: gsr-dbus-server <rpc-fd> <screencast-restore-token>\n");
- return 1;
- }
-
- const char *rpc_fd_str = argv[1];
- const char *screencast_restore_token = argv[2];
-
- int rpc_fd = -1;
- if(sscanf(rpc_fd_str, "%d", &rpc_fd) != 1) {
- fprintf(stderr, "gsr-dbus-server error: rpc-fd is not a number: %s\n", rpc_fd_str);
- return 1;
- }
-
- if(screencast_restore_token[0] == '\0')
- screencast_restore_token = NULL;
-
- gsr_dbus dbus;
- if(!gsr_dbus_init(&dbus, screencast_restore_token))
- return 1;
-
- /* Tell client we have started up */
- write(rpc_fd, "S", 1);
-
- gsr_dbus_request_message request;
- for(;;) {
- read(rpc_fd, &request, sizeof(request));
-
- if(request.protocol_version != GSR_DBUS_PROTOCOL_VERSION) {
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_ERROR,
- .error = (gsr_dbus_message_resp_error) {
- .error_code = 1
- }
- };
- snprintf(response.error.message, sizeof(response.error.message), "Client uses protocol version %d while the server is using protocol version %d", request.protocol_version, GSR_DBUS_PROTOCOL_VERSION);
- fprintf(stderr, "gsr-dbus-server error: %s\n", response.error.message);
- write(rpc_fd, &response, sizeof(response));
- continue;
- }
-
- int status = 0;
- switch(request.type) {
- case GSR_DBUS_MESSAGE_REQ_CREATE_SESSION: {
- status = handle_create_session(&dbus, rpc_fd, &request.create_session);
- break;
- }
- case GSR_DBUS_MESSAGE_REQ_SELECT_SOURCES: {
- status = handle_select_sources(&dbus, rpc_fd, &request.select_sources);
- break;
- }
- case GSR_DBUS_MESSAGE_REQ_START: {
- status = handle_start(&dbus, rpc_fd, &request.start);
- break;
- }
- case GSR_DBUS_MESSAGE_REQ_OPEN_PIPEWIRE_REMOTE: {
- if(!handle_open_pipewire_remote(&dbus, rpc_fd, &request.open_pipewire_remote))
- status = -1;
- break;
- }
- }
-
- if(status != 0) {
- gsr_dbus_response_message response = {
- .protocol_version = GSR_DBUS_PROTOCOL_VERSION,
- .type = GSR_DBUS_MESSAGE_RESP_ERROR,
- .error = (gsr_dbus_message_resp_error) {
- .error_code = status
- }
- };
- snprintf(response.error.message, sizeof(response.error.message), "%s", "Failed to handle request");
- write(rpc_fd, &response, sizeof(response));
- }
- }
-
- gsr_dbus_deinit(&dbus);
- return 0;
-} \ No newline at end of file
diff --git a/include/capture/capture.h b/include/capture/capture.h
index 634eee0..c2128c5 100644
--- a/include/capture/capture.h
+++ b/include/capture/capture.h
@@ -22,12 +22,13 @@ typedef struct {
} gsr_capture_metadata;
struct gsr_capture {
- /* These methods should not be called manually. Call gsr_capture_* instead */
+ /* These methods should not be called manually. Call gsr_capture_* instead. |capture_metdata->width| and |capture_metadata->height| should be set by this function */
int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
void (*tick)(gsr_capture *cap); /* can be NULL. If there is an event then |on_event| is called before this */
bool (*should_stop)(gsr_capture *cap, bool *err); /* can be NULL. If NULL, return false */
- int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion);
+ bool (*capture_has_synchronous_task)(gsr_capture *cap); /* can be NULL. If this returns true then the time spent in |capture| is ignored for video/audio (capture is paused while the synchronous task happens) */
+ int (*capture)(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion); /* Return 0 if the frame was captured */
bool (*uses_external_image)(gsr_capture *cap); /* can be NULL. If NULL, return false */
bool (*set_hdr_metadata)(gsr_capture *cap, AVMasteringDisplayMetadata *mastering_display_metadata, AVContentLightMetadata *light_metadata); /* can be NULL. If NULL, return false */
uint64_t (*get_window_id)(gsr_capture *cap); /* can be NULL. Returns 0 if unknown */
diff --git a/dbus/dbus_impl.h b/include/dbus.h
index c3f0751..229f7ea 100644
--- a/dbus/dbus_impl.h
+++ b/include/dbus.h
@@ -1,13 +1,25 @@
#ifndef GSR_DBUS_H
#define GSR_DBUS_H
-#include "portal.h"
#include <stdbool.h>
#include <stdint.h>
#include <dbus/dbus.h>
#define DBUS_RANDOM_STR_SIZE 16
+typedef enum {
+ GSR_PORTAL_CAPTURE_TYPE_MONITOR = 1 << 0,
+ GSR_PORTAL_CAPTURE_TYPE_WINDOW = 1 << 1,
+ GSR_PORTAL_CAPTURE_TYPE_VIRTUAL = 1 << 2,
+ GSR_PORTAL_CAPTURE_TYPE_ALL = GSR_PORTAL_CAPTURE_TYPE_MONITOR | GSR_PORTAL_CAPTURE_TYPE_WINDOW | GSR_PORTAL_CAPTURE_TYPE_VIRTUAL
+} gsr_portal_capture_type;
+
+typedef enum {
+ GSR_PORTAL_CURSOR_MODE_HIDDEN = 1 << 0,
+ GSR_PORTAL_CURSOR_MODE_EMBEDDED = 1 << 1,
+ GSR_PORTAL_CURSOR_MODE_METADATA = 1 << 2
+} gsr_portal_cursor_mode;
+
typedef struct {
DBusConnection *con;
DBusError err;
diff --git a/include/pipewire_video.h b/include/pipewire_video.h
index 785f56f..d98e43d 100644
--- a/include/pipewire_video.h
+++ b/include/pipewire_video.h
@@ -95,6 +95,9 @@ typedef struct {
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
size_t num_modifiers;
+
+ bool paused;
+ double paused_start_secs;
} gsr_pipewire_video;
/*
@@ -109,5 +112,6 @@ void gsr_pipewire_video_deinit(gsr_pipewire_video *self);
bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map texture_map, gsr_pipewire_video_region *region, gsr_pipewire_video_region *cursor_region, gsr_pipewire_video_dmabuf_data *dmabuf_data, int *num_dmabuf_data, uint32_t *fourcc, uint64_t *modifiers, bool *using_external_image);
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self);
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self);
+bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self);
#endif /* GSR_PIPEWIRE_VIDEO_H */
diff --git a/meson.build b/meson.build
index 43c429d..70f624b 100644
--- a/meson.build
+++ b/meson.build
@@ -76,7 +76,7 @@ uses_pipewire = false
if get_option('portal') == true
src += [
'src/capture/portal.c',
- 'dbus/client/dbus_client.c',
+ 'src/dbus.c',
'src/pipewire_video.c',
]
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
@@ -95,6 +95,7 @@ if uses_pipewire == true
dep += [
dependency('libpipewire-0.3'),
dependency('libspa-0.2'),
+ dependency('dbus-1'),
]
endif
@@ -103,10 +104,6 @@ add_project_arguments('-DGSR_VERSION="' + meson.project_version() + '"', languag
executable('gsr-kms-server', 'kms/server/kms_server.c', dependencies : dependency('libdrm'), c_args : '-fstack-protector-all', install : true)
executable('gpu-screen-recorder', src, dependencies : dep, install : true)
-if get_option('portal') == true
- executable('gsr-dbus-server', ['dbus/server/dbus_server.c', 'dbus/dbus_impl.c'], dependencies : dependency('dbus-1'), install : true)
-endif
-
if get_option('systemd') == true
install_data(files('extra/gpu-screen-recorder.service'), install_dir : 'lib/systemd/user')
endif
diff --git a/project.conf b/project.conf
index 7cf013b..0c235d4 100644
--- a/project.conf
+++ b/project.conf
@@ -5,7 +5,7 @@ version = "5.5.10"
platforms = ["posix"]
[config]
-ignore_dirs = ["kms/server", "build", "debug-build", "dbus/server"]
+ignore_dirs = ["kms/server", "build", "debug-build"]
#error_on_warning = "true"
[define]
diff --git a/src/capture/portal.c b/src/capture/portal.c
index 581a2ed..d2217d1 100644
--- a/src/capture/portal.c
+++ b/src/capture/portal.c
@@ -2,7 +2,7 @@
#include "../../include/color_conversion.h"
#include "../../include/egl.h"
#include "../../include/utils.h"
-#include "../../dbus/client/dbus_client.h"
+#include "../../include/dbus.h"
#include "../../include/pipewire_video.h"
#include <stdlib.h>
@@ -11,13 +11,22 @@
#include <limits.h>
#include <assert.h>
+#define PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE 60
+
+typedef enum {
+ PORTAL_CAPTURE_SETUP_IDLE,
+ PORTAL_CAPTURE_SETUP_IN_PROGRESS,
+ PORTAL_CAPTURE_SETUP_FINISHED,
+ PORTAL_CAPTURE_SETUP_FAILED
+} gsr_portal_capture_setup_state;
+
typedef struct {
gsr_capture_portal_params params;
gsr_texture_map texture_map;
- gsr_dbus_client dbus_client;
- char session_handle[128];
+ gsr_dbus dbus;
+ char *session_handle;
gsr_pipewire_video pipewire;
vec2i capture_size;
@@ -29,6 +38,9 @@ typedef struct {
uint32_t pipewire_fourcc;
uint64_t pipewire_modifiers;
bool using_external_image;
+
+ bool should_stop;
+ bool stop_is_error;
} gsr_capture_portal;
static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) {
@@ -59,7 +71,7 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
gsr_capture_portal_cleanup_plane_fds(self);
gsr_pipewire_video_deinit(&self->pipewire);
- gsr_dbus_client_deinit(&self->dbus_client);
+ gsr_dbus_deinit(&self->dbus);
}
static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) {
@@ -183,36 +195,36 @@ static int gsr_capture_portal_setup_dbus(gsr_capture_portal *self, int *pipewire
if(self->params.restore_portal_session)
gsr_capture_portal_get_restore_token_from_cache(restore_token, sizeof(restore_token), self->params.portal_session_token_filepath);
- if(!gsr_dbus_client_init(&self->dbus_client, restore_token))
+ if(!gsr_dbus_init(&self->dbus, restore_token))
return -1;
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n");
- response_status = gsr_dbus_client_screencast_create_session(&self->dbus_client, self->session_handle, sizeof(self->session_handle));
+ response_status = gsr_dbus_screencast_create_session(&self->dbus, &self->session_handle);
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: CreateSession failed\n");
return response_status;
}
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: SelectSources\n");
- response_status = gsr_dbus_client_screencast_select_sources(&self->dbus_client, self->session_handle, GSR_PORTAL_CAPTURE_TYPE_ALL, self->params.record_cursor ? GSR_PORTAL_CURSOR_MODE_EMBEDDED : GSR_PORTAL_CURSOR_MODE_HIDDEN);
+ response_status = gsr_dbus_screencast_select_sources(&self->dbus, self->session_handle, GSR_PORTAL_CAPTURE_TYPE_ALL, self->params.record_cursor ? GSR_PORTAL_CURSOR_MODE_EMBEDDED : GSR_PORTAL_CURSOR_MODE_HIDDEN);
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: SelectSources failed\n");
return response_status;
}
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: Start\n");
- response_status = gsr_dbus_client_screencast_start(&self->dbus_client, self->session_handle, pipewire_node);
+ response_status = gsr_dbus_screencast_start(&self->dbus, self->session_handle, pipewire_node);
if(response_status != 0) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
return response_status;
}
- const char *screencast_restore_token = gsr_dbus_client_screencast_get_restore_token(&self->dbus_client);
+ const char *screencast_restore_token = gsr_dbus_screencast_get_restore_token(&self->dbus);
if(screencast_restore_token)
gsr_capture_portal_save_restore_token(screencast_restore_token, self->params.portal_session_token_filepath);
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: OpenPipeWireRemote\n");
- if(!gsr_dbus_client_screencast_open_pipewire_remote(&self->dbus_client, self->session_handle, pipewire_fd)) {
+ if(!gsr_dbus_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) {
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
return -1;
}
@@ -239,45 +251,51 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
return false;
}
-static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
- gsr_capture_portal *self = cap->priv;
-
+static int gsr_capture_portal_setup(gsr_capture_portal *self, int fps) {
gsr_capture_portal_create_input_textures(self);
int pipewire_fd = 0;
uint32_t pipewire_node = 0;
const int response_status = gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node);
if(response_status != 0) {
- gsr_capture_portal_stop(self);
// Response status values:
// 0: Success, the request is carried out
// 1: The user cancelled the interaction
// 2: The user interaction was ended in some other way
// Response status value 2 happens usually if there was some kind of error in the desktop portal on the system
if(response_status == 2) {
- fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n");
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. Either you Wayland compositor doesn't support desktop portal capture or it's incorrectly setup on your system\n");
return 50;
} else if(response_status == 1) {
- fprintf(stderr, "gsr error: gsr_capture_portal_start: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n");
- return 60;
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: desktop portal capture failed. It seems like desktop portal capture was canceled by the user.\n");
+ return PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE;
} else {
return -1;
}
}
- fprintf(stderr, "gsr info: gsr_capture_portal_start: setting up pipewire\n");
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup: setting up pipewire\n");
/* TODO: support hdr when pipewire supports it */
/* gsr_pipewire closes the pipewire fd, even on failure */
- if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, capture_metadata->fps, self->params.record_cursor, self->params.egl)) {
- fprintf(stderr, "gsr error: gsr_capture_portal_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
- gsr_capture_portal_stop(self);
+ if(!gsr_pipewire_video_init(&self->pipewire, pipewire_fd, pipewire_node, fps, self->params.record_cursor, self->params.egl)) {
+ fprintf(stderr, "gsr error: gsr_capture_portal_setup: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
return -1;
}
- fprintf(stderr, "gsr info: gsr_capture_portal_start: pipewire setup finished\n");
+ fprintf(stderr, "gsr info: gsr_capture_portal_setup: pipewire setup finished\n");
- if(!gsr_capture_portal_get_frame_dimensions(self)) {
- gsr_capture_portal_stop(self);
+ if(!gsr_capture_portal_get_frame_dimensions(self))
return -1;
+
+ return 0;
+}
+
+static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
+ gsr_capture_portal *self = cap->priv;
+
+ const int result = gsr_capture_portal_setup(self, capture_metadata->fps);
+ if(result != 0) {
+ gsr_capture_portal_stop(self);
+ return result;
}
if(self->params.output_resolution.x == 0 && self->params.output_resolution.y == 0) {
@@ -296,10 +314,29 @@ static int max_int(int a, int b) {
return a > b ? a : b;
}
+static bool gsr_capture_portal_capture_has_synchronous_task(gsr_capture *cap) {
+ gsr_capture_portal *self = cap->priv;
+ return gsr_pipewire_video_should_restart(&self->pipewire);
+}
+
static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
(void)color_conversion;
gsr_capture_portal *self = cap->priv;
+ if(self->should_stop)
+ return -1;
+
+ if(gsr_pipewire_video_should_restart(&self->pipewire)) {
+ fprintf(stderr, "gsr info: gsr_capture_portal_capture: pipewire capture was paused, trying to start capture again\n");
+ gsr_capture_portal_stop(self);
+ const int result = gsr_capture_portal_setup(self, capture_metadata->fps);
+ if(result != 0) {
+ self->stop_is_error = result != PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE;
+ self->should_stop = true;
+ }
+ return -1;
+ }
+
/* TODO: Handle formats other than RGB(A) */
if(self->num_dmabuf_data == 0) {
if(gsr_pipewire_video_map_texture(&self->pipewire, self->texture_map, &self->region, &self->cursor_region, self->dmabuf_data, &self->num_dmabuf_data, &self->pipewire_fourcc, &self->pipewire_modifiers, &self->using_external_image)) {
@@ -362,6 +399,13 @@ static bool gsr_capture_portal_uses_external_image(gsr_capture *cap) {
return true;
}
+static bool gsr_capture_portal_should_stop(gsr_capture *cap, bool *err) {
+ gsr_capture_portal *self = cap->priv;
+ if(err)
+ *err = self->stop_is_error;
+ return self->should_stop;
+}
+
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
gsr_capture_portal *self = cap->priv;
return gsr_pipewire_video_is_damaged(&self->pipewire);
@@ -403,7 +447,8 @@ gsr_capture* gsr_capture_portal_create(const gsr_capture_portal_params *params)
*cap = (gsr_capture) {
.start = gsr_capture_portal_start,
.tick = NULL,
- .should_stop = NULL,
+ .should_stop = gsr_capture_portal_should_stop,
+ .capture_has_synchronous_task = gsr_capture_portal_capture_has_synchronous_task,
.capture = gsr_capture_portal_capture,
.uses_external_image = gsr_capture_portal_uses_external_image,
.is_damaged = gsr_capture_portal_is_damaged,
diff --git a/dbus/dbus_impl.c b/src/dbus.c
index 600fcc5..f12891f 100644
--- a/dbus/dbus_impl.c
+++ b/src/dbus.c
@@ -1,4 +1,4 @@
-#include "dbus_impl.h"
+#include "../include/dbus.h"
#include <sys/random.h>
diff --git a/src/main.cpp b/src/main.cpp
index 67619f9..f382dd0 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -5,7 +5,7 @@ extern "C" {
#include "../include/capture/kms.h"
#ifdef GSR_PORTAL
#include "../include/capture/portal.h"
-#include "../dbus/client/dbus_client.h"
+#include "../include/dbus.h"
#endif
#ifdef GSR_APP_AUDIO
#include "../include/pipewire_audio.h"
@@ -1847,15 +1847,15 @@ static void list_supported_capture_options(const gsr_window *window, const char
if(!wayland)
return;
- gsr_dbus_client dbus_client;
- if(!gsr_dbus_client_init(&dbus_client, NULL))
+ gsr_dbus dbus;
+ if(!gsr_dbus_init(&dbus, NULL))
return;
- char session_handle[128];
- if(gsr_dbus_client_screencast_create_session(&dbus_client, session_handle, sizeof(session_handle)) == 0)
+ char *session_handle = NULL;
+ if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0)
puts("portal");
- gsr_dbus_client_deinit(&dbus_client);
+ gsr_dbus_deinit(&dbus);
#endif
}
@@ -3312,7 +3312,7 @@ int main(int argc, char **argv) {
int damage_fps_counter = 0;
bool paused = false;
- double paused_time_offset = 0.0;
+ std::atomic<double> paused_time_offset(0.0);
double paused_time_start = 0.0;
bool replay_recording = false;
RecordingStartResult replay_recording_start_result;
@@ -3628,7 +3628,23 @@ int main(int argc, char **argv) {
// TODO: Dont do this if no damage?
egl.glClear(0);
+
+ bool capture_has_synchronous_task = false;
+ if(capture->capture_has_synchronous_task) {
+ capture_has_synchronous_task = capture->capture_has_synchronous_task(capture);
+ if(capture_has_synchronous_task) {
+ paused_time_start = clock_get_monotonic_seconds();
+ paused = true;
+ }
+ }
+
gsr_capture_capture(capture, &capture_metadata, &color_conversion);
+
+ if(capture_has_synchronous_task) {
+ paused_time_offset = paused_time_offset + (clock_get_monotonic_seconds() - paused_time_start);
+ paused = false;
+ }
+
gsr_egl_swap_buffers(&egl);
gsr_video_encoder_copy_textures_to_frame(video_encoder, video_frame, &color_conversion);
@@ -3678,7 +3694,7 @@ int main(int argc, char **argv) {
paused_time_start = clock_get_monotonic_seconds();
fprintf(stderr, "Paused\n");
} else {
- paused_time_offset += (clock_get_monotonic_seconds() - paused_time_start);
+ paused_time_offset = paused_time_offset + (clock_get_monotonic_seconds() - paused_time_start);
fprintf(stderr, "Unpaused\n");
}
diff --git a/src/pipewire_video.c b/src/pipewire_video.c
index 83b0bc3..277004c 100644
--- a/src/pipewire_video.c
+++ b/src/pipewire_video.c
@@ -280,13 +280,21 @@ static void on_param_changed_cb(void *user_data, uint32_t id, const struct spa_p
self->negotiated = true;
}
-static void on_state_changed_cb(void *user_data, enum pw_stream_state old, enum pw_stream_state state, const char *error) {
- (void)old;
+static void on_state_changed_cb(void *user_data, enum pw_stream_state prev_state, enum pw_stream_state new_state, const char *error) {
gsr_pipewire_video *self = user_data;
- fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n",
- (void*)self->stream, pw_stream_state_as_string(state),
+ fprintf(stderr, "gsr info: pipewire: stream %p previous state: \"%s\", new state: \"%s\" (error: %s)\n",
+ (void*)self->stream, pw_stream_state_as_string(prev_state), pw_stream_state_as_string(new_state),
error ? error : "none");
+
+ pthread_mutex_lock(&self->mutex);
+ if(new_state == PW_STREAM_STATE_PAUSED) {
+ self->paused_start_secs = clock_get_monotonic_seconds();
+ self->paused = true;
+ } else {
+ self->paused = false;
+ }
+ pthread_mutex_unlock(&self->mutex);
}
static const struct pw_stream_events stream_events = {
@@ -841,6 +849,9 @@ bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map te
}
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) {
+ if(!self->mutex_initialized)
+ return false;
+
bool damaged = false;
pthread_mutex_lock(&self->mutex);
damaged = self->damaged;
@@ -849,7 +860,21 @@ bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) {
}
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self) {
+ if(!self->mutex_initialized)
+ return;
+
pthread_mutex_lock(&self->mutex);
self->damaged = false;
pthread_mutex_unlock(&self->mutex);
}
+
+bool gsr_pipewire_video_should_restart(gsr_pipewire_video *self) {
+ if(!self->mutex_initialized)
+ return false;
+
+ bool should_restart = false;
+ pthread_mutex_lock(&self->mutex);
+ should_restart = self->paused && clock_get_monotonic_seconds() - self->paused_start_secs >= 3.0;
+ pthread_mutex_unlock(&self->mutex);
+ return should_restart;
+}