From a923db9d0a65e79ed79078f6fccd984d13528045 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Wed, 17 Apr 2024 21:41:04 -0400 Subject: [PATCH] pipewire: Use the core version info for the preferred version check When running in a container, the underlying Pipewire version may not match the library version, so retrieve and check the core version info to see if it meets the preferred version requirements. --- src/audio/pipewire/SDL_pipewire.c | 136 ++++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 25 deletions(-) diff --git a/src/audio/pipewire/SDL_pipewire.c b/src/audio/pipewire/SDL_pipewire.c index 499bf6aea9748..0a831b4dd5228 100644 --- a/src/audio/pipewire/SDL_pipewire.c +++ b/src/audio/pipewire/SDL_pipewire.c @@ -88,6 +88,11 @@ static SDL_bool pipewire_initialized = SDL_FALSE; static const char *(*PIPEWIRE_pw_get_library_version)(void); static void (*PIPEWIRE_pw_init)(int *, char ***); static void (*PIPEWIRE_pw_deinit)(void); +static struct pw_main_loop *(*PIPEWIRE_pw_main_loop_new)(struct pw_main_loop *loop); +static struct pw_loop *(*PIPEWIRE_pw_main_loop_get_loop)(struct pw_main_loop *loop); +static int (*PIPEWIRE_pw_main_loop_run)(struct pw_main_loop *loop); +static int (*PIPEWIRE_pw_main_loop_quit)(struct pw_main_loop *loop); +static void(*PIPEWIRE_pw_main_loop_destroy)(struct pw_main_loop *loop); static struct pw_thread_loop *(*PIPEWIRE_pw_thread_loop_new)(const char *, const struct spa_dict *); static void (*PIPEWIRE_pw_thread_loop_destroy)(struct pw_thread_loop *); static void (*PIPEWIRE_pw_thread_loop_stop)(struct pw_thread_loop *); @@ -116,9 +121,9 @@ static struct pw_properties *(*PIPEWIRE_pw_properties_new)(const char *, ...)SPA static int (*PIPEWIRE_pw_properties_set)(struct pw_properties *, const char *, const char *); static int (*PIPEWIRE_pw_properties_setf)(struct pw_properties *, const char *, const char *, ...) SPA_PRINTF_FUNC(3, 4); -static int pipewire_version_major; -static int pipewire_version_minor; -static int pipewire_version_patch; +static int pipewire_library_version_major; +static int pipewire_library_version_minor; +static int pipewire_library_version_patch; #ifdef SDL_AUDIO_DRIVER_PIPEWIRE_DYNAMIC @@ -176,6 +181,11 @@ static int load_pipewire_syms(void) SDL_PIPEWIRE_SYM(pw_get_library_version); SDL_PIPEWIRE_SYM(pw_init); SDL_PIPEWIRE_SYM(pw_deinit); + SDL_PIPEWIRE_SYM(pw_main_loop_new); + SDL_PIPEWIRE_SYM(pw_main_loop_get_loop); + SDL_PIPEWIRE_SYM(pw_main_loop_run); + SDL_PIPEWIRE_SYM(pw_main_loop_quit); + SDL_PIPEWIRE_SYM(pw_main_loop_destroy); SDL_PIPEWIRE_SYM(pw_thread_loop_new); SDL_PIPEWIRE_SYM(pw_thread_loop_destroy); SDL_PIPEWIRE_SYM(pw_thread_loop_stop); @@ -205,27 +215,115 @@ static int load_pipewire_syms(void) return 0; } -static SDL_bool pipewire_version_at_least(int major, int minor, int patch) +static SDL_bool pipewire_library_version_at_least(int major, int minor, int patch) { - return (pipewire_version_major >= major) && - (pipewire_version_major > major || pipewire_version_minor >= minor) && - (pipewire_version_major > major || pipewire_version_minor > minor || pipewire_version_patch >= patch); + return (pipewire_library_version_major >= major) && + (pipewire_library_version_major > major || pipewire_library_version_minor >= minor) && + (pipewire_library_version_major > major || pipewire_library_version_minor > minor || pipewire_library_version_patch >= patch); } -static int init_pipewire_library(int major, int minor, int patch) +/* When in a container, the library version can differ from the underlying core version, + * so make sure the underlying Pipewire implementation meets the version requirement. + */ +struct version_data +{ + struct pw_main_loop *loop; + int major, minor, patch; + int seq; +}; + +void version_check_core_info_callback(void *data, const struct pw_core_info *info) +{ + struct version_data *v = data; + + if (SDL_sscanf(info->version, "%d.%d.%d", &v->major, &v->minor, &v->patch) < 3) { + v->major = 0; + v->minor = 0; + v->patch = 0; + } +} + +void version_check_core_done_callback(void *data, uint32_t id, int seq) +{ + struct version_data *v = data; + + if (id == PW_ID_CORE && v->seq == seq) { + PIPEWIRE_pw_main_loop_quit(v->loop); + } +} + +static const struct pw_core_events version_check_core_events = { PW_VERSION_CORE_EVENTS, .info = version_check_core_info_callback, .done = version_check_core_done_callback }; + +SDL_bool pipewire_core_version_at_least(int major, int minor, int patch) +{ + struct pw_main_loop *loop = NULL; + struct pw_context *context = NULL; + struct pw_core *core = NULL; + struct version_data version_data; + struct spa_hook core_listener; + SDL_bool ret = SDL_FALSE; + + loop = PIPEWIRE_pw_main_loop_new(NULL); + if (!loop) { + goto done; + } + + context = PIPEWIRE_pw_context_new(PIPEWIRE_pw_main_loop_get_loop(loop), NULL, 0); + if (!context) { + goto done; + } + + core = PIPEWIRE_pw_context_connect(context, NULL, 0); + if (!core) { + goto done; + } + + /* Attach a core listener and get the version. */ + spa_zero(version_data); + version_data.loop = loop; + pw_core_add_listener(core, &core_listener, &version_check_core_events, &version_data); + version_data.seq = pw_core_sync(core, PW_ID_CORE, 0); + + PIPEWIRE_pw_main_loop_run(loop); + + spa_hook_remove(&core_listener); + + ret = (version_data.major >= major) && + (version_data.major > major || version_data.minor >= minor) && + (version_data.major > major || version_data.minor > minor || version_data.patch >= patch); + +done: + if (core) { + PIPEWIRE_pw_core_disconnect(core); + } + if (context) { + PIPEWIRE_pw_context_destroy(context); + } + if (loop) { + PIPEWIRE_pw_main_loop_destroy(loop); + } + + return ret; +} + +static int init_pipewire_library(SDL_bool check_preferred_version) { if (!load_pipewire_library()) { if (!load_pipewire_syms()) { int nargs; const char *version = PIPEWIRE_pw_get_library_version(); - nargs = SDL_sscanf(version, "%d.%d.%d", &pipewire_version_major, &pipewire_version_minor, &pipewire_version_patch); + nargs = SDL_sscanf(version, "%d.%d.%d", &pipewire_library_version_major, &pipewire_library_version_minor, &pipewire_library_version_patch); if (nargs < 3) { return -1; } - if (pipewire_version_at_least(major, minor, patch)) { + // SDL can build against 0.3.20, but requires 0.3.24 at minimum + if (pipewire_library_version_at_least(0, 3, 24)) { PIPEWIRE_pw_init(NULL, NULL); - return 0; + + if (!check_preferred_version || pipewire_core_version_at_least(1, 0, 0)) { + return 0; + } } } } @@ -1161,7 +1259,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device) * the stream to its target. The target_id parameter in pw_stream_connect() is * now deprecated and should always be PW_ID_ANY. */ - if (pipewire_version_at_least(0, 3, 44)) { + if (pipewire_library_version_at_least(0, 3, 44)) { if (node_id != PW_ID_ANY) { const struct io_node *node; @@ -1255,20 +1353,8 @@ static void PIPEWIRE_Deinitialize(void) static SDL_bool PipewireInitialize(SDL_AudioDriverImpl *impl, SDL_bool check_preferred_version) { if (!pipewire_initialized) { - int major, minor, patch; - - // SDL can build against 0.3.20, but requires 0.3.24 at minimum, and 1.0.0 for preferred default status. - if (check_preferred_version) { - major = 1; - minor = 0; - patch = 0; - } else { - major = 0; - minor = 3; - patch = 24; - } - if (init_pipewire_library(major, minor, patch) < 0) { + if (init_pipewire_library(check_preferred_version) < 0) { return SDL_FALSE; }