wasapi: Add a property for trying the AudioClient3 API

The AudioClient3 API is only available on Windows 10, and we will
automatically detect when it is available and use it.

However, using it for capturing audio with low latency and without
glitches seems to require setting the realtime priority of the entire
pipeline to "critical", which we cannot do from inside the element.

Hence, we can only enable that by default for wasapisink since
apps should be able to safely set the low-latency property to TRUE if
they need low-latency capture or playback.
This commit is contained in:
Nirbheek Chauhan 2018-02-26 15:55:19 +05:30
parent f7d0ce2477
commit 995059dc87
4 changed files with 64 additions and 8 deletions

View file

@ -54,6 +54,7 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
#define DEFAULT_MUTE FALSE #define DEFAULT_MUTE FALSE
#define DEFAULT_EXCLUSIVE FALSE #define DEFAULT_EXCLUSIVE FALSE
#define DEFAULT_LOW_LATENCY FALSE #define DEFAULT_LOW_LATENCY FALSE
#define DEFAULT_AUDIOCLIENT3 TRUE
enum enum
{ {
@ -62,7 +63,8 @@ enum
PROP_MUTE, PROP_MUTE,
PROP_DEVICE, PROP_DEVICE,
PROP_EXCLUSIVE, PROP_EXCLUSIVE,
PROP_LOW_LATENCY PROP_LOW_LATENCY,
PROP_AUDIOCLIENT3
}; };
static void gst_wasapi_sink_dispose (GObject * object); static void gst_wasapi_sink_dispose (GObject * object);
@ -132,6 +134,12 @@ gst_wasapi_sink_class_init (GstWasapiSinkClass * klass)
"Optimize all settings for lowest latency", "Optimize all settings for lowest latency",
DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_AUDIOCLIENT3,
g_param_spec_boolean ("use-audioclient3", "Use the AudioClient3 API",
"Use the Windows 10 AudioClient3 API when available",
DEFAULT_AUDIOCLIENT3, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class, &sink_template); gst_element_class_add_static_pad_template (gstelement_class, &sink_template);
gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc", gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc",
"Sink/Audio", "Sink/Audio",
@ -155,6 +163,11 @@ gst_wasapi_sink_class_init (GstWasapiSinkClass * klass)
static void static void
gst_wasapi_sink_init (GstWasapiSink * self) gst_wasapi_sink_init (GstWasapiSink * self)
{ {
self->role = DEFAULT_ROLE;
self->mute = DEFAULT_MUTE;
self->sharemode = AUDCLNT_SHAREMODE_SHARED;
self->low_latency = DEFAULT_LOW_LATENCY;
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL); self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
CoInitialize (NULL); CoInitialize (NULL);
@ -232,6 +245,9 @@ gst_wasapi_sink_set_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY: case PROP_LOW_LATENCY:
self->low_latency = g_value_get_boolean (value); self->low_latency = g_value_get_boolean (value);
break; break;
case PROP_AUDIOCLIENT3:
self->try_audioclient3 = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -262,12 +278,24 @@ gst_wasapi_sink_get_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY: case PROP_LOW_LATENCY:
g_value_set_boolean (value, self->low_latency); g_value_set_boolean (value, self->low_latency);
break; break;
case PROP_AUDIOCLIENT3:
g_value_set_boolean (value, self->try_audioclient3);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static gboolean
gst_wasapi_sink_can_audioclient3 (GstWasapiSink * self)
{
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
self->try_audioclient3 && gst_wasapi_util_have_audioclient3 ())
return TRUE;
return FALSE;
}
static GstCaps * static GstCaps *
gst_wasapi_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) gst_wasapi_sink_get_caps (GstBaseSink * bsink, GstCaps * filter)
{ {
@ -416,8 +444,7 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec)
guint bpf, rate, devicep_frames; guint bpf, rate, devicep_frames;
HRESULT hr; HRESULT hr;
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED && if (gst_wasapi_sink_can_audioclient3 (self)) {
gst_wasapi_util_have_audioclient3 ()) {
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec, if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
(IAudioClient3 *) self->client, self->mix_format, self->low_latency, (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
&devicep_frames)) &devicep_frames))
@ -521,7 +548,7 @@ gst_wasapi_sink_unprepare (GstAudioSink * asink)
GstWasapiSink *self = GST_WASAPI_SINK (asink); GstWasapiSink *self = GST_WASAPI_SINK (asink);
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE && if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
!gst_wasapi_util_have_audioclient3 ()) !gst_wasapi_sink_can_audioclient3 (self))
CoUninitialize (); CoUninitialize ();
if (self->thread_priority_handle != NULL) { if (self->thread_priority_handle != NULL) {

View file

@ -62,6 +62,7 @@ struct _GstWasapiSink
gint sharemode; gint sharemode;
gboolean mute; gboolean mute;
gboolean low_latency; gboolean low_latency;
gboolean try_audioclient3;
wchar_t *device_strid; wchar_t *device_strid;
}; };

