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