diff --git a/src/camera/pipewire/SDL_camera_pipewire.c b/src/camera/pipewire/SDL_camera_pipewire.c index 559078134e..dac5e3baf2 100644 --- a/src/camera/pipewire/SDL_camera_pipewire.c +++ b/src/camera/pipewire/SDL_camera_pipewire.c @@ -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; }