aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2019-11-18 16:25:34 +0200
committerGeorge Kiagiadakis <george.kiagiadakis@collabora.com>2019-11-19 19:09:05 +0200
commit14aa797951f9596334c03d6fd325a96fe9f465bb (patch)
tree8a73e02ba60ff6c4d3054be9d23a565d93d152ec
parentbfeb4ebec5e4d4ac4f35d10b2ddf05db24ca2a54 (diff)
pipewire: update to master as of Nov 19th 2019
* Refresh patches * Remove endpoint API patches, which have been merged * Remove log timestamp patch, which has been merged * Added a patch to module-access to avoid all those false-positive security errors in the journal * Update URLs to point to gitlab.freedesktop.org now that the project has moved * Switch from gitsm to git, since the project no longer makes use of submodules Bug-AGL: SPEC-2837 Change-Id: I53ef9548e48827b00595162c0a30e12b302eefd9 Signed-off-by: George Kiagiadakis <george.kiagiadakis@collabora.com>
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in2
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-meson-revert-version-check-to-require-meson-0.47-not.patch (renamed from meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-meson-revert-version-check-to-require-meson-0.47-not.patch)6
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch (renamed from meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch)8
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch52
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch7
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0004-gst-pwaudioringbuffer-request-pause-play-on-the-appr.patch7
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0005-gst-pwaudioringbuffer-wait-only-for-STREAM_STATE_CON.patch7
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0006-gst-pwaudiosink-set-the-default-latency-time-buffer-.patch7
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0007-gst-pwaudioringbuffer-set-node.latency-to-get-schedu.patch7
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-audioconvert-always-assume-that-output-ports-are-NOT.patch (renamed from meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch)8
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch5715
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-module-access-add-same-sec-label-mode.patch94
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch167
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch37
-rw-r--r--meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb15
15 files changed, 132 insertions, 6007 deletions
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in
index 24646593..6c055bcf 100644
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire-conf-agl/pipewire.conf.in
@@ -10,7 +10,7 @@ load-module libpipewire-module-protocol-native
load-module libpipewire-module-spa-node-factory
load-module libpipewire-module-client-node
load-module libpipewire-module-client-device
-load-module libpipewire-module-access
+load-module libpipewire-module-access same-sec-label-mode=1
load-module libpipewire-module-adapter
load-module libpipewire-module-link-factory
load-module libpipewire-module-session-manager
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/0001-meson-revert-version-check-to-require-meson-0.47-not.patch
index 462a680e..80a2b39e 100644
--- 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/0001-meson-revert-version-check-to-require-meson-0.47-not.patch
@@ -1,4 +1,4 @@
-From 6b65e729e7609b75f10cbb8e9db736f6273ea01b Mon Sep 17 00:00:00 2001
+From 8d448cc2d3bd141742ede4636d2208f4dedc16f6 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Sun, 22 Sep 2019 17:59:19 +0300
Subject: [PATCH] meson: revert version check to require meson 0.47, not 0.50
@@ -13,7 +13,7 @@ Upstream-Status: Inappropriate [workaround]
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/meson.build b/meson.build
-index c0bb7470..07117cbb 100644
+index 2734b0d2..c9da6b4d 100644
--- a/meson.build
+++ b/meson.build
@@ -1,7 +1,7 @@
@@ -26,5 +26,5 @@ index c0bb7470..07117cbb 100644
'c_std=gnu99',
'buildtype=debugoptimized' ])
--
-2.23.0
+2.24.0
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch
index b42c3330..3c4e45f4 100644
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0001-arm-build-with-mno-unaligned-access.patch
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-arm-build-with-mno-unaligned-access.patch
@@ -1,16 +1,16 @@
-From 9bf3ae655b4cb7a3a91a9c77e006ec72864a6e1a Mon Sep 17 00:00:00 2001
+From c55e7b8e0028686afb74f4444b88c9e1dd6605c9 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Mon, 24 Jun 2019 12:19:20 +0300
Subject: [PATCH] arm: build with -mno-unaligned-access
Upstream-Status: Inappropriate [workaround]
-See also https://github.com/PipeWire/pipewire/issues/161
+See also https://gitlab.freedesktop.org/pipewire/pipewire/issues/161
---
meson.build | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/meson.build b/meson.build
-index 74455378..c0bb7470 100644
+index c9da6b4d..5c121339 100644
--- a/meson.build
+++ b/meson.build
@@ -52,6 +52,11 @@ if cc.get_id() == 'gcc'
@@ -26,5 +26,5 @@ index 74455378..c0bb7470 100644
sse2_args = '-msse2'
ssse3_args = '-mssse3'
--
-2.23.0
+2.24.0
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
deleted file mode 100644
index 714ceb9a..00000000
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0002-logger-print-timestamps-on-logged-messages.patch
+++ /dev/null
@@ -1,52 +0,0 @@
-From aef31f71f7dd458b5c26509cea09f81bab89ee84 Mon Sep 17 00:00:00 2001
-From: George Kiagiadakis <george.kiagiadakis@collabora.com>
-Date: Wed, 3 Jul 2019 17:47:46 +0300
-Subject: [PATCH] logger: print timestamps on logged messages
-
-Timestamps have usec precision and the seconds are limited
-to 9 digits. Usually what matters in these messages is to spot
-delays between printouts and not really what is the absolute
-time of the system.
-
-Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/164]
----
- spa/plugins/support/logger.c | 9 +++++++--
- 1 file changed, 7 insertions(+), 2 deletions(-)
-
-diff --git a/spa/plugins/support/logger.c b/spa/plugins/support/logger.c
-index 9ed2896b..4100102c 100644
---- a/spa/plugins/support/logger.c
-+++ b/spa/plugins/support/logger.c
-@@ -27,6 +27,7 @@
- #include <string.h>
- #include <errno.h>
- #include <stdio.h>
-+#include <time.h>
-
- #include <spa/support/log.h>
- #include <spa/support/loop.h>
-@@ -72,6 +73,9 @@ impl_log_logv(void *object,
- const char *prefix = "", *suffix = "";
- int size;
- bool do_trace;
-+ struct timespec now;
-+
-+ clock_gettime(CLOCK_MONOTONIC_RAW, &now);
-
- if ((do_trace = (level == SPA_LOG_LEVEL_TRACE && impl->have_source)))
- level++;
-@@ -88,8 +92,9 @@ impl_log_logv(void *object,
- }
-
- vsnprintf(text, sizeof(text), fmt, args);
-- size = snprintf(location, sizeof(location), "%s[%s][%s:%i %s()] %s%s\n",
-- prefix, levels[level], strrchr(file, '/') + 1, line, func, text, suffix);
-+ size = snprintf(location, sizeof(location), "%s[%s][%09lu.%06lu][%s:%i %s()] %s%s\n",
-+ prefix, levels[level], now.tv_sec & 0x1FFFFFFF, now.tv_nsec / 1000,
-+ strrchr(file, '/') + 1, line, func, text, suffix);
-
- if (SPA_UNLIKELY(do_trace)) {
- uint32_t index;
---
-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
index b8ffbde5..bca551ca 100644
--- 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
@@ -1,4 +1,4 @@
-From e2426512322768a2d386740845c80e282aba5a6a Mon Sep 17 00:00:00 2001
+From 4696c0071d7a713d3e415f7d0cf29364a2b19aa2 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Tue, 19 Feb 2019 18:23:19 +0200
Subject: [PATCH] gst: Implement new pwaudio{src,sink} elements, based on
@@ -14,7 +14,8 @@ These are much more reliable elements to use for audio data.
Both elements share a common ringbuffer that actually implements
the pipewire integration.
-Upstream-Status: Submitted [https://github.com/PipeWire/pipewire/pull/140]
+Upstream-Status: Denied
+See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
---
src/gst/gstpipewire.c | 8 +-
src/gst/gstpwaudioringbuffer.c | 542 +++++++++++++++++++++++++++++++++
@@ -1245,5 +1246,5 @@ index ad0e0801..0e922347 100644
pipewire_gst_c_args = [
--
-2.23.0
+2.24.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
index bc6559f9..29febbfc 100644
--- 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
@@ -1,4 +1,4 @@
-From d2cd5c499528dcd0b6c25a4050a6b4d76c4aa459 Mon Sep 17 00:00:00 2001
+From 1027efb2ced95a9548007ccc2f07805d48073846 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Thu, 11 Jul 2019 16:21:17 +0300
Subject: [PATCH] gst/pwaudioringbuffer: request pause/play on the appropriate
@@ -14,7 +14,8 @@ 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]
+Upstream-Status: Denied
+See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
---
src/gst/gstpwaudioringbuffer.c | 27 +++++++++++++++++++++++----
1 file changed, 23 insertions(+), 4 deletions(-)
@@ -72,5 +73,5 @@ index 989b2cd7..97350f38 100644
}
pw_thread_loop_signal (self->main_loop, FALSE);
--
-2.23.0
+2.24.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
index eef08af8..66fd4247 100644
--- 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
@@ -1,4 +1,4 @@
-From df47442f5396897f8bdba5a37699d21f0e4da85f Mon Sep 17 00:00:00 2001
+From ea2ba0fcf561fecd7e62e80e2c34bf6bd23f917b Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Thu, 11 Jul 2019 16:34:35 +0300
Subject: [PATCH] gst/pwaudioringbuffer: wait only for STREAM_STATE_CONFIGURE
@@ -9,7 +9,8 @@ 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]
+Upstream-Status: Denied
+See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
---
src/gst/gstpwaudioringbuffer.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -31,5 +32,5 @@ index 97350f38..3efec6ec 100644
pw_thread_loop_unlock (self->main_loop);
--
-2.23.0
+2.24.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
index 1f56b1c4..b3c35fe5 100644
--- 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
@@ -1,4 +1,4 @@
-From 30622ed39774d3a2d3133effbc73caac5fb12596 Mon Sep 17 00:00:00 2001
+From cc5ad0de4afb4c283e3c6b373b5fd891f9c19381 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Thu, 11 Jul 2019 17:07:15 +0300
Subject: [PATCH] gst/pwaudiosink: set the default latency time (buffer size)
@@ -9,7 +9,8 @@ This is to solve underrun issues that seem to appear with the default
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]
+Upstream-Status: Denied
+See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
---
src/gst/gstpwaudiosink.c | 7 +++++++
1 file changed, 7 insertions(+)
@@ -33,5 +34,5 @@ index 6cb71385..069996c3 100644
static void
--
-2.23.0
+2.24.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
index c42e56b1..1f542cb4 100644
--- 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
@@ -1,10 +1,11 @@
-From 23e8913172cb50d8023aaa7522e196d087f9c785 Mon Sep 17 00:00:00 2001
+From 3357a7d5bfb32a26f6c4fd3df2ea3a8f04f20770 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
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
+Upstream-Status: Denied
+See https://gitlab.freedesktop.org/pipewire/pipewire/merge_requests/140
---
src/gst/gstpwaudioringbuffer.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
@@ -37,5 +38,5 @@ index 3efec6ec..8136b815 100644
pw_thread_loop_lock (self->main_loop);
--
-2.23.0
+2.24.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/0008-audioconvert-always-assume-that-output-ports-are-NOT.patch
index 06ffb3d7..b20881dd 100644
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0012-audioconvert-always-assume-that-output-ports-are-NOT.patch
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0008-audioconvert-always-assume-that-output-ports-are-NOT.patch
@@ -1,4 +1,4 @@
-From a95e87cd47de3585d9f4ef77b75c4a077bd95f78 Mon Sep 17 00:00:00 2001
+From c4028fd5d07bd6f901ca76cd7218150c658193f8 Mon Sep 17 00:00:00 2001
From: George Kiagiadakis <george.kiagiadakis@collabora.com>
Date: Wed, 2 Oct 2019 21:40:34 +0300
Subject: [PATCH] audioconvert: always assume that output ports are NOT monitor
@@ -14,10 +14,10 @@ Upstream-Status: Inappropriate [workaround]
1 file changed, 4 insertions(+)
diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c
-index 0859bf73..ac4dceef 100644
+index 74a62a35..72da37d1 100644
--- a/spa/plugins/audioconvert/audioconvert.c
+++ b/spa/plugins/audioconvert/audioconvert.c
-@@ -109,8 +109,12 @@ struct impl {
+@@ -113,8 +113,12 @@ struct impl {
unsigned int add_listener:1;
};
@@ -31,5 +31,5 @@ index 0859bf73..ac4dceef 100644
static void emit_node_info(struct impl *this, bool full)
{
--
-2.23.0
+2.24.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
deleted file mode 100644
index 325ade7a..00000000
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-extensions-implement-new-session-manager-extension.patch
+++ /dev/null
@@ -1,5715 +0,0 @@
-From cc47e191c42b836811c5fca1122505375a4e080b Mon Sep 17 00:00:00 2001
-From: George Kiagiadakis <george.kiagiadakis@collabora.com>
-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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H
-+#define PIPEWIRE_EXT_SESSION_MANAGER_IMPL_INTERFACES_H
-+
-+#include <spa/utils/defs.h>
-+#include <spa/utils/hook.h>
-+#include <errno.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H
-+#define PIPEWIRE_EXT_SESSION_MANAGER_INTERFACES_H
-+
-+#include <spa/utils/defs.h>
-+#include <spa/utils/hook.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H
-+#define PIPEWIRE_EXT_SESSION_MANAGER_INTROSPECT_H
-+
-+#include <spa/utils/defs.h>
-+#include <spa/utils/dict.h>
-+#include <spa/param/param.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include "config.h"
-+
-+#include <pipewire/pipewire.h>
-+
-+/* 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 <george.kiagiadakis@collabora.com>" },
-+ { 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include "client-endpoint.h"
-+#include "endpoint.h"
-+#include "endpoint-stream.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include "client-session.h"
-+#include "session.h"
-+#include "endpoint-link.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include <spa/pod/filter.h>
-+
-+#include "endpoint-link.h"
-+#include "client-session.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include <spa/pod/filter.h>
-+
-+#include "endpoint-stream.h"
-+#include "client-endpoint.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include <spa/pod/filter.h>
-+
-+#include "endpoint.h"
-+#include "client-endpoint.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <pipewire/pipewire.h>
-+#include <spa/pod/parser.h>
-+
-+#include <extensions/session-manager.h>
-+#include <extensions/protocol-native.h>
-+
-+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(&param)) < 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(&param)) < 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(&params[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(&params[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(&param)) < 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(&param)) < 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(&params[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(&params[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(&param)) < 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(&param)) < 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(&param)) < 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(&param)) < 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(&param)) < 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(&param)) < 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(&param)) < 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(&param)) < 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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#include <stdbool.h>
-+#include <string.h>
-+
-+#include <pipewire/pipewire.h>
-+#include <extensions/session-manager.h>
-+
-+#include <spa/pod/filter.h>
-+
-+#include "session.h"
-+#include "client-session.h"
-+
-+#include <pipewire/private.h>
-+
-+#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 <george.kiagiadakis@collabora.com>
-+ *
-+ * Permission is hereby granted, free of charge, to any person obtaining a
-+ * copy of this software and associated documentation files (the "Software"),
-+ * to deal in the Software without restriction, including without limitation
-+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
-+ * and/or sell copies of the Software, and to permit persons to whom the
-+ * Software is furnished to do so, subject to the following conditions:
-+ *
-+ * The above copyright notice and this permission notice (including the next
-+ * paragraph) shall be included in all copies or substantial portions of the
-+ * Software.
-+ *
-+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
-+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-+ * DEALINGS IN THE SOFTWARE.
-+ */
-+
-+#ifndef 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/0009-module-access-add-same-sec-label-mode.patch b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-module-access-add-same-sec-label-mode.patch
new file mode 100644
index 00000000..f0fc11b2
--- /dev/null
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire/0009-module-access-add-same-sec-label-mode.patch
@@ -0,0 +1,94 @@
+From 7bde580db9b67c503de90211d344b15b933fecbe Mon Sep 17 00:00:00 2001
+From: George Kiagiadakis <george.kiagiadakis@collabora.com>
+Date: Tue, 19 Nov 2019 17:09:07 +0200
+Subject: [PATCH] module-access: add same-sec-label-mode
+
+This is a mode where the access module allows all clients that have
+the same security label as the pipewire daemon, and every other
+client is put on the restricted state.
+
+In systems that use SMACK security labels, such as AGL, this allows
+the session manager (which is spawned by pipewire, inheriting the
+same smack label) to have full access to all objects, while every
+other client is restricted and the session manager must decide
+what to do with it
+
+Note that while this option is configurable, there is no loss of
+security if this option is not set in the configuration. Clients
+that don't have the same security context will be considered to
+be flatpak clients because pipewire will not be able to open
+/proc/pid/cmdline. This however results in some unwanted error
+messages that may be confusing.
+
+Upstream-Status: Inappropriate [agl/smack specific]
+---
+ src/modules/module-access.c | 45 ++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/src/modules/module-access.c b/src/modules/module-access.c
+index 09dafa43..f75306d9 100644
+--- a/src/modules/module-access.c
++++ b/src/modules/module-access.c
+@@ -50,6 +50,30 @@ struct impl {
+ struct spa_hook module_listener;
+ };
+
++static int check_seclabel(const char *str)
++{
++ char attr[1024];
++ int fd, len;
++
++ fd = open("/proc/self/attr/current", O_RDONLY);
++ if (fd < 0)
++ return -errno;
++
++ if ((len = read(fd, attr, 1024)) <= 0) {
++ close(fd);
++ return -EIO;
++ }
++ attr[len] = '\0';
++
++ if (strcmp(attr, str) == 0) {
++ close(fd);
++ return 1;
++ }
++
++ close(fd);
++ return 0;
++}
++
+ static int check_cmdline(struct pw_client *client, int pid, const char *str)
+ {
+ char path[2048];
+@@ -121,8 +145,27 @@ core_check_access(void *data, struct pw_client *client)
+ const char *str;
+ int pid, res;
+
++ props = pw_client_get_properties(client);
++
++ if (impl->properties &&
++ (str = pw_properties_get(impl->properties, "same-sec-label-mode")) != NULL &&
++ strcmp(str, "1") == 0) {
++ if (props && (str = pw_properties_get(props, PW_KEY_SEC_LABEL)) != NULL) {
++ res = check_seclabel(str);
++ if (res == 1)
++ goto granted;
++ else if (res < 0)
++ pw_log_warn("module %p: client %p seclabel check failed: %s",
++ impl, client, spa_strerror(res));
++ }
++ pw_log_debug("module %p: seclabel restricted client %p added",
++ impl, client);
++ items[0] = SPA_DICT_ITEM_INIT(PW_KEY_ACCESS, "restricted");
++ goto wait_permissions;
++ }
++
+ pid = -EINVAL;
+- if ((props = pw_client_get_properties(client)) != NULL) {
++ if (props != NULL) {
+ if ((str = pw_properties_get(props, PW_KEY_SEC_PID)) != NULL)
+ pid = atoi(str);
+ }
+--
+2.24.0
+
diff --git a/meta-pipewire/recipes-multimedia/pipewire/pipewire/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
deleted file mode 100644
index 0456f84a..00000000
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0010-pipewire-cli-add-support-for-printing-endpoint-info-.patch
+++ /dev/null
@@ -1,167 +0,0 @@
-From 76865803c2db13b753e1261e01de22760c7b398b Mon Sep 17 00:00:00 2001
-From: George Kiagiadakis <george.kiagiadakis@collabora.com>
-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 <pipewire/type.h>
- #include <pipewire/permission.h>
-
-+#include <extensions/session-manager.h>
-+
- 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
deleted file mode 100644
index e42e62a2..00000000
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire/0011-daemon-config-remote-load-module-session-manager-by-.patch
+++ /dev/null
@@ -1,37 +0,0 @@
-From f451147ffc1f89f4c0f705ac5c444e614dbff22a Mon Sep 17 00:00:00 2001
-From: George Kiagiadakis <george.kiagiadakis@collabora.com>
-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_git.bb b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb
index ce7e523c..ec3a0572 100644
--- a/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb
+++ b/meta-pipewire/recipes-multimedia/pipewire/pipewire_git.bb
@@ -1,21 +1,18 @@
require pipewire.inc
-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 \
+SRC_URI = "git://gitlab.freedesktop.org/pipewire/pipewire.git;protocol=https;branch=master \
+ file://0001-meson-revert-version-check-to-require-meson-0.47-not.patch \
+ file://0002-arm-build-with-mno-unaligned-access.patch \
file://0003-gst-Implement-new-pwaudio-src-sink-elements-based-on.patch \
file://0004-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 \
+ file://0008-audioconvert-always-assume-that-output-ports-are-NOT.patch \
+ file://0009-module-access-add-same-sec-label-mode.patch \
"
-SRCREV = "5693d72fcb0a0290faedcce64c57a3820a5cc660"
+SRCREV = "e18a24493a254c881a1bda384fdcd70cd671fd1c"
PV = "0.2.91+git${SRCPV}+1"
S = "${WORKDIR}/git"