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:
Nirbheek Chauhan 2019-01-15 02:03:23 +05:30
parent 7a01e9a8cb
commit d56aec8b0c
3 changed files with 67 additions and 27 deletions

View file

@ -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

View file

@ -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

View file

@ -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
{