PipeWire: Fix a possible deadlock during initialization.

This occurs when attempting to initialize a capture device when not such
device is available, such as if the microphone is unplugged.
This commit is contained in:
David Reid
2026-06-25 09:38:54 +10:00
parent 4679631f5b
commit 459eb8b492
+70 -10
View File
@@ -32078,9 +32078,10 @@ typedef struct
} ma_context_state_pipewire; } ma_context_state_pipewire;
#define MA_PIPEWIRE_INIT_STATUS_HAS_FORMAT 0x01 #define MA_PIPEWIRE_INIT_STATUS_ERROR 0x01
#define MA_PIPEWIRE_INIT_STATUS_HAS_LATENCY 0x02 #define MA_PIPEWIRE_INIT_STATUS_HAS_FORMAT 0x02
#define MA_PIPEWIRE_INIT_STATUS_INITIALIZED 0x04 #define MA_PIPEWIRE_INIT_STATUS_HAS_LATENCY 0x04
#define MA_PIPEWIRE_INIT_STATUS_INITIALIZED 0x08
typedef struct typedef struct
{ {
@@ -33210,6 +33211,26 @@ static ma_result ma_context_enumerate_devices__pipewire(ma_context* pContext, ma
} }
static void ma_stream_event_state_changed__pipewire(void* pUserData, enum ma_pw_stream_state oldState, enum ma_pw_stream_state state, const char* pError, ma_device_type deviceType)
{
ma_device_state_pipewire* pDeviceStatePipeWire = (ma_device_state_pipewire*)pUserData;
ma_pipewire_stream_state* pStreamState;
(void)oldState;
(void)pError;
if (deviceType == ma_device_type_playback) {
pStreamState = &pDeviceStatePipeWire->playback;
} else {
pStreamState = &pDeviceStatePipeWire->capture;
}
if (state == MA_PW_STREAM_STATE_ERROR) {
pStreamState->initStatus |= MA_PIPEWIRE_INIT_STATUS_ERROR;
}
}
static void ma_stream_event_param_changed__pipewire(void* pUserData, ma_uint32 id, const struct ma_spa_pod* pParam, ma_device_type deviceType) static void ma_stream_event_param_changed__pipewire(void* pUserData, ma_uint32 id, const struct ma_spa_pod* pParam, ma_device_type deviceType)
{ {
ma_device_state_pipewire* pDeviceStatePipeWire = (ma_device_state_pipewire*)pUserData; ma_device_state_pipewire* pDeviceStatePipeWire = (ma_device_state_pipewire*)pUserData;
@@ -33442,6 +33463,17 @@ static void ma_stream_event_process__pipewire(void* pUserData, ma_device_type de
} }
static void ma_stream_event_state_changed_playback__pipewire(void* pUserData, enum ma_pw_stream_state oldState, enum ma_pw_stream_state state, const char* pError)
{
ma_stream_event_state_changed__pipewire(pUserData, oldState, state, pError, ma_device_type_playback);
}
static void ma_stream_event_state_changed_capture__pipewire(void* pUserData, enum ma_pw_stream_state oldState, enum ma_pw_stream_state state, const char* pError)
{
ma_stream_event_state_changed__pipewire(pUserData, oldState, state, pError, ma_device_type_capture);
}
static void ma_stream_event_param_changed_playback__pipewire(void* pUserData, ma_uint32 id, const struct ma_spa_pod* pParam) static void ma_stream_event_param_changed_playback__pipewire(void* pUserData, ma_uint32 id, const struct ma_spa_pod* pParam)
{ {
ma_stream_event_param_changed__pipewire(pUserData, id, pParam, ma_device_type_playback); ma_stream_event_param_changed__pipewire(pUserData, id, pParam, ma_device_type_playback);
@@ -33468,7 +33500,7 @@ static const struct ma_pw_stream_events ma_gStreamEventsPipeWire_Playback =
{ {
MA_PW_VERSION_STREAM_EVENTS, MA_PW_VERSION_STREAM_EVENTS,
NULL, /* destroy */ NULL, /* destroy */
NULL, /* state_changed */ ma_stream_event_state_changed_playback__pipewire,
NULL, /* control_info */ NULL, /* control_info */
NULL, /* io_changed */ NULL, /* io_changed */
ma_stream_event_param_changed_playback__pipewire, ma_stream_event_param_changed_playback__pipewire,
@@ -33485,7 +33517,7 @@ static const struct ma_pw_stream_events ma_gStreamEventsPipeWire_Capture =
{ {
MA_PW_VERSION_STREAM_EVENTS, MA_PW_VERSION_STREAM_EVENTS,
NULL, /* destroy */ NULL, /* destroy */
NULL, /* state_changed */ ma_stream_event_state_changed_capture__pipewire,
NULL, /* control_info */ NULL, /* control_info */
NULL, /* io_changed */ NULL, /* io_changed */
ma_stream_event_param_changed_capture__pipewire, ma_stream_event_param_changed_capture__pipewire,
@@ -33589,9 +33621,25 @@ static ma_result ma_device_init_internal__pipewire(ma_device* pDevice, ma_contex
return MA_ERROR; return MA_ERROR;
} }
/* We need to keep iterating until we have finalized our internal format. */ /*
while ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_HAS_FORMAT) == 0) { We need to keep iterating until we have finalized our internal format. Note that when there is no
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, 1); microphone connected the process callback will never get fired which will result in this loop getting
stuck, so we'll use a timeout here.
*/
{
ma_uint32 timeoutMS = 2000;
ma_uint32 elapsedMS = 0;
while ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_HAS_FORMAT) == 0) {
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, 1);
elapsedMS += 1;
if (elapsedMS >= timeoutMS || ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_ERROR) != 0)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to initialize capture device. Possibly no microphone connected.");
pContextStatePipeWire->pw_stream_destroy(pStreamState->pStream);
return MA_ERROR;
}
}
} }
/* We should have our format at this point, but we will not know the exact period size yet until we've done the first processing callback. */ /* We should have our format at this point, but we will not know the exact period size yet until we've done the first processing callback. */
@@ -33601,8 +33649,20 @@ static ma_result ma_device_init_internal__pipewire(ma_device* pDevice, ma_contex
ma_channel_map_copy_or_default(pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pStreamState->channelMap, pStreamState->channels); ma_channel_map_copy_or_default(pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pStreamState->channelMap, pStreamState->channels);
/* Now we need to wait until we know our period size. */ /* Now we need to wait until we know our period size. */
while ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_HAS_LATENCY) == 0) { {
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, 1); ma_uint32 timeoutMS = 2000;
ma_uint32 elapsedMS = 0;
while ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_HAS_LATENCY) == 0) {
pContextStatePipeWire->pw_loop_iterate(pDeviceStatePipeWire->pLoop, 1);
elapsedMS += 1;
if (elapsedMS >= timeoutMS || ((pStreamState->initStatus & MA_PIPEWIRE_INIT_STATUS_ERROR) != 0)) {
ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "Failed to initialize capture device. Possibly no microphone connected.");
pContextStatePipeWire->pw_stream_destroy(pStreamState->pStream);
return MA_ERROR;
}
}
} }
pDescriptor->periodSizeInFrames = pStreamState->rbSizeInFrames; pDescriptor->periodSizeInFrames = pStreamState->rbSizeInFrames;