mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-05 15:08:53 +00:00
wasapi: Fix infinite loop when the device disappears
When the audio device goes away during playback or capture, we were going into an infinite loop of AUDCLNT_E_DEVICE_INVALIDATED. Return -1 and post an error message so the ringbuffer thread exits with an error.
This commit is contained in:
parent
7a01e9a8cb
commit
d56aec8b0c
3 changed files with 67 additions and 27 deletions
|
@ -458,7 +458,7 @@ gst_wasapi_sink_get_can_frames (GstWasapiSink * self)
|
||||||
|
|
||||||
/* Frames the card hasn't rendered yet */
|
/* Frames the card hasn't rendered yet */
|
||||||
hr = IAudioClient_GetCurrentPadding (self->client, &n_frames_padding);
|
hr = IAudioClient_GetCurrentPadding (self->client, &n_frames_padding);
|
||||||
HR_FAILED_RET (hr, IAudioClient::GetCurrentPadding, -1);
|
HR_FAILED_ELEMENT_ERROR_RET (hr, IAudioClient::GetCurrentPadding, self, -1);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "%i unread frames (padding)", n_frames_padding);
|
GST_DEBUG_OBJECT (self, "%i unread frames (padding)", n_frames_padding);
|
||||||
|
|
||||||
|
@ -604,8 +604,8 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
||||||
GST_OBJECT_LOCK (self);
|
GST_OBJECT_LOCK (self);
|
||||||
if (self->client_needs_restart) {
|
if (self->client_needs_restart) {
|
||||||
hr = IAudioClient_Start (self->client);
|
hr = IAudioClient_Start (self->client);
|
||||||
HR_FAILED_AND (hr, IAudioClient::Start, GST_OBJECT_UNLOCK (self);
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
|
||||||
goto beach);
|
GST_OBJECT_UNLOCK (self); goto err);
|
||||||
self->client_needs_restart = FALSE;
|
self->client_needs_restart = FALSE;
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (self);
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
@ -619,32 +619,43 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
||||||
if (dwWaitResult != WAIT_OBJECT_0) {
|
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||||
(guint) dwWaitResult);
|
(guint) dwWaitResult);
|
||||||
goto beach;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
can_frames = gst_wasapi_sink_get_can_frames (self);
|
can_frames = gst_wasapi_sink_get_can_frames (self);
|
||||||
|
if (can_frames < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Error getting frames to write to");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
/* In exclusive mode we need to fill the whole buffer in one go or
|
/* In exclusive mode we need to fill the whole buffer in one go or
|
||||||
* GetBuffer will error out */
|
* GetBuffer will error out */
|
||||||
if (can_frames != have_frames) {
|
if (can_frames != have_frames) {
|
||||||
GST_ERROR_OBJECT (self,
|
GST_ERROR_OBJECT (self,
|
||||||
"Need at %i frames to write for exclusive mode, but got %i",
|
"Need at %i frames to write for exclusive mode, but got %i",
|
||||||
can_frames, have_frames);
|
can_frames, have_frames);
|
||||||
written_len = -1;
|
goto err;
|
||||||
goto beach;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* In shared mode we can write parts of the buffer, so only wait
|
/* In shared mode we can write parts of the buffer, so only wait
|
||||||
* in case we can't write anything */
|
* in case we can't write anything */
|
||||||
can_frames = gst_wasapi_sink_get_can_frames (self);
|
can_frames = gst_wasapi_sink_get_can_frames (self);
|
||||||
|
if (can_frames < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Error getting frames to write to");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
if (can_frames == 0) {
|
if (can_frames == 0) {
|
||||||
dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
|
dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
|
||||||
if (dwWaitResult != WAIT_OBJECT_0) {
|
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||||
(guint) dwWaitResult);
|
(guint) dwWaitResult);
|
||||||
goto beach;
|
goto err;
|
||||||
}
|
}
|
||||||
can_frames = gst_wasapi_sink_get_can_frames (self);
|
can_frames = gst_wasapi_sink_get_can_frames (self);
|
||||||
|
if (can_frames < 0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Error getting frames to write to");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,19 +669,24 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
||||||
|
|
||||||
hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
|
hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
|
||||||
(BYTE **) & dst);
|
(BYTE **) & dst);
|
||||||
HR_FAILED_AND (hr, IAudioRenderClient::GetBuffer, goto beach);
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioRenderClient::GetBuffer, self,
|
||||||
|
goto err);
|
||||||
|
|
||||||
memcpy (dst, data, write_len);
|
memcpy (dst, data, write_len);
|
||||||
|
|
||||||
hr = IAudioRenderClient_ReleaseBuffer (self->render_client, n_frames,
|
hr = IAudioRenderClient_ReleaseBuffer (self->render_client, n_frames,
|
||||||
self->mute ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
|
self->mute ? AUDCLNT_BUFFERFLAGS_SILENT : 0);
|
||||||
HR_FAILED_AND (hr, IAudioRenderClient::ReleaseBuffer, goto beach);
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioRenderClient::ReleaseBuffer, self,
|
||||||
|
goto err);
|
||||||
|
|
||||||
written_len = write_len;
|
written_len = write_len;
|
||||||
|
|
||||||
beach:
|
out:
|
||||||
|
|
||||||
return written_len;
|
return written_len;
|
||||||
|
|
||||||
|
err:
|
||||||
|
written_len = -1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
|
|
|
@ -562,7 +562,8 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
||||||
GST_OBJECT_LOCK (self);
|
GST_OBJECT_LOCK (self);
|
||||||
if (self->client_needs_restart) {
|
if (self->client_needs_restart) {
|
||||||
hr = IAudioClient_Start (self->client);
|
hr = IAudioClient_Start (self->client);
|
||||||
HR_FAILED_AND (hr, IAudioClient::Start, length = 0; goto beach);
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
|
||||||
|
GST_OBJECT_UNLOCK (self); goto err);
|
||||||
self->client_needs_restart = FALSE;
|
self->client_needs_restart = FALSE;
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (self);
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
@ -576,23 +577,22 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
||||||
if (dwWaitResult != WAIT_OBJECT_0) {
|
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||||
(guint) dwWaitResult);
|
(guint) dwWaitResult);
|
||||||
length = 0;
|
goto err;
|
||||||
goto beach;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hr = IAudioCaptureClient_GetBuffer (self->capture_client,
|
hr = IAudioCaptureClient_GetBuffer (self->capture_client,
|
||||||
(BYTE **) & from, &have_frames, &flags, NULL, NULL);
|
(BYTE **) & from, &have_frames, &flags, NULL, NULL);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
|
if (hr == AUDCLNT_S_BUFFER_EMPTY) {
|
||||||
gchar *msg = gst_wasapi_util_hresult_to_string (hr);
|
gchar *msg = gst_wasapi_util_hresult_to_string (hr);
|
||||||
if (hr == AUDCLNT_S_BUFFER_EMPTY)
|
|
||||||
GST_WARNING_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s"
|
GST_WARNING_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s"
|
||||||
", retrying", msg);
|
", retrying", msg);
|
||||||
else
|
|
||||||
GST_ERROR_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s",
|
|
||||||
msg);
|
|
||||||
g_free (msg);
|
g_free (msg);
|
||||||
length = 0;
|
length = 0;
|
||||||
goto beach;
|
goto out;
|
||||||
|
}
|
||||||
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioCaptureClient::GetBuffer, self,
|
||||||
|
goto err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags != 0)
|
if (flags != 0)
|
||||||
|
@ -629,13 +629,17 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
||||||
|
|
||||||
/* Always release all captured buffers if we've captured any at all */
|
/* Always release all captured buffers if we've captured any at all */
|
||||||
hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, have_frames);
|
hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, have_frames);
|
||||||
HR_FAILED_AND (hr, IAudioClock::ReleaseBuffer, goto beach);
|
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioCaptureClient::ReleaseBuffer, self,
|
||||||
|
goto err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
beach:
|
out:
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
|
|
||||||
|
err:
|
||||||
|
length = -1;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
|
|
|
@ -42,19 +42,39 @@
|
||||||
|
|
||||||
/* Standard error path */
|
/* Standard error path */
|
||||||
#define HR_FAILED_AND(hr,func,and) \
|
#define HR_FAILED_AND(hr,func,and) \
|
||||||
do { \
|
G_STMT_START { \
|
||||||
if (FAILED (hr)) { \
|
if (FAILED (hr)) { \
|
||||||
gchar *msg = gst_wasapi_util_hresult_to_string (hr); \
|
gchar *msg = gst_wasapi_util_hresult_to_string (hr); \
|
||||||
GST_ERROR_OBJECT (self, #func " failed (%x): %s", (guint) hr, msg); \
|
GST_ERROR_OBJECT (self, #func " failed (%x): %s", (guint) hr, msg); \
|
||||||
g_free (msg); \
|
g_free (msg); \
|
||||||
and; \
|
and; \
|
||||||
} \
|
} \
|
||||||
} while (0)
|
} G_STMT_END
|
||||||
|
|
||||||
#define HR_FAILED_RET(hr,func,ret) HR_FAILED_AND(hr,func,return ret)
|
#define HR_FAILED_RET(hr,func,ret) HR_FAILED_AND(hr,func,return ret)
|
||||||
|
|
||||||
#define HR_FAILED_GOTO(hr,func,where) HR_FAILED_AND(hr,func,res = FALSE; goto where)
|
#define HR_FAILED_GOTO(hr,func,where) HR_FAILED_AND(hr,func,res = FALSE; goto where)
|
||||||
|
|
||||||
|
#define HR_FAILED_ELEMENT_ERROR_AND(hr,func,el,and) \
|
||||||
|
G_STMT_START { \
|
||||||
|
if (FAILED (hr)) { \
|
||||||
|
gchar *msg = gst_wasapi_util_hresult_to_string (hr); \
|
||||||
|
GST_ERROR_OBJECT (el, #func " failed (%x): %s", (guint) hr, msg); \
|
||||||
|
if (GST_IS_AUDIO_SRC (el)) \
|
||||||
|
GST_ELEMENT_ERROR(el, RESOURCE, READ, \
|
||||||
|
(#func " failed (%x): %s", (guint) hr, msg), (NULL)); \
|
||||||
|
else \
|
||||||
|
GST_ELEMENT_ERROR(el, RESOURCE, WRITE, \
|
||||||
|
(#func " failed (%x): %s", (guint) hr, msg), (NULL)); \
|
||||||
|
g_free (msg); \
|
||||||
|
and; \
|
||||||
|
} \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
|
#define HR_FAILED_ELEMENT_ERROR_RET(hr,func,el,ret) \
|
||||||
|
HR_FAILED_ELEMENT_ERROR_AND(hr,func,el,return ret)
|
||||||
|
|
||||||
|
|
||||||
/* Device role enum property */
|
/* Device role enum property */
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue