gstreamer/sys/wasapi/gstwasapisink.c
Ole André Vadla Ravnås 69fad589ac 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 ().
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.
2008-09-30 11:19:10 +00:00

267 lines
7.2 KiB
C

/*
* 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;
}