From d56aec8b0cceb15457712476f9690bd0f18e33ac Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Tue, 15 Jan 2019 02:03:23 +0530 Subject: [PATCH] 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. --- sys/wasapi/gstwasapisink.c | 38 +++++++++++++++++++++++++++----------- sys/wasapi/gstwasapisrc.c | 32 ++++++++++++++++++-------------- sys/wasapi/gstwasapiutil.h | 24 ++++++++++++++++++++++-- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c index 1e3b07a9d4..eda3b83f0f 100644 --- a/sys/wasapi/gstwasapisink.c +++ b/sys/wasapi/gstwasapisink.c @@ -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 diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c index 3d28b5c1be..6c11658f06 100644 --- a/sys/wasapi/gstwasapisrc.c +++ b/sys/wasapi/gstwasapisrc.c @@ -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) { - gchar *msg = gst_wasapi_util_hresult_to_string (hr); - if (hr == AUDCLNT_S_BUFFER_EMPTY) + if (hr == AUDCLNT_S_BUFFER_EMPTY) { + gchar *msg = gst_wasapi_util_hresult_to_string (hr); 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; + g_free (msg); + length = 0; + 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 diff --git a/sys/wasapi/gstwasapiutil.h b/sys/wasapi/gstwasapiutil.h index 502c76396e..599a603643 100644 --- a/sys/wasapi/gstwasapiutil.h +++ b/sys/wasapi/gstwasapiutil.h @@ -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 {