summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlfred E. Heggestad <aeh@db.org>2016-06-11 21:02:12 +0200
committerGitHub <noreply@github.com>2016-06-11 21:02:12 +0200
commitc02880777a5eafbcc57949928bfb2ac50c3c00b8 (patch)
tree7f6bfbd8d578bb7e4e2565b085a31b3b1693e6d3
parent90a65c8be70ef7ccca5fee3026e76b0384643657 (diff)
parent9eef4975c5f79a96e6cfe287b919ad707a47e87f (diff)
Merge pull request #142 from alfredh/pulse
Pulse
-rw-r--r--modules/pulse/module.mk14
-rw-r--r--modules/pulse/player.c140
-rw-r--r--modules/pulse/pulse.c52
-rw-r--r--modules/pulse/pulse.h14
-rw-r--r--modules/pulse/recorder.c147
5 files changed, 367 insertions, 0 deletions
diff --git a/modules/pulse/module.mk b/modules/pulse/module.mk
new file mode 100644
index 0000000..d5f7698
--- /dev/null
+++ b/modules/pulse/module.mk
@@ -0,0 +1,14 @@
+#
+# module.mk
+#
+# Copyright (C) 2010 - 2016 Creytiv.com
+#
+
+MOD := pulse
+$(MOD)_SRCS += pulse.c
+$(MOD)_SRCS += player.c
+$(MOD)_SRCS += recorder.c
+$(MOD)_LFLAGS += $(shell pkg-config --libs libpulse-simple)
+$(MOD)_CFLAGS += $(shell pkg-config --cflags libpulse-simple)
+
+include mk/mod.mk
diff --git a/modules/pulse/player.c b/modules/pulse/player.c
new file mode 100644
index 0000000..12cf559
--- /dev/null
+++ b/modules/pulse/player.c
@@ -0,0 +1,140 @@
+/**
+ * @file pulse/player.c Pulseaudio sound driver - player
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <pulse/pulseaudio.h>
+#include <pulse/simple.h>
+#include <pthread.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "pulse.h"
+
+
+struct auplay_st {
+ const struct auplay *ap; /* inheritance */
+
+ pa_simple *s;
+ pthread_t thread;
+ bool run;
+ int16_t *sampv;
+ size_t sampc;
+ auplay_write_h *wh;
+ void *arg;
+};
+
+
+static void auplay_destructor(void *arg)
+{
+ struct auplay_st *st = arg;
+
+ /* Wait for termination of other thread */
+ if (st->run) {
+ debug("pulse: stopping playback thread\n");
+ st->run = false;
+ (void)pthread_join(st->thread, NULL);
+ }
+
+ if (st->s)
+ pa_simple_free(st->s);
+
+ mem_deref(st->sampv);
+}
+
+
+static void *write_thread(void *arg)
+{
+ struct auplay_st *st = arg;
+ const size_t num_bytes = st->sampc * 2;
+ int ret, error = 0;
+
+ while (st->run) {
+
+ st->wh(st->sampv, st->sampc, st->arg);
+
+ ret = pa_simple_write(st->s, st->sampv, num_bytes, &error);
+ if (ret < 0) {
+ warning("pulse: pa_simple_write error (%s)\n",
+ pa_strerror(error));
+ }
+ }
+
+ return NULL;
+}
+
+
+int pulse_player_alloc(struct auplay_st **stp, const struct auplay *ap,
+ struct auplay_prm *prm, const char *device,
+ auplay_write_h *wh, void *arg)
+{
+ struct auplay_st *st;
+ pa_sample_spec ss;
+ pa_buffer_attr attr;
+ int err = 0, pa_error = 0;
+
+ if (!stp || !ap || !prm || !wh)
+ return EINVAL;
+
+ debug("pulse: opening player (%u Hz, %d channels, device '%s')\n",
+ prm->srate, prm->ch, device);
+
+ st = mem_zalloc(sizeof(*st), auplay_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->ap = ap;
+ st->wh = wh;
+ st->arg = arg;
+
+ st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+
+ st->sampv = mem_alloc(2 * st->sampc, NULL);
+ if (!st->sampv) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ ss.format = PA_SAMPLE_S16NE;
+ ss.channels = prm->ch;
+ ss.rate = prm->srate;
+
+ attr.maxlength = (uint32_t)-1;
+ attr.tlength = pa_usec_to_bytes(prm->ptime * 1000, &ss);
+ attr.prebuf = (uint32_t)-1;
+ attr.minreq = (uint32_t)-1;
+ attr.fragsize = (uint32_t)-1;
+
+ st->s = pa_simple_new(NULL,
+ "Baresip",
+ PA_STREAM_PLAYBACK,
+ str_isset(device) ? device : 0,
+ "VoIP Playback",
+ &ss,
+ NULL,
+ &attr,
+ &pa_error);
+ if (!st->s) {
+ warning("pulse: could not connect to server (%s)\n",
+ pa_strerror(pa_error));
+ err = ENODEV;
+ goto out;
+ }
+
+ st->run = true;
+ err = pthread_create(&st->thread, NULL, write_thread, st);
+ if (err) {
+ st->run = false;
+ goto out;
+ }
+
+ debug("pulse: playback started\n");
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}
diff --git a/modules/pulse/pulse.c b/modules/pulse/pulse.c
new file mode 100644
index 0000000..450b917
--- /dev/null
+++ b/modules/pulse/pulse.c
@@ -0,0 +1,52 @@
+/**
+ * @file pulse.c Pulseaudio sound driver
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "pulse.h"
+
+
+/**
+ * @defgroup pulse pulse
+ *
+ * Audio driver module for Pulseaudio
+ *
+ * This module is experimental and work-in-progress. It is using
+ * the pulseaudio "simple" interface.
+ */
+
+
+static struct auplay *auplay;
+static struct ausrc *ausrc;
+
+
+static int module_init(void)
+{
+ int err;
+
+ err = auplay_register(&auplay, "pulse", pulse_player_alloc);
+ err |= ausrc_register(&ausrc, "pulse", pulse_recorder_alloc);
+
+ return err;
+}
+
+
+static int module_close(void)
+{
+ auplay = mem_deref(auplay);
+ ausrc = mem_deref(ausrc);
+
+ return 0;
+}
+
+
+EXPORT_SYM const struct mod_export DECL_EXPORTS(pulse) = {
+ "pulse",
+ "audio",
+ module_init,
+ module_close,
+};
diff --git a/modules/pulse/pulse.h b/modules/pulse/pulse.h
new file mode 100644
index 0000000..51c392f
--- /dev/null
+++ b/modules/pulse/pulse.h
@@ -0,0 +1,14 @@
+/**
+ * @file pulse.h Pulseaudio sound driver -- internal API
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+
+
+int pulse_player_alloc(struct auplay_st **stp, const struct auplay *ap,
+ struct auplay_prm *prm, const char *device,
+ auplay_write_h *wh, void *arg);
+int pulse_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *device,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg);
diff --git a/modules/pulse/recorder.c b/modules/pulse/recorder.c
new file mode 100644
index 0000000..4ac27b7
--- /dev/null
+++ b/modules/pulse/recorder.c
@@ -0,0 +1,147 @@
+/**
+ * @file pulse/recorder.c Pulseaudio sound driver - recorder
+ *
+ * Copyright (C) 2010 - 2016 Creytiv.com
+ */
+#include <pulse/pulseaudio.h>
+#include <pulse/simple.h>
+#include <pthread.h>
+#include <re.h>
+#include <rem.h>
+#include <baresip.h>
+#include "pulse.h"
+
+
+struct ausrc_st {
+ const struct ausrc *as; /* inheritance */
+
+ pa_simple *s;
+ pthread_t thread;
+ bool run;
+ int16_t *sampv;
+ size_t sampc;
+ ausrc_read_h *rh;
+ void *arg;
+};
+
+
+static void ausrc_destructor(void *arg)
+{
+ struct ausrc_st *st = arg;
+
+ /* Wait for termination of other thread */
+ if (st->run) {
+ debug("pulse: stopping record thread\n");
+ st->run = false;
+ (void)pthread_join(st->thread, NULL);
+ }
+
+ if (st->s)
+ pa_simple_free(st->s);
+
+ mem_deref(st->sampv);
+}
+
+
+static void *read_thread(void *arg)
+{
+ struct ausrc_st *st = arg;
+ const size_t num_bytes = st->sampc * 2;
+ int ret, error = 0;
+
+ while (st->run) {
+
+ ret = pa_simple_read(st->s, st->sampv, num_bytes, &error);
+ if (ret < 0) {
+ warning("pulse: pa_simple_write error (%s)\n",
+ pa_strerror(error));
+ continue;
+ }
+
+ st->rh(st->sampv, st->sampc, st->arg);
+ }
+
+ return NULL;
+}
+
+
+int pulse_recorder_alloc(struct ausrc_st **stp, const struct ausrc *as,
+ struct media_ctx **ctx,
+ struct ausrc_prm *prm, const char *device,
+ ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
+{
+ struct ausrc_st *st;
+ pa_sample_spec ss;
+ pa_buffer_attr attr;
+ int pa_error;
+ int err;
+
+ (void)ctx;
+ (void)device;
+ (void)errh;
+
+ if (!stp || !as || !prm)
+ return EINVAL;
+
+ debug("pulse: opening recorder (%u Hz, %d channels, device '%s')\n",
+ prm->srate, prm->ch, device);
+
+ st = mem_zalloc(sizeof(*st), ausrc_destructor);
+ if (!st)
+ return ENOMEM;
+
+ st->as = as;
+ st->rh = rh;
+ st->arg = arg;
+
+ st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
+
+ st->sampv = mem_alloc(2 * st->sampc, NULL);
+ if (!st->sampv) {
+ err = ENOMEM;
+ goto out;
+ }
+
+ ss.format = PA_SAMPLE_S16NE;
+ ss.channels = prm->ch;
+ ss.rate = prm->srate;
+
+ attr.maxlength = (uint32_t)-1;
+ attr.tlength = (uint32_t)-1;
+ attr.prebuf = (uint32_t)-1;
+ attr.minreq = (uint32_t)-1;
+ attr.fragsize = pa_usec_to_bytes(prm->ptime * 1000, &ss);
+
+ st->s = pa_simple_new(NULL,
+ "Baresip",
+ PA_STREAM_RECORD,
+ str_isset(device) ? device : 0,
+ "VoIP Record",
+ &ss,
+ NULL,
+ &attr,
+ &pa_error);
+ if (!st->s) {
+ warning("pulse: could not connect to server (%s)\n",
+ pa_strerror(pa_error));
+ err = ENODEV;
+ goto out;
+ }
+
+ st->run = true;
+ err = pthread_create(&st->thread, NULL, read_thread, st);
+ if (err) {
+ st->run = false;
+ goto out;
+ }
+
+ debug("pulse: recording started\n");
+
+ out:
+ if (err)
+ mem_deref(st);
+ else
+ *stp = st;
+
+ return err;
+}