wasapi: Correctly set ringbuffer segsize/segtotal

This will set the actual-latency-time and actual-buffer-time of the sink
and source.

We completely ignore the latency-time/buffer-time values set
on the element because WASAPI is happiest when it is reading/writing at
the default period. Improving this will likely require the use of the
IAudioClient3 interfaces which are not available in MinGW yet.

https://bugzilla.gnome.org/show_bug.cgi?id=792897
This commit is contained in:
Nirbheek Chauhan 2018-01-31 03:51:47 +05:30
parent ec6a10ed06
commit 538ccb6093
2 changed files with 77 additions and 28 deletions

View file

@ -346,13 +346,29 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
{
GstWasapiSink *self = GST_WASAPI_SINK (asink);
gboolean res = FALSE;
HRESULT hr;
REFERENCE_TIME latency_rt;
IAudioRenderClient *render_client = NULL;
gint64 default_period, min_period;
guint bpf, rate;
HRESULT hr;
hr = IAudioClient_GetDevicePeriod (self->client, &default_period, &min_period);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetDevicePeriod failed");
goto beach;
}
GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT
", min period: %" G_GINT64_FORMAT, default_period, min_period);
/* Set hnsBufferDuration to 0, which should, in theory, tell the device to
* create a buffer with the smallest latency possible. In practice, this is
* usually 2 * default_period. See:
* https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx
*
* NOTE: min_period is a lie, and I have never seen WASAPI use it as the
* current period */
hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
spec->buffer_time * 10, 0, self->mix_format, NULL);
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL);
if (hr != S_OK) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
("IAudioClient::Initialize () failed: %s",
@ -360,6 +376,26 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
goto beach;
}
/* Total size of the allocated buffer that we will write to */
hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetBufferSize failed");
goto beach;
}
bpf = GST_AUDIO_INFO_BPF (&spec->info);
rate = GST_AUDIO_INFO_RATE (&spec->info);
GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, "
"rate is %i Hz", self->buffer_frame_count, bpf, rate);
/* Actual latency-time/buffer-time are different now */
spec->segsize = gst_util_uint64_scale_int_round (rate * bpf,
default_period * 100, GST_SECOND);
spec->segtotal = (self->buffer_frame_count * bpf) / spec->segsize;
GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize,
spec->segtotal);
/* Get latency for logging */
hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
if (hr != S_OK) {
@ -376,17 +412,6 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
goto beach;
}
/* Total size of the allocated buffer that we will write to
* XXX: Will this ever change while playing? */
hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetBufferSize failed");
goto beach;
}
GST_INFO_OBJECT (self, "frame count is %i, blockAlign is %i, "
"buffer_time is %" G_GINT64_FORMAT, self->buffer_frame_count,
self->mix_format->nBlockAlign, spec->buffer_time);
/* Get render sink client and start it up */
if (!gst_wasapi_util_get_render_client (GST_ELEMENT (self), self->client,
&render_client)) {

View file

@ -346,11 +346,27 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
guint64 client_clock_freq = 0;
IAudioCaptureClient *capture_client = NULL;
REFERENCE_TIME latency_rt;
gint64 default_period, min_period;
guint bpf, rate, buffer_frames;
HRESULT hr;
hr = IAudioClient_GetDevicePeriod (self->client, &default_period, &min_period);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetDevicePeriod failed");
goto beach;
}
GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT
", min period: %" G_GINT64_FORMAT, default_period, min_period);
/* Set hnsBufferDuration to 0, which should, in theory, tell the device to
* create a buffer with the smallest latency possible. In practice, this is
* usually 2 * default_period. See:
* https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx
*
* NOTE: min_period is a lie, and I have never seen WASAPI use it as the
* current period */
hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, spec->buffer_time * 10, 0,
self->mix_format, NULL);
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL);
if (hr != S_OK) {
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
("IAudioClient::Initialize failed: %s",
@ -358,7 +374,26 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
goto beach;
}
/* Get latency for logging */
/* Total size in frames of the allocated buffer that we will read from */
hr = IAudioClient_GetBufferSize (self->client, &buffer_frames);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetBufferSize failed");
goto beach;
}
bpf = GST_AUDIO_INFO_BPF (&spec->info);
rate = GST_AUDIO_INFO_RATE (&spec->info);
GST_INFO_OBJECT (self, "buffer size is %i frames, bpf is %i bytes, "
"rate is %i Hz", buffer_frames, bpf, rate);
spec->segsize = gst_util_uint64_scale_int_round (rate * bpf,
default_period * 100, GST_SECOND);
spec->segtotal = (buffer_frames * bpf) / spec->segsize;
GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize,
spec->segtotal);
/* Get WASAPI latency for logging */
hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetStreamLatency failed");
@ -386,17 +421,6 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
goto beach;
}
/* Total size of the allocated buffer that we will read from
* XXX: Will this ever change while playing? */
hr = IAudioClient_GetBufferSize (self->client, &self->buffer_frame_count);
if (hr != S_OK) {
GST_ERROR_OBJECT (self, "IAudioClient::GetBufferSize failed");
goto beach;
}
GST_INFO_OBJECT (self, "frame count is %i, blockAlign is %i, "
"buffer_time is %" G_GINT64_FORMAT, self->buffer_frame_count,
self->mix_format->nBlockAlign, spec->buffer_time);
/* Get capture source client and start it up */
if (!gst_wasapi_util_get_capture_client (GST_ELEMENT (self), self->client,
&capture_client)) {