mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-30 05:31:15 +00:00
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:
parent
ec6a10ed06
commit
538ccb6093
2 changed files with 77 additions and 28 deletions
|
@ -346,13 +346,29 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
|
||||||
{
|
{
|
||||||
GstWasapiSink *self = GST_WASAPI_SINK (asink);
|
GstWasapiSink *self = GST_WASAPI_SINK (asink);
|
||||||
gboolean res = FALSE;
|
gboolean res = FALSE;
|
||||||
HRESULT hr;
|
|
||||||
REFERENCE_TIME latency_rt;
|
REFERENCE_TIME latency_rt;
|
||||||
IAudioRenderClient *render_client = NULL;
|
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,
|
hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL);
|
||||||
spec->buffer_time * 10, 0, self->mix_format, NULL);
|
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
||||||
("IAudioClient::Initialize () failed: %s",
|
("IAudioClient::Initialize () failed: %s",
|
||||||
|
@ -360,6 +376,26 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
|
||||||
goto beach;
|
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 */
|
/* Get latency for logging */
|
||||||
hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
|
hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
|
@ -376,17 +412,6 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
|
||||||
goto beach;
|
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 */
|
/* Get render sink client and start it up */
|
||||||
if (!gst_wasapi_util_get_render_client (GST_ELEMENT (self), self->client,
|
if (!gst_wasapi_util_get_render_client (GST_ELEMENT (self), self->client,
|
||||||
&render_client)) {
|
&render_client)) {
|
||||||
|
|
|
@ -346,11 +346,27 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
|
||||||
guint64 client_clock_freq = 0;
|
guint64 client_clock_freq = 0;
|
||||||
IAudioCaptureClient *capture_client = NULL;
|
IAudioCaptureClient *capture_client = NULL;
|
||||||
REFERENCE_TIME latency_rt;
|
REFERENCE_TIME latency_rt;
|
||||||
|
gint64 default_period, min_period;
|
||||||
|
guint bpf, rate, buffer_frames;
|
||||||
HRESULT hr;
|
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,
|
hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED,
|
||||||
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, spec->buffer_time * 10, 0,
|
AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL);
|
||||||
self->mix_format, NULL);
|
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
|
||||||
("IAudioClient::Initialize failed: %s",
|
("IAudioClient::Initialize failed: %s",
|
||||||
|
@ -358,7 +374,26 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
|
||||||
goto beach;
|
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);
|
hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
|
||||||
if (hr != S_OK) {
|
if (hr != S_OK) {
|
||||||
GST_ERROR_OBJECT (self, "IAudioClient::GetStreamLatency failed");
|
GST_ERROR_OBJECT (self, "IAudioClient::GetStreamLatency failed");
|
||||||
|
@ -386,17 +421,6 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
|
||||||
goto beach;
|
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 */
|
/* Get capture source client and start it up */
|
||||||
if (!gst_wasapi_util_get_capture_client (GST_ELEMENT (self), self->client,
|
if (!gst_wasapi_util_get_capture_client (GST_ELEMENT (self), self->client,
|
||||||
&capture_client)) {
|
&capture_client)) {
|
||||||
|
|
Loading…
Reference in a new issue