camera: improve PipeWire version checks

Remove the custom server version check. We can easily do this as part of
starting the hotplug loop. Check that we are at least running against a
1.0.0 server.

Log the compiled, linked, server and required versions.

Check that we are compiled and linked with the right version before using
the time symbol of a struct.
This commit is contained in:
Wim Taymans
2024-05-09 12:36:35 +02:00
committed by Sam Lantinga
parent da06e67b1b
commit 2b9ac185cd

View File

@@ -41,6 +41,10 @@
#define PW_THREAD_NAME_BUFFER_LENGTH 128
#define PW_MAX_IDENTIFIER_LENGTH 256
#define PW_REQUIRED_MAJOR 1
#define PW_REQUIRED_MINOR 0
#define PW_REQUIRED_PATCH 0
enum PW_READY_FLAGS
{
PW_READY_FLAG_BUFFER_ADDED = 0x1,
@@ -55,6 +59,7 @@ static SDL_bool pipewire_initialized = SDL_FALSE;
// Pipewire entry points
static const char *(*PIPEWIRE_pw_get_library_version)(void);
static bool (*PIPEWIRE_pw_check_library_version)(int major, int minor, int micro);
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);
@@ -149,6 +154,7 @@ static void unload_pipewire_library(void)
static int load_pipewire_syms(void)
{
SDL_PIPEWIRE_SYM(pw_get_library_version);
SDL_PIPEWIRE_SYM(pw_check_library_version);
SDL_PIPEWIRE_SYM(pw_init);
SDL_PIPEWIRE_SYM(pw_deinit);
SDL_PIPEWIRE_SYM(pw_main_loop_new);
@@ -190,107 +196,14 @@ static int load_pipewire_syms(void)
return 0;
}
/* 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;
};
static 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;
}
}
static 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 =
{
.version = PW_VERSION_CORE_EVENTS,
.info = version_check_core_info_callback,
.done = version_check_core_done_callback
};
static 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)
static int init_pipewire_library(void)
{
if (!load_pipewire_library()) {
if (!load_pipewire_syms()) {
PIPEWIRE_pw_init(NULL, NULL);
if (!check_preferred_version || pipewire_core_version_at_least(1, 0, 0)) {
return 0;
}
return 0;
}
}
return -1;
}
@@ -309,6 +222,9 @@ static struct
struct pw_core *core;
struct spa_hook core_listener;
int server_major;
int server_minor;
int server_patch;
int last_seq;
int pending_seq;
@@ -317,6 +233,7 @@ static struct
struct spa_list global_list;
SDL_bool have_1_0_5;
SDL_bool init_complete;
SDL_bool events_enabled;
} hotplug;
@@ -648,7 +565,11 @@ static int PIPEWIRECAMERA_AcquireFrame(SDL_CameraDevice *device, SDL_Surface *fr
return 0;
}
*timestampNS = b->time;
#if PW_CHECK_VERSION(1,0,5)
*timestampNS = hotplug.have_1_0_5 ? b->time : SDL_GetTicksNS();
#else
*timestampNS = SDL_GetTicksNS();
#endif
frame->pixels = b->buffer->datas[0].data;
frame->pitch = b->buffer->datas[0].chunk->stride;
@@ -777,8 +698,6 @@ static void add_device(struct global *g)
SDL_zero(data);
SDL_Log("CAMERA: found %d", g->id);
spa_list_for_each(p, &g->param_list, link) {
if (p->id != SPA_PARAM_EnumFormat)
continue;
@@ -989,6 +908,21 @@ static const struct pw_registry_events hotplug_registry_events =
.global_remove = hotplug_registry_global_remove_callback
};
static void parse_version(const char *str, int *major, int *minor, int *patch)
{
if (SDL_sscanf(str, "%d.%d.%d", major, minor, patch) < 3) {
*major = 0;
*minor = 0;
*patch = 0;
}
}
// Core info, called with thread_loop lock
static void hotplug_core_info_callback(void *data, const struct pw_core_info *info)
{
parse_version(info->version, &hotplug.server_major, &hotplug.server_minor, &hotplug.server_patch);
}
// Core sync points, called with thread_loop lock
static void hotplug_core_done_callback(void *object, uint32_t id, int seq)
{
@@ -1015,9 +949,20 @@ static void hotplug_core_done_callback(void *object, uint32_t id, int seq)
static const struct pw_core_events hotplug_core_events =
{
.version = PW_VERSION_CORE_EVENTS,
.info = hotplug_core_info_callback,
.done = hotplug_core_done_callback
};
/* 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.
*/
static SDL_bool pipewire_server_version_at_least(int major, int minor, int patch)
{
return (hotplug.server_major >= major) &&
(hotplug.server_major > major || hotplug.server_minor >= minor) &&
(hotplug.server_major > major || hotplug.server_minor > minor || hotplug.server_patch >= patch);
}
// The hotplug thread
static int hotplug_loop_init(void)
{
@@ -1025,6 +970,8 @@ static int hotplug_loop_init(void)
spa_list_init(&hotplug.global_list);
hotplug.have_1_0_5 = PIPEWIRE_pw_check_library_version(1,0,5);
hotplug.loop = PIPEWIRE_pw_thread_loop_new("SDLAudioHotplug", NULL);
if (!hotplug.loop) {
return SDL_SetError("Pipewire: Failed to create hotplug detection loop (%i)", errno);
@@ -1050,13 +997,31 @@ static int hotplug_loop_init(void)
spa_zero(hotplug.registry_listener);
pw_registry_add_listener(hotplug.registry, &hotplug.registry_listener, &hotplug_registry_events, NULL);
hotplug.pending_seq = pw_core_sync(hotplug.core, PW_ID_CORE, 0);
do_resync();
res = PIPEWIRE_pw_thread_loop_start(hotplug.loop);
if (res != 0) {
return SDL_SetError("Pipewire: Failed to start hotplug detection loop");
}
PIPEWIRE_pw_thread_loop_lock(hotplug.loop);
while (!hotplug.init_complete) {
PIPEWIRE_pw_thread_loop_wait(hotplug.loop);
}
PIPEWIRE_pw_thread_loop_unlock(hotplug.loop);
SDL_Log("CAMERA: PipeWire compiled:%s library:%s server:%d.%d.%d required:%d.%d.%d",
pw_get_headers_version(),
PIPEWIRE_pw_get_library_version(),
hotplug.server_major, hotplug.server_minor, hotplug.server_patch,
PW_REQUIRED_MAJOR, PW_REQUIRED_MINOR, PW_REQUIRED_PATCH);
if (!pipewire_server_version_at_least(PW_REQUIRED_MAJOR, PW_REQUIRED_MINOR, PW_REQUIRED_PATCH)) {
return SDL_SetError("Pipewire: server version is too old %d.%d.%d < %d.%d.%d",
hotplug.server_major, hotplug.server_minor, hotplug.server_patch,
PW_REQUIRED_MAJOR, PW_REQUIRED_MINOR, PW_REQUIRED_PATCH);
}
return 0;
}
@@ -1092,7 +1057,7 @@ static SDL_bool PIPEWIRECAMERA_Init(SDL_CameraDriverImpl *impl)
{
if (!pipewire_initialized) {
if (init_pipewire_library(true) < 0) {
if (init_pipewire_library() < 0) {
return SDL_FALSE;
}