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;
|
} gsr_capture_metadata;
|
||||||
|
|
||||||
struct gsr_capture {
|
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);
|
int (*start)(gsr_capture *cap, gsr_capture_metadata *capture_metadata);
|
||||||
void (*on_event)(gsr_capture *cap, gsr_egl *egl); /* can be NULL */
|
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 */
|
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 */
|
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 (*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 */
|
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 */
|
uint64_t (*get_window_id)(gsr_capture *cap); /* can be NULL. Returns 0 if unknown */
|
||||||
|
|||||||
@@ -1,13 +1,25 @@
|
|||||||
#ifndef GSR_DBUS_H
|
#ifndef GSR_DBUS_H
|
||||||
#define GSR_DBUS_H
|
#define GSR_DBUS_H
|
||||||
|
|
||||||
#include "portal.h"
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <dbus/dbus.h>
|
#include <dbus/dbus.h>
|
||||||
|
|
||||||
#define DBUS_RANDOM_STR_SIZE 16
|
#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 {
|
typedef struct {
|
||||||
DBusConnection *con;
|
DBusConnection *con;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
@@ -95,6 +95,9 @@ typedef struct {
|
|||||||
|
|
||||||
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
|
uint64_t modifiers[GSR_PIPEWIRE_VIDEO_MAX_MODIFIERS];
|
||||||
size_t num_modifiers;
|
size_t num_modifiers;
|
||||||
|
|
||||||
|
bool paused;
|
||||||
|
double paused_start_secs;
|
||||||
} gsr_pipewire_video;
|
} 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_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);
|
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self);
|
||||||
void gsr_pipewire_video_clear_damage(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 */
|
#endif /* GSR_PIPEWIRE_VIDEO_H */
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ uses_pipewire = false
|
|||||||
if get_option('portal') == true
|
if get_option('portal') == true
|
||||||
src += [
|
src += [
|
||||||
'src/capture/portal.c',
|
'src/capture/portal.c',
|
||||||
'dbus/client/dbus_client.c',
|
'src/dbus.c',
|
||||||
'src/pipewire_video.c',
|
'src/pipewire_video.c',
|
||||||
]
|
]
|
||||||
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
|
add_project_arguments('-DGSR_PORTAL', language : ['c', 'cpp'])
|
||||||
@@ -95,6 +95,7 @@ if uses_pipewire == true
|
|||||||
dep += [
|
dep += [
|
||||||
dependency('libpipewire-0.3'),
|
dependency('libpipewire-0.3'),
|
||||||
dependency('libspa-0.2'),
|
dependency('libspa-0.2'),
|
||||||
|
dependency('dbus-1'),
|
||||||
]
|
]
|
||||||
endif
|
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('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)
|
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
|
if get_option('systemd') == true
|
||||||
install_data(files('extra/gpu-screen-recorder.service'), install_dir : 'lib/systemd/user')
|
install_data(files('extra/gpu-screen-recorder.service'), install_dir : 'lib/systemd/user')
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ version = "5.5.10"
|
|||||||
platforms = ["posix"]
|
platforms = ["posix"]
|
||||||
|
|
||||||
[config]
|
[config]
|
||||||
ignore_dirs = ["kms/server", "build", "debug-build", "dbus/server"]
|
ignore_dirs = ["kms/server", "build", "debug-build"]
|
||||||
#error_on_warning = "true"
|
#error_on_warning = "true"
|
||||||
|
|
||||||
[define]
|
[define]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "../../include/color_conversion.h"
|
#include "../../include/color_conversion.h"
|
||||||
#include "../../include/egl.h"
|
#include "../../include/egl.h"
|
||||||
#include "../../include/utils.h"
|
#include "../../include/utils.h"
|
||||||
#include "../../dbus/client/dbus_client.h"
|
#include "../../include/dbus.h"
|
||||||
#include "../../include/pipewire_video.h"
|
#include "../../include/pipewire_video.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -11,13 +11,22 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <assert.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 {
|
typedef struct {
|
||||||
gsr_capture_portal_params params;
|
gsr_capture_portal_params params;
|
||||||
|
|
||||||
gsr_texture_map texture_map;
|
gsr_texture_map texture_map;
|
||||||
|
|
||||||
gsr_dbus_client dbus_client;
|
gsr_dbus dbus;
|
||||||
char session_handle[128];
|
char *session_handle;
|
||||||
|
|
||||||
gsr_pipewire_video pipewire;
|
gsr_pipewire_video pipewire;
|
||||||
vec2i capture_size;
|
vec2i capture_size;
|
||||||
@@ -29,6 +38,9 @@ typedef struct {
|
|||||||
uint32_t pipewire_fourcc;
|
uint32_t pipewire_fourcc;
|
||||||
uint64_t pipewire_modifiers;
|
uint64_t pipewire_modifiers;
|
||||||
bool using_external_image;
|
bool using_external_image;
|
||||||
|
|
||||||
|
bool should_stop;
|
||||||
|
bool stop_is_error;
|
||||||
} gsr_capture_portal;
|
} gsr_capture_portal;
|
||||||
|
|
||||||
static void gsr_capture_portal_cleanup_plane_fds(gsr_capture_portal *self) {
|
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_capture_portal_cleanup_plane_fds(self);
|
||||||
gsr_pipewire_video_deinit(&self->pipewire);
|
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) {
|
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)
|
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);
|
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;
|
return -1;
|
||||||
|
|
||||||
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: CreateSession\n");
|
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) {
|
if(response_status != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: CreateSession failed\n");
|
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: CreateSession failed\n");
|
||||||
return response_status;
|
return response_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: SelectSources\n");
|
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) {
|
if(response_status != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: SelectSources failed\n");
|
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: SelectSources failed\n");
|
||||||
return response_status;
|
return response_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "gsr info: gsr_capture_portal_setup_dbus: Start\n");
|
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) {
|
if(response_status != 0) {
|
||||||
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
|
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: Start failed\n");
|
||||||
return response_status;
|
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)
|
if(screencast_restore_token)
|
||||||
gsr_capture_portal_save_restore_token(screencast_restore_token, self->params.portal_session_token_filepath);
|
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");
|
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");
|
fprintf(stderr, "gsr error: gsr_capture_portal_setup_dbus: OpenPipeWireRemote failed\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -239,45 +251,51 @@ static bool gsr_capture_portal_get_frame_dimensions(gsr_capture_portal *self) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int gsr_capture_portal_start(gsr_capture *cap, gsr_capture_metadata *capture_metadata) {
|
static int gsr_capture_portal_setup(gsr_capture_portal *self, int fps) {
|
||||||
gsr_capture_portal *self = cap->priv;
|
|
||||||
|
|
||||||
gsr_capture_portal_create_input_textures(self);
|
gsr_capture_portal_create_input_textures(self);
|
||||||
|
|
||||||
int pipewire_fd = 0;
|
int pipewire_fd = 0;
|
||||||
uint32_t pipewire_node = 0;
|
uint32_t pipewire_node = 0;
|
||||||
const int response_status = gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node);
|
const int response_status = gsr_capture_portal_setup_dbus(self, &pipewire_fd, &pipewire_node);
|
||||||
if(response_status != 0) {
|
if(response_status != 0) {
|
||||||
gsr_capture_portal_stop(self);
|
|
||||||
// Response status values:
|
// Response status values:
|
||||||
// 0: Success, the request is carried out
|
// 0: Success, the request is carried out
|
||||||
// 1: The user cancelled the interaction
|
// 1: The user cancelled the interaction
|
||||||
// 2: The user interaction was ended in some other way
|
// 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
|
// Response status value 2 happens usually if there was some kind of error in the desktop portal on the system
|
||||||
if(response_status == 2) {
|
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;
|
return 50;
|
||||||
} else if(response_status == 1) {
|
} 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");
|
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 60;
|
return PORTAL_CAPTURE_CANCELED_BY_USER_EXIT_CODE;
|
||||||
} else {
|
} else {
|
||||||
return -1;
|
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 */
|
/* TODO: support hdr when pipewire supports it */
|
||||||
/* gsr_pipewire closes the pipewire fd, even on failure */
|
/* 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)) {
|
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_start: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
|
fprintf(stderr, "gsr error: gsr_capture_portal_setup: failed to setup pipewire with fd: %d, node: %" PRIu32 "\n", pipewire_fd, pipewire_node);
|
||||||
gsr_capture_portal_stop(self);
|
|
||||||
return -1;
|
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)) {
|
if(!gsr_capture_portal_get_frame_dimensions(self))
|
||||||
gsr_capture_portal_stop(self);
|
|
||||||
return -1;
|
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) {
|
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;
|
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) {
|
static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *capture_metadata, gsr_color_conversion *color_conversion) {
|
||||||
(void)color_conversion;
|
(void)color_conversion;
|
||||||
gsr_capture_portal *self = cap->priv;
|
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) */
|
/* TODO: Handle formats other than RGB(A) */
|
||||||
if(self->num_dmabuf_data == 0) {
|
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)) {
|
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;
|
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) {
|
static bool gsr_capture_portal_is_damaged(gsr_capture *cap) {
|
||||||
gsr_capture_portal *self = cap->priv;
|
gsr_capture_portal *self = cap->priv;
|
||||||
return gsr_pipewire_video_is_damaged(&self->pipewire);
|
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) {
|
*cap = (gsr_capture) {
|
||||||
.start = gsr_capture_portal_start,
|
.start = gsr_capture_portal_start,
|
||||||
.tick = NULL,
|
.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,
|
.capture = gsr_capture_portal_capture,
|
||||||
.uses_external_image = gsr_capture_portal_uses_external_image,
|
.uses_external_image = gsr_capture_portal_uses_external_image,
|
||||||
.is_damaged = gsr_capture_portal_is_damaged,
|
.is_damaged = gsr_capture_portal_is_damaged,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#include "dbus_impl.h"
|
#include "../include/dbus.h"
|
||||||
|
|
||||||
#include <sys/random.h>
|
#include <sys/random.h>
|
||||||
|
|
||||||
32
src/main.cpp
32
src/main.cpp
@@ -5,7 +5,7 @@ extern "C" {
|
|||||||
#include "../include/capture/kms.h"
|
#include "../include/capture/kms.h"
|
||||||
#ifdef GSR_PORTAL
|
#ifdef GSR_PORTAL
|
||||||
#include "../include/capture/portal.h"
|
#include "../include/capture/portal.h"
|
||||||
#include "../dbus/client/dbus_client.h"
|
#include "../include/dbus.h"
|
||||||
#endif
|
#endif
|
||||||
#ifdef GSR_APP_AUDIO
|
#ifdef GSR_APP_AUDIO
|
||||||
#include "../include/pipewire_audio.h"
|
#include "../include/pipewire_audio.h"
|
||||||
@@ -1847,15 +1847,15 @@ static void list_supported_capture_options(const gsr_window *window, const char
|
|||||||
if(!wayland)
|
if(!wayland)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
gsr_dbus_client dbus_client;
|
gsr_dbus dbus;
|
||||||
if(!gsr_dbus_client_init(&dbus_client, NULL))
|
if(!gsr_dbus_init(&dbus, NULL))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
char session_handle[128];
|
char *session_handle = NULL;
|
||||||
if(gsr_dbus_client_screencast_create_session(&dbus_client, session_handle, sizeof(session_handle)) == 0)
|
if(gsr_dbus_screencast_create_session(&dbus, &session_handle) == 0)
|
||||||
puts("portal");
|
puts("portal");
|
||||||
|
|
||||||
gsr_dbus_client_deinit(&dbus_client);
|
gsr_dbus_deinit(&dbus);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3312,7 +3312,7 @@ int main(int argc, char **argv) {
|
|||||||
int damage_fps_counter = 0;
|
int damage_fps_counter = 0;
|
||||||
|
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
double paused_time_offset = 0.0;
|
std::atomic<double> paused_time_offset(0.0);
|
||||||
double paused_time_start = 0.0;
|
double paused_time_start = 0.0;
|
||||||
bool replay_recording = false;
|
bool replay_recording = false;
|
||||||
RecordingStartResult replay_recording_start_result;
|
RecordingStartResult replay_recording_start_result;
|
||||||
@@ -3628,7 +3628,23 @@ int main(int argc, char **argv) {
|
|||||||
|
|
||||||
// TODO: Dont do this if no damage?
|
// TODO: Dont do this if no damage?
|
||||||
egl.glClear(0);
|
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);
|
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_egl_swap_buffers(&egl);
|
||||||
gsr_video_encoder_copy_textures_to_frame(video_encoder, video_frame, &color_conversion);
|
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();
|
paused_time_start = clock_get_monotonic_seconds();
|
||||||
fprintf(stderr, "Paused\n");
|
fprintf(stderr, "Paused\n");
|
||||||
} else {
|
} 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");
|
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;
|
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) {
|
static void on_state_changed_cb(void *user_data, enum pw_stream_state prev_state, enum pw_stream_state new_state, const char *error) {
|
||||||
(void)old;
|
|
||||||
gsr_pipewire_video *self = user_data;
|
gsr_pipewire_video *self = user_data;
|
||||||
|
|
||||||
fprintf(stderr, "gsr info: pipewire: stream %p state: \"%s\" (error: %s)\n",
|
fprintf(stderr, "gsr info: pipewire: stream %p previous state: \"%s\", new state: \"%s\" (error: %s)\n",
|
||||||
(void*)self->stream, pw_stream_state_as_string(state),
|
(void*)self->stream, pw_stream_state_as_string(prev_state), pw_stream_state_as_string(new_state),
|
||||||
error ? error : "none");
|
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 = {
|
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) {
|
bool gsr_pipewire_video_is_damaged(gsr_pipewire_video *self) {
|
||||||
|
if(!self->mutex_initialized)
|
||||||
|
return false;
|
||||||
|
|
||||||
bool damaged = false;
|
bool damaged = false;
|
||||||
pthread_mutex_lock(&self->mutex);
|
pthread_mutex_lock(&self->mutex);
|
||||||
damaged = self->damaged;
|
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) {
|
void gsr_pipewire_video_clear_damage(gsr_pipewire_video *self) {
|
||||||
|
if(!self->mutex_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
pthread_mutex_lock(&self->mutex);
|
pthread_mutex_lock(&self->mutex);
|
||||||
self->damaged = false;
|
self->damaged = false;
|
||||||
pthread_mutex_unlock(&self->mutex);
|
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