Compare commits

...

15 Commits
5.5.0 ... 5.5.4

Author SHA1 Message Date
dec05eba
637d6ebfd6 Match gsr monitor name with wayland monitor name. Thanks info@leocodes 2025-05-25 19:07:59 +02:00
dec05eba
5a94122b8f m 2025-05-21 18:33:19 +02:00
dec05eba
2b34c78459 Revert "temp test for cosmic"
This reverts commit 35222bafe2.
2025-05-15 19:48:25 +02:00
dec05eba
35222bafe2 temp test for cosmic 2025-05-15 19:17:04 +02:00
dec05eba
2a0fb9f449 cap_sys_nice fix for nixos 2025-05-11 18:39:13 +02:00
dec05eba
73fd0a30cd Change default quality for systemd service file 2025-05-11 10:41:15 +02:00
dec05eba
085d4632d2 Show warning for missing capability 2025-05-10 21:51:14 +02:00
dec05eba
39503de742 README 2025-05-10 20:34:22 +02:00
dec05eba
0ae5b83e47 5.5.2 2025-05-10 19:23:13 +02:00
dec05eba
e1992ae3be Revert "temp debug"
This reverts commit ce3203ebc7.
2025-05-10 18:38:58 +02:00
dec05eba
ce3203ebc7 temp debug 2025-05-10 18:33:35 +02:00
dec05eba
e3225bc628 Move dbus code to a separate process to allow gpu-screen-recorder to use cap_sys_nice for better recording performance on amd 2025-05-10 17:10:59 +02:00
dec05eba
811a14481d m 2025-05-05 14:00:10 +02:00
dec05eba
5cda6c0bf0 Fix incorrect replay duration saved for disk replay 2025-05-05 01:44:27 +02:00
dec05eba
3f18462af4 Change file extension of temporary replay data file from .mp4 to .gsr 2025-05-05 01:28:01 +02:00
25 changed files with 691 additions and 149 deletions

View File

@@ -187,9 +187,11 @@ You have to either record in hdr mode (-k `hevc_hdr` or -k `av1_hdr` option) to
## GPU Screen Recorder records night light when recording in HDR mode
You can record with desktop portal option (`-w portal`) instead which ignores night light, if you are ok with recording without HDR.
## Kdenlive says that the video is not usable for editing because it has variable frame rate
To fix this you can either record the video in .mkv format or constant frame rate (-fm cfr).
To fix this you can either just press cancel, which will allow you to continue or record the video in .mkv format or constant frame rate (-fm cfr).
## Colors look incorrect when recording HDR (with hevc_hdr/av1_hdr) or using an ICC profile
KDE Plasma version 6.2 broke HDR and ICC profiles for screen recorders. This was changed in KDE plasma version 6.3 and recording HDR works now, as long as you set HDR brightness to 100% (which means setting "Maximum SDR Brightness" in KDE plasma display settings to 203) and set color accuracy to "Prefer color accuracy". If you want to convert HDR to SDR then record with desktop portal option (`-w portal`) instead.
I don't know how well recording HDR works in wayland compositors other than KDE plasma.
## GPU Screen Recorder starts lagging after 30-40 minutes when launching GPU Screen Recorder from steam command launcher
This is a [steam issue](https://github.com/ValveSoftware/steam-for-linux/issues/11446). Prepend the gpu-screen-recorder command with `LD_PREFIX=""`, for example `LD_PREFIX="" gpu-screen-recorder -w screen -o video.mp4`.
## The video isn't smooth when gpu usage is 100%
If you are using the flatpak version of GPU Screen Recorder then try installing GPU Screen Recorder from a non-flatpak source instead (such as from aur or from source). Flatpak has a limitation that prevents GPU Screen Recorder from running faster when playing very heavy games.

6
TODO
View File

@@ -283,3 +283,9 @@ Add the option to set audio track name, for example with -a "track-name:blabla|d
Maybe disable qp/vbr for replay. In that case we can preallocate all replay data (for both ram and disk) and write to that directly when receiving packet (dont do that when also recording at the same time).
That could improve performance/disk write optimization and maybe even reduce ram usage because of less blocks/fragmentation.
When rpc is added add the option to add/remove audio devices/app audio and also overlays (from new capture sources).
Support hdr screenshot.
Recreate opengl context on loss. This can happen if there is a gpu driver bug, causing context to need to be recreated. This is a nice improvement to not break recording even with buggy driver.

269
dbus/client/dbus_client.c Normal file
View File

@@ -0,0 +1,269 @@
#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 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]);
/* 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: 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
View 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 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 */

View File

@@ -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";

View File

@@ -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
View 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
View 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
View 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;
}

