mirror of
https://repo.dec05eba.com/gpu-screen-recorder
synced 2026-03-31 09:07:13 +09:00
Move dbus code to a separate process to allow gpu-screen-recorder to use cap_sys_nice for better recording performance on amd
This commit is contained in:
265
dbus/client/dbus_client.c
Normal file
265
dbus/client/dbus_client.c
Normal file
@@ -0,0 +1,265 @@
|
||||
#include "dbus_client.h"
|
||||
#include "../protocol.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/wait.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 side 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]);
|
||||
|
||||
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: execvp failed, 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;
|
||||
}
|
||||
36
dbus/client/dbus_client.h
Normal file
36
dbus/client/dbus_client.h
Normal file
@@ -0,0 +1,36 @@
|
||||
#ifndef GSR_DBUS_CLIENT_H
|
||||
#define GSR_DBUS_CLIENT_H
|
||||
|
||||
/*
|
||||
Using a client-server architecture is needed for dbus because cap_sys_nice doesn't work with 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,5 +1,6 @@
|
||||
#include "../include/dbus.h"
|
||||
#include "../include/utils.h"
|
||||
#include "dbus_impl.h"
|
||||
|
||||
#include <sys/random.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
@@ -28,6 +29,25 @@ typedef struct {
|
||||
};
|
||||
} dict_entry;
|
||||
|
||||
static bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size) {
|
||||
/* TODO: Use other functions on other platforms than linux */
|
||||
if(getrandom(buffer, buffer_size, 0) < buffer_size) {
|
||||
fprintf(stderr, "Failed to get random bytes, error: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < buffer_size; ++i) {
|
||||
unsigned char c = *(unsigned char*)&buffer[i];
|
||||
buffer[i] = alphabet[c % alphabet_size];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size) {
|
||||
return generate_random_characters(buffer, buffer_size, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", 62);
|
||||
}
|
||||
|
||||
static const char* dict_value_type_to_string(dict_value_type type) {
|
||||
switch(type) {
|
||||
case DICT_TYPE_STRING: return "string";
|
||||
@@ -1,6 +1,7 @@
|
||||
#ifndef GSR_DBUS_H
|
||||
#define GSR_DBUS_H
|
||||
|
||||
#include "portal.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <dbus/dbus.h>
|
||||
@@ -17,19 +18,6 @@ typedef struct {
|
||||
char *screencast_restore_token;
|
||||
} gsr_dbus;
|
||||
|
||||
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;
|
||||
|
||||
/* Blocking. TODO: Make non-blocking */
|
||||
bool gsr_dbus_init(gsr_dbus *self, const char *screencast_restore_token);
|
||||
void gsr_dbus_deinit(gsr_dbus *self);
|
||||
17
dbus/portal.h
Normal file
17
dbus/portal.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#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 */
|
||||
86
dbus/protocol.h
Normal file
86
dbus/protocol.h
Normal file
@@ -0,0 +1,86 @@
|
||||
#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 */
|
||||
175
dbus/server/dbus_server.c
Normal file
175
dbus/server/dbus_server.c
Normal file
@@ -0,0 +1,175 @@
|
||||
#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;
|
||||
}
|
||||
@@ -4,14 +4,7 @@
|
||||
/usr/sbin/setcap cap_sys_admin+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gsr-kms-server \
|
||||
|| echo "\n!!! Please re-run install as root\n"
|
||||
|
||||
# Cant do this because it breaks desktop portal (create session)!!!.
|
||||
# For some reason the desktop portal tries to access /proc/gpu-screen-recorder-pid/root from the portal process
|
||||
# which doesn't work because for some reason CAP_SYS_NICE on a program makes /proc/self/root not readable by other processes.
|
||||
# The reason portal reads that file might be because portal seems to have a security feature where its able to identify the
|
||||
# process and if the session token is stolen by another application then it will ignore the session token as it wasn't that
|
||||
# application that created the session token.
|
||||
# ---
|
||||
# This is needed (for EGL_CONTEXT_PRIORITY_HIGH_IMG) to allow gpu screen recorder to run faster than the heaviest application on AMD.
|
||||
# For example when trying to record a game at 60 fps and the game drops to 45 fps in some place that would also make gpu screen recorder
|
||||
# drop to 45 fps unless this setcap is used.
|
||||
#/usr/sbin/setcap cap_sys_nice+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gpu-screen-recorder
|
||||
# drop to 45 fps unless this setcap is used. Recording would also drop to below 60 fps in some games even though they run above 60 fps.
|
||||
/usr/sbin/setcap cap_sys_nice+ep ${MESON_INSTALL_DESTDIR_PREFIX}/bin/gpu-screen-recorder
|
||||
|
||||
@@ -387,6 +387,7 @@ void cleanup_socket(gsr_kms_client *self, bool kill_server) {
|
||||
|
||||
if(kill_server && self->kms_server_pid > 0) {
|
||||
kill(self->kms_server_pid, SIGKILL);
|
||||
// TODO:
|
||||
//int status;
|
||||
//waitpid(self->kms_server_pid, &status, 0);
|
||||
self->kms_server_pid = -1;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <locale.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
@@ -434,6 +435,8 @@ static double clock_get_monotonic_seconds(void) {
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
setlocale(LC_ALL, "C"); // Sigh... stupid C
|
||||
|
||||
int res = 0;
|
||||
int socket_fd = 0;
|
||||
gsr_drm drm;
|
||||
|
||||
@@ -76,10 +76,9 @@ uses_pipewire = false
|
||||
if get_option('portal') == true
|
||||
src += [
|
||||
'src/capture/portal.c',
|
||||
'src/dbus.c',
|
||||
'dbus/client/dbus_client.c',
|
||||
'src/pipewire_video.c',
|
||||
]
|
||||
dep += dependency('dbus-1')
|
||||
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
|
||||
uses_pipewire = true
|
||||
endif
|
||||
@@ -104,6 +103,10 @@ 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
option('systemd', type : 'boolean', value : true, description : 'Install systemd service file')
|
||||
option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability on gsr-kms-server binary to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland')
|
||||
option('capabilities', type : 'boolean', value : true, description : 'Set binary admin capability on gsr-kms-server binary to remove password prompt when recording monitor (without desktop portal option) on amd/intel or nvidia wayland. Also sets nice capability on gpu-screen-recorder to allow it to run as a high priority graphics process for better performance')
|
||||
option('nvidia_suspend_fix', type : 'boolean', value : true, description : 'Install nvidia modprobe config file to tell nvidia driver to preserve video memory on suspend. This is a workaround for an nvidia driver bug that breaks cuda (and gpu screen recorder) on suspend')
|
||||
option('portal', type : 'boolean', value : true, description : 'Build with support for xdg desktop portal ScreenCast capture (wayland only) (-w portal option). Requires pipewire')
|
||||
option('app_audio', type : 'boolean', value : true, description : 'Build with support for recording a single audio source (-a app: option). Requires pipewire')
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "../../include/color_conversion.h"
|
||||
#include "../../include/egl.h"
|
||||
#include "../../include/utils.h"
|
||||
#include "../../include/dbus.h"
|
||||
#include "../../dbus/client/dbus_client.h"
|
||||
#include "../../include/pipewire_video.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -16,8 +16,8 @@ typedef struct {
|
||||
|
||||
gsr_texture_map texture_map;
|
||||
|
||||
gsr_dbus dbus;
|
||||
char *session_handle;
|
||||
gsr_dbus_client dbus_client;
|
||||
char session_handle[128];
|
||||
|
||||
gsr_pipewire_video pipewire;
|
||||
vec2i capture_size;
|
||||
@@ -52,15 +52,8 @@ static void gsr_capture_portal_stop(gsr_capture_portal *self) {
|
||||
}
|
||||
|
||||
gsr_capture_portal_cleanup_plane_fds(self);
|
||||
|
||||
gsr_pipewire_video_deinit(&self->pipewire);
|
||||
|
||||
if(self->session_handle) {
|
||||
free(self->session_handle);
|
||||
self->session_handle = NULL;
|
||||
}
|
||||
|
||||
gsr_dbus_deinit(&self->dbus);
|
||||
gsr_dbus_client_deinit(&self->dbus_client);
|
||||
}
|
||||
|
||||
static void gsr_capture_portal_create_input_textures(gsr_capture_portal *self) {
|
||||
@@ -195,36 +188,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_init(&self->dbus, restore_token))
|
||||
if(!gsr_dbus_client_init(&self->dbus_client, restore_token))
|
||||
return -1;
|
||||
|
||||
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n");
|
||||
response_status = gsr_dbus_screencast_create_session(&self->dbus, &self->session_handle);
|
||||
response_status = gsr_dbus_client_screencast_create_session(&self->dbus_client, self->session_handle, sizeof(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_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);
|
||||
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);
|
||||
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_screencast_start(&self->dbus, self->session_handle, pipewire_node);
|
||||
response_status = gsr_dbus_client_screencast_start(&self->dbus_client, 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_screencast_get_restore_token(&self->dbus);
|
||||
const char *screencast_restore_token = gsr_dbus_client_screencast_get_restore_token(&self->dbus_client);
|
||||
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_screencast_open_pipewire_remote(&self->dbus, self->session_handle, pipewire_fd)) {
|
||||
if(!gsr_dbus_client_screencast_open_pipewire_remote(&self->dbus_client, self->session_handle, pipewire_fd)) {
|
||||
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ static bool gsr_egl_create_window(gsr_egl *self) {
|
||||
|
||||
const int32_t ctxattr[] = {
|
||||
EGL_CONTEXT_CLIENT_VERSION, 2,
|
||||
//EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
|
||||
EGL_CONTEXT_PRIORITY_LEVEL_IMG, EGL_CONTEXT_PRIORITY_HIGH_IMG, /* requires cap_sys_nice, ignored otherwise */
|
||||
EGL_NONE, EGL_NONE
|
||||
};
|
||||
|
||||
|
||||
15
src/main.cpp
15
src/main.cpp
@@ -5,7 +5,7 @@ extern "C" {
|
||||
#include "../include/capture/kms.h"
|
||||
#ifdef GSR_PORTAL
|
||||
#include "../include/capture/portal.h"
|
||||
#include "../include/dbus.h"
|
||||
#include "../dbus/client/dbus_client.h"
|
||||
#endif
|
||||
#ifdef GSR_APP_AUDIO
|
||||
#include "../include/pipewire_audio.h"
|
||||
@@ -1843,16 +1843,15 @@ static void list_supported_capture_options(const gsr_window *window, const char
|
||||
if(!wayland)
|
||||
return;
|
||||
|
||||
gsr_dbus dbus;
|
||||
if(!gsr_dbus_init(&dbus, NULL))
|
||||
gsr_dbus_client dbus_client;
|
||||
if(!gsr_dbus_client_init(&dbus_client, NULL))
|
||||
return;
|
||||
|
||||
char *session_handle = NULL;
|
||||
if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0) {
|
||||
free(session_handle);
|
||||
char session_handle[128];
|
||||
if(gsr_dbus_client_screencast_create_session(&dbus_client, session_handle, sizeof(session_handle)) == 0)
|
||||
puts("portal");
|
||||
}
|
||||
gsr_dbus_deinit(&dbus);
|
||||
|
||||
gsr_dbus_client_deinit(&dbus_client);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user