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 */ /* 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

View file

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

View file

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