baresip/baresip-3.0.0-pipewire.patch
Robert Scheck 5e6179159d - Upgrade to 3.0.0 (#2180064)
- Added (hopefully future upstream) patch for PipeWire support
2023-03-20 17:20:22 +01:00

1231 lines
31 KiB
Diff

From 5c473db1b9606134b400c0b1622df8740b16a0fc Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Thu, 2 Feb 2023 08:52:54 +0100
Subject: [PATCH 01/10] pipewire: add pipewire module
---
cmake/FindPIPEWIRE.cmake | 10 ++
cmake/modules.cmake | 4 +
modules/pipewire/CMakeLists.txt | 17 ++++
modules/pipewire/capture.c | 163 ++++++++++++++++++++++++++++++++
modules/pipewire/pipewire.c | 161 +++++++++++++++++++++++++++++++
modules/pipewire/pipewire.h | 17 ++++
modules/pipewire/playback.c | 163 ++++++++++++++++++++++++++++++++
7 files changed, 535 insertions(+)
create mode 100644 cmake/FindPIPEWIRE.cmake
create mode 100644 modules/pipewire/CMakeLists.txt
create mode 100644 modules/pipewire/capture.c
create mode 100644 modules/pipewire/pipewire.c
create mode 100644 modules/pipewire/pipewire.h
create mode 100644 modules/pipewire/playback.c
diff --git a/cmake/FindPIPEWIRE.cmake b/cmake/FindPIPEWIRE.cmake
new file mode 100644
index 000000000..bd9b90181
--- /dev/null
+++ b/cmake/FindPIPEWIRE.cmake
@@ -0,0 +1,10 @@
+# Find the system's pipewire includes and library
+#
+# PIPEWIRE_INCLUDE_DIRS - where to find pipewire.h
+# PIPEWIRE_LIBRARIES - List of libraries when using pipewire
+# PIPEWIRE_FOUND - True if pipewire found
+
+if(NOT WIN32)
+ find_package(PkgConfig)
+ pkg_search_module(PIPEWIRE libpipewire-0.3)
+endif()
diff --git a/cmake/modules.cmake b/cmake/modules.cmake
index ed99bf835..e68835595 100644
--- a/cmake/modules.cmake
+++ b/cmake/modules.cmake
@@ -20,6 +20,7 @@ find_package(OPUS)
find_package(PNG)
find_package(PORTAUDIO)
find_package(PULSE)
+find_package(PIPEWIRE)
find_package(SDL)
find_package(SNDFILE)
find_package(SPANDSP)
@@ -140,6 +141,9 @@ endif()
if(PULSE_FOUND)
list(APPEND MODULES pulse)
endif()
+if(PIPEWIRE_FOUND)
+ list(APPEND MODULES pipewire)
+endif()
if(SDL_FOUND)
list(APPEND MODULES sdl)
endif()
diff --git a/modules/pipewire/CMakeLists.txt b/modules/pipewire/CMakeLists.txt
new file mode 100644
index 000000000..8673873af
--- /dev/null
+++ b/modules/pipewire/CMakeLists.txt
@@ -0,0 +1,17 @@
+project(pipewire)
+
+set(SRCS pipewire.c playback.c capture.c)
+
+if(STATIC)
+ add_library(${PROJECT_NAME} OBJECT ${SRCS})
+else()
+ add_library(${PROJECT_NAME} MODULE ${SRCS})
+endif()
+
+target_include_directories(${PROJECT_NAME} PRIVATE ${PIPEWIRE_INCLUDE_DIRS})
+target_link_directories(${PROJECT_NAME} PRIVATE ${PIPEWIRE_LIBRARY_DIRS})
+target_link_libraries(${PROJECT_NAME} PRIVATE ${PIPEWIRE_LIBRARIES})
+target_compile_options(${PROJECT_NAME} PRIVATE
+ -Wno-pedantic
+ -Wno-bad-function-cast
+)
diff --git a/modules/pipewire/capture.c b/modules/pipewire/capture.c
new file mode 100644
index 000000000..3e92d131c
--- /dev/null
+++ b/modules/pipewire/capture.c
@@ -0,0 +1,163 @@
+/**
+ * @file capture.c Pipewire sound driver - capture
+ *
+ * Copyright (C) 2023 Commend.com - c.spielberger@commend.com
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <spa/param/audio/format-utils.h>
+#include <pipewire/pipewire.h>
+
+#include "pipewire.h"
+
+
+struct ausrc_st {
+ struct pw_stream *stream;
+
+ struct ausrc_prm prm;
+ ausrc_read_h *rh;
+ struct spa_hook listener;
+ ausrc_error_h *errh;
+
+ size_t sampsz;
+ uint64_t samps;
+
+ void *arg;
+};
+
+
+static void on_process(void *arg);
+
+static const struct pw_stream_events stream_events = {
+ PW_VERSION_STREAM_EVENTS,
+ .process = on_process,
+};
+
+
+static void ausrc_destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+
+ st->rh = NULL;
+ st->errh = NULL;
+ pw_stream_destroy(st->stream);
+}
+
+
+int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
+ struct ausrc_prm *prm, const char *dev, ausrc_read_h *rh,
+ ausrc_error_h *errh, void *arg)
+{
+ struct ausrc_st *st;
+ const struct spa_pod *params[1];
+ uint8_t buffer[1024];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer,
+ sizeof(buffer));
+ const char name[] = "baresip-capture";
+ int err = 0;
+
+ if (!stp || !as || !prm || !rh)
+ return EINVAL;
+
+ info ("pipewire: opening capture(%u Hz, %d channels,"
+ "device '%s')\n", prm->srate, prm->ch, dev);
+
+ st = mem_zalloc(sizeof(*st), ausrc_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->prm.srate = prm->srate;
+ st->prm.ch = prm->ch;
+ st->prm.ptime = prm->ptime;
+ st->prm.fmt = prm->fmt;
+
+ st->sampsz = aufmt_sample_size(prm->fmt);
+ st->samps = 0;
+
+ st->rh = rh;
+ st->errh = errh;
+ st->arg = arg;
+
+ pw_thread_loop_lock (pw_loop_instance());
+ st->stream = pw_stream_new(pw_core_instance(), name,
+ pw_properties_new(
+ PW_KEY_MEDIA_TYPE, "Audio",
+ PW_KEY_MEDIA_CATEGORY, "Capture",
+ PW_KEY_MEDIA_ROLE, "Communication",
+ NULL));
+ if (!st->stream) {
+ err = errno;
+ goto out;
+ }
+
+ pw_stream_add_listener(st->stream, &st->listener, &stream_events, st);
+ params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
+ &SPA_AUDIO_INFO_RAW_INIT(
+ .format = aufmt_to_pw_format(prm->fmt),
+ .channels = prm->ch,
+ .rate = prm->srate ));
+ if (!params[0])
+ goto out;
+
+ err = pw_stream_connect(st->stream,
+ PW_DIRECTION_INPUT,
+ PW_ID_ANY,
+ PW_STREAM_FLAG_AUTOCONNECT |
+ PW_STREAM_FLAG_MAP_BUFFERS |
+ PW_STREAM_FLAG_RT_PROCESS,
+ params, 1);
+
+ pw_thread_loop_unlock(pw_loop_instance());
+
+ info ("pipewire: stream %s started (%m)\n", name, err);
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
+
+
+/**
+ * Pipewire process callback
+ *
+ * @param arg Argument (ausrc_st object)
+ */
+static void on_process(void *arg)
+{
+ struct ausrc_st *st = arg;
+ struct pw_buffer *b;
+ struct spa_buffer *buf;
+ struct auframe af;
+
+ void *sampv;
+ size_t sampc;
+
+ b = pw_stream_dequeue_buffer(st->stream);
+ if (!b)
+ warning("pipewire: out of buffers (%m)\n", errno);
+
+ buf = b->buffer;
+ sampv = buf->datas[0].data;
+ if (!sampv)
+ return;
+
+ sampc = buf->datas[0].chunk->size / st->sampsz;
+
+ auframe_init(&af, st->prm.fmt, sampv, sampc,
+ st->prm.srate, st->prm.ch);
+
+ af.timestamp = st->samps * AUDIO_TIMEBASE /
+ (st->prm.srate * st->prm.ch);
+ st->samps += sampc;
+ st->rh(&af, st->arg);
+
+ pw_stream_queue_buffer(st->stream, b);
+}
diff --git a/modules/pipewire/pipewire.c b/modules/pipewire/pipewire.c
new file mode 100644
index 000000000..c9e6aaff4
--- /dev/null
+++ b/modules/pipewire/pipewire.c
@@ -0,0 +1,161 @@
+/**
+ * @file pipewire.c Pipewire sound driver
+ *
+ * Copyright (C) 2023 Commend.com - c.spielberger@commend.com
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <spa/param/audio/raw.h>
+#include <pipewire/pipewire.h>
+
+#include "pipewire.h"
+
+/**
+ * @defgroup pipewire pipewire
+ *
+ * Audio driver module for Pipewire
+ *
+ */
+
+enum {
+ RECONN_DELAY = 1500,
+};
+
+
+struct pw_stat {
+ struct pw_thread_loop *loop;
+ struct pw_context *context;
+ struct pw_core *core;
+};
+
+
+static struct pw_stat *d = NULL;
+
+static struct auplay *auplay = NULL;
+static struct ausrc *ausrc = NULL;
+
+
+static void destructor(void *arg)
+{
+ struct pw_stat *pw = arg;
+
+ if (pw->core)
+ pw_core_disconnect(pw->core);
+
+ if (pw->context)
+ pw_context_destroy(pw->context);
+
+ if (pw->loop) {
+ pw_thread_loop_stop(pw->loop);
+ pw_thread_loop_destroy(pw->loop);
+ }
+}
+
+
+static struct pw_stat *pw_stat_alloc(void)
+{
+ struct pw_stat *pw;
+ int err;
+
+ pw = mem_zalloc(sizeof(*pw), destructor);
+
+ pw->loop = pw_thread_loop_new("baresip pipewire", NULL);
+ if (!pw->loop)
+ goto errout;
+
+ err = pw_thread_loop_start(pw->loop);
+ if (err)
+ goto errout;
+
+ pw->context = pw_context_new(pw_thread_loop_get_loop(pw->loop),
+ NULL /* properties */,
+ 0 /* user_data size */);
+ if (!pw->context)
+ goto errout;
+
+ pw->core = pw_context_connect(pw->context,
+ NULL /* properties */,
+ 0 /* user_data size */);
+ if (!pw->core)
+ goto errout;
+
+ info("pipewire: connected to pipewire\n");
+ return pw;
+
+errout:
+ warning("pipewire: could not connect to pipewire\n");
+ mem_deref(pw);
+ return NULL;
+}
+
+
+struct pw_core *pw_core_instance(void)
+{
+ if (!d)
+ return NULL;
+
+ return d->core;
+}
+
+
+struct pw_thread_loop *pw_loop_instance(void)
+{
+ if (!d)
+ return NULL;
+
+ return d->loop;
+}
+
+
+int aufmt_to_pw_format(enum aufmt fmt)
+{
+ switch (fmt) {
+ case AUFMT_S16LE: return SPA_AUDIO_FORMAT_S16_LE;
+ case AUFMT_FLOAT: return SPA_AUDIO_FORMAT_F32;
+ default: return 0;
+ }
+}
+
+
+static int module_init(void)
+{
+ int err = 0;
+
+ pw_init(NULL, NULL);
+ info("pipewire: headers %s library %s \n",
+ pw_get_headers_version(), pw_get_library_version());
+
+ d = pw_stat_alloc();
+ if (!d)
+ return errno;
+
+ err = auplay_register(&auplay, baresip_auplayl(),
+ "pipewire", pw_playback_alloc);
+ err |= ausrc_register(&ausrc, baresip_ausrcl(),
+ "pipewire", pw_capture_alloc);
+
+ return err;
+}
+
+
+static int module_close(void)
+{
+ auplay = mem_deref(auplay);
+ ausrc = mem_deref(ausrc);
+
+ d = mem_deref(d);
+ pw_deinit();
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(pipewire) = {
+ "pipewire",
+ "audio",
+ module_init,
+ module_close,
+};
diff --git a/modules/pipewire/pipewire.h b/modules/pipewire/pipewire.h
new file mode 100644
index 000000000..0a93aaf52
--- /dev/null
+++ b/modules/pipewire/pipewire.h
@@ -0,0 +1,17 @@
+/**
+ * @file pipewire.h Pipewire sound driver - internal API
+ *
+ * Copyright (C) 2023 Commend.com - c.spielberger@commend.com
+ */
+
+int aufmt_to_pw_format(enum aufmt fmt);
+struct pw_core *pw_core_instance(void);
+struct pw_thread_loop *pw_loop_instance(void);
+
+int pw_playback_alloc(struct auplay_st **stp,
+ const struct auplay *ap,
+ struct auplay_prm *prm, const char *dev,
+ auplay_write_h *wh, void *arg);
+int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
+ struct ausrc_prm *prm, const char *dev, ausrc_read_h *rh,
+ ausrc_error_h *errh, void *arg);
diff --git a/modules/pipewire/playback.c b/modules/pipewire/playback.c
new file mode 100644
index 000000000..94fceb1b0
--- /dev/null
+++ b/modules/pipewire/playback.c
@@ -0,0 +1,163 @@
+/**
+ * @file playback.c Pipewire sound driver - playback
+ *
+ * Copyright (C) 2023 Commend.com - c.spielberger@commend.com
+ */
+
+#include <string.h>
+#include <errno.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include <spa/param/audio/format-utils.h>
+#include <pipewire/pipewire.h>
+
+#include "pipewire.h"
+
+struct auplay_st {
+ struct pw_stream *stream;
+
+ struct auplay_prm prm;
+ auplay_write_h *wh;
+ struct spa_hook listener;
+
+ size_t sampc;
+ size_t nbytes;
+ int32_t stride;
+
+ void *arg;
+};
+
+static void on_process(void *arg);
+
+static const struct pw_stream_events stream_events = {
+ PW_VERSION_STREAM_EVENTS,
+ .process = on_process,
+};
+
+
+static void auplay_destructor(void *arg)
+{
+ struct auplay_st *st = arg;
+
+ st->wh = NULL;
+ pw_stream_destroy(st->stream);
+}
+
+
+int pw_playback_alloc(struct auplay_st **stp, const struct auplay *ap,
+ struct auplay_prm *prm, const char *dev, auplay_write_h *wh, void *arg)
+{
+ struct auplay_st *st;
+ const struct spa_pod *params[1];
+ uint8_t buffer[1024];
+ struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer,
+ sizeof(buffer));
+ const char name[] = "baresip-playback";
+ size_t sampsz;
+ int err = 0;
+
+ if (!stp || !ap || !prm || !wh)
+ return EINVAL;
+
+ info ("pipewire: opening playback (%u Hz, %d channels, device %s, "
+ "ptime %u)\n", prm->srate, prm->ch, dev, prm->ptime);
+
+ st = mem_zalloc(sizeof(*st), auplay_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->prm.srate = prm->srate;
+ st->prm.ch = prm->ch;
+ st->prm.ptime = prm->ptime;
+ st->prm.fmt = prm->fmt;
+
+ sampsz = aufmt_sample_size(prm->fmt);
+ st->sampc = st->prm.ptime * st->prm.ch * st->prm.srate / 1000;
+ st->nbytes = st->sampc * sampsz;
+ st->stride = sampsz * prm->ch;
+
+ st->wh = wh;
+ st->arg = arg;
+
+ pw_thread_loop_lock (pw_loop_instance());
+ st->stream = pw_stream_new(pw_core_instance(), name,
+ pw_properties_new(
+ PW_KEY_MEDIA_TYPE, "Audio",
+ PW_KEY_MEDIA_CATEGORY, "Playback",
+ PW_KEY_MEDIA_ROLE, "Communication",
+ NULL));
+ if (!st->stream) {
+ err = errno;
+ goto out;
+ }
+
+ pw_stream_add_listener(st->stream, &st->listener, &stream_events, st);
+ params[0] = spa_format_audio_raw_build(&b, SPA_PARAM_EnumFormat,
+ &SPA_AUDIO_INFO_RAW_INIT(
+ .format = aufmt_to_pw_format(prm->fmt),
+ .channels = prm->ch,
+ .rate = prm->srate ));
+ if (!params[0])
+ goto out;
+
+ err = pw_stream_connect(st->stream,
+ PW_DIRECTION_OUTPUT,
+ PW_ID_ANY,
+ PW_STREAM_FLAG_AUTOCONNECT |
+ PW_STREAM_FLAG_MAP_BUFFERS |
+ PW_STREAM_FLAG_RT_PROCESS,
+ params, 1);
+
+ pw_thread_loop_unlock(pw_loop_instance());
+
+ info ("pipewire: stream %s started (%m)\n", name, err);
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
+
+
+/**
+ * Pipewire process callback
+ *
+ * @param arg Argument (auplay_st object)
+ */
+static void on_process(void *arg)
+{
+ struct auplay_st *st = arg;
+ struct pw_buffer *b;
+ struct spa_buffer *buf;
+ struct auframe af;
+ void *sampv;
+
+ b = pw_stream_dequeue_buffer(st->stream);
+ if (!b)
+ warning("pipewire: out of buffers (%m)\n", errno);
+
+ buf = b->buffer;
+ sampv = buf->datas[0].data;
+ if (!sampv)
+ return;
+
+ if (buf->datas[0].maxsize < st->nbytes) {
+ warning("pipewire: buffer to small\n");
+ return;
+ }
+
+ auframe_init(&af, st->prm.fmt, sampv, st->sampc,
+ st->prm.srate, st->prm.ch);
+
+ st->wh(&af, st->arg);
+
+ buf->datas[0].chunk->offset = 0;
+ buf->datas[0].chunk->stride = st->stride;
+ buf->datas[0].chunk->size = auframe_size(&af);
+
+ pw_stream_queue_buffer(st->stream, b);
+}
From 2c17a323ec53a9512ea2374e86b73057cecff9c5 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Thu, 2 Feb 2023 10:57:15 +0100
Subject: [PATCH 02/10] cmake: repair FindPIPEWIRE.cmake
---
cmake/FindPIPEWIRE.cmake | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/cmake/FindPIPEWIRE.cmake b/cmake/FindPIPEWIRE.cmake
index bd9b90181..20fa46a6f 100644
--- a/cmake/FindPIPEWIRE.cmake
+++ b/cmake/FindPIPEWIRE.cmake
@@ -8,3 +8,7 @@ if(NOT WIN32)
find_package(PkgConfig)
pkg_search_module(PIPEWIRE libpipewire-0.3)
endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PIPEWIRE DEFAULT_MSG PIPEWIRE_INCLUDE_DIRS
+ PIPEWIRE_LIBRARIES)
From fc4496f2b527a9216b59a2c2a1d2938b3eedef9f Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Thu, 2 Feb 2023 14:19:41 +0100
Subject: [PATCH 03/10] pipewire: set node latency and use chunk offset
---
modules/pipewire/capture.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/modules/pipewire/capture.c b/modules/pipewire/capture.c
index 3e92d131c..697845e7d 100644
--- a/modules/pipewire/capture.c
+++ b/modules/pipewire/capture.c
@@ -58,6 +58,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buffer,
sizeof(buffer));
const char name[] = "baresip-capture";
+ char nlat[10];
int err = 0;
if (!stp || !as || !prm || !rh)
@@ -81,6 +82,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->rh = rh;
st->errh = errh;
st->arg = arg;
+ re_snprintf(nlat, sizeof(nlat), "%u/1000", prm->ptime);
pw_thread_loop_lock (pw_loop_instance());
st->stream = pw_stream_new(pw_core_instance(), name,
@@ -88,6 +90,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Communication",
+ PW_KEY_NODE_LATENCY, nlat,
NULL));
if (!st->stream) {
err = errno;
@@ -135,7 +138,10 @@ static void on_process(void *arg)
struct ausrc_st *st = arg;
struct pw_buffer *b;
struct spa_buffer *buf;
+ struct spa_data *d;
struct auframe af;
+ uint32_t offs;
+ uint32_t size;
void *sampv;
size_t sampc;
@@ -145,11 +151,15 @@ static void on_process(void *arg)
warning("pipewire: out of buffers (%m)\n", errno);
buf = b->buffer;
- sampv = buf->datas[0].data;
- if (!sampv)
+ d = &buf->datas[0];
+
+ if (!d->data)
return;
- sampc = buf->datas[0].chunk->size / st->sampsz;
+ offs = SPA_MIN(d->chunk->offset, d->maxsize);
+ size = SPA_MIN(d->chunk->size, d->maxsize - offs);
+ sampv = SPA_PTROFF(d->data, offs, void);
+ sampc = size / st->sampsz;
auframe_init(&af, st->prm.fmt, sampv, sampc,
st->prm.srate, st->prm.ch);
From 6dd765174f55bb05ad9e4a339ab8f9b5d9ad4806 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Thu, 2 Feb 2023 14:20:30 +0100
Subject: [PATCH 04/10] pipewire: playback cleanup
---
modules/pipewire/playback.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)
diff --git a/modules/pipewire/playback.c b/modules/pipewire/playback.c
index 94fceb1b0..012731570 100644
--- a/modules/pipewire/playback.c
+++ b/modules/pipewire/playback.c
@@ -133,6 +133,7 @@ static void on_process(void *arg)
struct auplay_st *st = arg;
struct pw_buffer *b;
struct spa_buffer *buf;
+ struct spa_data *d;
struct auframe af;
void *sampv;
@@ -141,11 +142,13 @@ static void on_process(void *arg)
warning("pipewire: out of buffers (%m)\n", errno);
buf = b->buffer;
- sampv = buf->datas[0].data;
- if (!sampv)
+ d = &buf->datas[0];
+
+ if (!d->data)
return;
- if (buf->datas[0].maxsize < st->nbytes) {
+ sampv = d->data;
+ if (d->maxsize < st->nbytes) {
warning("pipewire: buffer to small\n");
return;
}
@@ -155,9 +158,9 @@ static void on_process(void *arg)
st->wh(&af, st->arg);
- buf->datas[0].chunk->offset = 0;
- buf->datas[0].chunk->stride = st->stride;
- buf->datas[0].chunk->size = auframe_size(&af);
+ d->chunk->offset = 0;
+ d->chunk->stride = st->stride;
+ d->chunk->size = auframe_size(&af);
pw_stream_queue_buffer(st->stream, b);
}
From d08fcfe3cf2eb5cbc2f29958f9d1aff3ce1ecb66 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Fri, 3 Feb 2023 08:19:30 +0100
Subject: [PATCH 05/10] pipewire: device selection
---
modules/pipewire/capture.c | 3 +-
modules/pipewire/pipewire.c | 155 +++++++++++++++++++++++++++++++++---
modules/pipewire/pipewire.h | 1 +
modules/pipewire/playback.c | 3 +-
4 files changed, 151 insertions(+), 11 deletions(-)
diff --git a/modules/pipewire/capture.c b/modules/pipewire/capture.c
index 697845e7d..36f6d5d90 100644
--- a/modules/pipewire/capture.c
+++ b/modules/pipewire/capture.c
@@ -90,6 +90,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Capture",
PW_KEY_MEDIA_ROLE, "Communication",
+ PW_KEY_TARGET_OBJECT, dev,
PW_KEY_NODE_LATENCY, nlat,
NULL));
if (!st->stream) {
@@ -108,7 +109,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
err = pw_stream_connect(st->stream,
PW_DIRECTION_INPUT,
- PW_ID_ANY,
+ pw_device_id(dev),
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
diff --git a/modules/pipewire/pipewire.c b/modules/pipewire/pipewire.c
index c9e6aaff4..182420f8e 100644
--- a/modules/pipewire/pipewire.c
+++ b/modules/pipewire/pipewire.c
@@ -22,7 +22,16 @@
*/
enum {
- RECONN_DELAY = 1500,
+ RECONN_DELAY = 1500,
+ DEV_HASH_SIZE = 16,
+};
+
+
+struct pw_dev {
+ struct le he;
+
+ char *node_name;
+ uint32_t id;
};
@@ -30,19 +39,27 @@ struct pw_stat {
struct pw_thread_loop *loop;
struct pw_context *context;
struct pw_core *core;
+ struct pw_registry *registry;
+ struct spa_hook registry_listener;
+
+ struct auplay *auplay;
+ struct ausrc *ausrc;
+ struct hash *devices;
};
static struct pw_stat *d = NULL;
-static struct auplay *auplay = NULL;
-static struct ausrc *ausrc = NULL;
-
static void destructor(void *arg)
{
struct pw_stat *pw = arg;
+ mem_deref(pw->auplay);
+ mem_deref(pw->ausrc);
+ hash_flush(pw->devices);
+ mem_deref(pw->devices);
+
if (pw->core)
pw_core_disconnect(pw->core);
@@ -56,6 +73,105 @@ static void destructor(void *arg)
}
+static void pw_dev_destructor(void *arg)
+{
+ struct pw_dev *pwd = arg;
+
+ mem_deref(pwd->node_name);
+}
+
+
+static int pw_dev_add(uint32_t id, const char *node_name)
+{
+ struct pw_dev *pwd;
+ int err;
+
+ pwd = mem_zalloc(sizeof(*pwd), pw_dev_destructor);
+ if (!pwd)
+ return ENOMEM;
+
+ pwd->id = id;
+ err = str_dup(&pwd->node_name, node_name);
+ if (err) {
+ mem_deref(pwd);
+ return ENOMEM;
+ }
+
+ hash_append(d->devices, hash_joaat_str(node_name), &pwd->he, pwd);
+ return 0;
+}
+
+
+static void registry_event_global(void *arg, uint32_t id,
+ uint32_t permissions, const char *type, uint32_t version,
+ const struct spa_dict *props)
+{
+ struct pw_stat *pw = arg;
+ const char *media_class;
+ const char *node_name;
+ (void)permissions;
+ (void)version;
+
+ if (str_cmp(type, PW_TYPE_INTERFACE_Node))
+ return;
+
+ media_class = spa_dict_lookup(props, PW_KEY_MEDIA_CLASS);
+ node_name = spa_dict_lookup(props, PW_KEY_NODE_NAME);
+ if (!str_cmp(media_class, "Audio/Source") && str_isset(node_name)) {
+ debug("pipewire: adding (%u) %s: \"%s\"\n",
+ id, media_class, node_name);
+ mediadev_add(&pw->ausrc->dev_list, node_name);
+ (void)pw_dev_add(id, node_name);
+ }
+
+ if (!str_cmp(media_class, "Audio/Sink") && str_isset(node_name)) {
+ debug("pipewire: adding (%u) %s: \"%s\"\n",
+ id, media_class, node_name);
+ mediadev_add(&pw->auplay->dev_list, node_name);
+ (void)pw_dev_add(id, node_name);
+ }
+}
+
+
+static const struct pw_registry_events registry_events = {
+ PW_VERSION_REGISTRY_EVENTS,
+ .global = registry_event_global,
+};
+
+
+struct pwd_cmp {
+ const char *node_name;
+};
+
+
+static bool pw_dev_cmp(struct le *le, void *arg)
+{
+ const struct pwd_cmp *cmp = arg;
+ const struct pw_dev *pwd = le->data;
+
+ return !str_cmp(pwd->node_name, cmp->node_name);
+}
+
+
+int pw_device_id(const char *node_name)
+{
+ struct le *le;
+ struct pw_dev *pwd;
+ struct pwd_cmp cmp;
+
+ cmp.node_name = node_name;
+
+ le = hash_lookup(d->devices, hash_joaat_str(node_name),
+ pw_dev_cmp, &cmp);
+
+ if (!le || !le->data)
+ return PW_ID_ANY;
+
+ pwd = le->data;
+ return pwd->id;
+}
+
+
static struct pw_stat *pw_stat_alloc(void)
{
struct pw_stat *pw;
@@ -93,6 +209,29 @@ static struct pw_stat *pw_stat_alloc(void)
}
+static int pw_start_registry_scan(struct pw_stat *pw)
+{
+ int err;
+
+ pw->registry = pw_core_get_registry(pw->core, PW_VERSION_REGISTRY,
+ 0 /* user_data size */);
+
+ if (!pw->registry)
+ return errno;
+
+ err = hash_alloc(&pw->devices, DEV_HASH_SIZE);
+ if (err)
+ return err;
+
+ pw_thread_loop_lock (pw_loop_instance());
+ spa_zero(pw->registry_listener);
+ pw_registry_add_listener(pw->registry, &pw->registry_listener,
+ &registry_events, pw);
+ pw_thread_loop_unlock(pw_loop_instance());
+ return 0;
+}
+
+
struct pw_core *pw_core_instance(void)
{
if (!d)
@@ -133,20 +272,18 @@ static int module_init(void)
if (!d)
return errno;
- err = auplay_register(&auplay, baresip_auplayl(),
+ err = auplay_register(&d->auplay, baresip_auplayl(),
"pipewire", pw_playback_alloc);
- err |= ausrc_register(&ausrc, baresip_ausrcl(),
+ err |= ausrc_register(&d->ausrc, baresip_ausrcl(),
"pipewire", pw_capture_alloc);
+ err |= pw_start_registry_scan(d);
return err;
}
static int module_close(void)
{
- auplay = mem_deref(auplay);
- ausrc = mem_deref(ausrc);
-
d = mem_deref(d);
pw_deinit();
return 0;
diff --git a/modules/pipewire/pipewire.h b/modules/pipewire/pipewire.h
index 0a93aaf52..4d4c9787b 100644
--- a/modules/pipewire/pipewire.h
+++ b/modules/pipewire/pipewire.h
@@ -7,6 +7,7 @@
int aufmt_to_pw_format(enum aufmt fmt);
struct pw_core *pw_core_instance(void);
struct pw_thread_loop *pw_loop_instance(void);
+int pw_device_id(const char *node_name);
int pw_playback_alloc(struct auplay_st **stp,
const struct auplay *ap,
diff --git a/modules/pipewire/playback.c b/modules/pipewire/playback.c
index 012731570..24b4f2c3a 100644
--- a/modules/pipewire/playback.c
+++ b/modules/pipewire/playback.c
@@ -86,6 +86,7 @@ int pw_playback_alloc(struct auplay_st **stp, const struct auplay *ap,
PW_KEY_MEDIA_TYPE, "Audio",
PW_KEY_MEDIA_CATEGORY, "Playback",
PW_KEY_MEDIA_ROLE, "Communication",
+ PW_KEY_TARGET_OBJECT, dev,
NULL));
if (!st->stream) {
err = errno;
@@ -103,7 +104,7 @@ int pw_playback_alloc(struct auplay_st **stp, const struct auplay *ap,
err = pw_stream_connect(st->stream,
PW_DIRECTION_OUTPUT,
- PW_ID_ANY,
+ pw_device_id(dev),
PW_STREAM_FLAG_AUTOCONNECT |
PW_STREAM_FLAG_MAP_BUFFERS |
PW_STREAM_FLAG_RT_PROCESS,
From 86e4a4fd4e0ec7723b7e3129e868f5dd599c5e2f Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Wed, 1 Mar 2023 09:30:21 +0100
Subject: [PATCH 06/10] pipewire: set _XOPEN_SOURCE=700
---
modules/pipewire/CMakeLists.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/modules/pipewire/CMakeLists.txt b/modules/pipewire/CMakeLists.txt
index 8673873af..bb2ad8f2a 100644
--- a/modules/pipewire/CMakeLists.txt
+++ b/modules/pipewire/CMakeLists.txt
@@ -14,4 +14,5 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${PIPEWIRE_LIBRARIES})
target_compile_options(${PROJECT_NAME} PRIVATE
-Wno-pedantic
-Wno-bad-function-cast
+ -D_XOPEN_SOURCE=700
)
From 63f9a8a2afad5eff06f6fc55b87276a881f25c08 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Wed, 1 Mar 2023 09:34:19 +0100
Subject: [PATCH 07/10] pipewire: replace _XOPEN_SOURCE by _GNU_SOURCE
---
modules/pipewire/CMakeLists.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/modules/pipewire/CMakeLists.txt b/modules/pipewire/CMakeLists.txt
index bb2ad8f2a..50a4befef 100644
--- a/modules/pipewire/CMakeLists.txt
+++ b/modules/pipewire/CMakeLists.txt
@@ -14,5 +14,5 @@ target_link_libraries(${PROJECT_NAME} PRIVATE ${PIPEWIRE_LIBRARIES})
target_compile_options(${PROJECT_NAME} PRIVATE
-Wno-pedantic
-Wno-bad-function-cast
- -D_XOPEN_SOURCE=700
+ -D_GNU_SOURCE
)
From 572d69b24917e66a76015b8605eb58b8e5377969 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Wed, 1 Mar 2023 10:28:30 +0100
Subject: [PATCH 08/10] pipewire: thread safe stream termination
---
modules/pipewire/capture.c | 5 ++++-
modules/pipewire/playback.c | 5 ++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/modules/pipewire/capture.c b/modules/pipewire/capture.c
index 36f6d5d90..8d20eec52 100644
--- a/modules/pipewire/capture.c
+++ b/modules/pipewire/capture.c
@@ -42,9 +42,11 @@ static void ausrc_destructor(void *arg)
{
struct ausrc_st *st = arg;
+ pw_thread_loop_lock (pw_loop_instance());
st->rh = NULL;
st->errh = NULL;
pw_stream_destroy(st->stream);
+ pw_thread_loop_unlock(pw_loop_instance());
}
@@ -168,7 +170,8 @@ static void on_process(void *arg)
af.timestamp = st->samps * AUDIO_TIMEBASE /
(st->prm.srate * st->prm.ch);
st->samps += sampc;
- st->rh(&af, st->arg);
+ if (st->rh)
+ st->rh(&af, st->arg);
pw_stream_queue_buffer(st->stream, b);
}
diff --git a/modules/pipewire/playback.c b/modules/pipewire/playback.c
index 24b4f2c3a..55aff42c6 100644
--- a/modules/pipewire/playback.c
+++ b/modules/pipewire/playback.c
@@ -40,8 +40,10 @@ static void auplay_destructor(void *arg)
{
struct auplay_st *st = arg;
+ pw_thread_loop_lock (pw_loop_instance());
st->wh = NULL;
pw_stream_destroy(st->stream);
+ pw_thread_loop_unlock(pw_loop_instance());
}
@@ -157,7 +159,8 @@ static void on_process(void *arg)
auframe_init(&af, st->prm.fmt, sampv, st->sampc,
st->prm.srate, st->prm.ch);
- st->wh(&af, st->arg);
+ if (st->wh)
+ st->wh(&af, st->arg);
d->chunk->offset = 0;
d->chunk->stride = st->stride;
From dd142036ac430c8905ef90b1f73da7fe742b7a41 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Wed, 1 Mar 2023 10:29:15 +0100
Subject: [PATCH 09/10] pipewire: remove unused errh
---
modules/pipewire/capture.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/modules/pipewire/capture.c b/modules/pipewire/capture.c
index 8d20eec52..00f6f5e99 100644
--- a/modules/pipewire/capture.c
+++ b/modules/pipewire/capture.c
@@ -21,7 +21,6 @@ struct ausrc_st {
struct ausrc_prm prm;
ausrc_read_h *rh;
struct spa_hook listener;
- ausrc_error_h *errh;
size_t sampsz;
uint64_t samps;
@@ -44,7 +43,6 @@ static void ausrc_destructor(void *arg)
pw_thread_loop_lock (pw_loop_instance());
st->rh = NULL;
- st->errh = NULL;
pw_stream_destroy(st->stream);
pw_thread_loop_unlock(pw_loop_instance());
}
@@ -62,6 +60,7 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
const char name[] = "baresip-capture";
char nlat[10];
int err = 0;
+ (void)errh;
if (!stp || !as || !prm || !rh)
return EINVAL;
@@ -82,7 +81,6 @@ int pw_capture_alloc(struct ausrc_st **stp, const struct ausrc *as,
st->samps = 0;
st->rh = rh;
- st->errh = errh;
st->arg = arg;
re_snprintf(nlat, sizeof(nlat), "%u/1000", prm->ptime);
From b3a65e2ddb353aeb28f45daad5e7bb575bb17ee0 Mon Sep 17 00:00:00 2001
From: Christian Spielberger <c.spielberger@commend.com>
Date: Thu, 2 Mar 2023 07:18:30 +0100
Subject: [PATCH 10/10] config: add pipewire as option for DEFAULT_AUDIO_DEVICE
---
docs/examples/config | 1 +
src/config.c | 9 ++++++++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/examples/config b/docs/examples/config
index 07a8c8920..ff5fbbe33 100644
--- a/docs/examples/config
+++ b/docs/examples/config
@@ -118,6 +118,7 @@ module auresamp.so
# Audio driver Modules
module alsa.so
#module pulse.so
+#module pipewire.so
#module jack.so
#module portaudio.so
#module aubridge.so
diff --git a/src/config.c b/src/config.c
index 7948215b3..37284966d 100644
--- a/src/config.c
+++ b/src/config.c
@@ -975,13 +975,20 @@ int config_write_template(const char *file, const struct config *cfg)
#elif defined (WIN32)
(void)re_fprintf(f, "module\t\t\t" "winwave" MOD_EXT "\n");
#else
- if (!strncmp(default_audio_device(), "pulse", 5)) {
+ if (!strncmp(default_audio_device(), "pipewire", 8)) {
+ (void)re_fprintf(f, "#module\t\t\t" "alsa" MOD_EXT "\n");
+ (void)re_fprintf(f, "#module\t\t\t" "pulse" MOD_EXT "\n");
+ (void)re_fprintf(f, "module\t\t\t" "pipewire" MOD_EXT "\n");
+ }
+ else if (!strncmp(default_audio_device(), "pulse", 5)) {
(void)re_fprintf(f, "#module\t\t\t" "alsa" MOD_EXT "\n");
(void)re_fprintf(f, "module\t\t\t" "pulse" MOD_EXT "\n");
+ (void)re_fprintf(f, "#module\t\t\t" "pipewire" MOD_EXT "\n");
}
else {
(void)re_fprintf(f, "module\t\t\t" "alsa" MOD_EXT "\n");
(void)re_fprintf(f, "#module\t\t\t" "pulse" MOD_EXT"\n");
+ (void)re_fprintf(f, "#module\t\t\t" "pipewire" MOD_EXT "\n");
}
#endif
(void)re_fprintf(f, "#module\t\t\t" "jack" MOD_EXT "\n");