diff options
author | George Kiagiadakis <george.kiagiadakis@collabora.com> | 2021-02-11 14:13:55 +0200 |
---|---|---|
committer | Jan-Simon Moeller <jsmoeller@linuxfoundation.org> | 2021-04-26 14:21:03 +0000 |
commit | 761e6467d12e2935785774383adca9ddbd3e1c26 (patch) | |
tree | 24c48f24a83004afcbee78ff33e73aaa4e3f2683 /meta-pipewire/recipes-multimedia/pipewire | |
parent | 69dbc40c36265760a27094b960ef9faebda3a08d (diff) |
meta-pipewire: update to pipewire 0.3.25 and wireplumber master
Bug-AGL: SPEC-3844
Change-Id: Ie32bfa43bf078c7d218d3150dc616501b8848bd0
Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
Reviewed-on: https://gerrit.automotivelinux.org/gerrit/c/AGL/meta-agl/+/26094
Reviewed-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
Tested-by: Jan-Simon Moeller <jsmoeller@linuxfoundation.org>
Diffstat (limited to 'meta-pipewire/recipes-multimedia/pipewire')
13 files changed, 289 insertions, 1818 deletions
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc deleted file mode 100644 index b3081ca43..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc +++ /dev/null @@ -1,120 +0,0 @@ -SUMMARY = "Multimedia processing server for Linux" -HOMEPAGE = "https://pipewire.org" -BUGTRACKER = "https://github.com/PipeWire/pipewire/issues" -AUTHOR = "Wim Taymans <wtaymans@redhat.com>" -SECTION = "multimedia" - -LICENSE = "MIT & LGPL-2.1" -LIC_FILES_CHKSUM = "\ - file://COPYING;beginline=3;md5=b3adc775ca6ee80056383a5ae814cc75 \ - file://pipewire-alsa/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ - file://pipewire-jack/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ - file://pipewire-pulseaudio/LICENSE;md5=fc178bcd425090939a8b634d1d6a9594 \ -" - -inherit meson pkgconfig systemd manpages - -DEPENDS = "dbus" - -PACKAGECONFIG ??= "\ - ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ - ${@bb.utils.filter('DISTRO_FEATURES', 'bluez5', d)} \ - alsa audioconvert audiomixer \ - pipewire-alsa \ - gstreamer \ -" - -GST_VER = "1.0" - -# systemd integration -PACKAGECONFIG[systemd] = "-Dsystemd=true,-Dsystemd=false,systemd" - -# SPA plugins -PACKAGECONFIG[alsa] = "-Dalsa=true,-Dalsa=false,udev alsa-lib" -PACKAGECONFIG[audioconvert] = "-Daudioconvert=true,-Daudioconvert=false," -PACKAGECONFIG[audiomixer] = "-Daudiomixer=true,-Daudiomixer=false," -PACKAGECONFIG[audiotestsrc] = "-Daudiotestsrc=true,-Daudiotestsrc=false, " -PACKAGECONFIG[bluez5] = "-Dbluez5=true,-Dbluez5=false,bluez5 sbc" -PACKAGECONFIG[jack] = "-Djack=true,-Djack=false,jack" -PACKAGECONFIG[v4l2] = "-Dv4l2=true,-Dv4l2=false,udev v4l-utils" -PACKAGECONFIG[videotestsrc] = "-Dvideotestsrc=true,-Dvideotestsrc=false, " -PACKAGECONFIG[vulkan] = "-Dvulkan=true,-Dvulkan=false,vulkan" - -# alsa plugin to redirect audio to pipewire -PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=true,-Dpipewire-alsa=false,alsa-lib" -# pulseaudio drop-in replacement library -PACKAGECONFIG[pipewire-pulseaudio] = "-Dpipewire-pulseaudio=true,-Dpipewire-pulseaudio=false,pulseaudio glib-2.0" -# jack drop-in replacement library -PACKAGECONFIG[pipewire-jack] = "-Dpipewire-jack=true,-Dpipewire-jack=false,jack" - -# GStreamer plugins -PACKAGECONFIG[gstreamer] = "-Dgstreamer=true,-Dgstreamer=false,glib-2.0 gstreamer${GST_VER} gstreamer${GST_VER}-plugins-base" - -# man pages -PACKAGECONFIG[manpages] = "-Dman=true,-Dman=false,libxml-parser-perl-native" - -do_install_append() { - # only install the alsa config file if the alsa-lib plugin has been built - # this avoids creating the pipewire-alsa package when the pipewire-alsa - # feature is not enabled - if [ -d ${D}${libdir}/alsa-lib ] - then - mkdir -p ${D}${datadir}/alsa/alsa.conf.d - install -m 0644 ${S}/pipewire-alsa/conf/50-pipewire.conf ${D}${datadir}/alsa/alsa.conf.d/50-pipewire.conf - fi -} - -PACKAGES =+ "\ - ${PN}-spa-plugins \ - ${PN}-alsa \ - ${PN}-pulseaudio \ - ${PN}-jack \ - ${PN}-config \ - gstreamer${GST_VER}-${PN} \ - lib${PN} \ - lib${PN}-modules \ -" - -FILES_${PN} = "\ - ${bindir}/pipewire* \ - ${systemd_user_unitdir}/* \ -" - -FILES_lib${PN} = "\ - ${libdir}/libpipewire-*.so.* \ -" - -FILES_lib${PN}-modules = "\ - ${libdir}/pipewire-*/* \ -" - -FILES_${PN}-spa-plugins = "\ - ${bindir}/spa-* \ - ${libdir}/spa/* \ -" - -FILES_${PN}-alsa = "\ - ${libdir}/alsa-lib/* \ - ${datadir}/alsa/alsa.conf.d/50-pipewire.conf \ -" - -FILES_${PN}-pulseaudio = "\ - ${libdir}/libpulse*.so.* \ -" - -FILES_gstreamer${GST_VER}-${PN} = "\ - ${libdir}/gstreamer-${GST_VER}/* \ -" - -RDEPENDS_lib${PN} += "lib${PN}-modules ${PN}-spa-plugins" - -# The default pipewire config. -# Replace in your own package using -# "virtual/pipewire-config" -FILES_${PN}-config = "\ - ${sysconfdir}/pipewire/pipewire.conf \ -" -CONFFILES_${PN}-config += "\ - ${sysconfdir}/pipewire/pipewire.conf \ -" -RPROVIDES_${PN}-config += "virtual/pipewire-config" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch deleted file mode 100644 index 4e7bb0d4f..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 5a249321aa84cd74e3d83bcd555c85fba3cd682d Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Sun, 22 Sep 2019 17:59:19 +0300 -Subject: [PATCH] meson: revert version check to require meson 0.47, not 0.50 - -meson 0.50 is not really needed, but there are some strange warnings -if you require an older version; in any case, AGL does not have 0.50 -yet, so let's not fail compilation because of that... - -Upstream-Status: Inappropriate [workaround] ---- - meson.build | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/meson.build b/meson.build -index 2734b0d2..c9da6b4d 100644 ---- a/meson.build -+++ b/meson.build -@@ -1,7 +1,7 @@ - project('pipewire', ['c' ], - version : '0.2.9', - license : 'MIT', -- meson_version : '>= 0.50.0', -+ meson_version : '>= 0.47.0', - default_options : [ 'warning_level=1', - 'c_std=gnu99', - 'buildtype=debugoptimized' ]) --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch deleted file mode 100644 index 2077af63d..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch +++ /dev/null @@ -1,30 +0,0 @@ -From e4b81946baf2d8c08de87088c01a1d87ae4f03d9 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Mon, 24 Jun 2019 12:19:20 +0300 -Subject: [PATCH] arm: build with -mno-unaligned-access - -Upstream-Status: Inappropriate [workaround] -See also https://gitlab.freedesktop.org/pipewire/pipewire/issues/161 ---- - meson.build | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/meson.build b/meson.build -index c9da6b4d..5c121339 100644 ---- a/meson.build -+++ b/meson.build -@@ -52,6 +52,11 @@ if cc.get_id() == 'gcc' - language : 'c') - endif - -+if host_machine.cpu_family() == 'arm' -+ add_global_arguments('-mno-unaligned-access', -+ language: 'c') -+endif -+ - sse_args = '-msse' - sse2_args = '-msse2' - ssse3_args = '-mssse3' --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch deleted file mode 100644 index b3eba21f7..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch +++ /dev/null @@ -1,1280 +0,0 @@ -From 1b1f884a165ed7b2147affbdddf85a641d4cf180 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Tue, 19 Feb 2019 18:23:19 +0200 -Subject: [PATCH] gst: Implement new pwaudio{src,sink} elements, based on - GstAudioBase{Src,Sink} - -These are much more reliable elements to use for audio data. -* GstAudioBaseSink provides a reliable clock implementation based - on the number of samples read/written -* on the pipewire side we make sure to dequeue, fill and enqueue - a single buffer inside the process() function, which avoids - underruns - -Both elements share a common ringbuffer that actually implements -the pipewire integration. - -Upstream-Status: Denied -See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140 ---- - src/gst/gstpipewire.c | 8 +- - src/gst/gstpwaudioringbuffer.c | 565 +++++++++++++++++++++++++++++++++ - src/gst/gstpwaudioringbuffer.h | 83 +++++ - src/gst/gstpwaudiosink.c | 207 ++++++++++++ - src/gst/gstpwaudiosink.h | 48 +++ - src/gst/gstpwaudiosrc.c | 200 ++++++++++++ - src/gst/gstpwaudiosrc.h | 48 +++ - src/gst/meson.build | 6 + - 8 files changed, 1164 insertions(+), 1 deletion(-) - create mode 100644 src/gst/gstpwaudioringbuffer.c - create mode 100644 src/gst/gstpwaudioringbuffer.h - create mode 100644 src/gst/gstpwaudiosink.c - create mode 100644 src/gst/gstpwaudiosink.h - create mode 100644 src/gst/gstpwaudiosrc.c - create mode 100644 src/gst/gstpwaudiosrc.h - -diff --git a/src/gst/gstpipewire.c b/src/gst/gstpipewire.c -index 4040264b..68fd446f 100644 ---- a/src/gst/gstpipewire.c -+++ b/src/gst/gstpipewire.c -@@ -40,6 +40,8 @@ - #include "gstpipewiresrc.h" - #include "gstpipewiresink.h" - #include "gstpipewiredeviceprovider.h" -+#include "gstpwaudiosrc.h" -+#include "gstpwaudiosink.h" - - GST_DEBUG_CATEGORY (pipewire_debug); - -@@ -52,12 +54,16 @@ plugin_init (GstPlugin *plugin) - GST_TYPE_PIPEWIRE_SRC); - gst_element_register (plugin, "pipewiresink", GST_RANK_NONE, - GST_TYPE_PIPEWIRE_SINK); -+ gst_element_register (plugin, "pwaudiosrc", GST_RANK_NONE, -+ GST_TYPE_PW_AUDIO_SRC); -+ gst_element_register (plugin, "pwaudiosink", GST_RANK_NONE, -+ GST_TYPE_PW_AUDIO_SINK); - - if (!gst_device_provider_register (plugin, "pipewiredeviceprovider", - GST_RANK_PRIMARY + 1, GST_TYPE_PIPEWIRE_DEVICE_PROVIDER)) - return FALSE; - -- GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWirie elements"); -+ GST_DEBUG_CATEGORY_INIT (pipewire_debug, "pipewire", 0, "PipeWire elements"); - - return TRUE; - } -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -new file mode 100644 -index 00000000..babf2d83 ---- /dev/null -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -0,0 +1,565 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudioringbuffer.h" -+ -+#include <spa/param/audio/format-utils.h> -+#include <spa/pod/builder.h> -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_ring_buffer_debug); -+#define GST_CAT_DEFAULT pw_audio_ring_buffer_debug -+ -+#define gst_pw_audio_ring_buffer_parent_class parent_class -+G_DEFINE_TYPE (GstPwAudioRingBuffer, gst_pw_audio_ring_buffer, GST_TYPE_AUDIO_RING_BUFFER); -+ -+enum -+{ -+ PROP_0, -+ PROP_ELEMENT, -+ PROP_DIRECTION, -+ PROP_PROPS -+}; -+ -+static void -+gst_pw_audio_ring_buffer_init (GstPwAudioRingBuffer * self) -+{ -+ self->loop = pw_loop_new (NULL); -+ self->main_loop = pw_thread_loop_new (self->loop, "pw-audioringbuffer-loop"); -+ self->core = pw_core_new (self->loop, NULL, 0); -+} -+ -+static void -+gst_pw_audio_ring_buffer_finalize (GObject * object) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object); -+ -+ pw_core_destroy (self->core); -+ pw_thread_loop_destroy (self->main_loop); -+ pw_loop_destroy (self->loop); -+} -+ -+static void -+gst_pw_audio_ring_buffer_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (object); -+ -+ switch (prop_id) { -+ case PROP_ELEMENT: -+ self->elem = g_value_get_object (value); -+ break; -+ -+ case PROP_DIRECTION: -+ self->direction = g_value_get_int (value); -+ break; -+ -+ case PROP_PROPS: -+ self->props = g_value_get_pointer (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+on_remote_state_changed (void *data, enum pw_remote_state old, -+ enum pw_remote_state state, const char *error) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ -+ GST_DEBUG_OBJECT (self->elem, "got remote state %d", state); -+ -+ switch (state) { -+ case PW_REMOTE_STATE_UNCONNECTED: -+ case PW_REMOTE_STATE_CONNECTING: -+ case PW_REMOTE_STATE_CONNECTED: -+ break; -+ case PW_REMOTE_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("remote error: %s", error), (NULL)); -+ break; -+ } -+ pw_thread_loop_signal (self->main_loop, FALSE); -+} -+ -+static const struct pw_remote_events remote_events = { -+ PW_VERSION_REMOTE_EVENTS, -+ .state_changed = on_remote_state_changed, -+}; -+ -+static gboolean -+wait_for_remote_state (GstPwAudioRingBuffer *self, -+ enum pw_remote_state target) -+{ -+ while (TRUE) { -+ enum pw_remote_state state = pw_remote_get_state (self->remote, NULL); -+ if (state == target) -+ return TRUE; -+ if (state == PW_REMOTE_STATE_ERROR) -+ return FALSE; -+ pw_thread_loop_wait (self->main_loop); -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_open_device (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "open device"); -+ -+ if (pw_thread_loop_start (self->main_loop) < 0) -+ goto mainloop_error; -+ -+ pw_thread_loop_lock (self->main_loop); -+ -+ self->remote = pw_remote_new (self->core, NULL, 0); -+ pw_remote_add_listener (self->remote, &self->remote_listener, &remote_events, -+ self); -+ -+ if (self->props->fd == -1) -+ pw_remote_connect (self->remote); -+ else -+ pw_remote_connect_fd (self->remote, self->props->fd); -+ -+ GST_DEBUG_OBJECT (self->elem, "waiting for connection"); -+ -+ if (!wait_for_remote_state (self, PW_REMOTE_STATE_CONNECTED)) -+ goto connect_error; -+ -+ pw_thread_loop_unlock (self->main_loop); -+ -+ return TRUE; -+ -+ /* ERRORS */ -+mainloop_error: -+ { -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("Failed to start mainloop"), (NULL)); -+ return FALSE; -+ } -+connect_error: -+ { -+ pw_thread_loop_unlock (self->main_loop); -+ return FALSE; -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_close_device (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "closing device"); -+ -+ pw_thread_loop_lock (self->main_loop); -+ if (self->remote) { -+ pw_remote_disconnect (self->remote); -+ wait_for_remote_state (self, PW_REMOTE_STATE_UNCONNECTED); -+ } -+ pw_thread_loop_unlock (self->main_loop); -+ -+ pw_thread_loop_stop (self->main_loop); -+ -+ if (self->remote) { -+ pw_remote_destroy (self->remote); -+ self->remote = NULL; -+ } -+ return TRUE; -+} -+ -+static void -+on_stream_state_changed (void *data, enum pw_stream_state old, -+ enum pw_stream_state state, const char *error) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ GstMessage *msg; -+ -+ GST_DEBUG_OBJECT (self->elem, "got stream state: %s", -+ pw_stream_state_as_string (state)); -+ -+ switch (state) { -+ case PW_STREAM_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream error: %s", error), (NULL)); -+ break; -+ case PW_STREAM_STATE_UNCONNECTED: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream disconnected unexpectedly"), (NULL)); -+ break; -+ case PW_STREAM_STATE_CONNECTING: -+ break; -+ case PW_STREAM_STATE_PAUSED: -+ if (old == PW_STREAM_STATE_STREAMING) { -+ if (GST_STATE (self->elem) != GST_STATE_PAUSED && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PAUSED) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PAUSED"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PAUSED); -+ gst_element_post_message (self->elem, msg); -+ } -+ } -+ break; -+ case PW_STREAM_STATE_STREAMING: -+ if (GST_STATE (self->elem) != GST_STATE_PLAYING && -+ GST_STATE_TARGET (self->elem) != GST_STATE_PLAYING) { -+ GST_DEBUG_OBJECT (self->elem, "requesting GST_STATE_PLAYING"); -+ msg = gst_message_new_request_state (GST_OBJECT (self->elem), -+ GST_STATE_PLAYING); -+ gst_element_post_message (self->elem, msg); -+ } -+ break; -+ } -+ pw_thread_loop_signal (self->main_loop, FALSE); -+} -+ -+static gboolean -+wait_for_stream_state (GstPwAudioRingBuffer *self, -+ enum pw_stream_state target) -+{ -+ while (TRUE) { -+ enum pw_stream_state state = pw_stream_get_state (self->stream, NULL); -+ if (state >= target) -+ return TRUE; -+ if (state == PW_STREAM_STATE_ERROR || state == PW_STREAM_STATE_UNCONNECTED) -+ return FALSE; -+ pw_thread_loop_wait (self->main_loop); -+ } -+} -+ -+static void -+on_stream_param_changed (void *data, uint32_t id, const struct spa_pod *format) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ const struct spa_pod *params[1]; -+ struct spa_pod_builder b = { NULL }; -+ uint8_t buffer[512]; -+ -+ if (format == NULL || id != SPA_PARAM_Format) -+ return; -+ -+ spa_pod_builder_init (&b, buffer, sizeof (buffer)); -+ params[0] = spa_pod_builder_add_object (&b, -+ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, -+ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int(16, 1, INT32_MAX), -+ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int(1), -+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(self->segsize), -+ SPA_PARAM_BUFFERS_stride, SPA_POD_Int(self->bpf), -+ SPA_PARAM_BUFFERS_align, SPA_POD_Int(16)); -+ -+ GST_DEBUG_OBJECT (self->elem, "doing finish format, buffer size:%d", self->segsize); -+ pw_stream_update_params (self->stream, params, 1); -+} -+ -+static void -+on_stream_process (void *data) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (data); -+ GstAudioRingBuffer *buf = GST_AUDIO_RING_BUFFER (data); -+ struct pw_buffer *b; -+ struct spa_data *d; -+ gint size; /*< size to read/write from/to the spa buffer */ -+ gint offset; /*< offset to read/write from/to in the spa buffer */ -+ gint segment; /*< the current segment number in the ringbuffer */ -+ guint8 *ringptr; /*< pointer to the beginning of the current segment */ -+ gint segsize; /*< the size of one segment in the ringbuffer */ -+ gint copy_size; /*< the bytes to copy in one memcpy() invocation */ -+ gint remain; /*< remainder of bytes available in the spa buffer */ -+ -+ if (g_atomic_int_get (&buf->state) != GST_AUDIO_RING_BUFFER_STATE_STARTED) { -+ GST_LOG_OBJECT (self->elem, "ring buffer is not started"); -+ return; -+ } -+ -+ b = pw_stream_dequeue_buffer (self->stream); -+ if (!b) { -+ GST_INFO_OBJECT (self->elem, "no pipewire buffer available"); -+ return; -+ } -+ -+ d = &b->buffer->datas[0]; -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ /* in output mode, always fill the entire spa buffer */ -+ offset = d->chunk->offset = 0; -+ size = d->chunk->size = d->maxsize; -+ b->size = size / self->bpf; -+ } else { -+ offset = SPA_MIN (d->chunk->offset, d->maxsize); -+ size = SPA_MIN (d->chunk->size, d->maxsize - offset); -+ } -+ -+ do { -+ gst_audio_ring_buffer_prepare_read (buf, &segment, &ringptr, &segsize); -+ -+ /* in INPUT (src) mode, it is possible that the skew algorithm -+ * advances the ringbuffer behind our back */ -+ if (self->segoffset > 0 && self->cur_segment != segment) -+ self->segoffset = 0; -+ -+ copy_size = SPA_MIN (size, segsize - self->segoffset); -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ memcpy (((guint8*) d->data) + offset, ringptr + self->segoffset, -+ copy_size); -+ } else { -+ memcpy (ringptr + self->segoffset, ((guint8*) d->data) + offset, -+ copy_size); -+ } -+ -+ remain = size - (segsize - self->segoffset); -+ -+ GST_TRACE_OBJECT (self->elem, -+ "seg %d: %s %d bytes remained:%d offset:%d segoffset:%d", segment, -+ self->direction == PW_DIRECTION_INPUT ? "INPUT" : "OUTPUT", -+ copy_size, remain, offset, self->segoffset); -+ -+ if (remain >= 0) { -+ offset += (segsize - self->segoffset); -+ size = remain; -+ -+ /* write silence on the segment we just read */ -+ if (self->direction == PW_DIRECTION_OUTPUT) -+ gst_audio_ring_buffer_clear (buf, segment); -+ -+ /* notify that we have read a complete segment */ -+ gst_audio_ring_buffer_advance (buf, 1); -+ self->segoffset = 0; -+ } else { -+ self->segoffset += size; -+ self->cur_segment = segment; -+ } -+ } while (remain > 0); -+ -+ pw_stream_queue_buffer (self->stream, b); -+} -+ -+static const struct pw_stream_events stream_events = { -+ PW_VERSION_STREAM_EVENTS, -+ .state_changed = on_stream_state_changed, -+ .param_changed = on_stream_param_changed, -+ .process = on_stream_process, -+}; -+ -+static gboolean -+copy_properties (GQuark field_id, const GValue *value, gpointer user_data) -+{ -+ struct pw_properties *properties = user_data; -+ -+ if (G_VALUE_HOLDS_STRING (value)) -+ pw_properties_set (properties, -+ g_quark_to_string (field_id), -+ g_value_get_string (value)); -+ return TRUE; -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, -+ GstAudioRingBufferSpec *spec) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ struct pw_properties *props; -+ struct spa_pod_builder b = { NULL }; -+ uint8_t buffer[512]; -+ const struct spa_pod *params[1]; -+ -+ g_return_val_if_fail (spec, FALSE); -+ g_return_val_if_fail (GST_AUDIO_INFO_IS_VALID (&spec->info), FALSE); -+ g_return_val_if_fail (!self->stream, TRUE); /* already acquired */ -+ -+ g_return_val_if_fail (spec->type == GST_AUDIO_RING_BUFFER_FORMAT_TYPE_RAW, FALSE); -+ g_return_val_if_fail (GST_AUDIO_INFO_IS_FLOAT (&spec->info), FALSE); -+ -+ GST_DEBUG_OBJECT (self->elem, "acquire"); -+ -+ /* construct param & props objects */ -+ -+ props = pw_properties_new (NULL, NULL); -+ if (self->props->properties) { -+ gst_structure_foreach (self->props->properties, copy_properties, props); -+ } -+ -+ spa_pod_builder_init (&b, buffer, sizeof (buffer)); -+ params[0] = spa_pod_builder_add_object (&b, -+ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, -+ SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_audio), -+ SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw), -+ SPA_FORMAT_AUDIO_format, SPA_POD_Id (SPA_AUDIO_FORMAT_F32), -+ SPA_FORMAT_AUDIO_rate, SPA_POD_Int (GST_AUDIO_INFO_RATE (&spec->info)), -+ SPA_FORMAT_AUDIO_channels, SPA_POD_Int (GST_AUDIO_INFO_CHANNELS (&spec->info))); -+ -+ self->segsize = spec->segsize; -+ self->bpf = GST_AUDIO_INFO_BPF (&spec->info); -+ self->rate = GST_AUDIO_INFO_RATE (&spec->info); -+ self->segoffset = 0; -+ -+ pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%u/%u", -+ self->segsize / self->bpf, self->rate); -+ GST_DEBUG_OBJECT (self->elem, "segsize:%u, bpf:%u, node.latency = %s", -+ self->segsize, self->bpf, pw_properties_get (props, PW_KEY_NODE_LATENCY)); -+ -+ /* connect stream */ -+ -+ pw_thread_loop_lock (self->main_loop); -+ -+ GST_DEBUG_OBJECT (self->elem, "creating stream"); -+ -+ self->stream = pw_stream_new (self->remote, self->props->client_name, props); -+ pw_stream_add_listener(self->stream, &self->stream_listener, &stream_events, -+ self); -+ -+ if (pw_stream_connect (self->stream, -+ self->direction, -+ self->props->path ? (uint32_t)atoi(self->props->path) : SPA_ID_INVALID, -+ PW_STREAM_FLAG_AUTOCONNECT | -+ PW_STREAM_FLAG_MAP_BUFFERS | -+ PW_STREAM_FLAG_RT_PROCESS, -+ params, 1) < 0) -+ goto start_error; -+ -+ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); -+ -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_PAUSED)) -+ goto start_error; -+ -+ pw_thread_loop_unlock (self->main_loop); -+ -+ /* allocate the internal ringbuffer */ -+ -+ spec->seglatency = spec->segtotal + 1; -+ buf->size = spec->segtotal * spec->segsize; -+ buf->memory = g_malloc (buf->size); -+ -+ gst_audio_format_fill_silence (buf->spec.info.finfo, buf->memory, -+ buf->size); -+ -+ GST_DEBUG_OBJECT (self->elem, "acquire done"); -+ -+ return TRUE; -+ -+start_error: -+ { -+ GST_ERROR_OBJECT (self->elem, "could not start stream"); -+ pw_stream_destroy (self->stream); -+ self->stream = NULL; -+ pw_thread_loop_unlock (self->main_loop); -+ return FALSE; -+ } -+} -+ -+static gboolean -+gst_pw_audio_ring_buffer_release (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ -+ GST_DEBUG_OBJECT (self->elem, "release"); -+ -+ pw_thread_loop_lock (self->main_loop); -+ if (self->stream) { -+ spa_hook_remove (&self->stream_listener); -+ pw_stream_disconnect (self->stream); -+ pw_stream_destroy (self->stream); -+ self->stream = NULL; -+ } -+ pw_thread_loop_unlock (self->main_loop); -+ -+ /* free the buffer */ -+ g_free (buf->memory); -+ buf->memory = NULL; -+ -+ return TRUE; -+} -+ -+static guint -+gst_pw_audio_ring_buffer_delay (GstAudioRingBuffer *buf) -+{ -+ GstPwAudioRingBuffer *self = GST_PW_AUDIO_RING_BUFFER (buf); -+ struct pw_time t; -+ -+ if (!self->stream || pw_stream_get_time (self->stream, &t) < 0) -+ return 0; -+ -+ if (self->direction == PW_DIRECTION_OUTPUT) { -+ /* on output streams, we set the pw_buffer.size in frames, -+ so no conversion is necessary */ -+ return t.queued; -+ } else { -+ /* on input streams, pw_buffer.size is set by pw_stream in ticks, -+ so we need to convert it to frames and also add segoffset, which -+ is the number of bytes we have read but not advertised yet, as -+ the segment is incomplete */ -+ if (t.rate.denom > 0) -+ return -+ gst_util_uint64_scale (t.queued, self->rate * t.rate.num, t.rate.denom) -+ + self->segoffset / self->bpf; -+ else -+ return self->segoffset / self->bpf; -+ } -+ -+ return 0; -+} -+ -+static void -+gst_pw_audio_ring_buffer_class_init (GstPwAudioRingBufferClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstAudioRingBufferClass *gstaudiorbuf_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstaudiorbuf_class = (GstAudioRingBufferClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_ring_buffer_finalize; -+ gobject_class->set_property = gst_pw_audio_ring_buffer_set_property; -+ -+ gstaudiorbuf_class->open_device = gst_pw_audio_ring_buffer_open_device; -+ gstaudiorbuf_class->acquire = gst_pw_audio_ring_buffer_acquire; -+ gstaudiorbuf_class->release = gst_pw_audio_ring_buffer_release; -+ gstaudiorbuf_class->close_device = gst_pw_audio_ring_buffer_close_device; -+ gstaudiorbuf_class->delay = gst_pw_audio_ring_buffer_delay; -+ -+ g_object_class_install_property (gobject_class, PROP_ELEMENT, -+ g_param_spec_object ("element", "Element", "The audio source or sink", -+ GST_TYPE_ELEMENT, -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_DIRECTION, -+ g_param_spec_int ("direction", "Direction", "The stream direction", -+ PW_DIRECTION_INPUT, PW_DIRECTION_OUTPUT, PW_DIRECTION_INPUT, -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_PROPS, -+ g_param_spec_pointer ("props", "Properties", "The properties struct", -+ G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_ring_buffer_debug, "pwaudioringbuffer", 0, -+ "PipeWire Audio Ring Buffer"); -+} -diff --git a/src/gst/gstpwaudioringbuffer.h b/src/gst/gstpwaudioringbuffer.h -new file mode 100644 -index 00000000..f47f668a ---- /dev/null -+++ b/src/gst/gstpwaudioringbuffer.h -@@ -0,0 +1,83 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_RING_BUFFER_H__ -+#define __GST_PW_AUDIO_RING_BUFFER_H__ -+ -+#include <gst/gst.h> -+#include <gst/audio/audio.h> -+#include <pipewire/pipewire.h> -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_RING_BUFFER \ -+ (gst_pw_audio_ring_buffer_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioRingBuffer, gst_pw_audio_ring_buffer, -+ GST, PW_AUDIO_RING_BUFFER, GstAudioRingBuffer); -+ -+typedef struct _GstPwAudioRingBufferProps GstPwAudioRingBufferProps; -+ -+struct _GstPwAudioRingBuffer -+{ -+ GstAudioRingBuffer parent; -+ -+ /* properties */ -+ GstElement *elem; -+ enum pw_direction direction; -+ GstPwAudioRingBufferProps *props; -+ -+ /* internal */ -+ struct pw_loop *loop; -+ struct pw_thread_loop *main_loop; -+ -+ struct pw_core *core; -+ struct pw_remote *remote; -+ struct spa_hook remote_listener; -+ -+ struct pw_stream *stream; -+ struct spa_hook stream_listener; -+ -+ gint segsize; -+ gint bpf; -+ gint rate; -+ -+ /* on_stream_process() state */ -+ gint segoffset; -+ gint cur_segment; -+}; -+ -+struct _GstPwAudioRingBufferProps -+{ -+ gchar *path; -+ gchar *client_name; -+ GstStructure *properties; -+ int fd; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c -new file mode 100644 -index 00000000..069996c3 ---- /dev/null -+++ b/src/gst/gstpwaudiosink.c -@@ -0,0 +1,207 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudiosink.h" -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_sink_debug); -+#define GST_CAT_DEFAULT pw_audio_sink_debug -+ -+G_DEFINE_TYPE (GstPwAudioSink, gst_pw_audio_sink, GST_TYPE_AUDIO_BASE_SINK); -+ -+enum -+{ -+ PROP_0, -+ PROP_PATH, -+ PROP_CLIENT_NAME, -+ PROP_STREAM_PROPERTIES, -+ PROP_FD -+}; -+ -+static GstStaticPadTemplate gst_pw_audio_sink_template = -+GST_STATIC_PAD_TEMPLATE ("sink", -+ GST_PAD_SINK, -+ GST_PAD_ALWAYS, -+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) -+ ", layout = (string)\"interleaved\"") -+); -+ -+ -+static void -+gst_pw_audio_sink_init (GstPwAudioSink * self) -+{ -+ self->props.fd = -1; -+ -+ /* Bump the default buffer size up to 21.3 ms, which is the default on most -+ * sound cards, in hope to match the alsa buffer size on the pipewire server. -+ * This may not always happen, but it still sounds better than the 10ms -+ * default latency. This is temporary until we have a better mechanism to -+ * select the appropriate latency */ -+ GST_AUDIO_BASE_SINK (self)->latency_time = 21333; -+} -+ -+static void -+gst_pw_audio_sink_finalize (GObject * object) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ g_free (pwsink->props.path); -+ g_free (pwsink->props.client_name); -+ if (pwsink->props.properties) -+ gst_structure_free (pwsink->props.properties); -+} -+ -+static void -+gst_pw_audio_sink_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_free (pwsink->props.path); -+ pwsink->props.path = g_value_dup_string (value); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_free (pwsink->props.client_name); -+ pwsink->props.client_name = g_value_dup_string (value); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ if (pwsink->props.properties) -+ gst_structure_free (pwsink->props.properties); -+ pwsink->props.properties = -+ gst_structure_copy (gst_value_get_structure (value)); -+ break; -+ -+ case PROP_FD: -+ pwsink->props.fd = g_value_get_int (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+gst_pw_audio_sink_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSink *pwsink = GST_PW_AUDIO_SINK (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_value_set_string (value, pwsink->props.path); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_value_set_string (value, pwsink->props.client_name); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ gst_value_set_structure (value, pwsink->props.properties); -+ break; -+ -+ case PROP_FD: -+ g_value_set_int (value, pwsink->props.fd); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static GstAudioRingBuffer * -+gst_pw_audio_sink_create_ringbuffer (GstAudioBaseSink * sink) -+{ -+ GstPwAudioSink *self = GST_PW_AUDIO_SINK (sink); -+ GstAudioRingBuffer *buffer; -+ -+ GST_DEBUG_OBJECT (sink, "creating ringbuffer"); -+ buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER, -+ "element", sink, -+ "direction", PW_DIRECTION_OUTPUT, -+ "props", &self->props, -+ NULL); -+ GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); -+ -+ return buffer; -+} -+ -+static void -+gst_pw_audio_sink_class_init (GstPwAudioSinkClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstElementClass *gstelement_class; -+ GstAudioBaseSinkClass *gstaudiobsink_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstelement_class = (GstElementClass *) klass; -+ gstaudiobsink_class = (GstAudioBaseSinkClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_sink_finalize; -+ gobject_class->set_property = gst_pw_audio_sink_set_property; -+ gobject_class->get_property = gst_pw_audio_sink_get_property; -+ -+ gstaudiobsink_class->create_ringbuffer = gst_pw_audio_sink_create_ringbuffer; -+ -+ g_object_class_install_property (gobject_class, PROP_PATH, -+ g_param_spec_string ("path", "Path", -+ "The sink path to connect to (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, -+ g_param_spec_string ("client-name", "Client Name", -+ "The client name to use (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES, -+ g_param_spec_boxed ("stream-properties", "Stream properties", -+ "List of PipeWire stream properties", GST_TYPE_STRUCTURE, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_FD, -+ g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ gst_element_class_set_static_metadata (gstelement_class, -+ "PipeWire Audio sink", "Sink/Audio", -+ "Send audio to PipeWire", -+ "George Kiagiadakis <george.kiagiadakis@collabora.com>"); -+ -+ gst_element_class_add_pad_template (gstelement_class, -+ gst_static_pad_template_get (&gst_pw_audio_sink_template)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_sink_debug, "pwaudiosink", 0, -+ "PipeWire Audio Sink"); -+} -+ -diff --git a/src/gst/gstpwaudiosink.h b/src/gst/gstpwaudiosink.h -new file mode 100644 -index 00000000..7ed0de7b ---- /dev/null -+++ b/src/gst/gstpwaudiosink.h -@@ -0,0 +1,48 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_SINK_H__ -+#define __GST_PW_AUDIO_SINK_H__ -+ -+#include "gstpwaudioringbuffer.h" -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_SINK \ -+ (gst_pw_audio_sink_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioSink, gst_pw_audio_sink, -+ GST, PW_AUDIO_SINK, GstAudioBaseSink); -+ -+struct _GstPwAudioSink -+{ -+ GstAudioBaseSink parent; -+ GstPwAudioRingBufferProps props; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/gstpwaudiosrc.c b/src/gst/gstpwaudiosrc.c -new file mode 100644 -index 00000000..6c522982 ---- /dev/null -+++ b/src/gst/gstpwaudiosrc.c -@@ -0,0 +1,200 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifdef HAVE_CONFIG_H -+#include "config.h" -+#endif -+ -+#include "gstpwaudiosrc.h" -+ -+GST_DEBUG_CATEGORY_STATIC (pw_audio_src_debug); -+#define GST_CAT_DEFAULT pw_audio_src_debug -+ -+G_DEFINE_TYPE (GstPwAudioSrc, gst_pw_audio_src, GST_TYPE_AUDIO_BASE_SRC); -+ -+enum -+{ -+ PROP_0, -+ PROP_PATH, -+ PROP_CLIENT_NAME, -+ PROP_STREAM_PROPERTIES, -+ PROP_FD -+}; -+ -+static GstStaticPadTemplate gst_pw_audio_src_template = -+GST_STATIC_PAD_TEMPLATE ("src", -+ GST_PAD_SRC, -+ GST_PAD_ALWAYS, -+ GST_STATIC_CAPS (GST_AUDIO_CAPS_MAKE (GST_AUDIO_NE (F32)) -+ ", layout = (string)\"interleaved\"") -+); -+ -+ -+static void -+gst_pw_audio_src_init (GstPwAudioSrc * self) -+{ -+ self->props.fd = -1; -+} -+ -+static void -+gst_pw_audio_src_finalize (GObject * object) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ g_free (self->props.path); -+ g_free (self->props.client_name); -+ if (self->props.properties) -+ gst_structure_free (self->props.properties); -+} -+ -+static void -+gst_pw_audio_src_set_property (GObject * object, guint prop_id, -+ const GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_free (self->props.path); -+ self->props.path = g_value_dup_string (value); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_free (self->props.client_name); -+ self->props.client_name = g_value_dup_string (value); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ if (self->props.properties) -+ gst_structure_free (self->props.properties); -+ self->props.properties = -+ gst_structure_copy (gst_value_get_structure (value)); -+ break; -+ -+ case PROP_FD: -+ self->props.fd = g_value_get_int (value); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static void -+gst_pw_audio_src_get_property (GObject * object, guint prop_id, -+ GValue * value, GParamSpec * pspec) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (object); -+ -+ switch (prop_id) { -+ case PROP_PATH: -+ g_value_set_string (value, self->props.path); -+ break; -+ -+ case PROP_CLIENT_NAME: -+ g_value_set_string (value, self->props.client_name); -+ break; -+ -+ case PROP_STREAM_PROPERTIES: -+ gst_value_set_structure (value, self->props.properties); -+ break; -+ -+ case PROP_FD: -+ g_value_set_int (value, self->props.fd); -+ break; -+ -+ default: -+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); -+ break; -+ } -+} -+ -+static GstAudioRingBuffer * -+gst_pw_audio_src_create_ringbuffer (GstAudioBaseSrc * sink) -+{ -+ GstPwAudioSrc *self = GST_PW_AUDIO_SRC (sink); -+ GstAudioRingBuffer *buffer; -+ -+ GST_DEBUG_OBJECT (sink, "creating ringbuffer"); -+ buffer = g_object_new (GST_TYPE_PW_AUDIO_RING_BUFFER, -+ "element", sink, -+ "direction", PW_DIRECTION_INPUT, -+ "props", &self->props, -+ NULL); -+ GST_DEBUG_OBJECT (sink, "created ringbuffer @%p", buffer); -+ -+ return buffer; -+} -+ -+static void -+gst_pw_audio_src_class_init (GstPwAudioSrcClass * klass) -+{ -+ GObjectClass *gobject_class; -+ GstElementClass *gstelement_class; -+ GstAudioBaseSrcClass *gstaudiobsrc_class; -+ -+ gobject_class = (GObjectClass *) klass; -+ gstelement_class = (GstElementClass *) klass; -+ gstaudiobsrc_class = (GstAudioBaseSrcClass *) klass; -+ -+ gobject_class->finalize = gst_pw_audio_src_finalize; -+ gobject_class->set_property = gst_pw_audio_src_set_property; -+ gobject_class->get_property = gst_pw_audio_src_get_property; -+ -+ gstaudiobsrc_class->create_ringbuffer = gst_pw_audio_src_create_ringbuffer; -+ -+ g_object_class_install_property (gobject_class, PROP_PATH, -+ g_param_spec_string ("path", "Path", -+ "The sink path to connect to (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_CLIENT_NAME, -+ g_param_spec_string ("client-name", "Client Name", -+ "The client name to use (NULL = default)", NULL, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_STREAM_PROPERTIES, -+ g_param_spec_boxed ("stream-properties", "Stream properties", -+ "List of PipeWire stream properties", GST_TYPE_STRUCTURE, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ g_object_class_install_property (gobject_class, PROP_FD, -+ g_param_spec_int ("fd", "Fd", "The fd to connect with", -1, G_MAXINT, -1, -+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); -+ -+ gst_element_class_set_static_metadata (gstelement_class, -+ "PipeWire Audio source", "Source/Audio", -+ "Receive audio from PipeWire", -+ "George Kiagiadakis <george.kiagiadakis@collabora.com>"); -+ -+ gst_element_class_add_pad_template (gstelement_class, -+ gst_static_pad_template_get (&gst_pw_audio_src_template)); -+ -+ GST_DEBUG_CATEGORY_INIT (pw_audio_src_debug, "pwaudiosrc", 0, -+ "PipeWire Audio Src"); -+} -+ -diff --git a/src/gst/gstpwaudiosrc.h b/src/gst/gstpwaudiosrc.h -new file mode 100644 -index 00000000..c46e644c ---- /dev/null -+++ b/src/gst/gstpwaudiosrc.h -@@ -0,0 +1,48 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis <george.kiagiadakis@collabora.com> -+ * -+ * Permission is hereby granted, free of charge, to any person obtaining a -+ * copy of this software and associated documentation files (the "Software"), -+ * to deal in the Software without restriction, including without limitation -+ * the rights to use, copy, modify, merge, publish, distribute, sublicense, -+ * and/or sell copies of the Software, and to permit persons to whom the -+ * Software is furnished to do so, subject to the following conditions: -+ * -+ * The above copyright notice and this permission notice (including the next -+ * paragraph) shall be included in all copies or substantial portions of the -+ * Software. -+ * -+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL -+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -+ * DEALINGS IN THE SOFTWARE. -+ */ -+ -+#ifndef __GST_PW_AUDIO_SRC_H__ -+#define __GST_PW_AUDIO_SRC_H__ -+ -+#include "gstpwaudioringbuffer.h" -+ -+G_BEGIN_DECLS -+ -+#define GST_TYPE_PW_AUDIO_SRC \ -+ (gst_pw_audio_src_get_type ()) -+ -+G_DECLARE_FINAL_TYPE(GstPwAudioSrc, gst_pw_audio_src, -+ GST, PW_AUDIO_SRC, GstAudioBaseSrc); -+ -+struct _GstPwAudioSrc -+{ -+ GstAudioBaseSrc parent; -+ GstPwAudioRingBufferProps props; -+}; -+ -+G_END_DECLS -+ -+#endif -diff --git a/src/gst/meson.build b/src/gst/meson.build -index ad0e0801..0e922347 100644 ---- a/src/gst/meson.build -+++ b/src/gst/meson.build -@@ -6,6 +6,9 @@ pipewire_gst_sources = [ - 'gstpipewirepool.c', - 'gstpipewiresink.c', - 'gstpipewiresrc.c', -+ 'gstpwaudioringbuffer.c', -+ 'gstpwaudiosink.c', -+ 'gstpwaudiosrc.c', - ] - - pipewire_gst_headers = [ -@@ -15,6 +18,9 @@ pipewire_gst_headers = [ - 'gstpipewirepool.h', - 'gstpipewiresink.h', - 'gstpipewiresrc.h', -+ 'gstpwaudioringbuffer.h', -+ 'gstpwaudiosink.h', -+ 'gstpwaudiosrc.h', - ] - - pipewire_gst_c_args = [ --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch deleted file mode 100644 index beb878390..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-audioconvert-always-assume-that-output-ports-are-NOT.patch +++ /dev/null @@ -1,35 +0,0 @@ -From ce155eb0073fba84556782633f79bb7d03492c07 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Wed, 2 Oct 2019 21:40:34 +0300 -Subject: [PATCH] audioconvert: always assume that output ports are NOT monitor - ports - -Otherwise, when we setup audioconvert in merge+split mode, -it assumes that the splitter's ports are monitor ports and -belong to the merger. - -Upstream-Status: Inappropriate [workaround] ---- - spa/plugins/audioconvert/audioconvert.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c -index 74a62a35..72da37d1 100644 ---- a/spa/plugins/audioconvert/audioconvert.c -+++ b/spa/plugins/audioconvert/audioconvert.c -@@ -113,8 +113,12 @@ struct impl { - unsigned int add_listener:1; - }; - -+#if 0 - #define IS_MONITOR_PORT(this,dir,port_id) (dir == SPA_DIRECTION_OUTPUT && port_id > 0 && \ - this->mode[SPA_DIRECTION_INPUT] == SPA_PARAM_PORT_CONFIG_MODE_dsp) -+#else -+#define IS_MONITOR_PORT(this,dir,port_id) (false) -+#endif - - static void emit_node_info(struct impl *this, bool full) - { --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch deleted file mode 100644 index 07a1ec114..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-module-access-add-same-sec-label-mode.patch +++ /dev/null @@ -1,94 +0,0 @@ -From 19fad1a4fa8bdc4f02aac4e169e7ff9cab18bdcd Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Tue, 19 Nov 2019 17:09:07 +0200 -Subject: [PATCH] module-access: add same-sec-label-mode - -This is a mode where the access module allows all clients that have -the same security label as the pipewire daemon, and every other -client is put on the restricted state. - -In systems that use SMACK security labels, such as AGL, this allows -the session manager (which is spawned by pipewire, inheriting the -same smack label) to have full access to all objects, while every -other client is restricted and the session manager must decide -what to do with it - -Note that while this option is configurable, there is no loss of -security if this option is not set in the configuration. Clients -that don't have the same security context will be considered to -be flatpak clients because pipewire will not be able to open -/proc/pid/cmdline. This however results in some unwanted error -messages that may be confusing. - -Upstream-Status: Inappropriate [agl/smack specific] ---- - src/modules/module-access.c | 45 ++++++++++++++++++++++++++++++++++++- - 1 file changed, 44 insertions(+), 1 deletion(-) - -diff --git a/src/modules/module-access.c b/src/modules/module-access.c -index 09dafa43..f75306d9 100644 ---- a/src/modules/module-access.c -+++ b/src/modules/module-access.c -@@ -50,6 +50,30 @@ struct impl { - struct spa_hook module_listener; - }; - -+static int check_seclabel(const char *str) -+{ -+ char attr[1024]; -+ int fd, len; -+ -+ fd = open("/proc/self/attr/current", O_RDONLY); -+ if (fd < 0) -+ return -errno; -+ -+ if ((len = read(fd, attr, 1024)) <= 0) { -+ close(fd); -+ return -EIO; -+ } -+ attr[len] = '\0'; -+ -+ if (strcmp(attr, str) == 0) { -+ close(fd); -+ return 1; -+ } -+ -+ close(fd); -+ return 0; -+} -+ - static int check_cmdline(struct pw_client *client, int pid, const char *str) - { - char path[2048]; -@@ -121,8 +145,27 @@ core_check_access(void *data, struct pw_client *client) - const char *str; - int pid, res; - -+ props = pw_client_get_properties(client); -+ -+ if (impl->properties && -+ (str = pw_properties_get(impl->properties, "same-sec-label-mode")) != NULL && -+ strcmp(str, "1") == 0) { -+ if (props && (str = pw_properties_get(props, PW_KEY_SEC_LABEL)) != NULL) { -+ res = check_seclabel(str); -+ if (res == 1) -+ goto granted; -+ else if (res < 0) -+ pw_log_warn("module %p: client %p seclabel check failed: %s", -+ impl, client, spa_strerror(res)); -+ } -+ pw_log_debug("module %p: seclabel restricted client %p added", -+ impl, client); -+ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, "restricted"); -+ goto wait_permissions; -+ } -+ - pid = -EINVAL; -- if ((props = pw_client_get_properties(client)) != NULL) { -+ if (props != NULL) { - if ((str = pw_properties_get(props, PW_KEY_SEC_PID)) != NULL) - pid = atoi(str); - } --- -2.24.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch deleted file mode 100644 index cae4d70f6..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 5946fbd2ca3a7f892b4ebc10090f62df6bb1ec88 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis <george.kiagiadakis@collabora.com> -Date: Thu, 9 Jan 2020 19:27:23 +0200 -Subject: [PATCH] alsa-pcm: call reuse_buffers when resetting the state of the - buffers - -This allows the upstream node to put buffers back to its pool in case -they were left around in the ready list locally when the alsa-pcm-sink -was last paused. - -Fixes #203 ---- - spa/plugins/alsa/alsa-pcm.c | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 63d75549..a6f22cf0 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -1115,6 +1115,7 @@ static void reset_buffers(struct state *this) - struct buffer *b = &this->buffers[i]; - if (this->stream == SND_PCM_STREAM_PLAYBACK) { - SPA_FLAG_SET(b->flags, BUFFER_FLAG_OUT); -+ spa_node_call_reuse_buffer(&this->callbacks, 0, b->id); - } else { - spa_list_append(&this->free, &b->link); - SPA_FLAG_CLEAR(b->flags, BUFFER_FLAG_OUT); --- -2.24.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch deleted file mode 100644 index f9649400e..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-alsa-Set-period_size-depending-on-hardware.patch +++ /dev/null @@ -1,35 +0,0 @@ -From bbf9c767d5b353142e03080762bdd805e124d50b Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Fri, 7 Aug 2020 10:58:29 -0300 -Subject: [PATCH] alsa: Set period_size depending on hardware - -Currently PipeWire is unable to reproduce audio in systems where DMA -granularity is not burst. - -In order to mitigate this issue, set period_size depending on hardware, -lowering it when snd_pcm_hw_params_is_batch == 1, to reduce DMA -transfers size. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 4 ++++ - 1 file changed, 4 insertions(+) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 63d75549..2d3850b7 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -463,6 +463,10 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ - - dir = 0; - period_size = 1024; -+ if (snd_pcm_hw_params_is_batch(params)) { -+ period_size = 512; -+ spa_log_warn(state->log, NAME" hardware does double buffering, changing period_size to %ld", period_size); -+ } - CHECK(snd_pcm_hw_params_set_period_size_near(hndl, params, &period_size, &dir), "set_period_size_near"); - CHECK(snd_pcm_hw_params_get_buffer_size_max(params, &state->buffer_frames), "get_buffer_size_max"); - CHECK(snd_pcm_hw_params_set_buffer_size_near(hndl, params, &state->buffer_frames), "set_buffer_size_near"); --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch deleted file mode 100644 index 98a2c98fc..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-alsa-add-warning-in-case-of-partial-read-write.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 45658f75e61da47b274f2eba3a55e62d016f8b42 Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Mon, 24 Aug 2020 12:08:32 -0300 -Subject: [PATCH 8/9] alsa: add warning in case of partial read/write - -Currently alsa_read and alsa_write assumes that all the frames committed -using snd_pcm_mmap_commit are read or written, which is probably true. -However, as it could be some corner cases add a warning to notice this -fact. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 28 ++++++++++++++++++++-------- - 1 file changed, 20 insertions(+), 8 deletions(-) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index ed9bf42b..92ef2151 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -721,6 +721,7 @@ int spa_alsa_write(struct state *state, snd_pcm_uframes_t silence) - snd_pcm_t *hndl = state->hndl; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t written, frames, offset, off, to_write, total_written; -+ snd_pcm_sframes_t commitres; - int res; - - if (state->position && state->duration != state->position->clock.duration) { -@@ -834,11 +835,16 @@ again: - state, offset, written, state->sample_count); - total_written += written; - -- if ((res = snd_pcm_mmap_commit(hndl, offset, written)) < 0) { -+ if ((commitres = snd_pcm_mmap_commit(hndl, offset, written)) < 0) { - spa_log_error(state->log, NAME" %p: snd_pcm_mmap_commit error: %s", -- state, snd_strerror(res)); -- if (res != -EPIPE && res != -ESTRPIPE) -- return res; -+ state, snd_strerror(commitres)); -+ if (commitres != -EPIPE && commitres != -ESTRPIPE) -+ return commitres; -+ } -+ -+ if (commitres > 0 && written != (snd_pcm_uframes_t) commitres) { -+ spa_log_warn(state->log, NAME" %p: mmap_commit wrote %ld instead of %ld", -+ state, commitres, written); - } - - if (!spa_list_is_empty(&state->ready) && written > 0) -@@ -922,6 +928,7 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) - snd_pcm_uframes_t total_read = 0, to_read; - const snd_pcm_channel_area_t *my_areas; - snd_pcm_uframes_t read, frames, offset; -+ snd_pcm_sframes_t commitres; - int res; - - if (state->position) { -@@ -994,11 +1001,16 @@ int spa_alsa_read(struct state *state, snd_pcm_uframes_t silence) - offset, read, state->sample_count); - total_read += read; - -- if ((res = snd_pcm_mmap_commit(hndl, offset, read)) < 0) { -+ if ((commitres = snd_pcm_mmap_commit(hndl, offset, read)) < 0) { - spa_log_error(state->log, NAME" %p: snd_pcm_mmap_commit error: %s", -- state, snd_strerror(res)); -- if (res != -EPIPE && res != -ESTRPIPE) -- return res; -+ state, snd_strerror(commitres)); -+ if (commitres != -EPIPE && commitres != -ESTRPIPE) -+ return commitres; -+ } -+ -+ if (commitres > 0 && read != (snd_pcm_uframes_t) commitres) { -+ spa_log_warn(state->log, NAME" %p: mmap_commit read %ld instead of %ld", -+ state, commitres, read); - } - - state->sample_count += total_read; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch deleted file mode 100644 index a448063f1..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-adjust-delay-depending-on-hardware.patch +++ /dev/null @@ -1,64 +0,0 @@ -From 38cdfa4483de4c2e91bfccb9c22ec72d9c3720f4 Mon Sep 17 00:00:00 2001 -From: Walter Lozano <walter.lozano@collabora.com> -Date: Sat, 22 Aug 2020 11:51:30 -0300 -Subject: [PATCH 9/9] alsa: adjust delay depending on hardware - -Currently PipeWire is able to reproduce audio in systems where -DMA granularity is not burst but it could face an xrun. - -In order to mitigate this issue, adjust the delay PipeWire -calculates to make sure that a period is available in the buffer -when snd_pcm_hw_params_is_batch == 1. - -Signed-off-by: Walter Lozano <walter.lozano@collabora.com> ---- - spa/plugins/alsa/alsa-pcm.c | 12 +++++++++++- - spa/plugins/alsa/alsa-pcm.h | 1 + - 2 files changed, 12 insertions(+), 1 deletion(-) - -diff --git a/spa/plugins/alsa/alsa-pcm.c b/spa/plugins/alsa/alsa-pcm.c -index 92ef2151..1f15085f 100644 ---- a/spa/plugins/alsa/alsa-pcm.c -+++ b/spa/plugins/alsa/alsa-pcm.c -@@ -462,8 +462,9 @@ int spa_alsa_set_format(struct state *state, struct spa_audio_info *fmt, uint32_ - state->frame_size = info->channels * (snd_pcm_format_physical_width(format) / 8); - - dir = 0; -+ state->pcm_is_batch = snd_pcm_hw_params_is_batch(params); - period_size = 1024; -- if (snd_pcm_hw_params_is_batch(params)) { -+ if (state->pcm_is_batch) { - period_size = 512; - spa_log_warn(state->log, NAME" hardware does double buffering, changing period_size to %ld", period_size); - } -@@ -639,6 +640,15 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr - - if (state->stream == SND_PCM_STREAM_PLAYBACK) { - *delay = state->buffer_frames - avail; -+ if (state->pcm_is_batch) { -+ /* In this case, as we don't have a good granularity in the -+ * avail report try to compensate this by tweaking the delay -+ * and make sure that a period is available in the buffer */ -+ if (*delay > state->period_frames) -+ *delay = *delay - state->period_frames; -+ else -+ *delay = 0; -+ } - } - else { - *delay = avail; -diff --git a/spa/plugins/alsa/alsa-pcm.h b/spa/plugins/alsa/alsa-pcm.h -index b7a2dd29..3b5c0d7b 100644 ---- a/spa/plugins/alsa/alsa-pcm.h -+++ b/spa/plugins/alsa/alsa-pcm.h -@@ -100,6 +100,7 @@ struct state { - - bool have_format; - struct spa_audio_info current_format; -+ bool pcm_is_batch; - - snd_pcm_uframes_t buffer_frames; - snd_pcm_uframes_t period_frames; --- -2.20.1 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bb new file mode 100644 index 000000000..e18109cc2 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bb @@ -0,0 +1,282 @@ +# This recipe was written by Carlos Rafael Giani <crg7475@mailbox.org> +# for meta-oe +# +# The intention is to keep this synced with meta-oe and remove it when we +# can depend on meta-oe to provide this recipe for us +# +# AGL-specific overrides and configuration should go in the .bbappend file +# +SUMMARY = "Multimedia processing server for Linux" +DESCRIPTION = "Linux server for handling and routing audio and video streams between applications and multimedia I/O devices" +HOMEPAGE = "https://pipewire.org/" +BUGTRACKER = "https://gitlab.freedesktop.org/pipewire/pipewire/issues" +LICENSE = "MIT" +LIC_FILES_CHKSUM = " \ + file://LICENSE;md5=e2c0b7d86d04e716a3c4c9ab34260e69 \ + file://COPYING;md5=97be96ca4fab23e9657ffa590b931c1a \ +" +SECTION = "multimedia" + +DEPENDS = "dbus" + +SRCREV = "136511fbc80320400fc0de58b3766c389f92a887" +SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;branch=master;protocol=https" + +S = "${WORKDIR}/git" + +inherit meson pkgconfig systemd manpages gettext useradd + +USERADD_PACKAGES = "${PN}" + +GROUPADD_PARAM_${PN} = "--system pipewire" + +USERADD_PARAM_${PN} = "--system --home / --no-create-home \ + --comment 'PipeWire multimedia daemon' \ + --gid pipewire --groups audio,video \ + pipewire" + +# For "EVL", look up https://evlproject.org/ . It involves +# a specially prepared kernel, and is currently unavailable +# in Yocto. +# FFmpeg and Vulkan aren't really supported - at the current +# stage (version 0.3.22), these are just experiments, not +# actual features. +# libcamera support currently does not build successfully. +# systemd user service files are disabled because per-user +# PipeWire instances aren't really something that makes +# much sense in an embedded environment. A system-wide +# instance does. +EXTRA_OEMESON += " \ + -Daudiotestsrc=enabled \ + -Devl=disabled \ + -Dsystemd-user-service=disabled \ + -Dtests=disabled \ + -Dudevrulesdir=${nonarch_base_libdir}/udev/rules.d/ \ + -Dvideotestsrc=enabled \ + -Dffmpeg=disabled \ + -Dvulkan=disabled \ + -Dlibcamera=disabled \ +" + +PACKAGECONFIG ??= "\ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluetooth', 'bluez', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'alsa systemd', d)} \ + gstreamer jack v4l2 \ +" + +# "jack" and "pipewire-jack" packageconfigs cannot be both enabled, +# since "jack" imports libjack, and "pipewire-jack" generates +# libjack.so* files, thus colliding with the libpack package. This +# is why these two are marked in their respective packageconfigs +# as being in conflict. + +PACKAGECONFIG[alsa] = "-Dalsa=enabled,-Dalsa=disabled,alsa-lib udev" +PACKAGECONFIG[bluez] = "-Dbluez5=enabled,-Dbluez5=disabled,bluez5 sbc" +PACKAGECONFIG[docs] = "-Ddocs=enabled,-Ddocs=disabled,doxygen" +PACKAGECONFIG[gstreamer] = "-Dgstreamer=enabled,-Dgstreamer=disabled,glib-2.0 gstreamer1.0 gstreamer1.0-plugins-base" +PACKAGECONFIG[jack] = "-Djack=enabled,-Djack=disabled,jack,,,pipewire-jack" +PACKAGECONFIG[manpages] = "-Dman=enabled,-Dman=disabled,libxml-parser-perl-native" +PACKAGECONFIG[sdl2] = "-Dsdl2=enabled,-Dsdl2=disabled,virtual/libsdl2" +PACKAGECONFIG[sndfile] = "-Dsndfile=enabled,-Dsndfile=disabled,libsndfile1" +PACKAGECONFIG[systemd] = "-Dsystemd=enabled -Dsystemd-system-service=enabled ,-Dsystemd=disabled -Dsystemd-system-service=disabled,systemd" +PACKAGECONFIG[v4l2] = "-Dv4l2=enabled,-Dv4l2=disabled,udev" +PACKAGECONFIG[pipewire-alsa] = "-Dpipewire-alsa=enabled,-Dpipewire-alsa=disabled,alsa-lib" +PACKAGECONFIG[pipewire-jack] = "-Dpipewire-jack=enabled -Dlibjack-path=${libdir}/${PW_MODULE_SUBDIR}/jack,-Dpipewire-jack=disabled,jack,,,jack" + +PACKAGESPLITFUNCS_prepend = " split_dynamic_packages " +PACKAGESPLITFUNCS_append = " set_dynamic_metapkg_rdepends " + +SPA_SUBDIR = "spa-0.2" +PW_MODULE_SUBDIR = "pipewire-0.3" + +remove_unused_installed_files() { + # jack.conf is used by pipewire-jack (not the JACK SPA plugin). + # Remove it if pipewire-jack is not built to avoid creating the + # pipewire-jack package. + if ${@bb.utils.contains('PACKAGECONFIG', 'pipewire-jack', 'false', 'true', d)}; then + rm -f "${D}${sysconfdir}/pipewire/jack.conf" + fi +} + +do_install[postfuncs] += "remove_unused_installed_files" + +python split_dynamic_packages () { + # Create packages for each SPA plugin. These plugins are located + # in individual subdirectories, so a recursive search is needed. + spa_libdir = d.expand('${libdir}/${SPA_SUBDIR}') + do_split_packages(d, spa_libdir, r'^libspa-(.*)\.so$', d.expand('${PN}-spa-plugins-%s'), 'PipeWire SPA plugin for %s', extra_depends='', recursive=True) + + # Create packages for each PipeWire module. + pw_module_libdir = d.expand('${libdir}/${PW_MODULE_SUBDIR}') + do_split_packages(d, pw_module_libdir, r'^libpipewire-module-(.*)\.so$', d.expand('${PN}-modules-%s'), 'PipeWire %s module', extra_depends='', recursive=False) +} + +python set_dynamic_metapkg_rdepends () { + import os + import oe.utils + + # Go through all generated SPA plugin and PipeWire module packages + # (excluding the main package and the -meta package itself) and + # add them to the -meta package as RDEPENDS. + + base_pn = d.getVar('PN') + + spa_pn = base_pn + '-spa-plugins' + spa_metapkg = spa_pn + '-meta' + + pw_module_pn = base_pn + '-modules' + pw_module_metapkg = pw_module_pn + '-meta' + + d.setVar('ALLOW_EMPTY_' + spa_metapkg, "1") + d.setVar('FILES_' + spa_metapkg, "") + + d.setVar('ALLOW_EMPTY_' + pw_module_metapkg, "1") + d.setVar('FILES_' + pw_module_metapkg, "") + + blacklist = [ spa_pn, spa_metapkg, pw_module_pn, pw_module_metapkg ] + spa_metapkg_rdepends = [] + pw_module_metapkg_rdepends = [] + pkgdest = d.getVar('PKGDEST') + + for pkg in oe.utils.packages_filter_out_system(d): + if pkg in blacklist: + continue + + is_spa_pkg = pkg.startswith(spa_pn) + is_pw_module_pkg = pkg.startswith(pw_module_pn) + if not is_spa_pkg and not is_pw_module_pkg: + continue + + if pkg in spa_metapkg_rdepends or pkg in pw_module_metapkg_rdepends: + continue + + # See if the package is empty by looking at the contents of its + # PKGDEST subdirectory. If this subdirectory is empty, then then + # package is empty as well. Empty packages do not get added to + # the meta package's RDEPENDS. + pkgdir = os.path.join(pkgdest, pkg) + if os.path.exists(pkgdir): + dir_contents = os.listdir(pkgdir) or [] + else: + dir_contents = [] + is_empty = len(dir_contents) == 0 + if not is_empty: + if is_spa_pkg: + spa_metapkg_rdepends.append(pkg) + if is_pw_module_pkg: + pw_module_metapkg_rdepends.append(pkg) + + d.setVar('RDEPENDS_' + spa_metapkg, ' '.join(spa_metapkg_rdepends)) + d.setVar('DESCRIPTION_' + spa_metapkg, spa_pn + ' meta package') + + d.setVar('RDEPENDS_' + pw_module_metapkg, ' '.join(pw_module_metapkg_rdepends)) + d.setVar('DESCRIPTION_' + pw_module_metapkg, pw_module_pn + ' meta package') +} + +PACKAGES =+ "\ + libpipewire \ + ${PN}-tools \ + ${PN}-pulse \ + ${PN}-alsa \ + ${PN}-jack \ + ${PN}-media-session \ + ${PN}-spa-plugins \ + ${PN}-spa-plugins-meta \ + ${PN}-spa-tools \ + ${PN}-modules \ + ${PN}-modules-meta \ + ${PN}-alsa-card-profile \ + gstreamer1.0-pipewire \ +" + +PACKAGES_DYNAMIC = "^${PN}-spa-plugins.* ^${PN}-modules.*" + +SYSTEMD_SERVICE_${PN} = "pipewire.service" +CONFFILES_${PN} += "${sysconfdir}/pipewire/pipewire.conf" +FILES_${PN} = " \ + ${sysconfdir}/pipewire/pipewire.conf \ + ${systemd_system_unitdir}/pipewire.* \ + ${bindir}/pipewire \ +" + +FILES_${PN}-dev += " \ + ${libdir}/${PW_MODULE_SUBDIR}/jack/libjack*.so \ +" + +CONFFILES_libpipewire += "${sysconfdir}/pipewire/client.conf" +FILES_libpipewire = " \ + ${sysconfdir}/pipewire/client.conf \ + ${libdir}/libpipewire-*.so.* \ +" +# Add the bare minimum modules and plugins required to be able +# to use libpipewire. Without these, it is essentially unusable. +RDEPENDS_libpipewire += " \ + ${PN}-modules-client-node \ + ${PN}-modules-protocol-native \ + ${PN}-spa-plugins-support \ +" + +FILES_${PN}-tools = " \ + ${bindir}/pw-* \ +" + +# This is a shim daemon that is intended to be used as a +# drop-in PulseAudio replacement, providing a pulseaudio-compatible +# socket that can be used by applications that use libpulse. +CONFFILES_${PN}-pulse += "${sysconfdir}/pipewire/pipewire-pulse.conf" +FILES_${PN}-pulse = " \ + ${sysconfdir}/pipewire/pipewire-pulse.conf \ + ${bindir}/pipewire-pulse \ +" +RDEPENDS_${PN}-pulse += " \ + ${PN}-modules-protocol-pulse \ +" + +# alsa plugin to redirect audio to pipewire +FILES_${PN}-alsa = "\ + ${libdir}/alsa-lib/* \ + ${datadir}/alsa/alsa.conf.d/* \ +" + +# jack drop-in libraries to redirect audio to pipewire +CONFFILES_${PN}-jack = "${sysconfdir}/pipewire/jack.conf" +FILES_${PN}-jack = "\ + ${sysconfdir}/pipewire/jack.conf \ + ${libdir}/${PW_MODULE_SUBDIR}/jack/libjack*.so.* \ +" + +# Example session manager. Not intended for use in production. +SYSTEMD_SERVICE_${PN}-media-session = "pipewire-media-session.service" +CONFFILES_${PN}-media-session = "${sysconfdir}/pipewire/media-session.d/*" +FILES_${PN}-media-session = " \ + ${bindir}/pipewire-media-session \ + ${systemd_system_unitdir}/pipewire-media-session.service \ + ${sysconfdir}/pipewire/media-session.d/* \ +" +RPROVIDES_${PN}-media-session = "virtual/pipewire-sessionmanager" + +# Dynamic packages (see set_dynamic_metapkg_rdepends). +FILES_${PN}-spa-plugins = "" +RRECOMMENDS_${PN}-spa-plugins += "${PN}-spa-plugins-meta" + +FILES_${PN}-spa-tools = " \ + ${bindir}/spa-* \ +" + +# Dynamic packages (see set_dynamic_metapkg_rdepends). +FILES_${PN}-modules = "" +RRECOMMENDS_${PN}-modules += "${PN}-modules-meta" + +CONFFILES_${PN}-modules-rtkit = "${sysconfdir}/pipewire/client-rt.conf" +FILES_${PN}-modules-rtkit += " \ + ${sysconfdir}/pipewire/client-rt.conf \ + " + +FILES_${PN}-alsa-card-profile = " \ + ${datadir}/alsa-card-profile/* \ + ${nonarch_base_libdir}/udev/rules.d/90-pipewire-alsa.rules \ +" + +FILES_gstreamer1.0-pipewire = " \ + ${libdir}/gstreamer-1.0/* \ +" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bbappend b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bbappend new file mode 100644 index 000000000..cc2308e95 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_0.3.25.bbappend @@ -0,0 +1,7 @@ +PACKAGECONFIG = "\ + ${@bb.utils.contains('DISTRO_FEATURES', 'bluez5', 'bluez', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'alsa', 'alsa pipewire-alsa', '', d)} \ + ${@bb.utils.contains('DISTRO_FEATURES', 'agl-devel', 'sndfile', '', d)} \ + ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ + gstreamer v4l2 \ +" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb deleted file mode 100644 index e2560ad9d..000000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb +++ /dev/null @@ -1,20 +0,0 @@ -require pipewire.inc - -SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;protocol=https;branch=master \ - file://0001-meson-revert-version-check-to-require-meson-0.47-not.patch \ - file://0002-arm-build-with-mno-unaligned-access.patch \ - file://0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ - file://0004-audioconvert-always-assume-that-output-ports-are-NOT.patch \ - file://0005-module-access-add-same-sec-label-mode.patch \ - file://0006-alsa-pcm-call-reuse_buffers-when-resetting-the-state.patch \ - file://0007-alsa-Set-period_size-depending-on-hardware.patch \ - file://0008-alsa-add-warning-in-case-of-partial-read-write.patch \ - file://0009-alsa-adjust-delay-depending-on-hardware.patch \ - " - -SRCREV = "b0932e687fc47e0872ca291531f2291d99042d70" - -PV = "0.2.91+git${SRCPV}+2" -S = "${WORKDIR}/git" - -RDEPENDS_${PN} += "virtual/pipewire-sessionmanager virtual/pipewire-config" |