aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authordec05eba <dec05eba@protonmail.com>2025-09-25 18:45:43 +0200
committerdec05eba <dec05eba@protonmail.com>2025-09-25 18:45:43 +0200
commitc11dd77c44fffbfea652e346cee2acadd12efb16 (patch)
treeeafa4846f3eaa17e35d2ad5860a555bd28a20993
parentcb8104d107000ae3b78ae3ca9de5fd045f35f84d (diff)
App audio capture: fix audio sources getting paused when closing gsr
-rw-r--r--TODO1
-rw-r--r--include/pipewire_audio.h2
-rw-r--r--meson.build2
-rw-r--r--project.conf2
-rw-r--r--src/pipewire_audio.c46
5 files changed, 48 insertions, 5 deletions
diff --git a/TODO b/TODO
index cce117c..e32bedd 100644
--- a/TODO
+++ b/TODO
@@ -330,7 +330,6 @@ 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.
diff --git a/include/pipewire_audio.h b/include/pipewire_audio.h
index 68e5356..b8b447d 100644
--- a/include/pipewire_audio.h
+++ b/include/pipewire_audio.h
@@ -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);
diff --git a/meson.build b/meson.build
index e144d80..bf08235 100644
--- a/meson.build
+++ b/meson.build
@@ -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.3', default_options : ['warning_level=2'])
add_project_arguments('-Wshadow', language : ['c', 'cpp'])
if get_option('buildtype') == 'debug'
diff --git a/project.conf b/project.conf
index 4517f65..0401fcb 100644
--- a/project.conf
+++ b/project.conf
@@ -1,7 +1,7 @@
[package]
name = "gpu-screen-recorder"
type = "executable"
-version = "5.7.2"
+version = "5.7.3"
platforms = ["posix"]
[config]
diff --git a/src/pipewire_audio.c b/src/pipewire_audio.c
index 80e3208..945f0ca 100644
--- a/src/pipewire_audio.c
+++ b/src/pipewire_audio.c
@@ -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);
}