diff --git a/include/libremidi/backends/linux/pipewire/context.hpp b/include/libremidi/backends/linux/pipewire/context.hpp index dfdeec63..a983d0a5 100644 --- a/include/libremidi/backends/linux/pipewire/context.hpp +++ b/include/libremidi/backends/linux/pipewire/context.hpp @@ -308,7 +308,13 @@ class context : public std::enable_shared_from_this } return 0; }; - pw_loop_invoke(m_loop, trampoline, 0, nullptr, 0, /*block=*/true, &f); + // A blocking pw_loop_invoke from a foreign thread fires the loop + // control hooks (thread-loop unlock before the wait / lock after), + // which REQUIRE the caller to hold the thread-loop lock. Calling + // without it underflows the recurse counter (refused unlock) and + // then acquires the mutex on the way out, deadlocking the loop. + with_lock( + [&] { pw_loop_invoke(m_loop, trampoline, 0, nullptr, 0, /*block=*/true, &f); }); } else { @@ -330,7 +336,9 @@ class context : public std::enable_shared_from_this } return 0; }; - pw_loop_invoke(m_loop, trampoline, 0, nullptr, 0, /*block=*/true, &p); + // See the void branch: blocking invokes require the thread-loop lock. + with_lock( + [&] { pw_loop_invoke(m_loop, trampoline, 0, nullptr, 0, /*block=*/true, &p); }); return std::move(*p.result); } } diff --git a/include/libremidi/backends/linux/pipewire/format.hpp b/include/libremidi/backends/linux/pipewire/format.hpp index d83c7eb3..6d11780c 100644 --- a/include/libremidi/backends/linux/pipewire/format.hpp +++ b/include/libremidi/backends/linux/pipewire/format.hpp @@ -320,10 +320,15 @@ inline const spa_pod* format_negotiation::build_buffers_param( data_type = (1u << SPA_DATA_MemPtr); break; } + // size/stride are RANGES with our tight computation as the minimum: + // producers commonly deliver row-padded buffers (GPU-allocated strides), + // and a fixed Int here fails the param intersection against any producer + // whose size differs — killing the link instead of accepting padding. return reinterpret_cast(spa_pod_builder_add_object( &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(8, 2, 16), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(blocks), - SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride, SPA_POD_Int(stride), + SPA_PARAM_BUFFERS_size, SPA_POD_CHOICE_RANGE_Int(size, size, INT32_MAX), + SPA_PARAM_BUFFERS_stride, SPA_POD_CHOICE_RANGE_Int(stride, stride, INT32_MAX), SPA_PARAM_BUFFERS_dataType, SPA_POD_CHOICE_FLAGS_Int(data_type))); }