summaryrefslogtreecommitdiff
path: root/sys/wasapi
diff options
context:
space:
mode:
authorOle André Vadla Ravnås <ole.andre.ravnas@tandberg.com>2008-09-30 11:19:10 +0000
committerOle André Vadla Ravnås <ole.andre.ravnas@tandberg.com>2008-09-30 11:19:10 +0000
commit69fad589acd5653e95a5cee8986548861b7eb1b3 (patch)
tree618539dca60e87537ebf0b55f9a067c9d587f9c3 /sys/wasapi
parent34a993ef50b0d24e58b36ec3dbc416544c8f64a2 (diff)
downloadgstreamer-plugins-bad-69fad589acd5653e95a5cee8986548861b7eb1b3.tar.gz
sys/: New plugin for audio capture and playback using Windows Audio Session
Original commit message from CVS: * sys/Makefile.am: * sys/wasapi/Makefile.am: * sys/wasapi/gstwasapi.c: * sys/wasapi/gstwasapisink.c: * sys/wasapi/gstwasapisink.h: * sys/wasapi/gstwasapisrc.c: * sys/wasapi/gstwasapisrc.h: * sys/wasapi/gstwasapiutil.c: * sys/wasapi/gstwasapiutil.h: New plugin for audio capture and playback using Windows Audio Session API (WASAPI) available with Vista and newer (#520901). Comes with hardcoded caps and obviously needs lots of love. Haven't had time to work on this code since it was written, was initially just a quick experiment to play around with this new API.
Diffstat (limited to 'sys/wasapi')
-rw-r--r--sys/wasapi/Makefile.am5
-rw-r--r--sys/wasapi/gstwasapi.c45
-rw-r--r--sys/wasapi/gstwasapisink.c267
-rw-r--r--sys/wasapi/gstwasapisink.h67
-rw-r--r--sys/wasapi/gstwasapisrc.c443
-rw-r--r--sys/wasapi/gstwasapisrc.h74
-rw-r--r--sys/wasapi/gstwasapiutil.c261
-rw-r--r--sys/wasapi/gstwasapiutil.h41
8 files changed, 1203 insertions, 0 deletions
diff --git a/sys/wasapi/Makefile.am b/sys/wasapi/Makefile.am
new file mode 100644
index 000000000..905817835
--- /dev/null
+++ b/sys/wasapi/Makefile.am
@@ -0,0 +1,5 @@
+EXTRA_DIST = \
+ gstwasapi.c \
+ gstwasapisrc.c gstwasapisrc.h \
+ gstwasapisink.c gstwasapisink.h \
+ gstwasapiutil.c gstwasapiutil.h
diff --git a/sys/wasapi/gstwasapi.c b/sys/wasapi/gstwasapi.c
new file mode 100644
index 000000000..9fef21fe0
--- /dev/null
+++ b/sys/wasapi/gstwasapi.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstwasapisrc.h"
+#include "gstwasapisink.h"
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ gboolean ret;
+
+ ret = gst_element_register (plugin, "wasapisrc",
+ GST_RANK_NONE, GST_TYPE_WASAPI_SRC);
+ if (!ret)
+ return ret;
+
+ return gst_element_register (plugin, "wasapisink",
+ GST_RANK_NONE, GST_TYPE_WASAPI_SINK);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "wasapi",
+ "Windows audio session API plugin",
+ plugin_init, VERSION, "LGPL", "GStreamer", "http://gstreamer.net/")
diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c
new file mode 100644
index 000000000..f26e56d6d
--- /dev/null
+++ b/sys/wasapi/gstwasapisink.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-wasapisink
+ *
+ * Provides audio playback using the Windows Audio Session API available with
+ * Vista and newer.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch-0.10 -v audiotestsrc samplesperbuffer=160 ! wasapisink
+ * ]| Generate 20 ms buffers and render to the default audio device.
+ * </refsect2>
+ */
+
+#include "gstwasapisink.h"
+
+GST_DEBUG_CATEGORY_STATIC (gst_wasapi_sink_debug);
+#define GST_CAT_DEFAULT gst_wasapi_sink_debug
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-int, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) 8000, "
+ "channels = (int) 1, "
+ "signed = (boolean) TRUE, "
+ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER)));
+
+static void gst_wasapi_sink_dispose (GObject * object);
+static void gst_wasapi_sink_finalize (GObject * object);
+
+static void gst_wasapi_sink_get_times (GstBaseSink * sink, GstBuffer * buffer,
+ GstClockTime * start, GstClockTime * end);
+static gboolean gst_wasapi_sink_start (GstBaseSink * sink);
+static gboolean gst_wasapi_sink_stop (GstBaseSink * sink);
+static GstFlowReturn gst_wasapi_sink_render (GstBaseSink * sink,
+ GstBuffer * buffer);
+
+GST_BOILERPLATE (GstWasapiSink, gst_wasapi_sink, GstBaseSink,
+ GST_TYPE_BASE_SINK);
+
+static void
+gst_wasapi_sink_base_init (gpointer gclass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+ static GstElementDetails element_details = {
+ "WasapiSrc",
+ "Sink/Audio",
+ "Stream audio to an audio capture device through WASAPI",
+ "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>"
+ };
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_template));
+ gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+gst_wasapi_sink_class_init (GstWasapiSinkClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstBaseSinkClass *gstbasesink_class = GST_BASE_SINK_CLASS (klass);
+
+ gobject_class->dispose = gst_wasapi_sink_dispose;
+ gobject_class->finalize = gst_wasapi_sink_finalize;
+
+ gstbasesink_class->get_times = gst_wasapi_sink_get_times;
+ gstbasesink_class->start = gst_wasapi_sink_start;
+ gstbasesink_class->stop = gst_wasapi_sink_stop;
+ gstbasesink_class->render = gst_wasapi_sink_render;
+
+ GST_DEBUG_CATEGORY_INIT (gst_wasapi_sink_debug, "wasapisink",
+ 0, "Windows audio session API sink");
+}
+
+static void
+gst_wasapi_sink_init (GstWasapiSink * self, GstWasapiSinkClass * gclass)
+{
+ self->rate = 8000;
+ self->buffer_time = 20 * GST_MSECOND;
+ self->period_time = 20 * GST_MSECOND;
+ self->latency = GST_CLOCK_TIME_NONE;
+
+ self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+ CoInitialize (NULL);
+}
+
+static void
+gst_wasapi_sink_dispose (GObject * object)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (object);
+
+ if (self->event_handle != NULL) {
+ CloseHandle (self->event_handle);
+ self->event_handle = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_wasapi_sink_finalize (GObject * object)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (object);
+
+ CoUninitialize ();
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_wasapi_sink_get_times (GstBaseSink * sink,
+ GstBuffer * buffer, GstClockTime * start, GstClockTime * end)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (sink);
+
+ if (GST_BUFFER_TIMESTAMP_IS_VALID (buffer)) {
+ *start = GST_BUFFER_TIMESTAMP (buffer);
+
+ if (GST_BUFFER_DURATION_IS_VALID (buffer)) {
+ *end = *start + GST_BUFFER_DURATION (buffer);
+ } else {
+ *end = *start + self->buffer_time;
+ }
+
+ *start += self->latency;
+ *end += self->latency;
+ }
+}
+
+static gboolean
+gst_wasapi_sink_start (GstBaseSink * sink)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (sink);
+ gboolean res = FALSE;
+ IAudioClient *client = NULL;
+ HRESULT hr;
+ IAudioRenderClient *render_client = NULL;
+
+ if (!gst_wasapi_util_get_default_device_client (GST_ELEMENT (self),
+ FALSE, self->rate, self->buffer_time, self->period_time,
+ AUDCLNT_STREAMFLAGS_EVENTCALLBACK, &client, &self->latency))
+ goto beach;
+
+ hr = IAudioClient_SetEventHandle (client, self->event_handle);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::SetEventHandle () failed");
+ goto beach;
+ }
+
+ hr = IAudioClient_GetService (client, &IID_IAudioRenderClient,
+ &render_client);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::GetService "
+ "(IID_IAudioRenderClient) failed");
+ goto beach;
+ }
+
+ hr = IAudioClient_Start (client);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::Start failed");
+ goto beach;
+ }
+
+ self->client = client;
+ self->render_client = render_client;
+
+ res = TRUE;
+
+beach:
+ if (!res) {
+ if (render_client != NULL)
+ IUnknown_Release (render_client);
+
+ if (client != NULL)
+ IUnknown_Release (client);
+ }
+
+ return res;
+}
+
+static gboolean
+gst_wasapi_sink_stop (GstBaseSink * sink)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (sink);
+
+ if (self->client != NULL) {
+ IAudioClient_Stop (self->client);
+ }
+
+ if (self->render_client != NULL) {
+ IUnknown_Release (self->render_client);
+ self->render_client = NULL;
+ }
+
+ if (self->client != NULL) {
+ IUnknown_Release (self->client);
+ self->client = NULL;
+ }
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_wasapi_sink_render (GstBaseSink * sink, GstBuffer * buffer)
+{
+ GstWasapiSink *self = GST_WASAPI_SINK (sink);
+ GstFlowReturn ret = GST_FLOW_OK;
+ HRESULT hr;
+ gint16 *src = (gint16 *) GST_BUFFER_DATA (buffer);
+ gint16 *dst = NULL;
+ guint nsamples = GST_BUFFER_SIZE (buffer) / sizeof (gint16);
+ guint i;
+
+ WaitForSingleObject (self->event_handle, INFINITE);
+
+ hr = IAudioRenderClient_GetBuffer (self->render_client, nsamples,
+ (BYTE **) & dst);
+ if (hr != S_OK) {
+ GST_ELEMENT_ERROR (self, RESOURCE, WRITE, (NULL),
+ ("IAudioRenderClient::GetBuffer () failed: %s",
+ gst_wasapi_util_hresult_to_string (hr)));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ for (i = 0; i < nsamples; i++) {
+ dst[0] = *src;
+ dst[1] = *src;
+
+ src++;
+ dst += 2;
+ }
+
+ hr = IAudioRenderClient_ReleaseBuffer (self->render_client, nsamples, 0);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioRenderClient::ReleaseBuffer () failed: %s",
+ gst_wasapi_util_hresult_to_string (hr));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+beach:
+ return ret;
+}
diff --git a/sys/wasapi/gstwasapisink.h b/sys/wasapi/gstwasapisink.h
new file mode 100644
index 000000000..16cc22f1f
--- /dev/null
+++ b/sys/wasapi/gstwasapisink.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_WASAPI_SINK_H__
+#define __GST_WASAPI_SINK_H__
+
+#include "gstwasapiutil.h"
+
+#include <gst/base/gstbasesink.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_WASAPI_SINK \
+ (gst_wasapi_sink_get_type ())
+#define GST_WASAPI_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WASAPI_SINK, GstWasapiSink))
+#define GST_WASAPI_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WASAPI_SINK, GstWasapiSinkClass))
+#define GST_IS_WASAPI_SINK(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WASAPI_SINK))
+#define GST_IS_WASAPI_SINK_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WASAPI_SINK))
+
+typedef struct _GstWasapiSink GstWasapiSink;
+typedef struct _GstWasapiSinkClass GstWasapiSinkClass;
+
+struct _GstWasapiSink
+{
+ GstBaseSink base_sink;
+
+ guint rate;
+ GstClockTime buffer_time;
+ GstClockTime period_time;
+ GstClockTime latency;
+
+ IAudioClient * client;
+ IAudioRenderClient * render_client;
+ HANDLE event_handle;
+};
+
+struct _GstWasapiSinkClass
+{
+ GstBaseSinkClass parent_class;
+};
+
+GType gst_wasapi_sink_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_WASAPI_SINK_H__ */
+
diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c
new file mode 100644
index 000000000..0a0edd2b7
--- /dev/null
+++ b/sys/wasapi/gstwasapisrc.c
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:element-wasapisrc
+ *
+ * Provides audio capture from the Windows Audio Session API available with
+ * Vista and newer.
+ *
+ * <refsect2>
+ * <title>Example pipelines</title>
+ * |[
+ * gst-launch-0.10 -v wasapisrc ! fakesink
+ * ]| Capture from the default audio device and render to fakesink.
+ * </refsect2>
+ */
+
+#include "gstwasapisrc.h"
+#include <gst/audio/gstaudioclock.h>
+
+GST_DEBUG_CATEGORY_STATIC (gst_wasapi_src_debug);
+#define GST_CAT_DEFAULT gst_wasapi_src_debug
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw-int, "
+ "width = (int) 16, "
+ "depth = (int) 16, "
+ "rate = (int) 8000, "
+ "channels = (int) 1, "
+ "signed = (boolean) TRUE, "
+ "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER)));
+
+static void gst_wasapi_src_dispose (GObject * object);
+static void gst_wasapi_src_finalize (GObject * object);
+
+static GstClock *gst_wasapi_src_provide_clock (GstElement * element);
+
+static gboolean gst_wasapi_src_start (GstBaseSrc * src);
+static gboolean gst_wasapi_src_stop (GstBaseSrc * src);
+static gboolean gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query);
+
+static GstFlowReturn gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf);
+
+static GstClockTime gst_wasapi_src_get_time (GstClock * clock,
+ gpointer user_data);
+
+GST_BOILERPLATE (GstWasapiSrc, gst_wasapi_src, GstPushSrc, GST_TYPE_PUSH_SRC);
+
+static void
+gst_wasapi_src_base_init (gpointer gclass)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
+ static GstElementDetails element_details = {
+ "WasapiSrc",
+ "Source/Audio",
+ "Stream audio from an audio capture device through WASAPI",
+ "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>"
+ };
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_template));
+ gst_element_class_set_details (element_class, &element_details);
+}
+
+static void
+gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
+ GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
+ GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
+
+ gobject_class->dispose = gst_wasapi_src_dispose;
+ gobject_class->finalize = gst_wasapi_src_finalize;
+
+ gstelement_class->provide_clock = gst_wasapi_src_provide_clock;
+
+ gstbasesrc_class->start = gst_wasapi_src_start;
+ gstbasesrc_class->stop = gst_wasapi_src_stop;
+ gstbasesrc_class->query = gst_wasapi_src_query;
+
+ gstpushsrc_class->create = gst_wasapi_src_create;
+
+ GST_DEBUG_CATEGORY_INIT (gst_wasapi_src_debug, "wasapisrc",
+ 0, "Windows audio session API source");
+}
+
+static void
+gst_wasapi_src_init (GstWasapiSrc * self, GstWasapiSrcClass * gclass)
+{
+ GstBaseSrc *basesrc = GST_BASE_SRC (self);
+
+ gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
+ gst_base_src_set_live (basesrc, TRUE);
+
+ self->rate = 8000;
+ self->buffer_time = 20 * GST_MSECOND;
+ self->period_time = 20 * GST_MSECOND;
+ self->latency = GST_CLOCK_TIME_NONE;
+ self->samples_per_buffer = self->rate / (GST_SECOND / self->period_time);
+
+ self->start_time = GST_CLOCK_TIME_NONE;
+ self->next_time = GST_CLOCK_TIME_NONE;
+
+ self->clock = gst_audio_clock_new ("GstWasapiSrcClock",
+ gst_wasapi_src_get_time, self);
+
+ CoInitialize (NULL);
+}
+
+static void
+gst_wasapi_src_dispose (GObject * object)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (object);
+
+ if (self->clock != NULL) {
+ gst_object_unref (self->clock);
+ self->clock = NULL;
+ }
+
+ G_OBJECT_CLASS (parent_class)->dispose (object);
+}
+
+static void
+gst_wasapi_src_finalize (GObject * object)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (object);
+
+ CoUninitialize ();
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static GstClock *
+gst_wasapi_src_provide_clock (GstElement * element)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (element);
+ GstClock *clock;
+
+ GST_OBJECT_LOCK (self);
+
+ if (self->client_clock == NULL)
+ goto wrong_state;
+
+ clock = GST_CLOCK (gst_object_ref (self->clock));
+
+ GST_OBJECT_UNLOCK (self);
+ return clock;
+
+ /* ERRORS */
+wrong_state:
+ {
+ GST_OBJECT_UNLOCK (self);
+ GST_DEBUG_OBJECT (self, "IAudioClock not acquired");
+ return NULL;
+ }
+}
+
+static gboolean
+gst_wasapi_src_start (GstBaseSrc * src)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (src);
+ gboolean res = FALSE;
+ IAudioClient *client = NULL;
+ IAudioClock *client_clock = NULL;
+ guint64 client_clock_freq = 0;
+ IAudioCaptureClient *capture_client = NULL;
+ HRESULT hr;
+
+ if (!gst_wasapi_util_get_default_device_client (GST_ELEMENT (self),
+ TRUE, self->rate, self->buffer_time, self->period_time, 0, &client,
+ &self->latency))
+ goto beach;
+
+ hr = IAudioClient_GetService (client, &IID_IAudioClock, &client_clock);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::GetService (IID_IAudioClock) "
+ "failed");
+ goto beach;
+ }
+
+ hr = IAudioClock_GetFrequency (client_clock, &client_clock_freq);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClock::GetFrequency () failed");
+ goto beach;
+ }
+
+ hr = IAudioClient_GetService (client, &IID_IAudioCaptureClient,
+ &capture_client);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::GetService "
+ "(IID_IAudioCaptureClient) failed");
+ goto beach;
+ }
+
+ hr = IAudioClient_Start (client);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioClient::Start failed");
+ goto beach;
+ }
+
+ self->client = client;
+ self->client_clock = client_clock;
+ self->client_clock_freq = client_clock_freq;
+ self->capture_client = capture_client;
+
+ res = TRUE;
+
+beach:
+ if (!res) {
+ if (capture_client != NULL)
+ IUnknown_Release (capture_client);
+
+ if (client_clock != NULL)
+ IUnknown_Release (client_clock);
+
+ if (client != NULL)
+ IUnknown_Release (client);
+ }
+
+ return res;
+}
+
+static gboolean
+gst_wasapi_src_stop (GstBaseSrc * src)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (src);
+
+ if (self->client != NULL) {
+ IAudioClient_Stop (self->client);
+ }
+
+ if (self->capture_client != NULL) {
+ IUnknown_Release (self->capture_client);
+ self->capture_client = NULL;
+ }
+
+ if (self->client_clock != NULL) {
+ IUnknown_Release (self->client_clock);
+ self->client_clock = NULL;
+ }
+
+ if (self->client != NULL) {
+ IUnknown_Release (self->client);
+ self->client = NULL;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (src);
+ gboolean ret = FALSE;
+
+ GST_DEBUG_OBJECT (self, "query for %s",
+ gst_query_type_get_name (GST_QUERY_TYPE (query)));
+
+ switch (GST_QUERY_TYPE (query)) {
+ case GST_QUERY_LATENCY:{
+ GstClockTime min_latency, max_latency;
+
+ min_latency = self->latency + self->period_time;
+ max_latency = min_latency;
+
+ GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
+ " max %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
+
+ gst_query_set_latency (query, TRUE, min_latency, max_latency);
+ ret = TRUE;
+ break;
+ }
+
+ default:
+ ret = GST_BASE_SRC_CLASS (parent_class)->query (src, query);
+ break;
+ }
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (src);
+ GstFlowReturn ret = GST_FLOW_OK;
+ GstClock *clock;
+ GstClockTime timestamp, duration = self->period_time;
+ HRESULT hr;
+ gint16 *samples = NULL;
+ guint32 nsamples_read = 0, nsamples;
+ DWORD flags = 0;
+ guint64 devpos;
+
+ GST_OBJECT_LOCK (self);
+ clock = GST_ELEMENT_CLOCK (self);
+ if (clock != NULL)
+ gst_object_ref (clock);
+ GST_OBJECT_UNLOCK (self);
+
+ if (clock != NULL && GST_CLOCK_TIME_IS_VALID (self->next_time)) {
+ GstClockID id;
+
+ id = gst_clock_new_single_shot_id (clock, self->next_time);
+ gst_clock_id_wait (id, NULL);
+ gst_clock_id_unref (id);
+ }
+
+ do {
+ hr = IAudioCaptureClient_GetBuffer (self->capture_client,
+ (BYTE **) & samples, &nsamples_read, &flags, &devpos, NULL);
+ }
+ while (hr == AUDCLNT_S_BUFFER_EMPTY);
+
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioCaptureClient::GetBuffer () failed: %s",
+ gst_wasapi_util_hresult_to_string (hr));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+ if (flags != 0) {
+ GST_WARNING_OBJECT (self, "devpos %" G_GUINT64_FORMAT ": flags=0x%08x",
+ devpos, flags);
+ }
+
+ /* FIXME: Why do we get 1024 sometimes and not a multiple of
+ * samples_per_buffer? Shouldn't WASAPI provide a DISCONT
+ * flag if we read too slow?
+ */
+ nsamples = nsamples_read;
+ g_assert (nsamples >= self->samples_per_buffer);
+ if (nsamples > self->samples_per_buffer) {
+ GST_WARNING_OBJECT (self,
+ "devpos %" G_GUINT64_FORMAT ": got %d samples, expected %d, clipping!",
+ devpos, nsamples, self->samples_per_buffer);
+
+ nsamples = self->samples_per_buffer;
+ }
+
+ if (clock == NULL || clock == self->clock) {
+ timestamp =
+ gst_util_uint64_scale (devpos, GST_SECOND, self->client_clock_freq);
+ } else {
+ GstClockTime base_time;
+
+ timestamp = gst_clock_get_time (clock);
+
+ base_time = GST_ELEMENT_CAST (self)->base_time;
+ if (timestamp > base_time)
+ timestamp -= base_time;
+ else
+ timestamp = 0;
+
+ if (timestamp > duration)
+ timestamp -= duration;
+ else
+ timestamp = 0;
+ }
+
+ ret = gst_pad_alloc_buffer_and_set_caps (GST_BASE_SRC_PAD (self),
+ devpos,
+ nsamples * sizeof (gint16), GST_PAD_CAPS (GST_BASE_SRC_PAD (self)), buf);
+
+ if (ret == GST_FLOW_OK) {
+ guint i;
+ gint16 *dst;
+
+ GST_BUFFER_OFFSET_END (*buf) = devpos + self->samples_per_buffer;
+ GST_BUFFER_TIMESTAMP (*buf) = timestamp;
+ GST_BUFFER_DURATION (*buf) = duration;
+
+ dst = (gint16 *) GST_BUFFER_DATA (*buf);
+ for (i = 0; i < nsamples; i++) {
+ *dst = *samples;
+
+ samples += 2;
+ dst++;
+ }
+ }
+
+ hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, nsamples_read);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (self, "IAudioCaptureClient::ReleaseBuffer () failed: %s",
+ gst_wasapi_util_hresult_to_string (hr));
+ ret = GST_FLOW_ERROR;
+ goto beach;
+ }
+
+beach:
+ if (clock != NULL)
+ gst_object_unref (clock);
+
+ return ret;
+}
+
+static GstClockTime
+gst_wasapi_src_get_time (GstClock * clock, gpointer user_data)
+{
+ GstWasapiSrc *self = GST_WASAPI_SRC (user_data);
+ HRESULT hr;
+ guint64 devpos;
+ GstClockTime result;
+
+ if (G_UNLIKELY (self->client_clock == NULL))
+ return GST_CLOCK_TIME_NONE;
+
+ hr = IAudioClock_GetPosition (self->client_clock, &devpos, NULL);
+ if (G_UNLIKELY (hr != S_OK))
+ return GST_CLOCK_TIME_NONE;
+
+ result = gst_util_uint64_scale_int (devpos, GST_SECOND,
+ self->client_clock_freq);
+
+ /*
+ GST_DEBUG_OBJECT (self, "devpos = %" G_GUINT64_FORMAT
+ " frequency = %" G_GUINT64_FORMAT
+ " result = %" G_GUINT64_FORMAT " ms",
+ devpos, self->client_clock_freq, GST_TIME_AS_MSECONDS (result));
+ */
+
+ return result;
+}
diff --git a/sys/wasapi/gstwasapisrc.h b/sys/wasapi/gstwasapisrc.h
new file mode 100644
index 000000000..26d14fbe8
--- /dev/null
+++ b/sys/wasapi/gstwasapisrc.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_WASAPI_SRC_H__
+#define __GST_WASAPI_SRC_H__
+
+#include "gstwasapiutil.h"
+
+#include <gst/base/gstpushsrc.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_WASAPI_SRC \
+ (gst_wasapi_src_get_type ())
+#define GST_WASAPI_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_WASAPI_SRC, GstWasapiSrc))
+#define GST_WASAPI_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_WASAPI_SRC, GstWasapiSrcClass))
+#define GST_IS_WASAPI_SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_WASAPI_SRC))
+#define GST_IS_WASAPI_SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_WASAPI_SRC))
+
+typedef struct _GstWasapiSrc GstWasapiSrc;
+typedef struct _GstWasapiSrcClass GstWasapiSrcClass;
+
+struct _GstWasapiSrc
+{
+ GstPushSrc audio_src;
+
+ GstClock * clock;
+
+ guint rate;
+ GstClockTime buffer_time;
+ GstClockTime period_time;
+ GstClockTime latency;
+ guint samples_per_buffer;
+
+ IAudioClient * client;
+ IAudioClock * client_clock;
+ guint64 client_clock_freq;
+ IAudioCaptureClient * capture_client;
+
+ GstClockTime start_time;
+ GstClockTime next_time;
+};
+
+struct _GstWasapiSrcClass
+{
+ GstPushSrcClass parent_class;
+};
+
+GType gst_wasapi_src_get_type (void);
+
+G_END_DECLS
+
+#endif /* __GST_WASAPI_SRC_H__ */
+
diff --git a/sys/wasapi/gstwasapiutil.c b/sys/wasapi/gstwasapiutil.c
new file mode 100644
index 000000000..79c924959
--- /dev/null
+++ b/sys/wasapi/gstwasapiutil.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "gstwasapiutil.h"
+
+#include <mmdeviceapi.h>
+
+/* These seem to be missing in the Windows SDK... */
+const CLSID CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,
+ {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e}
+};
+const IID IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,
+ {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6}
+};
+const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
+ {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
+};
+const IID IID_IAudioClock = { 0xcd63314f, 0x3fba, 0x4a1b,
+ {0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7}
+};
+const IID IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,
+ {0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17}
+};
+const IID IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,
+ {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2}
+};
+
+const gchar *
+gst_wasapi_util_hresult_to_string (HRESULT hr)
+{
+ const gchar *s = "AUDCLNT_E_UNKNOWN";
+
+ switch (hr) {
+ case AUDCLNT_E_NOT_INITIALIZED:
+ s = "AUDCLNT_E_NOT_INITIALIZED";
+ break;
+ case AUDCLNT_E_ALREADY_INITIALIZED:
+ s = "AUDCLNT_E_ALREADY_INITIALIZED";
+ break;
+ case AUDCLNT_E_WRONG_ENDPOINT_TYPE:
+ s = "AUDCLNT_E_WRONG_ENDPOINT_TYPE";
+ break;
+ case AUDCLNT_E_DEVICE_INVALIDATED:
+ s = "AUDCLNT_E_DEVICE_INVALIDATED";
+ break;
+ case AUDCLNT_E_NOT_STOPPED:
+ s = "AUDCLNT_E_NOT_STOPPED";
+ break;
+ case AUDCLNT_E_BUFFER_TOO_LARGE:
+ s = "AUDCLNT_E_BUFFER_TOO_LARGE";
+ break;
+ case AUDCLNT_E_OUT_OF_ORDER:
+ s = "AUDCLNT_E_OUT_OF_ORDER";
+ break;
+ case AUDCLNT_E_UNSUPPORTED_FORMAT:
+ s = "AUDCLNT_E_UNSUPPORTED_FORMAT";
+ break;
+ case AUDCLNT_E_INVALID_SIZE:
+ s = "AUDCLNT_E_INVALID_SIZE";
+ break;
+ case AUDCLNT_E_DEVICE_IN_USE:
+ s = "AUDCLNT_E_DEVICE_IN_USE";
+ break;
+ case AUDCLNT_E_BUFFER_OPERATION_PENDING:
+ s = "AUDCLNT_E_BUFFER_OPERATION_PENDING";
+ break;
+ case AUDCLNT_E_THREAD_NOT_REGISTERED:
+ s = "AUDCLNT_E_THREAD_NOT_REGISTERED";
+ break;
+ case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:
+ s = "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED";
+ break;
+ case AUDCLNT_E_ENDPOINT_CREATE_FAILED:
+ s = "AUDCLNT_E_ENDPOINT_CREATE_FAILED";
+ break;
+ case AUDCLNT_E_SERVICE_NOT_RUNNING:
+ s = "AUDCLNT_E_SERVICE_NOT_RUNNING";
+ break;
+ case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED:
+ s = "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED";
+ break;
+ case AUDCLNT_E_EXCLUSIVE_MODE_ONLY:
+ s = "AUDCLNT_E_EXCLUSIVE_MODE_ONLY";
+ break;
+ case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL:
+ s = "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL";
+ break;
+ case AUDCLNT_E_EVENTHANDLE_NOT_SET:
+ s = "AUDCLNT_E_EVENTHANDLE_NOT_SET";
+ break;
+ case AUDCLNT_E_INCORRECT_BUFFER_SIZE:
+ s = "AUDCLNT_E_INCORRECT_BUFFER_SIZE";
+ break;
+ case AUDCLNT_E_BUFFER_SIZE_ERROR:
+ s = "AUDCLNT_E_BUFFER_SIZE_ERROR";
+ break;
+ case AUDCLNT_E_CPUUSAGE_EXCEEDED:
+ s = "AUDCLNT_E_CPUUSAGE_EXCEEDED";
+ break;
+ case AUDCLNT_S_BUFFER_EMPTY:
+ s = "AUDCLNT_S_BUFFER_EMPTY";
+ break;
+ case AUDCLNT_S_THREAD_ALREADY_REGISTERED:
+ s = "AUDCLNT_S_THREAD_ALREADY_REGISTERED";
+ break;
+ case AUDCLNT_S_POSITION_STALLED:
+ s = "AUDCLNT_S_POSITION_STALLED";
+ break;
+ }
+
+ return s;
+}
+
+gboolean
+gst_wasapi_util_get_default_device_client (GstElement * element,
+ gboolean capture,
+ guint rate,
+ GstClockTime buffer_time,
+ GstClockTime period_time,
+ DWORD flags, IAudioClient ** ret_client, GstClockTime * ret_latency)
+{
+ gboolean res = FALSE;
+ HRESULT hr;
+ IMMDeviceEnumerator *enumerator = NULL;
+ IMMDevice *device = NULL;
+ IAudioClient *client = NULL;
+ REFERENCE_TIME latency_rt, def_period, min_period;
+ WAVEFORMATEXTENSIBLE format;
+
+ hr = CoCreateInstance (&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
+ &IID_IMMDeviceEnumerator, &enumerator);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (element, "CoCreateInstance (MMDeviceEnumerator) failed");
+ goto beach;
+ }
+
+ hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
+ (capture) ? eCapture : eRender, eCommunications, &device);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (element,
+ "IMMDeviceEnumerator::GetDefaultAudioEndpoint () failed");
+ goto beach;
+ }
+
+ hr = IMMDevice_Activate (device, &IID_IAudioClient, CLSCTX_ALL, NULL,
+ &client);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (element, "IMMDevice::Activate (IID_IAudioClient) failed");
+ goto beach;
+ }
+
+ hr = IAudioClient_GetDevicePeriod (client, &def_period, &min_period);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (element, "IAudioClient::GetDevicePeriod () failed");
+ goto beach;
+ }
+
+ ZeroMemory (&format, sizeof (format));
+ format.Format.cbSize = sizeof (format) - sizeof (format.Format);
+ format.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ format.Format.nChannels = 2;
+ format.Format.nSamplesPerSec = rate;
+ format.Format.wBitsPerSample = 16;
+ format.Format.nBlockAlign = format.Format.nChannels
+ * (format.Format.wBitsPerSample / 8);
+ format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec
+ * format.Format.nBlockAlign;
+ format.Samples.wValidBitsPerSample = 16;
+ format.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ format.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
+
+ hr = IAudioClient_Initialize (client, AUDCLNT_SHAREMODE_EXCLUSIVE, /* or AUDCLNT_SHAREMODE_SHARED */
+ flags, buffer_time / 100, /* buffer duration in 100s of ns */
+ period_time / 100, /* periodicity in 100s of ns */
+ (WAVEFORMATEX *) & format, NULL);
+ if (hr != S_OK) {
+ GST_ELEMENT_ERROR (element, RESOURCE, OPEN_READ, (NULL),
+ ("IAudioClient::Initialize () failed: %s",
+ gst_wasapi_util_hresult_to_string (hr)));
+ goto beach;
+ }
+
+ hr = IAudioClient_GetStreamLatency (client, &latency_rt);
+ if (hr != S_OK) {
+ GST_ERROR_OBJECT (element, "IAudioClient::GetStreamLatency () failed");
+ goto beach;
+ }
+
+ GST_INFO_OBJECT (element, "default period: %d (%d ms), "
+ "minimum period: %d (%d ms), "
+ "latency: %d (%d ms)",
+ (guint32) def_period, (guint32) def_period / 10000,
+ (guint32) min_period, (guint32) min_period / 10000,
+ (guint32) latency_rt, (guint32) latency_rt / 10000);
+
+ IUnknown_AddRef (client);
+ *ret_client = client;
+
+ *ret_latency = latency_rt * 100;
+
+ res = TRUE;
+
+beach:
+ if (client != NULL)
+ IUnknown_Release (client);
+
+ if (device != NULL)
+ IUnknown_Release (device);
+
+ if (enumerator != NULL)
+ IUnknown_Release (enumerator);
+
+ return res;
+}
+
+#if 0
+static WAVEFORMATEXTENSIBLE *
+gst_wasapi_src_probe_device_format (GstWasapiSrc * self, IMMDevice * device)
+{
+ HRESULT hr;
+ IPropertyStore *props = NULL;
+ PROPVARIANT format_prop;
+ WAVEFORMATEXTENSIBLE *format = NULL;
+
+ hr = IMMDevice_OpenPropertyStore (device, STGM_READ, &props);
+ if (hr != S_OK)
+ goto beach;
+
+ PropVariantInit (&format_prop);
+ hr = IPropertyStore_GetValue (props, &PKEY_AudioEngine_DeviceFormat,
+ &format_prop);
+ if (hr != S_OK)
+ goto beach;
+
+ format = (WAVEFORMATEXTENSIBLE *) format_prop.blob.pBlobData;
+
+ /* hmm: HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\MMDevices\Audio\Capture\{64adb8b7-9716-4c02-8929-96e53f5642da}\Properties */
+
+beach:
+ if (props != NULL)
+ IUnknown_Release (props);
+
+ return format;
+}
+#endif
diff --git a/sys/wasapi/gstwasapiutil.h b/sys/wasapi/gstwasapiutil.h
new file mode 100644
index 000000000..fc060abf8
--- /dev/null
+++ b/sys/wasapi/gstwasapiutil.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_WASAPI_UTIL_H__
+#define __GST_WASAPI_UTIL_H__
+
+#include <gst/gst.h>
+
+#include <audioclient.h>
+
+const gchar *
+gst_wasapi_util_hresult_to_string (HRESULT hr);
+
+gboolean
+gst_wasapi_util_get_default_device_client (GstElement * element,
+ gboolean capture,
+ guint rate,
+ GstClockTime buffer_time,
+ GstClockTime period_time,
+ DWORD flags,
+ IAudioClient ** ret_client,
+ GstClockTime * ret_latency);
+
+#endif /* __GST_WASAPI_UTIL_H__ */
+