mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-10 17:35:59 +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 */
|
||||
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);
|
||||
|
||||
|
@ -604,8 +604,8 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
|||
GST_OBJECT_LOCK (self);
|
||||
if (self->client_needs_restart) {
|
||||
hr = IAudioClient_Start (self->client);
|
||||
HR_FAILED_AND (hr, IAudioClient::Start, GST_OBJECT_UNLOCK (self);
|
||||
goto beach);
|
||||
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
|
||||
GST_OBJECT_UNLOCK (self); goto err);
|
||||
self->client_needs_restart = FALSE;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
@ -619,32 +619,43 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
|||
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||
(guint) dwWaitResult);
|
||||
goto beach;
|
||||
goto err;
|
||||
}
|
||||
|
||||
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
|
||||
* GetBuffer will error out */
|
||||
if (can_frames != have_frames) {
|
||||
GST_ERROR_OBJECT (self,
|
||||
"Need at %i frames to write for exclusive mode, but got %i",
|
||||
can_frames, have_frames);
|
||||
written_len = -1;
|
||||
goto beach;
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
/* In shared mode we can write parts of the buffer, so only wait
|
||||
* in case we can't write anything */
|
||||
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) {
|
||||
dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
|
||||
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||
(guint) dwWaitResult);
|
||||
goto beach;
|
||||
goto err;
|
||||
}
|
||||
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,
|
||||
(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);
|
||||
|
||||
hr = IAudioRenderClient_ReleaseBuffer (self->render_client, n_frames,
|
||||
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;
|
||||
|
||||
beach:
|
||||
|
||||
out:
|
||||
return written_len;
|
||||
|
||||
err:
|
||||
written_len = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static guint
|
||||
|
|
|
@ -562,7 +562,8 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
|||
GST_OBJECT_LOCK (self);
|
||||
if (self->client_needs_restart) {
|
||||
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;
|
||||
}
|
||||
GST_OBJECT_UNLOCK (self);
|
||||
|
@ -576,23 +577,22 @@ gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
|
|||
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||
(guint) dwWaitResult);
|
||||
length = 0;
|
||||
goto beach;
|
||||
goto err;
|
||||
}
|
||||
|
||||
hr = IAudioCaptureClient_GetBuffer (self->capture_client,
|
||||
(BYTE **) & from, &have_frames, &flags, NULL, NULL);
|
||||
if (hr != S_OK) {
|
||||
if (hr == AUDCLNT_S_BUFFER_EMPTY) {
|
||||
gchar *msg = gst_wasapi_util_hresult_to_string (hr);
|
||||
if (hr == AUDCLNT_S_BUFFER_EMPTY)
|
||||
GST_WARNING_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s"
|
||||
", retrying", msg);
|
||||
else
|
||||
GST_ERROR_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s",
|
||||
msg);
|
||||
g_free (msg);
|
||||
length = 0;
|
||||
goto beach;
|
||||
goto out;
|
||||
}
|
||||
HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioCaptureClient::GetBuffer, self,
|
||||
goto err);
|
||||
}
|
||||
|
||||
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 */
|
||||
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;
|
||||
|
||||
err:
|
||||
length = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static guint
|
||||
|
|
|
@ -42,19 +42,39 @@
|
|||
|
||||
/* Standard error path */
|
||||
#define HR_FAILED_AND(hr,func,and) \
|
||||
do { \
|
||||
G_STMT_START { \
|
||||
if (FAILED (hr)) { \
|
||||
gchar *msg = gst_wasapi_util_hresult_to_string (hr); \
|
||||
GST_ERROR_OBJECT (self, #func " failed (%x): %s", (guint) hr, msg); \
|
||||
g_free (msg); \
|
||||
and; \
|
||||
} \
|
||||
} while (0)
|
||||
} G_STMT_END
|
||||
|
||||
#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_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 */
|
||||
typedef enum
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue