Compare commits

...

6 Commits
5.7.2 ... 5.7.4

Author SHA1 Message Date
dec05eba
50fbee2b2f 5.7.4 2025-10-01 18:17:53 +02:00
Theodoros Orfanidis
c60783fdcb Fix texture size when recording via portal 2025-10-01 17:47:54 +02:00
dec05eba
313d3227d8 TODO 2025-10-01 17:47:45 +02:00
dec05eba
3c5514480c Remove use of libavcodec/defs.h (fix compile on old ffmpeg) 2025-09-29 10:07:08 +02:00
dec05eba
afd140f33b m 2025-09-28 22:26:47 +02:00
dec05eba
c11dd77c44 App audio capture: fix audio sources getting paused when closing gsr 2025-09-25 18:45:43 +02:00
10 changed files with 60 additions and 9 deletions

View File

@@ -33,7 +33,7 @@ If you are running an Arch Linux based distro then you can find gpu screen recor
If you are running another distro then you can run `sudo ./install.sh`, but you need to manually install the dependencies, as described below.\
You can also install gpu screen recorder ([the gtk gui version](https://git.dec05eba.com/gpu-screen-recorder-gtk/)) from [flathub](https://flathub.org/apps/details/com.dec05eba.gpu_screen_recorder), which is the easiest method
to install GPU Screen Recorder on non-arch based distros.\
If you install GPU Screen Recorder flatpak, which is the gtk gui version then you can still run GPU Screen Recorder command line by using the flatpak command option, for example `flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4`. Note that if you want to record your monitor on AMD/Intel then you need to install the flatpak system-wide (like so: `flatpak install flathub --system com.dec05eba.gpu_screen_recorder`).
If you install GPU Screen Recorder flatpak, which is the gtk gui version then you can still run GPU Screen Recorder command line by using the flatpak command option, for example `flatpak run --command=gpu-screen-recorder com.dec05eba.gpu_screen_recorder -w screen -f 60 -o video.mp4`. Note that if you want to record your monitor on AMD/Intel then you need to install the flatpak system-wide (like so: `flatpak install --system com.dec05eba.gpu_screen_recorder`).
## Unofficial install methods
The only official ways to install GPU Screen Recorder is either from source, AUR or flathub. Other sources may be out of date and missing features or may not work correctly.\

3
TODO
View File

@@ -330,8 +330,9 @@ Set top level window argument for portal capture. Same for gpu-screen-recorder-g
Remove unix domain socket code from kms-client/server and use socketpair directly. To make this possible always execute the kms server permission setup in flatpak, before starting recording (in gpu-screen-recorder-gtk).
Application audio capture isn't good enough. It creates a sink that for some automatically gets selected as the default output device and it's visible as an output device.
When shutting down gpu screen recorder it will also cause audio applications to pause.
Fix some of these issues by setting gsr-app-sink media class to "Stream/Input/Audio" and node.virtual=true.
However that causes pulseaudio to be unable to record from gsr-app-sink, and it ends up being stuck in pa_sound_device_handle_reconnect in the loop with pa_mainloop_iterate.
Add -k best/best_hdr/best_10bit option, to automatically choose the best codec (prefer av1, then hevc and then h264. For webm files choose vp9 and then vp8).
Check if region capture works properly with fractional scaling on wayland.

View File

@@ -97,6 +97,8 @@ typedef struct {
struct pw_proxy **virtual_sink_proxies;
size_t num_virtual_sink_proxies;
size_t virtual_sink_proxies_capacity_items;
bool running;
} gsr_pipewire_audio;
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self);

View File

@@ -58,6 +58,8 @@ typedef struct {
uint64_t modifiers;
bool using_external_image;
gsr_monitor_rotation rotation;
int texture_width;
int texture_height;
} gsr_map_texture_output;
typedef struct {

View File

@@ -1,4 +1,4 @@
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.2', default_options : ['warning_level=2'])
project('gpu-screen-recorder', ['c', 'cpp'], version : '5.7.4', 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.7.2"
version = "5.7.4"
platforms = ["posix"]
[config]

View File

@@ -356,9 +356,11 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
const bool is_scaled = self->params.output_resolution.x > 0 && self->params.output_resolution.y > 0;
vec2i output_size = is_scaled ? self->params.output_resolution : self->capture_size;
output_size = scale_keep_aspect_ratio(self->capture_size, output_size);
const vec2i target_pos = { max_int(0, capture_metadata->width / 2 - output_size.x / 2), max_int(0, capture_metadata->height / 2 - output_size.y / 2) };
const vec2i actual_texture_size = {self->pipewire_data.texture_width, self->pipewire_data.texture_height};
//self->params.egl->glFlush();
//self->params.egl->glFinish();
@@ -370,7 +372,7 @@ static int gsr_capture_portal_capture(gsr_capture *cap, gsr_capture_metadata *ca
gsr_color_conversion_draw(color_conversion, self->pipewire_data.using_external_image ? self->texture_map.external_texture_id : self->texture_map.texture_id,
target_pos, output_size,
(vec2i){self->pipewire_data.region.x, self->pipewire_data.region.y}, self->capture_size, self->capture_size,
(vec2i){self->pipewire_data.region.x, self->pipewire_data.region.y}, (vec2i){self->pipewire_data.region.width, self->pipewire_data.region.height}, actual_texture_size,
gsr_monitor_rotation_to_rotation(self->pipewire_data.rotation), GSR_SOURCE_COLOR_RGB, self->pipewire_data.using_external_image, fourcc_alpha);
if(self->params.record_cursor && self->texture_map.cursor_texture_id > 0 && self->pipewire_data.cursor_region.width > 0) {

View File

@@ -48,7 +48,6 @@ extern "C" {
extern "C" {
#include <libavutil/pixfmt.h>
#include <libavcodec/avcodec.h>
#include <libavcodec/defs.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>

View File

@@ -392,12 +392,12 @@ static void registry_event_global(void *data, uint32_t id, uint32_t permissions,
const struct spa_dict *props)
{
//fprintf(stderr, "add: id: %d, type: %s\n", (int)id, type);
if(!props || !type)
gsr_pipewire_audio *self = (gsr_pipewire_audio*)data;
if(!props || !type || !self->running)
return;
//pw_properties_new_dict(props);
gsr_pipewire_audio *self = (gsr_pipewire_audio*)data;
if(strcmp(type, PW_TYPE_INTERFACE_Node) == 0) {
const char *node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
const char *media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
@@ -547,6 +547,7 @@ static const struct pw_registry_events registry_events = {
bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
memset(self, 0, sizeof(*self));
self->running = true;
pw_init(NULL, NULL);
@@ -594,8 +595,49 @@ bool gsr_pipewire_audio_init(gsr_pipewire_audio *self) {
return true;
}
static gsr_pipewire_audio_link* gsr_pipewire_audio_get_first_link_to_node(gsr_pipewire_audio *self, uint32_t node_id) {
for(size_t i = 0; i < self->num_links; ++i) {
if(self->links[i].input_node_id == node_id)
return &self->links[i];
}
return NULL;
}
static void gsr_pipewire_audio_destroy_requested_links(gsr_pipewire_audio *self) {
pw_thread_loop_lock(self->thread_loop);
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
pw_thread_loop_wait(self->thread_loop);
for(size_t requested_link_index = 0; requested_link_index < self->num_requested_links; ++requested_link_index) {
const gsr_pipewire_audio_node_type requested_link_node_type = self->requested_links[requested_link_index].input_type == GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_STREAM ? GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_INPUT : GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE;
const gsr_pipewire_audio_node *stream_input_node = gsr_pipewire_audio_get_node_by_name_case_insensitive(self, self->requested_links[requested_link_index].input_name, requested_link_node_type);
if(!stream_input_node)
continue;
for(;;) {
gsr_pipewire_audio_link *link = gsr_pipewire_audio_get_first_link_to_node(self, stream_input_node->id);
if(!link)
break;
pw_registry_destroy(self->registry, link->id);
self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync);
pw_thread_loop_wait(self->thread_loop);
usleep(10 * 1000);
}
}
pw_thread_loop_unlock(self->thread_loop);
}
void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) {
self->running = false;
if(self->thread_loop) {
/* We need to manually destroy links first, otherwise the linked audio sources will be paused when closing the program */
gsr_pipewire_audio_destroy_requested_links(self);
//pw_thread_loop_wait(self->thread_loop);
pw_thread_loop_stop(self->thread_loop);
}

View File

@@ -871,6 +871,9 @@ bool gsr_pipewire_video_map_texture(gsr_pipewire_video *self, gsr_texture_map te
gsr_pipewire_video_update_cursor_texture(self, texture_map);
output->texture_width = self->format.info.raw.size.width;
output->texture_height = self->format.info.raw.size.height;
output->region.x = 0;
output->region.y = 0;