2 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
21 * SECTION:element-wasapisrc
23 * Provides audio capture from the Windows Audio Session API available with
27 * <title>Example pipelines</title>
29 * gst-launch-0.10 -v wasapisrc ! fakesink
30 * ]| Capture from the default audio device and render to fakesink.
37 #include "gstwasapisrc.h"
38 #include <gst/audio/gstaudioclock.h>
40 GST_DEBUG_CATEGORY_STATIC (gst_wasapi_src_debug);
41 #define GST_CAT_DEFAULT gst_wasapi_src_debug
43 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
46 GST_STATIC_CAPS ("audio/x-raw-int, "
50 "channels = (int) 1, "
51 "signed = (boolean) TRUE, "
52 "endianness = (int) " G_STRINGIFY (G_BYTE_ORDER)));
54 static void gst_wasapi_src_dispose (GObject * object);
55 static void gst_wasapi_src_finalize (GObject * object);
57 static GstClock *gst_wasapi_src_provide_clock (GstElement * element);
59 static gboolean gst_wasapi_src_start (GstBaseSrc * src);
60 static gboolean gst_wasapi_src_stop (GstBaseSrc * src);
61 static gboolean gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query);
63 static GstFlowReturn gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf);
65 static GstClockTime gst_wasapi_src_get_time (GstClock * clock,
68 GST_BOILERPLATE (GstWasapiSrc, gst_wasapi_src, GstPushSrc, GST_TYPE_PUSH_SRC);
71 gst_wasapi_src_base_init (gpointer gclass)
73 GstElementClass *element_class = GST_ELEMENT_CLASS (gclass);
75 gst_element_class_add_pad_template (element_class,
76 gst_static_pad_template_get (&src_template));
77 gst_element_class_set_static_metadata (element_class, "WasapiSrc",
79 "Stream audio from an audio capture device through WASAPI",
80 "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
84 gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
86 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
87 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
88 GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
89 GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
91 gobject_class->dispose = gst_wasapi_src_dispose;
92 gobject_class->finalize = gst_wasapi_src_finalize;
94 gstelement_class->provide_clock = gst_wasapi_src_provide_clock;
96 gstbasesrc_class->start = gst_wasapi_src_start;
97 gstbasesrc_class->stop = gst_wasapi_src_stop;
98 gstbasesrc_class->query = gst_wasapi_src_query;
100 gstpushsrc_class->create = gst_wasapi_src_create;
102 GST_DEBUG_CATEGORY_INIT (gst_wasapi_src_debug, "wasapisrc",
103 0, "Windows audio session API source");
107 gst_wasapi_src_init (GstWasapiSrc * self, GstWasapiSrcClass * gclass)
109 GstBaseSrc *basesrc = GST_BASE_SRC (self);
111 gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
112 gst_base_src_set_live (basesrc, TRUE);
115 self->buffer_time = 20 * GST_MSECOND;
116 self->period_time = 20 * GST_MSECOND;
117 self->latency = GST_CLOCK_TIME_NONE;
118 self->samples_per_buffer = self->rate / (GST_SECOND / self->period_time);
120 self->start_time = GST_CLOCK_TIME_NONE;
121 self->next_time = GST_CLOCK_TIME_NONE;
123 #if GST_CHECK_VERSION(0, 10, 31) || (GST_CHECK_VERSION(0, 10, 30) && GST_VERSION_NANO > 0)
124 self->clock = gst_audio_clock_new_full ("GstWasapiSrcClock",
125 gst_wasapi_src_get_time, gst_object_ref (self),
126 (GDestroyNotify) gst_object_unref);
128 self->clock = gst_audio_clock_new ("GstWasapiSrcClock",
129 gst_wasapi_src_get_time, self);
136 gst_wasapi_src_dispose (GObject * object)
138 GstWasapiSrc *self = GST_WASAPI_SRC (object);
140 if (self->clock != NULL) {
141 gst_object_unref (self->clock);
145 G_OBJECT_CLASS (parent_class)->dispose (object);
149 gst_wasapi_src_finalize (GObject * object)
151 GstWasapiSrc *self = GST_WASAPI_SRC (object);
155 G_OBJECT_CLASS (parent_class)->finalize (object);
159 gst_wasapi_src_provide_clock (GstElement * element)
161 GstWasapiSrc *self = GST_WASAPI_SRC (element);
164 GST_OBJECT_LOCK (self);
166 if (self->client_clock == NULL)
169 clock = GST_CLOCK (gst_object_ref (self->clock));
171 GST_OBJECT_UNLOCK (self);
177 GST_OBJECT_UNLOCK (self);
178 GST_DEBUG_OBJECT (self, "IAudioClock not acquired");
184 gst_wasapi_src_start (GstBaseSrc * src)
186 GstWasapiSrc *self = GST_WASAPI_SRC (src);
187 gboolean res = FALSE;
188 IAudioClient *client = NULL;
189 IAudioClock *client_clock = NULL;
190 guint64 client_clock_freq = 0;
191 IAudioCaptureClient *capture_client = NULL;
194 if (!gst_wasapi_util_get_default_device_client (GST_ELEMENT (self),
195 TRUE, self->rate, self->buffer_time, self->period_time, 0, &client,
199 hr = IAudioClient_GetService (client, &IID_IAudioClock, &client_clock);
201 GST_ERROR_OBJECT (self, "IAudioClient::GetService (IID_IAudioClock) "
206 hr = IAudioClock_GetFrequency (client_clock, &client_clock_freq);
208 GST_ERROR_OBJECT (self, "IAudioClock::GetFrequency () failed");
212 hr = IAudioClient_GetService (client, &IID_IAudioCaptureClient,
215 GST_ERROR_OBJECT (self, "IAudioClient::GetService "
216 "(IID_IAudioCaptureClient) failed");
220 hr = IAudioClient_Start (client);
222 GST_ERROR_OBJECT (self, "IAudioClient::Start failed");
226 self->client = client;
227 self->client_clock = client_clock;
228 self->client_clock_freq = client_clock_freq;
229 self->capture_client = capture_client;
235 if (capture_client != NULL)
236 IUnknown_Release (capture_client);
238 if (client_clock != NULL)
239 IUnknown_Release (client_clock);
242 IUnknown_Release (client);
249 gst_wasapi_src_stop (GstBaseSrc * src)
251 GstWasapiSrc *self = GST_WASAPI_SRC (src);
253 if (self->client != NULL) {
254 IAudioClient_Stop (self->client);
257 if (self->capture_client != NULL) {
258 IUnknown_Release (self->capture_client);
259 self->capture_client = NULL;
262 if (self->client_clock != NULL) {
263 IUnknown_Release (self->client_clock);
264 self->client_clock = NULL;
267 if (self->client != NULL) {
268 IUnknown_Release (self->client);
276 gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query)
278 GstWasapiSrc *self = GST_WASAPI_SRC (src);
279 gboolean ret = FALSE;
281 GST_DEBUG_OBJECT (self, "query for %s",
282 gst_query_type_get_name (GST_QUERY_TYPE (query)));
284 switch (GST_QUERY_TYPE (query)) {
285 case GST_QUERY_LATENCY:{
286 GstClockTime min_latency, max_latency;
288 min_latency = self->latency + self->period_time;
289 max_latency = min_latency;
291 GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
292 " max %" GST_TIME_FORMAT,
293 GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
295 gst_query_set_latency (query, TRUE, min_latency, max_latency);
301 ret = GST_BASE_SRC_CLASS (parent_class)->query (src, query);
309 gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf)
311 GstWasapiSrc *self = GST_WASAPI_SRC (src);
312 GstFlowReturn ret = GST_FLOW_OK;
314 GstClockTime timestamp, duration = self->period_time;
316 gint16 *samples = NULL;
317 guint32 nsamples_read = 0, nsamples;
321 GST_OBJECT_LOCK (self);
322 clock = GST_ELEMENT_CLOCK (self);
324 gst_object_ref (clock);
325 GST_OBJECT_UNLOCK (self);
327 if (clock != NULL && GST_CLOCK_TIME_IS_VALID (self->next_time)) {
330 id = gst_clock_new_single_shot_id (clock, self->next_time);
331 gst_clock_id_wait (id, NULL);
332 gst_clock_id_unref (id);
336 hr = IAudioCaptureClient_GetBuffer (self->capture_client,
337 (BYTE **) & samples, &nsamples_read, &flags, &devpos, NULL);
339 while (hr == AUDCLNT_S_BUFFER_EMPTY);
342 GST_ERROR_OBJECT (self, "IAudioCaptureClient::GetBuffer () failed: %s",
343 gst_wasapi_util_hresult_to_string (hr));
344 ret = GST_FLOW_ERROR;
349 GST_WARNING_OBJECT (self, "devpos %" G_GUINT64_FORMAT ": flags=0x%08x",
353 /* FIXME: Why do we get 1024 sometimes and not a multiple of
354 * samples_per_buffer? Shouldn't WASAPI provide a DISCONT
355 * flag if we read too slow?
357 nsamples = nsamples_read;
358 g_assert (nsamples >= self->samples_per_buffer);
359 if (nsamples > self->samples_per_buffer) {
360 GST_WARNING_OBJECT (self,
361 "devpos %" G_GUINT64_FORMAT ": got %d samples, expected %d, clipping!",
362 devpos, nsamples, self->samples_per_buffer);
364 nsamples = self->samples_per_buffer;
367 if (clock == NULL || clock == self->clock) {
369 gst_util_uint64_scale (devpos, GST_SECOND, self->client_clock_freq);
371 GstClockTime base_time;
373 timestamp = gst_clock_get_time (clock);
375 base_time = GST_ELEMENT_CAST (self)->base_time;
376 if (timestamp > base_time)
377 timestamp -= base_time;
381 if (timestamp > duration)
382 timestamp -= duration;
387 ret = gst_pad_alloc_buffer_and_set_caps (GST_BASE_SRC_PAD (self),
389 nsamples * sizeof (gint16), GST_PAD_CAPS (GST_BASE_SRC_PAD (self)), buf);
391 if (ret == GST_FLOW_OK) {
395 GST_BUFFER_OFFSET_END (*buf) = devpos + self->samples_per_buffer;
396 GST_BUFFER_TIMESTAMP (*buf) = timestamp;
397 GST_BUFFER_DURATION (*buf) = duration;
399 dst = (gint16 *) GST_BUFFER_DATA (*buf);
400 for (i = 0; i < nsamples; i++) {
408 hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, nsamples_read);
410 GST_ERROR_OBJECT (self, "IAudioCaptureClient::ReleaseBuffer () failed: %s",
411 gst_wasapi_util_hresult_to_string (hr));
412 ret = GST_FLOW_ERROR;
418 gst_object_unref (clock);
424 gst_wasapi_src_get_time (GstClock * clock, gpointer user_data)
426 GstWasapiSrc *self = GST_WASAPI_SRC (user_data);
431 if (G_UNLIKELY (self->client_clock == NULL))
432 return GST_CLOCK_TIME_NONE;
434 hr = IAudioClock_GetPosition (self->client_clock, &devpos, NULL);
435 if (G_UNLIKELY (hr != S_OK))
436 return GST_CLOCK_TIME_NONE;
438 result = gst_util_uint64_scale_int (devpos, GST_SECOND,
439 self->client_clock_freq);
442 GST_DEBUG_OBJECT (self, "devpos = %" G_GUINT64_FORMAT
443 " frequency = %" G_GUINT64_FORMAT
444 " result = %" G_GUINT64_FORMAT " ms",
445 devpos, self->client_clock_freq, GST_TIME_AS_MSECONDS (result));