wasapisink: recover from low buffer levels in shared mode
In case the wasapi buffer levels got low in shared mode we would still wait until more buffer is available until writing something in it, which means we could never catch up and recover. Instead only wait for a new buffer in case the existing one is full and always write what we can. Also don't loop until all data is written since the base class can handle that for us and under normal circumstances this doesn't happen anyway. This only works in shared mode, as in exclusive mode we have to exactly fill the buffer and always have to wait first. This fixes noisy (buffer underrun) playback with the wasapisink under load. https://bugzilla.gnome.org/show_bug.cgi?id=796354
This commit is contained in:
parent
0aed64426b
commit
3b1c7ef8e4
@ -608,58 +608,78 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length)
|
|||||||
GstWasapiSink *self = GST_WASAPI_SINK (asink);
|
GstWasapiSink *self = GST_WASAPI_SINK (asink);
|
||||||
HRESULT hr;
|
HRESULT hr;
|
||||||
gint16 *dst = NULL;
|
gint16 *dst = NULL;
|
||||||
guint pending = length;
|
DWORD dwWaitResult;
|
||||||
|
guint can_frames, have_frames, n_frames, write_len, written_len = 0;
|
||||||
|
|
||||||
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,
|
HR_FAILED_AND (hr, IAudioClient::Start, GST_OBJECT_UNLOCK (self); goto beach);
|
||||||
GST_OBJECT_UNLOCK (self); length = 0; goto beach);
|
|
||||||
self->client_needs_restart = FALSE;
|
self->client_needs_restart = FALSE;
|
||||||
}
|
}
|
||||||
GST_OBJECT_UNLOCK (self);
|
GST_OBJECT_UNLOCK (self);
|
||||||
|
|
||||||
while (pending > 0) {
|
/* We have N frames to be written out */
|
||||||
DWORD dwWaitResult;
|
have_frames = length / (self->mix_format->nBlockAlign);
|
||||||
guint can_frames, have_frames, n_frames, write_len;
|
|
||||||
|
|
||||||
|
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) {
|
||||||
|
/* In exlusive mode we have to wait always */
|
||||||
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);
|
||||||
length -= pending;
|
|
||||||
goto beach;
|
goto beach;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have N frames to be written out */
|
|
||||||
have_frames = pending / (self->mix_format->nBlockAlign);
|
|
||||||
/* We have can_frames space in the output buffer */
|
|
||||||
can_frames = gst_wasapi_sink_get_can_frames (self);
|
can_frames = gst_wasapi_sink_get_can_frames (self);
|
||||||
/* We will write out these many frames, and this much length */
|
/* In exclusive mode we need to fill the whole buffer in one go or
|
||||||
n_frames = MIN (can_frames, have_frames);
|
* GetBuffer will error out */
|
||||||
write_len = n_frames * self->mix_format->nBlockAlign;
|
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;
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (self, "total: %i, have_frames: %i (%i bytes), "
|
if (can_frames == 0) {
|
||||||
"can_frames: %i, will write: %i (%i bytes)", self->buffer_frame_count,
|
dwWaitResult = WaitForSingleObject (self->event_handle, INFINITE);
|
||||||
have_frames, pending, can_frames, n_frames, write_len);
|
if (dwWaitResult != WAIT_OBJECT_0) {
|
||||||
|
GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
|
||||||
hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
|
(guint) dwWaitResult);
|
||||||
(BYTE **) & dst);
|
goto beach;
|
||||||
HR_FAILED_AND (hr, IAudioRenderClient::GetBuffer, length = 0; goto beach);
|
}
|
||||||
|
can_frames = gst_wasapi_sink_get_can_frames (self);
|
||||||
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, length = 0;
|
|
||||||
goto beach);
|
|
||||||
|
|
||||||
pending -= write_len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* We will write out these many frames, and this much length */
|
||||||
|
n_frames = MIN (can_frames, have_frames);
|
||||||
|
write_len = n_frames * self->mix_format->nBlockAlign;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (self, "total: %i, have_frames: %i (%i bytes), "
|
||||||
|
"can_frames: %i, will write: %i (%i bytes)", self->buffer_frame_count,
|
||||||
|
have_frames, length, can_frames, n_frames, write_len);
|
||||||
|
|
||||||
|
hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames,
|
||||||
|
(BYTE **) & dst);
|
||||||
|
HR_FAILED_AND (hr, IAudioRenderClient::GetBuffer, goto beach);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
written_len = write_len;
|
||||||
|
|
||||||
beach:
|
beach:
|
||||||
|
|
||||||
return length;
|
return written_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
static guint
|
static guint
|
||||||
|
Loading…
x
Reference in New Issue
Block a user