mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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 */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "dbus_impl.h"
|
||||
#include "../include/dbus.h"
|
||||
|
||||
#include <sys/random.h>
|
||||
|
||||
32
src/main.cpp
32
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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user