wasapi: Port wasapisink to GstAudioSink
[platform/upstream/gstreamer.git] / sys / wasapi / gstwasapisrc.c
1 /*
2  * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /**
21  * SECTION:element-wasapisrc
22  *
23  * Provides audio capture from the Windows Audio Session API available with
24  * Vista and newer.
25  *
26  * <refsect2>
27  * <title>Example pipelines</title>
28  * |[
29  * gst-launch-1.0 -v wasapisrc ! fakesink
30  * ]| Capture from the default audio device and render to fakesink.
31  * </refsect2>
32  */
33 #ifdef HAVE_CONFIG_H
34 #  include <config.h>
35 #endif
36
37 #include "gstwasapisrc.h"
38 #include <gst/audio/gstaudioclock.h>
39
40 GST_DEBUG_CATEGORY_STATIC (gst_wasapi_src_debug);
41 #define GST_CAT_DEFAULT gst_wasapi_src_debug
42
43 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
44     GST_PAD_SRC,
45     GST_PAD_ALWAYS,
46     GST_STATIC_CAPS ("audio/x-raw, "
47         "format = (string) S16LE, "
48         "layout = (string) interleaved, "
49         "rate = (int) 8000, " "channels = (int) 1"));
50
51 static void gst_wasapi_src_dispose (GObject * object);
52 static void gst_wasapi_src_finalize (GObject * object);
53
54 static GstClock *gst_wasapi_src_provide_clock (GstElement * element);
55
56 static gboolean gst_wasapi_src_start (GstBaseSrc * src);
57 static gboolean gst_wasapi_src_stop (GstBaseSrc * src);
58 static gboolean gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query);
59
60 static GstFlowReturn gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf);
61
62 static GstClockTime gst_wasapi_src_get_time (GstClock * clock,
63     gpointer user_data);
64
65 G_DEFINE_TYPE (GstWasapiSrc, gst_wasapi_src, GST_TYPE_PUSH_SRC);
66
67 static void
68 gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
69 {
70   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
71   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
72   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
73   GstPushSrcClass *gstpushsrc_class = GST_PUSH_SRC_CLASS (klass);
74
75   gobject_class->dispose = gst_wasapi_src_dispose;
76   gobject_class->finalize = gst_wasapi_src_finalize;
77
78   gstelement_class->provide_clock = gst_wasapi_src_provide_clock;
79
80   gst_element_class_add_pad_template (gstelement_class,
81       gst_static_pad_template_get (&src_template));
82   gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc",
83       "Source/Audio",
84       "Stream audio from an audio capture device through WASAPI",
85       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
86
87   gstbasesrc_class->start = gst_wasapi_src_start;
88   gstbasesrc_class->stop = gst_wasapi_src_stop;
89   gstbasesrc_class->query = gst_wasapi_src_query;
90
91   gstpushsrc_class->create = gst_wasapi_src_create;
92
93   GST_DEBUG_CATEGORY_INIT (gst_wasapi_src_debug, "wasapisrc",
94       0, "Windows audio session API source");
95 }
96
97 static void
98 gst_wasapi_src_init (GstWasapiSrc * self)
99 {
100   GstBaseSrc *basesrc = GST_BASE_SRC (self);
101
102   gst_base_src_set_format (basesrc, GST_FORMAT_TIME);
103   gst_base_src_set_live (basesrc, TRUE);
104
105   self->rate = 8000;
106   self->buffer_time = 20 * GST_MSECOND;
107   self->period_time = 20 * GST_MSECOND;
108   self->latency = GST_CLOCK_TIME_NONE;
109   self->samples_per_buffer = self->rate / (GST_SECOND / self->period_time);
110
111   self->start_time = GST_CLOCK_TIME_NONE;
112   self->next_time = GST_CLOCK_TIME_NONE;
113
114   self->clock = gst_audio_clock_new ("GstWasapiSrcClock",
115       gst_wasapi_src_get_time, gst_object_ref (self),
116       (GDestroyNotify) gst_object_unref);
117
118   CoInitialize (NULL);
119 }
120
121 static void
122 gst_wasapi_src_dispose (GObject * object)
123 {
124   GstWasapiSrc *self = GST_WASAPI_SRC (object);
125
126   if (self->clock != NULL) {
127     gst_object_unref (self->clock);
128     self->clock = NULL;
129   }
130
131   G_OBJECT_CLASS (gst_wasapi_src_parent_class)->dispose (object);
132 }
133
134 static void
135 gst_wasapi_src_finalize (GObject * object)
136 {
137   CoUninitialize ();
138
139   G_OBJECT_CLASS (gst_wasapi_src_parent_class)->finalize (object);
140 }
141
142 static GstClock *
143 gst_wasapi_src_provide_clock (GstElement * element)
144 {
145   GstWasapiSrc *self = GST_WASAPI_SRC (element);
146   GstClock *clock;
147
148   GST_OBJECT_LOCK (self);
149
150   if (self->client_clock == NULL)
151     goto wrong_state;
152
153   clock = GST_CLOCK (gst_object_ref (self->clock));
154
155   GST_OBJECT_UNLOCK (self);
156   return clock;
157
158   /* ERRORS */
159 wrong_state:
160   {
161     GST_OBJECT_UNLOCK (self);
162     GST_DEBUG_OBJECT (self, "IAudioClock not acquired");
163     return NULL;
164   }
165 }
166
167 static gboolean
168 gst_wasapi_src_start (GstBaseSrc * src)
169 {
170   GstWasapiSrc *self = GST_WASAPI_SRC (src);
171   gboolean res = FALSE;
172   IAudioClient *client = NULL;
173   IAudioClock *client_clock = NULL;
174   guint64 client_clock_freq = 0;
175   IAudioCaptureClient *capture_client = NULL;
176   HRESULT hr;
177
178   if (!gst_wasapi_util_get_default_device_client (GST_ELEMENT (self),
179           TRUE, self->rate, self->buffer_time, self->period_time, 0, &client,
180           &self->latency))
181     goto beach;
182
183   hr = IAudioClient_GetService (client, &IID_IAudioClock,
184       (void **) &client_clock);
185   if (hr != S_OK) {
186     GST_ERROR_OBJECT (self, "IAudioClient::GetService (IID_IAudioClock) "
187         "failed");
188     goto beach;
189   }
190
191   hr = IAudioClock_GetFrequency (client_clock, &client_clock_freq);
192   if (hr != S_OK) {
193     GST_ERROR_OBJECT (self, "IAudioClock::GetFrequency () failed");
194     goto beach;
195   }
196
197   hr = IAudioClient_GetService (client, &IID_IAudioCaptureClient,
198       (void **) &capture_client);
199   if (hr != S_OK) {
200     GST_ERROR_OBJECT (self, "IAudioClient::GetService "
201         "(IID_IAudioCaptureClient) failed");
202     goto beach;
203   }
204
205   hr = IAudioClient_Start (client);
206   if (hr != S_OK) {
207     GST_ERROR_OBJECT (self, "IAudioClient::Start failed");
208     goto beach;
209   }
210
211   self->client = client;
212   self->client_clock = client_clock;
213   self->client_clock_freq = client_clock_freq;
214   self->capture_client = capture_client;
215
216   res = TRUE;
217
218 beach:
219   if (!res) {
220     if (capture_client != NULL)
221       IUnknown_Release (capture_client);
222
223     if (client_clock != NULL)
224       IUnknown_Release (client_clock);
225
226     if (client != NULL)
227       IUnknown_Release (client);
228   }
229
230   return res;
231 }
232
233 static gboolean
234 gst_wasapi_src_stop (GstBaseSrc * src)
235 {
236   GstWasapiSrc *self = GST_WASAPI_SRC (src);
237
238   if (self->client != NULL) {
239     IAudioClient_Stop (self->client);
240   }
241
242   if (self->capture_client != NULL) {
243     IUnknown_Release (self->capture_client);
244     self->capture_client = NULL;
245   }
246
247   if (self->client_clock != NULL) {
248     IUnknown_Release (self->client_clock);
249     self->client_clock = NULL;
250   }
251
252   if (self->client != NULL) {
253     IUnknown_Release (self->client);
254     self->client = NULL;
255   }
256
257   return TRUE;
258 }
259
260 static gboolean
261 gst_wasapi_src_query (GstBaseSrc * src, GstQuery * query)
262 {
263   GstWasapiSrc *self = GST_WASAPI_SRC (src);
264   gboolean ret = FALSE;
265
266   GST_DEBUG_OBJECT (self, "query for %s",
267       gst_query_type_get_name (GST_QUERY_TYPE (query)));
268
269   switch (GST_QUERY_TYPE (query)) {
270     case GST_QUERY_LATENCY:{
271       GstClockTime min_latency, max_latency;
272
273       min_latency = self->latency + self->period_time;
274       max_latency = min_latency;
275
276       GST_DEBUG_OBJECT (self, "reporting latency of min %" GST_TIME_FORMAT
277           " max %" GST_TIME_FORMAT,
278           GST_TIME_ARGS (min_latency), GST_TIME_ARGS (max_latency));
279
280       gst_query_set_latency (query, TRUE, min_latency, max_latency);
281       ret = TRUE;
282       break;
283     }
284
285     default:
286       ret =
287           GST_BASE_SRC_CLASS (gst_wasapi_src_parent_class)->query (src, query);
288       break;
289   }
290
291   return ret;
292 }
293
294 static GstFlowReturn
295 gst_wasapi_src_create (GstPushSrc * src, GstBuffer ** buf)
296 {
297   GstWasapiSrc *self = GST_WASAPI_SRC (src);
298   GstFlowReturn ret = GST_FLOW_OK;
299   GstClock *clock;
300   GstClockTime timestamp, duration = self->period_time;
301   HRESULT hr;
302   gint16 *samples = NULL;
303   guint32 nsamples_read = 0, nsamples;
304   DWORD flags = 0;
305   guint64 devpos;
306   guint i;
307   GstMapInfo minfo;
308   gint16 *dst;
309
310   GST_OBJECT_LOCK (self);
311   clock = GST_ELEMENT_CLOCK (self);
312   if (clock != NULL)
313     gst_object_ref (clock);
314   GST_OBJECT_UNLOCK (self);
315
316   if (clock != NULL && GST_CLOCK_TIME_IS_VALID (self->next_time)) {
317     GstClockID id;
318
319     id = gst_clock_new_single_shot_id (clock, self->next_time);
320     gst_clock_id_wait (id, NULL);
321     gst_clock_id_unref (id);
322   }
323
324   do {
325     hr = IAudioCaptureClient_GetBuffer (self->capture_client,
326         (BYTE **) & samples, &nsamples_read, &flags, &devpos, NULL);
327   }
328   while (hr == AUDCLNT_S_BUFFER_EMPTY);
329
330   if (hr != S_OK) {
331     GST_ERROR_OBJECT (self, "IAudioCaptureClient::GetBuffer () failed: %s",
332         gst_wasapi_util_hresult_to_string (hr));
333     ret = GST_FLOW_ERROR;
334     goto beach;
335   }
336
337   if (flags != 0) {
338     GST_WARNING_OBJECT (self, "devpos %" G_GUINT64_FORMAT ": flags=0x%08x",
339         devpos, (guint) flags);
340   }
341
342   /* FIXME: Why do we get 1024 sometimes and not a multiple of
343    *        samples_per_buffer? Shouldn't WASAPI provide a DISCONT
344    *        flag if we read too slow?
345    */
346   nsamples = nsamples_read;
347   g_assert (nsamples >= self->samples_per_buffer);
348   if (nsamples > self->samples_per_buffer) {
349     GST_WARNING_OBJECT (self,
350         "devpos %" G_GUINT64_FORMAT ": got %d samples, expected %d, clipping!",
351         devpos, nsamples, self->samples_per_buffer);
352
353     nsamples = self->samples_per_buffer;
354   }
355
356   if (clock == NULL || clock == self->clock) {
357     timestamp =
358         gst_util_uint64_scale (devpos, GST_SECOND, self->client_clock_freq);
359   } else {
360     GstClockTime base_time;
361
362     timestamp = gst_clock_get_time (clock);
363
364     base_time = GST_ELEMENT_CAST (self)->base_time;
365     if (timestamp > base_time)
366       timestamp -= base_time;
367     else
368       timestamp = 0;
369
370     if (timestamp > duration)
371       timestamp -= duration;
372     else
373       timestamp = 0;
374   }
375
376   *buf = gst_buffer_new_and_alloc (nsamples * sizeof (gint16));
377
378   GST_BUFFER_OFFSET_END (*buf) = devpos + self->samples_per_buffer;
379   GST_BUFFER_TIMESTAMP (*buf) = timestamp;
380   GST_BUFFER_DURATION (*buf) = duration;
381
382   gst_buffer_map (*buf, &minfo, GST_MAP_WRITE);
383   dst = (gint16 *) minfo.data;
384   for (i = 0; i < nsamples; i++) {
385     *dst = *samples;
386
387     samples += 2;
388     dst++;
389   }
390   gst_buffer_unmap (*buf, &minfo);
391
392   hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, nsamples_read);
393   if (hr != S_OK) {
394     GST_ERROR_OBJECT (self, "IAudioCaptureClient::ReleaseBuffer () failed: %s",
395         gst_wasapi_util_hresult_to_string (hr));
396     ret = GST_FLOW_ERROR;
397     goto beach;
398   }
399
400 beach:
401   if (clock != NULL)
402     gst_object_unref (clock);
403
404   return ret;
405 }
406
407 static GstClockTime
408 gst_wasapi_src_get_time (GstClock * clock, gpointer user_data)
409 {
410   GstWasapiSrc *self = GST_WASAPI_SRC (user_data);
411   HRESULT hr;
412   guint64 devpos;
413   GstClockTime result;
414
415   if (G_UNLIKELY (self->client_clock == NULL))
416     return GST_CLOCK_TIME_NONE;
417
418   hr = IAudioClock_GetPosition (self->client_clock, &devpos, NULL);
419   if (G_UNLIKELY (hr != S_OK))
420     return GST_CLOCK_TIME_NONE;
421
422   result = gst_util_uint64_scale_int (devpos, GST_SECOND,
423       self->client_clock_freq);
424
425   /*
426      GST_DEBUG_OBJECT (self, "devpos = %" G_GUINT64_FORMAT
427      " frequency = %" G_GUINT64_FORMAT
428      " result = %" G_GUINT64_FORMAT " ms",
429      devpos, self->client_clock_freq, GST_TIME_AS_MSECONDS (result));
430    */
431
432   return result;
433 }