View file

@ -51,6 +51,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE #define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE
#define DEFAULT_EXCLUSIVE FALSE #define DEFAULT_EXCLUSIVE FALSE
#define DEFAULT_LOW_LATENCY FALSE #define DEFAULT_LOW_LATENCY FALSE
#define DEFAULT_AUDIOCLIENT3 FALSE
enum enum
{ {
@ -58,7 +59,8 @@ enum
PROP_ROLE, PROP_ROLE,
PROP_DEVICE, PROP_DEVICE,
PROP_EXCLUSIVE, PROP_EXCLUSIVE,
PROP_LOW_LATENCY PROP_LOW_LATENCY,
PROP_AUDIOCLIENT3
}; };
static void gst_wasapi_src_dispose (GObject * object); static void gst_wasapi_src_dispose (GObject * object);
@ -124,6 +126,12 @@ gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
"Optimize all settings for lowest latency", "Optimize all settings for lowest latency",
DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class,
PROP_AUDIOCLIENT3,
g_param_spec_boolean ("use-audioclient3", "Use the AudioClient3 API",
"Whether to use the Windows 10 AudioClient3 API when available",
DEFAULT_AUDIOCLIENT3, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (gstelement_class, &src_template); gst_element_class_add_static_pad_template (gstelement_class, &src_template);
gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc", gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc",
"Source/Audio", "Source/Audio",
@ -155,6 +163,10 @@ gst_wasapi_src_init (GstWasapiSrc * self)
gst_wasapi_src_get_time, gst_object_ref (self), gst_wasapi_src_get_time, gst_object_ref (self),
(GDestroyNotify) gst_object_unref); (GDestroyNotify) gst_object_unref);
self->role = DEFAULT_ROLE;
self->sharemode = AUDCLNT_SHAREMODE_SHARED;
self->low_latency = DEFAULT_LOW_LATENCY;
self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL); self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
CoInitialize (NULL); CoInitialize (NULL);
@ -229,6 +241,9 @@ gst_wasapi_src_set_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY: case PROP_LOW_LATENCY:
self->low_latency = g_value_get_boolean (value); self->low_latency = g_value_get_boolean (value);
break; break;
case PROP_AUDIOCLIENT3:
self->try_audioclient3 = g_value_get_boolean (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -256,12 +271,24 @@ gst_wasapi_src_get_property (GObject * object, guint prop_id,
case PROP_LOW_LATENCY: case PROP_LOW_LATENCY:
g_value_set_boolean (value, self->low_latency); g_value_set_boolean (value, self->low_latency);
break; break;
case PROP_AUDIOCLIENT3:
g_value_set_boolean (value, self->try_audioclient3);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
} }
} }
static gboolean
gst_wasapi_src_can_audioclient3 (GstWasapiSrc * self)
{
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
self->try_audioclient3 && gst_wasapi_util_have_audioclient3 ())
return TRUE;
return FALSE;
}
static GstCaps * static GstCaps *
gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
{ {
@ -384,8 +411,7 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
guint bpf, rate, devicep_frames, buffer_frames; guint bpf, rate, devicep_frames, buffer_frames;
HRESULT hr; HRESULT hr;
if (self->sharemode == AUDCLNT_SHAREMODE_SHARED && if (gst_wasapi_src_can_audioclient3 (self)) {
gst_wasapi_util_have_audioclient3 ()) {
if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec, if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
(IAudioClient3 *) self->client, self->mix_format, self->low_latency, (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
&devicep_frames)) &devicep_frames))
@ -466,7 +492,8 @@ gst_wasapi_src_unprepare (GstAudioSrc * asrc)
{ {
GstWasapiSrc *self = GST_WASAPI_SRC (asrc); GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE &&
!gst_wasapi_src_can_audioclient3 (self))
CoUninitialize (); CoUninitialize ();
if (self->thread_priority_handle != NULL) { if (self->thread_priority_handle != NULL) {

View file

@ -63,6 +63,7 @@ struct _GstWasapiSrc
gint role; gint role;
gint sharemode; gint sharemode;
gboolean low_latency; gboolean low_latency;
gboolean try_audioclient3;
wchar_t *device_strid; wchar_t *device_strid;
}; };