meta-agl-core: remove IMAGE_FSTYPES override
[AGL/meta-agl.git] / meta-pipewire / recipes-multimedia / pipewire / pipewire / 0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch
1 From 1b1f884a165ed7b2147affbdddf85a641d4cf180 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 | 565 +++++++++++++++++++++++++++++++++
22  src/gst/gstpwaudioringbuffer.h |  83 +++++
23  src/gst/gstpwaudiosink.c       | 207 ++++++++++++
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, 1164 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..babf2d83
70 --- /dev/null
71 +++ b/src/gst/gstpwaudioringbuffer.c
72 @@ -0,0 +1,565 @@
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 +  GstMessage *msg;
278 +
279 +  GST_DEBUG_OBJECT (self->elem, "got stream state: %s",
280 +      pw_stream_state_as_string (state));
281 +
282 +  switch (state) {
283 +    case PW_STREAM_STATE_ERROR:
284 +      GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
285 +          ("stream error: %s", error), (NULL));
286 +      break;
287 +    case PW_STREAM_STATE_UNCONNECTED:
288 +      GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED,
289 +          ("stream disconnected unexpectedly"), (NULL));
290 +      break;
291 +    case PW_STREAM_STATE_CONNECTING:
292 +      break;
293 +    case PW_STREAM_STATE_PAUSED:
294 +      if (old == PW_STREAM_STATE_STREAMING) {
295 +        if (GST_STATE (self->elem) != GST_STATE_PAUSED &&
296 +            GST_STATE_TARGET (self->elem) != GST_STATE_PAUSED) {
297 +          GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PAUSED");
298 +          msg = gst_message_new_request_state (GST_OBJECT (self->elem),
299 +              GST_STATE_PAUSED);
300 +          gst_element_post_message (self->elem, msg);
301 +        }
302 +      }
303 +      break;
304 +    case PW_STREAM_STATE_STREAMING:
305 +      if (GST_STATE (self->elem) != GST_STATE_PLAYING &&
306 +          GST_STATE_TARGET (self->elem) != GST_STATE_PLAYING) {
307 +        GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PLAYING");
308 +        msg = gst_message_new_request_state (GST_OBJECT (self->elem),
309 +            GST_STATE_PLAYING);
310 +        gst_element_post_message (self->elem, msg);
311 +      }
312 +      break;
313 +  }
314 +  pw_thread_loop_signal (self->main_loop, FALSE);
315 +}
316 +
317 +static gboolean
318 +wait_for_stream_state (GstPwAudioRingBuffer *self,
319 +    enum pw_stream_state target)
320 +{
321 +  while (TRUE) {
322 +    enum pw_stream_state state = pw_stream_get_state (self->stream, NULL);
323 +    if (state >= target)
324 +      return TRUE;
325 +    if (state == PW_STREAM_STATE_ERROR || state == PW_STREAM_STATE_UNCONNECTED)
326 +      return FALSE;
327 +    pw_thread_loop_wait (self->main_loop);
328 +  }
329 +}
330 +
331 +static void
332 +on_stream_param_changed (void *data, uint32_t id, const struct spa_pod *format)
333 +{
334 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
335 +  const struct spa_pod *params[1];
336 +  struct spa_pod_builder b = { NULL };
337 +  uint8_t buffer[512];
338 +
339 +  if (format == NULL || id != SPA_PARAM_Format)
340 +    return;
341 +
342 +  spa_pod_builder_init (&b, buffer, sizeof (buffer));
343 +  params[0] = spa_pod_builder_add_object (&b,
344 +      SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
345 +      SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 1, INT32_MAX),
346 +      SPA_PARAM_BUFFERS_blocks,  SPA_POD_Int(1),
347 +      SPA_PARAM_BUFFERS_size,    SPA_POD_Int(self->segsize),
348 +      SPA_PARAM_BUFFERS_stride,  SPA_POD_Int(self->bpf),
349 +      SPA_PARAM_BUFFERS_align,   SPA_POD_Int(16));
350 +
351 +  GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize);
352 +  pw_stream_update_params (self->stream, params, 1);
353 +}
354 +
355 +static void
356 +on_stream_process (void *data)
357 +{
358 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data);
359 +  GstAudioRingBuffer *buf = GST_AUDIO_RING_BUFFER (data);
360 +  struct pw_buffer *b;
361 +  struct spa_data *d;
362 +  gint size;       /*< size to read/write from/to the spa buffer */
363 +  gint offset;     /*< offset to read/write from/to in the spa buffer */
364 +  gint segment;    /*< the current segment number in the ringbuffer */
365 +  guint8 *ringptr; /*< pointer to the beginning of the current segment */
366 +  gint segsize;    /*< the size of one segment in the ringbuffer */
367 +  gint copy_size;  /*< the bytes to copy in one memcpy() invocation */
368 +  gint remain;     /*< remainder of bytes available in the spa buffer */
369 +
370 +  if (g_atomic_int_get (&buf->state) != GST_AUDIO_RING_BUFFER_STATE_STARTED) {
371 +    GST_LOG_OBJECT (self->elem, "ring buffer is not started");
372 +    return;
373 +  }
374 +
375 +  b = pw_stream_dequeue_buffer (self->stream);
376 +  if (!b) {
377 +    GST_INFO_OBJECT (self->elem, "no pipewire buffer available");
378 +    return;
379 +  }
380 +
381 +  d = &b->buffer->datas[0];
382 +
383 +  if (self->direction == PW_DIRECTION_OUTPUT) {
384 +    /* in output mode, always fill the entire spa buffer */
385 +    offset = d->chunk->offset = 0;
386 +    size = d->chunk->size = d->maxsize;
387 +    b->size = size / self->bpf;
388 +  } else {
389 +    offset = SPA_MIN (d->chunk->offset, d->maxsize);
390 +    size = SPA_MIN (d->chunk->size, d->maxsize - offset);
391 +  }
392 +
393 +  do {
394 +    gst_audio_ring_buffer_prepare_read (buf, &segment, &ringptr, &segsize);
395 +
396 +    /* in INPUT (src) mode, it is possible that the skew algorithm
397 +     * advances the ringbuffer behind our back */
398 +    if (self->segoffset > 0 && self->cur_segment != segment)
399 +      self->segoffset = 0;
400 +
401 +    copy_size = SPA_MIN (size, segsize - self->segoffset);
402 +
403 +    if (self->direction == PW_DIRECTION_OUTPUT) {
404 +      memcpy (((guint8*) d->data) + offset, ringptr + self->segoffset,
405 +          copy_size);
406 +    } else {
407 +      memcpy (ringptr + self->segoffset, ((guint8*) d->data) + offset,
408 +          copy_size);
409 +    }
410 +
411 +    remain = size - (segsize - self->segoffset);
412 +
413 +    GST_TRACE_OBJECT (self->elem,
414 +        "seg %d: %s %d bytes remained:%d offset:%d segoffset:%d", segment,
415 +        self->direction == PW_DIRECTION_INPUT ? "INPUT" : "OUTPUT",
416 +        copy_size, remain, offset, self->segoffset);
417 +
418 +    if (remain >= 0) {
419 +      offset += (segsize - self->segoffset);
420 +      size = remain;
421 +
422 +      /* write silence on the segment we just read */
423 +      if (self->direction == PW_DIRECTION_OUTPUT)
424 +        gst_audio_ring_buffer_clear (buf, segment);
425 +
426 +      /* notify that we have read a complete segment */
427 +      gst_audio_ring_buffer_advance (buf, 1);
428 +      self->segoffset = 0;
429 +    } else {
430 +      self->segoffset += size;
431 +      self->cur_segment = segment;
432 +    }
433 +  } while (remain > 0);
434 +
435 +  pw_stream_queue_buffer (self->stream, b);
436 +}
437 +
438 +static const struct pw_stream_events stream_events = {
439 +  PW_VERSION_STREAM_EVENTS,
440 +  .state_changed = on_stream_state_changed,
441 +  .param_changed = on_stream_param_changed,
442 +  .process = on_stream_process,
443 +};
444 +
445 +static gboolean
446 +copy_properties (GQuark field_id, const GValue *value, gpointer user_data)
447 +{
448 +  struct pw_properties *properties = user_data;
449 +
450 +  if (G_VALUE_HOLDS_STRING (value))
451 +    pw_properties_set (properties,
452 +                       g_quark_to_string (field_id),
453 +                       g_value_get_string (value));
454 +  return TRUE;
455 +}
456 +
457 +static gboolean
458 +gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf,
459 +    GstAudioRingBufferSpec *spec)
460 +{
461 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
462 +  struct pw_properties *props;
463 +  struct spa_pod_builder b = { NULL };
464 +  uint8_t buffer[512];
465 +  const struct spa_pod *params[1];
466 +
467 +  g_return_val_if_fail (spec, FALSE);
468 +  g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&spec->info), FALSE);
469 +  g_return_val_if_fail (!self->stream, TRUE); /* already acquired */
470 +
471 +  g_return_val_if_fail (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW, FALSE);
472 +  g_return_val_if_fail (GST_AUDIO_INFO_IS_FLOAT (&spec->info), FALSE);
473 +
474 +  GST_DEBUG_OBJECT (self->elem, "acquire");
475 +
476 +  /* construct param & props objects */
477 +
478 +  props = pw_properties_new (NULL, NULL);
479 +  if (self->props->properties) {
480 +    gst_structure_foreach (self->props->properties, copy_properties, props);
481 +  }
482 +
483 +  spa_pod_builder_init (&b, buffer, sizeof (buffer));
484 +  params[0] = spa_pod_builder_add_object (&b,
485 +      SPA_TYPE_OBJECT_Format,    SPA_PARAM_EnumFormat,
486 +      SPA_FORMAT_mediaType,      SPA_POD_Id (SPA_MEDIA_TYPE_audio),
487 +      SPA_FORMAT_mediaSubtype,   SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw),
488 +      SPA_FORMAT_AUDIO_format,   SPA_POD_Id (SPA_AUDIO_FORMAT_F32),
489 +      SPA_FORMAT_AUDIO_rate,     SPA_POD_Int (GST_AUDIO_INFO_RATE (&spec->info)),
490 +      SPA_FORMAT_AUDIO_channels, SPA_POD_Int (GST_AUDIO_INFO_CHANNELS (&spec->info)));
491 +
492 +  self->segsize = spec->segsize;
493 +  self->bpf = GST_AUDIO_INFO_BPF (&spec->info);
494 +  self->rate = GST_AUDIO_INFO_RATE (&spec->info);
495 +  self->segoffset = 0;
496 +
497 +  pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u",
498 +      self->segsize / self->bpf, self->rate);
499 +  GST_DEBUG_OBJECT (self->elem, "segsize:%u, bpf:%u, node.latency = %s",
500 +      self->segsize, self->bpf, pw_properties_get (props, PW_KEY_NODE_LATENCY));
501 +
502 +  /* connect stream */
503 +
504 +  pw_thread_loop_lock (self->main_loop);
505 +
506 +  GST_DEBUG_OBJECT (self->elem, "creating stream");
507 +
508 +  self->stream = pw_stream_new (self->remote, self->props->client_name, props);
509 +  pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events,
510 +      self);
511 +
512 +  if (pw_stream_connect (self->stream,
513 +          self->direction,
514 +          self->props->path ? (uint32_t)atoi(self->props->path) : SPA_ID_INVALID,
515 +          PW_STREAM_FLAG_AUTOCONNECT |
516 +          PW_STREAM_FLAG_MAP_BUFFERS |
517 +          PW_STREAM_FLAG_RT_PROCESS,
518 +          params, 1) < 0)
519 +    goto start_error;
520 +
521 +  GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE");
522 +
523 +  if (!wait_for_stream_state (self, PW_STREAM_STATE_PAUSED))
524 +    goto start_error;
525 +
526 +  pw_thread_loop_unlock (self->main_loop);
527 +
528 +  /* allocate the internal ringbuffer */
529 +
530 +  spec->seglatency = spec->segtotal + 1;
531 +  buf->size = spec->segtotal * spec->segsize;
532 +  buf->memory = g_malloc (buf->size);
533 +
534 +  gst_audio_format_fill_silence (buf->spec.info.finfo, buf->memory,
535 +      buf->size);
536 +
537 +  GST_DEBUG_OBJECT (self->elem, "acquire done");
538 +
539 +  return TRUE;
540 +
541 +start_error:
542 +  {
543 +    GST_ERROR_OBJECT (self->elem, "could not start stream");
544 +    pw_stream_destroy (self->stream);
545 +    self->stream = NULL;
546 +    pw_thread_loop_unlock (self->main_loop);
547 +    return FALSE;
548 +  }
549 +}
550 +
551 +static gboolean
552 +gst_pw_audio_ring_buffer_release (GstAudioRingBuffer *buf)
553 +{
554 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
555 +
556 +  GST_DEBUG_OBJECT (self->elem, "release");
557 +
558 +  pw_thread_loop_lock (self->main_loop);
559 +  if (self->stream) {
560 +    spa_hook_remove (&self->stream_listener);
561 +    pw_stream_disconnect (self->stream);
562 +    pw_stream_destroy (self->stream);
563 +    self->stream = NULL;
564 +  }
565 +  pw_thread_loop_unlock (self->main_loop);
566 +
567 +  /* free the buffer */
568 +  g_free (buf->memory);
569 +  buf->memory = NULL;
570 +
571 +  return TRUE;
572 +}
573 +
574 +static guint
575 +gst_pw_audio_ring_buffer_delay (GstAudioRingBuffer *buf)
576 +{
577 +  GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf);
578 +  struct pw_time t;
579 +
580 +  if (!self->stream || pw_stream_get_time (self->stream, &t) < 0)
581 +    return 0;
582 +
583 +  if (self->direction == PW_DIRECTION_OUTPUT) {
584 +    /* on output streams, we set the pw_buffer.size in frames,
585 +       so no conversion is necessary */
586 +    return t.queued;
587 +  } else {
588 +    /* on input streams, pw_buffer.size is set by pw_stream in ticks,
589 +       so we need to convert it to frames and also add segoffset, which
590 +       is the number of bytes we have read but not advertised yet, as
591 +       the segment is incomplete */
592 +    if (t.rate.denom > 0)
593 +      return
594 +        gst_util_uint64_scale (t.queued, self->rate * t.rate.num, t.rate.denom)
595 +        + self->segoffset / self->bpf;
596 +    else
597 +      return self->segoffset / self->bpf;
598 +  }
599 +
600 +  return 0;
601 +}
602 +
603 +static void
604 +gst_pw_audio_ring_buffer_class_init (GstPwAudioRingBufferClass * klass)
605 +{
606 +  GObjectClass *gobject_class;
607 +  GstAudioRingBufferClass *gstaudiorbuf_class;
608 +
609 +  gobject_class = (GObjectClass *) klass;
610 +  gstaudiorbuf_class = (GstAudioRingBufferClass *) klass;
611 +
612 +  gobject_class->finalize = gst_pw_audio_ring_buffer_finalize;
613 +  gobject_class->set_property = gst_pw_audio_ring_buffer_set_property;
614 +
615 +  gstaudiorbuf_class->open_device = gst_pw_audio_ring_buffer_open_device;
616 +  gstaudiorbuf_class->acquire = gst_pw_audio_ring_buffer_acquire;
617 +  gstaudiorbuf_class->release = gst_pw_audio_ring_buffer_release;
618 +  gstaudiorbuf_class->close_device = gst_pw_audio_ring_buffer_close_device;
619 +  gstaudiorbuf_class->delay = gst_pw_audio_ring_buffer_delay;
620 +
621 +  g_object_class_install_property (gobject_class, PROP_ELEMENT,
622 +      g_param_spec_object ("element", "Element", "The audio source or sink",
623 +          GST_TYPE_ELEMENT,
624 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
625 +
626 +  g_object_class_install_property (gobject_class, PROP_DIRECTION,
627 +      g_param_spec_int ("direction", "Direction", "The stream direction",
628 +          PW_DIRECTION_INPUT, PW_DIRECTION_OUTPUT, PW_DIRECTION_INPUT,
629 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
630 +
631 +  g_object_class_install_property (gobject_class, PROP_PROPS,
632 +      g_param_spec_pointer ("props", "Properties", "The properties struct",
633 +          G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
634 +
635 +  GST_DEBUG_CATEGORY_INIT (pw_audio_ring_buffer_debug, "pwaudioringbuffer", 0,
636 +      "PipeWire Audio Ring Buffer");
637 +}
638 diff --git a/src/gst/gstpwaudioringbuffer.h b/src/gst/gstpwaudioringbuffer.h
639 new file mode 100644
640 index 00000000..f47f668a
641 --- /dev/null
642 +++ b/src/gst/gstpwaudioringbuffer.h
643 @@ -0,0 +1,83 @@
644 +/* PipeWire
645 + *
646 + * Copyright © 2018 Wim Taymans
647 + * Copyright © 2019 Collabora Ltd.
648 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
649 + *
650 + * Permission is hereby granted, free of charge, to any person obtaining a
651 + * copy of this software and associated documentation files (the "Software"),
652 + * to deal in the Software without restriction, including without limitation
653 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
654 + * and/or sell copies of the Software, and to permit persons to whom the
655 + * Software is furnished to do so, subject to the following conditions:
656 + *
657 + * The above copyright notice and this permission notice (including the next
658 + * paragraph) shall be included in all copies or substantial portions of the
659 + * Software.
660 + *
661 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
662 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
663 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
664 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
665 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
666 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
667 + * DEALINGS IN THE SOFTWARE.
668 + */
669 +
670 +#ifndef __GST_PW_AUDIO_RING_BUFFER_H__
671 +#define __GST_PW_AUDIO_RING_BUFFER_H__
672 +
673 +#include <gst/gst.h>
674 +#include <gst/audio/audio.h>
675 +#include <pipewire/pipewire.h>
676 +
677 +G_BEGIN_DECLS
678 +
679 +#define GST_TYPE_PW_AUDIO_RING_BUFFER \
680 +    (gst_pw_audio_ring_buffer_get_type ())
681 +
682 +G_DECLARE_FINAL_TYPE(GstPwAudioRingBuffer, gst_pw_audio_ring_buffer,
683 +                     GST, PW_AUDIO_RING_BUFFER, GstAudioRingBuffer);
684 +
685 +typedef struct _GstPwAudioRingBufferProps GstPwAudioRingBufferProps;
686 +
687 +struct _GstPwAudioRingBuffer
688 +{
689 +  GstAudioRingBuffer parent;
690 +
691 +  /* properties */
692 +  GstElement *elem;
693 +  enum pw_direction direction;
694 +  GstPwAudioRingBufferProps *props;
695 +
696 +  /* internal */
697 +  struct pw_loop *loop;
698 +  struct pw_thread_loop *main_loop;
699 +
700 +  struct pw_core *core;
701 +  struct pw_remote *remote;
702 +  struct spa_hook remote_listener;
703 +
704 +  struct pw_stream *stream;
705 +  struct spa_hook stream_listener;
706 +
707 +  gint segsize;
708 +  gint bpf;
709 +  gint rate;
710 +
711 +  /* on_stream_process() state */
712 +  gint segoffset;
713 +  gint cur_segment;
714 +};
715 +
716 +struct _GstPwAudioRingBufferProps
717 +{
718 +  gchar *path;
719 +  gchar *client_name;
720 +  GstStructure *properties;
721 +  int fd;
722 +};
723 +
724 +G_END_DECLS
725 +
726 +#endif
727 diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c
728 new file mode 100644
729 index 00000000..069996c3
730 --- /dev/null
731 +++ b/src/gst/gstpwaudiosink.c
732 @@ -0,0 +1,207 @@
733 +/* PipeWire
734 + *
735 + * Copyright © 2018 Wim Taymans
736 + * Copyright © 2019 Collabora Ltd.
737 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
738 + *
739 + * Permission is hereby granted, free of charge, to any person obtaining a
740 + * copy of this software and associated documentation files (the "Software"),
741 + * to deal in the Software without restriction, including without limitation
742 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
743 + * and/or sell copies of the Software, and to permit persons to whom the
744 + * Software is furnished to do so, subject to the following conditions:
745 + *
746 + * The above copyright notice and this permission notice (including the next
747 + * paragraph) shall be included in all copies or substantial portions of the
748 + * Software.
749 + *
750 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
751 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
752 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
753 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
754 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
755 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
756 + * DEALINGS IN THE SOFTWARE.
757 + */
758 +
759 +#ifdef HAVE_CONFIG_H
760 +#include "config.h"
761 +#endif
762 +
763 +#include "gstpwaudiosink.h"
764 +
765 +GST_DEBUG_CATEGORY_STATIC (pw_audio_sink_debug);
766 +#define GST_CAT_DEFAULT pw_audio_sink_debug
767 +
768 +G_DEFINE_TYPE (GstPwAudioSink, gst_pw_audio_sink, GST_TYPE_AUDIO_BASE_SINK);
769 +
770 +enum
771 +{
772 +  PROP_0,
773 +  PROP_PATH,
774 +  PROP_CLIENT_NAME,
775 +  PROP_STREAM_PROPERTIES,
776 +  PROP_FD
777 +};
778 +
779 +static GstStaticPadTemplate gst_pw_audio_sink_template =
780 +GST_STATIC_PAD_TEMPLATE ("sink",
781 +    GST_PAD_SINK,
782 +    GST_PAD_ALWAYS,
783 +    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32))
784 +                     ", layout = (string)\"interleaved\"")
785 +);
786 +
787 +
788 +static void
789 +gst_pw_audio_sink_init (GstPwAudioSink * self)
790 +{
791 +  self->props.fd = -1;
792 +
793 +  /* Bump the default buffer size up to 21.3 ms, which is the default on most
794 +   * sound cards, in hope to match the alsa buffer size on the pipewire server.
795 +   * This may not always happen, but it still sounds better than the 10ms
796 +   * default latency. This is temporary until we have a better mechanism to
797 +   * select the appropriate latency */
798 +  GST_AUDIO_BASE_SINK (self)->latency_time = 21333;
799 +}
800 +
801 +static void
802 +gst_pw_audio_sink_finalize (GObject * object)
803 +{
804 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
805 +
806 +  g_free (pwsink->props.path);
807 +  g_free (pwsink->props.client_name);
808 +  if (pwsink->props.properties)
809 +    gst_structure_free (pwsink->props.properties);
810 +}
811 +
812 +static void
813 +gst_pw_audio_sink_set_property (GObject * object, guint prop_id,
814 +    const GValue * value, GParamSpec * pspec)
815 +{
816 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
817 +
818 +  switch (prop_id) {
819 +    case PROP_PATH:
820 +      g_free (pwsink->props.path);
821 +      pwsink->props.path = g_value_dup_string (value);
822 +      break;
823 +
824 +    case PROP_CLIENT_NAME:
825 +      g_free (pwsink->props.client_name);
826 +      pwsink->props.client_name = g_value_dup_string (value);
827 +      break;
828 +
829 +    case PROP_STREAM_PROPERTIES:
830 +      if (pwsink->props.properties)
831 +        gst_structure_free (pwsink->props.properties);
832 +      pwsink->props.properties =
833 +          gst_structure_copy (gst_value_get_structure (value));
834 +      break;
835 +
836 +    case PROP_FD:
837 +      pwsink->props.fd = g_value_get_int (value);
838 +      break;
839 +
840 +    default:
841 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
842 +      break;
843 +  }
844 +}
845 +
846 +static void
847 +gst_pw_audio_sink_get_property (GObject * object, guint prop_id,
848 +    GValue * value, GParamSpec * pspec)
849 +{
850 +  GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object);
851 +
852 +  switch (prop_id) {
853 +    case PROP_PATH:
854 +      g_value_set_string (value, pwsink->props.path);
855 +      break;
856 +
857 +    case PROP_CLIENT_NAME:
858 +      g_value_set_string (value, pwsink->props.client_name);
859 +      break;
860 +
861 +    case PROP_STREAM_PROPERTIES:
862 +      gst_value_set_structure (value, pwsink->props.properties);
863 +      break;
864 +
865 +    case PROP_FD:
866 +      g_value_set_int (value, pwsink->props.fd);
867 +      break;
868 +
869 +    default:
870 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
871 +      break;
872 +  }
873 +}
874 +
875 +static GstAudioRingBuffer *
876 +gst_pw_audio_sink_create_ringbuffer (GstAudioBaseSink * sink)
877 +{
878 +  GstPwAudioSink *self = GST_PW_AUDIO_SINK (sink);
879 +  GstAudioRingBuffer *buffer;
880 +
881 +  GST_DEBUG_OBJECT (sink, "creating ringbuffer");
882 +  buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER,
883 +      "element", sink,
884 +      "direction", PW_DIRECTION_OUTPUT,
885 +      "props", &self->props,
886 +      NULL);
887 +  GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer);
888 +
889 +  return buffer;
890 +}
891 +
892 +static void
893 +gst_pw_audio_sink_class_init (GstPwAudioSinkClass * klass)
894 +{
895 +  GObjectClass *gobject_class;
896 +  GstElementClass *gstelement_class;
897 +  GstAudioBaseSinkClass *gstaudiobsink_class;
898 +
899 +  gobject_class = (GObjectClass *) klass;
900 +  gstelement_class = (GstElementClass *) klass;
901 +  gstaudiobsink_class = (GstAudioBaseSinkClass *) klass;
902 +
903 +  gobject_class->finalize = gst_pw_audio_sink_finalize;
904 +  gobject_class->set_property = gst_pw_audio_sink_set_property;
905 +  gobject_class->get_property = gst_pw_audio_sink_get_property;
906 +
907 +  gstaudiobsink_class->create_ringbuffer = gst_pw_audio_sink_create_ringbuffer;
908 +
909 +  g_object_class_install_property (gobject_class, PROP_PATH,
910 +      g_param_spec_string ("path", "Path",
911 +          "The sink path to connect to (NULL = default)", NULL,
912 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
913 +
914 +  g_object_class_install_property (gobject_class, PROP_CLIENT_NAME,
915 +      g_param_spec_string ("client-name", "Client Name",
916 +          "The client name to use (NULL = default)", NULL,
917 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
918 +
919 +   g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES,
920 +      g_param_spec_boxed ("stream-properties", "Stream properties",
921 +          "List of PipeWire stream properties", GST_TYPE_STRUCTURE,
922 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
923 +
924 +   g_object_class_install_property (gobject_class, PROP_FD,
925 +      g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1,
926 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
927 +
928 +  gst_element_class_set_static_metadata (gstelement_class,
929 +      "PipeWire Audio sink", "Sink/Audio",
930 +      "Send audio to PipeWire",
931 +      "George Kiagiadakis <george.kiagiadakis@collabora.com>");
932 +
933 +  gst_element_class_add_pad_template (gstelement_class,
934 +      gst_static_pad_template_get (&gst_pw_audio_sink_template));
935 +
936 +  GST_DEBUG_CATEGORY_INIT (pw_audio_sink_debug, "pwaudiosink", 0,
937 +      "PipeWire Audio Sink");
938 +}
939 +
940 diff --git a/src/gst/gstpwaudiosink.h b/src/gst/gstpwaudiosink.h
941 new file mode 100644
942 index 00000000..7ed0de7b
943 --- /dev/null
944 +++ b/src/gst/gstpwaudiosink.h
945 @@ -0,0 +1,48 @@
946 +/* PipeWire
947 + *
948 + * Copyright © 2018 Wim Taymans
949 + * Copyright © 2019 Collabora Ltd.
950 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
951 + *
952 + * Permission is hereby granted, free of charge, to any person obtaining a
953 + * copy of this software and associated documentation files (the "Software"),
954 + * to deal in the Software without restriction, including without limitation
955 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
956 + * and/or sell copies of the Software, and to permit persons to whom the
957 + * Software is furnished to do so, subject to the following conditions:
958 + *
959 + * The above copyright notice and this permission notice (including the next
960 + * paragraph) shall be included in all copies or substantial portions of the
961 + * Software.
962 + *
963 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
964 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
965 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
966 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
967 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
968 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
969 + * DEALINGS IN THE SOFTWARE.
970 + */
971 +
972 +#ifndef __GST_PW_AUDIO_SINK_H__
973 +#define __GST_PW_AUDIO_SINK_H__
974 +
975 +#include "gstpwaudioringbuffer.h"
976 +
977 +G_BEGIN_DECLS
978 +
979 +#define GST_TYPE_PW_AUDIO_SINK \
980 +    (gst_pw_audio_sink_get_type ())
981 +
982 +G_DECLARE_FINAL_TYPE(GstPwAudioSink, gst_pw_audio_sink,
983 +                     GST, PW_AUDIO_SINK, GstAudioBaseSink);
984 +
985 +struct _GstPwAudioSink
986 +{
987 +  GstAudioBaseSink parent;
988 +  GstPwAudioRingBufferProps props;
989 +};
990 +
991 +G_END_DECLS
992 +
993 +#endif
994 diff --git a/src/gst/gstpwaudiosrc.c b/src/gst/gstpwaudiosrc.c
995 new file mode 100644
996 index 00000000..6c522982
997 --- /dev/null
998 +++ b/src/gst/gstpwaudiosrc.c
999 @@ -0,0 +1,200 @@
1000 +/* PipeWire
1001 + *
1002 + * Copyright © 2018 Wim Taymans
1003 + * Copyright © 2019 Collabora Ltd.
1004 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
1005 + *
1006 + * Permission is hereby granted, free of charge, to any person obtaining a
1007 + * copy of this software and associated documentation files (the "Software"),
1008 + * to deal in the Software without restriction, including without limitation
1009 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1010 + * and/or sell copies of the Software, and to permit persons to whom the
1011 + * Software is furnished to do so, subject to the following conditions:
1012 + *
1013 + * The above copyright notice and this permission notice (including the next
1014 + * paragraph) shall be included in all copies or substantial portions of the
1015 + * Software.
1016 + *
1017 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1018 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1019 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1020 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1021 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1022 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1023 + * DEALINGS IN THE SOFTWARE.
1024 + */
1025 +
1026 +#ifdef HAVE_CONFIG_H
1027 +#include "config.h"
1028 +#endif
1029 +
1030 +#include "gstpwaudiosrc.h"
1031 +
1032 +GST_DEBUG_CATEGORY_STATIC (pw_audio_src_debug);
1033 +#define GST_CAT_DEFAULT pw_audio_src_debug
1034 +
1035 +G_DEFINE_TYPE (GstPwAudioSrc, gst_pw_audio_src, GST_TYPE_AUDIO_BASE_SRC);
1036 +
1037 +enum
1038 +{
1039 +  PROP_0,
1040 +  PROP_PATH,
1041 +  PROP_CLIENT_NAME,
1042 +  PROP_STREAM_PROPERTIES,
1043 +  PROP_FD
1044 +};
1045 +
1046 +static GstStaticPadTemplate gst_pw_audio_src_template =
1047 +GST_STATIC_PAD_TEMPLATE ("src",
1048 +    GST_PAD_SRC,
1049 +    GST_PAD_ALWAYS,
1050 +    GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32))
1051 +                     ", layout = (string)\"interleaved\"")
1052 +);
1053 +
1054 +
1055 +static void
1056 +gst_pw_audio_src_init (GstPwAudioSrc * self)
1057 +{
1058 +  self->props.fd = -1;
1059 +}
1060 +
1061 +static void
1062 +gst_pw_audio_src_finalize (GObject * object)
1063 +{
1064 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1065 +
1066 +  g_free (self->props.path);
1067 +  g_free (self->props.client_name);
1068 +  if (self->props.properties)
1069 +    gst_structure_free (self->props.properties);
1070 +}
1071 +
1072 +static void
1073 +gst_pw_audio_src_set_property (GObject * object, guint prop_id,
1074 +    const GValue * value, GParamSpec * pspec)
1075 +{
1076 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1077 +
1078 +  switch (prop_id) {
1079 +    case PROP_PATH:
1080 +      g_free (self->props.path);
1081 +      self->props.path = g_value_dup_string (value);
1082 +      break;
1083 +
1084 +    case PROP_CLIENT_NAME:
1085 +      g_free (self->props.client_name);
1086 +      self->props.client_name = g_value_dup_string (value);
1087 +      break;
1088 +
1089 +    case PROP_STREAM_PROPERTIES:
1090 +      if (self->props.properties)
1091 +        gst_structure_free (self->props.properties);
1092 +      self->props.properties =
1093 +          gst_structure_copy (gst_value_get_structure (value));
1094 +      break;
1095 +
1096 +    case PROP_FD:
1097 +      self->props.fd = g_value_get_int (value);
1098 +      break;
1099 +
1100 +    default:
1101 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1102 +      break;
1103 +  }
1104 +}
1105 +
1106 +static void
1107 +gst_pw_audio_src_get_property (GObject * object, guint prop_id,
1108 +    GValue * value, GParamSpec * pspec)
1109 +{
1110 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object);
1111 +
1112 +  switch (prop_id) {
1113 +    case PROP_PATH:
1114 +      g_value_set_string (value, self->props.path);
1115 +      break;
1116 +
1117 +    case PROP_CLIENT_NAME:
1118 +      g_value_set_string (value, self->props.client_name);
1119 +      break;
1120 +
1121 +    case PROP_STREAM_PROPERTIES:
1122 +      gst_value_set_structure (value, self->props.properties);
1123 +      break;
1124 +
1125 +    case PROP_FD:
1126 +      g_value_set_int (value, self->props.fd);
1127 +      break;
1128 +
1129 +    default:
1130 +      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1131 +      break;
1132 +  }
1133 +}
1134 +
1135 +static GstAudioRingBuffer *
1136 +gst_pw_audio_src_create_ringbuffer (GstAudioBaseSrc * sink)
1137 +{
1138 +  GstPwAudioSrc *self = GST_PW_AUDIO_SRC (sink);
1139 +  GstAudioRingBuffer *buffer;
1140 +
1141 +  GST_DEBUG_OBJECT (sink, "creating ringbuffer");
1142 +  buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER,
1143 +      "element", sink,
1144 +      "direction", PW_DIRECTION_INPUT,
1145 +      "props", &self->props,
1146 +      NULL);
1147 +  GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer);
1148 +
1149 +  return buffer;
1150 +}
1151 +
1152 +static void
1153 +gst_pw_audio_src_class_init (GstPwAudioSrcClass * klass)
1154 +{
1155 +  GObjectClass *gobject_class;
1156 +  GstElementClass *gstelement_class;
1157 +  GstAudioBaseSrcClass *gstaudiobsrc_class;
1158 +
1159 +  gobject_class = (GObjectClass *) klass;
1160 +  gstelement_class = (GstElementClass *) klass;
1161 +  gstaudiobsrc_class = (GstAudioBaseSrcClass *) klass;
1162 +
1163 +  gobject_class->finalize = gst_pw_audio_src_finalize;
1164 +  gobject_class->set_property = gst_pw_audio_src_set_property;
1165 +  gobject_class->get_property = gst_pw_audio_src_get_property;
1166 +
1167 +  gstaudiobsrc_class->create_ringbuffer = gst_pw_audio_src_create_ringbuffer;
1168 +
1169 +  g_object_class_install_property (gobject_class, PROP_PATH,
1170 +      g_param_spec_string ("path", "Path",
1171 +          "The sink path to connect to (NULL = default)", NULL,
1172 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1173 +
1174 +  g_object_class_install_property (gobject_class, PROP_CLIENT_NAME,
1175 +      g_param_spec_string ("client-name", "Client Name",
1176 +          "The client name to use (NULL = default)", NULL,
1177 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1178 +
1179 +   g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES,
1180 +      g_param_spec_boxed ("stream-properties", "Stream properties",
1181 +          "List of PipeWire stream properties", GST_TYPE_STRUCTURE,
1182 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1183 +
1184 +   g_object_class_install_property (gobject_class, PROP_FD,
1185 +      g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1,
1186 +          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1187 +
1188 +  gst_element_class_set_static_metadata (gstelement_class,
1189 +      "PipeWire Audio source", "Source/Audio",
1190 +      "Receive audio from PipeWire",
1191 +      "George Kiagiadakis <george.kiagiadakis@collabora.com>");
1192 +
1193 +  gst_element_class_add_pad_template (gstelement_class,
1194 +      gst_static_pad_template_get (&gst_pw_audio_src_template));
1195 +
1196 +  GST_DEBUG_CATEGORY_INIT (pw_audio_src_debug, "pwaudiosrc", 0,
1197 +      "PipeWire Audio Src");
1198 +}
1199 +
1200 diff --git a/src/gst/gstpwaudiosrc.h b/src/gst/gstpwaudiosrc.h
1201 new file mode 100644
1202 index 00000000..c46e644c
1203 --- /dev/null
1204 +++ b/src/gst/gstpwaudiosrc.h
1205 @@ -0,0 +1,48 @@
1206 +/* PipeWire
1207 + *
1208 + * Copyright © 2018 Wim Taymans
1209 + * Copyright © 2019 Collabora Ltd.
1210 + *   @author George Kiagiadakis <george.kiagiadakis@collabora.com>
1211 + *
1212 + * Permission is hereby granted, free of charge, to any person obtaining a
1213 + * copy of this software and associated documentation files (the "Software"),
1214 + * to deal in the Software without restriction, including without limitation
1215 + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
1216 + * and/or sell copies of the Software, and to permit persons to whom the
1217 + * Software is furnished to do so, subject to the following conditions:
1218 + *
1219 + * The above copyright notice and this permission notice (including the next
1220 + * paragraph) shall be included in all copies or substantial portions of the
1221 + * Software.
1222 + *
1223 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1224 + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1225 + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
1226 + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1227 + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1228 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
1229 + * DEALINGS IN THE SOFTWARE.
1230 + */
1231 +
1232 +#ifndef __GST_PW_AUDIO_SRC_H__
1233 +#define __GST_PW_AUDIO_SRC_H__
1234 +
1235 +#include "gstpwaudioringbuffer.h"
1236 +
1237 +G_BEGIN_DECLS
1238 +
1239 +#define GST_TYPE_PW_AUDIO_SRC \
1240 +    (gst_pw_audio_src_get_type ())
1241 +
1242 +G_DECLARE_FINAL_TYPE(GstPwAudioSrc, gst_pw_audio_src,
1243 +                     GST, PW_AUDIO_SRC, GstAudioBaseSrc);
1244 +
1245 +struct _GstPwAudioSrc
1246 +{
1247 +  GstAudioBaseSrc parent;
1248 +  GstPwAudioRingBufferProps props;
1249 +};
1250 +
1251 +G_END_DECLS
1252 +
1253 +#endif
1254 diff --git a/src/gst/meson.build b/src/gst/meson.build
1255 index ad0e0801..0e922347 100644
1256 --- a/src/gst/meson.build
1257 +++ b/src/gst/meson.build
1258 @@ -6,6 +6,9 @@ pipewire_gst_sources = [
1259    'gstpipewirepool.c',
1260    'gstpipewiresink.c',
1261    'gstpipewiresrc.c',
1262 +  'gstpwaudioringbuffer.c',
1263 +  'gstpwaudiosink.c',
1264 +  'gstpwaudiosrc.c',
1265  ]
1266  
1267  pipewire_gst_headers = [
1268 @@ -15,6 +18,9 @@ pipewire_gst_headers = [
1269    'gstpipewirepool.h',
1270    'gstpipewiresink.h',
1271    'gstpipewiresrc.h',
1272 +  'gstpwaudioringbuffer.h',
1273 +  'gstpwaudiosink.h',
1274 +  'gstpwaudiosrc.h',
1275  ]
1276  
1277  pipewire_gst_c_args = [
1278 -- 
1279 2.24.0
1280