App audio capture: fix audio sources getting paused when closing gsr

This commit is contained in:
dec05eba
2025-09-25 18:45:43 +02:00
parent cb8104d107
commit c11dd77c44
5 changed files with 48 additions and 5 deletions

1
TODO
View File

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

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

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

View File

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

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