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