View File

@@ -5,7 +5,7 @@ Description=GPU Screen Recorder Service
EnvironmentFile=-%h/.config/gpu-screen-recorder.env
Environment=WINDOW=screen
Environment=CONTAINER=mp4
Environment=QUALITY=50000
Environment=QUALITY=40000
Environment=BITRATE_MODE=cbr
Environment=CODEC=auto
Environment=AUDIO_CODEC=opus
@@ -27,4 +27,4 @@ Restart=on-failure
RestartSec=5s
[Install]
WantedBy=default.target
WantedBy=default.target

View File

@@ -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

View File

@@ -7,8 +7,6 @@
#include <stdbool.h>
#include <stdint.h>
#define CONNECTOR_TYPE_COUNTS 32
typedef struct AVCodecContext AVCodecContext;
typedef struct AVFrame AVFrame;
@@ -29,12 +27,6 @@ typedef struct {
bool found_monitor;
} get_monitor_by_name_userdata;
typedef struct {
int type;
int count;
int count_active;
} drm_connector_type_count;
double clock_get_monotonic_seconds(void);
bool generate_random_characters(char *buffer, int buffer_size, const char *alphabet, size_t alphabet_size);
bool generate_random_characters_standard_alphabet(char *buffer, int buffer_size);
@@ -46,7 +38,7 @@ bool get_monitor_by_name(const gsr_egl *egl, gsr_connection_type connection_type
bool drm_monitor_get_display_server_data(const gsr_window *window, const gsr_monitor *monitor, gsr_monitor_rotation *monitor_rotation, vec2i *monitor_position);
int get_connector_type_by_name(const char *name);
drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type);
int get_connector_type_id_by_name(const char *name);
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count);
bool gl_get_gpu_info(gsr_egl *egl, gsr_gpu_info *info);
@@ -61,7 +53,6 @@ int create_directory_recursive(char *path);
/* |img_attr| needs to be at least 44 in size */
void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, uint32_t height, const int *fds, const uint32_t *offsets, const uint32_t *pitches, const uint64_t *modifiers, int num_planes, bool use_modifier);
bool video_codec_context_is_vaapi(AVCodecContext *video_codec_context);
vec2i scale_keep_aspect_ratio(vec2i from, vec2i to);

View File

@@ -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;

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.5.0', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.5.4', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'
@@ -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

View File

@@ -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')

View File

@@ -1,11 +1,11 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
version = "5.5.0"
version = "5.5.4"
platforms = ["posix"]
[config]
ignore_dirs = ["kms/server", "build", "debug-build"]
ignore_dirs = ["kms/server", "build", "debug-build", "dbus/server"]
#error_on_warning = "true"
[define]

View File

@@ -3,4 +3,4 @@
window=$(xdotool selectwindow)
window_name=$(xdotool getwindowname "$window" || xdotool getwindowclassname "$window" || echo "Game")
window_name="$(echo "$window_name" | tr '/\\' '_')"
gpu-screen-recorder -w "$window" -f 60 -c mkv -a default_output -bm cbr -q 45000 -r 60 -o "$HOME/Videos/Replays/$window_name"
gpu-screen-recorder -w "$window" -f 60 -c mkv -a default_output -bm cbr -q 40000 -r 60 -o "$HOME/Videos/Replays/$window_name"

View File

@@ -3,4 +3,4 @@
pidof -q gpu-screen-recorder && exit 0
video_path="$HOME/Videos"
mkdir -p "$video_path"
gpu-screen-recorder -w screen -f 60 -a default_output -c mkv -bm cbr -q 45000 -r 30 -o "$video_path"
gpu-screen-recorder -w screen -f 60 -a default_output -c mkv -bm cbr -q 40000 -r 30 -o "$video_path"

