diff options
| author | dec05eba <dec05eba@protonmail.com> | 2025-12-07 18:10:18 +0100 |
|---|---|---|
| committer | dec05eba <dec05eba@protonmail.com> | 2025-12-07 18:10:18 +0100 |
| commit | 3ac17b99a01feb713f1d561a2b09aeb59f93f77a (patch) | |
| tree | 4e2dfad36b0e437d5572c284a29e5b6a4b57e311 /src | |
| parent | 2064d109ee0f3f89923b7bd8d3b17fa78cf0962c (diff) | |
App audio capture: remove gsr-app-sink
Connect application/device audio directly to gsr recording node.
This fixes an issue for some users where gsr-app-sink got selected by
default as an output device.
Dont wait until audio node first receives audio before recording audio
from the device. This might fix audio/video desync issue when recording
from microphone for example.
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 17 | ||||
| -rw-r--r-- | src/pipewire_audio.c | 92 | ||||
| -rw-r--r-- | src/sound.cpp | 29 |
3 files changed, 47 insertions, 91 deletions
diff --git a/src/main.cpp b/src/main.cpp index 1e542ba..583072c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2911,7 +2911,7 @@ static std::vector<AudioDeviceData> create_device_audio_inputs(const std::vector audio_device.sound_device.frames = 0; } else { const std::string description = "gsr-" + audio_input.name; - if(sound_device_get_by_name(&audio_device.sound_device, audio_input.name.c_str(), description.c_str(), num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) { + if(sound_device_get_by_name(&audio_device.sound_device, description.c_str(), audio_input.name.c_str(), description.c_str(), num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) { fprintf(stderr, "gsr error: failed to get \"%s\" audio device\n", audio_input.name.c_str()); _exit(1); } @@ -2936,17 +2936,12 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp fprintf(stderr, "gsr error: failed to generate random string\n"); _exit(1); } + std::string combined_sink_name = "gsr-combined-"; combined_sink_name.append(random_str, sizeof(random_str)); - - if(!gsr_pipewire_audio_create_virtual_sink(pipewire_audio, combined_sink_name.c_str())) { - fprintf(stderr, "gsr error: failed to create virtual sink for application audio\n"); - _exit(1); - } - combined_sink_name += ".monitor"; - if(sound_device_get_by_name(&audio_device.sound_device, combined_sink_name.c_str(), "gpu-screen-recorder", num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) { + if(sound_device_get_by_name(&audio_device.sound_device, combined_sink_name.c_str(), "", "gpu-screen-recorder", num_channels, audio_codec_context->frame_size, audio_codec_context_get_audio_format(audio_codec_context)) != 0) { fprintf(stderr, "gsr error: failed to setup audio recording to combined sink\n"); _exit(1); } @@ -2967,19 +2962,19 @@ static AudioDeviceData create_application_audio_audio_input(const MergedAudioInp } if(!audio_devices_sources.empty()) { - if(!gsr_pipewire_audio_add_link_from_sources_to_sink(pipewire_audio, audio_devices_sources.data(), audio_devices_sources.size(), combined_sink_name.c_str())) { + if(!gsr_pipewire_audio_add_link_from_sources_to_stream(pipewire_audio, audio_devices_sources.data(), audio_devices_sources.size(), combined_sink_name.c_str())) { fprintf(stderr, "gsr error: failed to add application audio link\n"); _exit(1); } } if(app_audio_inverted) { - if(!gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) { + if(!gsr_pipewire_audio_add_link_from_apps_to_stream_inverted(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) { fprintf(stderr, "gsr error: failed to add application audio link\n"); _exit(1); } } else { - if(!gsr_pipewire_audio_add_link_from_apps_to_sink(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) { + if(!gsr_pipewire_audio_add_link_from_apps_to_stream(pipewire_audio, app_names.data(), app_names.size(), combined_sink_name.c_str())) { fprintf(stderr, "gsr error: failed to add application audio link\n"); _exit(1); } diff --git a/src/pipewire_audio.c b/src/pipewire_audio.c index 945f0ca..b8ef8ea 100644 --- a/src/pipewire_audio.c +++ b/src/pipewire_audio.c @@ -58,6 +58,29 @@ static bool requested_link_matches_name_case_insensitive(const gsr_pipewire_audi return false; } +static bool requested_link_matches_name_case_insensitive_any_type(const gsr_pipewire_audio *self, const gsr_pipewire_audio_requested_link *requested_link, const char *name) { + for(int i = 0; i < requested_link->num_outputs; ++i) { + switch(requested_link->outputs[i].type) { + case GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_STANDARD: { + if(strcasecmp(requested_link->outputs[i].name, name) == 0) + return true; + break; + } + case GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_OUTPUT: { + if(strcasecmp(self->default_output_device_name, name) == 0) + return true; + break; + } + case GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_INPUT: { + if(strcasecmp(self->default_input_device_name, name) == 0) + return true; + break; + } + } + } + return false; +} + static bool requested_link_has_type(const gsr_pipewire_audio_requested_link *requested_link, gsr_pipewire_audio_requested_type type) { for(int i = 0; i < requested_link->num_outputs; ++i) { if(requested_link->outputs[i].type == type) @@ -168,7 +191,7 @@ static void gsr_pipewire_audio_create_link(gsr_pipewire_audio *self, const gsr_p if(output_node->type != requested_link->output_type) continue; - const bool requested_link_matches_app = requested_link_matches_name_case_insensitive(requested_link, output_node->name); + const bool requested_link_matches_app = requested_link_matches_name_case_insensitive_any_type(self, requested_link, output_node->name); if(requested_link->inverted) { if(requested_link_matches_app) continue; @@ -642,20 +665,6 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) { pw_thread_loop_stop(self->thread_loop); } - for(size_t i = 0; i < self->num_virtual_sink_proxies; ++i) { - if(self->virtual_sink_proxies[i]) { - pw_proxy_destroy(self->virtual_sink_proxies[i]); - self->virtual_sink_proxies[i] = NULL; - } - } - self->num_virtual_sink_proxies = 0; - self->virtual_sink_proxies_capacity_items = 0; - - if(self->virtual_sink_proxies) { - free(self->virtual_sink_proxies); - self->virtual_sink_proxies = NULL; - } - if(self->metadata_proxy) { spa_hook_remove(&self->metadata_listener); spa_hook_remove(&self->metadata_proxy_listener); @@ -733,54 +742,6 @@ void gsr_pipewire_audio_deinit(gsr_pipewire_audio *self) { #endif } -static struct pw_properties* gsr_pipewire_create_null_audio_sink(const char *name) { - char props_str[512]; - snprintf(props_str, sizeof(props_str), - "{ factory.name=support.null-audio-sink node.name=\"%s\" media.class=Audio/Sink object.linger=false audio.position=[FL FR]" - " monitor.channel-volumes=true monitor.passthrough=true adjust_time=0 node.description=gsr-app-sink slaves=\"\" priority.driver=1 priority.session=1 }", name); - struct pw_properties *props = pw_properties_new_string(props_str); - if(!props) { - fprintf(stderr, "gsr error: gsr_pipewire_create_null_audio_sink: failed to create virtual sink properties\n"); - return NULL; - } - return props; -} - -bool gsr_pipewire_audio_create_virtual_sink(gsr_pipewire_audio *self, const char *name) { - if(!array_ensure_capacity((void**)&self->virtual_sink_proxies, self->num_virtual_sink_proxies, &self->virtual_sink_proxies_capacity_items, sizeof(struct pw_proxy*))) - return false; - - pw_thread_loop_lock(self->thread_loop); - - struct pw_properties *virtual_sink_props = gsr_pipewire_create_null_audio_sink(name); - if(!virtual_sink_props) { - pw_thread_loop_unlock(self->thread_loop); - return false; - } - - struct pw_proxy *virtual_sink_proxy = pw_core_create_object(self->core, "adapter", PW_TYPE_INTERFACE_Node, PW_VERSION_NODE, &virtual_sink_props->dict, 0); - // TODO: - // If these are done then the above needs sizeof(*self) as the last argument - //pw_proxy_add_object_listener(virtual_sink_proxy, &pd->object_listener, &node_events, self); - //pw_proxy_add_listener(virtual_sink_proxy, &pd->proxy_listener, &proxy_events, self); - // TODO: proxy - pw_properties_free(virtual_sink_props); - if(!virtual_sink_proxy) { - fprintf(stderr, "gsr error: gsr_pipewire_audio_create_virtual_sink: failed to create virtual sink\n"); - pw_thread_loop_unlock(self->thread_loop); - return false; - } - - self->server_version_sync = pw_core_sync(self->core, PW_ID_CORE, self->server_version_sync); - pw_thread_loop_wait(self->thread_loop); - pw_thread_loop_unlock(self->thread_loop); - - self->virtual_sink_proxies[self->num_virtual_sink_proxies] = virtual_sink_proxy; - ++self->num_virtual_sink_proxies; - - return true; -} - static bool string_remove_suffix(char *str, const char *suffix) { int str_len = strlen(str); int suffix_len = strlen(suffix); @@ -834,6 +795,7 @@ static bool gsr_pipewire_audio_add_links_to_output(gsr_pipewire_audio *self, con self->requested_links[self->num_requested_links].inverted = inverted; ++self->num_requested_links; gsr_pipewire_audio_create_link(self, &self->requested_links[self->num_requested_links - 1]); + // TODO: Remove these? gsr_pipewire_audio_create_link_for_default_devices(self, &self->requested_links[self->num_requested_links - 1], GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_OUTPUT); gsr_pipewire_audio_create_link_for_default_devices(self, &self->requested_links[self->num_requested_links - 1], GSR_PIPEWIRE_AUDIO_REQUESTED_TYPE_DEFAULT_INPUT); pw_thread_loop_unlock(self->thread_loop); @@ -865,6 +827,10 @@ bool gsr_pipewire_audio_add_link_from_apps_to_sink_inverted(gsr_pipewire_audio * return gsr_pipewire_audio_add_links_to_output(self, app_names, num_app_names, sink_name_input, GSR_PIPEWIRE_AUDIO_NODE_TYPE_STREAM_OUTPUT, GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_SINK, true); } +bool gsr_pipewire_audio_add_link_from_sources_to_stream(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *stream_name_input) { + return gsr_pipewire_audio_add_links_to_output(self, source_names, num_source_names, stream_name_input, GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE, GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_STREAM, false); +} + bool gsr_pipewire_audio_add_link_from_sources_to_sink(gsr_pipewire_audio *self, const char **source_names, int num_source_names, const char *sink_name_input) { return gsr_pipewire_audio_add_links_to_output(self, source_names, num_source_names, sink_name_input, GSR_PIPEWIRE_AUDIO_NODE_TYPE_SINK_OR_SOURCE, GSR_PIPEWIRE_AUDIO_LINK_INPUT_TYPE_SINK, false); } diff --git a/src/sound.cpp b/src/sound.cpp index 8ff8def..69ad1e6 100644 --- a/src/sound.cpp +++ b/src/sound.cpp @@ -189,7 +189,7 @@ static pa_handle* pa_sound_device_new(const char *server, snprintf(p->stream_name, sizeof(p->stream_name), "%s", stream_name); p->reconnect = true; - p->reconnect_last_tried_seconds = clock_get_monotonic_seconds() - 1000.0; + p->reconnect_last_tried_seconds = clock_get_monotonic_seconds() - (RECONNECT_TRY_TIMEOUT_SECONDS * 1000.0 * 2.0); p->default_output_device_name[0] = '\0'; p->default_input_device_name[0] = '\0'; p->device_type = DeviceType::STANDARD; @@ -208,6 +208,10 @@ static pa_handle* pa_sound_device_new(const char *server, pa_proplist *proplist = pa_proplist_new(); pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "production"); + if(strcmp(device_name, "") == 0) { + pa_proplist_sets(proplist, "node.autoconnect", "false"); + pa_proplist_sets(proplist, "node.dont-reconnect", "true"); + } if (!(p->mainloop = pa_mainloop_new())) goto fail; @@ -288,20 +292,6 @@ static bool pa_sound_device_handle_reconnect(pa_handle *p, char *device_name, si return false; } - for(;;) { - pa_stream_state_t state = pa_stream_get_state(p->stream); - - if(state == PA_STREAM_READY) - break; - - if(!PA_STREAM_IS_GOOD(state)) { - //pa_context_errno(p->context); - return false; - } - - pa_mainloop_iterate(p->mainloop, 1, NULL); - } - std::lock_guard<std::mutex> lock(p->reconnect_mutex); p->reconnect = false; return true; @@ -322,6 +312,11 @@ static int pa_sound_device_read(pa_handle *p, double timeout_seconds) { if(!pa_sound_device_handle_reconnect(p, device_name, sizeof(device_name), start_time)) goto fail; + if(pa_stream_get_state(p->stream) != PA_STREAM_READY) { + pa_mainloop_iterate(p->mainloop, 0, NULL); + goto fail; + } + CHECK_DEAD_GOTO(p, rerror, fail); while (p->output_index < p->output_length) { @@ -415,7 +410,7 @@ static int audio_format_to_get_bytes_per_sample(AudioFormat audio_format) { return 2; } -int sound_device_get_by_name(SoundDevice *device, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format) { +int sound_device_get_by_name(SoundDevice *device, const char *node_name, const char *device_name, const char *description, unsigned int num_channels, unsigned int period_frame_size, AudioFormat audio_format) { pa_sample_spec ss; ss.format = audio_format_to_pulse_audio_format(audio_format); ss.rate = 48000; @@ -429,7 +424,7 @@ int sound_device_get_by_name(SoundDevice *device, const char *device_name, const buffer_attr.maxlength = buffer_attr.fragsize; int error = 0; - pa_handle *handle = pa_sound_device_new(nullptr, description, device_name, description, &ss, &buffer_attr, &error); + pa_handle *handle = pa_sound_device_new(nullptr, node_name, device_name, description, &ss, &buffer_attr, &error); if(!handle) { fprintf(stderr, "gsr error: pa_sound_device_new() failed: %s. Audio input device %s might not be valid\n", pa_strerror(error), device_name); return -1; |
