pipewire: update to master as of Nov 19th 2019
[AGL/meta-agl-devel.git] / meta-pipewire / recipes-multimedia / pipewire / pipewire / 0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch
1 From 4696c0071d7a713d3e415f7d0cf29364a2b19aa2 Mon Sep 17 00:00:00 2001
2 From: George Kiagiadakis <george.kiagiadakis@collabora.com>
3 Date: Tue, 19 Feb 2019 18:23:19 +0200
4 Subject: [PATCH] gst: Implement new pwaudio{src,sink} elements, based on
5  GstAudioBase{Src,Sink}
6
7 These are much more reliable elements to use for audio data.
8 * GstAudioBaseSink provides a reliable clock implementation based
9   on the number of samples read/written
10 * on the pipewire side we make sure to dequeue, fill and enqueue
11   a single buffer inside the process() function, which avoids
12   underruns
13
14 Both elements share a common ringbuffer that actually implements
15 the pipewire integration.
16
17 Upstream-Status: Denied
18 See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
19 ---
20  src/gst/gstpipewire.c          |   8 +-
21  src/gst/gstpwaudioringbuffer.c | 542 +++++++++++++++++++++++++++++++++
22  src/gst/gstpwaudioringbuffer.h |  83 +++++
23  src/gst/gstpwaudiosink.c       | 200 ++++++++++++
24  src/gst/gstpwaudiosink.h       |  48 +++
25  src/gst/gstpwaudiosrc.c        | 200 ++++++++++++
26  src/gst/gstpwaudiosrc.h        |  48 +++
27  src/gst/meson.build            |   6 +
28  8 files changed, 1134 insertions(+), 1 deletion(-)
29  create mode 100644 src/gst/gstpwaudioringbuffer.c
30  create mode 100644 src/gst/gstpwaudioringbuffer.h
31  create mode 100644 src/gst/gstpwaudiosink.c
32  create mode 100644 src/gst/gstpwaudiosink.h
33  create mode 100644 src/gst/gstpwaudiosrc.c
34  create mode 100644 src/gst/gstpwaudiosrc.h
35
36 diff --git a/src/gst/gstpipewire.c b/src/gst/gstpipewire.c
37 index 4040264b..68fd446f 100644
38 --- a/src/gst/gstpipewire.c
39 +++ b/src/gst/gstpipewire.c
40 @@ -40,6 +40,8 @@
41  #include "gstpipewiresrc.h"
42  #include "gstpipewiresink.h"
43  #include "gstpipewiredeviceprovider.h"
44 +#include "gstpwaudiosrc.h"
45 +#include "gstpwaudiosink.h"
46  
47  GST_DEBUG_CATEGORY (pipewire_debug);
48  
49 @@ -52,12 +54,16 @@ plugin_init (GstPlugin *plugin)
50        GST_TYPE_PIPEWIRE_SRC);
51    gst_element_register (plugin, "pipewiresink", GST_RANK_NONE,
52        GST_TYPE_PIPEWIRE_SINK);
53 +  gst_element_register (plugin, "pwaudiosrc", GST_RANK_NONE,
54 +      GST_TYPE_PW_AUDIO_SRC);
55 +  gst_element_register (plugin, "pwaudiosink", GST_RANK_NONE,
56 +      GST_TYPE_PW_AUDIO_SINK);
57  
58    if (!gst_device_provider_register (plugin, "pipewiredeviceprovider",
59         GST_RANK_PRIMARY + 1, GST_TYPE_PIPEWIRE_DEVICE_PROVIDER))
60      return FALSE;
61  
62 -  GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWirie elements");
63 +  GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWire elements");
64  
65    return TRUE;
66  }
67 diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c
68 new file mode 100644
69 index 00000000..989b2cd7
70 --- /dev/null
71 +++ b/src/gst/gstpwaudioringbuffer.c
72 @@ -0,0 +1,542 @@
73 +/* PipeWire
74 + *
75 + * Copyright © 2018 Wim Taymans
76 + * Copyright © 2019 Collabora Ltd.
77 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
78 + *
79 + * Permission is hereby granted, free of charge, to any person obtaining a
80 + * copy of this software and associated documentation files (the "Software"),
81 + * to deal in the Software without restriction, including without limitation
82 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
83 + * and/or sell copies of the Software, and to permit persons to whom the
84 + * Software is furnished to do so, subject to the following conditions:
85 + *
86 + * The above copyright notice and this permission notice (including the next
87 + * paragraph) shall be included in all copies or substantial portions of the
88 + * Software.
89 + *
90 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
91 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
92 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
93 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
94 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
95 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
96 + * DEALINGS IN THE SOFTWARE.
97 + */
98 +
99 +#ifdef HAVE_CONFIG_H
100 +#include "config.h"
101 +#endif
102 +
103 +#include "gstpwaudioringbuffer.h"
104 +
105 +#include <spa/param/audio/format-utils.h>
106 +#include <spa/pod/builder.h>
107 +
108 +GST_DEBUG_CATEGORY_STATIC (pw_audio_ring_buffer_debug);
109 +#define GST_CAT_DEFAULT pw_audio_ring_buffer_debug
110 +
111 +#define gst_pw_audio_ring_buffer_parent_class parent_class
112 +G_DEFINE_TYPE (GstPwAudioRingBuffer, gst_pw_audio_ring_buffer, GST_TYPE_AUDIO_RING_BUFFER);
113 +
114 +enum
115 +{
116 +  PROP_0,
117 +  PROP_ELEMENT,
118 +  PROP_DIRECTION,
119 +  PROP_PROPS
120 +};
121 +
122 +static void
123 +gst_pw_audio_ring_buffer_init (GstPwAudioRingBuffer * self)
124 +{
125 +  self->loop = pw_loop_new (NULL);
126 +  self->main_loop = pw_thread_loop_new (self->loop, "pw-audioringbuffer-loop");
127 +  self->core = pw_core_new (self->loop, NULL, 0);
128 +}
129 +
130 +static void
131 +gst_pw_audio_ring_buffer_finalize (GObject * object)
132 +{
133 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object);
134 +
135 +  pw_core_destroy (self->core);
136 +  pw_thread_loop_destroy (self->main_loop);
137 +  pw_loop_destroy (self->loop);
138 +}
139 +
140 +static void
141 +gst_pw_audio_ring_buffer_set_property (GObject * object, guint prop_id,
142 +    const GValue * value, GParamSpec * pspec)
143 +{
144 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object);
145 +
146 +  switch (prop_id) {
147 +    case PROP_ELEMENT:
148 +      self->elem = g_value_get_object (value);
149 +      break;
150 +
151 +    case PROP_DIRECTION:
152 +      self->direction = g_value_get_int (value);
153 +      break;
154 +
155 +    case PROP_PROPS:
156 +      self->props = g_value_get_pointer (value);
157 +      break;
158 +
159 +    default:
160 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161 +      break;
162 +  }
163 +}
164 +
165 +static void
166 +on_remote_state_changed (void *data, enum pw_remote_state old,
167 +    enum pw_remote_state state, const char *error)
168 +{
169 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
170 +
171 +  GST_DEBUG_OBJECT (self->elem, "got remote state %d", state);
172 +
173 +  switch (state) {
174 +    case PW_REMOTE_STATE_UNCONNECTED:
175 +    case PW_REMOTE_STATE_CONNECTING:
176 +    case PW_REMOTE_STATE_CONNECTED:
177 +      break;
178 +    case PW_REMOTE_STATE_ERROR:
179 +      GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
180 +          ("remote error: %s", error), (NULL));
181 +      break;
182 +  }
183 +  pw_thread_loop_signal (self->main_loop, FALSE);
184 +}
185 +
186 +static const struct pw_remote_events remote_events = {
187 +  PW_VERSION_REMOTE_EVENTS,
188 +  .state_changed = on_remote_state_changed,
189 +};
190 +
191 +static gboolean
192 +wait_for_remote_state (GstPwAudioRingBuffer *self,
193 +    enum pw_remote_state target)
194 +{
195 +  while (TRUE) {
196 +    enum pw_remote_state state = pw_remote_get_state (self->remote, NULL);
197 +    if (state == target)
198 +      return TRUE;
199 +    if (state == PW_REMOTE_STATE_ERROR)
200 +      return FALSE;
201 +    pw_thread_loop_wait (self->main_loop);
202 +  }
203 +}
204 +
205 +static gboolean
206 +gst_pw_audio_ring_buffer_open_device (GstAudioRingBuffer *buf)
207 +{
208 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
209 +
210 +  GST_DEBUG_OBJECT (self->elem, "open device");
211 +
212 +  if (pw_thread_loop_start (self->main_loop) < 0)
213 +    goto mainloop_error;
214 +
215 +  pw_thread_loop_lock (self->main_loop);
216 +
217 +  self->remote = pw_remote_new (self->core, NULL, 0);
218 +  pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events,
219 +      self);
220 +
221 +  if (self->props->fd == -1)
222 +    pw_remote_connect (self->remote);
223 +  else
224 +    pw_remote_connect_fd (self->remote, self->props->fd);
225 +
226 +  GST_DEBUG_OBJECT (self->elem, "waiting for connection");
227 +
228 +  if (!wait_for_remote_state (self, PW_REMOTE_STATE_CONNECTED))
229 +    goto connect_error;
230 +
231 +  pw_thread_loop_unlock (self->main_loop);
232 +
233 +  return TRUE;
234 +
235 +  /* ERRORS */
236 +mainloop_error:
237 +  {
238 +    GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
239 +        ("Failed to start mainloop"), (NULL));
240 +    return FALSE;
241 +  }
242 +connect_error:
243 +  {
244 +    pw_thread_loop_unlock (self->main_loop);
245 +    return FALSE;
246 +  }
247 +}
248 +
249 +static gboolean
250 +gst_pw_audio_ring_buffer_close_device (GstAudioRingBuffer *buf)
251 +{
252 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
253 +
254 +  GST_DEBUG_OBJECT (self->elem, "closing device");
255 +
256 +  pw_thread_loop_lock (self->main_loop);
257 +  if (self->remote) {
258 +    pw_remote_disconnect (self->remote);
259 +    wait_for_remote_state (self, PW_REMOTE_STATE_UNCONNECTED);
260 +  }
261 +  pw_thread_loop_unlock (self->main_loop);
262 +
263 +  pw_thread_loop_stop (self->main_loop);
264 +
265 +  if (self->remote) {
266 +    pw_remote_destroy (self->remote);
267 +    self->remote = NULL;
268 +  }
269 +  return TRUE;
270 +}
271 +
272 +static void
273 +on_stream_state_changed (void *data, enum pw_stream_state old,
274 +    enum pw_stream_state state, const char *error)
275 +{
276 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
277 +
278 +  GST_DEBUG_OBJECT (self->elem, "got stream state: %s",
279 +      pw_stream_state_as_string (state));
280 +
281 +  switch (state) {
282 +    case PW_STREAM_STATE_UNCONNECTED:
283 +      GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
284 +          ("stream disconnected unexpectedly"), (NULL));
285 +      break;
286 +    case PW_STREAM_STATE_CONNECTING:
287 +    case PW_STREAM_STATE_CONFIGURE:
288 +    case PW_STREAM_STATE_READY:
289 +    case PW_STREAM_STATE_PAUSED:
290 +    case PW_STREAM_STATE_STREAMING:
291 +      break;
292 +    case PW_STREAM_STATE_ERROR:
293 +      GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
294 +          ("stream error: %s", error), (NULL));
295 +      break;
296 +  }
297 +  pw_thread_loop_signal (self->main_loop, FALSE);
298 +}
299 +
300 +static gboolean
301 +wait_for_stream_state (GstPwAudioRingBuffer *self,
302 +    enum pw_stream_state target)
303 +{
304 +  while (TRUE) {
305 +    enum pw_stream_state state = pw_stream_get_state (self->stream, NULL);
306 +    if (state >= target)
307 +      return TRUE;
308 +    if (state == PW_STREAM_STATE_ERROR || state == PW_STREAM_STATE_UNCONNECTED)
309 +      return FALSE;
310 +    pw_thread_loop_wait (self->main_loop);
311 +  }
312 +}
313 +
314 +static void
315 +on_stream_format_changed (void *data, const struct spa_pod *format)
316 +{
317 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
318 +  const struct spa_pod *params[1];
319 +  struct spa_pod_builder b = { NULL };
320 +  uint8_t buffer[512];
321 +
322 +  spa_pod_builder_init (&b, buffer, sizeof (buffer));
323 +  params[0] = spa_pod_builder_add_object (&b,
324 +      SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
325 +      SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 1, INT32_MAX),
326 +      SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
327 +      SPA_PARAM_BUFFERS_size,    SPA_POD_Int(self->segsize),
328 +      SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(self->bpf),
329 +      SPA_PARAM_BUFFERS_align,   SPA_POD_Int(16));
330 +
331 +  GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize);
332 +  pw_stream_finish_format (self->stream, 0, params, 1);
333 +}
334 +
335 +static void
336 +on_stream_process (void *data)
337 +{
338 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
339 +  GstAudioRingBuffer *buf = GST_AUDIO_RING_BUFFER (data);
340 +  struct pw_buffer *b;
341 +  struct spa_data *d;
342 +  gint size;       /*< size to read/write from/to the spa buffer */
343 +  gint offset;     /*< offset to read/write from/to in the spa buffer */
344 +  gint segment;    /*< the current segment number in the ringbuffer */
345 +  guint8 *ringptr; /*< pointer to the beginning of the current segment */
346 +  gint segsize;    /*< the size of one segment in the ringbuffer */
347 +  gint copy_size;  /*< the bytes to copy in one memcpy() invocation */
348 +  gint remain;     /*< remainder of bytes available in the spa buffer */
349 +
350 +  if (g_atomic_int_get (&buf->state) != GST_AUDIO_RING_BUFFER_STATE_STARTED) {
351 +    GST_LOG_OBJECT (self->elem, "ring buffer is not started");
352 +    return;
353 +  }
354 +
355 +  b = pw_stream_dequeue_buffer (self->stream);
356 +  if (!b) {
357 +    GST_WARNING_OBJECT (self->elem, "no pipewire buffer available");
358 +    return;
359 +  }
360 +
361 +  d = &b->buffer->datas[0];
362 +
363 +  if (self->direction == PW_DIRECTION_OUTPUT) {
364 +    /* in output mode, always fill the entire spa buffer */
365 +    offset = d->chunk->offset = 0;
366 +    size = d->chunk->size = d->maxsize;
367 +    b->size = size / self->bpf;
368 +  } else {
369 +    offset = SPA_MIN (d->chunk->offset, d->maxsize);
370 +    size = SPA_MIN (d->chunk->size, d->maxsize - offset);
371 +  }
372 +
373 +  do {
374 +    gst_audio_ring_buffer_prepare_read (buf, &segment, &ringptr, &segsize);
375 +
376 +    /* in INPUT (src) mode, it is possible that the skew algorithm
377 +     * advances the ringbuffer behind our back */
378 +    if (self->segoffset > 0 && self->cur_segment != segment)
379 +      self->segoffset = 0;
380 +
381 +    copy_size = SPA_MIN (size, segsize - self->segoffset);
382 +
383 +    if (self->direction == PW_DIRECTION_OUTPUT) {
384 +      memcpy (((guint8*) d->data) + offset, ringptr + self->segoffset,
385 +          copy_size);
386 +    } else {
387 +      memcpy (ringptr + self->segoffset, ((guint8*) d->data) + offset,
388 +          copy_size);
389 +    }
390 +
391 +    remain = size - (segsize - self->segoffset);
392 +
393 +    GST_TRACE_OBJECT (self->elem,
394 +        "seg %d: %s %d bytes remained:%d offset:%d segoffset:%d", segment,
395 +        self->direction == PW_DIRECTION_INPUT ? "INPUT" : "OUTPUT",
396 +        copy_size, remain, offset, self->segoffset);
397 +
398 +    if (remain >= 0) {
399 +      offset += (segsize - self->segoffset);
400 +      size = remain;
401 +
402 +      /* write silence on the segment we just read */
403 +      if (self->direction == PW_DIRECTION_OUTPUT)
404 +        gst_audio_ring_buffer_clear (buf, segment);
405 +
406 +      /* notify that we have read a complete segment */
407 +      gst_audio_ring_buffer_advance (buf, 1);
408 +      self->segoffset = 0;
409 +    } else {
410 +      self->segoffset += size;
411 +      self->cur_segment = segment;
412 +    }
413 +  } while (remain > 0);
414 +
415 +  pw_stream_queue_buffer (self->stream, b);
416 +}
417 +
418 +static const struct pw_stream_events stream_events = {
419 +  PW_VERSION_STREAM_EVENTS,
420 +  .state_changed = on_stream_state_changed,
421 +  .format_changed = on_stream_format_changed,
422 +  .process = on_stream_process,
423 +};
424 +
425 +static gboolean
426 +copy_properties (GQuark field_id, const GValue *value, gpointer user_data)
427 +{
428 +  struct pw_properties *properties = user_data;
429 +
430 +  if (G_VALUE_HOLDS_STRING (value))
431 +    pw_properties_set (properties,
432 +                       g_quark_to_string (field_id),
433 +                       g_value_get_string (value));
434 +  return TRUE;
435 +}
436 +
437 +static gboolean
438 +gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf,
439 +    GstAudioRingBufferSpec *spec)
440 +{
441 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
442 +  struct pw_properties *props;
443 +  struct spa_pod_builder b = { NULL };
444 +  uint8_t buffer[512];
445 +  const struct spa_pod *params[1];
446 +
447 +  g_return_val_if_fail (spec, FALSE);
448 +  g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&spec->info), FALSE);
449 +  g_return_val_if_fail (!self->stream, TRUE); /* already acquired */
450 +
451 +  g_return_val_if_fail (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW, FALSE);
452 +  g_return_val_if_fail (GST_AUDIO_INFO_IS_FLOAT (&spec->info), FALSE);
453 +
454 +  GST_DEBUG_OBJECT (self->elem, "acquire");
455 +
456 +  /* construct param & props objects */
457 +
458 +  if (self->props->properties) {
459 +    props = pw_properties_new (NULL, NULL);
460 +    gst_structure_foreach (self->props->properties, copy_properties, props);
461 +  } else {
462 +    props = NULL;
463 +  }
464 +
465 +  spa_pod_builder_init (&b, buffer, sizeof (buffer));
466 +  params[0] = spa_pod_builder_add_object (&b,
467 +      SPA_TYPE_OBJECT_Format,    SPA_PARAM_EnumFormat,
468 +      SPA_FORMAT_mediaType,      SPA_POD_Id (SPA_MEDIA_TYPE_audio),
469 +      SPA_FORMAT_mediaSubtype,   SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
470 +      SPA_FORMAT_AUDIO_format,   SPA_POD_Id (SPA_AUDIO_FORMAT_F32),
471 +      SPA_FORMAT_AUDIO_rate,     SPA_POD_Int (GST_AUDIO_INFO_RATE (&spec->info)),
472 +      SPA_FORMAT_AUDIO_channels, SPA_POD_Int (GST_AUDIO_INFO_CHANNELS (&spec->info)));
473 +
474 +  self->segsize = spec->segsize;
475 +  self->bpf = GST_AUDIO_INFO_BPF (&spec->info);
476 +  self->rate = GST_AUDIO_INFO_RATE (&spec->info);
477 +  self->segoffset = 0;
478 +
479 +  /* connect stream */
480 +
481 +  pw_thread_loop_lock (self->main_loop);
482 +
483 +  GST_DEBUG_OBJECT (self->elem, "creating stream");
484 +
485 +  self->stream = pw_stream_new (self->remote, self->props->client_name, props);
486 +  pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events,
487 +      self);
488 +
489 +  if (pw_stream_connect (self->stream,
490 +          self->direction,
491 +          self->props->path ? (uint32_t)atoi(self->props->path) : SPA_ID_INVALID,
492 +          PW_STREAM_FLAG_AUTOCONNECT |
493 +          PW_STREAM_FLAG_MAP_BUFFERS |
494 +          PW_STREAM_FLAG_RT_PROCESS,
495 +          params, 1) < 0)
496 +    goto start_error;
497 +
498 +  GST_DEBUG_OBJECT (self->elem, "waiting for stream READY");
499 +
500 +  if (!wait_for_stream_state (self, PW_STREAM_STATE_READY))
501 +    goto start_error;
502 +
503 +  pw_thread_loop_unlock (self->main_loop);
504 +
505 +  /* allocate the internal ringbuffer */
506 +
507 +  spec->seglatency = spec->segtotal + 1;
508 +  buf->size = spec->segtotal * spec->segsize;
509 +  buf->memory = g_malloc (buf->size);
510 +
511 +  gst_audio_format_fill_silence (buf->spec.info.finfo, buf->memory,
512 +      buf->size);
513 +
514 +  GST_DEBUG_OBJECT (self->elem, "acquire done");
515 +
516 +  return TRUE;
517 +
518 +start_error:
519 +  {
520 +    GST_ERROR_OBJECT (self->elem, "could not start stream");
521 +    pw_stream_destroy (self->stream);
522 +    self->stream = NULL;
523 +    pw_thread_loop_unlock (self->main_loop);
524 +    return FALSE;
525 +  }
526 +}
527 +
528 +static gboolean
529 +gst_pw_audio_ring_buffer_release (GstAudioRingBuffer *buf)
530 +{
531 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
532 +
533 +  GST_DEBUG_OBJECT (self->elem, "release");
534 +
535 +  pw_thread_loop_lock (self->main_loop);
536 +  if (self->stream) {
537 +    spa_hook_remove (&self->stream_listener);
538 +    pw_stream_disconnect (self->stream);
539 +    pw_stream_destroy (self->stream);
540 +    self->stream = NULL;
541 +  }
542 +  pw_thread_loop_unlock (self->main_loop);
543 +
544 +  /* free the buffer */
545 +  g_free (buf->memory);
546 +  buf->memory = NULL;
547 +
548 +  return TRUE;
549 +}
550 +
551 +static guint
552 +gst_pw_audio_ring_buffer_delay (GstAudioRingBuffer *buf)
553 +{
554 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
555 +  struct pw_time t;
556 +
557 +  if (!self->stream || pw_stream_get_time (self->stream, &t) < 0)
558 +    return 0;
559 +
560 +  if (self->direction == PW_DIRECTION_OUTPUT) {
561 +    /* on output streams, we set the pw_buffer.size in frames,
562 +       so no conversion is necessary */
563 +    return t.queued;
564 +  } else {
565 +    /* on input streams, pw_buffer.size is set by pw_stream in ticks,
566 +       so we need to convert it to frames and also add segoffset, which
567 +       is the number of bytes we have read but not advertised yet, as
568 +       the segment is incomplete */
569 +    if (t.rate.denom > 0)
570 +      return
571 +        gst_util_uint64_scale (t.queued, self->rate * t.rate.num, t.rate.denom)
572 +        + self->segoffset / self->bpf;
573 +    else
574 +      return self->segoffset / self->bpf;
575 +  }
576 +
577 +  return 0;
578 +}
579 +
580 +static void
581 +gst_pw_audio_ring_buffer_class_init (GstPwAudioRingBufferClass * klass)
582 +{
583 +  GObjectClass *gobject_class;
584 +  GstAudioRingBufferClass *gstaudiorbuf_class;
585 +
586 +  gobject_class = (GObjectClass *) klass;
587 +  gstaudiorbuf_class = (GstAudioRingBufferClass *) klass;
588 +
589 +  gobject_class->finalize = gst_pw_audio_ring_buffer_finalize;
590 +  gobject_class->set_property = gst_pw_audio_ring_buffer_set_property;
591 +
592 +  gstaudiorbuf_class->open_device = gst_pw_audio_ring_buffer_open_device;
593 +  gstaudiorbuf_class->acquire = gst_pw_audio_ring_buffer_acquire;
594 +  gstaudiorbuf_class->release = gst_pw_audio_ring_buffer_release;
595 +  gstaudiorbuf_class->close_device = gst_pw_audio_ring_buffer_close_device;
596 +  gstaudiorbuf_class->delay = gst_pw_audio_ring_buffer_delay;
597 +
598 +  g_object_class_install_property (gobject_class, PROP_ELEMENT,
599 +      g_param_spec_object ("element", "Element", "The audio source or sink",
600 +          GST_TYPE_ELEMENT,
601 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
602 +
603 +  g_object_class_install_property (gobject_class, PROP_DIRECTION,
604 +      g_param_spec_int ("direction", "Direction", "The stream direction",
605 +          PW_DIRECTION_INPUT, PW_DIRECTION_OUTPUT, PW_DIRECTION_INPUT,
606 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
607 +
608 +  g_object_class_install_property (gobject_class, PROP_PROPS,
609 +      g_param_spec_pointer ("props", "Properties", "The properties struct",
610 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
611 +
612 +  GST_DEBUG_CATEGORY_INIT (pw_audio_ring_buffer_debug, "pwaudioringbuffer", 0,
613 +      "PipeWire Audio Ring Buffer");
614 +}
615 diff --git a/src/gst/gstpwaudioringbuffer.h b/src/gst/gstpwaudioringbuffer.h
616 new file mode 100644
617 index 00000000..f47f668a
618 --- /dev/null
619 +++ b/src/gst/gstpwaudioringbuffer.h
620 @@ -0,0 +1,83 @@
621 +/* PipeWire
622 + *
623 + * Copyright © 2018 Wim Taymans
624 + * Copyright © 2019 Collabora Ltd.
625 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
626 + *
627 + * Permission is hereby granted, free of charge, to any person obtaining a
628 + * copy of this software and associated documentation files (the "Software"),
629 + * to deal in the Software without restriction, including without limitation
630 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
631 + * and/or sell copies of the Software, and to permit persons to whom the
632 + * Software is furnished to do so, subject to the following conditions:
633 + *
634 + * The above copyright notice and this permission notice (including the next
635 + * paragraph) shall be included in all copies or substantial portions of the
636 + * Software.
637 + *
638 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
639 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
640 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
641 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
642 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
643 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
644 + * DEALINGS IN THE SOFTWARE.
645 + */
646 +
647 +#ifndef __GST_PW_AUDIO_RING_BUFFER_H__
648 +#define __GST_PW_AUDIO_RING_BUFFER_H__
649 +
650 +#include <gst/gst.h>
651 +#include <gst/audio/audio.h>
652 +#include <pipewire/pipewire.h>
653 +
654 +G_BEGIN_DECLS
655 +
656 +#define GST_TYPE_PW_AUDIO_RING_BUFFER \
657 +    (gst_pw_audio_ring_buffer_get_type ())
658 +
659 +G_DECLARE_FINAL_TYPE(GstPwAudioRingBuffer, gst_pw_audio_ring_buffer,
660 +                     GST, PW_AUDIO_RING_BUFFER, GstAudioRingBuffer);
661 +
662 +typedef struct _GstPwAudioRingBufferProps GstPwAudioRingBufferProps;
663 +
664 +struct _GstPwAudioRingBuffer
665 +{
666 +  GstAudioRingBuffer parent;
667 +
668 +  /* properties */
669 +  GstElement *elem;
670 +  enum pw_direction direction;
671 +  GstPwAudioRingBufferProps *props;
672 +
673 +  /* internal */
674 +  struct pw_loop *loop;
675 +  struct pw_thread_loop *main_loop;
676 +
677 +  struct pw_core *core;
678 +  struct pw_remote *remote;
679 +  struct spa_hook remote_listener;
680 +
681 +  struct pw_stream *stream;
682 +  struct spa_hook stream_listener;
683 +
684 +  gint segsize;
685 +  gint bpf;
686 +  gint rate;
687 +
688 +  /* on_stream_process() state */
689 +  gint segoffset;
690 +  gint cur_segment;
691 +};
692 +
693 +struct _GstPwAudioRingBufferProps
694 +{
695 +  gchar *path;
696 +  gchar *client_name;
697 +  GstStructure *properties;
698 +  int fd;
699 +};
700 +
701 +G_END_DECLS
702 +
703 +#endif
704 diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c
705 new file mode 100644
706 index 00000000..6cb71385
707 --- /dev/null
708 +++ b/src/gst/gstpwaudiosink.c
709 @@ -0,0 +1,200 @@
710 +/* PipeWire
711 + *
712 + * Copyright © 2018 Wim Taymans
713 + * Copyright © 2019 Collabora Ltd.
714 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
715 + *
716 + * Permission is hereby granted, free of charge, to any person obtaining a
717 + * copy of this software and associated documentation files (the "Software"),
718 + * to deal in the Software without restriction, including without limitation
719 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
720 + * and/or sell copies of the Software, and to permit persons to whom the
721 + * Software is furnished to do so, subject to the following conditions:
722 + *
723 + * The above copyright notice and this permission notice (including the next
724 + * paragraph) shall be included in all copies or substantial portions of the
725 + * Software.
726 + *
727 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
728 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
729 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
730 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
731 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
732 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
733 + * DEALINGS IN THE SOFTWARE.
734 + */
735 +
736 +#ifdef HAVE_CONFIG_H
737 +#include "config.h"
738 +#endif
739 +
740 +#include "gstpwaudiosink.h"
741 +
742 +GST_DEBUG_CATEGORY_STATIC (pw_audio_sink_debug);
743 +#define GST_CAT_DEFAULT pw_audio_sink_debug
744 +
745 +G_DEFINE_TYPE (GstPwAudioSink, gst_pw_audio_sink, GST_TYPE_AUDIO_BASE_SINK);
746 +
747 +enum
748 +{
749 +  PROP_0,
750 +  PROP_PATH,
751 +  PROP_CLIENT_NAME,
752 +  PROP_STREAM_PROPERTIES,
753 +  PROP_FD
754 +};
755 +
756 +static GstStaticPadTemplate gst_pw_audio_sink_template =
757 +GST_STATIC_PAD_TEMPLATE ("sink",
758 +    GST_PAD_SINK,
759 +    GST_PAD_ALWAYS,
760 +    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32))
761 +                     ", layout = (string)\"interleaved\"")
762 +);
763 +
764 +
765 +static void
766 +gst_pw_audio_sink_init (GstPwAudioSink * self)
767 +{
768 +  self->props.fd = -1;
769 +}
770 +
771 +static void
772 +gst_pw_audio_sink_finalize (GObject * object)
773 +{
774 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
775 +
776 +  g_free (pwsink->props.path);
777 +  g_free (pwsink->props.client_name);
778 +  if (pwsink->props.properties)
779 +    gst_structure_free (pwsink->props.properties);
780 +}
781 +
782 +static void
783 +gst_pw_audio_sink_set_property (GObject * object, guint prop_id,
784 +    const GValue * value, GParamSpec * pspec)
785 +{
786 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
787 +
788 +  switch (prop_id) {
789 +    case PROP_PATH:
790 +      g_free (pwsink->props.path);
791 +      pwsink->props.path = g_value_dup_string (value);
792 +      break;
793 +
794 +    case PROP_CLIENT_NAME:
795 +      g_free (pwsink->props.client_name);
796 +      pwsink->props.client_name = g_value_dup_string (value);
797 +      break;
798 +
799 +    case PROP_STREAM_PROPERTIES:
800 +      if (pwsink->props.properties)
801 +        gst_structure_free (pwsink->props.properties);
802 +      pwsink->props.properties =
803 +          gst_structure_copy (gst_value_get_structure (value));
804 +      break;
805 +
806 +    case PROP_FD:
807 +      pwsink->props.fd = g_value_get_int (value);
808 +      break;
809 +
810 +    default:
811 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
812 +      break;
813 +  }
814 +}
815 +
816 +static void
817 +gst_pw_audio_sink_get_property (GObject * object, guint prop_id,
818 +    GValue * value, GParamSpec * pspec)
819 +{
820 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
821 +
822 +  switch (prop_id) {
823 +    case PROP_PATH:
824 +      g_value_set_string (value, pwsink->props.path);
825 +      break;
826 +
827 +    case PROP_CLIENT_NAME:
828 +      g_value_set_string (value, pwsink->props.client_name);
829 +      break;
830 +
831 +    case PROP_STREAM_PROPERTIES:
832 +      gst_value_set_structure (value, pwsink->props.properties);
833 +      break;
834 +
835 +    case PROP_FD:
836 +      g_value_set_int (value, pwsink->props.fd);
837 +      break;
838 +
839 +    default:
840 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
841 +      break;
842 +  }
843 +}
844 +
845 +static GstAudioRingBuffer *
846 +gst_pw_audio_sink_create_ringbuffer (GstAudioBaseSink * sink)
847 +{
848 +  GstPwAudioSink *self = GST_PW_AUDIO_SINK (sink);
849 +  GstAudioRingBuffer *buffer;
850 +
851 +  GST_DEBUG_OBJECT (sink, "creating ringbuffer");
852 +  buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER,
853 +      "element", sink,
854 +      "direction", PW_DIRECTION_OUTPUT,
855 +      "props", &self->props,
856 +      NULL);
857 +  GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer);
858 +
859 +  return buffer;
860 +}
861 +
862 +static void
863 +gst_pw_audio_sink_class_init (GstPwAudioSinkClass * klass)
864 +{
865 +  GObjectClass *gobject_class;
866 +  GstElementClass *gstelement_class;
867 +  GstAudioBaseSinkClass *gstaudiobsink_class;
868 +
869 +  gobject_class = (GObjectClass *) klass;
870 +  gstelement_class = (GstElementClass *) klass;
871 +  gstaudiobsink_class = (GstAudioBaseSinkClass *) klass;
872 +
873 +  gobject_class->finalize = gst_pw_audio_sink_finalize;
874 +  gobject_class->set_property = gst_pw_audio_sink_set_property;
875 +  gobject_class->get_property = gst_pw_audio_sink_get_property;
876 +
877 +  gstaudiobsink_class->create_ringbuffer = gst_pw_audio_sink_create_ringbuffer;
878 +
879 +  g_object_class_install_property (gobject_class, PROP_PATH,
880 +      g_param_spec_string ("path", "Path",
881 +          "The sink path to connect to (NULL = default)", NULL,
882 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
883 +
884 +  g_object_class_install_property (gobject_class, PROP_CLIENT_NAME,
885 +      g_param_spec_string ("client-name", "Client Name",
886 +          "The client name to use (NULL = default)", NULL,
887 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
888 +
889 +   g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES,
890 +      g_param_spec_boxed ("stream-properties", "Stream properties",
891 +          "List of PipeWire stream properties", GST_TYPE_STRUCTURE,
892 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
893 +
894 +   g_object_class_install_property (gobject_class, PROP_FD,
895 +      g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1,
896 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
897 +
898 +  gst_element_class_set_static_metadata (gstelement_class,
899 +      "PipeWire Audio sink", "Sink/Audio",
900 +      "Send audio to PipeWire",
901 +      "George Kiagiadakis <george.kiagiadakis@collabora.com>");
902 +
903 +  gst_element_class_add_pad_template (gstelement_class,
904 +      gst_static_pad_template_get (&gst_pw_audio_sink_template));
905 +
906 +  GST_DEBUG_CATEGORY_INIT (pw_audio_sink_debug, "pwaudiosink", 0,
907 +      "PipeWire Audio Sink");
908 +}
909 +
910 diff --git a/src/gst/gstpwaudiosink.h b/src/gst/gstpwaudiosink.h
911 new file mode 100644
912 index 00000000..7ed0de7b
913 --- /dev/null
914 +++ b/src/gst/gstpwaudiosink.h
915 @@ -0,0 +1,48 @@
916 +/* PipeWire
917 + *
918 + * Copyright © 2018 Wim Taymans
919 + * Copyright © 2019 Collabora Ltd.
920 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
921 + *
922 + * Permission is hereby granted, free of charge, to any person obtaining a
923 + * copy of this software and associated documentation files (the "Software"),
924 + * to deal in the Software without restriction, including without limitation
925 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
926 + * and/or sell copies of the Software, and to permit persons to whom the
927 + * Software is furnished to do so, subject to the following conditions:
928 + *
929 + * The above copyright notice and this permission notice (including the next
930 + * paragraph) shall be included in all copies or substantial portions of the
931 + * Software.
932 + *
933 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
934 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
935 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
936 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
937 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
938 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
939 + * DEALINGS IN THE SOFTWARE.
940 + */
941 +
942 +#ifndef __GST_PW_AUDIO_SINK_H__
943 +#define __GST_PW_AUDIO_SINK_H__
944 +
945 +#include "gstpwaudioringbuffer.h"
946 +
947 +G_BEGIN_DECLS
948 +
949 +#define GST_TYPE_PW_AUDIO_SINK \
950 +    (gst_pw_audio_sink_get_type ())
951 +
952 +G_DECLARE_FINAL_TYPE(GstPwAudioSink, gst_pw_audio_sink,
953 +                     GST, PW_AUDIO_SINK, GstAudioBaseSink);
954 +
955 +struct _GstPwAudioSink
956 +{
957 +  GstAudioBaseSink parent;
958 +  GstPwAudioRingBufferProps props;
959 +};
960 +
961 +G_END_DECLS
962 +
963 +#endif
964 diff --git a/src/gst/gstpwaudiosrc.c b/src/gst/gstpwaudiosrc.c
965 new file mode 100644
966 index 00000000..6c522982
967 --- /dev/null
968 +++ b/src/gst/gstpwaudiosrc.c
969 @@ -0,0 +1,200 @@
970 +/* PipeWire
971 + *
972 + * Copyright © 2018 Wim Taymans
973 + * Copyright © 2019 Collabora Ltd.
974 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
975 + *
976 + * Permission is hereby granted, free of charge, to any person obtaining a
977 + * copy of this software and associated documentation files (the "Software"),
978 + * to deal in the Software without restriction, including without limitation
979 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
980 + * and/or sell copies of the Software, and to permit persons to whom the
981 + * Software is furnished to do so, subject to the following conditions:
982 + *
983 + * The above copyright notice and this permission notice (including the next
984 + * paragraph) shall be included in all copies or substantial portions of the
985 + * Software.
986 + *
987 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
988 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
989 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
990 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
991 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
992 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
993 + * DEALINGS IN THE SOFTWARE.
994 + */
995 +
996 +#ifdef HAVE_CONFIG_H
997 +#include "config.h"
998 +#endif
999 +
1000 +#include "gstpwaudiosrc.h"
1001 +
1002 +GST_DEBUG_CATEGORY_STATIC (pw_audio_src_debug);
1003 +#define GST_CAT_DEFAULT pw_audio_src_debug
1004 +
1005 +G_DEFINE_TYPE (GstPwAudioSrc, gst_pw_audio_src, GST_TYPE_AUDIO_BASE_SRC);
1006 +
1007 +enum
1008 +{
1009 +  PROP_0,
1010 +  PROP_PATH,
1011 +  PROP_CLIENT_NAME,
1012 +  PROP_STREAM_PROPERTIES,
1013 +  PROP_FD
1014 +};
1015 +
1016 +static GstStaticPadTemplate gst_pw_audio_src_template =
1017 +GST_STATIC_PAD_TEMPLATE ("src",
1018 +    GST_PAD_SRC,
1019 +    GST_PAD_ALWAYS,
1020 +    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32))
1021 +                     ", layout = (string)\"interleaved\"")
1022 +);
1023 +
1024 +
1025 +static void
1026 +gst_pw_audio_src_init (GstPwAudioSrc * self)
1027 +{
1028 +  self->props.fd = -1;
1029 +}
1030 +
1031 +static void
1032 +gst_pw_audio_src_finalize (GObject * object)
1033 +{
1034 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1035 +
1036 +  g_free (self->props.path);
1037 +  g_free (self->props.client_name);
1038 +  if (self->props.properties)
1039 +    gst_structure_free (self->props.properties);
1040 +}
1041 +
1042 +static void
1043 +gst_pw_audio_src_set_property (GObject * object, guint prop_id,
1044 +    const GValue * value, GParamSpec * pspec)
1045 +{
1046 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1047 +
1048 +  switch (prop_id) {
1049 +    case PROP_PATH:
1050 +      g_free (self->props.path);
1051 +      self->props.path = g_value_dup_string (value);
1052 +      break;
1053 +
1054 +    case PROP_CLIENT_NAME:
1055 +      g_free (self->props.client_name);
1056 +      self->props.client_name = g_value_dup_string (value);
1057 +      break;
1058 +
1059 +    case PROP_STREAM_PROPERTIES:
1060 +      if (self->props.properties)
1061 +        gst_structure_free (self->props.properties);
1062 +      self->props.properties =
1063 +          gst_structure_copy (gst_value_get_structure (value));
1064 +      break;
1065 +
1066 +    case PROP_FD:
1067 +      self->props.fd = g_value_get_int (value);
1068 +      break;
1069 +
1070 +    default:
1071 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1072 +      break;
1073 +  }
1074 +}
1075 +
1076 +static void
1077 +gst_pw_audio_src_get_property (GObject * object, guint prop_id,
1078 +    GValue * value, GParamSpec * pspec)
1079 +{
1080 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1081 +
1082 +  switch (prop_id) {
1083 +    case PROP_PATH:
1084 +      g_value_set_string (value, self->props.path);
1085 +      break;
1086 +
1087 +    case PROP_CLIENT_NAME:
1088 +      g_value_set_string (value, self->props.client_name);
1089 +      break;
1090 +
1091 +    case PROP_STREAM_PROPERTIES:
1092 +      gst_value_set_structure (value, self->props.properties);
1093 +      break;
1094 +
1095 +    case PROP_FD:
1096 +      g_value_set_int (value, self->props.fd);
1097 +      break;
1098 +
1099 +    default:
1100 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1101 +      break;
1102 +  }
1103 +}
1104 +
1105 +static GstAudioRingBuffer *
1106 +gst_pw_audio_src_create_ringbuffer (GstAudioBaseSrc * sink)
1107 +{
1108 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (sink);
1109 +  GstAudioRingBuffer *buffer;
1110 +
1111 +  GST_DEBUG_OBJECT (sink, "creating ringbuffer");
1112 +  buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER,
1113 +      "element", sink,
1114 +      "direction", PW_DIRECTION_INPUT,
1115 +      "props", &self->props,
1116 +      NULL);
1117 +  GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer);
1118 +
1119 +  return buffer;
1120 +}
1121 +
1122 +static void
1123 +gst_pw_audio_src_class_init (GstPwAudioSrcClass * klass)
1124 +{
1125 +  GObjectClass *gobject_class;
1126 +  GstElementClass *gstelement_class;
1127 +  GstAudioBaseSrcClass *gstaudiobsrc_class;
1128 +
1129 +  gobject_class = (GObjectClass *) klass;
1130 +  gstelement_class = (GstElementClass *) klass;
1131 +  gstaudiobsrc_class = (GstAudioBaseSrcClass *) klass;
1132 +
1133 +  gobject_class->finalize = gst_pw_audio_src_finalize;
1134 +  gobject_class->set_property = gst_pw_audio_src_set_property;
1135 +  gobject_class->get_property = gst_pw_audio_src_get_property;
1136 +
1137 +  gstaudiobsrc_class->create_ringbuffer = gst_pw_audio_src_create_ringbuffer;
1138 +
1139 +  g_object_class_install_property (gobject_class, PROP_PATH,
1140 +      g_param_spec_string ("path", "Path",
1141 +          "The sink path to connect to (NULL = default)", NULL,
1142 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1143 +
1144 +  g_object_class_install_property (gobject_class, PROP_CLIENT_NAME,
1145 +      g_param_spec_string ("client-name", "Client Name",
1146 +          "The client name to use (NULL = default)", NULL,
1147 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1148 +
1149 +   g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES,
1150 +      g_param_spec_boxed ("stream-properties", "Stream properties",
1151 +          "List of PipeWire stream properties", GST_TYPE_STRUCTURE,
1152 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1153 +
1154 +   g_object_class_install_property (gobject_class, PROP_FD,
1155 +      g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1,
1156 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1157 +
1158 +  gst_element_class_set_static_metadata (gstelement_class,
1159 +      "PipeWire Audio source", "Source/Audio",
1160 +      "Receive audio from PipeWire",
1161 +      "George Kiagiadakis <george.kiagiadakis@collabora.com>");
1162 +
1163 +  gst_element_class_add_pad_template (gstelement_class,
1164 +      gst_static_pad_template_get (&gst_pw_audio_src_template));
1165 +
1166 +  GST_DEBUG_CATEGORY_INIT (pw_audio_src_debug, "pwaudiosrc", 0,
1167 +      "PipeWire Audio Src");
1168 +}
1169 +
1170 diff --git a/src/gst/gstpwaudiosrc.h b/src/gst/gstpwaudiosrc.h
1171 new file mode 100644
1172 index 00000000..c46e644c
1173 --- /dev/null
1174 +++ b/src/gst/gstpwaudiosrc.h
1175 @@ -0,0 +1,48 @@
1176 +/* PipeWire
1177 + *
1178 + * Copyright © 2018 Wim Taymans
1179 + * Copyright © 2019 Collabora Ltd.
1180 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
1181 + *
1182 + * Permission is hereby granted, free of charge, to any person obtaining a
1183 + * copy of this software and associated documentation files (the "Software"),
1184 + * to deal in the Software without restriction, including without limitation
1185 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1186 + * and/or sell copies of the Software, and to permit persons to whom the
1187 + * Software is furnished to do so, subject to the following conditions:
1188 + *
1189 + * The above copyright notice and this permission notice (including the next
1190 + * paragraph) shall be included in all copies or substantial portions of the
1191 + * Software.
1192 + *
1193 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1194 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1195 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1196 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1197 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1198 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1199 + * DEALINGS IN THE SOFTWARE.
1200 + */
1201 +
1202 +#ifndef __GST_PW_AUDIO_SRC_H__
1203 +#define __GST_PW_AUDIO_SRC_H__
1204 +
1205 +#include "gstpwaudioringbuffer.h"
1206 +
1207 +G_BEGIN_DECLS
1208 +
1209 +#define GST_TYPE_PW_AUDIO_SRC \
1210 +    (gst_pw_audio_src_get_type ())
1211 +
1212 +G_DECLARE_FINAL_TYPE(GstPwAudioSrc, gst_pw_audio_src,
1213 +                     GST, PW_AUDIO_SRC, GstAudioBaseSrc);
1214 +
1215 +struct _GstPwAudioSrc
1216 +{
1217 +  GstAudioBaseSrc parent;
1218 +  GstPwAudioRingBufferProps props;
1219 +};
1220 +
1221 +G_END_DECLS
1222 +
1223 +#endif
1224 diff --git a/src/gst/meson.build b/src/gst/meson.build
1225 index ad0e0801..0e922347 100644
1226 --- a/src/gst/meson.build
1227 +++ b/src/gst/meson.build
1228 @@ -6,6 +6,9 @@ pipewire_gst_sources = [
1229    'gstpipewirepool.c',
1230    'gstpipewiresink.c',
1231    'gstpipewiresrc.c',
1232 +  'gstpwaudioringbuffer.c',
1233 +  'gstpwaudiosink.c',
1234 +  'gstpwaudiosrc.c',
1235  ]
1236  
1237  pipewire_gst_headers = [
1238 @@ -15,6 +18,9 @@ pipewire_gst_headers = [
1239    'gstpipewirepool.h',
1240    'gstpipewiresink.h',
1241    'gstpipewiresrc.h',
1242 +  'gstpwaudioringbuffer.h',
1243 +  'gstpwaudiosink.h',
1244 +  'gstpwaudiosrc.h',
1245  ]
1246  
1247  pipewire_gst_c_args = [
1248 -- 
1249 2.24.0
1250