View File

@@ -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;
}

View File

@@ -39,6 +39,12 @@ static void reset_cap_nice(void) {
if(!caps)
return;
cap_flag_value_t cap_sys_nice_value = CAP_CLEAR;
cap_get_flag(caps, CAP_SYS_NICE, CAP_EFFECTIVE, &cap_sys_nice_value);
if(cap_sys_nice_value == CAP_CLEAR) {
fprintf(stderr, "gsr warning: cap_sys_nice capability is missing on the gpu-screen-recorder binary, performance might be affected. If you are using the flatpak version of gpu-screen-recorder then the only fix is to use a non-flatpak version of gpu-screen-recorder\n");
}
const cap_value_t cap_to_remove = CAP_SYS_NICE;
cap_set_flag(caps, CAP_EFFECTIVE, 1, &cap_to_remove, CAP_CLEAR);
cap_set_flag(caps, CAP_PERMITTED, 1, &cap_to_remove, CAP_CLEAR);
@@ -60,7 +66,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
};

View File

@@ -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
}
@@ -3320,7 +3319,6 @@ int main(int argc, char **argv) {
std::mutex audio_filter_mutex;
const double record_start_time = clock_get_monotonic_seconds();
std::atomic<double> replay_start_time(record_start_time);
const size_t audio_buffer_size = audio_max_frame_size * 4 * 2; // max 4 bytes/sample, 2 channels
uint8_t *empty_audio = (uint8_t*)malloc(audio_buffer_size);
@@ -3761,10 +3759,8 @@ int main(int argc, char **argv) {
save_replay_output_filepath.clear();
save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, encoder.replay_buffer, arg_parser.filename, arg_parser.container_format, file_extension, arg_parser.date_folders, hdr, capture, current_save_replay_seconds);
if(arg_parser.restart_replay_on_save && current_save_replay_seconds == save_replay_seconds_full) {
if(arg_parser.restart_replay_on_save && current_save_replay_seconds == save_replay_seconds_full)
gsr_replay_buffer_clear(encoder.replay_buffer);
replay_start_time = clock_get_monotonic_seconds() - paused_time_offset;
}
}
const double frame_end = clock_get_monotonic_seconds();

View File

