From a349a7d85434679a7d62fe5affe7074c33f0cb8a Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Mon, 7 Oct 2019 18:28:10 +0300 Subject: pipewire: update to latest master Update patches: * remove merged patches * remove the alsa algorithm correction that was needed for qemu, as it seems to cause severe underruns on other platforms now; I will recheck qemu to see if this is still an issue * add a workaround needed to get the audioconvert plugin running in merge+split mode (needed to get all the channels out instead of just the front left channel that we were getting before) Update version to 0.2.91 to reflect the fact that we are now tracking the master branch instead of the work branch. Enable the spa audiomixer plugin which is now required to mix audio on ports Bug-AGL: SPEC-2837 Change-Id: I2558aa5487b9c9918e077bf450230c143abf7e6c Signed-off-by: George Kiagiadakis --- .../recipes-multimedia/pipewire/pipewire.inc | 3 +- .../0001-arm-build-with-mno-unaligned-access.patch | 6 +- ...ogger-print-timestamps-on-logged-messages.patch | 2 +- ...orrections-on-the-timeout-based-on-how-fa.patch | 121 - ...nt-new-pwaudio-src-sink-elements-based-on.patch | 1249 +++++ ...nt-new-pwaudio-src-sink-elements-based-on.patch | 1249 ----- ...ringbuffer-request-pause-play-on-the-appr.patch | 76 + ...ringbuffer-request-pause-play-on-the-appr.patch | 76 - ...ringbuffer-wait-only-for-STREAM_STATE_CON.patch | 35 + ...ringbuffer-wait-only-for-STREAM_STATE_CON.patch | 35 - ...sink-set-the-default-latency-time-buffer-.patch | 37 + ...ringbuffer-set-node.latency-to-get-schedu.patch | 41 + ...sink-set-the-default-latency-time-buffer-.patch | 37 - ...ringbuffer-set-node.latency-to-get-schedu.patch | 41 - ...t-version-check-to-require-meson-0.47-not.patch | 30 + ...-expose-non-interleaved-formats-since-the.patch | 37 - ...s-implement-new-session-manager-extension.patch | 5715 ++++++++++++++++++++ ...or-fix-usage-of-pw_properties_setf-withou.patch | 27 - ...i-add-support-for-printing-endpoint-info-.patch | 167 + ...ig-remote-load-module-session-manager-by-.patch | 37 + ...t-version-check-to-require-meson-0.47-not.patch | 30 - ...t-always-assume-that-output-ports-are-NOT.patch | 35 + ...s-implement-new-session-manager-extension.patch | 5715 -------------------- ...i-add-support-for-printing-endpoint-info-.patch | 167 - ...ig-remote-load-module-session-manager-by-.patch | 37 - ...15-connection-move-remaining-data-and-fds.patch | 61 - .../pipewire/0016-protocol-improve-flushing.patch | 243 - .../recipes-multimedia/pipewire/pipewire_git.bb | 30 +- 28 files changed, 7441 insertions(+), 7898 deletions(-) delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-meson-revert-version-check-to-require-meson-0.47-not.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch create mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-connection-move-remaining-data-and-fds.patch delete mode 100644 meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-protocol-improve-flushing.patch diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc index de6d9b86..b3081ca4 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire.inc @@ -19,7 +19,7 @@ DEPENDS = "dbus" PACKAGECONFIG ??= "\ ${@bb.utils.filter('DISTRO_FEATURES', 'systemd', d)} \ ${@bb.utils.filter('DISTRO_FEATURES', 'bluez5', d)} \ - alsa audioconvert \ + alsa audioconvert audiomixer \ pipewire-alsa \ gstreamer \ " @@ -32,6 +32,7 @@ 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" diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch index 3309836a..b42c3330 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch @@ -1,4 +1,4 @@ -From e1d8927b7963a9bf2c09e50cd95943c3139313cf Mon Sep 17 00:00:00 2001 +From 9bf3ae655b4cb7a3a91a9c77e006ec72864a6e1a Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Mon, 24 Jun 2019 12:19:20 +0300 Subject: [PATCH] arm: build with -mno-unaligned-access @@ -10,10 +10,10 @@ See also https://github.com/PipeWire/pipewire/issues/161 1 file changed, 5 insertions(+) diff --git a/meson.build b/meson.build -index aa60db35..5cd61fde 100644 +index 74455378..c0bb7470 100644 --- a/meson.build +++ b/meson.build -@@ -50,6 +50,11 @@ if cc.get_id() == 'gcc' +@@ -52,6 +52,11 @@ if cc.get_id() == 'gcc' language : 'c') endif diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch index 6012aa56..714ceb9a 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch @@ -1,4 +1,4 @@ -From 289f58b815badd54a32f2409bae7abd7e5474327 Mon Sep 17 00:00:00 2001 +From aef31f71f7dd458b5c26509cea09f81bab89ee84 Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Wed, 3 Jul 2019 17:47:46 +0300 Subject: [PATCH] logger: print timestamps on logged messages diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch deleted file mode 100644 index 17280da0..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch +++ /dev/null @@ -1,121 +0,0 @@ -From 0c232229f3dc6b0cdf02ef44ae212b256c1828a3 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Tue, 9 Jul 2019 18:06:18 +0300 -Subject: [PATCH] alsa: make corrections on the timeout based on how fast ALSA - consumes samples - -This feels a bit hacky, but it actually makes huge difference when pipewire is -running in qemu. - -The idea is that it keeps track of how much samples are in the device -(fill level) and calculates how many are consumed when a timeout occurs. -Then it converts that into a time based on the sample rate and compares it to -the system clock time that elapsed since the last write to the device. -The division between the two gives a rate (drift) that can be used to shorten -the timeout window. - -So for instance, if the timeout window was 21.3 ms, but the device actually -consumed an equivalent of 28 ms in samples, the drift will be 21.3/28 = 0.76 -and the next timeout window will be approximately 21.3 * 0.76 = 16.1 ms - -To avoid making things worse, the drift is clamped between 0.6 and 1.0. -Min 0.6 was arbitrarily chosen, but sometimes alsa does report strange numbers, -causing the drift to be very low, which in turn causes an early wakeup. -Max 1.0 basically means that we don't care if the device is consuming samples -slower. In that case, the early wakeup mechanism will throttle pipewire. - -Fixes #163 - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/166] ---- - spa/plugins/alsa/alsa-utils.c | 29 +++++++++++++++++++++++++---- - spa/plugins/alsa/alsa-utils.h | 2 ++ - 2 files changed, 27 insertions(+), 4 deletions(-) - -diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c -index 7cf1d0d2..e8548345 100644 ---- a/spa/plugins/alsa/alsa-utils.c -+++ b/spa/plugins/alsa/alsa-utils.c -@@ -624,7 +624,21 @@ static int get_status(struct state *state, snd_pcm_uframes_t *delay, snd_pcm_ufr - static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t delay, - snd_pcm_sframes_t target, bool slave) - { -- double err, corr; -+ double err, corr, drift; -+ snd_pcm_sframes_t consumed; -+ -+ consumed = state->fill_level - delay; -+ if (state->alsa_started && consumed > 0) { -+ double sysclk_diff = nsec - state->last_time; -+ double devclk_diff = ((double) consumed) * 1e9 / state->rate; -+ drift = sysclk_diff / devclk_diff; -+ drift = SPA_CLAMP(drift, 0.6, 1.0); -+ -+ spa_log_trace_fp(state->log, "cons:%ld sclk:%f dclk:%f drift:%f", -+ consumed, sysclk_diff, devclk_diff, drift); -+ } else { -+ drift = 1.0; -+ } - - if (state->stream == SND_PCM_STREAM_PLAYBACK) - err = delay - target; -@@ -677,11 +691,11 @@ static int update_time(struct state *state, uint64_t nsec, snd_pcm_sframes_t del - state->clock->next_nsec = state->next_time; - } - -- spa_log_trace_fp(state->log, "slave:%d %"PRIu64" %f %ld %f %f %d", slave, nsec, -- corr, delay, err, state->threshold * corr, -+ spa_log_trace_fp(state->log, "slave:%d %"PRIu64" %f %ld %f %f %f %d", slave, nsec, -+ corr, delay, err, state->threshold * corr, drift, - state->threshold); - -- state->next_time += state->threshold / corr * 1e9 / state->rate; -+ state->next_time += state->threshold / corr * drift * 1e9 / state->rate; - state->last_threshold = state->threshold; - - return 0; -@@ -812,6 +826,10 @@ again: - goto again; - - state->sample_count += total_written; -+ state->fill_level += total_written; -+ -+ clock_gettime(CLOCK_MONOTONIC, &state->now); -+ state->last_time = SPA_TIMESPEC_TO_NSEC (&state->now); - - if (!state->alsa_started && total_written > 0) { - spa_log_trace(state->log, "snd_pcm_start %lu", written); -@@ -981,6 +999,8 @@ static int handle_play(struct state *state, uint64_t nsec, - if ((res = update_time(state, nsec, delay, target, false)) < 0) - return res; - -+ state->fill_level = delay; -+ - if (spa_list_is_empty(&state->ready)) { - struct spa_io_buffers *io = state->io; - -@@ -1120,6 +1140,7 @@ int spa_alsa_start(struct state *state) - - state->threshold = (state->duration * state->rate + state->rate_denom-1) / state->rate_denom; - state->last_threshold = state->threshold; -+ state->fill_level = 0; - - init_loop(state); - state->safety = 0.0; -diff --git a/spa/plugins/alsa/alsa-utils.h b/spa/plugins/alsa/alsa-utils.h -index 110d4204..bab0f67b 100644 ---- a/spa/plugins/alsa/alsa-utils.h -+++ b/spa/plugins/alsa/alsa-utils.h -@@ -141,7 +141,9 @@ struct state { - int64_t sample_time; - uint64_t next_time; - uint64_t base_time; -+ uint64_t last_time; - -+ snd_pcm_uframes_t fill_level; - uint64_t underrun; - double safety; - --- -2.23.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 new file mode 100644 index 00000000..b8ffbde5 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch @@ -0,0 +1,1249 @@ +From e2426512322768a2d386740845c80e282aba5a6a Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +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: Submitted [https://github.com/PipeWire/pipewire/pull/140] +--- + src/gst/gstpipewire.c | 8 +- + src/gst/gstpwaudioringbuffer.c | 542 +++++++++++++++++++++++++++++++++ + src/gst/gstpwaudioringbuffer.h | 83 +++++ + src/gst/gstpwaudiosink.c | 200 ++++++++++++ + src/gst/gstpwaudiosink.h | 48 +++ + src/gst/gstpwaudiosrc.c | 200 ++++++++++++ + src/gst/gstpwaudiosrc.h | 48 +++ + src/gst/meson.build | 6 + + 8 files changed, 1134 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..989b2cd7 +--- /dev/null ++++ b/src/gst/gstpwaudioringbuffer.c +@@ -0,0 +1,542 @@ ++/* PipeWire ++ * ++ * Copyright © 2018 Wim Taymans ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 ++#include ++ ++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); ++ ++ GST_DEBUG_OBJECT (self->elem, "got stream state: %s", ++ pw_stream_state_as_string (state)); ++ ++ switch (state) { ++ case PW_STREAM_STATE_UNCONNECTED: ++ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, ++ ("stream disconnected unexpectedly"), (NULL)); ++ break; ++ case PW_STREAM_STATE_CONNECTING: ++ case PW_STREAM_STATE_CONFIGURE: ++ case PW_STREAM_STATE_READY: ++ case PW_STREAM_STATE_PAUSED: ++ case PW_STREAM_STATE_STREAMING: ++ break; ++ case PW_STREAM_STATE_ERROR: ++ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, ++ ("stream error: %s", error), (NULL)); ++ 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_format_changed (void *data, 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]; ++ ++ 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_finish_format (self->stream, 0, 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_WARNING_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, ++ .format_changed = on_stream_format_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 */ ++ ++ if (self->props->properties) { ++ props = pw_properties_new (NULL, NULL); ++ gst_structure_foreach (self->props->properties, copy_properties, props); ++ } else { ++ props = NULL; ++ } ++ ++ 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; ++ ++ /* 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 READY"); ++ ++ if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) ++ 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 ++ * ++ * 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 ++#include ++#include ++ ++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..6cb71385 +--- /dev/null ++++ b/src/gst/gstpwaudiosink.c +@@ -0,0 +1,200 @@ ++/* PipeWire ++ * ++ * Copyright © 2018 Wim Taymans ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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; ++} ++ ++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 "); ++ ++ 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 ++ * ++ * 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 ++ * ++ * 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 "); ++ ++ 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 ++ * ++ * 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.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch deleted file mode 100644 index 84b35e64..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch +++ /dev/null @@ -1,1249 +0,0 @@ -From 2570b2f404ce094098e2244833ab7dddf62d02a0 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -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: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpipewire.c | 8 +- - src/gst/gstpwaudioringbuffer.c | 542 +++++++++++++++++++++++++++++++++ - src/gst/gstpwaudioringbuffer.h | 83 +++++ - src/gst/gstpwaudiosink.c | 200 ++++++++++++ - src/gst/gstpwaudiosink.h | 48 +++ - src/gst/gstpwaudiosrc.c | 200 ++++++++++++ - src/gst/gstpwaudiosrc.h | 48 +++ - src/gst/meson.build | 6 + - 8 files changed, 1134 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..989b2cd7 ---- /dev/null -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -0,0 +1,542 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 -+#include -+ -+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); -+ -+ GST_DEBUG_OBJECT (self->elem, "got stream state: %s", -+ pw_stream_state_as_string (state)); -+ -+ switch (state) { -+ case PW_STREAM_STATE_UNCONNECTED: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream disconnected unexpectedly"), (NULL)); -+ break; -+ case PW_STREAM_STATE_CONNECTING: -+ case PW_STREAM_STATE_CONFIGURE: -+ case PW_STREAM_STATE_READY: -+ case PW_STREAM_STATE_PAUSED: -+ case PW_STREAM_STATE_STREAMING: -+ break; -+ case PW_STREAM_STATE_ERROR: -+ GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -+ ("stream error: %s", error), (NULL)); -+ 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_format_changed (void *data, 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]; -+ -+ 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_finish_format (self->stream, 0, 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_WARNING_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, -+ .format_changed = on_stream_format_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 */ -+ -+ if (self->props->properties) { -+ props = pw_properties_new (NULL, NULL); -+ gst_structure_foreach (self->props->properties, copy_properties, props); -+ } else { -+ props = NULL; -+ } -+ -+ 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; -+ -+ /* 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 READY"); -+ -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) -+ 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 -+ * -+ * 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 -+#include -+#include -+ -+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..6cb71385 ---- /dev/null -+++ b/src/gst/gstpwaudiosink.c -@@ -0,0 +1,200 @@ -+/* PipeWire -+ * -+ * Copyright © 2018 Wim Taymans -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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; -+} -+ -+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 "); -+ -+ 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 -+ * -+ * 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 -+ * -+ * 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 "); -+ -+ 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 -+ * -+ * 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.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch new file mode 100644 index 00000000..bc6559f9 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch @@ -0,0 +1,76 @@ +From d2cd5c499528dcd0b6c25a4050a6b4d76c4aa459 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Thu, 11 Jul 2019 16:21:17 +0300 +Subject: [PATCH] gst/pwaudioringbuffer: request pause/play on the appropriate + stream state changes + +This allows the client to properly go to PAUSED when the session manager +unlinks the stream and go again to PLAYING when the sm re-links it. +This allows the session manager to implement policies without letting +the client pipeline freeze (in the absence of a running audio clock) +when it is unlinked. Note that in case the client doesn't handle the +request, there is still no issue. Like in pulseaudio, the clock just +freezes, so the pipeline stops progressing. + +This is similar to the pulseaudio cork/uncork mechanism. + +Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] +--- + src/gst/gstpwaudioringbuffer.c | 27 +++++++++++++++++++++++---- + 1 file changed, 23 insertions(+), 4 deletions(-) + +diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c +index 989b2cd7..97350f38 100644 +--- a/src/gst/gstpwaudioringbuffer.c ++++ b/src/gst/gstpwaudioringbuffer.c +@@ -202,11 +202,16 @@ 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)); +@@ -214,12 +219,26 @@ on_stream_state_changed (void *data, enum pw_stream_state old, + case PW_STREAM_STATE_CONNECTING: + case PW_STREAM_STATE_CONFIGURE: + case PW_STREAM_STATE_READY: ++ break; + case PW_STREAM_STATE_PAUSED: +- case PW_STREAM_STATE_STREAMING: ++ 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_ERROR: +- GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, +- ("stream error: %s", error), (NULL)); ++ 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); +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch deleted file mode 100644 index 7d0bc5a2..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 9a01115acc2316908afaed004e2f042125b7f5e3 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Thu, 11 Jul 2019 16:21:17 +0300 -Subject: [PATCH] gst/pwaudioringbuffer: request pause/play on the appropriate - stream state changes - -This allows the client to properly go to PAUSED when the session manager -unlinks the stream and go again to PLAYING when the sm re-links it. -This allows the session manager to implement policies without letting -the client pipeline freeze (in the absence of a running audio clock) -when it is unlinked. Note that in case the client doesn't handle the -request, there is still no issue. Like in pulseaudio, the clock just -freezes, so the pipeline stops progressing. - -This is similar to the pulseaudio cork/uncork mechanism. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudioringbuffer.c | 27 +++++++++++++++++++++++---- - 1 file changed, 23 insertions(+), 4 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 989b2cd7..97350f38 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -202,11 +202,16 @@ 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)); -@@ -214,12 +219,26 @@ on_stream_state_changed (void *data, enum pw_stream_state old, - case PW_STREAM_STATE_CONNECTING: - case PW_STREAM_STATE_CONFIGURE: - case PW_STREAM_STATE_READY: -+ break; - case PW_STREAM_STATE_PAUSED: -- case PW_STREAM_STATE_STREAMING: -+ 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_ERROR: -- GST_ELEMENT_ERROR (self->elem, RESOURCE, FAILED, -- ("stream error: %s", error), (NULL)); -+ 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); --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch new file mode 100644 index 00000000..eef08af8 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch @@ -0,0 +1,35 @@ +From df47442f5396897f8bdba5a37699d21f0e4da85f Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Thu, 11 Jul 2019 16:34:35 +0300 +Subject: [PATCH] gst/pwaudioringbuffer: wait only for STREAM_STATE_CONFIGURE + when starting + +The CONFIGURE state is reached when the pw_client_node is exported, +while the READY state requires the session manager to try and link +the stream. If the SM does not want to link the stream due to policy, +the client should not hang there forever. + +Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] +--- + src/gst/gstpwaudioringbuffer.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c +index 97350f38..3efec6ec 100644 +--- a/src/gst/gstpwaudioringbuffer.c ++++ b/src/gst/gstpwaudioringbuffer.c +@@ -442,9 +442,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, + params, 1) < 0) + goto start_error; + +- GST_DEBUG_OBJECT (self->elem, "waiting for stream READY"); ++ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); + +- if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) ++ if (!wait_for_stream_state (self, PW_STREAM_STATE_CONFIGURE)) + goto start_error; + + pw_thread_loop_unlock (self->main_loop); +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch deleted file mode 100644 index 124a3804..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch +++ /dev/null @@ -1,35 +0,0 @@ -From f4903fe9c356b58737eb33fcfa389e006d18f801 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Thu, 11 Jul 2019 16:34:35 +0300 -Subject: [PATCH] gst/pwaudioringbuffer: wait only for STREAM_STATE_CONFIGURE - when starting - -The CONFIGURE state is reached when the pw_client_node is exported, -while the READY state requires the session manager to try and link -the stream. If the SM does not want to link the stream due to policy, -the client should not hang there forever. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudioringbuffer.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 97350f38..3efec6ec 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -442,9 +442,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - params, 1) < 0) - goto start_error; - -- GST_DEBUG_OBJECT (self->elem, "waiting for stream READY"); -+ GST_DEBUG_OBJECT (self->elem, "waiting for stream CONFIGURE"); - -- if (!wait_for_stream_state (self, PW_STREAM_STATE_READY)) -+ if (!wait_for_stream_state (self, PW_STREAM_STATE_CONFIGURE)) - goto start_error; - - pw_thread_loop_unlock (self->main_loop); --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch new file mode 100644 index 00000000..1f56b1c4 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch @@ -0,0 +1,37 @@ +From 30622ed39774d3a2d3133effbc73caac5fb12596 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Thu, 11 Jul 2019 17:07:15 +0300 +Subject: [PATCH] gst/pwaudiosink: set the default latency time (buffer size) + to be 21.3ms + +This is to solve underrun issues that seem to appear with the default +10ms latency that GstBaseAudioSink has. +Hopefully in the future we will have a better mechanism to pick +the appropriate latency instead of hardcoding it here. + +Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] +--- + src/gst/gstpwaudiosink.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c +index 6cb71385..069996c3 100644 +--- a/src/gst/gstpwaudiosink.c ++++ b/src/gst/gstpwaudiosink.c +@@ -57,6 +57,13 @@ 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 +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch new file mode 100644 index 00000000..c42e56b1 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch @@ -0,0 +1,41 @@ +From 23e8913172cb50d8023aaa7522e196d087f9c785 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Tue, 20 Aug 2019 18:33:35 +0300 +Subject: [PATCH] gst: pwaudioringbuffer: set node.latency to get scheduled + correctly in capture mode + +Upstream-Status: Pending +--- + src/gst/gstpwaudioringbuffer.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c +index 3efec6ec..8136b815 100644 +--- a/src/gst/gstpwaudioringbuffer.c ++++ b/src/gst/gstpwaudioringbuffer.c +@@ -402,11 +402,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, + + /* construct param & props objects */ + ++ props = pw_properties_new (NULL, NULL); + if (self->props->properties) { +- props = pw_properties_new (NULL, NULL); + gst_structure_foreach (self->props->properties, copy_properties, props); +- } else { +- props = NULL; + } + + spa_pod_builder_init (&b, buffer, sizeof (buffer)); +@@ -423,6 +421,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, + 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); ++ + /* connect stream */ + + pw_thread_loop_lock (self->main_loop); +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch deleted file mode 100644 index 9ae8eab3..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 4d8e0de16717f250d22b24f335df8f27c67f2c52 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Thu, 11 Jul 2019 17:07:15 +0300 -Subject: [PATCH] gst/pwaudiosink: set the default latency time (buffer size) - to be 21.3ms - -This is to solve underrun issues that seem to appear with the default -10ms latency that GstBaseAudioSink has. -Hopefully in the future we will have a better mechanism to pick -the appropriate latency instead of hardcoding it here. - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140] ---- - src/gst/gstpwaudiosink.c | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/src/gst/gstpwaudiosink.c b/src/gst/gstpwaudiosink.c -index 6cb71385..069996c3 100644 ---- a/src/gst/gstpwaudiosink.c -+++ b/src/gst/gstpwaudiosink.c -@@ -57,6 +57,13 @@ 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 --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch deleted file mode 100644 index a6d7724f..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch +++ /dev/null @@ -1,41 +0,0 @@ -From a9fb1fa9ce662ee3f06afda5fd9eb2182520ea4d Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Tue, 20 Aug 2019 18:33:35 +0300 -Subject: [PATCH] gst: pwaudioringbuffer: set node.latency to get scheduled - correctly in capture mode - -Upstream-Status: Pending ---- - src/gst/gstpwaudioringbuffer.c | 7 ++++--- - 1 file changed, 4 insertions(+), 3 deletions(-) - -diff --git a/src/gst/gstpwaudioringbuffer.c b/src/gst/gstpwaudioringbuffer.c -index 3efec6ec..8136b815 100644 ---- a/src/gst/gstpwaudioringbuffer.c -+++ b/src/gst/gstpwaudioringbuffer.c -@@ -402,11 +402,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - - /* construct param & props objects */ - -+ props = pw_properties_new (NULL, NULL); - if (self->props->properties) { -- props = pw_properties_new (NULL, NULL); - gst_structure_foreach (self->props->properties, copy_properties, props); -- } else { -- props = NULL; - } - - spa_pod_builder_init (&b, buffer, sizeof (buffer)); -@@ -423,6 +421,9 @@ gst_pw_audio_ring_buffer_acquire (GstAudioRingBuffer *buf, - 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); -+ - /* connect stream */ - - pw_thread_loop_lock (self->main_loop); --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-meson-revert-version-check-to-require-meson-0.47-not.patch new file mode 100644 index 00000000..462a680e --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-meson-revert-version-check-to-require-meson-0.47-not.patch @@ -0,0 +1,30 @@ +From 6b65e729e7609b75f10cbb8e9db736f6273ea01b Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +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 c0bb7470..07117cbb 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.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch deleted file mode 100644 index 8d8d4d11..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch +++ /dev/null @@ -1,37 +0,0 @@ -From b80645ad1b348a99b2cbdc170e122dc06d367ea9 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Wed, 18 Sep 2019 12:31:36 +0300 -Subject: [PATCH] alsa: do not expose non-interleaved formats, since they won't - work - -This bug appeared when testing bluez-alsa nodes with pipewire. -bluez-alsa exposes also non-interleaved formats and if such a format -is picked, then nothing works because the converters are not working - -Upstream-Status: Inappropriate [workaround] ---- - spa/plugins/alsa/alsa-utils.c | 2 ++ - 1 file changed, 2 insertions(+) - -diff --git a/spa/plugins/alsa/alsa-utils.c b/spa/plugins/alsa/alsa-utils.c -index e8548345..76c81c7c 100644 ---- a/spa/plugins/alsa/alsa-utils.c -+++ b/spa/plugins/alsa/alsa-utils.c -@@ -289,12 +289,14 @@ spa_alsa_enum_format(struct state *state, int seq, uint32_t start, uint32_t num, - spa_pod_builder_id(&b, fi->spa_format); - spa_pod_builder_id(&b, fi->spa_format); - } -+ /* - if (snd_pcm_access_mask_test(amask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) && - fi->spa_pformat != SPA_AUDIO_FORMAT_UNKNOWN) { - if (j++ == 0) - spa_pod_builder_id(&b, fi->spa_pformat); - spa_pod_builder_id(&b, fi->spa_pformat); - } -+ */ - } - } - if (j > 1) --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch new file mode 100644 index 00000000..325ade7a --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch @@ -0,0 +1,5715 @@ +From cc47e191c42b836811c5fca1122505375a4e080b Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Thu, 23 May 2019 18:59:05 +0300 +Subject: [PATCH] extensions: implement new session manager extension + +This extension, implemented in module-session-manager, implements +a set of objects that are useful for session managers. + +Upstream-Status: Pending +--- + src/extensions/meson.build | 9 + + src/extensions/session-manager.h | 34 + + .../session-manager/impl-interfaces.h | 329 +++ + src/extensions/session-manager/interfaces.h | 465 ++++ + src/extensions/session-manager/introspect.h | 131 + + src/extensions/session-manager/keys.h | 40 + + src/modules/meson.build | 17 + + src/modules/module-session-manager.c | 56 + + .../module-session-manager/client-endpoint.c | 270 +++ + .../module-session-manager/client-endpoint.h | 60 + + .../module-session-manager/client-session.c | 270 +++ + .../module-session-manager/client-session.h | 62 + + .../module-session-manager/endpoint-link.c | 359 +++ + .../module-session-manager/endpoint-link.h | 64 + + .../module-session-manager/endpoint-stream.c | 329 +++ + .../module-session-manager/endpoint-stream.h | 64 + + src/modules/module-session-manager/endpoint.c | 343 +++ + src/modules/module-session-manager/endpoint.h | 61 + + .../module-session-manager/protocol-native.c | 2125 +++++++++++++++++ + src/modules/module-session-manager/session.c | 341 +++ + src/modules/module-session-manager/session.h | 61 + + src/pipewire/pipewire.c | 6 + + src/pipewire/type.h | 7 +- + 23 files changed, 5502 insertions(+), 1 deletion(-) + create mode 100644 src/extensions/session-manager.h + create mode 100644 src/extensions/session-manager/impl-interfaces.h + create mode 100644 src/extensions/session-manager/interfaces.h + create mode 100644 src/extensions/session-manager/introspect.h + create mode 100644 src/extensions/session-manager/keys.h + create mode 100644 src/modules/module-session-manager.c + create mode 100644 src/modules/module-session-manager/client-endpoint.c + create mode 100644 src/modules/module-session-manager/client-endpoint.h + create mode 100644 src/modules/module-session-manager/client-session.c + create mode 100644 src/modules/module-session-manager/client-session.h + create mode 100644 src/modules/module-session-manager/endpoint-link.c + create mode 100644 src/modules/module-session-manager/endpoint-link.h + create mode 100644 src/modules/module-session-manager/endpoint-stream.c + create mode 100644 src/modules/module-session-manager/endpoint-stream.h + create mode 100644 src/modules/module-session-manager/endpoint.c + create mode 100644 src/modules/module-session-manager/endpoint.h + create mode 100644 src/modules/module-session-manager/protocol-native.c + create mode 100644 src/modules/module-session-manager/session.c + create mode 100644 src/modules/module-session-manager/session.h + +diff --git a/src/extensions/meson.build b/src/extensions/meson.build +index a7f5d3cb..95377faa 100644 +--- a/src/extensions/meson.build ++++ b/src/extensions/meson.build +@@ -1,6 +1,15 @@ ++pipewire_ext_sm_headers = [ ++ 'session-manager/impl-interfaces.h', ++ 'session-manager/interfaces.h', ++ 'session-manager/introspect.h', ++ 'session-manager/keys.h', ++] ++ + pipewire_ext_headers = [ + 'client-node.h', + 'protocol-native.h', ++ 'session-manager.h', + ] + ++install_headers(pipewire_ext_sm_headers, subdir : 'pipewire/extensions/session-manager') + install_headers(pipewire_ext_headers, subdir : 'pipewire/extensions') +diff --git a/src/extensions/session-manager.h b/src/extensions/session-manager.h +new file mode 100644 +index 00000000..95e759b0 +--- /dev/null ++++ b/src/extensions/session-manager.h +@@ -0,0 +1,34 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 PIPEWIRE_EXT_SESSION_MANAGER_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_H ++ ++#include "session-manager/introspect.h" ++#include "session-manager/interfaces.h" ++#include "session-manager/impl-interfaces.h" ++#include "session-manager/keys.h" ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_H */ +diff --git a/src/extensions/session-manager/impl-interfaces.h b/src/extensions/session-manager/impl-interfaces.h +new file mode 100644 +index 00000000..66daa0b9 +--- /dev/null ++++ b/src/extensions/session-manager/impl-interfaces.h +@@ -0,0 +1,329 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H ++ ++#include ++#include ++#include ++ ++#include "introspect.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY 0 ++struct pw_client_endpoint_proxy { struct spa_interface iface; }; ++ ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID 0 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID 1 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM 2 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM 3 ++#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 4 ++ ++struct pw_client_endpoint_proxy_events { ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Sets the id of the \a endpoint. ++ * ++ * On endpoint implementations, this is called by the server to notify ++ * the implementation of the assigned global id of the endpoint. The ++ * implementation is obliged to set this id in the ++ * #struct pw_endpoint_info \a id field. The implementation should also ++ * not emit the info() event before this method is called. ++ * ++ * \param endpoint a #pw_endpoint ++ * \param id the global id assigned to this endpoint ++ * ++ * \return 0 on success ++ * -EINVAL when the id has already been set ++ * -ENOTSUP on the server-side endpoint implementation ++ */ ++ int (*set_id) (void *endpoint, uint32_t id); ++ ++ /** ++ * Sets the session id of the \a endpoint. ++ * ++ * On endpoints that are not session masters, this method notifies ++ * the implementation that it has been associated with a session. ++ * The implementation is obliged to set this id in the ++ * #struct pw_endpoint_info \a session_id field. ++ * ++ * \param endpoint a #pw_endpoint ++ * \param id the session id associated with this endpoint ++ * ++ * \return 0 on success ++ * -EINVAL when the session id has already been set ++ * -ENOTSUP when the endpoint is a session master ++ */ ++ int (*set_session_id) (void *endpoint, uint32_t session_id); ++ ++ /** ++ * Set the configurable parameter in \a endpoint. ++ * ++ * Usually, \a param will be obtained from enum_params and then ++ * modified but it is also possible to set another spa_pod ++ * as long as its keys and types match a supported object. ++ * ++ * Objects with property keys that are not known are ignored. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param endpoint a #struct pw_endpoint ++ * \param id the parameter id to configure ++ * \param flags additional flags ++ * \param param the parameter to configure ++ * ++ * \return 0 on success ++ * -EINVAL when \a endpoint is NULL ++ * -ENOTSUP when there are no parameters implemented on \a endpoint ++ * -ENOENT the parameter is unknown ++ */ ++ int (*set_param) (void *endpoint, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ /** ++ * Set a parameter on \a stream_id of \a endpoint. ++ * ++ * When \a param is NULL, the parameter will be unset. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param endpoint a #struct pw_endpoint ++ * \param stream_id the stream to configure ++ * \param id the parameter id to set ++ * \param flags optional flags ++ * \param param a #struct spa_pod with the parameter to set ++ * \return 0 on success ++ * 1 on success, the value of \a param might have been ++ * changed depending on \a flags and the final value can ++ * be found by doing stream_enum_params. ++ * -EINVAL when \a endpoint is NULL or invalid arguments are given ++ * -ESRCH when the type or size of a property is not correct. ++ * -ENOENT when the param id is not found ++ */ ++ int (*stream_set_param) (void *endpoint, uint32_t stream_id, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++}; ++ ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE 1 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE 2 ++#define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM 3 ++ ++struct pw_client_endpoint_proxy_methods { ++#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_client_endpoint_proxy_events *events, ++ void *data); ++ ++ /** Update endpoint information */ ++ int (*update) (void *object, ++#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_ENDPOINT_UPDATE_INFO (1 << 1) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info); ++ ++ /** Update stream information */ ++ int (*stream_update) (void *object, ++ uint32_t stream_id, ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO (1 << 1) ++#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED (1 << 2) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info); ++}; ++ ++#define pw_client_endpoint_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_client_endpoint_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_client_endpoint_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_client_endpoint_proxy_add_listener(o,...) pw_client_endpoint_proxy_method(o,add_listener,0,__VA_ARGS__) ++#define pw_client_endpoint_proxy_update(o,...) pw_client_endpoint_proxy_method(o,update,0,__VA_ARGS__) ++#define pw_client_endpoint_proxy_stream_update(o,...) pw_client_endpoint_proxy_method(o,stream_update,0,__VA_ARGS__) ++ ++ ++#define PW_VERSION_CLIENT_SESSION_PROXY 0 ++struct pw_client_session_proxy { struct spa_interface iface; }; ++ ++#define PW_CLIENT_SESSION_PROXY_EVENT_SET_ID 0 ++#define PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM 1 ++#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM 2 ++#define PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK 3 ++#define PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK 4 ++#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE 5 ++#define PW_CLIENT_SESSION_PROXY_EVENT_NUM 6 ++ ++struct pw_client_session_proxy_events { ++#define PW_VERSION_CLIENT_SESSION_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Sets the id of the \a session. ++ * ++ * On session implementations, this is called by the server to notify ++ * the implementation of the assigned global id of the session. The ++ * implementation is obliged to set this id in the ++ * #struct pw_session_info \a id field. The implementation should also ++ * not emit the info() event before this method is called. ++ * ++ * \param session a #pw_session ++ * \param id the global id assigned to this session ++ * ++ * \return 0 on success ++ * -EINVAL when the id has already been set ++ * -ENOTSUP on the server-side session implementation ++ */ ++ int (*set_id) (void *session, uint32_t id); ++ ++ /** ++ * Set the configurable parameter in \a session. ++ * ++ * Usually, \a param will be obtained from enum_params and then ++ * modified but it is also possible to set another spa_pod ++ * as long as its keys and types match a supported object. ++ * ++ * Objects with property keys that are not known are ignored. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param session a #struct pw_session ++ * \param id the parameter id to configure ++ * \param flags additional flags ++ * \param param the parameter to configure ++ * ++ * \return 0 on success ++ * -EINVAL when \a session is NULL ++ * -ENOTSUP when there are no parameters implemented on \a session ++ * -ENOENT the parameter is unknown ++ */ ++ int (*set_param) (void *session, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ /** ++ * Set a parameter on \a link_id of \a session. ++ * ++ * When \a param is NULL, the parameter will be unset. ++ * ++ * This function must be called from the main thread. ++ * ++ * \param session a #struct pw_session ++ * \param link_id the link to configure ++ * \param id the parameter id to set ++ * \param flags optional flags ++ * \param param a #struct spa_pod with the parameter to set ++ * \return 0 on success ++ * 1 on success, the value of \a param might have been ++ * changed depending on \a flags and the final value can ++ * be found by doing link_enum_params. ++ * -EINVAL when \a session is NULL or invalid arguments are given ++ * -ESRCH when the type or size of a property is not correct. ++ * -ENOENT when the param id is not found ++ */ ++ int (*link_set_param) (void *session, uint32_t link_id, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ int (*create_link) (void *session, const struct spa_dict *props); ++ ++ int (*destroy_link) (void *session, uint32_t link_id); ++ ++ int (*link_request_state) (void *session, uint32_t link_id, uint32_t state); ++}; ++ ++#define PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_CLIENT_SESSION_PROXY_METHOD_UPDATE 1 ++#define PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE 2 ++#define PW_CLIENT_SESSION_PROXY_METHOD_NUM 3 ++ ++struct pw_client_session_proxy_methods { ++#define PW_VERSION_CLIENT_SESSION_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_client_session_proxy_events *events, ++ void *data); ++ ++ /** Update session information */ ++ int (*update) (void *object, ++#define PW_CLIENT_SESSION_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_SESSION_UPDATE_INFO (1 << 1) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info); ++ ++ /** Update link information */ ++ int (*link_update) (void *object, ++ uint32_t link_id, ++#define PW_CLIENT_SESSION_LINK_UPDATE_PARAMS (1 << 0) ++#define PW_CLIENT_SESSION_LINK_UPDATE_INFO (1 << 1) ++#define PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED (1 << 2) ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info); ++}; ++ ++#define pw_client_session_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_client_session_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_client_session_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_client_session_proxy_add_listener(o,...) pw_client_session_proxy_method(o,add_listener,0,__VA_ARGS__) ++#define pw_client_session_proxy_update(o,...) pw_client_session_proxy_method(o,update,0,__VA_ARGS__) ++#define pw_client_session_proxy_link_update(o,...) pw_client_session_proxy_method(o,link_update,0,__VA_ARGS__) ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H */ +diff --git a/src/extensions/session-manager/interfaces.h b/src/extensions/session-manager/interfaces.h +new file mode 100644 +index 00000000..0651e8bf +--- /dev/null ++++ b/src/extensions/session-manager/interfaces.h +@@ -0,0 +1,465 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H ++ ++#include ++#include ++ ++#include "introspect.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_VERSION_SESSION_PROXY 0 ++struct pw_session_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_PROXY 0 ++struct pw_endpoint_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_STREAM_PROXY 0 ++struct pw_endpoint_stream_proxy { struct spa_interface iface; }; ++#define PW_VERSION_ENDPOINT_LINK_PROXY 0 ++struct pw_endpoint_link_proxy { struct spa_interface iface; }; ++ ++/* Session */ ++ ++#define PW_SESSION_PROXY_EVENT_INFO 0 ++#define PW_SESSION_PROXY_EVENT_PARAM 1 ++#define PW_SESSION_PROXY_EVENT_NUM 2 ++ ++struct pw_session_proxy_events { ++#define PW_VERSION_SESSION_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify session info ++ * ++ * \param info info about the session ++ */ ++ void (*info) (void *object, const struct pw_session_info *info); ++ ++ /** ++ * Notify a session param ++ * ++ * Event emited as a result of the enum_params method. ++ * ++ * \param seq the sequence number of the request ++ * \param id the param id ++ * \param index the param index ++ * \param next the param index of the next param ++ * \param param the parameter ++ */ ++ void (*param) (void *object, int seq, ++ uint32_t id, uint32_t index, uint32_t next, ++ const struct spa_pod *param); ++}; ++ ++#define PW_SESSION_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_SESSION_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_SESSION_PROXY_METHOD_SET_PARAM 3 ++#define PW_SESSION_PROXY_METHOD_CREATE_LINK 4 ++#define PW_SESSION_PROXY_METHOD_NUM 5 ++ ++struct pw_session_proxy_methods { ++#define PW_VERSION_SESSION_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_session_proxy_events *events, ++ void *data); ++ ++ /** ++ * Subscribe to parameter changes ++ * ++ * Automatically emit param events for the given ids when ++ * they are changed. ++ * ++ * \param ids an array of param ids ++ * \param n_ids the number of ids in \a ids ++ */ ++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); ++ ++ /** ++ * Enumerate session parameters ++ * ++ * Start enumeration of session parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \param start the start index or 0 for the first param ++ * \param num the maximum number of params to retrieve ++ * \param filter a param filter or NULL ++ */ ++ int (*enum_params) (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter); ++ ++ /** ++ * Set a parameter on the session ++ * ++ * \param id the parameter id to set ++ * \param flags extra parameter flags ++ * \param param the parameter to set ++ */ ++ int (*set_param) (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ int (*create_link) (void *object, const struct spa_dict *props); ++}; ++ ++#define pw_session_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_session_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_session_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_session_proxy_add_listener(c,...) pw_session_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_session_proxy_subscribe_params(c,...) pw_session_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_session_proxy_enum_params(c,...) pw_session_proxy_method(c,enum_params,0,__VA_ARGS__) ++#define pw_session_proxy_create_link(c,...) pw_session_proxy_method(c,create_link,0,__VA_ARGS__) ++ ++/* Endpoint */ ++ ++#define PW_ENDPOINT_PROXY_EVENT_INFO 0 ++#define PW_ENDPOINT_PROXY_EVENT_PARAM 1 ++#define PW_ENDPOINT_PROXY_EVENT_NUM 2 ++ ++struct pw_endpoint_proxy_events { ++#define PW_VERSION_ENDPOINT_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint info ++ * ++ * \param info info about the endpoint ++ */ ++ void (*info) (void *object, const struct pw_endpoint_info *info); ++ ++ /** ++ * Notify a endpoint param ++ * ++ * Event emited as a result of the enum_params method. ++ * ++ * \param seq the sequence number of the request ++ * \param id the param id ++ * \param index the param index ++ * \param next the param index of the next param ++ * \param param the parameter ++ */ ++ void (*param) (void *object, int seq, ++ uint32_t id, uint32_t index, uint32_t next, ++ const struct spa_pod *param); ++}; ++ ++#define PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_PROXY_METHOD_NUM 4 ++ ++struct pw_endpoint_proxy_methods { ++#define PW_VERSION_ENDPOINT_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_proxy_events *events, ++ void *data); ++ ++ /** ++ * Subscribe to parameter changes ++ * ++ * Automatically emit param events for the given ids when ++ * they are changed. ++ * ++ * \param ids an array of param ids ++ * \param n_ids the number of ids in \a ids ++ */ ++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); ++ ++ /** ++ * Enumerate endpoint parameters ++ * ++ * Start enumeration of endpoint parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \param start the start index or 0 for the first param ++ * \param num the maximum number of params to retrieve ++ * \param filter a param filter or NULL ++ */ ++ int (*enum_params) (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter); ++ ++ /** ++ * Set a parameter on the endpoint ++ * ++ * \param id the parameter id to set ++ * \param flags extra parameter flags ++ * \param param the parameter to set ++ */ ++ int (*set_param) (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++}; ++ ++#define pw_endpoint_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_proxy_add_listener(c,...) pw_endpoint_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_proxy_subscribe_params(c,...) pw_endpoint_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_proxy_enum_params(c,...) pw_endpoint_proxy_method(c,enum_params,0,__VA_ARGS__) ++ ++/* Endpoint Stream */ ++ ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_INFO 0 ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM 1 ++#define PW_ENDPOINT_STREAM_PROXY_EVENT_NUM 2 ++ ++struct pw_endpoint_stream_proxy_events { ++#define PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint stream info ++ * ++ * \param info info about the endpoint stream ++ */ ++ void (*info) (void *object, const struct pw_endpoint_stream_info *info); ++ ++ /** ++ * Notify a endpoint stream param ++ * ++ * Event emited as a result of the enum_params method. ++ * ++ * \param seq the sequence number of the request ++ * \param id the param id ++ * \param index the param index ++ * \param next the param index of the next param ++ * \param param the parameter ++ */ ++ void (*param) (void *object, int seq, ++ uint32_t id, uint32_t index, uint32_t next, ++ const struct spa_pod *param); ++}; ++ ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_STREAM_PROXY_METHOD_NUM 4 ++ ++struct pw_endpoint_stream_proxy_methods { ++#define PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_stream_proxy_events *events, ++ void *data); ++ ++ /** ++ * Subscribe to parameter changes ++ * ++ * Automatically emit param events for the given ids when ++ * they are changed. ++ * ++ * \param ids an array of param ids ++ * \param n_ids the number of ids in \a ids ++ */ ++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); ++ ++ /** ++ * Enumerate stream parameters ++ * ++ * Start enumeration of stream parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \param start the start index or 0 for the first param ++ * \param num the maximum number of params to retrieve ++ * \param filter a param filter or NULL ++ */ ++ int (*enum_params) (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter); ++ ++ /** ++ * Set a parameter on the stream ++ * ++ * \param id the parameter id to set ++ * \param flags extra parameter flags ++ * \param param the parameter to set ++ */ ++ int (*set_param) (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++}; ++ ++#define pw_endpoint_stream_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_stream_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_stream_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_stream_proxy_add_listener(c,...) pw_endpoint_stream_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_stream_proxy_subscribe_params(c,...) pw_endpoint_stream_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_stream_proxy_enum_params(c,...) pw_endpoint_stream_proxy_method(c,enum_params,0,__VA_ARGS__) ++ ++/* Endpoint Link */ ++ ++#define PW_ENDPOINT_LINK_PROXY_EVENT_INFO 0 ++#define PW_ENDPOINT_LINK_PROXY_EVENT_PARAM 1 ++#define PW_ENDPOINT_LINK_PROXY_EVENT_NUM 2 ++ ++struct pw_endpoint_link_proxy_events { ++#define PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ /** ++ * Notify endpoint link info ++ * ++ * \param info info about the endpoint link ++ */ ++ void (*info) (void *object, const struct pw_endpoint_link_info *info); ++ ++ /** ++ * Notify a endpoint link param ++ * ++ * Event emited as a result of the enum_params method. ++ * ++ * \param seq the sequence number of the request ++ * \param id the param id ++ * \param index the param index ++ * \param next the param index of the next param ++ * \param param the parameter ++ */ ++ void (*param) (void *object, int seq, ++ uint32_t id, uint32_t index, uint32_t next, ++ const struct spa_pod *param); ++}; ++ ++#define PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER 0 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS 1 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS 2 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM 3 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE 4 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY 5 ++#define PW_ENDPOINT_LINK_PROXY_METHOD_NUM 6 ++ ++struct pw_endpoint_link_proxy_methods { ++#define PW_VERSION_ENDPOINT_LINK_PROXY_METHODS 0 ++ uint32_t version; /**< version of this structure */ ++ ++ int (*add_listener) (void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_link_proxy_events *events, ++ void *data); ++ ++ /** ++ * Subscribe to parameter changes ++ * ++ * Automatically emit param events for the given ids when ++ * they are changed. ++ * ++ * \param ids an array of param ids ++ * \param n_ids the number of ids in \a ids ++ */ ++ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); ++ ++ /** ++ * Enumerate link parameters ++ * ++ * Start enumeration of link parameters. For each param, a ++ * param event will be emited. ++ * ++ * \param seq a sequence number returned in the reply ++ * \param id the parameter id to enumerate ++ * \param start the start index or 0 for the first param ++ * \param num the maximum number of params to retrieve ++ * \param filter a param filter or NULL ++ */ ++ int (*enum_params) (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter); ++ ++ /** ++ * Set a parameter on the link ++ * ++ * \param id the parameter id to set ++ * \param flags extra parameter flags ++ * \param param the parameter to set ++ */ ++ int (*set_param) (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param); ++ ++ int (*request_state) (void *object, enum pw_endpoint_link_state state); ++ ++ int (*destroy) (void *object); ++ ++}; ++ ++#define pw_endpoint_link_proxy_method(o,method,version,...) \ ++({ \ ++ int _res = -ENOTSUP; \ ++ struct pw_endpoint_link_proxy *_p = o; \ ++ spa_interface_call_res(&_p->iface, \ ++ struct pw_endpoint_link_proxy_methods, _res, \ ++ method, version, ##__VA_ARGS__); \ ++ _res; \ ++}) ++ ++#define pw_endpoint_link_proxy_add_listener(c,...) pw_endpoint_link_proxy_method(c,add_listener,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_subscribe_params(c,...) pw_endpoint_link_proxy_method(c,subscribe_params,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_enum_params(c,...) pw_endpoint_link_proxy_method(c,enum_params,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_request_state(c,...) pw_endpoint_link_proxy_method(c,request_state,0,__VA_ARGS__) ++#define pw_endpoint_link_proxy_destroy(c,...) pw_endpoint_link_proxy_method(c,destroy,0,__VA_ARGS__) ++ ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H */ +diff --git a/src/extensions/session-manager/introspect.h b/src/extensions/session-manager/introspect.h +new file mode 100644 +index 00000000..3b0e4113 +--- /dev/null ++++ b/src/extensions/session-manager/introspect.h +@@ -0,0 +1,131 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H ++ ++#include ++#include ++#include ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_KEY_ENDPOINT_ID "endpoint.id" ++#define PW_KEY_SESSION_ID "session.id" ++ ++enum pw_endpoint_direction { ++ PW_ENDPOINT_DIRECTION_SINK_INPUT = SPA_DIRECTION_INPUT, ++ PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT = SPA_DIRECTION_OUTPUT, ++ PW_ENDPOINT_DIRECTION_SOURCE, ++ PW_ENDPOINT_DIRECTION_SINK, ++}; ++ ++enum pw_endpoint_link_state { ++ PW_ENDPOINT_LINK_STATE_ERROR = -1, ++ PW_ENDPOINT_LINK_STATE_PREPARING, ++ PW_ENDPOINT_LINK_STATE_INACTIVE, ++ PW_ENDPOINT_LINK_STATE_ACTIVE, ++}; ++ ++struct pw_session_info { ++#define PW_VERSION_SESSION_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the session id (global) */ ++#define PW_SESSION_CHANGE_MASK_PROPS (1 << 0) ++#define PW_SESSION_CHANGE_MASK_PARAMS (1 << 1) ++#define PW_SESSION_CHANGE_MASK_ALL ((1 << 2)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_info { ++#define PW_VERSION_ENDPOINT_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the endpoint id (global) */ ++ char *name; /**< name of the endpoint */ ++ char *media_class; /**< media class of the endpoint */ ++ enum pw_endpoint_direction direction; /**< direction of the endpoint */ ++#define PW_ENDPOINT_FLAG_PROVIDES_SESSION (1 << 0) ++ uint32_t flags; /**< additional flags */ ++#define PW_ENDPOINT_CHANGE_MASK_STREAMS (1 << 0) ++#define PW_ENDPOINT_CHANGE_MASK_SESSION (1 << 1) ++#define PW_ENDPOINT_CHANGE_MASK_PROPS (1 << 2) ++#define PW_ENDPOINT_CHANGE_MASK_PARAMS (1 << 3) ++#define PW_ENDPOINT_CHANGE_MASK_ALL ((1 << 4)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ uint32_t n_streams; /**< number of streams available */ ++ uint32_t session_id; /**< the id of the controlling session */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_stream_info { ++#define PW_VERSION_ENDPOINT_STREAM_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the stream id (local or global) */ ++ uint32_t endpoint_id; /**< the endpoint id (global) */ ++ char *name; /**< name of the stream */ ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS (1 << 0) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS (1 << 1) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS (1 << 2) ++#define PW_ENDPOINT_STREAM_CHANGE_MASK_ALL ((1 << 3)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ struct spa_pod *link_params; /**< information for linking this stream */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++struct pw_endpoint_link_info { ++#define PW_VERSION_ENDPOINT_LINK_INFO 0 ++ uint32_t version; /**< version of this structure */ ++ uint32_t id; /**< the link id (global) */ ++ uint32_t session_id; /**< the session id (global) */ ++ uint32_t output_endpoint_id; /**< the output endpoint id (global) */ ++ uint32_t output_stream_id; /**< the output stream id (local or global) */ ++ uint32_t input_endpoint_id; /**< the input endpoint id (global) */ ++ uint32_t input_stream_id; /**< the input stream id (local or global) */ ++#define PW_ENDPOINT_LINK_CHANGE_MASK_STATE (1 << 0) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_PROPS (1 << 1) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS (1 << 2) ++#define PW_ENDPOINT_LINK_CHANGE_MASK_ALL ((1 << 3)-1) ++ uint32_t change_mask; /**< bitfield of changed fields since last call */ ++ enum pw_endpoint_link_state state; /**< the state of the link */ ++ char *error; /**< error string if state == ERROR */ ++ struct spa_dict *props; /**< extra properties */ ++ struct spa_param_info *params; /**< parameters */ ++ uint32_t n_params; /**< number of items in \a params */ ++}; ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H */ +diff --git a/src/extensions/session-manager/keys.h b/src/extensions/session-manager/keys.h +new file mode 100644 +index 00000000..a7167510 +--- /dev/null ++++ b/src/extensions/session-manager/keys.h +@@ -0,0 +1,40 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H ++#define PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define PW_KEY_ENDPOINT_ID "endpoint.id" ++#define PW_KEY_SESSION_ID "session.id" ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H */ +diff --git a/src/modules/meson.build b/src/modules/meson.build +index bec6f558..23e8bba3 100644 +--- a/src/modules/meson.build ++++ b/src/modules/meson.build +@@ -99,3 +99,20 @@ pipewire_module_adapter = shared_library('pipewire-module-adapter', + install_dir : modules_install_dir, + dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep], + ) ++ ++pipewire_module_session_manager = shared_library('pipewire-module-session-manager', ++ [ 'module-session-manager.c', ++ 'module-session-manager/client-endpoint.c', ++ 'module-session-manager/client-session.c', ++ 'module-session-manager/endpoint-link.c', ++ 'module-session-manager/endpoint-stream.c', ++ 'module-session-manager/endpoint.c', ++ 'module-session-manager/session.c', ++ 'module-session-manager/protocol-native.c', ++ ], ++ c_args : pipewire_module_c_args, ++ include_directories : [configinc, spa_inc], ++ install : true, ++ install_dir : modules_install_dir, ++ dependencies : [mathlib, dl_lib, pipewire_dep], ++) +diff --git a/src/modules/module-session-manager.c b/src/modules/module-session-manager.c +new file mode 100644 +index 00000000..dbea3357 +--- /dev/null ++++ b/src/modules/module-session-manager.c +@@ -0,0 +1,56 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include "config.h" ++ ++#include ++ ++/* client-endpoint.c */ ++int client_endpoint_factory_init(struct pw_module *module); ++/* client-session.c */ ++int client_session_factory_init(struct pw_module *module); ++/* protocol-native.c */ ++struct pw_protocol *pw_protocol_native_ext_session_manager_init(struct pw_core *core); ++ ++static const struct spa_dict_item module_props[] = { ++ { PW_KEY_MODULE_AUTHOR, "George Kiagiadakis " }, ++ { PW_KEY_MODULE_DESCRIPTION, "Implements objects for session management" }, ++ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, ++}; ++ ++SPA_EXPORT ++int pipewire__module_init(struct pw_module *module, const char *args) ++{ ++ struct pw_core *core = pw_module_get_core(module); ++ ++ client_endpoint_factory_init(module); ++ client_session_factory_init(module); ++ ++ pw_protocol_native_ext_session_manager_init(core); ++ ++ pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-endpoint.c b/src/modules/module-session-manager/client-endpoint.c +new file mode 100644 +index 00000000..0e501c9f +--- /dev/null ++++ b/src/modules/module-session-manager/client-endpoint.c +@@ -0,0 +1,270 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "client-endpoint.h" ++#include "endpoint.h" ++#include "endpoint-stream.h" ++ ++#include ++ ++#define NAME "client-endpoint" ++ ++struct factory_data { ++ struct pw_factory *factory; ++ struct pw_module *module; ++ struct spa_hook module_listener; ++}; ++ ++static struct endpoint_stream *find_stream(struct client_endpoint *this, uint32_t id) ++{ ++ struct endpoint_stream *s; ++ spa_list_for_each(s, &this->streams, link) { ++ if (s->id == id) ++ return s; ++ } ++ return NULL; ++} ++ ++static int client_endpoint_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info) ++{ ++ struct client_endpoint *this = object; ++ struct endpoint *endpoint = &this->endpoint; ++ ++ return endpoint_update(endpoint, change_mask, n_params, params, info); ++} ++ ++static int client_endpoint_stream_update(void *object, ++ uint32_t stream_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct client_endpoint *this = object; ++ struct endpoint *endpoint = &this->endpoint; ++ struct endpoint_stream *stream = find_stream(this, stream_id); ++ struct pw_properties *props = NULL; ++ ++ if (!stream) { ++ struct pw_core *core = pw_global_get_core(endpoint->global); ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ PW_KEY_ENDPOINT_ID, ++ NULL ++ }; ++ ++ stream = calloc(1, sizeof(struct endpoint_stream)); ++ if (!stream) ++ goto no_mem; ++ ++ props = pw_properties_new(NULL, NULL); ++ if (!props) ++ goto no_mem; ++ pw_properties_copy_keys (endpoint->props, props, keys); ++ ++ if (endpoint_stream_init(stream, stream_id, endpoint->info.id, ++ this, core, props) < 0) ++ goto no_mem; ++ ++ spa_list_append(&this->streams, &stream->link); ++ } ++ else if (change_mask & PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED) { ++ endpoint_stream_clear(stream); ++ spa_list_remove(&stream->link); ++ free(stream); ++ stream = NULL; ++ } ++ ++ return stream ? ++ endpoint_stream_update(stream, change_mask, n_params, params, info) ++ : 0; ++ ++ no_mem: ++ if (props) ++ pw_properties_free(props); ++ free(stream); ++ pw_log_error(NAME" %p: cannot update stream: no memory", this); ++ pw_resource_error(this->resource, -ENOMEM, ++ NAME" %p: cannot update stream: no memory", this); ++ return -ENOMEM; ++} ++ ++static struct pw_client_endpoint_proxy_methods methods = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, ++ .update = client_endpoint_update, ++ .stream_update = client_endpoint_stream_update, ++}; ++ ++static void client_endpoint_destroy(void *data) ++{ ++ struct client_endpoint *this = data; ++ struct endpoint_stream *s; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ spa_list_consume(s, &this->streams, link) { ++ endpoint_stream_clear(s); ++ spa_list_remove(&s->link); ++ free(s); ++ } ++ endpoint_clear(&this->endpoint); ++ spa_hook_remove(&this->resource_listener); ++ ++ free(this); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = client_endpoint_destroy, ++}; ++ ++static void *create_object(void *data, ++ struct pw_resource *owner_resource, ++ uint32_t type, ++ uint32_t version, ++ struct pw_properties *properties, ++ uint32_t new_id) ++{ ++ struct factory_data *d = data; ++ struct pw_factory *factory = d->factory; ++ struct client_endpoint *this; ++ struct pw_client *owner = pw_resource_get_client(owner_resource); ++ struct pw_core *core = pw_client_get_core(owner); ++ ++ this = calloc(1, sizeof(struct client_endpoint)); ++ if (this == NULL) ++ goto no_mem; ++ ++ spa_list_init(&this->streams); ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ if (!properties) ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); ++ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); ++ ++ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); ++ if (this->resource == NULL) ++ goto no_mem; ++ ++ if (endpoint_init(&this->endpoint, this, core, properties) < 0) ++ goto no_mem; ++ ++ pw_resource_add_listener(this->resource, &this->resource_listener, ++ &resource_events, this); ++ pw_resource_add_object_listener(this->resource, &this->object_listener, ++ &methods, this); ++ ++ return this; ++ ++ no_mem: ++ if (properties) ++ pw_properties_free(properties); ++ if (this && this->resource) ++ pw_resource_destroy(this->resource); ++ free(this); ++ pw_log_error("can't create client endpoint: no memory"); ++ pw_resource_error(owner_resource, -ENOMEM, ++ "can't create client endpoint: no memory"); ++ return NULL; ++} ++ ++static const struct pw_factory_implementation impl_factory = { ++ PW_VERSION_FACTORY_IMPLEMENTATION, ++ .create_object = create_object, ++}; ++ ++static void module_destroy(void *data) ++{ ++ struct factory_data *d = data; ++ ++ spa_hook_remove(&d->module_listener); ++ pw_factory_destroy(d->factory); ++} ++ ++static void module_registered(void *data) ++{ ++ struct factory_data *d = data; ++ struct pw_module *module = d->module; ++ struct pw_factory *factory = d->factory; ++ struct spa_dict_item items[1]; ++ char id[16]; ++ int res; ++ ++ snprintf(id, sizeof(id), "%d", module->global->id); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); ++ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); ++ ++ if ((res = pw_factory_register(factory, NULL)) < 0) { ++ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); ++ } ++} ++ ++static const struct pw_module_events module_events = { ++ PW_VERSION_MODULE_EVENTS, ++ .destroy = module_destroy, ++ .registered = module_registered, ++}; ++ ++int client_endpoint_factory_init(struct pw_module *module) ++{ ++ struct pw_core *core = pw_module_get_core(module); ++ struct pw_factory *factory; ++ struct factory_data *data; ++ ++ factory = pw_factory_new(core, ++ "client-endpoint", ++ PW_TYPE_INTERFACE_ClientEndpoint, ++ PW_VERSION_CLIENT_ENDPOINT_PROXY, ++ NULL, ++ sizeof(*data)); ++ if (factory == NULL) ++ return -ENOMEM; ++ ++ data = pw_factory_get_user_data(factory); ++ data->factory = factory; ++ data->module = module; ++ ++ pw_factory_set_implementation(factory, &impl_factory, data); ++ ++ pw_module_add_listener(module, &data->module_listener, &module_events, data); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-endpoint.h b/src/modules/module-session-manager/client-endpoint.h +new file mode 100644 +index 00000000..394e9fa8 +--- /dev/null ++++ b/src/modules/module-session-manager/client-endpoint.h +@@ -0,0 +1,60 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H ++#define MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H ++ ++#include "endpoint.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint { ++ struct pw_resource *resource; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ struct endpoint endpoint; ++ struct spa_list streams; ++}; ++ ++#define pw_client_endpoint_resource(r,m,v,...) \ ++ pw_resource_call_res(r,struct pw_client_endpoint_proxy_events,m,v,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_id(r,...) \ ++ pw_client_endpoint_resource(r,set_id,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_session_id(r,...) \ ++ pw_client_endpoint_resource(r,set_session_id,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_set_param(r,...) \ ++ pw_client_endpoint_resource(r,set_param,0,__VA_ARGS__) ++#define pw_client_endpoint_resource_stream_set_param(r,...) \ ++ pw_client_endpoint_resource(r,stream_set_param,0,__VA_ARGS__) ++ ++int client_endpoint_factory_init(struct pw_module *module); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H */ +diff --git a/src/modules/module-session-manager/client-session.c b/src/modules/module-session-manager/client-session.c +new file mode 100644 +index 00000000..9b20d833 +--- /dev/null ++++ b/src/modules/module-session-manager/client-session.c +@@ -0,0 +1,270 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include "client-session.h" ++#include "session.h" ++#include "endpoint-link.h" ++ ++#include ++ ++#define NAME "client-session" ++ ++struct factory_data { ++ struct pw_factory *factory; ++ struct pw_module *module; ++ struct spa_hook module_listener; ++}; ++ ++static struct endpoint_link *find_link(struct client_session *this, uint32_t id) ++{ ++ struct endpoint_link *l; ++ spa_list_for_each(l, &this->links, link) { ++ if (l->id == id) ++ return l; ++ } ++ return NULL; ++} ++ ++static int client_session_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info) ++{ ++ struct client_session *this = object; ++ struct session *session = &this->session; ++ ++ return session_update(session, change_mask, n_params, params, info); ++} ++ ++static int client_session_link_update(void *object, ++ uint32_t link_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct client_session *this = object; ++ struct session *session = &this->session; ++ struct endpoint_link *link = find_link(this, link_id); ++ struct pw_properties *props = NULL; ++ ++ if (!link) { ++ struct pw_core *core = pw_global_get_core(session->global); ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ PW_KEY_SESSION_ID, ++ NULL ++ }; ++ ++ link = calloc(1, sizeof(struct endpoint_link)); ++ if (!link) ++ goto no_mem; ++ ++ props = pw_properties_new(NULL, NULL); ++ if (!props) ++ goto no_mem; ++ pw_properties_copy_keys (session->props, props, keys); ++ ++ if (endpoint_link_init(link, link_id, session->info.id, ++ this, core, props) < 0) ++ goto no_mem; ++ ++ spa_list_append(&this->links, &link->link); ++ } ++ else if (change_mask & PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED) { ++ endpoint_link_clear(link); ++ spa_list_remove(&link->link); ++ free(link); ++ link = NULL; ++ } ++ ++ return link ? ++ endpoint_link_update(link, change_mask, n_params, params, info) ++ : 0; ++ ++ no_mem: ++ if (props) ++ pw_properties_free(props); ++ free(link); ++ pw_log_error(NAME" %p: cannot update link: no memory", this); ++ pw_resource_error(this->resource, -ENOMEM, ++ NAME" %p: cannot update link: no memory", this); ++ return -ENOMEM; ++} ++ ++static struct pw_client_session_proxy_methods methods = { ++ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, ++ .update = client_session_update, ++ .link_update = client_session_link_update, ++}; ++ ++static void client_session_destroy(void *data) ++{ ++ struct client_session *this = data; ++ struct endpoint_link *l; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ spa_list_consume(l, &this->links, link) { ++ endpoint_link_clear(l); ++ spa_list_remove(&l->link); ++ free(l); ++ } ++ session_clear(&this->session); ++ spa_hook_remove(&this->resource_listener); ++ ++ free(this); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = client_session_destroy, ++}; ++ ++static void *create_object(void *data, ++ struct pw_resource *owner_resource, ++ uint32_t type, ++ uint32_t version, ++ struct pw_properties *properties, ++ uint32_t new_id) ++{ ++ struct factory_data *d = data; ++ struct pw_factory *factory = d->factory; ++ struct client_session *this; ++ struct pw_client *owner = pw_resource_get_client(owner_resource); ++ struct pw_core *core = pw_client_get_core(owner); ++ ++ this = calloc(1, sizeof(struct client_session)); ++ if (this == NULL) ++ goto no_mem; ++ ++ spa_list_init(&this->links); ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ if (!properties) ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); ++ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); ++ ++ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); ++ if (this->resource == NULL) ++ goto no_mem; ++ ++ if (session_init(&this->session, this, core, properties) < 0) ++ goto no_mem; ++ ++ pw_resource_add_listener(this->resource, &this->resource_listener, ++ &resource_events, this); ++ pw_resource_add_object_listener(this->resource, &this->object_listener, ++ &methods, this); ++ ++ return this; ++ ++ no_mem: ++ if (properties) ++ pw_properties_free(properties); ++ if (this && this->resource) ++ pw_resource_destroy(this->resource); ++ free(this); ++ pw_log_error("can't create client session: no memory"); ++ pw_resource_error(owner_resource, -ENOMEM, ++ "can't create client session: no memory"); ++ return NULL; ++} ++ ++static const struct pw_factory_implementation impl_factory = { ++ PW_VERSION_FACTORY_IMPLEMENTATION, ++ .create_object = create_object, ++}; ++ ++static void module_destroy(void *data) ++{ ++ struct factory_data *d = data; ++ ++ spa_hook_remove(&d->module_listener); ++ pw_factory_destroy(d->factory); ++} ++ ++static void module_registered(void *data) ++{ ++ struct factory_data *d = data; ++ struct pw_module *module = d->module; ++ struct pw_factory *factory = d->factory; ++ struct spa_dict_item items[1]; ++ char id[16]; ++ int res; ++ ++ snprintf(id, sizeof(id), "%d", module->global->id); ++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); ++ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); ++ ++ if ((res = pw_factory_register(factory, NULL)) < 0) { ++ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); ++ } ++} ++ ++static const struct pw_module_events module_events = { ++ PW_VERSION_MODULE_EVENTS, ++ .destroy = module_destroy, ++ .registered = module_registered, ++}; ++ ++int client_session_factory_init(struct pw_module *module) ++{ ++ struct pw_core *core = pw_module_get_core(module); ++ struct pw_factory *factory; ++ struct factory_data *data; ++ ++ factory = pw_factory_new(core, ++ "client-session", ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_VERSION_CLIENT_SESSION_PROXY, ++ NULL, ++ sizeof(*data)); ++ if (factory == NULL) ++ return -ENOMEM; ++ ++ data = pw_factory_get_user_data(factory); ++ data->factory = factory; ++ data->module = module; ++ ++ pw_factory_set_implementation(factory, &impl_factory, data); ++ ++ pw_module_add_listener(module, &data->module_listener, &module_events, data); ++ ++ return 0; ++} +diff --git a/src/modules/module-session-manager/client-session.h b/src/modules/module-session-manager/client-session.h +new file mode 100644 +index 00000000..c764564d +--- /dev/null ++++ b/src/modules/module-session-manager/client-session.h +@@ -0,0 +1,62 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_CLIENT_SESSION_H ++#define MODULE_SESSION_MANAGER_CLIENT_SESSION_H ++ ++#include "session.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session { ++ struct pw_resource *resource; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ struct session session; ++ struct spa_list links; ++}; ++ ++#define pw_client_session_resource(r,m,v,...) \ ++ pw_resource_call_res(r,struct pw_client_session_proxy_events,m,v,__VA_ARGS__) ++#define pw_client_session_resource_set_id(r,...) \ ++ pw_client_session_resource(r,set_id,0,__VA_ARGS__) ++#define pw_client_session_resource_set_param(r,...) \ ++ pw_client_session_resource(r,set_param,0,__VA_ARGS__) ++#define pw_client_session_resource_link_set_param(r,...) \ ++ pw_client_session_resource(r,link_set_param,0,__VA_ARGS__) ++#define pw_client_session_resource_create_link(r,...) \ ++ pw_client_session_resource(r,create_link,0,__VA_ARGS__) ++#define pw_client_session_resource_destroy_link(r,...) \ ++ pw_client_session_resource(r,destroy_link,0,__VA_ARGS__) ++#define pw_client_session_resource_link_request_state(r,...) \ ++ pw_client_session_resource(r,link_request_state,0,__VA_ARGS__) ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_CLIENT_SESSION_H */ +diff --git a/src/modules/module-session-manager/endpoint-link.c b/src/modules/module-session-manager/endpoint-link.c +new file mode 100644 +index 00000000..bce06598 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-link.c +@@ -0,0 +1,359 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "endpoint-link.h" ++#include "client-session.h" ++ ++#include ++ ++#define NAME "endpoint-link" ++ ++struct resource_data { ++ struct endpoint_link *link; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_link_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_link_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_link_resource_info(r,...) \ ++ pw_endpoint_link_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_link_resource_param(r,...) \ ++ pw_endpoint_link_resource(r,param,0,__VA_ARGS__) ++ ++static int endpoint_link_enum_params (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ struct spa_pod *result; ++ struct spa_pod *param; ++ uint8_t buffer[1024]; ++ struct spa_pod_builder b = { 0 }; ++ uint32_t index; ++ uint32_t next = start; ++ uint32_t count = 0; ++ ++ while (true) { ++ index = next++; ++ if (index >= this->n_params) ++ break; ++ ++ param = this->params[index]; ++ ++ if (param == NULL || !spa_pod_is_object_id(param, id)) ++ continue; ++ ++ spa_pod_builder_init(&b, buffer, sizeof(buffer)); ++ if (spa_pod_filter(&b, &result, param, filter) != 0) ++ continue; ++ ++ pw_log_debug(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_endpoint_link_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int endpoint_link_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ uint32_t i; ++ ++ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); ++ data->n_subscribe_ids = n_ids; ++ ++ for (i = 0; i < n_ids; i++) { ++ data->subscribe_ids[i] = ids[i]; ++ pw_log_debug(NAME" %p: resource %d subscribe param %u", ++ data->link, resource->id, ids[i]); ++ endpoint_link_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int endpoint_link_set_param (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ ++ pw_client_session_resource_set_param(this->client_sess->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static int endpoint_link_request_state(void *object, enum pw_endpoint_link_state state) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ ++ pw_client_session_resource_link_request_state(this->client_sess->resource, ++ this->id, state); ++ ++ return 0; ++} ++ ++static int endpoint_link_destroy(void *object) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_link *this = data->link; ++ ++ pw_client_session_resource_destroy_link(this->client_sess->resource, ++ this->id); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_link_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, ++ .subscribe_params = endpoint_link_subscribe_params, ++ .enum_params = endpoint_link_enum_params, ++ .set_param = endpoint_link_set_param, ++ .request_state = endpoint_link_request_state, ++ .destroy = endpoint_link_destroy, ++}; ++ ++static void endpoint_link_notify_subscribed(struct endpoint_link *this, ++ uint32_t index, uint32_t next) ++{ ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ struct spa_pod *param = this->params[index]; ++ uint32_t id; ++ uint32_t i; ++ ++ if (!param || !spa_pod_is_object (param)) ++ return; ++ ++ id = SPA_POD_OBJECT_ID (param); ++ ++ spa_list_for_each(resource, &global->resource_list, link) { ++ data = pw_resource_get_user_data(resource); ++ for (i = 0; i < data->n_subscribe_ids; i++) { ++ if (data->subscribe_ids[i] == id) { ++ pw_endpoint_link_resource_param(resource, 1, ++ id, index, next, param); ++ } ++ } ++ } ++} ++ ++int endpoint_link_update(struct endpoint_link *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info) ++{ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ endpoint_link_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_STATE) { ++ this->info.state = info->state; ++ free(this->info.error); ++ this->info.error = info->error ? strdup(info->error) : NULL; ++ } ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.output_endpoint_id) { ++ this->info.output_endpoint_id = info->output_endpoint_id; ++ this->info.output_stream_id = info->output_stream_id; ++ this->info.input_endpoint_id = info->input_endpoint_id; ++ this->info.input_stream_id = info->input_stream_id; ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_link_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" %p: can't update: no memory", this); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" %p: can't update: no memory", this); ++ return -ENOMEM; ++} ++ ++static void endpoint_link_unbind(void *data) ++{ ++ struct pw_resource *resource = data; ++ spa_list_remove(&resource->link); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = endpoint_link_unbind, ++}; ++ ++static int endpoint_link_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct endpoint_link *this = _data; ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ ++ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); ++ if (resource == NULL) ++ goto no_mem; ++ ++ data = pw_resource_get_user_data(resource); ++ data->link = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_LINK_CHANGE_MASK_ALL; ++ pw_endpoint_link_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" %p: can't create resource: no memory", this); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" %p: can't create resource: no memory", this); ++ return -ENOMEM; ++} ++ ++int endpoint_link_init(struct endpoint_link *this, ++ uint32_t id, uint32_t session_id, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_sess = client_sess; ++ this->id = id; ++ this->props = properties; ++ ++ properties = pw_properties_copy(properties); ++ if (!properties) ++ goto no_mem; ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_EndpointLink, ++ PW_VERSION_ENDPOINT_LINK_PROXY, ++ properties, endpoint_link_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ this->info.version = PW_VERSION_ENDPOINT_LINK_INFO; ++ this->info.id = this->global->id; ++ this->info.session_id = session_id; ++ this->info.props = &this->props->dict; ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_link_clear(struct endpoint_link *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ pw_global_destroy(this->global); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ free(this->params); ++ ++ free(this->info.error); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint-link.h b/src/modules/module-session-manager/endpoint-link.h +new file mode 100644 +index 00000000..a9c18d32 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-link.h +@@ -0,0 +1,64 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_ENDPOINT_LINK_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_LINK_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session; ++ ++struct endpoint_link { ++ struct spa_list link; ++ struct client_session *client_sess; ++ struct pw_global *global; ++ uint32_t id; /* session-local link id */ ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_link_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_link_init(struct endpoint_link *this, ++ uint32_t id, uint32_t session_id, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_link_clear(struct endpoint_link *this); ++ ++int endpoint_link_update(struct endpoint_link *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_LINK_H */ +diff --git a/src/modules/module-session-manager/endpoint-stream.c b/src/modules/module-session-manager/endpoint-stream.c +new file mode 100644 +index 00000000..47d2a4ea +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-stream.c +@@ -0,0 +1,329 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "endpoint-stream.h" ++#include "client-endpoint.h" ++ ++#include ++ ++#define NAME "endpoint-stream" ++ ++struct resource_data { ++ struct endpoint_stream *stream; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_stream_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_stream_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_stream_resource_info(r,...) \ ++ pw_endpoint_stream_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_stream_resource_param(r,...) \ ++ pw_endpoint_stream_resource(r,param,0,__VA_ARGS__) ++ ++static int endpoint_stream_enum_params (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_stream *this = data->stream; ++ struct spa_pod *result; ++ struct spa_pod *param; ++ uint8_t buffer[1024]; ++ struct spa_pod_builder b = { 0 }; ++ uint32_t index; ++ uint32_t next = start; ++ uint32_t count = 0; ++ ++ while (true) { ++ index = next++; ++ if (index >= this->n_params) ++ break; ++ ++ param = this->params[index]; ++ ++ if (param == NULL || !spa_pod_is_object_id(param, id)) ++ continue; ++ ++ spa_pod_builder_init(&b, buffer, sizeof(buffer)); ++ if (spa_pod_filter(&b, &result, param, filter) != 0) ++ continue; ++ ++ pw_log_debug(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_endpoint_stream_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int endpoint_stream_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ uint32_t i; ++ ++ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); ++ data->n_subscribe_ids = n_ids; ++ ++ for (i = 0; i < n_ids; i++) { ++ data->subscribe_ids[i] = ids[i]; ++ pw_log_debug(NAME" %p: resource %d subscribe param %u", ++ data->stream, resource->id, ids[i]); ++ endpoint_stream_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int endpoint_stream_set_param (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint_stream *this = data->stream; ++ ++ pw_client_endpoint_resource_set_param(this->client_ep->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_stream_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, ++ .subscribe_params = endpoint_stream_subscribe_params, ++ .enum_params = endpoint_stream_enum_params, ++ .set_param = endpoint_stream_set_param, ++}; ++ ++static void endpoint_stream_notify_subscribed(struct endpoint_stream *this, ++ uint32_t index, uint32_t next) ++{ ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ struct spa_pod *param = this->params[index]; ++ uint32_t id; ++ uint32_t i; ++ ++ if (!param || !spa_pod_is_object (param)) ++ return; ++ ++ id = SPA_POD_OBJECT_ID (param); ++ ++ spa_list_for_each(resource, &global->resource_list, link) { ++ data = pw_resource_get_user_data(resource); ++ for (i = 0; i < data->n_subscribe_ids; i++) { ++ if (data->subscribe_ids[i] == id) { ++ pw_endpoint_stream_resource_param(resource, 1, ++ id, index, next, param); ++ } ++ } ++ } ++} ++ ++int endpoint_stream_update(struct endpoint_stream *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info) ++{ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ endpoint_stream_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS) { ++ free(this->info.link_params); ++ this->info.link_params = spa_pod_copy(info->link_params); ++ } ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.name) ++ this->info.name = strdup(info->name); ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_stream_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++static void endpoint_stream_unbind(void *data) ++{ ++ struct pw_resource *resource = data; ++ spa_list_remove(&resource->link); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = endpoint_stream_unbind, ++}; ++ ++static int endpoint_stream_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct endpoint_stream *this = _data; ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ ++ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); ++ if (resource == NULL) ++ goto no_mem; ++ ++ data = pw_resource_get_user_data(resource); ++ data->stream = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; ++ pw_endpoint_stream_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int endpoint_stream_init(struct endpoint_stream *this, ++ uint32_t id, uint32_t endpoint_id, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_ep = client_ep; ++ this->id = id; ++ this->props = properties; ++ ++ properties = pw_properties_copy(properties); ++ if (!properties) ++ goto no_mem; ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_VERSION_ENDPOINT_STREAM_PROXY, ++ properties, endpoint_stream_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ this->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; ++ this->info.id = this->global->id; ++ this->info.endpoint_id = endpoint_id; ++ this->info.props = &this->props->dict; ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_stream_clear(struct endpoint_stream *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ pw_global_destroy(this->global); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ free(this->params); ++ ++ free(this->info.name); ++ free(this->info.link_params); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint-stream.h b/src/modules/module-session-manager/endpoint-stream.h +new file mode 100644 +index 00000000..99bd4836 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint-stream.h +@@ -0,0 +1,64 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint; ++ ++struct endpoint_stream { ++ struct spa_list link; ++ struct client_endpoint *client_ep; ++ struct pw_global *global; ++ uint32_t id; /* endpoint-local stream id */ ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_stream_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_stream_init(struct endpoint_stream *this, ++ uint32_t id, uint32_t endpoint_id, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_stream_clear(struct endpoint_stream *this); ++ ++int endpoint_stream_update(struct endpoint_stream *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H */ +diff --git a/src/modules/module-session-manager/endpoint.c b/src/modules/module-session-manager/endpoint.c +new file mode 100644 +index 00000000..0866e71d +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint.c +@@ -0,0 +1,343 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "endpoint.h" ++#include "client-endpoint.h" ++ ++#include ++ ++#define NAME "endpoint" ++ ++struct resource_data { ++ struct endpoint *endpoint; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_endpoint_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_endpoint_proxy_events,m,v,__VA_ARGS__) ++#define pw_endpoint_resource_info(r,...) \ ++ pw_endpoint_resource(r,info,0,__VA_ARGS__) ++#define pw_endpoint_resource_param(r,...) \ ++ pw_endpoint_resource(r,param,0,__VA_ARGS__) ++ ++static int endpoint_enum_params (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint *this = data->endpoint; ++ struct spa_pod *result; ++ struct spa_pod *param; ++ uint8_t buffer[1024]; ++ struct spa_pod_builder b = { 0 }; ++ uint32_t index; ++ uint32_t next = start; ++ uint32_t count = 0; ++ ++ while (true) { ++ index = next++; ++ if (index >= this->n_params) ++ break; ++ ++ param = this->params[index]; ++ ++ if (param == NULL || !spa_pod_is_object_id(param, id)) ++ continue; ++ ++ spa_pod_builder_init(&b, buffer, sizeof(buffer)); ++ if (spa_pod_filter(&b, &result, param, filter) != 0) ++ continue; ++ ++ pw_log_debug(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_endpoint_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int endpoint_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ uint32_t i; ++ ++ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); ++ data->n_subscribe_ids = n_ids; ++ ++ for (i = 0; i < n_ids; i++) { ++ data->subscribe_ids[i] = ids[i]; ++ pw_log_debug(NAME" %p: resource %d subscribe param %u", ++ data->endpoint, resource->id, ids[i]); ++ endpoint_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int endpoint_set_param (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct endpoint *this = data->endpoint; ++ ++ pw_client_endpoint_resource_set_param(this->client_ep->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static const struct pw_endpoint_proxy_methods methods = { ++ PW_VERSION_ENDPOINT_PROXY_METHODS, ++ .subscribe_params = endpoint_subscribe_params, ++ .enum_params = endpoint_enum_params, ++ .set_param = endpoint_set_param, ++}; ++ ++static void endpoint_notify_subscribed(struct endpoint *this, ++ uint32_t index, uint32_t next) ++{ ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ struct spa_pod *param = this->params[index]; ++ uint32_t id; ++ uint32_t i; ++ ++ if (!param || !spa_pod_is_object (param)) ++ return; ++ ++ id = SPA_POD_OBJECT_ID (param); ++ ++ spa_list_for_each(resource, &global->resource_list, link) { ++ data = pw_resource_get_user_data(resource); ++ for (i = 0; i < data->n_subscribe_ids; i++) { ++ if (data->subscribe_ids[i] == id) { ++ pw_endpoint_resource_param(resource, 1, id, ++ index, next, param); ++ } ++ } ++ } ++} ++ ++int endpoint_update(struct endpoint *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info) ++{ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ endpoint_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) ++ this->info.n_streams = info->n_streams; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) ++ this->info.session_id = info->session_id; ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ if (!this->info.name) { ++ this->info.name = strdup(info->name); ++ this->info.media_class = strdup(info->media_class); ++ this->info.direction = info->direction; ++ this->info.flags = info->flags; ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_endpoint_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++static void endpoint_unbind(void *data) ++{ ++ struct pw_resource *resource = data; ++ spa_list_remove(&resource->link); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = endpoint_unbind, ++}; ++ ++static int endpoint_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct endpoint *this = _data; ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ ++ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); ++ if (resource == NULL) ++ goto no_mem; ++ ++ data = pw_resource_get_user_data(resource); ++ data->endpoint = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_ENDPOINT_CHANGE_MASK_ALL; ++ pw_endpoint_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_ep->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int endpoint_init(struct endpoint *this, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ NULL ++ }; ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_ep = client_ep; ++ this->props = properties; ++ ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_copy_keys(this->props, properties, keys); ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_VERSION_ENDPOINT_PROXY, ++ properties, endpoint_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ pw_properties_setf(this->props, PW_KEY_ENDPOINT_ID, "%u", this->global->id); ++ ++ this->info.version = PW_VERSION_ENDPOINT_INFO; ++ this->info.id = this->global->id; ++ this->info.props = &this->props->dict; ++ ++ pw_client_endpoint_resource_set_id(client_ep->resource, this->global->id); ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void endpoint_clear(struct endpoint *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ pw_global_destroy(this->global); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ free(this->params); ++ ++ free(this->info.name); ++ free(this->info.media_class); ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/endpoint.h b/src/modules/module-session-manager/endpoint.h +new file mode 100644 +index 00000000..89d26028 +--- /dev/null ++++ b/src/modules/module-session-manager/endpoint.h +@@ -0,0 +1,61 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_ENDPOINT_H ++#define MODULE_SESSION_MANAGER_ENDPOINT_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_endpoint; ++ ++struct endpoint { ++ struct client_endpoint *client_ep; ++ struct pw_global *global; ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_endpoint_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int endpoint_init(struct endpoint *this, ++ struct client_endpoint *client_ep, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void endpoint_clear(struct endpoint *this); ++ ++int endpoint_update(struct endpoint *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_ENDPOINT_H */ +diff --git a/src/modules/module-session-manager/protocol-native.c b/src/modules/module-session-manager/protocol-native.c +new file mode 100644 +index 00000000..2c791ffc +--- /dev/null ++++ b/src/modules/module-session-manager/protocol-native.c +@@ -0,0 +1,2125 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++static void push_dict(struct spa_pod_builder *b, const struct spa_dict *dict) ++{ ++ struct spa_pod_frame f; ++ uint32_t n_items; ++ uint32_t i; ++ ++ n_items = dict ? dict->n_items : 0; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, SPA_POD_Int(n_items), NULL); ++ for (i = 0; i < n_items; i++) { ++ spa_pod_builder_add(b, ++ SPA_POD_String(dict->items[i].key), ++ SPA_POD_String(dict->items[i].value), ++ NULL); ++ } ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define parse_dict(p, f, dict) \ ++do { \ ++ uint32_t i; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, SPA_POD_Int(&(dict)->n_items), NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ if ((dict)->n_items > 0) { \ ++ (dict)->items = alloca((dict)->n_items * sizeof(struct spa_dict_item)); \ ++ for (i = 0; i < (dict)->n_items; i++) { \ ++ if (spa_pod_parser_get(p, \ ++ SPA_POD_String(&(dict)->items[i].key), \ ++ SPA_POD_String(&(dict)->items[i].value), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ } \ ++ } \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void push_param_infos(struct spa_pod_builder *b, uint32_t n_params, ++ const struct spa_param_info *params) ++{ ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, SPA_POD_Int(n_params), NULL); ++ for (i = 0; i < n_params; i++) { ++ spa_pod_builder_add(b, ++ SPA_POD_Id(params[i].id), ++ SPA_POD_Int(params[i].flags), ++ NULL); ++ } ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define parse_param_infos(p, f, n_params_p, params_p) \ ++do { \ ++ uint32_t i; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, SPA_POD_Int(n_params_p), NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ if (*(n_params_p) > 0) { \ ++ *(params_p) = alloca(*(n_params_p) * sizeof(struct spa_param_info)); \ ++ for (i = 0; i < *(n_params_p); i++) { \ ++ if (spa_pod_parser_get(p, \ ++ SPA_POD_Id(&(*(params_p))[i].id), \ ++ SPA_POD_Int(&(*(params_p))[i].flags), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ } \ ++ } \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++/*********************************************** ++ * INFO STRUCTURES ++ ***********************************************/ ++ ++static void ++marshal_pw_session_info(struct spa_pod_builder *b, ++ const struct pw_session_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->change_mask), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_session_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ SPA_POD_Int(&(info)->n_params), \ ++ SPA_POD_Int(&(info)->props->n_items), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_SESSION_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_String(info->name), ++ SPA_POD_String(info->media_class), ++ SPA_POD_Int(info->direction), ++ SPA_POD_Int(info->flags), ++ SPA_POD_Int(info->change_mask), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_String(&(info)->name), \ ++ SPA_POD_String(&(info)->media_class), \ ++ SPA_POD_Int(&(info)->direction), \ ++ SPA_POD_Int(&(info)->flags), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_stream_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->endpoint_id), ++ SPA_POD_String(info->name), ++ SPA_POD_Int(info->change_mask), ++ SPA_POD_Pod(info->link_params), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_stream_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_Int(&(info)->endpoint_id), \ ++ SPA_POD_String(&(info)->name), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ SPA_POD_Pod(&(info)->link_params), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++static void ++marshal_pw_endpoint_link_info(struct spa_pod_builder *b, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct spa_pod_frame f; ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(info->version), ++ SPA_POD_Int(info->id), ++ SPA_POD_Int(info->session_id), ++ SPA_POD_Int(info->output_endpoint_id), ++ SPA_POD_Int(info->output_stream_id), ++ SPA_POD_Int(info->input_endpoint_id), ++ SPA_POD_Int(info->input_stream_id), ++ SPA_POD_Int(info->change_mask), ++ SPA_POD_Int(info->state), ++ SPA_POD_String(info->error), ++ NULL); ++ push_dict(b, info->props); ++ push_param_infos(b, info->n_params, info->params); ++ spa_pod_builder_pop(b, &f); ++} ++ ++/* macro because of alloca() */ ++#define demarshal_pw_endpoint_link_info(p, f, info) \ ++do { \ ++ struct spa_pod_frame sub_f; \ ++ uint32_t version; \ ++ \ ++ if (spa_pod_parser_push_struct(p, f) < 0 || \ ++ spa_pod_parser_get(p, \ ++ SPA_POD_Int(&version), \ ++ SPA_POD_Int(&(info)->id), \ ++ SPA_POD_Int(&(info)->session_id), \ ++ SPA_POD_Int(&(info)->output_endpoint_id), \ ++ SPA_POD_Int(&(info)->output_stream_id), \ ++ SPA_POD_Int(&(info)->input_endpoint_id), \ ++ SPA_POD_Int(&(info)->input_stream_id), \ ++ SPA_POD_Int(&(info)->change_mask), \ ++ SPA_POD_Int(&(info)->state), \ ++ SPA_POD_String(&(info)->error), \ ++ NULL) < 0) \ ++ return -EINVAL; \ ++ \ ++ (info)->change_mask &= PW_ENDPOINT_LINK_CHANGE_MASK_ALL; \ ++ \ ++ parse_dict(p, &sub_f, (info)->props); \ ++ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ ++ \ ++ spa_pod_parser_pop(p, f); \ ++} while(0) ++ ++/*********************************************** ++ * CLIENT ENDPOINT ++ ***********************************************/ ++ ++static int client_endpoint_marshal_set_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_set_session_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_set_param (void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_stream_set_param (void *object, ++ uint32_t stream_id, uint32_t id, ++ uint32_t flags, const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(stream_id), ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_endpoint_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_client_endpoint_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int client_endpoint_marshal_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_info *info) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(change_mask), ++ SPA_POD_Int(n_params), ++ NULL); ++ ++ for (i = 0; i < n_params; i++) ++ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); ++ ++ if (info) ++ marshal_pw_endpoint_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_endpoint_marshal_stream_update(void *object, ++ uint32_t stream_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(stream_id), ++ SPA_POD_Int(change_mask), ++ SPA_POD_Int(n_params), ++ NULL); ++ ++ for (i = 0; i < n_params; i++) ++ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); ++ ++ if (info) ++ marshal_pw_endpoint_stream_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_endpoint_demarshal_set_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_id, 0, id); ++} ++ ++static int client_endpoint_demarshal_set_session_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_session_id, 0, id); ++} ++ ++static int client_endpoint_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ const struct spa_pod *param = NULL; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ set_param, 0, id, flags, param); ++} ++ ++static int client_endpoint_demarshal_stream_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t stream_id, id, flags; ++ const struct spa_pod *param = NULL; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&stream_id), ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, ++ stream_set_param, 0, stream_id, id, flags, param); ++} ++ ++static int client_endpoint_demarshal_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&change_mask), ++ SPA_POD_Int(&n_params), NULL) < 0) ++ return -EINVAL; ++ ++ if (n_params > 0) ++ params = alloca(n_params * sizeof(struct spa_pod *)); ++ for (i = 0; i < n_params; i++) ++ if (spa_pod_parser_get(&prs[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, ++ update, 0, change_mask, n_params, params, infop); ++} ++ ++static int client_endpoint_demarshal_stream_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t stream_id, change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_stream_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&stream_id), ++ SPA_POD_Int(&change_mask), ++ SPA_POD_Int(&n_params), NULL) < 0) ++ return -EINVAL; ++ ++ if (n_params > 0) ++ params = alloca(n_params * sizeof(struct spa_pod *)); ++ for (i = 0; i < n_params; i++) ++ if (spa_pod_parser_get(&prs[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_stream_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, ++ stream_update, 0, stream_id, change_mask, n_params, params, infop); ++} ++ ++static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_endpoint_event_marshal = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS, ++ .set_id = client_endpoint_marshal_set_id, ++ .set_session_id = client_endpoint_marshal_set_session_id, ++ .set_param = client_endpoint_marshal_set_param, ++ .stream_set_param = client_endpoint_marshal_stream_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_endpoint_event_demarshal[PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM] = ++{ ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID] = { client_endpoint_demarshal_set_id, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID] = { client_endpoint_demarshal_set_session_id, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM] = { client_endpoint_demarshal_set_param, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM] = { client_endpoint_demarshal_stream_set_param, 0 }, ++}; ++ ++static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = { ++ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, ++ .add_listener = client_endpoint_marshal_add_listener, ++ .update = client_endpoint_marshal_update, ++ .stream_update = client_endpoint_marshal_stream_update, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_endpoint_method_demarshal[PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM] = ++{ ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE] = { client_endpoint_demarshal_update, 0 }, ++ [PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE] = { client_endpoint_demarshal_stream_update, 0 }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_client_endpoint_marshal = { ++ PW_TYPE_INTERFACE_ClientEndpoint, ++ PW_VERSION_CLIENT_ENDPOINT_PROXY, ++ PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM, ++ PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM, ++ &pw_protocol_native_client_endpoint_method_marshal, ++ &pw_protocol_native_client_endpoint_method_demarshal, ++ &pw_protocol_native_client_endpoint_event_marshal, ++ &pw_protocol_native_client_endpoint_event_demarshal, ++}; ++ ++/*********************************************** ++ * CLIENT SESSION ++ ***********************************************/ ++ ++static int client_session_marshal_set_id (void *object, uint32_t id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_SET_ID, NULL); ++ ++ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_set_param (void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_link_set_param (void *object, ++ uint32_t link_id, uint32_t id, ++ uint32_t flags, const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(link_id), ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_create_link(void *object, ++ const struct spa_dict *props) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ spa_return_val_if_fail(props, -EINVAL); ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK, NULL); ++ ++ push_dict(b, props); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_destroy_link (void *object, uint32_t link_id) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK, NULL); ++ ++ spa_pod_builder_add(b, SPA_POD_Int(link_id), NULL); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_link_request_state (void *object, ++ uint32_t link_id, uint32_t state) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(link_id), ++ SPA_POD_Int(state)); ++ ++ return pw_protocol_native_end_resource(resource, b); ++} ++ ++static int client_session_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_client_session_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int client_session_marshal_update(void *object, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_CLIENT_SESSION_PROXY_METHOD_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(change_mask), ++ SPA_POD_Int(n_params), ++ NULL); ++ ++ for (i = 0; i < n_params; i++) ++ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); ++ ++ if (info) ++ marshal_pw_session_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_session_marshal_link_update(void *object, ++ uint32_t link_id, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ struct spa_pod_frame f; ++ uint32_t i; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE, NULL); ++ ++ spa_pod_builder_push_struct(b, &f); ++ spa_pod_builder_add(b, ++ SPA_POD_Int(link_id), ++ SPA_POD_Int(change_mask), ++ SPA_POD_Int(n_params), ++ NULL); ++ ++ for (i = 0; i < n_params; i++) ++ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); ++ ++ if (info) ++ marshal_pw_endpoint_link_info(b, info); ++ else ++ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); ++ ++ spa_pod_builder_pop(b, &f); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int client_session_demarshal_set_id(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ set_id, 0, id); ++} ++ ++static int client_session_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ const struct spa_pod *param = NULL; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ set_param, 0, id, flags, param); ++} ++ ++static int client_session_demarshal_link_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id, id, flags; ++ const struct spa_pod *param = NULL; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&link_id), ++ SPA_POD_Int(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_PodObject(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ link_set_param, 0, link_id, id, flags, param); ++} ++ ++static int client_session_demarshal_create_link(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ parse_dict(&prs, &f, &props); ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ create_link, 0, &props); ++} ++ ++static int client_session_demarshal_destroy_link(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&link_id)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ destroy_link, 0, link_id); ++} ++ ++static int client_session_demarshal_link_request_state(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t link_id, state; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&link_id), ++ SPA_POD_Int(&state)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, ++ link_request_state, 0, link_id, state); ++} ++ ++static int client_session_demarshal_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_session_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&change_mask), ++ SPA_POD_Int(&n_params), NULL) < 0) ++ return -EINVAL; ++ ++ if (n_params > 0) ++ params = alloca(n_params * sizeof(struct spa_pod *)); ++ for (i = 0; i < n_params; i++) ++ if (spa_pod_parser_get(&prs[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_session_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, ++ update, 0, change_mask, n_params, params, infop); ++} ++ ++static int client_session_demarshal_link_update(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs[2]; ++ struct spa_pod_frame f[2]; ++ uint32_t link_id, change_mask, n_params; ++ const struct spa_pod **params = NULL; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_link_info info = { .props = &props }, *infop = NULL; ++ struct spa_pod *ipod; ++ uint32_t i; ++ ++ spa_pod_parser_init(&prs[0], msg->data, msg->size); ++ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || ++ spa_pod_parser_get(&prs[0], ++ SPA_POD_Int(&link_id), ++ SPA_POD_Int(&change_mask), ++ SPA_POD_Int(&n_params), NULL) < 0) ++ return -EINVAL; ++ ++ if (n_params > 0) ++ params = alloca(n_params * sizeof(struct spa_pod *)); ++ for (i = 0; i < n_params; i++) ++ if (spa_pod_parser_get(&prs[0], ++ SPA_POD_PodObject(¶ms[i]), NULL) < 0) ++ return -EINVAL; ++ ++ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) ++ return -EINVAL; ++ if (ipod) { ++ infop = &info; ++ spa_pod_parser_pod(&prs[1], ipod); ++ demarshal_pw_endpoint_link_info(&prs[1], &f[1], infop); ++ } ++ ++ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, ++ link_update, 0, link_id, change_mask, n_params, params, infop); ++} ++ ++static const struct pw_client_session_proxy_events pw_protocol_native_client_session_event_marshal = { ++ PW_VERSION_CLIENT_SESSION_PROXY_EVENTS, ++ .set_id = client_session_marshal_set_id, ++ .set_param = client_session_marshal_set_param, ++ .link_set_param = client_session_marshal_link_set_param, ++ .create_link = client_session_marshal_create_link, ++ .destroy_link = client_session_marshal_destroy_link, ++ .link_request_state = client_session_marshal_link_request_state, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_session_event_demarshal[PW_CLIENT_SESSION_PROXY_EVENT_NUM] = ++{ ++ [PW_CLIENT_SESSION_PROXY_EVENT_SET_ID] = { client_session_demarshal_set_id, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM] = { client_session_demarshal_set_param, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM] = { client_session_demarshal_link_set_param, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK] = { client_session_demarshal_create_link, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK] = { client_session_demarshal_destroy_link, 0 }, ++ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE] = { client_session_demarshal_link_request_state, 0 }, ++}; ++ ++static const struct pw_client_session_proxy_methods pw_protocol_native_client_session_method_marshal = { ++ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, ++ .add_listener = client_session_marshal_add_listener, ++ .update = client_session_marshal_update, ++ .link_update = client_session_marshal_link_update, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_client_session_method_demarshal[PW_CLIENT_SESSION_PROXY_METHOD_NUM] = ++{ ++ [PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_CLIENT_SESSION_PROXY_METHOD_UPDATE] = { client_session_demarshal_update, 0 }, ++ [PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE] = { client_session_demarshal_link_update, 0 }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_client_session_marshal = { ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_VERSION_CLIENT_SESSION_PROXY, ++ PW_CLIENT_SESSION_PROXY_METHOD_NUM, ++ PW_CLIENT_SESSION_PROXY_EVENT_NUM, ++ &pw_protocol_native_client_session_method_marshal, ++ &pw_protocol_native_client_session_method_demarshal, ++ &pw_protocol_native_client_session_event_marshal, ++ &pw_protocol_native_client_session_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT LINK ++ ***********************************************/ ++ ++static void endpoint_link_marshal_info (void *object, ++ const struct pw_endpoint_link_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_LINK_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_endpoint_link_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void endpoint_link_marshal_param (void *object, int seq, uint32_t id, ++ uint32_t index, uint32_t next, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_LINK_PROXY_EVENT_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(seq), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(next), ++ SPA_POD_Pod(param)); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static int endpoint_link_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_link_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int endpoint_link_marshal_subscribe_params(void *object, ++ uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(num), ++ SPA_POD_Pod(filter)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_marshal_set_param(void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_marshal_request_state(void *object, ++ enum pw_endpoint_link_state state) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE, NULL); ++ ++ spa_pod_builder_add_struct(b, SPA_POD_Int(state)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_marshal_destroy(void *object) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY, NULL); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_link_demarshal_info(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_link_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_link_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, ++ info, 0, &info); ++} ++ ++static int endpoint_link_demarshal_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, next; ++ int seq; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&next), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int endpoint_link_demarshal_subscribe_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t csize, ctype, n_ids; ++ uint32_t *ids; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) ++ return -EINVAL; ++ ++ if (ctype != SPA_TYPE_Id) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int endpoint_link_demarshal_enum_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, num; ++ int seq; ++ struct spa_pod *filter; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&num), ++ SPA_POD_Pod(&filter)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int endpoint_link_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static int endpoint_link_demarshal_request_state(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ enum pw_endpoint_link_state state; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&state)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ request_state, 0, state); ++} ++ ++static int endpoint_link_demarshal_destroy(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, ++ destroy, 0); ++} ++ ++static const struct pw_endpoint_link_proxy_events pw_protocol_native_endpoint_link_event_marshal = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS, ++ .info = endpoint_link_marshal_info, ++ .param = endpoint_link_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_link_event_demarshal[PW_ENDPOINT_LINK_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_LINK_PROXY_EVENT_INFO] = { endpoint_link_demarshal_info, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_EVENT_PARAM] = { endpoint_link_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_link_proxy_methods pw_protocol_native_endpoint_link_method_marshal = { ++ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, ++ .add_listener = endpoint_link_marshal_add_listener, ++ .subscribe_params = endpoint_link_marshal_subscribe_params, ++ .enum_params = endpoint_link_marshal_enum_params, ++ .set_param = endpoint_link_marshal_set_param, ++ .request_state = endpoint_link_marshal_request_state, ++ .destroy = endpoint_link_marshal_destroy, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_link_method_demarshal[PW_ENDPOINT_LINK_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_link_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS] = { endpoint_link_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM] = { endpoint_link_demarshal_set_param, PW_PERM_W }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE] = { endpoint_link_demarshal_request_state, PW_PERM_W }, ++ [PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY] = { endpoint_link_demarshal_destroy, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_link_marshal = { ++ PW_TYPE_INTERFACE_EndpointLink, ++ PW_VERSION_ENDPOINT_LINK_PROXY, ++ PW_ENDPOINT_LINK_PROXY_METHOD_NUM, ++ PW_ENDPOINT_LINK_PROXY_EVENT_NUM, ++ &pw_protocol_native_endpoint_link_method_marshal, ++ &pw_protocol_native_endpoint_link_method_demarshal, ++ &pw_protocol_native_endpoint_link_event_marshal, ++ &pw_protocol_native_endpoint_link_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT STREAM ++ ***********************************************/ ++ ++static void endpoint_stream_marshal_info (void *object, ++ const struct pw_endpoint_stream_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_STREAM_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_endpoint_stream_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void endpoint_stream_marshal_param (void *object, int seq, uint32_t id, ++ uint32_t index, uint32_t next, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(seq), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(next), ++ SPA_POD_Pod(param)); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static int endpoint_stream_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_stream_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int endpoint_stream_marshal_subscribe_params(void *object, ++ uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_stream_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(num), ++ SPA_POD_Pod(filter)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_stream_marshal_set_param(void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_stream_demarshal_info(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_stream_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_stream_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, ++ info, 0, &info); ++} ++ ++static int endpoint_stream_demarshal_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, next; ++ int seq; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&next), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int endpoint_stream_demarshal_subscribe_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t csize, ctype, n_ids; ++ uint32_t *ids; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) ++ return -EINVAL; ++ ++ if (ctype != SPA_TYPE_Id) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int endpoint_stream_demarshal_enum_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, num; ++ int seq; ++ struct spa_pod *filter; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&num), ++ SPA_POD_Pod(&filter)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int endpoint_stream_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static const struct pw_endpoint_stream_proxy_events pw_protocol_native_endpoint_stream_event_marshal = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS, ++ .info = endpoint_stream_marshal_info, ++ .param = endpoint_stream_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_stream_event_demarshal[PW_ENDPOINT_STREAM_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_STREAM_PROXY_EVENT_INFO] = { endpoint_stream_demarshal_info, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM] = { endpoint_stream_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_stream_proxy_methods pw_protocol_native_endpoint_stream_method_marshal = { ++ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, ++ .add_listener = endpoint_stream_marshal_add_listener, ++ .subscribe_params = endpoint_stream_marshal_subscribe_params, ++ .enum_params = endpoint_stream_marshal_enum_params, ++ .set_param = endpoint_stream_marshal_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_stream_method_demarshal[PW_ENDPOINT_STREAM_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_stream_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS] = { endpoint_stream_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM] = { endpoint_stream_demarshal_set_param, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_stream_marshal = { ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_VERSION_ENDPOINT_STREAM_PROXY, ++ PW_ENDPOINT_STREAM_PROXY_METHOD_NUM, ++ PW_ENDPOINT_STREAM_PROXY_EVENT_NUM, ++ &pw_protocol_native_endpoint_stream_method_marshal, ++ &pw_protocol_native_endpoint_stream_method_demarshal, ++ &pw_protocol_native_endpoint_stream_event_marshal, ++ &pw_protocol_native_endpoint_stream_event_demarshal, ++}; ++ ++/*********************************************** ++ * ENDPOINT ++ ***********************************************/ ++ ++static void endpoint_marshal_info (void *object, ++ const struct pw_endpoint_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_endpoint_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void endpoint_marshal_param (void *object, int seq, uint32_t id, ++ uint32_t index, uint32_t next, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_ENDPOINT_PROXY_EVENT_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(seq), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(next), ++ SPA_POD_Pod(param)); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static int endpoint_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_endpoint_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int endpoint_marshal_subscribe_params(void *object, ++ uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(num), ++ SPA_POD_Pod(filter)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_marshal_set_param(void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_ENDPOINT_PROXY_METHOD_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int endpoint_demarshal_info(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_endpoint_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_endpoint_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, ++ info, 0, &info); ++} ++ ++static int endpoint_demarshal_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, next; ++ int seq; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&next), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int endpoint_demarshal_subscribe_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t csize, ctype, n_ids; ++ uint32_t *ids; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) ++ return -EINVAL; ++ ++ if (ctype != SPA_TYPE_Id) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int endpoint_demarshal_enum_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, num; ++ int seq; ++ struct spa_pod *filter; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&num), ++ SPA_POD_Pod(&filter)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int endpoint_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = { ++ PW_VERSION_ENDPOINT_PROXY_EVENTS, ++ .info = endpoint_marshal_info, ++ .param = endpoint_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_event_demarshal[PW_ENDPOINT_PROXY_EVENT_NUM] = ++{ ++ [PW_ENDPOINT_PROXY_EVENT_INFO] = { endpoint_demarshal_info, 0 }, ++ [PW_ENDPOINT_PROXY_EVENT_PARAM] = { endpoint_demarshal_param, 0 }, ++}; ++ ++static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method_marshal = { ++ PW_VERSION_ENDPOINT_PROXY_METHODS, ++ .add_listener = endpoint_marshal_add_listener, ++ .subscribe_params = endpoint_marshal_subscribe_params, ++ .enum_params = endpoint_marshal_enum_params, ++ .set_param = endpoint_marshal_set_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_endpoint_method_demarshal[PW_ENDPOINT_PROXY_METHOD_NUM] = ++{ ++ [PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_demarshal_subscribe_params, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS] = { endpoint_demarshal_enum_params, 0 }, ++ [PW_ENDPOINT_PROXY_METHOD_SET_PARAM] = { endpoint_demarshal_set_param, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = { ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_VERSION_ENDPOINT_PROXY, ++ PW_ENDPOINT_PROXY_METHOD_NUM, ++ PW_ENDPOINT_PROXY_EVENT_NUM, ++ &pw_protocol_native_endpoint_method_marshal, ++ &pw_protocol_native_endpoint_method_demarshal, ++ &pw_protocol_native_endpoint_event_marshal, ++ &pw_protocol_native_endpoint_event_demarshal, ++}; ++ ++/*********************************************** ++ * SESSION ++ ***********************************************/ ++ ++static void session_marshal_info (void *object, ++ const struct pw_session_info *info) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_SESSION_PROXY_EVENT_INFO, NULL); ++ ++ marshal_pw_session_info(b, info); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static void session_marshal_param (void *object, int seq, uint32_t id, ++ uint32_t index, uint32_t next, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_resource(resource, ++ PW_SESSION_PROXY_EVENT_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(seq), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(next), ++ SPA_POD_Pod(param)); ++ ++ pw_protocol_native_end_resource(resource, b); ++} ++ ++static int session_marshal_add_listener(void *object, ++ struct spa_hook *listener, ++ const struct pw_session_proxy_events *events, ++ void *data) ++{ ++ struct pw_proxy *proxy = object; ++ pw_proxy_add_object_listener(proxy, listener, events, data); ++ return 0; ++} ++ ++static int session_marshal_subscribe_params(void *object, ++ uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int session_marshal_enum_params(void *object, ++ int seq, uint32_t id, ++ uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_protocol_native_message *msg; ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_ENUM_PARAMS, &msg); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), ++ SPA_POD_Id(id), ++ SPA_POD_Int(index), ++ SPA_POD_Int(num), ++ SPA_POD_Pod(filter)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int session_marshal_set_param(void *object, ++ uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_SET_PARAM, NULL); ++ ++ spa_pod_builder_add_struct(b, ++ SPA_POD_Id(id), ++ SPA_POD_Int(flags), ++ SPA_POD_Pod(param)); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int session_marshal_create_link(void *object, ++ const struct spa_dict *props) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_builder *b; ++ ++ b = pw_protocol_native_begin_proxy(proxy, ++ PW_SESSION_PROXY_METHOD_CREATE_LINK, NULL); ++ ++ push_dict(b, props); ++ ++ return pw_protocol_native_end_proxy(proxy, b); ++} ++ ++static int session_demarshal_info(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ struct pw_session_info info = { .props = &props }; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ demarshal_pw_session_info(&prs, &f, &info); ++ ++ return pw_proxy_notify(proxy, struct pw_session_proxy_events, ++ info, 0, &info); ++} ++ ++static int session_demarshal_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_proxy *proxy = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, next; ++ int seq; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&next), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_proxy_notify(proxy, struct pw_session_proxy_events, ++ param, 0, seq, id, index, next, param); ++} ++ ++static int session_demarshal_subscribe_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t csize, ctype, n_ids; ++ uint32_t *ids; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) ++ return -EINVAL; ++ ++ if (ctype != SPA_TYPE_Id) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ subscribe_params, 0, ids, n_ids); ++} ++ ++static int session_demarshal_enum_params(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, index, num; ++ int seq; ++ struct spa_pod *filter; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Int(&seq), ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&index), ++ SPA_POD_Int(&num), ++ SPA_POD_Pod(&filter)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ enum_params, 0, seq, id, index, num, filter); ++} ++ ++static int session_demarshal_set_param(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ uint32_t id, flags; ++ struct spa_pod *param; ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ if (spa_pod_parser_get_struct(&prs, ++ SPA_POD_Id(&id), ++ SPA_POD_Int(&flags), ++ SPA_POD_Pod(¶m)) < 0) ++ return -EINVAL; ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ set_param, 0, id, flags, param); ++} ++ ++static int session_demarshal_create_link(void *object, ++ const struct pw_protocol_native_message *msg) ++{ ++ struct pw_resource *resource = object; ++ struct spa_pod_parser prs; ++ struct spa_pod_frame f; ++ struct spa_dict props = SPA_DICT_INIT(NULL, 0); ++ ++ spa_pod_parser_init(&prs, msg->data, msg->size); ++ ++ parse_dict(&prs, &f, &props); ++ ++ return pw_resource_notify(resource, struct pw_session_proxy_methods, ++ create_link, 0, &props); ++} ++ ++static const struct pw_session_proxy_events pw_protocol_native_session_event_marshal = { ++ PW_VERSION_SESSION_PROXY_EVENTS, ++ .info = session_marshal_info, ++ .param = session_marshal_param, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_session_event_demarshal[PW_SESSION_PROXY_EVENT_NUM] = ++{ ++ [PW_SESSION_PROXY_EVENT_INFO] = { session_demarshal_info, 0 }, ++ [PW_SESSION_PROXY_EVENT_PARAM] = { session_demarshal_param, 0 }, ++}; ++ ++static const struct pw_session_proxy_methods pw_protocol_native_session_method_marshal = { ++ PW_VERSION_SESSION_PROXY_METHODS, ++ .add_listener = session_marshal_add_listener, ++ .subscribe_params = session_marshal_subscribe_params, ++ .enum_params = session_marshal_enum_params, ++ .set_param = session_marshal_set_param, ++ .create_link = session_marshal_create_link, ++}; ++ ++static const struct pw_protocol_native_demarshal ++pw_protocol_native_session_method_demarshal[PW_SESSION_PROXY_METHOD_NUM] = ++{ ++ [PW_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, ++ [PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS] = { session_demarshal_subscribe_params, 0 }, ++ [PW_SESSION_PROXY_METHOD_ENUM_PARAMS] = { session_demarshal_enum_params, 0 }, ++ [PW_SESSION_PROXY_METHOD_SET_PARAM] = { session_demarshal_set_param, PW_PERM_W }, ++ [PW_SESSION_PROXY_METHOD_CREATE_LINK] = { session_demarshal_create_link, PW_PERM_W }, ++}; ++ ++static const struct pw_protocol_marshal pw_protocol_native_session_marshal = { ++ PW_TYPE_INTERFACE_Session, ++ PW_VERSION_SESSION_PROXY, ++ PW_SESSION_PROXY_METHOD_NUM, ++ PW_SESSION_PROXY_EVENT_NUM, ++ &pw_protocol_native_session_method_marshal, ++ &pw_protocol_native_session_method_demarshal, ++ &pw_protocol_native_session_event_marshal, ++ &pw_protocol_native_session_event_demarshal, ++}; ++ ++struct pw_protocol *pw_protocol_native_ext_session_manager_init(struct pw_core *core) ++{ ++ struct pw_protocol *protocol; ++ ++ protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native); ++ ++ if (protocol == NULL) ++ return NULL; ++ ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_client_endpoint_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_client_session_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_link_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_stream_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_marshal); ++ pw_protocol_add_marshal(protocol, &pw_protocol_native_session_marshal); ++ ++ return protocol; ++} +diff --git a/src/modules/module-session-manager/session.c b/src/modules/module-session-manager/session.c +new file mode 100644 +index 00000000..226eba4e +--- /dev/null ++++ b/src/modules/module-session-manager/session.c +@@ -0,0 +1,341 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++ ++#include ++ ++#include "session.h" ++#include "client-session.h" ++ ++#include ++ ++#define NAME "session" ++ ++struct resource_data { ++ struct session *session; ++ struct spa_hook resource_listener; ++ struct spa_hook object_listener; ++ uint32_t n_subscribe_ids; ++ uint32_t subscribe_ids[32]; ++}; ++ ++#define pw_session_resource(r,m,v,...) \ ++ pw_resource_call(r,struct pw_session_proxy_events,m,v,__VA_ARGS__) ++#define pw_session_resource_info(r,...) \ ++ pw_session_resource(r,info,0,__VA_ARGS__) ++#define pw_session_resource_param(r,...) \ ++ pw_session_resource(r,param,0,__VA_ARGS__) ++ ++static int session_enum_params (void *object, int seq, ++ uint32_t id, uint32_t start, uint32_t num, ++ const struct spa_pod *filter) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct session *this = data->session; ++ struct spa_pod *result; ++ struct spa_pod *param; ++ uint8_t buffer[1024]; ++ struct spa_pod_builder b = { 0 }; ++ uint32_t index; ++ uint32_t next = start; ++ uint32_t count = 0; ++ ++ while (true) { ++ index = next++; ++ if (index >= this->n_params) ++ break; ++ ++ param = this->params[index]; ++ ++ if (param == NULL || !spa_pod_is_object_id(param, id)) ++ continue; ++ ++ spa_pod_builder_init(&b, buffer, sizeof(buffer)); ++ if (spa_pod_filter(&b, &result, param, filter) != 0) ++ continue; ++ ++ pw_log_debug(NAME" %p: %d param %u", this, seq, index); ++ ++ pw_session_resource_param(resource, seq, id, index, next, result); ++ ++ if (++count == num) ++ break; ++ } ++ return 0; ++} ++ ++static int session_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ uint32_t i; ++ ++ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); ++ data->n_subscribe_ids = n_ids; ++ ++ for (i = 0; i < n_ids; i++) { ++ data->subscribe_ids[i] = ids[i]; ++ pw_log_debug(NAME" %p: resource %d subscribe param %u", ++ data->session, resource->id, ids[i]); ++ session_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); ++ } ++ return 0; ++} ++ ++static int session_set_param (void *object, uint32_t id, uint32_t flags, ++ const struct spa_pod *param) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct session *this = data->session; ++ ++ pw_client_session_resource_set_param(this->client_sess->resource, ++ id, flags, param); ++ ++ return 0; ++} ++ ++static int session_create_link(void *object, const struct spa_dict *props) ++{ ++ struct pw_resource *resource = object; ++ struct resource_data *data = pw_resource_get_user_data(resource); ++ struct session *this = data->session; ++ ++ pw_client_session_resource_create_link(this->client_sess->resource, ++ props); ++ ++ return 0; ++} ++ ++static const struct pw_session_proxy_methods methods = { ++ PW_VERSION_SESSION_PROXY_METHODS, ++ .subscribe_params = session_subscribe_params, ++ .enum_params = session_enum_params, ++ .set_param = session_set_param, ++ .create_link = session_create_link, ++}; ++ ++static void session_notify_subscribed(struct session *this, ++ uint32_t index, uint32_t next) ++{ ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ struct spa_pod *param = this->params[index]; ++ uint32_t id; ++ uint32_t i; ++ ++ if (!param || !spa_pod_is_object (param)) ++ return; ++ ++ id = SPA_POD_OBJECT_ID (param); ++ ++ spa_list_for_each(resource, &global->resource_list, link) { ++ data = pw_resource_get_user_data(resource); ++ for (i = 0; i < data->n_subscribe_ids; i++) { ++ if (data->subscribe_ids[i] == id) { ++ pw_session_resource_param(resource, 1, id, ++ index, next, param); ++ } ++ } ++ } ++} ++ ++int session_update(struct session *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info) ++{ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { ++ uint32_t i; ++ size_t size = n_params * sizeof(struct spa_pod *); ++ ++ pw_log_debug(NAME" %p: update %d params", this, n_params); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ this->params = realloc(this->params, size); ++ if (size > 0 && !this->params) { ++ this->n_params = 0; ++ goto no_mem; ++ } ++ this->n_params = n_params; ++ ++ for (i = 0; i < this->n_params; i++) { ++ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; ++ session_notify_subscribed(this, i, i+1); ++ } ++ } ++ ++ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { ++ struct pw_resource *resource; ++ ++ if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS) ++ pw_properties_update(this->props, info->props); ++ ++ if (info->change_mask & PW_SESSION_CHANGE_MASK_PARAMS) { ++ size_t size = info->n_params * sizeof(struct spa_param_info); ++ ++ this->info.params = realloc(this->info.params, size); ++ if (size > 0 && !this->info.params) { ++ this->info.n_params = 0; ++ goto no_mem; ++ } ++ this->info.n_params = info->n_params; ++ ++ memcpy(this->info.params, info->params, size); ++ } ++ ++ this->info.change_mask = info->change_mask; ++ spa_list_for_each(resource, &this->global->resource_list, link) { ++ pw_session_resource_info(resource, &this->info); ++ } ++ this->info.change_mask = 0; ++ } ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't update: no memory"); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" can't update: no memory"); ++ return -ENOMEM; ++} ++ ++static void session_unbind(void *data) ++{ ++ struct pw_resource *resource = data; ++ spa_list_remove(&resource->link); ++} ++ ++static const struct pw_resource_events resource_events = { ++ PW_VERSION_RESOURCE_EVENTS, ++ .destroy = session_unbind, ++}; ++ ++static int session_bind(void *_data, struct pw_client *client, ++ uint32_t permissions, uint32_t version, uint32_t id) ++{ ++ struct session *this = _data; ++ struct pw_global *global = this->global; ++ struct pw_resource *resource; ++ struct resource_data *data; ++ ++ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); ++ if (resource == NULL) ++ goto no_mem; ++ ++ data = pw_resource_get_user_data(resource); ++ data->session = this; ++ pw_resource_add_listener(resource, &data->resource_listener, ++ &resource_events, resource); ++ pw_resource_add_object_listener(resource, &data->object_listener, ++ &methods, resource); ++ ++ pw_log_debug(NAME" %p: bound to %d", this, resource->id); ++ ++ spa_list_append(&global->resource_list, &resource->link); ++ ++ this->info.change_mask = PW_SESSION_CHANGE_MASK_ALL; ++ pw_session_resource_info(resource, &this->info); ++ this->info.change_mask = 0; ++ ++ return 0; ++ ++ no_mem: ++ pw_log_error(NAME" can't create resource: no memory"); ++ pw_resource_error(this->client_sess->resource, -ENOMEM, ++ NAME" can't create resource: no memory"); ++ return -ENOMEM; ++} ++ ++int session_init(struct session *this, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties) ++{ ++ const char *keys[] = { ++ PW_KEY_FACTORY_ID, ++ PW_KEY_CLIENT_ID, ++ NULL ++ }; ++ ++ pw_log_debug(NAME" %p: new", this); ++ ++ this->client_sess = client_sess; ++ this->props = properties; ++ ++ properties = pw_properties_new(NULL, NULL); ++ if (!properties) ++ goto no_mem; ++ ++ pw_properties_copy_keys(this->props, properties, keys); ++ ++ this->global = pw_global_new (core, ++ PW_TYPE_INTERFACE_Session, ++ PW_VERSION_SESSION_PROXY, ++ properties, session_bind, this); ++ if (!this->global) ++ goto no_mem; ++ ++ pw_properties_setf(this->props, PW_KEY_SESSION_ID, "%u", this->global->id); ++ ++ this->info.version = PW_VERSION_SESSION_INFO; ++ this->info.id = this->global->id; ++ this->info.props = &this->props->dict; ++ ++ pw_client_session_resource_set_id(client_sess->resource, this->global->id); ++ ++ return pw_global_register(this->global); ++ ++ no_mem: ++ pw_log_error(NAME" - can't create - out of memory"); ++ return -ENOMEM; ++} ++ ++void session_clear(struct session *this) ++{ ++ uint32_t i; ++ ++ pw_log_debug(NAME" %p: destroy", this); ++ ++ pw_global_destroy(this->global); ++ ++ for (i = 0; i < this->n_params; i++) ++ free(this->params[i]); ++ free(this->params); ++ ++ free(this->info.params); ++ ++ if (this->props) ++ pw_properties_free(this->props); ++} +diff --git a/src/modules/module-session-manager/session.h b/src/modules/module-session-manager/session.h +new file mode 100644 +index 00000000..ad0b9b1b +--- /dev/null ++++ b/src/modules/module-session-manager/session.h +@@ -0,0 +1,61 @@ ++/* PipeWire ++ * ++ * Copyright © 2019 Collabora Ltd. ++ * @author George Kiagiadakis ++ * ++ * 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 MODULE_SESSION_MANAGER_SESSION_H ++#define MODULE_SESSION_MANAGER_SESSION_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++struct client_session; ++ ++struct session { ++ struct client_session *client_sess; ++ struct pw_global *global; ++ uint32_t n_params; ++ struct spa_pod **params; ++ struct pw_session_info info; ++ struct pw_properties *props; /* wrapper of info.props */ ++}; ++ ++int session_init(struct session *this, ++ struct client_session *client_sess, ++ struct pw_core *core, ++ struct pw_properties *properties); ++ ++void session_clear(struct session *this); ++ ++int session_update(struct session *this, ++ uint32_t change_mask, ++ uint32_t n_params, ++ const struct spa_pod **params, ++ const struct pw_session_info *info); ++ ++#ifdef __cplusplus ++} /* extern "C" */ ++#endif ++ ++#endif /* MODULE_SESSION_MANAGER_SESSION_H */ +diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c +index c556a1db..06ccccc3 100644 +--- a/src/pipewire/pipewire.c ++++ b/src/pipewire/pipewire.c +@@ -577,6 +577,12 @@ static const struct spa_type_info type_info[] = { + { PW_TYPE_INTERFACE_Module, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Module", NULL }, + { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL }, + { PW_TYPE_INTERFACE_Device, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Device", NULL }, ++ { PW_TYPE_INTERFACE_ClientEndpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientEndpoint", NULL}, ++ { PW_TYPE_INTERFACE_Endpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Endpoint", NULL}, ++ { PW_TYPE_INTERFACE_EndpointStream, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointStream", NULL}, ++ { PW_TYPE_INTERFACE_ClientSession, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientSession", NULL}, ++ { PW_TYPE_INTERFACE_Session, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Session", NULL}, ++ { PW_TYPE_INTERFACE_EndpointLink, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointLink", NULL}, + { SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types }, + { 0, 0, NULL, NULL }, + }; +diff --git a/src/pipewire/type.h b/src/pipewire/type.h +index a1b205f7..6b1b8b50 100644 +--- a/src/pipewire/type.h ++++ b/src/pipewire/type.h +@@ -48,7 +48,12 @@ enum { + /* extensions */ + PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000, + PW_TYPE_INTERFACE_ClientNode, +- ++ PW_TYPE_INTERFACE_ClientEndpoint, ++ PW_TYPE_INTERFACE_Endpoint, ++ PW_TYPE_INTERFACE_EndpointStream, ++ PW_TYPE_INTERFACE_ClientSession, ++ PW_TYPE_INTERFACE_Session, ++ PW_TYPE_INTERFACE_EndpointLink, + }; + + #define PW_TYPE_INFO_BASE "PipeWire:" +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch deleted file mode 100644 index 284d01d0..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch +++ /dev/null @@ -1,27 +0,0 @@ -From 6f3335cfc06053e3ea5598058e0a2581e7ffa4af Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Sun, 22 Sep 2019 17:37:49 +0300 -Subject: [PATCH] bluez-monitor: fix usage of pw_properties_setf without a - format string - -Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/189] ---- - src/examples/bluez-monitor.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/examples/bluez-monitor.c b/src/examples/bluez-monitor.c -index 1ae5ffe0..4bdfeb0d 100644 ---- a/src/examples/bluez-monitor.c -+++ b/src/examples/bluez-monitor.c -@@ -123,7 +123,7 @@ static struct bluez5_node *bluez5_create_node(struct bluez5_object *obj, uint32_ - - pw_properties_setf(node->props, PW_KEY_NODE_NAME, "%s.%s", info->factory_name, str); - pw_properties_set(node->props, PW_KEY_NODE_DESCRIPTION, str); -- pw_properties_setf(node->props, "factory.name", info->factory_name); -+ pw_properties_set(node->props, "factory.name", info->factory_name); - - node->monitor = monitor; - node->object = obj; --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch new file mode 100644 index 00000000..0456f84a --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch @@ -0,0 +1,167 @@ +From 76865803c2db13b753e1261e01de22760c7b398b Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Tue, 28 May 2019 11:46:36 +0300 +Subject: [PATCH] pipewire-cli: add support for printing endpoint info & params + +Upstream-Status: Pending +--- + src/tools/pipewire-cli.c | 107 ++++++++++++++++++++++++++++++++++++++- + 1 file changed, 106 insertions(+), 1 deletion(-) + +diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c +index 6110d170..0dbc8368 100644 +--- a/src/tools/pipewire-cli.c ++++ b/src/tools/pipewire-cli.c +@@ -37,6 +37,8 @@ + #include + #include + ++#include ++ + static const char WHITESPACE[] = " \t"; + + struct remote_data; +@@ -174,8 +176,10 @@ static void print_params(struct spa_param_info *params, uint32_t n_params, char + return; + } + for (i = 0; i < n_params; i++) { ++ const struct spa_type_info *type_info = spa_type_param; ++ + fprintf(stdout, "%c\t %d (%s) %c%c\n", mark, params[i].id, +- spa_debug_type_find_name(spa_type_param, params[i].id), ++ spa_debug_type_find_name(type_info, params[i].id), + params[i].flags & SPA_PARAM_INFO_READ ? 'r' : '-', + params[i].flags & SPA_PARAM_INFO_WRITE ? 'w' : '-'); + } +@@ -652,6 +656,40 @@ static void info_device(struct proxy_data *pd) + info->change_mask = 0; + } + ++static void info_endpoint(struct proxy_data *pd) ++{ ++ struct pw_endpoint_info *info = pd->info; ++ const char *direction; ++ ++ info_global(pd); ++ fprintf(stdout, "\tname: %s\n", info->name); ++ fprintf(stdout, "\tmedia-class: %s\n", info->media_class); ++ switch(info->direction) { ++ case PW_ENDPOINT_DIRECTION_SINK_INPUT: ++ direction = "sink-input"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT: ++ direction = "source-output"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SOURCE: ++ direction = "source"; ++ break; ++ case PW_ENDPOINT_DIRECTION_SINK: ++ direction = "sink"; ++ break; ++ default: ++ direction = "invalid"; ++ break; ++ } ++ fprintf(stdout, "\tdirection: %s\n", direction); ++ fprintf(stdout, "\tflags: 0x%x\n", info->flags); ++ fprintf(stdout, "%c\tstreams: %u\n", MARK_CHANGE(0), info->n_streams); ++ fprintf(stdout, "%c\tsession: %u\n", MARK_CHANGE(0), info->session_id); ++ print_properties(info->props, MARK_CHANGE(2), true); ++ print_params(info->params, info->n_params, MARK_CHANGE(3), true); ++ info->change_mask = 0; ++} ++ + static void core_event_info(void *object, const struct pw_core_info *info) + { + struct proxy_data *pd = object; +@@ -853,6 +891,63 @@ static const struct pw_device_proxy_events device_events = { + .param = event_param + }; + ++static void endpoint_info_free(struct pw_endpoint_info *info) ++{ ++ free(info->name); ++ free(info->media_class); ++ free(info->params); ++ if (info->props) ++ pw_properties_free ((struct pw_properties *)info->props); ++ free(info); ++} ++ ++static void endpoint_event_info(void *object, ++ const struct pw_endpoint_info *update) ++{ ++ struct proxy_data *pd = object; ++ struct remote_data *rd = pd->rd; ++ struct pw_endpoint_info *info = pd->info; ++ ++ if (!info) { ++ info = pd->info = calloc(1, sizeof(*info)); ++ info->id = update->id; ++ info->name = strdup(update->name); ++ info->media_class = strdup(update->media_class); ++ info->direction = update->direction; ++ info->flags = update->flags; ++ } ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) ++ info->n_streams = update->n_streams; ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) ++ info->session_id = update->session_id; ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { ++ info->n_params = update->n_params; ++ free(info->params); ++ info->params = malloc(info->n_params * sizeof(struct spa_param_info)); ++ memcpy(info->params, update->params, ++ info->n_params * sizeof(struct spa_param_info)); ++ } ++ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { ++ if (info->props) ++ pw_properties_free ((struct pw_properties *)info->props); ++ info->props = ++ (struct spa_dict *) pw_properties_new_dict (update->props); ++ } ++ ++ if (pd->global == NULL) ++ pd->global = pw_map_lookup(&rd->globals, info->id); ++ if (pd->global && pd->global->info_pending) { ++ info_endpoint(pd); ++ pd->global->info_pending = false; ++ } ++} ++ ++static const struct pw_endpoint_proxy_events endpoint_events = { ++ PW_VERSION_ENDPOINT_PROXY_EVENTS, ++ .info = endpoint_event_info, ++ .param = event_param ++}; ++ + static void + destroy_proxy (void *data) + { +@@ -939,6 +1034,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er + destroy = (pw_destroy_t) pw_link_info_free; + info_func = info_link; + break; ++ case PW_TYPE_INTERFACE_Endpoint: ++ events = &endpoint_events; ++ client_version = PW_VERSION_ENDPOINT_PROXY; ++ destroy = (pw_destroy_t) endpoint_info_free; ++ info_func = info_endpoint; ++ break; + default: + asprintf(error, "unsupported type %s", spa_debug_type_find_name(pw_type_info(), global->type)); + return false; +@@ -1213,6 +1314,10 @@ static bool do_enum_params(struct data *data, const char *cmd, char *args, char + pw_device_proxy_enum_params((struct pw_device_proxy*)global->proxy, 0, + param_id, 0, 0, NULL); + break; ++ case PW_TYPE_INTERFACE_Endpoint: ++ pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)global->proxy, 0, ++ param_id, 0, 0, NULL); ++ break; + default: + asprintf(error, "enum-params not implemented on object %d", atoi(a[0])); + return false; +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch new file mode 100644 index 00000000..e42e62a2 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch @@ -0,0 +1,37 @@ +From f451147ffc1f89f4c0f705ac5c444e614dbff22a Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +Date: Fri, 9 Aug 2019 13:26:15 +0300 +Subject: [PATCH] daemon config & remote: load module-session-manager by + default + +Upstream-Status: Pending +--- + src/daemon/pipewire.conf.in | 1 + + src/pipewire/remote.c | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in +index 174ae97d..bf64c574 100644 +--- a/src/daemon/pipewire.conf.in ++++ b/src/daemon/pipewire.conf.in +@@ -23,4 +23,5 @@ load-module libpipewire-module-client-device + load-module libpipewire-module-access + load-module libpipewire-module-adapter + load-module libpipewire-module-link-factory ++load-module libpipewire-module-session-manager + exec build/src/examples/media-session +diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c +index fe62582c..e89474b0 100644 +--- a/src/pipewire/remote.c ++++ b/src/pipewire/remote.c +@@ -242,6 +242,7 @@ struct pw_remote *pw_remote_new(struct pw_core *core, + pw_module_load(core, "libpipewire-module-rtkit", NULL, NULL); + pw_module_load(core, "libpipewire-module-client-node", NULL, NULL); + pw_module_load(core, "libpipewire-module-adapter", NULL, NULL); ++ pw_module_load(core, "libpipewire-module-session-manager", NULL, NULL); + + spa_list_append(&core->remote_list, &this->link); + +-- +2.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch deleted file mode 100644 index 8471276b..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-meson-revert-version-check-to-require-meson-0.47-not.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 544adb27b33843c0bb970ddb07485f57c2dad7a9 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -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 5cd61fde..51a839fa 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.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch new file mode 100644 index 00000000..06ffb3d7 --- /dev/null +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch @@ -0,0 +1,35 @@ +From a95e87cd47de3585d9f4ef77b75c4a077bd95f78 Mon Sep 17 00:00:00 2001 +From: George Kiagiadakis +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 0859bf73..ac4dceef 100644 +--- a/spa/plugins/audioconvert/audioconvert.c ++++ b/spa/plugins/audioconvert/audioconvert.c +@@ -109,8 +109,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.23.0 + diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch deleted file mode 100644 index cdb030e1..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-extensions-implement-new-session-manager-extension.patch +++ /dev/null @@ -1,5715 +0,0 @@ -From acbce75de9587917cfa659ebc0e3404b6b1d4c29 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Thu, 23 May 2019 18:59:05 +0300 -Subject: [PATCH] extensions: implement new session manager extension - -This extension, implemented in module-session-manager, implements -a set of objects that are useful for session managers. - -Upstream-Status: Pending ---- - src/extensions/meson.build | 9 + - src/extensions/session-manager.h | 34 + - .../session-manager/impl-interfaces.h | 329 +++ - src/extensions/session-manager/interfaces.h | 465 ++++ - src/extensions/session-manager/introspect.h | 131 + - src/extensions/session-manager/keys.h | 40 + - src/modules/meson.build | 17 + - src/modules/module-session-manager.c | 56 + - .../module-session-manager/client-endpoint.c | 270 +++ - .../module-session-manager/client-endpoint.h | 60 + - .../module-session-manager/client-session.c | 270 +++ - .../module-session-manager/client-session.h | 62 + - .../module-session-manager/endpoint-link.c | 359 +++ - .../module-session-manager/endpoint-link.h | 64 + - .../module-session-manager/endpoint-stream.c | 329 +++ - .../module-session-manager/endpoint-stream.h | 64 + - src/modules/module-session-manager/endpoint.c | 343 +++ - src/modules/module-session-manager/endpoint.h | 61 + - .../module-session-manager/protocol-native.c | 2125 +++++++++++++++++ - src/modules/module-session-manager/session.c | 341 +++ - src/modules/module-session-manager/session.h | 61 + - src/pipewire/pipewire.c | 6 + - src/pipewire/type.h | 7 +- - 23 files changed, 5502 insertions(+), 1 deletion(-) - create mode 100644 src/extensions/session-manager.h - create mode 100644 src/extensions/session-manager/impl-interfaces.h - create mode 100644 src/extensions/session-manager/interfaces.h - create mode 100644 src/extensions/session-manager/introspect.h - create mode 100644 src/extensions/session-manager/keys.h - create mode 100644 src/modules/module-session-manager.c - create mode 100644 src/modules/module-session-manager/client-endpoint.c - create mode 100644 src/modules/module-session-manager/client-endpoint.h - create mode 100644 src/modules/module-session-manager/client-session.c - create mode 100644 src/modules/module-session-manager/client-session.h - create mode 100644 src/modules/module-session-manager/endpoint-link.c - create mode 100644 src/modules/module-session-manager/endpoint-link.h - create mode 100644 src/modules/module-session-manager/endpoint-stream.c - create mode 100644 src/modules/module-session-manager/endpoint-stream.h - create mode 100644 src/modules/module-session-manager/endpoint.c - create mode 100644 src/modules/module-session-manager/endpoint.h - create mode 100644 src/modules/module-session-manager/protocol-native.c - create mode 100644 src/modules/module-session-manager/session.c - create mode 100644 src/modules/module-session-manager/session.h - -diff --git a/src/extensions/meson.build b/src/extensions/meson.build -index a7f5d3cb..95377faa 100644 ---- a/src/extensions/meson.build -+++ b/src/extensions/meson.build -@@ -1,6 +1,15 @@ -+pipewire_ext_sm_headers = [ -+ 'session-manager/impl-interfaces.h', -+ 'session-manager/interfaces.h', -+ 'session-manager/introspect.h', -+ 'session-manager/keys.h', -+] -+ - pipewire_ext_headers = [ - 'client-node.h', - 'protocol-native.h', -+ 'session-manager.h', - ] - -+install_headers(pipewire_ext_sm_headers, subdir : 'pipewire/extensions/session-manager') - install_headers(pipewire_ext_headers, subdir : 'pipewire/extensions') -diff --git a/src/extensions/session-manager.h b/src/extensions/session-manager.h -new file mode 100644 -index 00000000..95e759b0 ---- /dev/null -+++ b/src/extensions/session-manager.h -@@ -0,0 +1,34 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 PIPEWIRE_EXT_SESSION_MANAGER_H -+#define PIPEWIRE_EXT_SESSION_MANAGER_H -+ -+#include "session-manager/introspect.h" -+#include "session-manager/interfaces.h" -+#include "session-manager/impl-interfaces.h" -+#include "session-manager/keys.h" -+ -+#endif /* PIPEWIRE_EXT_SESSION_MANAGER_H */ -diff --git a/src/extensions/session-manager/impl-interfaces.h b/src/extensions/session-manager/impl-interfaces.h -new file mode 100644 -index 00000000..66daa0b9 ---- /dev/null -+++ b/src/extensions/session-manager/impl-interfaces.h -@@ -0,0 +1,329 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H -+#define PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H -+ -+#include -+#include -+#include -+ -+#include "introspect.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define PW_VERSION_CLIENT_ENDPOINT_PROXY 0 -+struct pw_client_endpoint_proxy { struct spa_interface iface; }; -+ -+#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID 0 -+#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID 1 -+#define PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM 2 -+#define PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM 3 -+#define PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM 4 -+ -+struct pw_client_endpoint_proxy_events { -+#define PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Sets the id of the \a endpoint. -+ * -+ * On endpoint implementations, this is called by the server to notify -+ * the implementation of the assigned global id of the endpoint. The -+ * implementation is obliged to set this id in the -+ * #struct pw_endpoint_info \a id field. The implementation should also -+ * not emit the info() event before this method is called. -+ * -+ * \param endpoint a #pw_endpoint -+ * \param id the global id assigned to this endpoint -+ * -+ * \return 0 on success -+ * -EINVAL when the id has already been set -+ * -ENOTSUP on the server-side endpoint implementation -+ */ -+ int (*set_id) (void *endpoint, uint32_t id); -+ -+ /** -+ * Sets the session id of the \a endpoint. -+ * -+ * On endpoints that are not session masters, this method notifies -+ * the implementation that it has been associated with a session. -+ * The implementation is obliged to set this id in the -+ * #struct pw_endpoint_info \a session_id field. -+ * -+ * \param endpoint a #pw_endpoint -+ * \param id the session id associated with this endpoint -+ * -+ * \return 0 on success -+ * -EINVAL when the session id has already been set -+ * -ENOTSUP when the endpoint is a session master -+ */ -+ int (*set_session_id) (void *endpoint, uint32_t session_id); -+ -+ /** -+ * Set the configurable parameter in \a endpoint. -+ * -+ * Usually, \a param will be obtained from enum_params and then -+ * modified but it is also possible to set another spa_pod -+ * as long as its keys and types match a supported object. -+ * -+ * Objects with property keys that are not known are ignored. -+ * -+ * This function must be called from the main thread. -+ * -+ * \param endpoint a #struct pw_endpoint -+ * \param id the parameter id to configure -+ * \param flags additional flags -+ * \param param the parameter to configure -+ * -+ * \return 0 on success -+ * -EINVAL when \a endpoint is NULL -+ * -ENOTSUP when there are no parameters implemented on \a endpoint -+ * -ENOENT the parameter is unknown -+ */ -+ int (*set_param) (void *endpoint, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+ -+ /** -+ * Set a parameter on \a stream_id of \a endpoint. -+ * -+ * When \a param is NULL, the parameter will be unset. -+ * -+ * This function must be called from the main thread. -+ * -+ * \param endpoint a #struct pw_endpoint -+ * \param stream_id the stream to configure -+ * \param id the parameter id to set -+ * \param flags optional flags -+ * \param param a #struct spa_pod with the parameter to set -+ * \return 0 on success -+ * 1 on success, the value of \a param might have been -+ * changed depending on \a flags and the final value can -+ * be found by doing stream_enum_params. -+ * -EINVAL when \a endpoint is NULL or invalid arguments are given -+ * -ESRCH when the type or size of a property is not correct. -+ * -ENOENT when the param id is not found -+ */ -+ int (*stream_set_param) (void *endpoint, uint32_t stream_id, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+}; -+ -+#define PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE 1 -+#define PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE 2 -+#define PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM 3 -+ -+struct pw_client_endpoint_proxy_methods { -+#define PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_client_endpoint_proxy_events *events, -+ void *data); -+ -+ /** Update endpoint information */ -+ int (*update) (void *object, -+#define PW_CLIENT_ENDPOINT_UPDATE_PARAMS (1 << 0) -+#define PW_CLIENT_ENDPOINT_UPDATE_INFO (1 << 1) -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_info *info); -+ -+ /** Update stream information */ -+ int (*stream_update) (void *object, -+ uint32_t stream_id, -+#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_PARAMS (1 << 0) -+#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_INFO (1 << 1) -+#define PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED (1 << 2) -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_stream_info *info); -+}; -+ -+#define pw_client_endpoint_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_client_endpoint_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_client_endpoint_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_client_endpoint_proxy_add_listener(o,...) pw_client_endpoint_proxy_method(o,add_listener,0,__VA_ARGS__) -+#define pw_client_endpoint_proxy_update(o,...) pw_client_endpoint_proxy_method(o,update,0,__VA_ARGS__) -+#define pw_client_endpoint_proxy_stream_update(o,...) pw_client_endpoint_proxy_method(o,stream_update,0,__VA_ARGS__) -+ -+ -+#define PW_VERSION_CLIENT_SESSION_PROXY 0 -+struct pw_client_session_proxy { struct spa_interface iface; }; -+ -+#define PW_CLIENT_SESSION_PROXY_EVENT_SET_ID 0 -+#define PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM 1 -+#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM 2 -+#define PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK 3 -+#define PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK 4 -+#define PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE 5 -+#define PW_CLIENT_SESSION_PROXY_EVENT_NUM 6 -+ -+struct pw_client_session_proxy_events { -+#define PW_VERSION_CLIENT_SESSION_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Sets the id of the \a session. -+ * -+ * On session implementations, this is called by the server to notify -+ * the implementation of the assigned global id of the session. The -+ * implementation is obliged to set this id in the -+ * #struct pw_session_info \a id field. The implementation should also -+ * not emit the info() event before this method is called. -+ * -+ * \param session a #pw_session -+ * \param id the global id assigned to this session -+ * -+ * \return 0 on success -+ * -EINVAL when the id has already been set -+ * -ENOTSUP on the server-side session implementation -+ */ -+ int (*set_id) (void *session, uint32_t id); -+ -+ /** -+ * Set the configurable parameter in \a session. -+ * -+ * Usually, \a param will be obtained from enum_params and then -+ * modified but it is also possible to set another spa_pod -+ * as long as its keys and types match a supported object. -+ * -+ * Objects with property keys that are not known are ignored. -+ * -+ * This function must be called from the main thread. -+ * -+ * \param session a #struct pw_session -+ * \param id the parameter id to configure -+ * \param flags additional flags -+ * \param param the parameter to configure -+ * -+ * \return 0 on success -+ * -EINVAL when \a session is NULL -+ * -ENOTSUP when there are no parameters implemented on \a session -+ * -ENOENT the parameter is unknown -+ */ -+ int (*set_param) (void *session, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+ -+ /** -+ * Set a parameter on \a link_id of \a session. -+ * -+ * When \a param is NULL, the parameter will be unset. -+ * -+ * This function must be called from the main thread. -+ * -+ * \param session a #struct pw_session -+ * \param link_id the link to configure -+ * \param id the parameter id to set -+ * \param flags optional flags -+ * \param param a #struct spa_pod with the parameter to set -+ * \return 0 on success -+ * 1 on success, the value of \a param might have been -+ * changed depending on \a flags and the final value can -+ * be found by doing link_enum_params. -+ * -EINVAL when \a session is NULL or invalid arguments are given -+ * -ESRCH when the type or size of a property is not correct. -+ * -ENOENT when the param id is not found -+ */ -+ int (*link_set_param) (void *session, uint32_t link_id, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+ -+ int (*create_link) (void *session, const struct spa_dict *props); -+ -+ int (*destroy_link) (void *session, uint32_t link_id); -+ -+ int (*link_request_state) (void *session, uint32_t link_id, uint32_t state); -+}; -+ -+#define PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_CLIENT_SESSION_PROXY_METHOD_UPDATE 1 -+#define PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE 2 -+#define PW_CLIENT_SESSION_PROXY_METHOD_NUM 3 -+ -+struct pw_client_session_proxy_methods { -+#define PW_VERSION_CLIENT_SESSION_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_client_session_proxy_events *events, -+ void *data); -+ -+ /** Update session information */ -+ int (*update) (void *object, -+#define PW_CLIENT_SESSION_UPDATE_PARAMS (1 << 0) -+#define PW_CLIENT_SESSION_UPDATE_INFO (1 << 1) -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_session_info *info); -+ -+ /** Update link information */ -+ int (*link_update) (void *object, -+ uint32_t link_id, -+#define PW_CLIENT_SESSION_LINK_UPDATE_PARAMS (1 << 0) -+#define PW_CLIENT_SESSION_LINK_UPDATE_INFO (1 << 1) -+#define PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED (1 << 2) -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_link_info *info); -+}; -+ -+#define pw_client_session_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_client_session_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_client_session_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_client_session_proxy_add_listener(o,...) pw_client_session_proxy_method(o,add_listener,0,__VA_ARGS__) -+#define pw_client_session_proxy_update(o,...) pw_client_session_proxy_method(o,update,0,__VA_ARGS__) -+#define pw_client_session_proxy_link_update(o,...) pw_client_session_proxy_method(o,link_update,0,__VA_ARGS__) -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H */ -diff --git a/src/extensions/session-manager/interfaces.h b/src/extensions/session-manager/interfaces.h -new file mode 100644 -index 00000000..0651e8bf ---- /dev/null -+++ b/src/extensions/session-manager/interfaces.h -@@ -0,0 +1,465 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H -+#define PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H -+ -+#include -+#include -+ -+#include "introspect.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define PW_VERSION_SESSION_PROXY 0 -+struct pw_session_proxy { struct spa_interface iface; }; -+#define PW_VERSION_ENDPOINT_PROXY 0 -+struct pw_endpoint_proxy { struct spa_interface iface; }; -+#define PW_VERSION_ENDPOINT_STREAM_PROXY 0 -+struct pw_endpoint_stream_proxy { struct spa_interface iface; }; -+#define PW_VERSION_ENDPOINT_LINK_PROXY 0 -+struct pw_endpoint_link_proxy { struct spa_interface iface; }; -+ -+/* Session */ -+ -+#define PW_SESSION_PROXY_EVENT_INFO 0 -+#define PW_SESSION_PROXY_EVENT_PARAM 1 -+#define PW_SESSION_PROXY_EVENT_NUM 2 -+ -+struct pw_session_proxy_events { -+#define PW_VERSION_SESSION_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Notify session info -+ * -+ * \param info info about the session -+ */ -+ void (*info) (void *object, const struct pw_session_info *info); -+ -+ /** -+ * Notify a session param -+ * -+ * Event emited as a result of the enum_params method. -+ * -+ * \param seq the sequence number of the request -+ * \param id the param id -+ * \param index the param index -+ * \param next the param index of the next param -+ * \param param the parameter -+ */ -+ void (*param) (void *object, int seq, -+ uint32_t id, uint32_t index, uint32_t next, -+ const struct spa_pod *param); -+}; -+ -+#define PW_SESSION_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS 1 -+#define PW_SESSION_PROXY_METHOD_ENUM_PARAMS 2 -+#define PW_SESSION_PROXY_METHOD_SET_PARAM 3 -+#define PW_SESSION_PROXY_METHOD_CREATE_LINK 4 -+#define PW_SESSION_PROXY_METHOD_NUM 5 -+ -+struct pw_session_proxy_methods { -+#define PW_VERSION_SESSION_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_session_proxy_events *events, -+ void *data); -+ -+ /** -+ * Subscribe to parameter changes -+ * -+ * Automatically emit param events for the given ids when -+ * they are changed. -+ * -+ * \param ids an array of param ids -+ * \param n_ids the number of ids in \a ids -+ */ -+ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); -+ -+ /** -+ * Enumerate session parameters -+ * -+ * Start enumeration of session parameters. For each param, a -+ * param event will be emited. -+ * -+ * \param seq a sequence number returned in the reply -+ * \param id the parameter id to enumerate -+ * \param start the start index or 0 for the first param -+ * \param num the maximum number of params to retrieve -+ * \param filter a param filter or NULL -+ */ -+ int (*enum_params) (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter); -+ -+ /** -+ * Set a parameter on the session -+ * -+ * \param id the parameter id to set -+ * \param flags extra parameter flags -+ * \param param the parameter to set -+ */ -+ int (*set_param) (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+ -+ int (*create_link) (void *object, const struct spa_dict *props); -+}; -+ -+#define pw_session_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_session_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_session_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_session_proxy_add_listener(c,...) pw_session_proxy_method(c,add_listener,0,__VA_ARGS__) -+#define pw_session_proxy_subscribe_params(c,...) pw_session_proxy_method(c,subscribe_params,0,__VA_ARGS__) -+#define pw_session_proxy_enum_params(c,...) pw_session_proxy_method(c,enum_params,0,__VA_ARGS__) -+#define pw_session_proxy_create_link(c,...) pw_session_proxy_method(c,create_link,0,__VA_ARGS__) -+ -+/* Endpoint */ -+ -+#define PW_ENDPOINT_PROXY_EVENT_INFO 0 -+#define PW_ENDPOINT_PROXY_EVENT_PARAM 1 -+#define PW_ENDPOINT_PROXY_EVENT_NUM 2 -+ -+struct pw_endpoint_proxy_events { -+#define PW_VERSION_ENDPOINT_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Notify endpoint info -+ * -+ * \param info info about the endpoint -+ */ -+ void (*info) (void *object, const struct pw_endpoint_info *info); -+ -+ /** -+ * Notify a endpoint param -+ * -+ * Event emited as a result of the enum_params method. -+ * -+ * \param seq the sequence number of the request -+ * \param id the param id -+ * \param index the param index -+ * \param next the param index of the next param -+ * \param param the parameter -+ */ -+ void (*param) (void *object, int seq, -+ uint32_t id, uint32_t index, uint32_t next, -+ const struct spa_pod *param); -+}; -+ -+#define PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS 1 -+#define PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS 2 -+#define PW_ENDPOINT_PROXY_METHOD_SET_PARAM 3 -+#define PW_ENDPOINT_PROXY_METHOD_NUM 4 -+ -+struct pw_endpoint_proxy_methods { -+#define PW_VERSION_ENDPOINT_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_proxy_events *events, -+ void *data); -+ -+ /** -+ * Subscribe to parameter changes -+ * -+ * Automatically emit param events for the given ids when -+ * they are changed. -+ * -+ * \param ids an array of param ids -+ * \param n_ids the number of ids in \a ids -+ */ -+ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); -+ -+ /** -+ * Enumerate endpoint parameters -+ * -+ * Start enumeration of endpoint parameters. For each param, a -+ * param event will be emited. -+ * -+ * \param seq a sequence number returned in the reply -+ * \param id the parameter id to enumerate -+ * \param start the start index or 0 for the first param -+ * \param num the maximum number of params to retrieve -+ * \param filter a param filter or NULL -+ */ -+ int (*enum_params) (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter); -+ -+ /** -+ * Set a parameter on the endpoint -+ * -+ * \param id the parameter id to set -+ * \param flags extra parameter flags -+ * \param param the parameter to set -+ */ -+ int (*set_param) (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+}; -+ -+#define pw_endpoint_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_endpoint_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_endpoint_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_endpoint_proxy_add_listener(c,...) pw_endpoint_proxy_method(c,add_listener,0,__VA_ARGS__) -+#define pw_endpoint_proxy_subscribe_params(c,...) pw_endpoint_proxy_method(c,subscribe_params,0,__VA_ARGS__) -+#define pw_endpoint_proxy_enum_params(c,...) pw_endpoint_proxy_method(c,enum_params,0,__VA_ARGS__) -+ -+/* Endpoint Stream */ -+ -+#define PW_ENDPOINT_STREAM_PROXY_EVENT_INFO 0 -+#define PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM 1 -+#define PW_ENDPOINT_STREAM_PROXY_EVENT_NUM 2 -+ -+struct pw_endpoint_stream_proxy_events { -+#define PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Notify endpoint stream info -+ * -+ * \param info info about the endpoint stream -+ */ -+ void (*info) (void *object, const struct pw_endpoint_stream_info *info); -+ -+ /** -+ * Notify a endpoint stream param -+ * -+ * Event emited as a result of the enum_params method. -+ * -+ * \param seq the sequence number of the request -+ * \param id the param id -+ * \param index the param index -+ * \param next the param index of the next param -+ * \param param the parameter -+ */ -+ void (*param) (void *object, int seq, -+ uint32_t id, uint32_t index, uint32_t next, -+ const struct spa_pod *param); -+}; -+ -+#define PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS 1 -+#define PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS 2 -+#define PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM 3 -+#define PW_ENDPOINT_STREAM_PROXY_METHOD_NUM 4 -+ -+struct pw_endpoint_stream_proxy_methods { -+#define PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_stream_proxy_events *events, -+ void *data); -+ -+ /** -+ * Subscribe to parameter changes -+ * -+ * Automatically emit param events for the given ids when -+ * they are changed. -+ * -+ * \param ids an array of param ids -+ * \param n_ids the number of ids in \a ids -+ */ -+ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); -+ -+ /** -+ * Enumerate stream parameters -+ * -+ * Start enumeration of stream parameters. For each param, a -+ * param event will be emited. -+ * -+ * \param seq a sequence number returned in the reply -+ * \param id the parameter id to enumerate -+ * \param start the start index or 0 for the first param -+ * \param num the maximum number of params to retrieve -+ * \param filter a param filter or NULL -+ */ -+ int (*enum_params) (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter); -+ -+ /** -+ * Set a parameter on the stream -+ * -+ * \param id the parameter id to set -+ * \param flags extra parameter flags -+ * \param param the parameter to set -+ */ -+ int (*set_param) (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+}; -+ -+#define pw_endpoint_stream_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_endpoint_stream_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_endpoint_stream_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_endpoint_stream_proxy_add_listener(c,...) pw_endpoint_stream_proxy_method(c,add_listener,0,__VA_ARGS__) -+#define pw_endpoint_stream_proxy_subscribe_params(c,...) pw_endpoint_stream_proxy_method(c,subscribe_params,0,__VA_ARGS__) -+#define pw_endpoint_stream_proxy_enum_params(c,...) pw_endpoint_stream_proxy_method(c,enum_params,0,__VA_ARGS__) -+ -+/* Endpoint Link */ -+ -+#define PW_ENDPOINT_LINK_PROXY_EVENT_INFO 0 -+#define PW_ENDPOINT_LINK_PROXY_EVENT_PARAM 1 -+#define PW_ENDPOINT_LINK_PROXY_EVENT_NUM 2 -+ -+struct pw_endpoint_link_proxy_events { -+#define PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ /** -+ * Notify endpoint link info -+ * -+ * \param info info about the endpoint link -+ */ -+ void (*info) (void *object, const struct pw_endpoint_link_info *info); -+ -+ /** -+ * Notify a endpoint link param -+ * -+ * Event emited as a result of the enum_params method. -+ * -+ * \param seq the sequence number of the request -+ * \param id the param id -+ * \param index the param index -+ * \param next the param index of the next param -+ * \param param the parameter -+ */ -+ void (*param) (void *object, int seq, -+ uint32_t id, uint32_t index, uint32_t next, -+ const struct spa_pod *param); -+}; -+ -+#define PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER 0 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS 1 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS 2 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM 3 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE 4 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY 5 -+#define PW_ENDPOINT_LINK_PROXY_METHOD_NUM 6 -+ -+struct pw_endpoint_link_proxy_methods { -+#define PW_VERSION_ENDPOINT_LINK_PROXY_METHODS 0 -+ uint32_t version; /**< version of this structure */ -+ -+ int (*add_listener) (void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_link_proxy_events *events, -+ void *data); -+ -+ /** -+ * Subscribe to parameter changes -+ * -+ * Automatically emit param events for the given ids when -+ * they are changed. -+ * -+ * \param ids an array of param ids -+ * \param n_ids the number of ids in \a ids -+ */ -+ int (*subscribe_params) (void *object, uint32_t *ids, uint32_t n_ids); -+ -+ /** -+ * Enumerate link parameters -+ * -+ * Start enumeration of link parameters. For each param, a -+ * param event will be emited. -+ * -+ * \param seq a sequence number returned in the reply -+ * \param id the parameter id to enumerate -+ * \param start the start index or 0 for the first param -+ * \param num the maximum number of params to retrieve -+ * \param filter a param filter or NULL -+ */ -+ int (*enum_params) (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter); -+ -+ /** -+ * Set a parameter on the link -+ * -+ * \param id the parameter id to set -+ * \param flags extra parameter flags -+ * \param param the parameter to set -+ */ -+ int (*set_param) (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param); -+ -+ int (*request_state) (void *object, enum pw_endpoint_link_state state); -+ -+ int (*destroy) (void *object); -+ -+}; -+ -+#define pw_endpoint_link_proxy_method(o,method,version,...) \ -+({ \ -+ int _res = -ENOTSUP; \ -+ struct pw_endpoint_link_proxy *_p = o; \ -+ spa_interface_call_res(&_p->iface, \ -+ struct pw_endpoint_link_proxy_methods, _res, \ -+ method, version, ##__VA_ARGS__); \ -+ _res; \ -+}) -+ -+#define pw_endpoint_link_proxy_add_listener(c,...) pw_endpoint_link_proxy_method(c,add_listener,0,__VA_ARGS__) -+#define pw_endpoint_link_proxy_subscribe_params(c,...) pw_endpoint_link_proxy_method(c,subscribe_params,0,__VA_ARGS__) -+#define pw_endpoint_link_proxy_enum_params(c,...) pw_endpoint_link_proxy_method(c,enum_params,0,__VA_ARGS__) -+#define pw_endpoint_link_proxy_request_state(c,...) pw_endpoint_link_proxy_method(c,request_state,0,__VA_ARGS__) -+#define pw_endpoint_link_proxy_destroy(c,...) pw_endpoint_link_proxy_method(c,destroy,0,__VA_ARGS__) -+ -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H */ -diff --git a/src/extensions/session-manager/introspect.h b/src/extensions/session-manager/introspect.h -new file mode 100644 -index 00000000..3b0e4113 ---- /dev/null -+++ b/src/extensions/session-manager/introspect.h -@@ -0,0 +1,131 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H -+#define PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H -+ -+#include -+#include -+#include -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define PW_KEY_ENDPOINT_ID "endpoint.id" -+#define PW_KEY_SESSION_ID "session.id" -+ -+enum pw_endpoint_direction { -+ PW_ENDPOINT_DIRECTION_SINK_INPUT = SPA_DIRECTION_INPUT, -+ PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT = SPA_DIRECTION_OUTPUT, -+ PW_ENDPOINT_DIRECTION_SOURCE, -+ PW_ENDPOINT_DIRECTION_SINK, -+}; -+ -+enum pw_endpoint_link_state { -+ PW_ENDPOINT_LINK_STATE_ERROR = -1, -+ PW_ENDPOINT_LINK_STATE_PREPARING, -+ PW_ENDPOINT_LINK_STATE_INACTIVE, -+ PW_ENDPOINT_LINK_STATE_ACTIVE, -+}; -+ -+struct pw_session_info { -+#define PW_VERSION_SESSION_INFO 0 -+ uint32_t version; /**< version of this structure */ -+ uint32_t id; /**< the session id (global) */ -+#define PW_SESSION_CHANGE_MASK_PROPS (1 << 0) -+#define PW_SESSION_CHANGE_MASK_PARAMS (1 << 1) -+#define PW_SESSION_CHANGE_MASK_ALL ((1 << 2)-1) -+ uint32_t change_mask; /**< bitfield of changed fields since last call */ -+ struct spa_dict *props; /**< extra properties */ -+ struct spa_param_info *params; /**< parameters */ -+ uint32_t n_params; /**< number of items in \a params */ -+}; -+ -+struct pw_endpoint_info { -+#define PW_VERSION_ENDPOINT_INFO 0 -+ uint32_t version; /**< version of this structure */ -+ uint32_t id; /**< the endpoint id (global) */ -+ char *name; /**< name of the endpoint */ -+ char *media_class; /**< media class of the endpoint */ -+ enum pw_endpoint_direction direction; /**< direction of the endpoint */ -+#define PW_ENDPOINT_FLAG_PROVIDES_SESSION (1 << 0) -+ uint32_t flags; /**< additional flags */ -+#define PW_ENDPOINT_CHANGE_MASK_STREAMS (1 << 0) -+#define PW_ENDPOINT_CHANGE_MASK_SESSION (1 << 1) -+#define PW_ENDPOINT_CHANGE_MASK_PROPS (1 << 2) -+#define PW_ENDPOINT_CHANGE_MASK_PARAMS (1 << 3) -+#define PW_ENDPOINT_CHANGE_MASK_ALL ((1 << 4)-1) -+ uint32_t change_mask; /**< bitfield of changed fields since last call */ -+ uint32_t n_streams; /**< number of streams available */ -+ uint32_t session_id; /**< the id of the controlling session */ -+ struct spa_dict *props; /**< extra properties */ -+ struct spa_param_info *params; /**< parameters */ -+ uint32_t n_params; /**< number of items in \a params */ -+}; -+ -+struct pw_endpoint_stream_info { -+#define PW_VERSION_ENDPOINT_STREAM_INFO 0 -+ uint32_t version; /**< version of this structure */ -+ uint32_t id; /**< the stream id (local or global) */ -+ uint32_t endpoint_id; /**< the endpoint id (global) */ -+ char *name; /**< name of the stream */ -+#define PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS (1 << 0) -+#define PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS (1 << 1) -+#define PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS (1 << 2) -+#define PW_ENDPOINT_STREAM_CHANGE_MASK_ALL ((1 << 3)-1) -+ uint32_t change_mask; /**< bitfield of changed fields since last call */ -+ struct spa_pod *link_params; /**< information for linking this stream */ -+ struct spa_dict *props; /**< extra properties */ -+ struct spa_param_info *params; /**< parameters */ -+ uint32_t n_params; /**< number of items in \a params */ -+}; -+ -+struct pw_endpoint_link_info { -+#define PW_VERSION_ENDPOINT_LINK_INFO 0 -+ uint32_t version; /**< version of this structure */ -+ uint32_t id; /**< the link id (global) */ -+ uint32_t session_id; /**< the session id (global) */ -+ uint32_t output_endpoint_id; /**< the output endpoint id (global) */ -+ uint32_t output_stream_id; /**< the output stream id (local or global) */ -+ uint32_t input_endpoint_id; /**< the input endpoint id (global) */ -+ uint32_t input_stream_id; /**< the input stream id (local or global) */ -+#define PW_ENDPOINT_LINK_CHANGE_MASK_STATE (1 << 0) -+#define PW_ENDPOINT_LINK_CHANGE_MASK_PROPS (1 << 1) -+#define PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS (1 << 2) -+#define PW_ENDPOINT_LINK_CHANGE_MASK_ALL ((1 << 3)-1) -+ uint32_t change_mask; /**< bitfield of changed fields since last call */ -+ enum pw_endpoint_link_state state; /**< the state of the link */ -+ char *error; /**< error string if state == ERROR */ -+ struct spa_dict *props; /**< extra properties */ -+ struct spa_param_info *params; /**< parameters */ -+ uint32_t n_params; /**< number of items in \a params */ -+}; -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H */ -diff --git a/src/extensions/session-manager/keys.h b/src/extensions/session-manager/keys.h -new file mode 100644 -index 00000000..a7167510 ---- /dev/null -+++ b/src/extensions/session-manager/keys.h -@@ -0,0 +1,40 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H -+#define PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+#define PW_KEY_ENDPOINT_ID "endpoint.id" -+#define PW_KEY_SESSION_ID "session.id" -+ -+#ifdef __cplusplus -+} -+#endif -+ -+#endif /* PIPEWIRE_EXT_SESSION_MANAGER_KEYS_H */ -diff --git a/src/modules/meson.build b/src/modules/meson.build -index 9e1e94bd..da2684b6 100644 ---- a/src/modules/meson.build -+++ b/src/modules/meson.build -@@ -100,3 +100,20 @@ pipewire_module_adapter = shared_library('pipewire-module-adapter', - install_dir : modules_install_dir, - dependencies : [mathlib, dl_lib, rt_lib, pipewire_dep], - ) -+ -+pipewire_module_session_manager = shared_library('pipewire-module-session-manager', -+ [ 'module-session-manager.c', -+ 'module-session-manager/client-endpoint.c', -+ 'module-session-manager/client-session.c', -+ 'module-session-manager/endpoint-link.c', -+ 'module-session-manager/endpoint-stream.c', -+ 'module-session-manager/endpoint.c', -+ 'module-session-manager/session.c', -+ 'module-session-manager/protocol-native.c', -+ ], -+ c_args : pipewire_module_c_args, -+ include_directories : [configinc, spa_inc], -+ install : true, -+ install_dir : modules_install_dir, -+ dependencies : [mathlib, dl_lib, pipewire_dep], -+) -diff --git a/src/modules/module-session-manager.c b/src/modules/module-session-manager.c -new file mode 100644 -index 00000000..dbea3357 ---- /dev/null -+++ b/src/modules/module-session-manager.c -@@ -0,0 +1,56 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include "config.h" -+ -+#include -+ -+/* client-endpoint.c */ -+int client_endpoint_factory_init(struct pw_module *module); -+/* client-session.c */ -+int client_session_factory_init(struct pw_module *module); -+/* protocol-native.c */ -+struct pw_protocol *pw_protocol_native_ext_session_manager_init(struct pw_core *core); -+ -+static const struct spa_dict_item module_props[] = { -+ { PW_KEY_MODULE_AUTHOR, "George Kiagiadakis " }, -+ { PW_KEY_MODULE_DESCRIPTION, "Implements objects for session management" }, -+ { PW_KEY_MODULE_VERSION, PACKAGE_VERSION }, -+}; -+ -+SPA_EXPORT -+int pipewire__module_init(struct pw_module *module, const char *args) -+{ -+ struct pw_core *core = pw_module_get_core(module); -+ -+ client_endpoint_factory_init(module); -+ client_session_factory_init(module); -+ -+ pw_protocol_native_ext_session_manager_init(core); -+ -+ pw_module_update_properties(module, &SPA_DICT_INIT_ARRAY(module_props)); -+ -+ return 0; -+} -diff --git a/src/modules/module-session-manager/client-endpoint.c b/src/modules/module-session-manager/client-endpoint.c -new file mode 100644 -index 00000000..0e501c9f ---- /dev/null -+++ b/src/modules/module-session-manager/client-endpoint.c -@@ -0,0 +1,270 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "client-endpoint.h" -+#include "endpoint.h" -+#include "endpoint-stream.h" -+ -+#include -+ -+#define NAME "client-endpoint" -+ -+struct factory_data { -+ struct pw_factory *factory; -+ struct pw_module *module; -+ struct spa_hook module_listener; -+}; -+ -+static struct endpoint_stream *find_stream(struct client_endpoint *this, uint32_t id) -+{ -+ struct endpoint_stream *s; -+ spa_list_for_each(s, &this->streams, link) { -+ if (s->id == id) -+ return s; -+ } -+ return NULL; -+} -+ -+static int client_endpoint_update(void *object, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_info *info) -+{ -+ struct client_endpoint *this = object; -+ struct endpoint *endpoint = &this->endpoint; -+ -+ return endpoint_update(endpoint, change_mask, n_params, params, info); -+} -+ -+static int client_endpoint_stream_update(void *object, -+ uint32_t stream_id, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_stream_info *info) -+{ -+ struct client_endpoint *this = object; -+ struct endpoint *endpoint = &this->endpoint; -+ struct endpoint_stream *stream = find_stream(this, stream_id); -+ struct pw_properties *props = NULL; -+ -+ if (!stream) { -+ struct pw_core *core = pw_global_get_core(endpoint->global); -+ const char *keys[] = { -+ PW_KEY_FACTORY_ID, -+ PW_KEY_CLIENT_ID, -+ PW_KEY_ENDPOINT_ID, -+ NULL -+ }; -+ -+ stream = calloc(1, sizeof(struct endpoint_stream)); -+ if (!stream) -+ goto no_mem; -+ -+ props = pw_properties_new(NULL, NULL); -+ if (!props) -+ goto no_mem; -+ pw_properties_copy_keys (endpoint->props, props, keys); -+ -+ if (endpoint_stream_init(stream, stream_id, endpoint->info.id, -+ this, core, props) < 0) -+ goto no_mem; -+ -+ spa_list_append(&this->streams, &stream->link); -+ } -+ else if (change_mask & PW_CLIENT_ENDPOINT_STREAM_UPDATE_DESTROYED) { -+ endpoint_stream_clear(stream); -+ spa_list_remove(&stream->link); -+ free(stream); -+ stream = NULL; -+ } -+ -+ return stream ? -+ endpoint_stream_update(stream, change_mask, n_params, params, info) -+ : 0; -+ -+ no_mem: -+ if (props) -+ pw_properties_free(props); -+ free(stream); -+ pw_log_error(NAME" %p: cannot update stream: no memory", this); -+ pw_resource_error(this->resource, -ENOMEM, -+ NAME" %p: cannot update stream: no memory", this); -+ return -ENOMEM; -+} -+ -+static struct pw_client_endpoint_proxy_methods methods = { -+ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, -+ .update = client_endpoint_update, -+ .stream_update = client_endpoint_stream_update, -+}; -+ -+static void client_endpoint_destroy(void *data) -+{ -+ struct client_endpoint *this = data; -+ struct endpoint_stream *s; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ spa_list_consume(s, &this->streams, link) { -+ endpoint_stream_clear(s); -+ spa_list_remove(&s->link); -+ free(s); -+ } -+ endpoint_clear(&this->endpoint); -+ spa_hook_remove(&this->resource_listener); -+ -+ free(this); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = client_endpoint_destroy, -+}; -+ -+static void *create_object(void *data, -+ struct pw_resource *owner_resource, -+ uint32_t type, -+ uint32_t version, -+ struct pw_properties *properties, -+ uint32_t new_id) -+{ -+ struct factory_data *d = data; -+ struct pw_factory *factory = d->factory; -+ struct client_endpoint *this; -+ struct pw_client *owner = pw_resource_get_client(owner_resource); -+ struct pw_core *core = pw_client_get_core(owner); -+ -+ this = calloc(1, sizeof(struct client_endpoint)); -+ if (this == NULL) -+ goto no_mem; -+ -+ spa_list_init(&this->streams); -+ -+ pw_log_debug(NAME" %p: new", this); -+ -+ if (!properties) -+ properties = pw_properties_new(NULL, NULL); -+ if (!properties) -+ goto no_mem; -+ -+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); -+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); -+ -+ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); -+ if (this->resource == NULL) -+ goto no_mem; -+ -+ if (endpoint_init(&this->endpoint, this, core, properties) < 0) -+ goto no_mem; -+ -+ pw_resource_add_listener(this->resource, &this->resource_listener, -+ &resource_events, this); -+ pw_resource_add_object_listener(this->resource, &this->object_listener, -+ &methods, this); -+ -+ return this; -+ -+ no_mem: -+ if (properties) -+ pw_properties_free(properties); -+ if (this && this->resource) -+ pw_resource_destroy(this->resource); -+ free(this); -+ pw_log_error("can't create client endpoint: no memory"); -+ pw_resource_error(owner_resource, -ENOMEM, -+ "can't create client endpoint: no memory"); -+ return NULL; -+} -+ -+static const struct pw_factory_implementation impl_factory = { -+ PW_VERSION_FACTORY_IMPLEMENTATION, -+ .create_object = create_object, -+}; -+ -+static void module_destroy(void *data) -+{ -+ struct factory_data *d = data; -+ -+ spa_hook_remove(&d->module_listener); -+ pw_factory_destroy(d->factory); -+} -+ -+static void module_registered(void *data) -+{ -+ struct factory_data *d = data; -+ struct pw_module *module = d->module; -+ struct pw_factory *factory = d->factory; -+ struct spa_dict_item items[1]; -+ char id[16]; -+ int res; -+ -+ snprintf(id, sizeof(id), "%d", module->global->id); -+ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); -+ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); -+ -+ if ((res = pw_factory_register(factory, NULL)) < 0) { -+ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); -+ } -+} -+ -+static const struct pw_module_events module_events = { -+ PW_VERSION_MODULE_EVENTS, -+ .destroy = module_destroy, -+ .registered = module_registered, -+}; -+ -+int client_endpoint_factory_init(struct pw_module *module) -+{ -+ struct pw_core *core = pw_module_get_core(module); -+ struct pw_factory *factory; -+ struct factory_data *data; -+ -+ factory = pw_factory_new(core, -+ "client-endpoint", -+ PW_TYPE_INTERFACE_ClientEndpoint, -+ PW_VERSION_CLIENT_ENDPOINT_PROXY, -+ NULL, -+ sizeof(*data)); -+ if (factory == NULL) -+ return -ENOMEM; -+ -+ data = pw_factory_get_user_data(factory); -+ data->factory = factory; -+ data->module = module; -+ -+ pw_factory_set_implementation(factory, &impl_factory, data); -+ -+ pw_module_add_listener(module, &data->module_listener, &module_events, data); -+ -+ return 0; -+} -diff --git a/src/modules/module-session-manager/client-endpoint.h b/src/modules/module-session-manager/client-endpoint.h -new file mode 100644 -index 00000000..394e9fa8 ---- /dev/null -+++ b/src/modules/module-session-manager/client-endpoint.h -@@ -0,0 +1,60 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H -+#define MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H -+ -+#include "endpoint.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_endpoint { -+ struct pw_resource *resource; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ struct endpoint endpoint; -+ struct spa_list streams; -+}; -+ -+#define pw_client_endpoint_resource(r,m,v,...) \ -+ pw_resource_call_res(r,struct pw_client_endpoint_proxy_events,m,v,__VA_ARGS__) -+#define pw_client_endpoint_resource_set_id(r,...) \ -+ pw_client_endpoint_resource(r,set_id,0,__VA_ARGS__) -+#define pw_client_endpoint_resource_set_session_id(r,...) \ -+ pw_client_endpoint_resource(r,set_session_id,0,__VA_ARGS__) -+#define pw_client_endpoint_resource_set_param(r,...) \ -+ pw_client_endpoint_resource(r,set_param,0,__VA_ARGS__) -+#define pw_client_endpoint_resource_stream_set_param(r,...) \ -+ pw_client_endpoint_resource(r,stream_set_param,0,__VA_ARGS__) -+ -+int client_endpoint_factory_init(struct pw_module *module); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_CLIENT_ENDPOINT_H */ -diff --git a/src/modules/module-session-manager/client-session.c b/src/modules/module-session-manager/client-session.c -new file mode 100644 -index 00000000..9b20d833 ---- /dev/null -+++ b/src/modules/module-session-manager/client-session.c -@@ -0,0 +1,270 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "client-session.h" -+#include "session.h" -+#include "endpoint-link.h" -+ -+#include -+ -+#define NAME "client-session" -+ -+struct factory_data { -+ struct pw_factory *factory; -+ struct pw_module *module; -+ struct spa_hook module_listener; -+}; -+ -+static struct endpoint_link *find_link(struct client_session *this, uint32_t id) -+{ -+ struct endpoint_link *l; -+ spa_list_for_each(l, &this->links, link) { -+ if (l->id == id) -+ return l; -+ } -+ return NULL; -+} -+ -+static int client_session_update(void *object, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_session_info *info) -+{ -+ struct client_session *this = object; -+ struct session *session = &this->session; -+ -+ return session_update(session, change_mask, n_params, params, info); -+} -+ -+static int client_session_link_update(void *object, -+ uint32_t link_id, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_link_info *info) -+{ -+ struct client_session *this = object; -+ struct session *session = &this->session; -+ struct endpoint_link *link = find_link(this, link_id); -+ struct pw_properties *props = NULL; -+ -+ if (!link) { -+ struct pw_core *core = pw_global_get_core(session->global); -+ const char *keys[] = { -+ PW_KEY_FACTORY_ID, -+ PW_KEY_CLIENT_ID, -+ PW_KEY_SESSION_ID, -+ NULL -+ }; -+ -+ link = calloc(1, sizeof(struct endpoint_link)); -+ if (!link) -+ goto no_mem; -+ -+ props = pw_properties_new(NULL, NULL); -+ if (!props) -+ goto no_mem; -+ pw_properties_copy_keys (session->props, props, keys); -+ -+ if (endpoint_link_init(link, link_id, session->info.id, -+ this, core, props) < 0) -+ goto no_mem; -+ -+ spa_list_append(&this->links, &link->link); -+ } -+ else if (change_mask & PW_CLIENT_SESSION_LINK_UPDATE_DESTROYED) { -+ endpoint_link_clear(link); -+ spa_list_remove(&link->link); -+ free(link); -+ link = NULL; -+ } -+ -+ return link ? -+ endpoint_link_update(link, change_mask, n_params, params, info) -+ : 0; -+ -+ no_mem: -+ if (props) -+ pw_properties_free(props); -+ free(link); -+ pw_log_error(NAME" %p: cannot update link: no memory", this); -+ pw_resource_error(this->resource, -ENOMEM, -+ NAME" %p: cannot update link: no memory", this); -+ return -ENOMEM; -+} -+ -+static struct pw_client_session_proxy_methods methods = { -+ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, -+ .update = client_session_update, -+ .link_update = client_session_link_update, -+}; -+ -+static void client_session_destroy(void *data) -+{ -+ struct client_session *this = data; -+ struct endpoint_link *l; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ spa_list_consume(l, &this->links, link) { -+ endpoint_link_clear(l); -+ spa_list_remove(&l->link); -+ free(l); -+ } -+ session_clear(&this->session); -+ spa_hook_remove(&this->resource_listener); -+ -+ free(this); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = client_session_destroy, -+}; -+ -+static void *create_object(void *data, -+ struct pw_resource *owner_resource, -+ uint32_t type, -+ uint32_t version, -+ struct pw_properties *properties, -+ uint32_t new_id) -+{ -+ struct factory_data *d = data; -+ struct pw_factory *factory = d->factory; -+ struct client_session *this; -+ struct pw_client *owner = pw_resource_get_client(owner_resource); -+ struct pw_core *core = pw_client_get_core(owner); -+ -+ this = calloc(1, sizeof(struct client_session)); -+ if (this == NULL) -+ goto no_mem; -+ -+ spa_list_init(&this->links); -+ -+ pw_log_debug(NAME" %p: new", this); -+ -+ if (!properties) -+ properties = pw_properties_new(NULL, NULL); -+ if (!properties) -+ goto no_mem; -+ -+ pw_properties_setf(properties, PW_KEY_CLIENT_ID, "%d", owner->global->id); -+ pw_properties_setf(properties, PW_KEY_FACTORY_ID, "%d", factory->global->id); -+ -+ this->resource = pw_resource_new(owner, new_id, PW_PERM_RWX, type, version, 0); -+ if (this->resource == NULL) -+ goto no_mem; -+ -+ if (session_init(&this->session, this, core, properties) < 0) -+ goto no_mem; -+ -+ pw_resource_add_listener(this->resource, &this->resource_listener, -+ &resource_events, this); -+ pw_resource_add_object_listener(this->resource, &this->object_listener, -+ &methods, this); -+ -+ return this; -+ -+ no_mem: -+ if (properties) -+ pw_properties_free(properties); -+ if (this && this->resource) -+ pw_resource_destroy(this->resource); -+ free(this); -+ pw_log_error("can't create client session: no memory"); -+ pw_resource_error(owner_resource, -ENOMEM, -+ "can't create client session: no memory"); -+ return NULL; -+} -+ -+static const struct pw_factory_implementation impl_factory = { -+ PW_VERSION_FACTORY_IMPLEMENTATION, -+ .create_object = create_object, -+}; -+ -+static void module_destroy(void *data) -+{ -+ struct factory_data *d = data; -+ -+ spa_hook_remove(&d->module_listener); -+ pw_factory_destroy(d->factory); -+} -+ -+static void module_registered(void *data) -+{ -+ struct factory_data *d = data; -+ struct pw_module *module = d->module; -+ struct pw_factory *factory = d->factory; -+ struct spa_dict_item items[1]; -+ char id[16]; -+ int res; -+ -+ snprintf(id, sizeof(id), "%d", module->global->id); -+ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_MODULE_ID, id); -+ pw_factory_update_properties(factory, &SPA_DICT_INIT(items, 1)); -+ -+ if ((res = pw_factory_register(factory, NULL)) < 0) { -+ pw_log_error(NAME" %p: can't register factory: %s", factory, spa_strerror(res)); -+ } -+} -+ -+static const struct pw_module_events module_events = { -+ PW_VERSION_MODULE_EVENTS, -+ .destroy = module_destroy, -+ .registered = module_registered, -+}; -+ -+int client_session_factory_init(struct pw_module *module) -+{ -+ struct pw_core *core = pw_module_get_core(module); -+ struct pw_factory *factory; -+ struct factory_data *data; -+ -+ factory = pw_factory_new(core, -+ "client-session", -+ PW_TYPE_INTERFACE_ClientSession, -+ PW_VERSION_CLIENT_SESSION_PROXY, -+ NULL, -+ sizeof(*data)); -+ if (factory == NULL) -+ return -ENOMEM; -+ -+ data = pw_factory_get_user_data(factory); -+ data->factory = factory; -+ data->module = module; -+ -+ pw_factory_set_implementation(factory, &impl_factory, data); -+ -+ pw_module_add_listener(module, &data->module_listener, &module_events, data); -+ -+ return 0; -+} -diff --git a/src/modules/module-session-manager/client-session.h b/src/modules/module-session-manager/client-session.h -new file mode 100644 -index 00000000..c764564d ---- /dev/null -+++ b/src/modules/module-session-manager/client-session.h -@@ -0,0 +1,62 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_CLIENT_SESSION_H -+#define MODULE_SESSION_MANAGER_CLIENT_SESSION_H -+ -+#include "session.h" -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_session { -+ struct pw_resource *resource; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ struct session session; -+ struct spa_list links; -+}; -+ -+#define pw_client_session_resource(r,m,v,...) \ -+ pw_resource_call_res(r,struct pw_client_session_proxy_events,m,v,__VA_ARGS__) -+#define pw_client_session_resource_set_id(r,...) \ -+ pw_client_session_resource(r,set_id,0,__VA_ARGS__) -+#define pw_client_session_resource_set_param(r,...) \ -+ pw_client_session_resource(r,set_param,0,__VA_ARGS__) -+#define pw_client_session_resource_link_set_param(r,...) \ -+ pw_client_session_resource(r,link_set_param,0,__VA_ARGS__) -+#define pw_client_session_resource_create_link(r,...) \ -+ pw_client_session_resource(r,create_link,0,__VA_ARGS__) -+#define pw_client_session_resource_destroy_link(r,...) \ -+ pw_client_session_resource(r,destroy_link,0,__VA_ARGS__) -+#define pw_client_session_resource_link_request_state(r,...) \ -+ pw_client_session_resource(r,link_request_state,0,__VA_ARGS__) -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_CLIENT_SESSION_H */ -diff --git a/src/modules/module-session-manager/endpoint-link.c b/src/modules/module-session-manager/endpoint-link.c -new file mode 100644 -index 00000000..bce06598 ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint-link.c -@@ -0,0 +1,359 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "endpoint-link.h" -+#include "client-session.h" -+ -+#include -+ -+#define NAME "endpoint-link" -+ -+struct resource_data { -+ struct endpoint_link *link; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ uint32_t n_subscribe_ids; -+ uint32_t subscribe_ids[32]; -+}; -+ -+#define pw_endpoint_link_resource(r,m,v,...) \ -+ pw_resource_call(r,struct pw_endpoint_link_proxy_events,m,v,__VA_ARGS__) -+#define pw_endpoint_link_resource_info(r,...) \ -+ pw_endpoint_link_resource(r,info,0,__VA_ARGS__) -+#define pw_endpoint_link_resource_param(r,...) \ -+ pw_endpoint_link_resource(r,param,0,__VA_ARGS__) -+ -+static int endpoint_link_enum_params (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_link *this = data->link; -+ struct spa_pod *result; -+ struct spa_pod *param; -+ uint8_t buffer[1024]; -+ struct spa_pod_builder b = { 0 }; -+ uint32_t index; -+ uint32_t next = start; -+ uint32_t count = 0; -+ -+ while (true) { -+ index = next++; -+ if (index >= this->n_params) -+ break; -+ -+ param = this->params[index]; -+ -+ if (param == NULL || !spa_pod_is_object_id(param, id)) -+ continue; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ if (spa_pod_filter(&b, &result, param, filter) != 0) -+ continue; -+ -+ pw_log_debug(NAME" %p: %d param %u", this, seq, index); -+ -+ pw_endpoint_link_resource_param(resource, seq, id, index, next, result); -+ -+ if (++count == num) -+ break; -+ } -+ return 0; -+} -+ -+static int endpoint_link_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ uint32_t i; -+ -+ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); -+ data->n_subscribe_ids = n_ids; -+ -+ for (i = 0; i < n_ids; i++) { -+ data->subscribe_ids[i] = ids[i]; -+ pw_log_debug(NAME" %p: resource %d subscribe param %u", -+ data->link, resource->id, ids[i]); -+ endpoint_link_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); -+ } -+ return 0; -+} -+ -+static int endpoint_link_set_param (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_link *this = data->link; -+ -+ pw_client_session_resource_set_param(this->client_sess->resource, -+ id, flags, param); -+ -+ return 0; -+} -+ -+static int endpoint_link_request_state(void *object, enum pw_endpoint_link_state state) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_link *this = data->link; -+ -+ pw_client_session_resource_link_request_state(this->client_sess->resource, -+ this->id, state); -+ -+ return 0; -+} -+ -+static int endpoint_link_destroy(void *object) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_link *this = data->link; -+ -+ pw_client_session_resource_destroy_link(this->client_sess->resource, -+ this->id); -+ -+ return 0; -+} -+ -+static const struct pw_endpoint_link_proxy_methods methods = { -+ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, -+ .subscribe_params = endpoint_link_subscribe_params, -+ .enum_params = endpoint_link_enum_params, -+ .set_param = endpoint_link_set_param, -+ .request_state = endpoint_link_request_state, -+ .destroy = endpoint_link_destroy, -+}; -+ -+static void endpoint_link_notify_subscribed(struct endpoint_link *this, -+ uint32_t index, uint32_t next) -+{ -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ struct spa_pod *param = this->params[index]; -+ uint32_t id; -+ uint32_t i; -+ -+ if (!param || !spa_pod_is_object (param)) -+ return; -+ -+ id = SPA_POD_OBJECT_ID (param); -+ -+ spa_list_for_each(resource, &global->resource_list, link) { -+ data = pw_resource_get_user_data(resource); -+ for (i = 0; i < data->n_subscribe_ids; i++) { -+ if (data->subscribe_ids[i] == id) { -+ pw_endpoint_link_resource_param(resource, 1, -+ id, index, next, param); -+ } -+ } -+ } -+} -+ -+int endpoint_link_update(struct endpoint_link *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_link_info *info) -+{ -+ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { -+ uint32_t i; -+ size_t size = n_params * sizeof(struct spa_pod *); -+ -+ pw_log_debug(NAME" %p: update %d params", this, n_params); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ this->params = realloc(this->params, size); -+ if (size > 0 && !this->params) { -+ this->n_params = 0; -+ goto no_mem; -+ } -+ this->n_params = n_params; -+ -+ for (i = 0; i < this->n_params; i++) { -+ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; -+ endpoint_link_notify_subscribed(this, i, i+1); -+ } -+ } -+ -+ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { -+ struct pw_resource *resource; -+ -+ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_STATE) { -+ this->info.state = info->state; -+ free(this->info.error); -+ this->info.error = info->error ? strdup(info->error) : NULL; -+ } -+ -+ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PROPS) -+ pw_properties_update(this->props, info->props); -+ -+ if (info->change_mask & PW_ENDPOINT_LINK_CHANGE_MASK_PARAMS) { -+ size_t size = info->n_params * sizeof(struct spa_param_info); -+ -+ this->info.params = realloc(this->info.params, size); -+ if (size > 0 && !this->info.params) { -+ this->info.n_params = 0; -+ goto no_mem; -+ } -+ this->info.n_params = info->n_params; -+ -+ memcpy(this->info.params, info->params, size); -+ } -+ -+ if (!this->info.output_endpoint_id) { -+ this->info.output_endpoint_id = info->output_endpoint_id; -+ this->info.output_stream_id = info->output_stream_id; -+ this->info.input_endpoint_id = info->input_endpoint_id; -+ this->info.input_stream_id = info->input_stream_id; -+ } -+ -+ this->info.change_mask = info->change_mask; -+ spa_list_for_each(resource, &this->global->resource_list, link) { -+ pw_endpoint_link_resource_info(resource, &this->info); -+ } -+ this->info.change_mask = 0; -+ } -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" %p: can't update: no memory", this); -+ pw_resource_error(this->client_sess->resource, -ENOMEM, -+ NAME" %p: can't update: no memory", this); -+ return -ENOMEM; -+} -+ -+static void endpoint_link_unbind(void *data) -+{ -+ struct pw_resource *resource = data; -+ spa_list_remove(&resource->link); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = endpoint_link_unbind, -+}; -+ -+static int endpoint_link_bind(void *_data, struct pw_client *client, -+ uint32_t permissions, uint32_t version, uint32_t id) -+{ -+ struct endpoint_link *this = _data; -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ -+ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); -+ if (resource == NULL) -+ goto no_mem; -+ -+ data = pw_resource_get_user_data(resource); -+ data->link = this; -+ pw_resource_add_listener(resource, &data->resource_listener, -+ &resource_events, resource); -+ pw_resource_add_object_listener(resource, &data->object_listener, -+ &methods, resource); -+ -+ pw_log_debug(NAME" %p: bound to %d", this, resource->id); -+ -+ spa_list_append(&global->resource_list, &resource->link); -+ -+ this->info.change_mask = PW_ENDPOINT_LINK_CHANGE_MASK_ALL; -+ pw_endpoint_link_resource_info(resource, &this->info); -+ this->info.change_mask = 0; -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" %p: can't create resource: no memory", this); -+ pw_resource_error(this->client_sess->resource, -ENOMEM, -+ NAME" %p: can't create resource: no memory", this); -+ return -ENOMEM; -+} -+ -+int endpoint_link_init(struct endpoint_link *this, -+ uint32_t id, uint32_t session_id, -+ struct client_session *client_sess, -+ struct pw_core *core, -+ struct pw_properties *properties) -+{ -+ pw_log_debug(NAME" %p: new", this); -+ -+ this->client_sess = client_sess; -+ this->id = id; -+ this->props = properties; -+ -+ properties = pw_properties_copy(properties); -+ if (!properties) -+ goto no_mem; -+ -+ this->global = pw_global_new (core, -+ PW_TYPE_INTERFACE_EndpointLink, -+ PW_VERSION_ENDPOINT_LINK_PROXY, -+ properties, endpoint_link_bind, this); -+ if (!this->global) -+ goto no_mem; -+ -+ this->info.version = PW_VERSION_ENDPOINT_LINK_INFO; -+ this->info.id = this->global->id; -+ this->info.session_id = session_id; -+ this->info.props = &this->props->dict; -+ -+ return pw_global_register(this->global); -+ -+ no_mem: -+ pw_log_error(NAME" - can't create - out of memory"); -+ return -ENOMEM; -+} -+ -+void endpoint_link_clear(struct endpoint_link *this) -+{ -+ uint32_t i; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ pw_global_destroy(this->global); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ free(this->params); -+ -+ free(this->info.error); -+ free(this->info.params); -+ -+ if (this->props) -+ pw_properties_free(this->props); -+} -diff --git a/src/modules/module-session-manager/endpoint-link.h b/src/modules/module-session-manager/endpoint-link.h -new file mode 100644 -index 00000000..a9c18d32 ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint-link.h -@@ -0,0 +1,64 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_ENDPOINT_LINK_H -+#define MODULE_SESSION_MANAGER_ENDPOINT_LINK_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_session; -+ -+struct endpoint_link { -+ struct spa_list link; -+ struct client_session *client_sess; -+ struct pw_global *global; -+ uint32_t id; /* session-local link id */ -+ uint32_t n_params; -+ struct spa_pod **params; -+ struct pw_endpoint_link_info info; -+ struct pw_properties *props; /* wrapper of info.props */ -+}; -+ -+int endpoint_link_init(struct endpoint_link *this, -+ uint32_t id, uint32_t session_id, -+ struct client_session *client_sess, -+ struct pw_core *core, -+ struct pw_properties *properties); -+ -+void endpoint_link_clear(struct endpoint_link *this); -+ -+int endpoint_link_update(struct endpoint_link *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_link_info *info); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_ENDPOINT_LINK_H */ -diff --git a/src/modules/module-session-manager/endpoint-stream.c b/src/modules/module-session-manager/endpoint-stream.c -new file mode 100644 -index 00000000..47d2a4ea ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint-stream.c -@@ -0,0 +1,329 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "endpoint-stream.h" -+#include "client-endpoint.h" -+ -+#include -+ -+#define NAME "endpoint-stream" -+ -+struct resource_data { -+ struct endpoint_stream *stream; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ uint32_t n_subscribe_ids; -+ uint32_t subscribe_ids[32]; -+}; -+ -+#define pw_endpoint_stream_resource(r,m,v,...) \ -+ pw_resource_call(r,struct pw_endpoint_stream_proxy_events,m,v,__VA_ARGS__) -+#define pw_endpoint_stream_resource_info(r,...) \ -+ pw_endpoint_stream_resource(r,info,0,__VA_ARGS__) -+#define pw_endpoint_stream_resource_param(r,...) \ -+ pw_endpoint_stream_resource(r,param,0,__VA_ARGS__) -+ -+static int endpoint_stream_enum_params (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_stream *this = data->stream; -+ struct spa_pod *result; -+ struct spa_pod *param; -+ uint8_t buffer[1024]; -+ struct spa_pod_builder b = { 0 }; -+ uint32_t index; -+ uint32_t next = start; -+ uint32_t count = 0; -+ -+ while (true) { -+ index = next++; -+ if (index >= this->n_params) -+ break; -+ -+ param = this->params[index]; -+ -+ if (param == NULL || !spa_pod_is_object_id(param, id)) -+ continue; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ if (spa_pod_filter(&b, &result, param, filter) != 0) -+ continue; -+ -+ pw_log_debug(NAME" %p: %d param %u", this, seq, index); -+ -+ pw_endpoint_stream_resource_param(resource, seq, id, index, next, result); -+ -+ if (++count == num) -+ break; -+ } -+ return 0; -+} -+ -+static int endpoint_stream_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ uint32_t i; -+ -+ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); -+ data->n_subscribe_ids = n_ids; -+ -+ for (i = 0; i < n_ids; i++) { -+ data->subscribe_ids[i] = ids[i]; -+ pw_log_debug(NAME" %p: resource %d subscribe param %u", -+ data->stream, resource->id, ids[i]); -+ endpoint_stream_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); -+ } -+ return 0; -+} -+ -+static int endpoint_stream_set_param (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint_stream *this = data->stream; -+ -+ pw_client_endpoint_resource_set_param(this->client_ep->resource, -+ id, flags, param); -+ -+ return 0; -+} -+ -+static const struct pw_endpoint_stream_proxy_methods methods = { -+ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, -+ .subscribe_params = endpoint_stream_subscribe_params, -+ .enum_params = endpoint_stream_enum_params, -+ .set_param = endpoint_stream_set_param, -+}; -+ -+static void endpoint_stream_notify_subscribed(struct endpoint_stream *this, -+ uint32_t index, uint32_t next) -+{ -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ struct spa_pod *param = this->params[index]; -+ uint32_t id; -+ uint32_t i; -+ -+ if (!param || !spa_pod_is_object (param)) -+ return; -+ -+ id = SPA_POD_OBJECT_ID (param); -+ -+ spa_list_for_each(resource, &global->resource_list, link) { -+ data = pw_resource_get_user_data(resource); -+ for (i = 0; i < data->n_subscribe_ids; i++) { -+ if (data->subscribe_ids[i] == id) { -+ pw_endpoint_stream_resource_param(resource, 1, -+ id, index, next, param); -+ } -+ } -+ } -+} -+ -+int endpoint_stream_update(struct endpoint_stream *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_stream_info *info) -+{ -+ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { -+ uint32_t i; -+ size_t size = n_params * sizeof(struct spa_pod *); -+ -+ pw_log_debug(NAME" %p: update %d params", this, n_params); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ this->params = realloc(this->params, size); -+ if (size > 0 && !this->params) { -+ this->n_params = 0; -+ goto no_mem; -+ } -+ this->n_params = n_params; -+ -+ for (i = 0; i < this->n_params; i++) { -+ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; -+ endpoint_stream_notify_subscribed(this, i, i+1); -+ } -+ } -+ -+ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { -+ struct pw_resource *resource; -+ -+ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_LINK_PARAMS) { -+ free(this->info.link_params); -+ this->info.link_params = spa_pod_copy(info->link_params); -+ } -+ -+ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PROPS) -+ pw_properties_update(this->props, info->props); -+ -+ if (info->change_mask & PW_ENDPOINT_STREAM_CHANGE_MASK_PARAMS) { -+ size_t size = info->n_params * sizeof(struct spa_param_info); -+ -+ this->info.params = realloc(this->info.params, size); -+ if (size > 0 && !this->info.params) { -+ this->info.n_params = 0; -+ goto no_mem; -+ } -+ this->info.n_params = info->n_params; -+ -+ memcpy(this->info.params, info->params, size); -+ } -+ -+ if (!this->info.name) -+ this->info.name = strdup(info->name); -+ -+ this->info.change_mask = info->change_mask; -+ spa_list_for_each(resource, &this->global->resource_list, link) { -+ pw_endpoint_stream_resource_info(resource, &this->info); -+ } -+ this->info.change_mask = 0; -+ } -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't update: no memory"); -+ pw_resource_error(this->client_ep->resource, -ENOMEM, -+ NAME" can't update: no memory"); -+ return -ENOMEM; -+} -+ -+static void endpoint_stream_unbind(void *data) -+{ -+ struct pw_resource *resource = data; -+ spa_list_remove(&resource->link); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = endpoint_stream_unbind, -+}; -+ -+static int endpoint_stream_bind(void *_data, struct pw_client *client, -+ uint32_t permissions, uint32_t version, uint32_t id) -+{ -+ struct endpoint_stream *this = _data; -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ -+ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); -+ if (resource == NULL) -+ goto no_mem; -+ -+ data = pw_resource_get_user_data(resource); -+ data->stream = this; -+ pw_resource_add_listener(resource, &data->resource_listener, -+ &resource_events, resource); -+ pw_resource_add_object_listener(resource, &data->object_listener, -+ &methods, resource); -+ -+ pw_log_debug(NAME" %p: bound to %d", this, resource->id); -+ -+ spa_list_append(&global->resource_list, &resource->link); -+ -+ this->info.change_mask = PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; -+ pw_endpoint_stream_resource_info(resource, &this->info); -+ this->info.change_mask = 0; -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't create resource: no memory"); -+ pw_resource_error(this->client_ep->resource, -ENOMEM, -+ NAME" can't create resource: no memory"); -+ return -ENOMEM; -+} -+ -+int endpoint_stream_init(struct endpoint_stream *this, -+ uint32_t id, uint32_t endpoint_id, -+ struct client_endpoint *client_ep, -+ struct pw_core *core, -+ struct pw_properties *properties) -+{ -+ pw_log_debug(NAME" %p: new", this); -+ -+ this->client_ep = client_ep; -+ this->id = id; -+ this->props = properties; -+ -+ properties = pw_properties_copy(properties); -+ if (!properties) -+ goto no_mem; -+ -+ this->global = pw_global_new (core, -+ PW_TYPE_INTERFACE_EndpointStream, -+ PW_VERSION_ENDPOINT_STREAM_PROXY, -+ properties, endpoint_stream_bind, this); -+ if (!this->global) -+ goto no_mem; -+ -+ this->info.version = PW_VERSION_ENDPOINT_STREAM_INFO; -+ this->info.id = this->global->id; -+ this->info.endpoint_id = endpoint_id; -+ this->info.props = &this->props->dict; -+ -+ return pw_global_register(this->global); -+ -+ no_mem: -+ pw_log_error(NAME" - can't create - out of memory"); -+ return -ENOMEM; -+} -+ -+void endpoint_stream_clear(struct endpoint_stream *this) -+{ -+ uint32_t i; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ pw_global_destroy(this->global); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ free(this->params); -+ -+ free(this->info.name); -+ free(this->info.link_params); -+ free(this->info.params); -+ -+ if (this->props) -+ pw_properties_free(this->props); -+} -diff --git a/src/modules/module-session-manager/endpoint-stream.h b/src/modules/module-session-manager/endpoint-stream.h -new file mode 100644 -index 00000000..99bd4836 ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint-stream.h -@@ -0,0 +1,64 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H -+#define MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_endpoint; -+ -+struct endpoint_stream { -+ struct spa_list link; -+ struct client_endpoint *client_ep; -+ struct pw_global *global; -+ uint32_t id; /* endpoint-local stream id */ -+ uint32_t n_params; -+ struct spa_pod **params; -+ struct pw_endpoint_stream_info info; -+ struct pw_properties *props; /* wrapper of info.props */ -+}; -+ -+int endpoint_stream_init(struct endpoint_stream *this, -+ uint32_t id, uint32_t endpoint_id, -+ struct client_endpoint *client_ep, -+ struct pw_core *core, -+ struct pw_properties *properties); -+ -+void endpoint_stream_clear(struct endpoint_stream *this); -+ -+int endpoint_stream_update(struct endpoint_stream *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_stream_info *info); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_ENDPOINT_STREAM_H */ -diff --git a/src/modules/module-session-manager/endpoint.c b/src/modules/module-session-manager/endpoint.c -new file mode 100644 -index 00000000..0866e71d ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint.c -@@ -0,0 +1,343 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "endpoint.h" -+#include "client-endpoint.h" -+ -+#include -+ -+#define NAME "endpoint" -+ -+struct resource_data { -+ struct endpoint *endpoint; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ uint32_t n_subscribe_ids; -+ uint32_t subscribe_ids[32]; -+}; -+ -+#define pw_endpoint_resource(r,m,v,...) \ -+ pw_resource_call(r,struct pw_endpoint_proxy_events,m,v,__VA_ARGS__) -+#define pw_endpoint_resource_info(r,...) \ -+ pw_endpoint_resource(r,info,0,__VA_ARGS__) -+#define pw_endpoint_resource_param(r,...) \ -+ pw_endpoint_resource(r,param,0,__VA_ARGS__) -+ -+static int endpoint_enum_params (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint *this = data->endpoint; -+ struct spa_pod *result; -+ struct spa_pod *param; -+ uint8_t buffer[1024]; -+ struct spa_pod_builder b = { 0 }; -+ uint32_t index; -+ uint32_t next = start; -+ uint32_t count = 0; -+ -+ while (true) { -+ index = next++; -+ if (index >= this->n_params) -+ break; -+ -+ param = this->params[index]; -+ -+ if (param == NULL || !spa_pod_is_object_id(param, id)) -+ continue; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ if (spa_pod_filter(&b, &result, param, filter) != 0) -+ continue; -+ -+ pw_log_debug(NAME" %p: %d param %u", this, seq, index); -+ -+ pw_endpoint_resource_param(resource, seq, id, index, next, result); -+ -+ if (++count == num) -+ break; -+ } -+ return 0; -+} -+ -+static int endpoint_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ uint32_t i; -+ -+ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); -+ data->n_subscribe_ids = n_ids; -+ -+ for (i = 0; i < n_ids; i++) { -+ data->subscribe_ids[i] = ids[i]; -+ pw_log_debug(NAME" %p: resource %d subscribe param %u", -+ data->endpoint, resource->id, ids[i]); -+ endpoint_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); -+ } -+ return 0; -+} -+ -+static int endpoint_set_param (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct endpoint *this = data->endpoint; -+ -+ pw_client_endpoint_resource_set_param(this->client_ep->resource, -+ id, flags, param); -+ -+ return 0; -+} -+ -+static const struct pw_endpoint_proxy_methods methods = { -+ PW_VERSION_ENDPOINT_PROXY_METHODS, -+ .subscribe_params = endpoint_subscribe_params, -+ .enum_params = endpoint_enum_params, -+ .set_param = endpoint_set_param, -+}; -+ -+static void endpoint_notify_subscribed(struct endpoint *this, -+ uint32_t index, uint32_t next) -+{ -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ struct spa_pod *param = this->params[index]; -+ uint32_t id; -+ uint32_t i; -+ -+ if (!param || !spa_pod_is_object (param)) -+ return; -+ -+ id = SPA_POD_OBJECT_ID (param); -+ -+ spa_list_for_each(resource, &global->resource_list, link) { -+ data = pw_resource_get_user_data(resource); -+ for (i = 0; i < data->n_subscribe_ids; i++) { -+ if (data->subscribe_ids[i] == id) { -+ pw_endpoint_resource_param(resource, 1, id, -+ index, next, param); -+ } -+ } -+ } -+} -+ -+int endpoint_update(struct endpoint *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_info *info) -+{ -+ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_PARAMS) { -+ uint32_t i; -+ size_t size = n_params * sizeof(struct spa_pod *); -+ -+ pw_log_debug(NAME" %p: update %d params", this, n_params); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ this->params = realloc(this->params, size); -+ if (size > 0 && !this->params) { -+ this->n_params = 0; -+ goto no_mem; -+ } -+ this->n_params = n_params; -+ -+ for (i = 0; i < this->n_params; i++) { -+ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; -+ endpoint_notify_subscribed(this, i, i+1); -+ } -+ } -+ -+ if (change_mask & PW_CLIENT_ENDPOINT_UPDATE_INFO) { -+ struct pw_resource *resource; -+ -+ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) -+ this->info.n_streams = info->n_streams; -+ -+ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) -+ this->info.session_id = info->session_id; -+ -+ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) -+ pw_properties_update(this->props, info->props); -+ -+ if (info->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { -+ size_t size = info->n_params * sizeof(struct spa_param_info); -+ -+ this->info.params = realloc(this->info.params, size); -+ if (size > 0 && !this->info.params) { -+ this->info.n_params = 0; -+ goto no_mem; -+ } -+ this->info.n_params = info->n_params; -+ -+ memcpy(this->info.params, info->params, size); -+ } -+ -+ if (!this->info.name) { -+ this->info.name = strdup(info->name); -+ this->info.media_class = strdup(info->media_class); -+ this->info.direction = info->direction; -+ this->info.flags = info->flags; -+ } -+ -+ this->info.change_mask = info->change_mask; -+ spa_list_for_each(resource, &this->global->resource_list, link) { -+ pw_endpoint_resource_info(resource, &this->info); -+ } -+ this->info.change_mask = 0; -+ } -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't update: no memory"); -+ pw_resource_error(this->client_ep->resource, -ENOMEM, -+ NAME" can't update: no memory"); -+ return -ENOMEM; -+} -+ -+static void endpoint_unbind(void *data) -+{ -+ struct pw_resource *resource = data; -+ spa_list_remove(&resource->link); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = endpoint_unbind, -+}; -+ -+static int endpoint_bind(void *_data, struct pw_client *client, -+ uint32_t permissions, uint32_t version, uint32_t id) -+{ -+ struct endpoint *this = _data; -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ -+ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); -+ if (resource == NULL) -+ goto no_mem; -+ -+ data = pw_resource_get_user_data(resource); -+ data->endpoint = this; -+ pw_resource_add_listener(resource, &data->resource_listener, -+ &resource_events, resource); -+ pw_resource_add_object_listener(resource, &data->object_listener, -+ &methods, resource); -+ -+ pw_log_debug(NAME" %p: bound to %d", this, resource->id); -+ -+ spa_list_append(&global->resource_list, &resource->link); -+ -+ this->info.change_mask = PW_ENDPOINT_CHANGE_MASK_ALL; -+ pw_endpoint_resource_info(resource, &this->info); -+ this->info.change_mask = 0; -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't create resource: no memory"); -+ pw_resource_error(this->client_ep->resource, -ENOMEM, -+ NAME" can't create resource: no memory"); -+ return -ENOMEM; -+} -+ -+int endpoint_init(struct endpoint *this, -+ struct client_endpoint *client_ep, -+ struct pw_core *core, -+ struct pw_properties *properties) -+{ -+ const char *keys[] = { -+ PW_KEY_FACTORY_ID, -+ PW_KEY_CLIENT_ID, -+ NULL -+ }; -+ -+ pw_log_debug(NAME" %p: new", this); -+ -+ this->client_ep = client_ep; -+ this->props = properties; -+ -+ properties = pw_properties_new(NULL, NULL); -+ if (!properties) -+ goto no_mem; -+ -+ pw_properties_copy_keys(this->props, properties, keys); -+ -+ this->global = pw_global_new (core, -+ PW_TYPE_INTERFACE_Endpoint, -+ PW_VERSION_ENDPOINT_PROXY, -+ properties, endpoint_bind, this); -+ if (!this->global) -+ goto no_mem; -+ -+ pw_properties_setf(this->props, PW_KEY_ENDPOINT_ID, "%u", this->global->id); -+ -+ this->info.version = PW_VERSION_ENDPOINT_INFO; -+ this->info.id = this->global->id; -+ this->info.props = &this->props->dict; -+ -+ pw_client_endpoint_resource_set_id(client_ep->resource, this->global->id); -+ -+ return pw_global_register(this->global); -+ -+ no_mem: -+ pw_log_error(NAME" - can't create - out of memory"); -+ return -ENOMEM; -+} -+ -+void endpoint_clear(struct endpoint *this) -+{ -+ uint32_t i; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ pw_global_destroy(this->global); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ free(this->params); -+ -+ free(this->info.name); -+ free(this->info.media_class); -+ free(this->info.params); -+ -+ if (this->props) -+ pw_properties_free(this->props); -+} -diff --git a/src/modules/module-session-manager/endpoint.h b/src/modules/module-session-manager/endpoint.h -new file mode 100644 -index 00000000..89d26028 ---- /dev/null -+++ b/src/modules/module-session-manager/endpoint.h -@@ -0,0 +1,61 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_ENDPOINT_H -+#define MODULE_SESSION_MANAGER_ENDPOINT_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_endpoint; -+ -+struct endpoint { -+ struct client_endpoint *client_ep; -+ struct pw_global *global; -+ uint32_t n_params; -+ struct spa_pod **params; -+ struct pw_endpoint_info info; -+ struct pw_properties *props; /* wrapper of info.props */ -+}; -+ -+int endpoint_init(struct endpoint *this, -+ struct client_endpoint *client_ep, -+ struct pw_core *core, -+ struct pw_properties *properties); -+ -+void endpoint_clear(struct endpoint *this); -+ -+int endpoint_update(struct endpoint *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_info *info); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_ENDPOINT_H */ -diff --git a/src/modules/module-session-manager/protocol-native.c b/src/modules/module-session-manager/protocol-native.c -new file mode 100644 -index 00000000..2c791ffc ---- /dev/null -+++ b/src/modules/module-session-manager/protocol-native.c -@@ -0,0 +1,2125 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+static void push_dict(struct spa_pod_builder *b, const struct spa_dict *dict) -+{ -+ struct spa_pod_frame f; -+ uint32_t n_items; -+ uint32_t i; -+ -+ n_items = dict ? dict->n_items : 0; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, SPA_POD_Int(n_items), NULL); -+ for (i = 0; i < n_items; i++) { -+ spa_pod_builder_add(b, -+ SPA_POD_String(dict->items[i].key), -+ SPA_POD_String(dict->items[i].value), -+ NULL); -+ } -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define parse_dict(p, f, dict) \ -+do { \ -+ uint32_t i; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, SPA_POD_Int(&(dict)->n_items), NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ if ((dict)->n_items > 0) { \ -+ (dict)->items = alloca((dict)->n_items * sizeof(struct spa_dict_item)); \ -+ for (i = 0; i < (dict)->n_items; i++) { \ -+ if (spa_pod_parser_get(p, \ -+ SPA_POD_String(&(dict)->items[i].key), \ -+ SPA_POD_String(&(dict)->items[i].value), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ } \ -+ } \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+static void push_param_infos(struct spa_pod_builder *b, uint32_t n_params, -+ const struct spa_param_info *params) -+{ -+ struct spa_pod_frame f; -+ uint32_t i; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, SPA_POD_Int(n_params), NULL); -+ for (i = 0; i < n_params; i++) { -+ spa_pod_builder_add(b, -+ SPA_POD_Id(params[i].id), -+ SPA_POD_Int(params[i].flags), -+ NULL); -+ } -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define parse_param_infos(p, f, n_params_p, params_p) \ -+do { \ -+ uint32_t i; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, SPA_POD_Int(n_params_p), NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ if (*(n_params_p) > 0) { \ -+ *(params_p) = alloca(*(n_params_p) * sizeof(struct spa_param_info)); \ -+ for (i = 0; i < *(n_params_p); i++) { \ -+ if (spa_pod_parser_get(p, \ -+ SPA_POD_Id(&(*(params_p))[i].id), \ -+ SPA_POD_Int(&(*(params_p))[i].flags), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ } \ -+ } \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+/*********************************************** -+ * INFO STRUCTURES -+ ***********************************************/ -+ -+static void -+marshal_pw_session_info(struct spa_pod_builder *b, -+ const struct pw_session_info *info) -+{ -+ struct spa_pod_frame f; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(info->version), -+ SPA_POD_Int(info->id), -+ SPA_POD_Int(info->change_mask), -+ NULL); -+ push_dict(b, info->props); -+ push_param_infos(b, info->n_params, info->params); -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define demarshal_pw_session_info(p, f, info) \ -+do { \ -+ struct spa_pod_frame sub_f; \ -+ uint32_t version; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, \ -+ SPA_POD_Int(&version), \ -+ SPA_POD_Int(&(info)->id), \ -+ SPA_POD_Int(&(info)->change_mask), \ -+ SPA_POD_Int(&(info)->n_params), \ -+ SPA_POD_Int(&(info)->props->n_items), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ (info)->change_mask &= PW_SESSION_CHANGE_MASK_ALL; \ -+ \ -+ parse_dict(p, &sub_f, (info)->props); \ -+ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ -+ \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+static void -+marshal_pw_endpoint_info(struct spa_pod_builder *b, -+ const struct pw_endpoint_info *info) -+{ -+ struct spa_pod_frame f; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(info->version), -+ SPA_POD_Int(info->id), -+ SPA_POD_String(info->name), -+ SPA_POD_String(info->media_class), -+ SPA_POD_Int(info->direction), -+ SPA_POD_Int(info->flags), -+ SPA_POD_Int(info->change_mask), -+ NULL); -+ push_dict(b, info->props); -+ push_param_infos(b, info->n_params, info->params); -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define demarshal_pw_endpoint_info(p, f, info) \ -+do { \ -+ struct spa_pod_frame sub_f; \ -+ uint32_t version; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, \ -+ SPA_POD_Int(&version), \ -+ SPA_POD_Int(&(info)->id), \ -+ SPA_POD_String(&(info)->name), \ -+ SPA_POD_String(&(info)->media_class), \ -+ SPA_POD_Int(&(info)->direction), \ -+ SPA_POD_Int(&(info)->flags), \ -+ SPA_POD_Int(&(info)->change_mask), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ (info)->change_mask &= PW_ENDPOINT_CHANGE_MASK_ALL; \ -+ \ -+ parse_dict(p, &sub_f, (info)->props); \ -+ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ -+ \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+static void -+marshal_pw_endpoint_stream_info(struct spa_pod_builder *b, -+ const struct pw_endpoint_stream_info *info) -+{ -+ struct spa_pod_frame f; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(info->version), -+ SPA_POD_Int(info->id), -+ SPA_POD_Int(info->endpoint_id), -+ SPA_POD_String(info->name), -+ SPA_POD_Int(info->change_mask), -+ SPA_POD_Pod(info->link_params), -+ NULL); -+ push_dict(b, info->props); -+ push_param_infos(b, info->n_params, info->params); -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define demarshal_pw_endpoint_stream_info(p, f, info) \ -+do { \ -+ struct spa_pod_frame sub_f; \ -+ uint32_t version; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, \ -+ SPA_POD_Int(&version), \ -+ SPA_POD_Int(&(info)->id), \ -+ SPA_POD_Int(&(info)->endpoint_id), \ -+ SPA_POD_String(&(info)->name), \ -+ SPA_POD_Int(&(info)->change_mask), \ -+ SPA_POD_Pod(&(info)->link_params), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ (info)->change_mask &= PW_ENDPOINT_STREAM_CHANGE_MASK_ALL; \ -+ \ -+ parse_dict(p, &sub_f, (info)->props); \ -+ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ -+ \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+static void -+marshal_pw_endpoint_link_info(struct spa_pod_builder *b, -+ const struct pw_endpoint_link_info *info) -+{ -+ struct spa_pod_frame f; -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(info->version), -+ SPA_POD_Int(info->id), -+ SPA_POD_Int(info->session_id), -+ SPA_POD_Int(info->output_endpoint_id), -+ SPA_POD_Int(info->output_stream_id), -+ SPA_POD_Int(info->input_endpoint_id), -+ SPA_POD_Int(info->input_stream_id), -+ SPA_POD_Int(info->change_mask), -+ SPA_POD_Int(info->state), -+ SPA_POD_String(info->error), -+ NULL); -+ push_dict(b, info->props); -+ push_param_infos(b, info->n_params, info->params); -+ spa_pod_builder_pop(b, &f); -+} -+ -+/* macro because of alloca() */ -+#define demarshal_pw_endpoint_link_info(p, f, info) \ -+do { \ -+ struct spa_pod_frame sub_f; \ -+ uint32_t version; \ -+ \ -+ if (spa_pod_parser_push_struct(p, f) < 0 || \ -+ spa_pod_parser_get(p, \ -+ SPA_POD_Int(&version), \ -+ SPA_POD_Int(&(info)->id), \ -+ SPA_POD_Int(&(info)->session_id), \ -+ SPA_POD_Int(&(info)->output_endpoint_id), \ -+ SPA_POD_Int(&(info)->output_stream_id), \ -+ SPA_POD_Int(&(info)->input_endpoint_id), \ -+ SPA_POD_Int(&(info)->input_stream_id), \ -+ SPA_POD_Int(&(info)->change_mask), \ -+ SPA_POD_Int(&(info)->state), \ -+ SPA_POD_String(&(info)->error), \ -+ NULL) < 0) \ -+ return -EINVAL; \ -+ \ -+ (info)->change_mask &= PW_ENDPOINT_LINK_CHANGE_MASK_ALL; \ -+ \ -+ parse_dict(p, &sub_f, (info)->props); \ -+ parse_param_infos(p, &sub_f, &(info)->n_params, &(info)->params); \ -+ \ -+ spa_pod_parser_pop(p, f); \ -+} while(0) -+ -+/*********************************************** -+ * CLIENT ENDPOINT -+ ***********************************************/ -+ -+static int client_endpoint_marshal_set_id (void *object, uint32_t id) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID, NULL); -+ -+ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_endpoint_marshal_set_session_id (void *object, uint32_t id) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID, NULL); -+ -+ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_endpoint_marshal_set_param (void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_endpoint_marshal_stream_set_param (void *object, -+ uint32_t stream_id, uint32_t id, -+ uint32_t flags, const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(stream_id), -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_endpoint_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_client_endpoint_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int client_endpoint_marshal_update(void *object, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_info *info) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ struct spa_pod_frame f; -+ uint32_t i; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE, NULL); -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(change_mask), -+ SPA_POD_Int(n_params), -+ NULL); -+ -+ for (i = 0; i < n_params; i++) -+ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); -+ -+ if (info) -+ marshal_pw_endpoint_info(b, info); -+ else -+ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); -+ -+ spa_pod_builder_pop(b, &f); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int client_endpoint_marshal_stream_update(void *object, -+ uint32_t stream_id, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_stream_info *info) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ struct spa_pod_frame f; -+ uint32_t i; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE, NULL); -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(stream_id), -+ SPA_POD_Int(change_mask), -+ SPA_POD_Int(n_params), -+ NULL); -+ -+ for (i = 0; i < n_params; i++) -+ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); -+ -+ if (info) -+ marshal_pw_endpoint_stream_info(b, info); -+ else -+ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); -+ -+ spa_pod_builder_pop(b, &f); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int client_endpoint_demarshal_set_id(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&id)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, -+ set_id, 0, id); -+} -+ -+static int client_endpoint_demarshal_set_session_id(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&id)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, -+ set_session_id, 0, id); -+} -+ -+static int client_endpoint_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ const struct spa_pod *param = NULL; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_PodObject(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, -+ set_param, 0, id, flags, param); -+} -+ -+static int client_endpoint_demarshal_stream_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t stream_id, id, flags; -+ const struct spa_pod *param = NULL; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&stream_id), -+ SPA_POD_Int(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_PodObject(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_endpoint_proxy_events, -+ stream_set_param, 0, stream_id, id, flags, param); -+} -+ -+static int client_endpoint_demarshal_update(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs[2]; -+ struct spa_pod_frame f[2]; -+ uint32_t change_mask, n_params; -+ const struct spa_pod **params = NULL; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_info info = { .props = &props }, *infop = NULL; -+ struct spa_pod *ipod; -+ uint32_t i; -+ -+ spa_pod_parser_init(&prs[0], msg->data, msg->size); -+ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || -+ spa_pod_parser_get(&prs[0], -+ SPA_POD_Int(&change_mask), -+ SPA_POD_Int(&n_params), NULL) < 0) -+ return -EINVAL; -+ -+ if (n_params > 0) -+ params = alloca(n_params * sizeof(struct spa_pod *)); -+ for (i = 0; i < n_params; i++) -+ if (spa_pod_parser_get(&prs[0], -+ SPA_POD_PodObject(¶ms[i]), NULL) < 0) -+ return -EINVAL; -+ -+ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) -+ return -EINVAL; -+ if (ipod) { -+ infop = &info; -+ spa_pod_parser_pod(&prs[1], ipod); -+ demarshal_pw_endpoint_info(&prs[1], &f[1], infop); -+ } -+ -+ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, -+ update, 0, change_mask, n_params, params, infop); -+} -+ -+static int client_endpoint_demarshal_stream_update(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs[2]; -+ struct spa_pod_frame f[2]; -+ uint32_t stream_id, change_mask, n_params; -+ const struct spa_pod **params = NULL; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_stream_info info = { .props = &props }, *infop = NULL; -+ struct spa_pod *ipod; -+ uint32_t i; -+ -+ spa_pod_parser_init(&prs[0], msg->data, msg->size); -+ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || -+ spa_pod_parser_get(&prs[0], -+ SPA_POD_Int(&stream_id), -+ SPA_POD_Int(&change_mask), -+ SPA_POD_Int(&n_params), NULL) < 0) -+ return -EINVAL; -+ -+ if (n_params > 0) -+ params = alloca(n_params * sizeof(struct spa_pod *)); -+ for (i = 0; i < n_params; i++) -+ if (spa_pod_parser_get(&prs[0], -+ SPA_POD_PodObject(¶ms[i]), NULL) < 0) -+ return -EINVAL; -+ -+ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) -+ return -EINVAL; -+ if (ipod) { -+ infop = &info; -+ spa_pod_parser_pod(&prs[1], ipod); -+ demarshal_pw_endpoint_stream_info(&prs[1], &f[1], infop); -+ } -+ -+ return pw_resource_notify(resource, struct pw_client_endpoint_proxy_methods, -+ stream_update, 0, stream_id, change_mask, n_params, params, infop); -+} -+ -+static const struct pw_client_endpoint_proxy_events pw_protocol_native_client_endpoint_event_marshal = { -+ PW_VERSION_CLIENT_ENDPOINT_PROXY_EVENTS, -+ .set_id = client_endpoint_marshal_set_id, -+ .set_session_id = client_endpoint_marshal_set_session_id, -+ .set_param = client_endpoint_marshal_set_param, -+ .stream_set_param = client_endpoint_marshal_stream_set_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_client_endpoint_event_demarshal[PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM] = -+{ -+ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_ID] = { client_endpoint_demarshal_set_id, 0 }, -+ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_SESSION_ID] = { client_endpoint_demarshal_set_session_id, 0 }, -+ [PW_CLIENT_ENDPOINT_PROXY_EVENT_SET_PARAM] = { client_endpoint_demarshal_set_param, 0 }, -+ [PW_CLIENT_ENDPOINT_PROXY_EVENT_STREAM_SET_PARAM] = { client_endpoint_demarshal_stream_set_param, 0 }, -+}; -+ -+static const struct pw_client_endpoint_proxy_methods pw_protocol_native_client_endpoint_method_marshal = { -+ PW_VERSION_CLIENT_ENDPOINT_PROXY_METHODS, -+ .add_listener = client_endpoint_marshal_add_listener, -+ .update = client_endpoint_marshal_update, -+ .stream_update = client_endpoint_marshal_stream_update, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_client_endpoint_method_demarshal[PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM] = -+{ -+ [PW_CLIENT_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_CLIENT_ENDPOINT_PROXY_METHOD_UPDATE] = { client_endpoint_demarshal_update, 0 }, -+ [PW_CLIENT_ENDPOINT_PROXY_METHOD_STREAM_UPDATE] = { client_endpoint_demarshal_stream_update, 0 }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_client_endpoint_marshal = { -+ PW_TYPE_INTERFACE_ClientEndpoint, -+ PW_VERSION_CLIENT_ENDPOINT_PROXY, -+ PW_CLIENT_ENDPOINT_PROXY_METHOD_NUM, -+ PW_CLIENT_ENDPOINT_PROXY_EVENT_NUM, -+ &pw_protocol_native_client_endpoint_method_marshal, -+ &pw_protocol_native_client_endpoint_method_demarshal, -+ &pw_protocol_native_client_endpoint_event_marshal, -+ &pw_protocol_native_client_endpoint_event_demarshal, -+}; -+ -+/*********************************************** -+ * CLIENT SESSION -+ ***********************************************/ -+ -+static int client_session_marshal_set_id (void *object, uint32_t id) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_SET_ID, NULL); -+ -+ spa_pod_builder_add (b, SPA_POD_Int(id), NULL); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_set_param (void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_link_set_param (void *object, -+ uint32_t link_id, uint32_t id, -+ uint32_t flags, const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(link_id), -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_create_link(void *object, -+ const struct spa_dict *props) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ spa_return_val_if_fail(props, -EINVAL); -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK, NULL); -+ -+ push_dict(b, props); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_destroy_link (void *object, uint32_t link_id) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK, NULL); -+ -+ spa_pod_builder_add(b, SPA_POD_Int(link_id), NULL); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_link_request_state (void *object, -+ uint32_t link_id, uint32_t state) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(link_id), -+ SPA_POD_Int(state)); -+ -+ return pw_protocol_native_end_resource(resource, b); -+} -+ -+static int client_session_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_client_session_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int client_session_marshal_update(void *object, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_session_info *info) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ struct spa_pod_frame f; -+ uint32_t i; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_CLIENT_SESSION_PROXY_METHOD_UPDATE, NULL); -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(change_mask), -+ SPA_POD_Int(n_params), -+ NULL); -+ -+ for (i = 0; i < n_params; i++) -+ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); -+ -+ if (info) -+ marshal_pw_session_info(b, info); -+ else -+ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); -+ -+ spa_pod_builder_pop(b, &f); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int client_session_marshal_link_update(void *object, -+ uint32_t link_id, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_endpoint_link_info *info) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ struct spa_pod_frame f; -+ uint32_t i; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE, NULL); -+ -+ spa_pod_builder_push_struct(b, &f); -+ spa_pod_builder_add(b, -+ SPA_POD_Int(link_id), -+ SPA_POD_Int(change_mask), -+ SPA_POD_Int(n_params), -+ NULL); -+ -+ for (i = 0; i < n_params; i++) -+ spa_pod_builder_add(b, SPA_POD_Pod(params[i]), NULL); -+ -+ if (info) -+ marshal_pw_endpoint_link_info(b, info); -+ else -+ spa_pod_builder_add(b, SPA_POD_Pod(NULL), NULL); -+ -+ spa_pod_builder_pop(b, &f); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int client_session_demarshal_set_id(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&id)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ set_id, 0, id); -+} -+ -+static int client_session_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ const struct spa_pod *param = NULL; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_PodObject(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ set_param, 0, id, flags, param); -+} -+ -+static int client_session_demarshal_link_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t link_id, id, flags; -+ const struct spa_pod *param = NULL; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&link_id), -+ SPA_POD_Int(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_PodObject(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ link_set_param, 0, link_id, id, flags, param); -+} -+ -+static int client_session_demarshal_create_link(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ parse_dict(&prs, &f, &props); -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ create_link, 0, &props); -+} -+ -+static int client_session_demarshal_destroy_link(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t link_id; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&link_id)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ destroy_link, 0, link_id); -+} -+ -+static int client_session_demarshal_link_request_state(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t link_id, state; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&link_id), -+ SPA_POD_Int(&state)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_client_session_proxy_events, -+ link_request_state, 0, link_id, state); -+} -+ -+static int client_session_demarshal_update(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs[2]; -+ struct spa_pod_frame f[2]; -+ uint32_t change_mask, n_params; -+ const struct spa_pod **params = NULL; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_session_info info = { .props = &props }, *infop = NULL; -+ struct spa_pod *ipod; -+ uint32_t i; -+ -+ spa_pod_parser_init(&prs[0], msg->data, msg->size); -+ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || -+ spa_pod_parser_get(&prs[0], -+ SPA_POD_Int(&change_mask), -+ SPA_POD_Int(&n_params), NULL) < 0) -+ return -EINVAL; -+ -+ if (n_params > 0) -+ params = alloca(n_params * sizeof(struct spa_pod *)); -+ for (i = 0; i < n_params; i++) -+ if (spa_pod_parser_get(&prs[0], -+ SPA_POD_PodObject(¶ms[i]), NULL) < 0) -+ return -EINVAL; -+ -+ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) -+ return -EINVAL; -+ if (ipod) { -+ infop = &info; -+ spa_pod_parser_pod(&prs[1], ipod); -+ demarshal_pw_session_info(&prs[1], &f[1], infop); -+ } -+ -+ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, -+ update, 0, change_mask, n_params, params, infop); -+} -+ -+static int client_session_demarshal_link_update(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs[2]; -+ struct spa_pod_frame f[2]; -+ uint32_t link_id, change_mask, n_params; -+ const struct spa_pod **params = NULL; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_link_info info = { .props = &props }, *infop = NULL; -+ struct spa_pod *ipod; -+ uint32_t i; -+ -+ spa_pod_parser_init(&prs[0], msg->data, msg->size); -+ if (spa_pod_parser_push_struct(&prs[0], &f[0]) < 0 || -+ spa_pod_parser_get(&prs[0], -+ SPA_POD_Int(&link_id), -+ SPA_POD_Int(&change_mask), -+ SPA_POD_Int(&n_params), NULL) < 0) -+ return -EINVAL; -+ -+ if (n_params > 0) -+ params = alloca(n_params * sizeof(struct spa_pod *)); -+ for (i = 0; i < n_params; i++) -+ if (spa_pod_parser_get(&prs[0], -+ SPA_POD_PodObject(¶ms[i]), NULL) < 0) -+ return -EINVAL; -+ -+ if (spa_pod_parser_get(&prs[0], SPA_POD_PodStruct(&ipod), NULL) < 0) -+ return -EINVAL; -+ if (ipod) { -+ infop = &info; -+ spa_pod_parser_pod(&prs[1], ipod); -+ demarshal_pw_endpoint_link_info(&prs[1], &f[1], infop); -+ } -+ -+ return pw_resource_notify(resource, struct pw_client_session_proxy_methods, -+ link_update, 0, link_id, change_mask, n_params, params, infop); -+} -+ -+static const struct pw_client_session_proxy_events pw_protocol_native_client_session_event_marshal = { -+ PW_VERSION_CLIENT_SESSION_PROXY_EVENTS, -+ .set_id = client_session_marshal_set_id, -+ .set_param = client_session_marshal_set_param, -+ .link_set_param = client_session_marshal_link_set_param, -+ .create_link = client_session_marshal_create_link, -+ .destroy_link = client_session_marshal_destroy_link, -+ .link_request_state = client_session_marshal_link_request_state, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_client_session_event_demarshal[PW_CLIENT_SESSION_PROXY_EVENT_NUM] = -+{ -+ [PW_CLIENT_SESSION_PROXY_EVENT_SET_ID] = { client_session_demarshal_set_id, 0 }, -+ [PW_CLIENT_SESSION_PROXY_EVENT_SET_PARAM] = { client_session_demarshal_set_param, 0 }, -+ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_SET_PARAM] = { client_session_demarshal_link_set_param, 0 }, -+ [PW_CLIENT_SESSION_PROXY_EVENT_CREATE_LINK] = { client_session_demarshal_create_link, 0 }, -+ [PW_CLIENT_SESSION_PROXY_EVENT_DESTROY_LINK] = { client_session_demarshal_destroy_link, 0 }, -+ [PW_CLIENT_SESSION_PROXY_EVENT_LINK_REQUEST_STATE] = { client_session_demarshal_link_request_state, 0 }, -+}; -+ -+static const struct pw_client_session_proxy_methods pw_protocol_native_client_session_method_marshal = { -+ PW_VERSION_CLIENT_SESSION_PROXY_METHODS, -+ .add_listener = client_session_marshal_add_listener, -+ .update = client_session_marshal_update, -+ .link_update = client_session_marshal_link_update, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_client_session_method_demarshal[PW_CLIENT_SESSION_PROXY_METHOD_NUM] = -+{ -+ [PW_CLIENT_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_CLIENT_SESSION_PROXY_METHOD_UPDATE] = { client_session_demarshal_update, 0 }, -+ [PW_CLIENT_SESSION_PROXY_METHOD_LINK_UPDATE] = { client_session_demarshal_link_update, 0 }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_client_session_marshal = { -+ PW_TYPE_INTERFACE_ClientSession, -+ PW_VERSION_CLIENT_SESSION_PROXY, -+ PW_CLIENT_SESSION_PROXY_METHOD_NUM, -+ PW_CLIENT_SESSION_PROXY_EVENT_NUM, -+ &pw_protocol_native_client_session_method_marshal, -+ &pw_protocol_native_client_session_method_demarshal, -+ &pw_protocol_native_client_session_event_marshal, -+ &pw_protocol_native_client_session_event_demarshal, -+}; -+ -+/*********************************************** -+ * ENDPOINT LINK -+ ***********************************************/ -+ -+static void endpoint_link_marshal_info (void *object, -+ const struct pw_endpoint_link_info *info) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_LINK_PROXY_EVENT_INFO, NULL); -+ -+ marshal_pw_endpoint_link_info(b, info); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static void endpoint_link_marshal_param (void *object, int seq, uint32_t id, -+ uint32_t index, uint32_t next, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_LINK_PROXY_EVENT_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(seq), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(next), -+ SPA_POD_Pod(param)); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static int endpoint_link_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_link_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int endpoint_link_marshal_subscribe_params(void *object, -+ uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_link_marshal_enum_params(void *object, -+ int seq, uint32_t id, -+ uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_protocol_native_message *msg; -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS, &msg); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(num), -+ SPA_POD_Pod(filter)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_link_marshal_set_param(void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_link_marshal_request_state(void *object, -+ enum pw_endpoint_link_state state) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE, NULL); -+ -+ spa_pod_builder_add_struct(b, SPA_POD_Int(state)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_link_marshal_destroy(void *object) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY, NULL); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_link_demarshal_info(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_link_info info = { .props = &props }; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ -+ demarshal_pw_endpoint_link_info(&prs, &f, &info); -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, -+ info, 0, &info); -+} -+ -+static int endpoint_link_demarshal_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, next; -+ int seq; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&next), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_link_proxy_events, -+ param, 0, seq, id, index, next, param); -+} -+ -+static int endpoint_link_demarshal_subscribe_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t csize, ctype, n_ids; -+ uint32_t *ids; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) -+ return -EINVAL; -+ -+ if (ctype != SPA_TYPE_Id) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, -+ subscribe_params, 0, ids, n_ids); -+} -+ -+static int endpoint_link_demarshal_enum_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, num; -+ int seq; -+ struct spa_pod *filter; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&num), -+ SPA_POD_Pod(&filter)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, -+ enum_params, 0, seq, id, index, num, filter); -+} -+ -+static int endpoint_link_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, -+ set_param, 0, id, flags, param); -+} -+ -+static int endpoint_link_demarshal_request_state(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ enum pw_endpoint_link_state state; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&state)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, -+ request_state, 0, state); -+} -+ -+static int endpoint_link_demarshal_destroy(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_link_proxy_methods, -+ destroy, 0); -+} -+ -+static const struct pw_endpoint_link_proxy_events pw_protocol_native_endpoint_link_event_marshal = { -+ PW_VERSION_ENDPOINT_LINK_PROXY_EVENTS, -+ .info = endpoint_link_marshal_info, -+ .param = endpoint_link_marshal_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_link_event_demarshal[PW_ENDPOINT_LINK_PROXY_EVENT_NUM] = -+{ -+ [PW_ENDPOINT_LINK_PROXY_EVENT_INFO] = { endpoint_link_demarshal_info, 0 }, -+ [PW_ENDPOINT_LINK_PROXY_EVENT_PARAM] = { endpoint_link_demarshal_param, 0 }, -+}; -+ -+static const struct pw_endpoint_link_proxy_methods pw_protocol_native_endpoint_link_method_marshal = { -+ PW_VERSION_ENDPOINT_LINK_PROXY_METHODS, -+ .add_listener = endpoint_link_marshal_add_listener, -+ .subscribe_params = endpoint_link_marshal_subscribe_params, -+ .enum_params = endpoint_link_marshal_enum_params, -+ .set_param = endpoint_link_marshal_set_param, -+ .request_state = endpoint_link_marshal_request_state, -+ .destroy = endpoint_link_marshal_destroy, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_link_method_demarshal[PW_ENDPOINT_LINK_PROXY_METHOD_NUM] = -+{ -+ [PW_ENDPOINT_LINK_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_ENDPOINT_LINK_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_link_demarshal_subscribe_params, 0 }, -+ [PW_ENDPOINT_LINK_PROXY_METHOD_ENUM_PARAMS] = { endpoint_link_demarshal_enum_params, 0 }, -+ [PW_ENDPOINT_LINK_PROXY_METHOD_SET_PARAM] = { endpoint_link_demarshal_set_param, PW_PERM_W }, -+ [PW_ENDPOINT_LINK_PROXY_METHOD_REQUEST_STATE] = { endpoint_link_demarshal_request_state, PW_PERM_W }, -+ [PW_ENDPOINT_LINK_PROXY_METHOD_DESTROY] = { endpoint_link_demarshal_destroy, PW_PERM_W }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_endpoint_link_marshal = { -+ PW_TYPE_INTERFACE_EndpointLink, -+ PW_VERSION_ENDPOINT_LINK_PROXY, -+ PW_ENDPOINT_LINK_PROXY_METHOD_NUM, -+ PW_ENDPOINT_LINK_PROXY_EVENT_NUM, -+ &pw_protocol_native_endpoint_link_method_marshal, -+ &pw_protocol_native_endpoint_link_method_demarshal, -+ &pw_protocol_native_endpoint_link_event_marshal, -+ &pw_protocol_native_endpoint_link_event_demarshal, -+}; -+ -+/*********************************************** -+ * ENDPOINT STREAM -+ ***********************************************/ -+ -+static void endpoint_stream_marshal_info (void *object, -+ const struct pw_endpoint_stream_info *info) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_STREAM_PROXY_EVENT_INFO, NULL); -+ -+ marshal_pw_endpoint_stream_info(b, info); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static void endpoint_stream_marshal_param (void *object, int seq, uint32_t id, -+ uint32_t index, uint32_t next, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(seq), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(next), -+ SPA_POD_Pod(param)); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static int endpoint_stream_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_stream_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int endpoint_stream_marshal_subscribe_params(void *object, -+ uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_stream_marshal_enum_params(void *object, -+ int seq, uint32_t id, -+ uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_protocol_native_message *msg; -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS, &msg); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(num), -+ SPA_POD_Pod(filter)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_stream_marshal_set_param(void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_stream_demarshal_info(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_stream_info info = { .props = &props }; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ -+ demarshal_pw_endpoint_stream_info(&prs, &f, &info); -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, -+ info, 0, &info); -+} -+ -+static int endpoint_stream_demarshal_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, next; -+ int seq; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&next), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_stream_proxy_events, -+ param, 0, seq, id, index, next, param); -+} -+ -+static int endpoint_stream_demarshal_subscribe_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t csize, ctype, n_ids; -+ uint32_t *ids; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) -+ return -EINVAL; -+ -+ if (ctype != SPA_TYPE_Id) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, -+ subscribe_params, 0, ids, n_ids); -+} -+ -+static int endpoint_stream_demarshal_enum_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, num; -+ int seq; -+ struct spa_pod *filter; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&num), -+ SPA_POD_Pod(&filter)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, -+ enum_params, 0, seq, id, index, num, filter); -+} -+ -+static int endpoint_stream_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_stream_proxy_methods, -+ set_param, 0, id, flags, param); -+} -+ -+static const struct pw_endpoint_stream_proxy_events pw_protocol_native_endpoint_stream_event_marshal = { -+ PW_VERSION_ENDPOINT_STREAM_PROXY_EVENTS, -+ .info = endpoint_stream_marshal_info, -+ .param = endpoint_stream_marshal_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_stream_event_demarshal[PW_ENDPOINT_STREAM_PROXY_EVENT_NUM] = -+{ -+ [PW_ENDPOINT_STREAM_PROXY_EVENT_INFO] = { endpoint_stream_demarshal_info, 0 }, -+ [PW_ENDPOINT_STREAM_PROXY_EVENT_PARAM] = { endpoint_stream_demarshal_param, 0 }, -+}; -+ -+static const struct pw_endpoint_stream_proxy_methods pw_protocol_native_endpoint_stream_method_marshal = { -+ PW_VERSION_ENDPOINT_STREAM_PROXY_METHODS, -+ .add_listener = endpoint_stream_marshal_add_listener, -+ .subscribe_params = endpoint_stream_marshal_subscribe_params, -+ .enum_params = endpoint_stream_marshal_enum_params, -+ .set_param = endpoint_stream_marshal_set_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_stream_method_demarshal[PW_ENDPOINT_STREAM_PROXY_METHOD_NUM] = -+{ -+ [PW_ENDPOINT_STREAM_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_ENDPOINT_STREAM_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_stream_demarshal_subscribe_params, 0 }, -+ [PW_ENDPOINT_STREAM_PROXY_METHOD_ENUM_PARAMS] = { endpoint_stream_demarshal_enum_params, 0 }, -+ [PW_ENDPOINT_STREAM_PROXY_METHOD_SET_PARAM] = { endpoint_stream_demarshal_set_param, PW_PERM_W }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_endpoint_stream_marshal = { -+ PW_TYPE_INTERFACE_EndpointStream, -+ PW_VERSION_ENDPOINT_STREAM_PROXY, -+ PW_ENDPOINT_STREAM_PROXY_METHOD_NUM, -+ PW_ENDPOINT_STREAM_PROXY_EVENT_NUM, -+ &pw_protocol_native_endpoint_stream_method_marshal, -+ &pw_protocol_native_endpoint_stream_method_demarshal, -+ &pw_protocol_native_endpoint_stream_event_marshal, -+ &pw_protocol_native_endpoint_stream_event_demarshal, -+}; -+ -+/*********************************************** -+ * ENDPOINT -+ ***********************************************/ -+ -+static void endpoint_marshal_info (void *object, -+ const struct pw_endpoint_info *info) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_PROXY_EVENT_INFO, NULL); -+ -+ marshal_pw_endpoint_info(b, info); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static void endpoint_marshal_param (void *object, int seq, uint32_t id, -+ uint32_t index, uint32_t next, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_ENDPOINT_PROXY_EVENT_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(seq), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(next), -+ SPA_POD_Pod(param)); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static int endpoint_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_endpoint_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int endpoint_marshal_subscribe_params(void *object, -+ uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_marshal_enum_params(void *object, -+ int seq, uint32_t id, -+ uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_protocol_native_message *msg; -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS, &msg); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(num), -+ SPA_POD_Pod(filter)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_marshal_set_param(void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_ENDPOINT_PROXY_METHOD_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int endpoint_demarshal_info(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_endpoint_info info = { .props = &props }; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ -+ demarshal_pw_endpoint_info(&prs, &f, &info); -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, -+ info, 0, &info); -+} -+ -+static int endpoint_demarshal_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, next; -+ int seq; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&next), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_endpoint_proxy_events, -+ param, 0, seq, id, index, next, param); -+} -+ -+static int endpoint_demarshal_subscribe_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t csize, ctype, n_ids; -+ uint32_t *ids; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) -+ return -EINVAL; -+ -+ if (ctype != SPA_TYPE_Id) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, -+ subscribe_params, 0, ids, n_ids); -+} -+ -+static int endpoint_demarshal_enum_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, num; -+ int seq; -+ struct spa_pod *filter; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&num), -+ SPA_POD_Pod(&filter)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, -+ enum_params, 0, seq, id, index, num, filter); -+} -+ -+static int endpoint_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_endpoint_proxy_methods, -+ set_param, 0, id, flags, param); -+} -+ -+static const struct pw_endpoint_proxy_events pw_protocol_native_endpoint_event_marshal = { -+ PW_VERSION_ENDPOINT_PROXY_EVENTS, -+ .info = endpoint_marshal_info, -+ .param = endpoint_marshal_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_event_demarshal[PW_ENDPOINT_PROXY_EVENT_NUM] = -+{ -+ [PW_ENDPOINT_PROXY_EVENT_INFO] = { endpoint_demarshal_info, 0 }, -+ [PW_ENDPOINT_PROXY_EVENT_PARAM] = { endpoint_demarshal_param, 0 }, -+}; -+ -+static const struct pw_endpoint_proxy_methods pw_protocol_native_endpoint_method_marshal = { -+ PW_VERSION_ENDPOINT_PROXY_METHODS, -+ .add_listener = endpoint_marshal_add_listener, -+ .subscribe_params = endpoint_marshal_subscribe_params, -+ .enum_params = endpoint_marshal_enum_params, -+ .set_param = endpoint_marshal_set_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_endpoint_method_demarshal[PW_ENDPOINT_PROXY_METHOD_NUM] = -+{ -+ [PW_ENDPOINT_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_ENDPOINT_PROXY_METHOD_SUBSCRIBE_PARAMS] = { endpoint_demarshal_subscribe_params, 0 }, -+ [PW_ENDPOINT_PROXY_METHOD_ENUM_PARAMS] = { endpoint_demarshal_enum_params, 0 }, -+ [PW_ENDPOINT_PROXY_METHOD_SET_PARAM] = { endpoint_demarshal_set_param, PW_PERM_W }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_endpoint_marshal = { -+ PW_TYPE_INTERFACE_Endpoint, -+ PW_VERSION_ENDPOINT_PROXY, -+ PW_ENDPOINT_PROXY_METHOD_NUM, -+ PW_ENDPOINT_PROXY_EVENT_NUM, -+ &pw_protocol_native_endpoint_method_marshal, -+ &pw_protocol_native_endpoint_method_demarshal, -+ &pw_protocol_native_endpoint_event_marshal, -+ &pw_protocol_native_endpoint_event_demarshal, -+}; -+ -+/*********************************************** -+ * SESSION -+ ***********************************************/ -+ -+static void session_marshal_info (void *object, -+ const struct pw_session_info *info) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_SESSION_PROXY_EVENT_INFO, NULL); -+ -+ marshal_pw_session_info(b, info); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static void session_marshal_param (void *object, int seq, uint32_t id, -+ uint32_t index, uint32_t next, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_resource(resource, -+ PW_SESSION_PROXY_EVENT_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(seq), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(next), -+ SPA_POD_Pod(param)); -+ -+ pw_protocol_native_end_resource(resource, b); -+} -+ -+static int session_marshal_add_listener(void *object, -+ struct spa_hook *listener, -+ const struct pw_session_proxy_events *events, -+ void *data) -+{ -+ struct pw_proxy *proxy = object; -+ pw_proxy_add_object_listener(proxy, listener, events, data); -+ return 0; -+} -+ -+static int session_marshal_subscribe_params(void *object, -+ uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Array(sizeof(uint32_t), SPA_TYPE_Id, n_ids, ids)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int session_marshal_enum_params(void *object, -+ int seq, uint32_t id, -+ uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_protocol_native_message *msg; -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_SESSION_PROXY_METHOD_ENUM_PARAMS, &msg); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Int(SPA_RESULT_RETURN_ASYNC(msg->seq)), -+ SPA_POD_Id(id), -+ SPA_POD_Int(index), -+ SPA_POD_Int(num), -+ SPA_POD_Pod(filter)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int session_marshal_set_param(void *object, -+ uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_SESSION_PROXY_METHOD_SET_PARAM, NULL); -+ -+ spa_pod_builder_add_struct(b, -+ SPA_POD_Id(id), -+ SPA_POD_Int(flags), -+ SPA_POD_Pod(param)); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int session_marshal_create_link(void *object, -+ const struct spa_dict *props) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_builder *b; -+ -+ b = pw_protocol_native_begin_proxy(proxy, -+ PW_SESSION_PROXY_METHOD_CREATE_LINK, NULL); -+ -+ push_dict(b, props); -+ -+ return pw_protocol_native_end_proxy(proxy, b); -+} -+ -+static int session_demarshal_info(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ struct pw_session_info info = { .props = &props }; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ -+ demarshal_pw_session_info(&prs, &f, &info); -+ -+ return pw_proxy_notify(proxy, struct pw_session_proxy_events, -+ info, 0, &info); -+} -+ -+static int session_demarshal_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_proxy *proxy = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, next; -+ int seq; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&next), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_proxy_notify(proxy, struct pw_session_proxy_events, -+ param, 0, seq, id, index, next, param); -+} -+ -+static int session_demarshal_subscribe_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t csize, ctype, n_ids; -+ uint32_t *ids; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Array(&csize, &ctype, &n_ids, &ids)) < 0) -+ return -EINVAL; -+ -+ if (ctype != SPA_TYPE_Id) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_session_proxy_methods, -+ subscribe_params, 0, ids, n_ids); -+} -+ -+static int session_demarshal_enum_params(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, index, num; -+ int seq; -+ struct spa_pod *filter; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Int(&seq), -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&index), -+ SPA_POD_Int(&num), -+ SPA_POD_Pod(&filter)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_session_proxy_methods, -+ enum_params, 0, seq, id, index, num, filter); -+} -+ -+static int session_demarshal_set_param(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ uint32_t id, flags; -+ struct spa_pod *param; -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ if (spa_pod_parser_get_struct(&prs, -+ SPA_POD_Id(&id), -+ SPA_POD_Int(&flags), -+ SPA_POD_Pod(¶m)) < 0) -+ return -EINVAL; -+ -+ return pw_resource_notify(resource, struct pw_session_proxy_methods, -+ set_param, 0, id, flags, param); -+} -+ -+static int session_demarshal_create_link(void *object, -+ const struct pw_protocol_native_message *msg) -+{ -+ struct pw_resource *resource = object; -+ struct spa_pod_parser prs; -+ struct spa_pod_frame f; -+ struct spa_dict props = SPA_DICT_INIT(NULL, 0); -+ -+ spa_pod_parser_init(&prs, msg->data, msg->size); -+ -+ parse_dict(&prs, &f, &props); -+ -+ return pw_resource_notify(resource, struct pw_session_proxy_methods, -+ create_link, 0, &props); -+} -+ -+static const struct pw_session_proxy_events pw_protocol_native_session_event_marshal = { -+ PW_VERSION_SESSION_PROXY_EVENTS, -+ .info = session_marshal_info, -+ .param = session_marshal_param, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_session_event_demarshal[PW_SESSION_PROXY_EVENT_NUM] = -+{ -+ [PW_SESSION_PROXY_EVENT_INFO] = { session_demarshal_info, 0 }, -+ [PW_SESSION_PROXY_EVENT_PARAM] = { session_demarshal_param, 0 }, -+}; -+ -+static const struct pw_session_proxy_methods pw_protocol_native_session_method_marshal = { -+ PW_VERSION_SESSION_PROXY_METHODS, -+ .add_listener = session_marshal_add_listener, -+ .subscribe_params = session_marshal_subscribe_params, -+ .enum_params = session_marshal_enum_params, -+ .set_param = session_marshal_set_param, -+ .create_link = session_marshal_create_link, -+}; -+ -+static const struct pw_protocol_native_demarshal -+pw_protocol_native_session_method_demarshal[PW_SESSION_PROXY_METHOD_NUM] = -+{ -+ [PW_SESSION_PROXY_METHOD_ADD_LISTENER] = { NULL, 0 }, -+ [PW_SESSION_PROXY_METHOD_SUBSCRIBE_PARAMS] = { session_demarshal_subscribe_params, 0 }, -+ [PW_SESSION_PROXY_METHOD_ENUM_PARAMS] = { session_demarshal_enum_params, 0 }, -+ [PW_SESSION_PROXY_METHOD_SET_PARAM] = { session_demarshal_set_param, PW_PERM_W }, -+ [PW_SESSION_PROXY_METHOD_CREATE_LINK] = { session_demarshal_create_link, PW_PERM_W }, -+}; -+ -+static const struct pw_protocol_marshal pw_protocol_native_session_marshal = { -+ PW_TYPE_INTERFACE_Session, -+ PW_VERSION_SESSION_PROXY, -+ PW_SESSION_PROXY_METHOD_NUM, -+ PW_SESSION_PROXY_EVENT_NUM, -+ &pw_protocol_native_session_method_marshal, -+ &pw_protocol_native_session_method_demarshal, -+ &pw_protocol_native_session_event_marshal, -+ &pw_protocol_native_session_event_demarshal, -+}; -+ -+struct pw_protocol *pw_protocol_native_ext_session_manager_init(struct pw_core *core) -+{ -+ struct pw_protocol *protocol; -+ -+ protocol = pw_core_find_protocol(core, PW_TYPE_INFO_PROTOCOL_Native); -+ -+ if (protocol == NULL) -+ return NULL; -+ -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_client_endpoint_marshal); -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_client_session_marshal); -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_link_marshal); -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_stream_marshal); -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_endpoint_marshal); -+ pw_protocol_add_marshal(protocol, &pw_protocol_native_session_marshal); -+ -+ return protocol; -+} -diff --git a/src/modules/module-session-manager/session.c b/src/modules/module-session-manager/session.c -new file mode 100644 -index 00000000..226eba4e ---- /dev/null -+++ b/src/modules/module-session-manager/session.c -@@ -0,0 +1,341 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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. -+ */ -+ -+#include -+#include -+ -+#include -+#include -+ -+#include -+ -+#include "session.h" -+#include "client-session.h" -+ -+#include -+ -+#define NAME "session" -+ -+struct resource_data { -+ struct session *session; -+ struct spa_hook resource_listener; -+ struct spa_hook object_listener; -+ uint32_t n_subscribe_ids; -+ uint32_t subscribe_ids[32]; -+}; -+ -+#define pw_session_resource(r,m,v,...) \ -+ pw_resource_call(r,struct pw_session_proxy_events,m,v,__VA_ARGS__) -+#define pw_session_resource_info(r,...) \ -+ pw_session_resource(r,info,0,__VA_ARGS__) -+#define pw_session_resource_param(r,...) \ -+ pw_session_resource(r,param,0,__VA_ARGS__) -+ -+static int session_enum_params (void *object, int seq, -+ uint32_t id, uint32_t start, uint32_t num, -+ const struct spa_pod *filter) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct session *this = data->session; -+ struct spa_pod *result; -+ struct spa_pod *param; -+ uint8_t buffer[1024]; -+ struct spa_pod_builder b = { 0 }; -+ uint32_t index; -+ uint32_t next = start; -+ uint32_t count = 0; -+ -+ while (true) { -+ index = next++; -+ if (index >= this->n_params) -+ break; -+ -+ param = this->params[index]; -+ -+ if (param == NULL || !spa_pod_is_object_id(param, id)) -+ continue; -+ -+ spa_pod_builder_init(&b, buffer, sizeof(buffer)); -+ if (spa_pod_filter(&b, &result, param, filter) != 0) -+ continue; -+ -+ pw_log_debug(NAME" %p: %d param %u", this, seq, index); -+ -+ pw_session_resource_param(resource, seq, id, index, next, result); -+ -+ if (++count == num) -+ break; -+ } -+ return 0; -+} -+ -+static int session_subscribe_params (void *object, uint32_t *ids, uint32_t n_ids) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ uint32_t i; -+ -+ n_ids = SPA_MIN(n_ids, SPA_N_ELEMENTS(data->subscribe_ids)); -+ data->n_subscribe_ids = n_ids; -+ -+ for (i = 0; i < n_ids; i++) { -+ data->subscribe_ids[i] = ids[i]; -+ pw_log_debug(NAME" %p: resource %d subscribe param %u", -+ data->session, resource->id, ids[i]); -+ session_enum_params(resource, 1, ids[i], 0, UINT32_MAX, NULL); -+ } -+ return 0; -+} -+ -+static int session_set_param (void *object, uint32_t id, uint32_t flags, -+ const struct spa_pod *param) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct session *this = data->session; -+ -+ pw_client_session_resource_set_param(this->client_sess->resource, -+ id, flags, param); -+ -+ return 0; -+} -+ -+static int session_create_link(void *object, const struct spa_dict *props) -+{ -+ struct pw_resource *resource = object; -+ struct resource_data *data = pw_resource_get_user_data(resource); -+ struct session *this = data->session; -+ -+ pw_client_session_resource_create_link(this->client_sess->resource, -+ props); -+ -+ return 0; -+} -+ -+static const struct pw_session_proxy_methods methods = { -+ PW_VERSION_SESSION_PROXY_METHODS, -+ .subscribe_params = session_subscribe_params, -+ .enum_params = session_enum_params, -+ .set_param = session_set_param, -+ .create_link = session_create_link, -+}; -+ -+static void session_notify_subscribed(struct session *this, -+ uint32_t index, uint32_t next) -+{ -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ struct spa_pod *param = this->params[index]; -+ uint32_t id; -+ uint32_t i; -+ -+ if (!param || !spa_pod_is_object (param)) -+ return; -+ -+ id = SPA_POD_OBJECT_ID (param); -+ -+ spa_list_for_each(resource, &global->resource_list, link) { -+ data = pw_resource_get_user_data(resource); -+ for (i = 0; i < data->n_subscribe_ids; i++) { -+ if (data->subscribe_ids[i] == id) { -+ pw_session_resource_param(resource, 1, id, -+ index, next, param); -+ } -+ } -+ } -+} -+ -+int session_update(struct session *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_session_info *info) -+{ -+ if (change_mask & PW_CLIENT_SESSION_UPDATE_PARAMS) { -+ uint32_t i; -+ size_t size = n_params * sizeof(struct spa_pod *); -+ -+ pw_log_debug(NAME" %p: update %d params", this, n_params); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ this->params = realloc(this->params, size); -+ if (size > 0 && !this->params) { -+ this->n_params = 0; -+ goto no_mem; -+ } -+ this->n_params = n_params; -+ -+ for (i = 0; i < this->n_params; i++) { -+ this->params[i] = params[i] ? spa_pod_copy(params[i]) : NULL; -+ session_notify_subscribed(this, i, i+1); -+ } -+ } -+ -+ if (change_mask & PW_CLIENT_SESSION_UPDATE_INFO) { -+ struct pw_resource *resource; -+ -+ if (info->change_mask & PW_SESSION_CHANGE_MASK_PROPS) -+ pw_properties_update(this->props, info->props); -+ -+ if (info->change_mask & PW_SESSION_CHANGE_MASK_PARAMS) { -+ size_t size = info->n_params * sizeof(struct spa_param_info); -+ -+ this->info.params = realloc(this->info.params, size); -+ if (size > 0 && !this->info.params) { -+ this->info.n_params = 0; -+ goto no_mem; -+ } -+ this->info.n_params = info->n_params; -+ -+ memcpy(this->info.params, info->params, size); -+ } -+ -+ this->info.change_mask = info->change_mask; -+ spa_list_for_each(resource, &this->global->resource_list, link) { -+ pw_session_resource_info(resource, &this->info); -+ } -+ this->info.change_mask = 0; -+ } -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't update: no memory"); -+ pw_resource_error(this->client_sess->resource, -ENOMEM, -+ NAME" can't update: no memory"); -+ return -ENOMEM; -+} -+ -+static void session_unbind(void *data) -+{ -+ struct pw_resource *resource = data; -+ spa_list_remove(&resource->link); -+} -+ -+static const struct pw_resource_events resource_events = { -+ PW_VERSION_RESOURCE_EVENTS, -+ .destroy = session_unbind, -+}; -+ -+static int session_bind(void *_data, struct pw_client *client, -+ uint32_t permissions, uint32_t version, uint32_t id) -+{ -+ struct session *this = _data; -+ struct pw_global *global = this->global; -+ struct pw_resource *resource; -+ struct resource_data *data; -+ -+ resource = pw_resource_new(client, id, permissions, global->type, version, sizeof(*data)); -+ if (resource == NULL) -+ goto no_mem; -+ -+ data = pw_resource_get_user_data(resource); -+ data->session = this; -+ pw_resource_add_listener(resource, &data->resource_listener, -+ &resource_events, resource); -+ pw_resource_add_object_listener(resource, &data->object_listener, -+ &methods, resource); -+ -+ pw_log_debug(NAME" %p: bound to %d", this, resource->id); -+ -+ spa_list_append(&global->resource_list, &resource->link); -+ -+ this->info.change_mask = PW_SESSION_CHANGE_MASK_ALL; -+ pw_session_resource_info(resource, &this->info); -+ this->info.change_mask = 0; -+ -+ return 0; -+ -+ no_mem: -+ pw_log_error(NAME" can't create resource: no memory"); -+ pw_resource_error(this->client_sess->resource, -ENOMEM, -+ NAME" can't create resource: no memory"); -+ return -ENOMEM; -+} -+ -+int session_init(struct session *this, -+ struct client_session *client_sess, -+ struct pw_core *core, -+ struct pw_properties *properties) -+{ -+ const char *keys[] = { -+ PW_KEY_FACTORY_ID, -+ PW_KEY_CLIENT_ID, -+ NULL -+ }; -+ -+ pw_log_debug(NAME" %p: new", this); -+ -+ this->client_sess = client_sess; -+ this->props = properties; -+ -+ properties = pw_properties_new(NULL, NULL); -+ if (!properties) -+ goto no_mem; -+ -+ pw_properties_copy_keys(this->props, properties, keys); -+ -+ this->global = pw_global_new (core, -+ PW_TYPE_INTERFACE_Session, -+ PW_VERSION_SESSION_PROXY, -+ properties, session_bind, this); -+ if (!this->global) -+ goto no_mem; -+ -+ pw_properties_setf(this->props, PW_KEY_SESSION_ID, "%u", this->global->id); -+ -+ this->info.version = PW_VERSION_SESSION_INFO; -+ this->info.id = this->global->id; -+ this->info.props = &this->props->dict; -+ -+ pw_client_session_resource_set_id(client_sess->resource, this->global->id); -+ -+ return pw_global_register(this->global); -+ -+ no_mem: -+ pw_log_error(NAME" - can't create - out of memory"); -+ return -ENOMEM; -+} -+ -+void session_clear(struct session *this) -+{ -+ uint32_t i; -+ -+ pw_log_debug(NAME" %p: destroy", this); -+ -+ pw_global_destroy(this->global); -+ -+ for (i = 0; i < this->n_params; i++) -+ free(this->params[i]); -+ free(this->params); -+ -+ free(this->info.params); -+ -+ if (this->props) -+ pw_properties_free(this->props); -+} -diff --git a/src/modules/module-session-manager/session.h b/src/modules/module-session-manager/session.h -new file mode 100644 -index 00000000..ad0b9b1b ---- /dev/null -+++ b/src/modules/module-session-manager/session.h -@@ -0,0 +1,61 @@ -+/* PipeWire -+ * -+ * Copyright © 2019 Collabora Ltd. -+ * @author George Kiagiadakis -+ * -+ * 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 MODULE_SESSION_MANAGER_SESSION_H -+#define MODULE_SESSION_MANAGER_SESSION_H -+ -+#ifdef __cplusplus -+extern "C" { -+#endif -+ -+struct client_session; -+ -+struct session { -+ struct client_session *client_sess; -+ struct pw_global *global; -+ uint32_t n_params; -+ struct spa_pod **params; -+ struct pw_session_info info; -+ struct pw_properties *props; /* wrapper of info.props */ -+}; -+ -+int session_init(struct session *this, -+ struct client_session *client_sess, -+ struct pw_core *core, -+ struct pw_properties *properties); -+ -+void session_clear(struct session *this); -+ -+int session_update(struct session *this, -+ uint32_t change_mask, -+ uint32_t n_params, -+ const struct spa_pod **params, -+ const struct pw_session_info *info); -+ -+#ifdef __cplusplus -+} /* extern "C" */ -+#endif -+ -+#endif /* MODULE_SESSION_MANAGER_SESSION_H */ -diff --git a/src/pipewire/pipewire.c b/src/pipewire/pipewire.c -index a7012ab2..ec5f1f85 100644 ---- a/src/pipewire/pipewire.c -+++ b/src/pipewire/pipewire.c -@@ -575,6 +575,12 @@ static const struct spa_type_info type_info[] = { - { PW_TYPE_INTERFACE_Module, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Module", NULL }, - { PW_TYPE_INTERFACE_ClientNode, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientNode", NULL }, - { PW_TYPE_INTERFACE_Device, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Device", NULL }, -+ { PW_TYPE_INTERFACE_ClientEndpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientEndpoint", NULL}, -+ { PW_TYPE_INTERFACE_Endpoint, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Endpoint", NULL}, -+ { PW_TYPE_INTERFACE_EndpointStream, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointStream", NULL}, -+ { PW_TYPE_INTERFACE_ClientSession, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "ClientSession", NULL}, -+ { PW_TYPE_INTERFACE_Session, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "Session", NULL}, -+ { PW_TYPE_INTERFACE_EndpointLink, SPA_TYPE_Pointer, PW_TYPE_INFO_INTERFACE_BASE "EndpointLink", NULL}, - { SPA_ID_INVALID, SPA_ID_INVALID, "spa_types", spa_types }, - { 0, 0, NULL, NULL }, - }; -diff --git a/src/pipewire/type.h b/src/pipewire/type.h -index a1b205f7..6b1b8b50 100644 ---- a/src/pipewire/type.h -+++ b/src/pipewire/type.h -@@ -48,7 +48,12 @@ enum { - /* extensions */ - PW_TYPE_INTERFACE_EXTENSIONS = PW_TYPE_INTERFACE_START + 0x1000, - PW_TYPE_INTERFACE_ClientNode, -- -+ PW_TYPE_INTERFACE_ClientEndpoint, -+ PW_TYPE_INTERFACE_Endpoint, -+ PW_TYPE_INTERFACE_EndpointStream, -+ PW_TYPE_INTERFACE_ClientSession, -+ PW_TYPE_INTERFACE_Session, -+ PW_TYPE_INTERFACE_EndpointLink, - }; - - #define PW_TYPE_INFO_BASE "PipeWire:" --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch deleted file mode 100644 index 8c71afb3..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch +++ /dev/null @@ -1,167 +0,0 @@ -From 1249a42d1380945fd8dc7924c1ac912570bef501 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Tue, 28 May 2019 11:46:36 +0300 -Subject: [PATCH] pipewire-cli: add support for printing endpoint info & params - -Upstream-Status: Pending ---- - src/tools/pipewire-cli.c | 107 ++++++++++++++++++++++++++++++++++++++- - 1 file changed, 106 insertions(+), 1 deletion(-) - -diff --git a/src/tools/pipewire-cli.c b/src/tools/pipewire-cli.c -index 6110d170..0dbc8368 100644 ---- a/src/tools/pipewire-cli.c -+++ b/src/tools/pipewire-cli.c -@@ -37,6 +37,8 @@ - #include - #include - -+#include -+ - static const char WHITESPACE[] = " \t"; - - struct remote_data; -@@ -174,8 +176,10 @@ static void print_params(struct spa_param_info *params, uint32_t n_params, char - return; - } - for (i = 0; i < n_params; i++) { -+ const struct spa_type_info *type_info = spa_type_param; -+ - fprintf(stdout, "%c\t %d (%s) %c%c\n", mark, params[i].id, -- spa_debug_type_find_name(spa_type_param, params[i].id), -+ spa_debug_type_find_name(type_info, params[i].id), - params[i].flags & SPA_PARAM_INFO_READ ? 'r' : '-', - params[i].flags & SPA_PARAM_INFO_WRITE ? 'w' : '-'); - } -@@ -652,6 +656,40 @@ static void info_device(struct proxy_data *pd) - info->change_mask = 0; - } - -+static void info_endpoint(struct proxy_data *pd) -+{ -+ struct pw_endpoint_info *info = pd->info; -+ const char *direction; -+ -+ info_global(pd); -+ fprintf(stdout, "\tname: %s\n", info->name); -+ fprintf(stdout, "\tmedia-class: %s\n", info->media_class); -+ switch(info->direction) { -+ case PW_ENDPOINT_DIRECTION_SINK_INPUT: -+ direction = "sink-input"; -+ break; -+ case PW_ENDPOINT_DIRECTION_SOURCE_OUTPUT: -+ direction = "source-output"; -+ break; -+ case PW_ENDPOINT_DIRECTION_SOURCE: -+ direction = "source"; -+ break; -+ case PW_ENDPOINT_DIRECTION_SINK: -+ direction = "sink"; -+ break; -+ default: -+ direction = "invalid"; -+ break; -+ } -+ fprintf(stdout, "\tdirection: %s\n", direction); -+ fprintf(stdout, "\tflags: 0x%x\n", info->flags); -+ fprintf(stdout, "%c\tstreams: %u\n", MARK_CHANGE(0), info->n_streams); -+ fprintf(stdout, "%c\tsession: %u\n", MARK_CHANGE(0), info->session_id); -+ print_properties(info->props, MARK_CHANGE(2), true); -+ print_params(info->params, info->n_params, MARK_CHANGE(3), true); -+ info->change_mask = 0; -+} -+ - static void core_event_info(void *object, const struct pw_core_info *info) - { - struct proxy_data *pd = object; -@@ -853,6 +891,63 @@ static const struct pw_device_proxy_events device_events = { - .param = event_param - }; - -+static void endpoint_info_free(struct pw_endpoint_info *info) -+{ -+ free(info->name); -+ free(info->media_class); -+ free(info->params); -+ if (info->props) -+ pw_properties_free ((struct pw_properties *)info->props); -+ free(info); -+} -+ -+static void endpoint_event_info(void *object, -+ const struct pw_endpoint_info *update) -+{ -+ struct proxy_data *pd = object; -+ struct remote_data *rd = pd->rd; -+ struct pw_endpoint_info *info = pd->info; -+ -+ if (!info) { -+ info = pd->info = calloc(1, sizeof(*info)); -+ info->id = update->id; -+ info->name = strdup(update->name); -+ info->media_class = strdup(update->media_class); -+ info->direction = update->direction; -+ info->flags = update->flags; -+ } -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_STREAMS) -+ info->n_streams = update->n_streams; -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_SESSION) -+ info->session_id = update->session_id; -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PARAMS) { -+ info->n_params = update->n_params; -+ free(info->params); -+ info->params = malloc(info->n_params * sizeof(struct spa_param_info)); -+ memcpy(info->params, update->params, -+ info->n_params * sizeof(struct spa_param_info)); -+ } -+ if (update->change_mask & PW_ENDPOINT_CHANGE_MASK_PROPS) { -+ if (info->props) -+ pw_properties_free ((struct pw_properties *)info->props); -+ info->props = -+ (struct spa_dict *) pw_properties_new_dict (update->props); -+ } -+ -+ if (pd->global == NULL) -+ pd->global = pw_map_lookup(&rd->globals, info->id); -+ if (pd->global && pd->global->info_pending) { -+ info_endpoint(pd); -+ pd->global->info_pending = false; -+ } -+} -+ -+static const struct pw_endpoint_proxy_events endpoint_events = { -+ PW_VERSION_ENDPOINT_PROXY_EVENTS, -+ .info = endpoint_event_info, -+ .param = event_param -+}; -+ - static void - destroy_proxy (void *data) - { -@@ -939,6 +1034,12 @@ static bool bind_global(struct remote_data *rd, struct global *global, char **er - destroy = (pw_destroy_t) pw_link_info_free; - info_func = info_link; - break; -+ case PW_TYPE_INTERFACE_Endpoint: -+ events = &endpoint_events; -+ client_version = PW_VERSION_ENDPOINT_PROXY; -+ destroy = (pw_destroy_t) endpoint_info_free; -+ info_func = info_endpoint; -+ break; - default: - asprintf(error, "unsupported type %s", spa_debug_type_find_name(pw_type_info(), global->type)); - return false; -@@ -1213,6 +1314,10 @@ static bool do_enum_params(struct data *data, const char *cmd, char *args, char - pw_device_proxy_enum_params((struct pw_device_proxy*)global->proxy, 0, - param_id, 0, 0, NULL); - break; -+ case PW_TYPE_INTERFACE_Endpoint: -+ pw_endpoint_proxy_enum_params((struct pw_endpoint_proxy*)global->proxy, 0, -+ param_id, 0, 0, NULL); -+ break; - default: - asprintf(error, "enum-params not implemented on object %d", atoi(a[0])); - return false; --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch deleted file mode 100644 index 30066451..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0014-daemon-config-remote-load-module-session-manager-by-.patch +++ /dev/null @@ -1,37 +0,0 @@ -From a7379880b3cb14ce81cbb3475b1b251df60c86f8 Mon Sep 17 00:00:00 2001 -From: George Kiagiadakis -Date: Fri, 9 Aug 2019 13:26:15 +0300 -Subject: [PATCH] daemon config & remote: load module-session-manager by - default - -Upstream-Status: Pending ---- - src/daemon/pipewire.conf.in | 1 + - src/pipewire/remote.c | 1 + - 2 files changed, 2 insertions(+) - -diff --git a/src/daemon/pipewire.conf.in b/src/daemon/pipewire.conf.in -index 079ec730..19c898b9 100644 ---- a/src/daemon/pipewire.conf.in -+++ b/src/daemon/pipewire.conf.in -@@ -21,4 +21,5 @@ load-module libpipewire-module-client-device - load-module libpipewire-module-access - load-module libpipewire-module-adapter - load-module libpipewire-module-link-factory -+load-module libpipewire-module-session-manager - exec build/src/examples/media-session -diff --git a/src/pipewire/remote.c b/src/pipewire/remote.c -index 2b545f79..62841e58 100644 ---- a/src/pipewire/remote.c -+++ b/src/pipewire/remote.c -@@ -242,6 +242,7 @@ struct pw_remote *pw_remote_new(struct pw_core *core, - pw_module_load(core, "libpipewire-module-rtkit", NULL, NULL); - pw_module_load(core, "libpipewire-module-client-node", NULL, NULL); - pw_module_load(core, "libpipewire-module-adapter", NULL, NULL); -+ pw_module_load(core, "libpipewire-module-session-manager", NULL, NULL); - - spa_list_append(&core->remote_list, &this->link); - --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-connection-move-remaining-data-and-fds.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-connection-move-remaining-data-and-fds.patch deleted file mode 100644 index 925dff78..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0015-connection-move-remaining-data-and-fds.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 1b739247dcab62d1da45109cf805b8a840307ccc Mon Sep 17 00:00:00 2001 -From: Wim Taymans -Date: Tue, 1 Oct 2019 10:43:48 +0200 -Subject: [PATCH] connection: move remaining data and fds - -If we can't send all of the data, move the remaining data to the -start of the buffer so that we can send it again later. - -See #111 - -Upstream-Status: Backport [3d48ba8394396fc8d8cadb1bff3514217ddd70e6] ---- - .../module-protocol-native/connection.c | 23 +++++++++++-------- - 1 file changed, 14 insertions(+), 9 deletions(-) - -diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c -index f51129df..7b6cf112 100644 ---- a/src/modules/module-protocol-native/connection.c -+++ b/src/modules/module-protocol-native/connection.c -@@ -500,8 +500,12 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - if (sent < 0) { - if (errno == EINTR) - continue; -- else -- goto send_error; -+ else { -+ res = -errno; -+ pw_log_error("could not sendmsg on fd:%d n_fds:%d: %s", -+ conn->fd, n_fds, spa_strerror(res)); -+ goto exit; -+ } - } - break; - } -@@ -513,15 +517,16 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - n_fds -= outfds; - fds += outfds; - } -- buf->buffer_size = size; -- buf->n_fds = n_fds; - -- return 0; -+ res = 0; - -- /* ERRORS */ --send_error: -- res = -errno; -- pw_log_error("could not sendmsg on fd:%d n_fds:%d: %s", conn->fd, n_fds, strerror(errno)); -+exit: -+ if (size > 0) -+ memmove(buf->buffer_data, data, size); -+ buf->buffer_size = size; -+ if (n_fds > 0) -+ memmove(buf->fds, fds, n_fds * sizeof(int)); -+ buf->n_fds = n_fds; - return res; - } - --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-protocol-improve-flushing.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-protocol-improve-flushing.patch deleted file mode 100644 index 124434e2..00000000 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0016-protocol-improve-flushing.patch +++ /dev/null @@ -1,243 +0,0 @@ -From dffeeeb74f5b0a0385c4131b7ff349ff04fcebce Mon Sep 17 00:00:00 2001 -From: Wim Taymans -Date: Tue, 1 Oct 2019 12:53:56 +0200 -Subject: [PATCH] protocol: improve flushing - -Use the IO_OUT flag to schedule flushing instead of a flush_event. - -Handle EGAIN and wait for IO_OUT to try again. - -Fixes #111 - -Upstream-Status: Backport [cc8e992cd155b4f19312a5036c7b744fc547410f] ---- - src/modules/module-protocol-native.c | 99 ++++++++++++------- - .../module-protocol-native/connection.c | 2 - - 2 files changed, 62 insertions(+), 39 deletions(-) - -diff --git a/src/modules/module-protocol-native.c b/src/modules/module-protocol-native.c -index cc8501d5..be2d4f57 100644 ---- a/src/modules/module-protocol-native.c -+++ b/src/modules/module-protocol-native.c -@@ -82,9 +82,8 @@ struct client { - struct pw_protocol_native_connection *connection; - struct spa_hook conn_listener; - -- struct spa_source *flush_event; - unsigned int disconnecting:1; -- unsigned int flush_signaled:1; -+ unsigned int flushing:1; - }; - - struct server { -@@ -106,6 +105,7 @@ struct client_data { - struct spa_source *source; - struct pw_protocol_native_connection *connection; - unsigned int busy:1; -+ unsigned int need_flush:1; - }; - - static void -@@ -200,12 +200,14 @@ client_busy_changed(void *data, bool busy) - { - struct client_data *c = data; - struct pw_client *client = c->client; -- uint32_t mask = SPA_IO_ERR | SPA_IO_HUP; -+ uint32_t mask = c->source->mask; - - c->busy = busy; - -- if (!busy) -- mask |= SPA_IO_IN; -+ if (busy) -+ SPA_FLAG_UNSET(mask, SPA_IO_IN); -+ else -+ SPA_FLAG_SET(mask, SPA_IO_IN); - - pw_log_debug(NAME" %p: busy changed %d", client->protocol, busy); - pw_loop_update_io(client->core->main_loop, c->source, mask); -@@ -220,13 +222,32 @@ connection_data(void *data, int fd, uint32_t mask) - { - struct client_data *this = data; - struct pw_client *client = this->client; -+ int res; - -- if (mask & (SPA_IO_ERR | SPA_IO_HUP)) { -+ if (mask & SPA_IO_HUP) { - pw_log_info(NAME" %p: client %p disconnected", client->protocol, client); - pw_client_destroy(client); - return; - } -- -+ if (mask & SPA_IO_ERR) { -+ pw_log_error(NAME" %p: client %p error", client->protocol, client); -+ pw_client_destroy(client); -+ return; -+ } -+ if (mask & SPA_IO_OUT) { -+ res = pw_protocol_native_connection_flush(this->connection); -+ if (res >= 0) { -+ int mask = this->source->mask; -+ SPA_FLAG_UNSET(mask, SPA_IO_OUT); -+ pw_loop_update_io(client->protocol->core->main_loop, -+ this->source, mask); -+ } else if (res != EAGAIN) { -+ pw_log_error("client %p: could not flush: %s", -+ client, spa_strerror(res)); -+ pw_client_destroy(client); -+ return; -+ } -+ } - if (mask & SPA_IO_IN) - process_messages(this); - } -@@ -296,7 +317,8 @@ static struct pw_client *client_new(struct server *s, int fd) - - this->client = client; - this->source = pw_loop_add_io(pw_core_get_main_loop(core), -- fd, SPA_IO_ERR | SPA_IO_HUP, true, connection_data, this); -+ fd, SPA_IO_ERR | SPA_IO_HUP, true, -+ connection_data, this); - if (this->source == NULL) - goto cleanup_client; - -@@ -408,7 +430,7 @@ socket_data(void *data, int fd, uint32_t mask) - - if (!client->busy) - pw_loop_update_io(client->protocol->core->main_loop, -- c->source, SPA_IO_IN | SPA_IO_ERR | SPA_IO_HUP); -+ c->source, c->source->mask | SPA_IO_IN); - } - - static int add_socket(struct pw_protocol *protocol, struct server *s) -@@ -514,6 +536,17 @@ on_remote_data(void *data, int fd, uint32_t mask) - res = -EPIPE; - goto error; - } -+ if (mask & SPA_IO_OUT) { -+ res = pw_protocol_native_connection_flush(conn); -+ if (res >= 0) { -+ int mask = impl->source->mask; -+ SPA_FLAG_UNSET(mask, SPA_IO_OUT); -+ pw_loop_update_io(core->main_loop, -+ impl->source, mask); -+ impl->flushing = false; -+ } else if (res != EAGAIN) -+ goto error; -+ } - - if (mask & SPA_IO_IN) { - const struct pw_protocol_native_message *msg; -@@ -588,23 +621,17 @@ error: - } - - --static void do_flush_event(void *data, uint64_t count) --{ -- struct client *impl = data; -- impl->flush_signaled = false; -- if (impl->connection) -- if (pw_protocol_native_connection_flush(impl->connection) < 0) -- impl->this.disconnect(&impl->this); --} -- - static void on_need_flush(void *data) - { - struct client *impl = data; - struct pw_remote *remote = impl->this.remote; - -- if (!impl->flush_signaled) { -- impl->flush_signaled = true; -- pw_loop_signal_event(remote->core->main_loop, impl->flush_event); -+ if (!impl->flushing) { -+ int mask = impl->source->mask; -+ impl->flushing = true; -+ SPA_FLAG_SET(mask, SPA_IO_OUT); -+ pw_loop_update_io(remote->core->main_loop, -+ impl->source, mask); - } - } - -@@ -669,12 +696,9 @@ static void impl_disconnect(struct pw_protocol_client *client) - static void impl_destroy(struct pw_protocol_client *client) - { - struct client *impl = SPA_CONTAINER_OF(client, struct client, this); -- struct pw_remote *remote = client->remote; - - impl_disconnect(client); - -- pw_loop_destroy_source(remote->core->main_loop, impl->flush_event); -- - spa_list_remove(&client->link); - free(impl); - } -@@ -687,7 +711,6 @@ impl_new_client(struct pw_protocol *protocol, - struct client *impl; - struct pw_protocol_client *this; - const char *str = NULL; -- int res; - - if ((impl = calloc(1, sizeof(struct client))) == NULL) - return NULL; -@@ -711,20 +734,9 @@ impl_new_client(struct pw_protocol *protocol, - this->disconnect = impl_disconnect; - this->destroy = impl_destroy; - -- impl->flush_event = pw_loop_add_event(remote->core->main_loop, do_flush_event, impl); -- if (impl->flush_event == NULL) { -- res = -errno; -- goto error_cleanup; -- } -- - spa_list_append(&protocol->client_list, &this->link); - - return this; -- --error_cleanup: -- free(impl); -- errno = -res; -- return NULL; - } - - static void destroy_server(struct pw_protocol_server *server) -@@ -757,10 +769,23 @@ static void on_before_hook(void *_data) - struct pw_protocol_server *this = &server->this; - struct pw_client *client, *tmp; - struct client_data *data; -+ int res; - - spa_list_for_each_safe(client, tmp, &this->client_list, protocol_link) { - data = client->user_data; -- pw_protocol_native_connection_flush(data->connection); -+ -+ res = pw_protocol_native_connection_flush(data->connection); -+ if (res == -EAGAIN) { -+ int mask = data->source->mask; -+ SPA_FLAG_SET(mask, SPA_IO_OUT); -+ pw_loop_update_io(client->protocol->core->main_loop, -+ data->source, mask); -+ } else if (res < 0) { -+ pw_log_warn("client %p: could not flush: %s", -+ data->client, spa_strerror(res)); -+ pw_client_destroy(client); -+ } -+ - } - } - -diff --git a/src/modules/module-protocol-native/connection.c b/src/modules/module-protocol-native/connection.c -index 7b6cf112..116457e3 100644 ---- a/src/modules/module-protocol-native/connection.c -+++ b/src/modules/module-protocol-native/connection.c -@@ -502,8 +502,6 @@ int pw_protocol_native_connection_flush(struct pw_protocol_native_connection *co - continue; - else { - res = -errno; -- pw_log_error("could not sendmsg on fd:%d n_fds:%d: %s", -- conn->fd, n_fds, spa_strerror(res)); - goto exit; - } - } --- -2.23.0 - diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb index e006f14a..ce7e523c 100644 --- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb +++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb @@ -1,27 +1,23 @@ require pipewire.inc -SRC_URI = "gitsm://github.com/PipeWire/pipewire;protocol=https;branch=work \ +SRC_URI = "gitsm://github.com/PipeWire/pipewire;protocol=https;branch=master \ file://0001-arm-build-with-mno-unaligned-access.patch \ file://0002-logger-print-timestamps-on-logged-messages.patch \ - file://0003-alsa-make-corrections-on-the-timeout-based-on-how-fa.patch \ - file://0004-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ - file://0005-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch \ - file://0006-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch \ - file://0007-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch \ - file://0008-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch \ - file://0009-alsa-do-not-expose-non-interleaved-formats-since-the.patch \ - file://0010-bluez-monitor-fix-usage-of-pw_properties_setf-withou.patch \ - file://0011-meson-revert-version-check-to-require-meson-0.47-not.patch \ - file://0012-extensions-implement-new-session-manager-extension.patch \ - file://0013-pipewire-cli-add-support-for-printing-endpoint-info-.patch \ - file://0014-daemon-config-remote-load-module-session-manager-by-.patch \ - file://0015-connection-move-remaining-data-and-fds.patch \ - file://0016-protocol-improve-flushing.patch \ + file://0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \ + file://0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch \ + file://0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch \ + file://0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch \ + file://0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch \ + file://0008-meson-revert-version-check-to-require-meson-0.47-not.patch \ + file://0009-extensions-implement-new-session-manager-extension.patch \ + file://0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch \ + file://0011-daemon-config-remote-load-module-session-manager-by-.patch \ + file://0012-audioconvert-always-assume-that-output-ports-are-NOT.patch \ " -SRCREV = "d3c7acb137134bddff3bc8a8964600252d3fb674" +SRCREV = "5693d72fcb0a0290faedcce64c57a3820a5cc660" -PV = "0.2.90+git${SRCPV}+2" +PV = "0.2.91+git${SRCPV}+1" S = "${WORKDIR}/git" RDEPENDS_${PN} += "virtual/pipewire-sessionmanager virtual/pipewire-config" -- cgit 1.2.3-korg