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