audio: Prefer Pipewire over Pulseaudio if the pipewire-pulse service is running

Use DBus to query Systemd to check if the pipewire-pulse service is in the "running" state. If it is, then it is certain that Pipewire is being used instead of Pulseaudio as the preferred system mixer.

If DBus support is not enabled or Systemd is not being used on the underlying system, this check will simply fail and the standard driver order will be tested.
main
Frank Praznik 2024-04-01 09:07:02 -04:00 committed by Ryan C. Gordon
parent b6cb63adef
commit 60f26182c3
5 changed files with 136 additions and 5 deletions

View File

@ -28,6 +28,9 @@
// Available audio drivers
static const AudioBootStrap *const bootstrap[] = {
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO
#ifdef SDL_AUDIO_DRIVER_PIPEWIRE
&PIPEWIRE_PREFERRED_bootstrap,
#endif
&PULSEAUDIO_bootstrap,
#endif
#ifdef SDL_AUDIO_DRIVER_PIPEWIRE
@ -98,15 +101,41 @@ static const AudioBootStrap *const bootstrap[] = {
static SDL_AudioDriver current_audio;
// Deduplicated list of audio bootstrap drivers.
static const AudioBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1];
int SDL_GetNumAudioDrivers(void)
{
return SDL_arraysize(bootstrap) - 1;
static int num_drivers = -1;
if (num_drivers >= 0) {
return num_drivers;
}
num_drivers = 0;
// Build a list of unique audio drivers.
for (int i = 0; bootstrap[i] != NULL; ++i) {
SDL_bool duplicate = SDL_FALSE;
for (int j = 0; j < i; ++j) {
if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) {
duplicate = SDL_TRUE;
break;
}
}
if (!duplicate) {
deduped_bootstrap[num_drivers++] = bootstrap[i];
}
}
return num_drivers;
}
const char *SDL_GetAudioDriver(int index)
{
if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
return bootstrap[index]->name;
return deduped_bootstrap[index]->name;
}
return NULL;
}
@ -887,10 +916,10 @@ int SDL_InitAudio(const char *driver_name)
current_audio.name = bootstrap[i]->name;
current_audio.desc = bootstrap[i]->desc;
initialized = SDL_TRUE;
}
break;
}
}
}
driver_attempt = (driver_attempt_end) ? (driver_attempt_end + 1) : NULL;
}

View File

@ -335,6 +335,7 @@ typedef struct AudioBootStrap
} AudioBootStrap;
// Not all of these are available in a given build. Use #ifdefs, etc.
extern AudioBootStrap PIPEWIRE_PREFERRED_bootstrap;
extern AudioBootStrap PIPEWIRE_bootstrap;
extern AudioBootStrap PULSEAUDIO_bootstrap;
extern AudioBootStrap ALSA_bootstrap;

View File

@ -29,6 +29,17 @@
#include <spa/param/audio/format-utils.h>
#include <spa/utils/json.h>
#include "../../core/linux/SDL_dbus.h"
static SDL_bool CheckPipewirePulseService()
{
#ifdef SDL_USE_LIBDBUS
return SDL_DBus_QuerySystemdUnitRunning("pipewire-pulse.service", SDL_TRUE);
#else
return SDL_FALSE;
#endif
}
/*
* The following keys are defined for compatibility when building against older versions of Pipewire
* prior to their introduction and can be removed if the minimum required Pipewire build version is
@ -1251,7 +1262,7 @@ static void PIPEWIRE_Deinitialize(void)
}
}
static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
static SDL_bool PipewireInitialize(SDL_AudioDriverImpl *impl)
{
if (!pipewire_initialized) {
if (init_pipewire_library() < 0) {
@ -1282,6 +1293,25 @@ static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
return SDL_TRUE;
}
AudioBootStrap PIPEWIRE_bootstrap = { "pipewire", "Pipewire", PIPEWIRE_Init, SDL_FALSE };
static SDL_bool PIPEWIRE_PREFERRED_Init(SDL_AudioDriverImpl *impl)
{
if (CheckPipewirePulseService()) {
return PipewireInitialize(impl);
}
return SDL_FALSE;
}
static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
{
return PipewireInitialize(impl);
}
AudioBootStrap PIPEWIRE_PREFERRED_bootstrap = {
"pipewire", "Pipewire", PIPEWIRE_PREFERRED_Init, SDL_FALSE
};
AudioBootStrap PIPEWIRE_bootstrap = {
"pipewire", "Pipewire", PIPEWIRE_Init, SDL_FALSE
};
#endif // SDL_AUDIO_DRIVER_PIPEWIRE

View File

@ -632,4 +632,73 @@ failed:
return NULL;
}
/* Check to see if a Systemd unit exists and is currently running. */
SDL_bool SDL_DBus_QuerySystemdUnitRunning(const char *unit_name, SDL_bool user_unit)
{
const char *path, *prop;
DBusError err;
SDL_bool running = SDL_FALSE;
/* Make sure we have a connection to the dbus session bus */
if (!SDL_DBus_GetContext() || !dbus.session_conn) {
/* We either cannot connect to the session bus or were unable to
* load the D-Bus library at all. */
return SDL_FALSE;
}
/* Make sure the appropriate bus is available. */
if ((user_unit && !dbus.session_conn) || (!user_unit && !dbus.system_conn)) {
return SDL_FALSE;
}
dbus.error_init(&err);
/* Get the object path for the unit. */
DBusMessage *method = dbus.message_new_method_call("org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnit");
if (!method) {
return SDL_FALSE;
}
if (!dbus.message_append_args(method, DBUS_TYPE_STRING, &unit_name, DBUS_TYPE_INVALID)) {
SDL_OutOfMemory();
dbus.message_unref(method);
return SDL_FALSE;
}
DBusMessage *reply = dbus.connection_send_with_reply_and_block(user_unit ? dbus.session_conn : dbus.system_conn, method, DBUS_TIMEOUT_USE_DEFAULT, &err);
dbus.message_unref(method);
if (!reply) {
if (dbus.error_is_set(&err)) {
SDL_SetError("%s: %s", err.name, err.message);
dbus.error_free(&err);
}
return SDL_FALSE;
}
DBusMessageIter reply_iter;
if (!dbus.message_iter_init(reply, &reply_iter)) {
goto done;
}
if (dbus.message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_OBJECT_PATH) {
goto done;
}
dbus.message_iter_get_basic(&reply_iter, &path);
/* We want to know the substate of the unit, which should be the string "running". */
if (SDL_DBus_QueryPropertyOnConnection(user_unit ? dbus.session_conn : dbus.system_conn,
"org.freedesktop.systemd1", path, "org.freedesktop.systemd1.Unit",
"SubState", DBUS_TYPE_STRING, &prop)) {
running = SDL_strcmp(prop, "running") == 0;
}
done:
dbus.message_unref(reply);
return running;
}
#endif

View File

@ -109,6 +109,8 @@ extern char *SDL_DBus_GetLocalMachineId(void);
extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);
extern SDL_bool SDL_DBus_QuerySystemdUnitRunning(const char *unit_name, SDL_bool user_unit);
#endif /* HAVE_DBUS_DBUS_H */
#endif /* SDL_dbus_h_ */