@@ -36,7 +36,7 @@ static gsr_replay_buffer_file* gsr_replay_buffer_file_create(char *replay_direct
}
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s_%d.mp4", replay_directory, FILE_PREFIX, (int)replay_storage_counter);
snprintf(filename, sizeof(filename), "%s/%s_%d.gsr", replay_directory, FILE_PREFIX, (int)replay_storage_counter);
*replay_storage_fd = creat(filename, 0700);
if(*replay_storage_fd <= 0) {
fprintf(stderr, "gsr error: gsr_av_packet_file_init: failed to create replay file: %s\n", filename);
@@ -71,7 +71,7 @@ static void gsr_replay_buffer_file_free(gsr_replay_buffer_file *self, const char
}
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s_%d.mp4", replay_directory, FILE_PREFIX, (int)self->id);
snprintf(filename, sizeof(filename), "%s/%s_%d.gsr", replay_directory, FILE_PREFIX, (int)self->id);
remove(filename);
if(self->packets) {
@@ -234,7 +234,7 @@ static uint8_t* gsr_replay_buffer_disk_iterator_get_packet_data(gsr_replay_buffe
if(file->fd <= 0) {
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s/%s_%d.mp4", self->replay_directory, FILE_PREFIX, (int)file->id);
snprintf(filename, sizeof(filename), "%s/%s_%d.gsr", self->replay_directory, FILE_PREFIX, (int)file->id);
file->fd = open(filename, O_RDONLY);
if(file->fd <= 0) {
fprintf(stderr, "gsr error: gsr_replay_buffer_disk_iterator_get_packet_data: failed to open file\n");
@@ -360,17 +360,20 @@ static gsr_replay_buffer_iterator gsr_replay_buffer_disk_find_keyframe(gsr_repla
gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
gsr_replay_buffer_iterator keyframe_iterator = {(size_t)-1, 0};
gsr_replay_buffer_lock(&self->replay_buffer);
size_t packet_index = start_iterator.packet_index;
for(size_t file_index = start_iterator.file_index; file_index < self->num_files; ++file_index) {
const gsr_replay_buffer_file *file = self->files[file_index];
for(size_t packet_index = start_iterator.packet_index; packet_index < file->num_packets; ++packet_index) {
for(; packet_index < file->num_packets; ++packet_index) {
const gsr_av_packet_disk *packet = &file->packets[packet_index];
if((packet->packet.flags & AV_PKT_FLAG_KEY) && (invert_stream_index ? packet->packet.stream_index != stream_index : packet->packet.stream_index == stream_index)) {
keyframe_iterator.packet_index = packet_index;
keyframe_iterator.file_index = file_index;
break;
goto done;
}
}
packet_index = 0;
}
done:
gsr_replay_buffer_unlock(&self->replay_buffer);
return keyframe_iterator;
}

View File

@@ -108,7 +108,7 @@ void for_each_active_monitor_output_x11_not_cached(Display *display, active_moni
// but gpu screen recorder captures the drm framebuffer instead of x11 api. This drm framebuffer which doesn't increase in size when using xrandr scaling.
// Maybe a better option would be to get the drm crtc size instead.
const XRRModeInfo *mode_info = get_mode_info(screen_res, crt_info->mode);
if(mode_info && out_info->nameLen < (int)sizeof(display_name)) {
if(mode_info) {
snprintf(display_name, sizeof(display_name), "%.*s", (int)out_info->nameLen, out_info->name);
const gsr_monitor_rotation rotation = x11_rotation_to_gsr_rotation(crt_info->rotation);
const vec2i monitor_size = get_monitor_size_rotated(mode_info->width, mode_info->height, rotation);
@@ -150,21 +150,22 @@ int get_connector_type_by_name(const char *name) {
return -1;
}
drm_connector_type_count* drm_connector_types_get_index(drm_connector_type_count *type_counts, int *num_type_counts, int connector_type) {
for(int i = 0; i < *num_type_counts; ++i) {
if(type_counts[i].type == connector_type)
return &type_counts[i];
int get_connector_type_id_by_name(const char *name) {
int len = strlen(name);
int num_start = 0;
for(int i = len - 1; i >= 0; --i) {
const bool is_num = name[i] >= '0' && name[i] <= '9';
if(!is_num) {
num_start = i + 1;
break;
}
}
if(*num_type_counts == CONNECTOR_TYPE_COUNTS)
return NULL;
const int num_len = len - num_start;
if(num_len <= 0)
return -1;
const int index = *num_type_counts;
type_counts[index].type = connector_type;
type_counts[index].count = 0;
type_counts[index].count_active = 0;
++*num_type_counts;
return &type_counts[index];
return atoi(name + num_start);
}
uint32_t monitor_identifier_from_type_and_count(int monitor_type_index, int monitor_type_count) {
@@ -195,9 +196,6 @@ static void for_each_active_monitor_output_drm(const char *card_path, active_mon
drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS];
int num_type_counts = 0;
char display_name[256];
drmModeResPtr resources = drmModeGetResources(fd);
if(resources) {
@@ -206,35 +204,29 @@ static void for_each_active_monitor_output_drm(const char *card_path, active_mon
if(!connector)
continue;
drm_connector_type_count *connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector->connector_type);
const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type);
const int connection_name_len = strlen(connection_name);
if(connector_type)
++connector_type->count;
if(connector->connection != DRM_MODE_CONNECTED) {
drmModeFreeConnector(connector);
continue;
}
if(connector_type)
++connector_type->count_active;
uint64_t crtc_id = 0;
connector_get_property_by_name(fd, connector, "CRTC_ID", &crtc_id);
drmModeCrtcPtr crtc = drmModeGetCrtc(fd, crtc_id);
if(connector_type && crtc_id > 0 && crtc && connection_name_len + 5 < (int)sizeof(display_name)) {
const int display_name_len = snprintf(display_name, sizeof(display_name), "%s-%d", connection_name, connector_type->count);
const char *connection_name = drmModeGetConnectorTypeName(connector->connector_type);
if(connection_name && crtc_id > 0 && crtc) {
const int connector_type_index_name = get_connector_type_by_name(display_name);
gsr_monitor monitor = {
const int display_name_len = snprintf(display_name, sizeof(display_name), "%s-%u", connection_name, connector->connector_type_id);
const gsr_monitor monitor = {
.name = display_name,
.name_len = display_name_len,
.pos = { .x = crtc->x, .y = crtc->y },
.size = { .x = (int)crtc->width, .y = (int)crtc->height },
.connector_id = connector->connector_id,
.rotation = GSR_MONITOR_ROT_0,
.monitor_identifier = connector_type_index_name != -1 ? monitor_identifier_from_type_and_count(connector_type_index_name, connector_type->count_active) : 0
.monitor_identifier = connector_type_index_name != -1 ? monitor_identifier_from_type_and_count(connector_type_index_name, connector->connector_type_id) : 0
};
callback(&monitor, userdata);
}
@@ -607,33 +599,6 @@ void setup_dma_buf_attrs(intptr_t *img_attr, uint32_t format, uint32_t width, ui
assert(img_attr_index <= 44);
}
static VADisplay video_codec_context_get_vaapi_display(AVCodecContext *video_codec_context) {
AVBufferRef *hw_frames_ctx = video_codec_context->hw_frames_ctx;
if(!hw_frames_ctx)
return NULL;
AVHWFramesContext *hw_frame_context = (AVHWFramesContext*)hw_frames_ctx->data;
AVHWDeviceContext *device_context = (AVHWDeviceContext*)hw_frame_context->device_ctx;
if(device_context->type != AV_HWDEVICE_TYPE_VAAPI)
return NULL;
AVVAAPIDeviceContext *vactx = device_context->hwctx;
return vactx->display;
}
bool video_codec_context_is_vaapi(AVCodecContext *video_codec_context) {
if(!video_codec_context)
return false;
AVBufferRef *hw_frames_ctx = video_codec_context->hw_frames_ctx;
if(!hw_frames_ctx)
return false;
AVHWFramesContext *hw_frame_context = (AVHWFramesContext*)hw_frames_ctx->data;
AVHWDeviceContext *device_context = (AVHWDeviceContext*)hw_frame_context->device_ctx;
return device_context->type == AV_HWDEVICE_TYPE_VAAPI;
}
vec2i scale_keep_aspect_ratio(vec2i from, vec2i to) {
if(from.x == 0 || from.y == 0)
return (vec2i){0, 0};

View File

@@ -339,24 +339,13 @@ static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) {
static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_window *window, active_monitor_callback callback, void *userdata) {
const gsr_window_wayland *self = window->priv;
drm_connector_type_count type_counts[CONNECTOR_TYPE_COUNTS];
int num_type_counts = 0;
for(int i = 0; i < self->num_outputs; ++i) {
const gsr_wayland_output *output = &self->outputs[i];
if(!output->name)
continue;
const int connector_type_index = get_connector_type_by_name(output->name);
drm_connector_type_count *connector_type = NULL;
if(connector_type_index != -1)
connector_type = drm_connector_types_get_index(type_counts, &num_type_counts, connector_type_index);
if(connector_type) {
++connector_type->count;
++connector_type->count_active;
}
const int connector_type_id = get_connector_type_id_by_name(output->name);
const gsr_monitor monitor = {
.name = output->name,
.name_len = strlen(output->name),
@@ -364,7 +353,7 @@ static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_w
.size = { .x = output->size.x, .y = output->size.y },
.connector_id = 0,
.rotation = wayland_transform_to_gsr_rotation(output->transform),
.monitor_identifier = connector_type ? monitor_identifier_from_type_and_count(connector_type_index, connector_type->count_active) : 0
.monitor_identifier = (connector_type_index != -1 && connector_type_id != -1) ? monitor_identifier_from_type_and_count(connector_type_index, connector_type_id) : 0
};
callback(&monitor, userdata);
}