Compare commits

..

9 Commits

Author SHA1 Message Date
dec05eba
c1af143406 5.12.2 2026-01-23 00:41:44 +01:00
dec05eba
4cebc3f0ee Fix negative relative position not working (incorrectly clamped to 0, 100) 2026-01-23 00:10:11 +01:00
dec05eba
f6013d094d Lock around get packet data 2026-01-21 18:29:41 +01:00
dec05eba
45daccff84 Clone replay buffer immediately, only one short mutex lock 2026-01-21 17:29:03 +01:00
dec05eba
ede1e46503 Fix possible replay race condition, move mutex out of replay 2026-01-21 02:24:30 +01:00
dec05eba
6b37b82f97 5.12.1 2026-01-20 18:46:22 +01:00
dec05eba
19add54c0c Make sure logical_size isn't 0 on gamescope 2026-01-20 14:39:11 +01:00
dec05eba
a44e119c43 Print correct regions when region is invalid 2026-01-20 13:56:52 +01:00
dec05eba
054282bafe Wayland region: fix incorrect region captured if monitor is scaled 2026-01-20 13:32:42 +01:00
14 changed files with 180 additions and 135 deletions

View File

@@ -24,8 +24,12 @@ typedef struct {
typedef struct {
gsr_replay_buffer *replay_buffer;
pthread_mutex_t file_write_mutex;
bool mutex_created;
bool file_write_mutex_created;
pthread_mutex_t replay_mutex;
bool replay_mutex_created;
gsr_encoder_recording_destination recording_destinations[GSR_MAX_RECORDING_DESTINATIONS];
size_t num_recording_destinations;

View File

@@ -2,7 +2,6 @@
#define GSR_REPLAY_BUFFER_H
#include "../defs.h"
#include <pthread.h>
#include <stdbool.h>
#include <libavcodec/packet.h>
@@ -27,17 +26,11 @@ struct gsr_replay_buffer {
/* Returns {-1, 0} if not found */
gsr_replay_buffer_iterator (*find_keyframe)(gsr_replay_buffer *self, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index);
bool (*iterator_next)(gsr_replay_buffer *self, gsr_replay_buffer_iterator *iterator);
pthread_mutex_t mutex;
bool mutex_initialized;
gsr_replay_buffer *original_replay_buffer;
};
gsr_replay_buffer* gsr_replay_buffer_create(gsr_replay_storage replay_storage, const char *replay_directory, double replay_buffer_time, size_t replay_buffer_num_packets);
void gsr_replay_buffer_destroy(gsr_replay_buffer *self);
void gsr_replay_buffer_lock(gsr_replay_buffer *self);
void gsr_replay_buffer_unlock(gsr_replay_buffer *self);
bool gsr_replay_buffer_append(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp);
void gsr_replay_buffer_clear(gsr_replay_buffer *self);
AVPacket* gsr_replay_buffer_iterator_get_packet(gsr_replay_buffer *self, gsr_replay_buffer_iterator iterator);

View File

@@ -16,6 +16,8 @@ typedef struct {
int name_len;
vec2i pos; /* This is 0, 0 on wayland. Use |drm_monitor_get_display_server_data| to get the position */
vec2i size;
vec2i logical_pos;
vec2i logical_size;
uint32_t connector_id; /* Only on x11 and drm */
gsr_monitor_rotation rotation; /* Only on x11 and wayland */
uint32_t monitor_identifier; /* On x11 this is the crtc id */

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.12.0', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.12.2', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'

View File

@@ -1,7 +1,7 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
version = "5.12.0"
version = "5.12.2"
platforms = ["posix"]
[config]

View File

@@ -9,7 +9,6 @@
#include <inttypes.h>
#include <limits.h>
#include <assert.h>
#include <errno.h>
#include <libgen.h>
#include <sys/stat.h>

View File

@@ -14,9 +14,17 @@ bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size
if(pthread_mutex_init(&self->file_write_mutex, NULL) != 0) {
fprintf(stderr, "gsr error: gsr_encoder_init: failed to create mutex\n");
gsr_encoder_deinit(self);
return false;
}
self->mutex_created = true;
self->file_write_mutex_created = true;
if(pthread_mutex_init(&self->replay_mutex, NULL) != 0) {
fprintf(stderr, "gsr error: gsr_encoder_init: failed to create mutex\n");
gsr_encoder_deinit(self);
return false;
}
self->replay_mutex_created = true;
if(replay_buffer_num_packets > 0) {
self->replay_buffer = gsr_replay_buffer_create(replay_storage, replay_directory, replay_buffer_time, replay_buffer_num_packets);
@@ -31,14 +39,21 @@ bool gsr_encoder_init(gsr_encoder *self, gsr_replay_storage replay_storage, size
}
void gsr_encoder_deinit(gsr_encoder *self) {
if(self->mutex_created) {
self->mutex_created = false;
if(self->replay_buffer) {
pthread_mutex_lock(&self->replay_mutex);
gsr_replay_buffer_destroy(self->replay_buffer);
self->replay_buffer = NULL;
pthread_mutex_unlock(&self->replay_mutex);
}
if(self->file_write_mutex_created) {
self->file_write_mutex_created = false;
pthread_mutex_destroy(&self->file_write_mutex);
}
if(self->replay_buffer) {
gsr_replay_buffer_destroy(self->replay_buffer);
self->replay_buffer = NULL;
if(self->replay_mutex_created) {
self->replay_mutex_created = false;
pthread_mutex_destroy(&self->replay_mutex);
}
self->num_recording_destinations = 0;
@@ -60,9 +75,11 @@ void gsr_encoder_receive_packets(gsr_encoder *self, AVCodecContext *codec_contex
av_packet->dts = pts;
if(self->replay_buffer) {
pthread_mutex_lock(&self->replay_mutex);
const double time_now = clock_get_monotonic_seconds();
if(!gsr_replay_buffer_append(self->replay_buffer, av_packet, time_now))
fprintf(stderr, "gsr error: gsr_encoder_receive_packets: failed to add replay buffer data\n");
pthread_mutex_unlock(&self->replay_mutex);
}
pthread_mutex_lock(&self->file_write_mutex);

View File

@@ -110,26 +110,23 @@ typedef struct {
char *output_name;
vec2i monitor_pos;
vec2i monitor_size;
double monitor_scale_inverted;
} MonitorByPositionCallback;
static void get_monitor_by_position_callback(const gsr_monitor *monitor, void *userdata) {
MonitorByPositionCallback *data = (MonitorByPositionCallback*)userdata;
vec2i monitor_position = monitor->pos;
vec2i monitor_size = monitor->size;
if(gsr_window_get_display_server(data->window) == GSR_DISPLAY_SERVER_WAYLAND) {
gsr_monitor_rotation monitor_rotation = GSR_MONITOR_ROT_0;
drm_monitor_get_display_server_data(data->window, monitor, &monitor_rotation, &monitor_position);
if(monitor_rotation == GSR_MONITOR_ROT_90 || monitor_rotation == GSR_MONITOR_ROT_270)
std::swap(monitor_size.x, monitor_size.y);
}
const vec2i monitor_position = monitor->logical_pos;
const vec2i monitor_size = monitor->size;
const vec2i monitor_logical_size = monitor->logical_size;
if(!data->output_name && data->position.x >= monitor_position.x && data->position.x <= monitor_position.x + monitor_size.x
&& data->position.y >= monitor_position.y && data->position.y <= monitor_position.y + monitor_size.y)
if(!data->output_name && data->position.x >= monitor_position.x && data->position.x <= monitor_position.x + monitor_logical_size.x
&& data->position.y >= monitor_position.y && data->position.y <= monitor_position.y + monitor_logical_size.y)
{
data->output_name = strdup(monitor->name);
data->monitor_pos = monitor_position;
data->monitor_size = monitor_size;
data->monitor_scale_inverted = (double)monitor_size.x / (double)monitor_logical_size.x;
}
}
@@ -1204,9 +1201,9 @@ struct VideoSource {
CaptureSource *capture_source;
};
static RecordingStartResult start_recording_create_streams(const char *filename, const args_parser &args_parser, AVCodecContext *video_codec_context, const std::vector<AudioTrack> &audio_tracks, bool hdr, std::vector<VideoSource> &video_sources) {
static RecordingStartResult start_recording_create_streams(const char *filename, const args_parser &arg_parser, AVCodecContext *video_codec_context, const std::vector<AudioTrack> &audio_tracks, bool hdr, std::vector<VideoSource> &video_sources) {
AVFormatContext *av_format_context;
avformat_alloc_output_context2(&av_format_context, nullptr, args_parser.container_format, filename);
avformat_alloc_output_context2(&av_format_context, nullptr, arg_parser.container_format, filename);
AVStream *video_stream = create_stream(av_format_context, video_codec_context);
avcodec_parameters_from_context(video_stream->codecpar, video_codec_context);
@@ -1231,8 +1228,8 @@ static RecordingStartResult start_recording_create_streams(const char *filename,
AVDictionary *options = nullptr;
av_dict_set(&options, "strict", "experimental", 0);
if(args_parser.ffmpeg_opts)
av_dict_parse_string(&options, args_parser.ffmpeg_opts, "=", ";", 0);
if(arg_parser.ffmpeg_opts)
av_dict_parse_string(&options, arg_parser.ffmpeg_opts, "=", ";", 0);
const int header_write_ret = avformat_write_header(av_format_context, &options);
av_dict_free(&options);
@@ -1296,48 +1293,60 @@ struct AudioPtsOffset {
int stream_index = 0;
};
static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector<AudioTrack> &audio_tracks, gsr_replay_buffer *replay_buffer, const args_parser &arg_parser, const std::string &file_extension, bool date_folders, bool hdr, std::vector<VideoSource> &video_sources, int current_save_replay_seconds) {
static void save_replay_async(AVCodecContext *video_codec_context, int video_stream_index, const std::vector<AudioTrack> &audio_tracks, gsr_encoder *encoder, const args_parser &arg_parser, const std::string &file_extension, bool date_folders, bool hdr, std::vector<VideoSource> &video_sources, int current_save_replay_seconds) {
if(save_replay_thread.valid())
return;
const gsr_replay_buffer_iterator search_start_iterator = current_save_replay_seconds == save_replay_seconds_full ? gsr_replay_buffer_iterator{0, 0} : gsr_replay_buffer_find_packet_index_by_time_passed(replay_buffer, current_save_replay_seconds);
const gsr_replay_buffer_iterator video_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, search_start_iterator, video_stream_index, false);
if(video_start_iterator.packet_index == (size_t)-1) {
fprintf(stderr, "gsr error: failed to save replay: failed to find a video keyframe. perhaps replay was saved too fast, before anything has been recorded\n");
return;
}
const int64_t video_pts_offset = gsr_replay_buffer_iterator_get_packet(replay_buffer, video_start_iterator)->pts;
std::vector<AudioPtsOffset> audio_pts_offsets;
audio_pts_offsets.reserve(audio_tracks.size());
for(const AudioTrack &audio_track : audio_tracks) {
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(replay_buffer, video_start_iterator, audio_track.stream_index, false);
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(replay_buffer, audio_start_iterator)->pts;
audio_pts_offsets.push_back(AudioPtsOffset{audio_pts_offset, audio_track.stream_index});
}
gsr_replay_buffer *cloned_replay_buffer = gsr_replay_buffer_clone(replay_buffer);
pthread_mutex_lock(&encoder->replay_mutex);
gsr_replay_buffer *cloned_replay_buffer = gsr_replay_buffer_clone(encoder->replay_buffer);
pthread_mutex_unlock(&encoder->replay_mutex);
if(!cloned_replay_buffer) {
// TODO: Return this error to mark the replay as failed
fprintf(stderr, "gsr error: failed to save replay: failed to clone replay buffer\n");
return;
}
const gsr_replay_buffer_iterator search_start_iterator = current_save_replay_seconds == save_replay_seconds_full ? gsr_replay_buffer_iterator{0, 0} : gsr_replay_buffer_find_packet_index_by_time_passed(cloned_replay_buffer, current_save_replay_seconds);
const gsr_replay_buffer_iterator video_start_iterator = gsr_replay_buffer_find_keyframe(cloned_replay_buffer, search_start_iterator, video_stream_index, false);
if(video_start_iterator.packet_index == (size_t)-1) {
fprintf(stderr, "gsr error: failed to save replay: failed to find a video keyframe. perhaps replay was saved too fast, before anything has been recorded\n");
pthread_mutex_lock(&encoder->replay_mutex);
gsr_replay_buffer_destroy(cloned_replay_buffer);
pthread_mutex_unlock(&encoder->replay_mutex);
return;
}
const int64_t video_pts_offset = gsr_replay_buffer_iterator_get_packet(cloned_replay_buffer, video_start_iterator)->pts;
std::vector<AudioPtsOffset> audio_pts_offsets;
audio_pts_offsets.reserve(audio_tracks.size());
for(const AudioTrack &audio_track : audio_tracks) {
const gsr_replay_buffer_iterator audio_start_iterator = gsr_replay_buffer_find_keyframe(cloned_replay_buffer, video_start_iterator, audio_track.stream_index, false);
const int64_t audio_pts_offset = audio_start_iterator.packet_index == (size_t)-1 ? 0 : gsr_replay_buffer_iterator_get_packet(cloned_replay_buffer, audio_start_iterator)->pts;
audio_pts_offsets.push_back(AudioPtsOffset{audio_pts_offset, audio_track.stream_index});
}
std::string output_filepath = create_new_recording_filepath_from_timestamp(arg_parser.filename, "Replay", file_extension, date_folders);
RecordingStartResult recording_start_result = start_recording_create_streams(output_filepath.c_str(), arg_parser, video_codec_context, audio_tracks, hdr, video_sources);
if(!recording_start_result.av_format_context)
if(!recording_start_result.av_format_context) {
pthread_mutex_lock(&encoder->replay_mutex);
gsr_replay_buffer_destroy(cloned_replay_buffer);
pthread_mutex_unlock(&encoder->replay_mutex);
return;
}
save_replay_output_filepath = std::move(output_filepath);
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offsets{std::move(audio_pts_offsets)}, video_codec_context, cloned_replay_buffer]() mutable {
save_replay_thread = std::async(std::launch::async, [video_stream_index, recording_start_result, video_start_iterator, video_pts_offset, audio_pts_offsets{std::move(audio_pts_offsets)}, video_codec_context, cloned_replay_buffer, encoder]() mutable {
gsr_replay_buffer_iterator replay_iterator = video_start_iterator;
for(;;) {
AVPacket *replay_packet = gsr_replay_buffer_iterator_get_packet(cloned_replay_buffer, replay_iterator);
uint8_t *replay_packet_data = NULL;
if(replay_packet)
if(replay_packet) {
pthread_mutex_lock(&encoder->replay_mutex);
replay_packet_data = gsr_replay_buffer_iterator_get_packet_data(cloned_replay_buffer, replay_iterator);
pthread_mutex_unlock(&encoder->replay_mutex);
}
if(!replay_packet) {
fprintf(stderr, "gsr error: save_replay_async: no replay packet\n");
@@ -1400,7 +1409,10 @@ static void save_replay_async(AVCodecContext *video_codec_context, int video_str
}
stop_recording_close_streams(recording_start_result.av_format_context);
pthread_mutex_lock(&encoder->replay_mutex);
gsr_replay_buffer_destroy(cloned_replay_buffer);
pthread_mutex_unlock(&encoder->replay_mutex);
});
}
@@ -2140,9 +2152,9 @@ static std::string validate_monitor_get_valid(const gsr_egl *egl, const char* wi
return capture_source_result;
}
static std::string get_monitor_by_region_center(const gsr_egl *egl, vec2i region_position, vec2i region_size, vec2i *monitor_pos, vec2i *monitor_size) {
static std::string get_monitor_by_region_center(const gsr_egl *egl, vec2i region_position, vec2i region_size, vec2i *monitor_pos, vec2i *monitor_size, double *monitor_scale_inverted) {
const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_WAYLAND;
MonitorByPositionCallback data;
data.window = egl->window;
@@ -2150,6 +2162,7 @@ static std::string get_monitor_by_region_center(const gsr_egl *egl, vec2i region
data.output_name = NULL;
data.monitor_pos = {0, 0};
data.monitor_size = {0, 0};
data.monitor_scale_inverted = 1.0;
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, get_monitor_by_position_callback, &data);
std::string result;
@@ -2159,6 +2172,7 @@ static std::string get_monitor_by_region_center(const gsr_egl *egl, vec2i region
}
*monitor_pos = data.monitor_pos;
*monitor_size = data.monitor_size;
*monitor_scale_inverted = data.monitor_scale_inverted;
return result;
}
@@ -2226,18 +2240,25 @@ static gsr_capture* create_monitor_capture(const args_parser &arg_parser, gsr_eg
}
}
static void monitor_output_callback_print_region(const gsr_monitor *monitor, void *userdata) {
const vec2i monitor_position = monitor->logical_pos;
const vec2i monitor_size = monitor->logical_size;
fprintf(stderr, " \"%.*s\" (%dx%d+%d+%d)\n", monitor->name_len, monitor->name, monitor_size.x, monitor_size.y, monitor_position.x, monitor_position.y);
}
static std::string region_get_data(gsr_egl *egl, vec2i *region_size, vec2i *region_position) {
vec2i monitor_pos = {0, 0};
vec2i monitor_size = {0, 0};
std::string window = get_monitor_by_region_center(egl, *region_position, *region_size, &monitor_pos, &monitor_size);
double monitor_scale_inverted = 1.0;
std::string window = get_monitor_by_region_center(egl, *region_position, *region_size, &monitor_pos, &monitor_size, &monitor_scale_inverted);
if(window.empty()) {
const bool is_x11 = gsr_window_get_display_server(egl->window) == GSR_DISPLAY_SERVER_X11;
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_DRM;
const gsr_connection_type connection_type = is_x11 ? GSR_CONNECTION_X11 : GSR_CONNECTION_WAYLAND;
fprintf(stderr, "gsr error: the region %dx%d+%d+%d doesn't match any monitor. Available monitors and their regions:\n", region_size->x, region_size->y, region_position->x, region_position->y);
MonitorOutputCallbackUserdata userdata;
userdata.window = egl->window;
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print, &userdata);
for_each_active_monitor_output(egl->window, egl->card_path, connection_type, monitor_output_callback_print_region, &userdata);
_exit(51);
}
@@ -2248,6 +2269,11 @@ static std::string region_get_data(gsr_egl *egl, vec2i *region_size, vec2i *regi
} else {
region_position->x -= monitor_pos.x;
region_position->y -= monitor_pos.y;
region_position->x *= monitor_scale_inverted;
region_position->y *= monitor_scale_inverted;
region_size->x *= monitor_scale_inverted;
region_size->y *= monitor_scale_inverted;
}
return window;
}
@@ -2774,8 +2800,8 @@ static bool string_to_bool(const char *str, size_t len, bool *value) {
}
static int clamp_scalar(int value) {
if(value < 0)
return 0;
if(value < -100)
return -100;
else if(value > 100)
return 100;
else
@@ -4579,10 +4605,13 @@ int main(int argc, char **argv) {
save_replay_seconds = 0;
save_replay_output_filepath.clear();
save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, encoder.replay_buffer, arg_parser, file_extension, arg_parser.date_folders, hdr, video_sources, current_save_replay_seconds);
save_replay_async(video_codec_context, VIDEO_STREAM_INDEX, audio_tracks, &encoder, arg_parser, file_extension, arg_parser.date_folders, hdr, video_sources, 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) {
pthread_mutex_lock(&encoder.replay_mutex);
gsr_replay_buffer_clear(encoder.replay_buffer);
pthread_mutex_unlock(&encoder.replay_mutex);
}
}
const double time_at_frame_end = clock_get_monotonic_seconds() - paused_time_offset;

View File

@@ -16,48 +16,14 @@ gsr_replay_buffer* gsr_replay_buffer_create(gsr_replay_storage replay_storage, c
replay_buffer = gsr_replay_buffer_disk_create(replay_directory, replay_buffer_time);
break;
}
replay_buffer->mutex_initialized = false;
replay_buffer->original_replay_buffer = NULL;
if(pthread_mutex_init(&replay_buffer->mutex, NULL) != 0) {
gsr_replay_buffer_destroy(replay_buffer);
return NULL;
}
replay_buffer->mutex_initialized = true;
return replay_buffer;
}
void gsr_replay_buffer_destroy(gsr_replay_buffer *self) {
self->destroy(self);
if(self->mutex_initialized && !self->original_replay_buffer) {
pthread_mutex_destroy(&self->mutex);
self->mutex_initialized = false;
}
self->original_replay_buffer = NULL;
free(self);
}
void gsr_replay_buffer_lock(gsr_replay_buffer *self) {
if(self->original_replay_buffer) {
gsr_replay_buffer_lock(self->original_replay_buffer);
return;
}
if(self->mutex_initialized)
pthread_mutex_lock(&self->mutex);
}
void gsr_replay_buffer_unlock(gsr_replay_buffer *self) {
if(self->original_replay_buffer) {
gsr_replay_buffer_unlock(self->original_replay_buffer);
return;
}
if(self->mutex_initialized)
pthread_mutex_unlock(&self->mutex);
}
bool gsr_replay_buffer_append(gsr_replay_buffer *self, const AVPacket *av_packet, double timestamp) {
return self->append(self, av_packet, timestamp);
}

View File

@@ -94,7 +94,6 @@ static void gsr_replay_buffer_file_unref(gsr_replay_buffer_file *self, const cha
static void gsr_replay_buffer_disk_clear(gsr_replay_buffer *replay_buffer) {
gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
for(size_t i = 0; i < self->num_files; ++i) {
gsr_replay_buffer_file_unref(self->files[i], self->replay_directory);
@@ -107,7 +106,6 @@ static void gsr_replay_buffer_disk_clear(gsr_replay_buffer *replay_buffer) {
}
self->storage_num_bytes_written = 0;
gsr_replay_buffer_unlock(&self->replay_buffer);
}
static void gsr_replay_buffer_disk_destroy(gsr_replay_buffer *replay_buffer) {
@@ -197,7 +195,6 @@ static void gsr_replay_buffer_disk_remove_first_file(gsr_replay_buffer_disk *sel
static bool gsr_replay_buffer_disk_append(gsr_replay_buffer *replay_buffer, const AVPacket *av_packet, double timestamp) {
gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
bool success = false;
gsr_replay_buffer_lock(&self->replay_buffer);
if(self->storage_fd <= 0) {
if(!gsr_replay_buffer_disk_create_next_file(self, timestamp))
@@ -215,7 +212,6 @@ static bool gsr_replay_buffer_disk_append(gsr_replay_buffer *replay_buffer, cons
success = data_written;
done:
gsr_replay_buffer_unlock(&self->replay_buffer);
return success;
}
@@ -265,11 +261,7 @@ static gsr_replay_buffer* gsr_replay_buffer_disk_clone(gsr_replay_buffer *replay
return NULL;
gsr_replay_buffer_disk_set_impl_funcs(destination);
gsr_replay_buffer_lock(&self->replay_buffer);
destination->replay_buffer.original_replay_buffer = replay_buffer;
destination->replay_buffer.mutex = self->replay_buffer.mutex;
destination->replay_buffer.mutex_initialized = self->replay_buffer.mutex_initialized;
destination->replay_buffer_time = self->replay_buffer_time;
destination->storage_counter = self->storage_counter;
destination->storage_num_bytes_written = self->storage_num_bytes_written;
@@ -283,7 +275,6 @@ static gsr_replay_buffer* gsr_replay_buffer_disk_clone(gsr_replay_buffer *replay
snprintf(destination->replay_directory, sizeof(destination->replay_directory), "%s", self->replay_directory);
destination->owns_directory = false;
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer*)destination;
}
@@ -319,11 +310,9 @@ static size_t gsr_replay_buffer_file_find_packet_index_by_time_passed(const gsr_
/* Binary search */
static gsr_replay_buffer_iterator gsr_replay_buffer_disk_find_file_index_by_time_passed(gsr_replay_buffer *replay_buffer, int seconds) {
gsr_replay_buffer_disk *self = (gsr_replay_buffer_disk*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
const double now = clock_get_monotonic_seconds();
if(self->num_files == 0) {
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer_iterator){0, 0};
}
@@ -352,14 +341,12 @@ static gsr_replay_buffer_iterator gsr_replay_buffer_disk_find_file_index_by_time
const gsr_replay_buffer_file *file = self->files[file_index];
const size_t packet_index = gsr_replay_buffer_file_find_packet_index_by_time_passed(file, seconds);
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer_iterator){packet_index, file_index};
}
static gsr_replay_buffer_iterator gsr_replay_buffer_disk_find_keyframe(gsr_replay_buffer *replay_buffer, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index) {
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];
@@ -374,7 +361,6 @@ static gsr_replay_buffer_iterator gsr_replay_buffer_disk_find_keyframe(gsr_repla
packet_index = 0;
}
done:
gsr_replay_buffer_unlock(&self->replay_buffer);
return keyframe_iterator;
}

View File

@@ -54,7 +54,6 @@ static void gsr_av_packet_ram_unref(gsr_av_packet_ram *self) {
static void gsr_replay_buffer_ram_destroy(gsr_replay_buffer *replay_buffer) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
for(size_t i = 0; i < self->num_packets; ++i) {
if(self->packets[i]) {
gsr_av_packet_ram_unref(self->packets[i]);
@@ -62,7 +61,6 @@ static void gsr_replay_buffer_ram_destroy(gsr_replay_buffer *replay_buffer) {
}
}
self->num_packets = 0;
gsr_replay_buffer_unlock(&self->replay_buffer);
if(self->packets) {
free(self->packets);
@@ -75,12 +73,9 @@ static void gsr_replay_buffer_ram_destroy(gsr_replay_buffer *replay_buffer) {
static bool gsr_replay_buffer_ram_append(gsr_replay_buffer *replay_buffer, const AVPacket *av_packet, double timestamp) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
gsr_av_packet_ram *packet = gsr_av_packet_ram_create(av_packet, timestamp);
if(!packet) {
gsr_replay_buffer_unlock(&self->replay_buffer);
if(!packet)
return false;
}
if(self->packets[self->index]) {
gsr_av_packet_ram_unref(self->packets[self->index]);
@@ -93,13 +88,11 @@ static bool gsr_replay_buffer_ram_append(gsr_replay_buffer *replay_buffer, const
if(self->num_packets > self->capacity_num_packets)
self->num_packets = self->capacity_num_packets;
gsr_replay_buffer_unlock(&self->replay_buffer);
return true;
}
static void gsr_replay_buffer_ram_clear(gsr_replay_buffer *replay_buffer) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
for(size_t i = 0; i < self->num_packets; ++i) {
if(self->packets[i]) {
gsr_av_packet_ram_unref(self->packets[i]);
@@ -108,7 +101,6 @@ static void gsr_replay_buffer_ram_clear(gsr_replay_buffer *replay_buffer) {
}
self->num_packets = 0;
self->index = 0;
gsr_replay_buffer_unlock(&self->replay_buffer);
}
static gsr_av_packet_ram* gsr_replay_buffer_ram_get_packet_at_index(gsr_replay_buffer *replay_buffer, size_t index) {
@@ -141,17 +133,12 @@ static gsr_replay_buffer* gsr_replay_buffer_ram_clone(gsr_replay_buffer *replay_
return NULL;
gsr_replay_buffer_ram_set_impl_funcs(destination);
gsr_replay_buffer_lock(&self->replay_buffer);
destination->replay_buffer.original_replay_buffer = replay_buffer;
destination->replay_buffer.mutex = self->replay_buffer.mutex;
destination->replay_buffer.mutex_initialized = self->replay_buffer.mutex_initialized;
destination->capacity_num_packets = self->capacity_num_packets;
destination->index = self->index;
destination->packets = calloc(destination->capacity_num_packets, sizeof(gsr_av_packet_ram*));
if(!destination->packets) {
free(destination);
gsr_replay_buffer_unlock(&self->replay_buffer);
return NULL;
}
@@ -160,18 +147,15 @@ static gsr_replay_buffer* gsr_replay_buffer_ram_clone(gsr_replay_buffer *replay_
destination->packets[i] = gsr_av_packet_ram_ref(self->packets[i]);
}
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer*)destination;
}
/* Binary search */
static gsr_replay_buffer_iterator gsr_replay_buffer_ram_find_packet_index_by_time_passed(gsr_replay_buffer *replay_buffer, int seconds) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
gsr_replay_buffer_lock(&self->replay_buffer);
const double now = clock_get_monotonic_seconds();
if(self->num_packets == 0) {
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer_iterator){0, 0};
}
@@ -194,14 +178,12 @@ static gsr_replay_buffer_iterator gsr_replay_buffer_ram_find_packet_index_by_tim
}
}
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer_iterator){index, 0};
}
static gsr_replay_buffer_iterator gsr_replay_buffer_ram_find_keyframe(gsr_replay_buffer *replay_buffer, gsr_replay_buffer_iterator start_iterator, int stream_index, bool invert_stream_index) {
gsr_replay_buffer_ram *self = (gsr_replay_buffer_ram*)replay_buffer;
size_t keyframe_index = (size_t)-1;
gsr_replay_buffer_lock(&self->replay_buffer);
for(size_t i = start_iterator.packet_index; i < self->num_packets; ++i) {
const gsr_av_packet_ram *packet = gsr_replay_buffer_ram_get_packet_at_index(replay_buffer, i);
if((packet->packet.flags & AV_PKT_FLAG_KEY) && (invert_stream_index ? packet->packet.stream_index != stream_index : packet->packet.stream_index == stream_index)) {
@@ -209,7 +191,6 @@ static gsr_replay_buffer_iterator gsr_replay_buffer_ram_find_keyframe(gsr_replay
break;
}
}
gsr_replay_buffer_unlock(&self->replay_buffer);
return (gsr_replay_buffer_iterator){keyframe_index, 0};
}

View File

@@ -121,6 +121,8 @@ void for_each_active_monitor_output_x11_not_cached(Display *display, active_moni
.name_len = out_info->nameLen,
.pos = { .x = crt_info->x, .y = crt_info->y },
.size = monitor_size,
.logical_pos = { .x = crt_info->x, .y = crt_info->y },
.logical_size = monitor_size,
.connector_id = x11_output_get_connector_id(display, screen_res->outputs[i], randr_connector_id_atom),
.rotation = rotation,
.monitor_identifier = out_info->crtc
@@ -229,6 +231,8 @@ static void for_each_active_monitor_output_drm(const char *card_path, active_mon
.name_len = display_name_len,
.pos = { .x = crtc->x, .y = crtc->y },
.size = { .x = (int)crtc->width, .y = (int)crtc->height },
.logical_pos = { .x = crtc->x, .y = crtc->y },
.logical_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->connector_type_id) : 0
@@ -264,6 +268,8 @@ static void get_monitor_by_name_callback(const gsr_monitor *monitor, void *userd
if(!data->found_monitor && strcmp(data->name, monitor->name) == 0) {
data->monitor->pos = monitor->pos;
data->monitor->size = monitor->size;
data->monitor->logical_pos = monitor->logical_pos;
data->monitor->logical_size = monitor->logical_size;
data->monitor->connector_id = monitor->connector_id;
data->monitor->rotation = monitor->rotation;
data->monitor->monitor_identifier = monitor->monitor_identifier;

View File

@@ -21,6 +21,7 @@ typedef struct {
struct zxdg_output_v1 *xdg_output;
vec2i pos;
vec2i size;
vec2i logical_size;
int32_t transform;
char *name;
} gsr_wayland_output;
@@ -123,6 +124,7 @@ static void registry_add_object(void *data, struct wl_registry *registry, uint32
.output = wl_registry_bind(registry, name, &wl_output_interface, 4),
.pos = { .x = 0, .y = 0 },
.size = { .x = 0, .y = 0 },
.logical_size = { .x = 0, .y = 0 },
.transform = 0,
.name = NULL,
};
@@ -160,10 +162,10 @@ static void xdg_output_logical_position(void *data, struct zxdg_output_v1 *zxdg_
}
static void xdg_output_handle_logical_size(void *data, struct zxdg_output_v1 *xdg_output, int32_t width, int32_t height) {
(void)data;
(void)xdg_output;
(void)width;
(void)height;
gsr_wayland_output *gsr_xdg_output = data;
gsr_xdg_output->logical_size.x = width;
gsr_xdg_output->logical_size.y = height;
}
static void xdg_output_handle_done(void *data, struct zxdg_output_v1 *xdg_output) {
@@ -206,6 +208,42 @@ static void gsr_window_wayland_set_monitor_outputs_from_xdg_output(gsr_window_wa
wl_display_roundtrip(self->display);
}
// static int monitor_sort_x_pos(const void* a, const void* b) {
// const gsr_wayland_output *arg1 = *(const gsr_wayland_output**)a;
// const gsr_wayland_output *arg2 = *(const gsr_wayland_output**)b;
// return arg1->logical_pos.x - arg2->logical_pos.x;
// }
// static int monitor_sort_y_pos(const void* a, const void* b) {
// const gsr_wayland_output *arg1 = *(const gsr_wayland_output**)a;
// const gsr_wayland_output *arg2 = *(const gsr_wayland_output**)b;
// return arg1->logical_pos.y - arg2->logical_pos.y;
// }
static void gsr_window_wayland_set_monitor_real_positions(gsr_window_wayland *self) {
gsr_wayland_output *sorted_outputs[GSR_MAX_OUTPUTS];
for(int i = 0; i < self->num_outputs; ++i) {
sorted_outputs[i] = &self->outputs[i];
}
// TODO: set correct physical positions
// qsort(sorted_outputs, self->num_outputs, sizeof(gsr_wayland_output*), monitor_sort_x_pos);
// int x_pos = 0;
// for(int i = 0; i < self->num_outputs; ++i) {
// fprintf(stderr, "monitor: %s\n", sorted_outputs[i]->name);
// sorted_outputs[i]->pos.x = x_pos;
// x_pos += sorted_outputs[i]->logical_size.x;
// }
// qsort(sorted_outputs, self->num_outputs, sizeof(gsr_wayland_output*), monitor_sort_y_pos);
// int y_pos = 0;
// for(int i = 0; i < self->num_outputs; ++i) {
// sorted_outputs[i]->pos.y = y_pos;
// y_pos += sorted_outputs[i]->logical_size.y;
// }
}
static void gsr_window_wayland_deinit(gsr_window_wayland *self) {
if(self->window) {
wl_egl_window_destroy(self->window);
@@ -273,6 +311,7 @@ static bool gsr_window_wayland_init(gsr_window_wayland *self) {
wl_display_roundtrip(self->display);
gsr_window_wayland_set_monitor_outputs_from_xdg_output(self);
gsr_window_wayland_set_monitor_real_positions(self);
if(!self->compositor) {
fprintf(stderr, "gsr error: gsr_window_wayland_init failed: failed to find compositor\n");
@@ -337,6 +376,16 @@ static gsr_monitor_rotation wayland_transform_to_gsr_rotation(int32_t rot) {
return GSR_MONITOR_ROT_0;
}
static vec2i get_monitor_size_rotated(int width, int height, gsr_monitor_rotation rotation) {
vec2i size = { .x = width, .y = height };
if(rotation == GSR_MONITOR_ROT_90 || rotation == GSR_MONITOR_ROT_270) {
int tmp_x = size.x;
size.x = size.y;
size.y = tmp_x;
}
return size;
}
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;
for(int i = 0; i < self->num_outputs; ++i) {
@@ -344,15 +393,26 @@ static void gsr_window_wayland_for_each_active_monitor_output_cached(const gsr_w
if(!output->name)
continue;
const gsr_monitor_rotation rotation = wayland_transform_to_gsr_rotation(output->transform);
vec2i size = { .x = output->size.x, .y = output->size.y };
size = get_monitor_size_rotated(size.x, size.y, rotation);
vec2i logical_size = { .x = output->logical_size.x, .y = output->logical_size.y };
if(logical_size.x == 0 || logical_size.y == 0)
logical_size = size;
const int connector_type_index = get_connector_type_by_name(output->name);
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),
.pos = { .x = output->pos.x, .y = output->pos.y },
.size = { .x = output->size.x, .y = output->size.y },
.size = size,
.logical_pos = { .x = output->pos.x, .y = output->pos.y },
.logical_size = logical_size,
.connector_id = 0,
.rotation = wayland_transform_to_gsr_rotation(output->transform),
.rotation = rotation,
.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);

View File

@@ -121,6 +121,8 @@ static void gsr_window_x11_for_each_active_monitor_output_cached(const gsr_windo
.name_len = strlen(output->name),
.pos = output->pos,
.size = output->size,
.logical_pos = output->pos,
.logical_size = output->size,
.connector_id = output->connector_id,
.rotation = output->rotation,
.monitor_identifier = output->monitor_identifier