From 24156f5471b8edcbcf7c446b2405bc3597cbe212 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Fri, 2 Jan 2026 12:34:37 -0500 Subject: [PATCH] pipewire: Check for the audio service when determining driver preference Wireplumber now exposes a list of session services, so check the "session.services" property for an "audio" entry to determine whether Pipewire is configured for audio playback/capture. --- src/audio/pipewire/SDL_pipewire.c | 51 ++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 4 deletions(-) diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c index ee59ca391f..d40453d478 100644 --- a/src/audio/pipewire/SDL_pipewire.c +++ b/src/audio/pipewire/SDL_pipewire.c @@ -256,6 +256,8 @@ static int hotplug_init_seq_val; static bool hotplug_init_complete; static bool hotplug_events_enabled; +static bool pipewire_have_session_services; +static bool pipewire_have_audio_service; static int pipewire_version_major; static int pipewire_version_minor; static int pipewire_version_patch; @@ -438,7 +440,7 @@ static void core_events_interface_callback(void *object, uint32_t id, int seq) } } -static void core_events_metadata_callback(void *object, uint32_t id, int seq) +static void core_events_generic_callback(void *object, uint32_t id, int seq) { struct node_object *node = object; @@ -449,7 +451,7 @@ static void core_events_metadata_callback(void *object, uint32_t id, int seq) static const struct pw_core_events hotplug_init_core_events = { PW_VERSION_CORE_EVENTS, .info = core_events_hotplug_info_callback, .done = core_events_hotplug_init_callback }; static const struct pw_core_events interface_core_events = { PW_VERSION_CORE_EVENTS, .done = core_events_interface_callback }; -static const struct pw_core_events metadata_core_events = { PW_VERSION_CORE_EVENTS, .done = core_events_metadata_callback }; +static const struct pw_core_events generic_core_events = { PW_VERSION_CORE_EVENTS, .done = core_events_generic_callback }; static void hotplug_core_sync(struct node_object *node) { @@ -652,6 +654,35 @@ static int metadata_property(void *object, Uint32 subject, const char *key, cons static const struct pw_metadata_events metadata_node_events = { PW_VERSION_METADATA_EVENTS, .property = metadata_property }; +// Client info node callback. +static void client_info(void *data, const struct pw_client_info *info) +{ + // If WirePlumber lists the session services, check to see if audio is enabled. + const char *services = spa_dict_lookup(info->props, "session.services"); + if (services) { + pipewire_have_session_services = true; + + // Services are in a JSON array. + struct spa_json iter[2]; + spa_json_init(&iter[0], services, SDL_strlen(services)); + if (spa_json_enter_array(&iter[0], &iter[1]) > 0) { + char element[PW_MAX_IDENTIFIER_LENGTH]; + while (spa_json_get_string(&iter[1], element, sizeof(element)) > 0) { + if (SDL_strcmp(element, "audio") == 0) { + pipewire_have_audio_service = true; + break; + } + } + } + } +} + +static const struct pw_client_events client_node_events = { + .version = PW_VERSION_CLIENT_EVENTS, + .info = client_info, + .permissions = NULL +}; + // Global registry callbacks static void registry_event_global_callback(void *object, uint32_t id, uint32_t permissions, const char *type, uint32_t version, const struct spa_dict *props) @@ -714,12 +745,21 @@ static void registry_event_global_callback(void *object, uint32_t id, uint32_t p } } } else if (!SDL_strcmp(type, PW_TYPE_INTERFACE_Metadata)) { - node = node_object_new(id, type, version, &metadata_node_events, &metadata_core_events); + node = node_object_new(id, type, version, &metadata_node_events, &generic_core_events); if (!node) { SDL_SetError("Pipewire: Failed to allocate metadata node"); return; } + // Update sync points + hotplug_core_sync(node); + } else if (!SDL_strcmp(type, PW_TYPE_INTERFACE_Client)) { + node = node_object_new(id, type, version, &client_node_events, &generic_core_events); + if (!node) { + SDL_SetError("Pipewire: Failed to allocate client info node"); + return; + } + // Update sync points hotplug_core_sync(node); } @@ -1282,6 +1322,8 @@ static void PIPEWIRE_Deinitialize(void) if (pipewire_initialized) { hotplug_loop_destroy(); deinit_pipewire_library(); + pipewire_have_session_services = false; + pipewire_have_audio_service = false; pipewire_initialized = false; } } @@ -1335,7 +1377,8 @@ static bool PIPEWIRE_PREFERRED_Init(SDL_AudioDriverImpl *impl) PIPEWIRE_pw_thread_loop_unlock(hotplug_loop); - if (no_devices || !pipewire_core_version_at_least(1, 0, 0)) { + if ((pipewire_have_session_services && !pipewire_have_audio_service) || + (!pipewire_have_session_services && (no_devices || !pipewire_core_version_at_least(1, 0, 0)))) { PIPEWIRE_Deinitialize(); return false; }