summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/arch.h3
-rw-r--r--configure.ac15
-rw-r--r--sesman/chansrv/Makefile.am5
-rw-r--r--sesman/chansrv/pulse/Makefile23
-rw-r--r--sesman/chansrv/pulse/module-xrdp-sink.c4
-rw-r--r--sesman/chansrv/pulse/module-xrdp-source-symdef.h29
-rw-r--r--sesman/chansrv/pulse/module-xrdp-source.c460
-rw-r--r--sesman/chansrv/pulse/pulse-notes.ubuntu.txt83
-rw-r--r--sesman/chansrv/sound.c871
-rw-r--r--sesman/chansrv/sound.h43
10 files changed, 1370 insertions, 166 deletions
diff --git a/common/arch.h b/common/arch.h
index 988153c2..b4eb4719 100644
--- a/common/arch.h
+++ b/common/arch.h
@@ -79,6 +79,7 @@ typedef signed short tsi16;
typedef int ti32;
typedef unsigned int tui32;
typedef signed int tsi32;
+typedef int tbool;
#if defined(_WIN64)
/* Microsoft's VC++ compiler uses the more backwards-compatible LLP64 model.
Most other 64 bit compilers(Solaris, AIX, HP, Linux, Mac OS X) use
@@ -102,6 +103,6 @@ typedef int tsock;
typedef unsigned long long tui64;
typedef signed long long tsi64;
#endif
-#endif
+#endif /* DEFINED_Ts */
#endif
diff --git a/configure.ac b/configure.ac
index 6c0134e6..07d61cd2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,14 +43,22 @@ AC_ARG_ENABLE(tjpeg, AS_HELP_STRING([--enable-tjpeg],
[Build turbo jpeg module(assumes /opt/libjpeg-turbo) (default: no)]),
[tjpeg=true], [tjpeg=false])
AM_CONDITIONAL(XRDP_TJPEG, [test x$tjpeg = xtrue])
+
AC_ARG_ENABLE(simplesound, AS_HELP_STRING([--enable-simplesound],
[Build simple pulse audio interface (default: no)]),
[simplesound=true], [simplesound=false])
AM_CONDITIONAL(XRDP_SIMPLESOUND, [test x$simplesound = xtrue])
+
AC_ARG_ENABLE(fuse, AS_HELP_STRING([--enable-fuse],
[Build fuse(clipboard file / drive redir) (default: no)]),
[fuse=true], [fuse=false])
AM_CONDITIONAL(XRDP_FUSE, [test x$fuse = xtrue])
+
+AC_ARG_ENABLE(load_pulse_modules, AS_HELP_STRING([--enable-load_pulse_modules],
+ [Build code to load pulse audio modules (default: no)]),
+ [load_pulse_modules=true], [load_pulse_modules=false])
+AM_CONDITIONAL(XRDP_LOAD_PULSE_MODULES, [test x$load_pulse_modules = xtrue])
+
AC_ARG_ENABLE(xrdpvr, AS_HELP_STRING([--enable-xrdpvr],
[Build xrdpvr module (default: no)]),
[xrdpvr=true], [xrdpvr=false])
@@ -105,6 +113,13 @@ then
[#define _FILE_OFFSET_BITS 64])
fi
+# checking for libpulse
+if ! test -z "$enable_load_pulse_modules"
+then
+ AC_CHECK_HEADER([pulse/util.h], [],
+ [AC_MSG_ERROR([please install libpulse-dev or libpulse-devel])])
+fi
+
# checking for libpulse libpulse-simple
if ! test -z "$enable_simplesound"
then
diff --git a/sesman/chansrv/Makefile.am b/sesman/chansrv/Makefile.am
index 2d73f05c..9aa3ebe7 100644
--- a/sesman/chansrv/Makefile.am
+++ b/sesman/chansrv/Makefile.am
@@ -26,6 +26,11 @@ EXTRA_DEFINES += -DXRDP_FUSE
EXTRA_LIBS += -lfuse
endif
+if XRDP_LOAD_PULSE_MODULES
+EXTRA_DEFINES += -DXRDP_LOAD_PULSE_MODULES
+EXTRA_LIBS += -lpulse
+endif
+
AM_CFLAGS = \
-DXRDP_CFG_PATH=\"${sysconfdir}/xrdp\" \
-DXRDP_SBIN_PATH=\"${sbindir}\" \
diff --git a/sesman/chansrv/pulse/Makefile b/sesman/chansrv/pulse/Makefile
index 6efbfc16..da1a89a1 100644
--- a/sesman/chansrv/pulse/Makefile
+++ b/sesman/chansrv/pulse/Makefile
@@ -1,18 +1,17 @@
+#
+# build xrdp pulseaudio modules
+#
+PULSE_DIR = /home/lk/pulseaudio-1.1
+CFLAGS = -Wall -O2 -I$(PULSE_DIR) -I$(PULSE_DIR)/src -DHAVE_CONFIG_H -fPIC
-#PULSE_DIR=/home/jay/temp/pulseaudio-0.9.21
-#PULSE_DIR=/home/jay/pulseaudio-0.9.22
-#PULSE_DIR=/home/jay/pulseaudio-0.9.21
-PULSE_DIR=/home/jay/pulseaudio-2.0
+all: module-xrdp-sink.so module-xrdp-source.so
-OBJS = module-xrdp-sink.o
+module-xrdp-sink.so: module-xrdp-sink.o
+ $(CC) $(LDFLAGS) -shared -o module-xrdp-sink.so module-xrdp-sink.o
-CFLAGS = -Wall -O2 -I$(PULSE_DIR) -I$(PULSE_DIR)/src -DHAVE_CONFIG_H -fPIC
-
-all: module-xrdp-sink.so
-
-module-xrdp-sink.so: $(OBJS)
- $(CC) $(LDFLAGS) -shared -o module-xrdp-sink.so $(OBJS)
+module-xrdp-source.so: module-xrdp-source.o
+ $(CC) $(LDFLAGS) -shared -o module-xrdp-source.so module-xrdp-source.o
clean:
- rm -f $(OBJS) module-xrdp-sink.so
+ rm -f module-xrdp-sink.o module-xrdp-sink.so module-xrdp-source.o module-xrdp-source.so
diff --git a/sesman/chansrv/pulse/module-xrdp-sink.c b/sesman/chansrv/pulse/module-xrdp-sink.c
index 3f09b727..d56a4883 100644
--- a/sesman/chansrv/pulse/module-xrdp-sink.c
+++ b/sesman/chansrv/pulse/module-xrdp-sink.c
@@ -72,10 +72,10 @@ PA_MODULE_USAGE(
"channels=<number of channels> "
"channel_map=<channel map>");
-#define DEFAULT_SINK_NAME "xrdp"
+#define DEFAULT_SINK_NAME "xrdp-sink"
#define BLOCK_USEC 30000
//#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
-#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_socket_%d"
+#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_out_socket_%d"
struct userdata {
pa_core *core;
diff --git a/sesman/chansrv/pulse/module-xrdp-source-symdef.h b/sesman/chansrv/pulse/module-xrdp-source-symdef.h
new file mode 100644
index 00000000..e0467648
--- /dev/null
+++ b/sesman/chansrv/pulse/module-xrdp-source-symdef.h
@@ -0,0 +1,29 @@
+#ifndef foomodulenullsourcesymdeffoo
+#define foomodulenullsourcesymdeffoo
+
+#include <pulsecore/core.h>
+#include <pulsecore/module.h>
+#include <pulsecore/macro.h>
+
+#define pa__init module_xrdp_source_LTX_pa__init
+#define pa__done module_xrdp_source_LTX_pa__done
+#define pa__get_author module_xrdp_source_LTX_pa__get_author
+#define pa__get_description module_xrdp_source_LTX_pa__get_description
+#define pa__get_usage module_xrdp_source_LTX_pa__get_usage
+#define pa__get_version module_xrdp_source_LTX_pa__get_version
+#define pa__get_deprecated module_xrdp_source_LTX_pa__get_deprecated
+#define pa__load_once module_xrdp_source_LTX_pa__load_once
+#define pa__get_n_used module_xrdp_source_LTX_pa__get_n_used
+
+int pa__init(pa_module*m);
+void pa__done(pa_module*m);
+int pa__get_n_used(pa_module*m);
+
+const char* pa__get_author(void);
+const char* pa__get_description(void);
+const char* pa__get_usage(void);
+const char* pa__get_version(void);
+const char* pa__get_deprecated(void);
+pa_bool_t pa__load_once(void);
+
+#endif
diff --git a/sesman/chansrv/pulse/module-xrdp-source.c b/sesman/chansrv/pulse/module-xrdp-source.c
new file mode 100644
index 00000000..2d7ec4fa
--- /dev/null
+++ b/sesman/chansrv/pulse/module-xrdp-source.c
@@ -0,0 +1,460 @@
+/***
+ This file is part of PulseAudio.
+
+ Copyright 2004-2008 Lennart Poettering
+ Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+
+ PulseAudio is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published
+ by the Free Software Foundation; either version 2 of the License,
+ or (at your option) any later version.
+
+ PulseAudio is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with PulseAudio; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.
+***/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <pulse/rtclock.h>
+#include <pulse/timeval.h>
+#include <pulse/xmalloc.h>
+
+#include <pulsecore/core-util.h>
+#include <pulsecore/log.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/modargs.h>
+#include <pulsecore/module.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/source.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/thread.h>
+
+#include "module-xrdp-source-symdef.h"
+
+PA_MODULE_AUTHOR("Laxmikant Rashinkar");
+PA_MODULE_DESCRIPTION("xrdp source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
+PA_MODULE_USAGE(
+ "format=<sample format> "
+ "channels=<number of channels> "
+ "rate=<sample rate> "
+ "source_name=<name of source> "
+ "channel_map=<channel map> "
+ "description=<description for the source> "
+ "latency_time=<latency time in ms>");
+
+#define DEFAULT_SOURCE_NAME "xrdp-source"
+#define DEFAULT_LATENCY_TIME 10
+#define MAX_LATENCY_USEC (PA_USEC_PER_SEC * 2)
+#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_in_socket_%d"
+
+struct userdata {
+ pa_core *core;
+ pa_module *module;
+ pa_source *source;
+
+ pa_thread *thread;
+ pa_thread_mq thread_mq;
+ pa_rtpoll *rtpoll;
+
+ size_t block_size;
+
+ pa_usec_t block_usec;
+ pa_usec_t timestamp;
+ pa_usec_t latency_time;
+
+ /* xrdp stuff */
+ int fd; /* UDS connection to xrdp chansrv */
+ int display_num; /* X display number */
+ int want_src_data;
+};
+
+static const char* const valid_modargs[] = {
+ "rate",
+ "format",
+ "channels",
+ "source_name",
+ "channel_map",
+ "description",
+ "latency_time",
+ NULL
+};
+
+static int get_display_num_from_display(char *display_text) ;
+
+static int source_process_msg(pa_msgobject *o, int code, void *data,
+ int64_t offset, pa_memchunk *chunk) {
+
+ struct userdata *u = PA_SOURCE(o)->userdata;
+
+ switch (code) {
+ case PA_SOURCE_MESSAGE_SET_STATE:
+
+ if (PA_PTR_TO_UINT(data) == PA_SOURCE_RUNNING)
+ u->timestamp = pa_rtclock_now();
+
+ break;
+
+ case PA_SOURCE_MESSAGE_GET_LATENCY: {
+ pa_usec_t now;
+
+ now = pa_rtclock_now();
+ *((pa_usec_t*) data) = u->timestamp > now ? u->timestamp - now : 0;
+
+ return 0;
+ }
+ }
+
+ return pa_source_process_msg(o, code, data, offset, chunk);
+}
+
+static void source_update_requested_latency_cb(pa_source *s) {
+ struct userdata *u;
+
+ pa_source_assert_ref(s);
+ u = s->userdata;
+ pa_assert(u);
+
+ u->block_usec = pa_source_get_requested_latency_within_thread(s);
+}
+
+static int data_get(struct userdata *u, pa_memchunk *chunk) {
+
+ int fd;
+ int bytes;
+ struct sockaddr_un s;
+ char *data;
+ char buf[11];
+ unsigned char ubuf[10];
+
+ if (u->fd == 0) {
+ /* connect to xrdp unix domain socket */
+ fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+ memset(&s, 0, sizeof(s));
+ s.sun_family = AF_UNIX;
+ bytes = sizeof(s.sun_path) - 1;
+ snprintf(s.sun_path, bytes, CHANSRV_PORT_STR, u->display_num);
+ pa_log_debug("Trying to connect to %s", s.sun_path);
+
+ if (connect(fd, (struct sockaddr *) &s, sizeof(struct sockaddr_un)) != 0) {
+ pa_log_debug("Connect failed");
+ close(fd);
+ return -1;
+ }
+
+ pa_log("Connected ok, fd=%d", fd);
+ pa_log_debug("###### connected to xrdp audio_in socket");
+ u->fd = fd;
+ }
+
+ data = (char *) pa_memblock_acquire(chunk->memblock);
+
+ if (!u->want_src_data) {
+ char buf[12];
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 11;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 1;
+ buf[9] = 0;
+ buf[10] = 0;
+
+ send(u->fd, buf, 11, 0);
+ u->want_src_data = 1;
+ pa_log_debug("###### started recording");
+ }
+
+ /* ask for more data */
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 11;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 3;
+ buf[9] = (unsigned char) chunk->length;
+ buf[10] = (unsigned char) ((chunk->length >> 8) & 0xff);
+
+ send(u->fd, buf, 11, 0);
+
+ /* read length of data available */
+ recv(u->fd, ubuf, 2, 0);
+ bytes = ((ubuf[1] << 8) & 0xff00) | (ubuf[0] & 0xff);
+
+ if (bytes == 0) {
+ pa_memblock_release(chunk->memblock);
+ return 0;
+ }
+
+ /* get data */
+ bytes = recv(u->fd, data, bytes, 0);
+
+ pa_memblock_release(chunk->memblock);
+
+ return bytes;
+}
+
+static void thread_func(void *userdata) {
+ struct userdata *u = userdata;
+
+ pa_assert(u);
+ pa_thread_mq_install(&u->thread_mq);
+ u->timestamp = pa_rtclock_now();
+
+ for (;;) {
+ int ret;
+
+ /* Generate some null data */
+ if (u->source->thread_info.state == PA_SOURCE_RUNNING) {
+ pa_usec_t now;
+ pa_memchunk chunk;
+
+ now = pa_rtclock_now();
+
+ if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) {
+ chunk.memblock = pa_memblock_new(u->core->mempool, (size_t) -1); /* or chunk.length? */
+ chunk.index = 0;
+ data_get(u, &chunk);
+ pa_source_post(u->source, &chunk);
+ pa_memblock_unref(chunk.memblock);
+ u->timestamp = now;
+ }
+
+ pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->latency_time * PA_USEC_PER_MSEC);
+ } else {
+ if (u->want_src_data)
+ {
+ /* we dont want source data anymore */
+ char buf[12];
+
+ buf[0] = 0;
+ buf[1] = 0;
+ buf[2] = 0;
+ buf[3] = 0;
+ buf[4] = 11;
+ buf[5] = 0;
+ buf[6] = 0;
+ buf[7] = 0;
+ buf[8] = 2;
+ buf[9] = 0;
+ buf[10] = 0;
+
+ send(u->fd, buf, 11, 0);
+ u->want_src_data = 0;
+ pa_log_debug("###### stopped recording");
+ }
+ pa_rtpoll_set_timer_disabled(u->rtpoll);
+ }
+
+ /* Hmm, nothing to do. Let's sleep */
+ if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+ goto fail;
+
+ if (ret == 0)
+ goto finish;
+ }
+
+fail:
+ /* If this was no regular exit from the loop we have to continue
+ * processing messages until we received PA_MESSAGE_SHUTDOWN */
+ pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+ pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+ pa_log_debug("###### thread shutting down");
+}
+
+int pa__init(pa_module *m) {
+ struct userdata *u = NULL;
+ pa_sample_spec ss;
+ pa_channel_map map;
+ pa_modargs *ma = NULL;
+ pa_source_new_data data;
+ uint32_t latency_time = DEFAULT_LATENCY_TIME;
+
+ pa_assert(m);
+
+ if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
+ pa_log("Failed to parse module arguments.");
+ goto fail;
+ }
+
+#if 0
+ ss = m->core->default_sample_spec;
+#else
+ ss.format = PA_SAMPLE_S16LE;
+ ss.rate = 22050;
+ ss.channels = 2;
+#endif
+
+ map = m->core->default_channel_map;
+ if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
+ pa_log("Invalid sample format specification or channel map");
+ goto fail;
+ }
+
+ m->userdata = u = pa_xnew0(struct userdata, 1);
+ u->core = m->core;
+ u->module = m;
+ u->rtpoll = pa_rtpoll_new();
+ pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+ pa_source_new_data_init(&data);
+ data.driver = __FILE__;
+ data.module = m;
+ pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+ pa_source_new_data_set_sample_spec(&data, &ss);
+ pa_source_new_data_set_channel_map(&data, &map);
+ //pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "Null Input"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "xrdp Input"));
+ pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
+
+ u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY);
+ pa_source_new_data_done(&data);
+
+ if (!u->source) {
+ pa_log("Failed to create source object.");
+ goto fail;
+ }
+
+ u->latency_time = DEFAULT_LATENCY_TIME;
+ if (pa_modargs_get_value_u32(ma, "latency_time", &latency_time) < 0) {
+ pa_log("Failed to parse latency_time value.");
+ goto fail;
+ }
+ u->latency_time = latency_time;
+
+ u->source->parent.process_msg = source_process_msg;
+ u->source->update_requested_latency = source_update_requested_latency_cb;
+ u->source->userdata = u;
+
+ pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+ pa_source_set_rtpoll(u->source, u->rtpoll);
+
+ pa_source_set_latency_range(u->source, 0, MAX_LATENCY_USEC);
+ u->block_usec = u->source->thread_info.max_latency;
+
+ u->source->thread_info.max_rewind =
+ pa_usec_to_bytes(u->block_usec, &u->source->sample_spec);
+
+ if (!(u->thread = pa_thread_new("null-source", thread_func, u))) {
+ pa_log("Failed to create thread.");
+ goto fail;
+ }
+
+ pa_source_put(u->source);
+
+ pa_modargs_free(ma);
+
+ u->display_num = get_display_num_from_display(getenv("DISPLAY"));
+
+ return 0;
+
+fail:
+ if (ma)
+ pa_modargs_free(ma);
+
+ pa__done(m);
+
+ return -1;
+}
+
+void pa__done(pa_module*m) {
+ struct userdata *u;
+
+ pa_assert(m);
+
+ if (!(u = m->userdata))
+ return;
+
+ if (u->source)
+ pa_source_unlink(u->source);
+
+ if (u->thread) {
+ pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+ pa_thread_free(u->thread);
+ }
+
+ pa_thread_mq_done(&u->thread_mq);
+
+ if (u->source)
+ pa_source_unref(u->source);
+
+ if (u->rtpoll)
+ pa_rtpoll_free(u->rtpoll);
+
+ pa_xfree(u);
+}
+
+static int get_display_num_from_display(char *display_text) {
+ int index;
+ int mode;
+ int host_index;
+ int disp_index;
+ int scre_index;
+ int display_num;
+ char host[256];
+ char disp[256];
+ char scre[256];
+
+ if (display_text == NULL) {
+ return 0;
+ }
+ memset(host, 0, 256);
+ memset(disp, 0, 256);
+ memset(scre, 0, 256);
+
+ index = 0;
+ host_index = 0;
+ disp_index = 0;
+ scre_index = 0;
+ mode = 0;
+
+ while (display_text[index] != 0) {
+ if (display_text[index] == ':') {
+ mode = 1;
+ } else if (display_text[index] == '.') {
+ mode = 2;
+ } else if (mode == 0) {
+ host[host_index] = display_text[index];
+ host_index++;
+ } else if (mode == 1) {
+ disp[disp_index] = display_text[index];
+ disp_index++;
+ } else if (mode == 2) {
+ scre[scre_index] = display_text[index];
+ scre_index++;
+ }
+ index++;
+ }
+
+ host[host_index] = 0;
+ disp[disp_index] = 0;
+ scre[scre_index] = 0;
+ display_num = atoi(disp);
+ return display_num;
+}
diff --git a/sesman/chansrv/pulse/pulse-notes.ubuntu.txt b/sesman/chansrv/pulse/pulse-notes.ubuntu.txt
index cb6ec90f..053a0022 100644
--- a/sesman/chansrv/pulse/pulse-notes.ubuntu.txt
+++ b/sesman/chansrv/pulse/pulse-notes.ubuntu.txt
@@ -1,34 +1,69 @@
-this is /etc/apt/sources.list
-I added the deb-src line
+--------------------------------------
+ Building pulseaudio modules for xrdp
+--------------------------------------
-----------------------
-deb http://packages.linuxmint.com/ maya main upstream import
-deb http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse
-deb http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse
-deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse
-deb http://security.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse
-deb http://archive.canonical.com/ubuntu/ precise partner
-deb http://packages.medibuntu.org/ precise free non-free
+o append the following line to /etc/apt/sources.list
-#deb http://archive.getdeb.net/ubuntu precise-getdeb apps
-#deb http://archive.getdeb.net/ubuntu precise-getdeb games
-deb http://drbl.sourceforge.net/drbl-core drbl stable
-----------------------
+ deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse
-do all this in a new directory
+ this is what my /etc/apt/sources.list looks like
-root
-sudo apt-get install dpkg-dev
+ deb http://packages.linuxmint.com/ maya main upstream import
+ deb http://archive.ubuntu.com/ubuntu/ precise main restricted universe multiverse
+ deb http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse
+ deb http://security.ubuntu.com/ubuntu/ precise-security main restricted universe multiverse
+ deb http://archive.canonical.com/ubuntu/ precise partner
+ deb http://packages.medibuntu.org/ precise free non-free
-non root
-apt-get source pulseaudio
+ #deb http://archive.getdeb.net/ubuntu precise-getdeb apps
+ #deb http://archive.getdeb.net/ubuntu precise-getdeb games
+ deb http://drbl.sourceforge.net/drbl-core drbl stable
+ deb-src http://archive.ubuntu.com/ubuntu/ precise-updates main restricted universe multiverse
+
+ NOTE: If you get an error message that goes something like this:
-root
-sudo apt-get build-dep pulseaudio
+ E: You must put some 'source' URIs in your sources.list
-cd pulseaudio-1.1
+ try running the following command first:
-non root
-dpkg-buildpackage -rfakeroot -uc -b
+ apt-get source pulseaudio
+o run these commands in your home directory
+
+ cd
+ sudo apt-get install dpkg-dev
+ apt-get source pulseaudio
+ sudo apt-get build-dep pulseaudio
+
+ cd pulseaudio-1.1
+ dpkg-buildpackage -rfakeroot -uc -b
+
+o edit Makefile and point PULSE_DIR to ~/pulseaudio<version> dir
+
+o run make; the outputs will be
+ module-xrdp-sink.so
+ module-xrdp-source.so
+
+o sudo cp module-xrdp-sink.so /usr/lib/pulse-<version>/modules
+ sudo cp module-xrdp-source.so /usr/lib/pulse-<version>/modules
+ note: on a 64bit machine use lib64 instead of lib
+
+--------------------------------------
+ To test sound/microphone redirection
+--------------------------------------
+
+o install gnome sound recorder or your favorite sound recorder
+
+o mplayer -ao pulse <audio file>
+
+o sudo apt-get install pavucontrol
+
+o in another window run pavucontrol and you should see xrdp-sink in use
+
+o to enable pulseaudio log
+ o edit /etc/pulse/daemon.conf and set
+ log-target = syslog
+ log-level = notice
+ o pulseaudio --kill
+ o log output will be in /var/log/syslog or /var/log/messages
diff --git a/sesman/chansrv/sound.c b/sesman/chansrv/sound.c
index 4d334051..cc327744 100644
--- a/sesman/chansrv/sound.c
+++ b/sesman/chansrv/sound.c
@@ -16,17 +16,36 @@
* limitations under the License.
*/
+#include <stdio.h>
+#include <pulse/util.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <signal.h>
+#include <sys/un.h>
+
#include "sound.h"
#include "thread_calls.h"
#include "defines.h"
+#include "fifo.h"
extern int g_rdpsnd_chan_id; /* in chansrv.c */
extern int g_display_num; /* in chansrv.c */
-static struct trans *g_audio_l_trans = 0; /* listener */
-static struct trans *g_audio_c_trans = 0; /* connection */
-static int g_training_sent_time = 0;
-static int g_cBlockNo = 0;
+/* audio out: sound_server -> xrdp -> NeutrinoRDP */
+static struct trans *g_audio_l_trans_out = 0; /* listener */
+static struct trans *g_audio_c_trans_out = 0; /* connection */
+
+/* audio in: sound_server <- xrdp <- NeutrinoRDP */
+static struct trans *g_audio_l_trans_in = 0; /* listener */
+static struct trans *g_audio_c_trans_in = 0; /* connection */
+
+static int g_training_sent_time = 0;
+static int g_cBlockNo = 0;
+static int g_bytes_in_stream = 0;
+static FIFO in_fifo;
+
+static struct stream *g_stream_inp = NULL;
#define BBUF_SIZE (1024 * 8)
char g_buffer[BBUF_SIZE];
@@ -38,7 +57,8 @@ static void *DEFAULT_CC
read_raw_audio_data(void *arg);
#endif
-#define CHANSRV_PORT_STR "/tmp/.xrdp/xrdp_chansrv_audio_socket_%d"
+#define CHANSRV_PORT_OUT_STR "/tmp/.xrdp/xrdp_chansrv_audio_out_socket_%d"
+#define CHANSRV_PORT_IN_STR "/tmp/.xrdp/xrdp_chansrv_audio_in_socket_%d"
struct xr_wave_format_ex
{
@@ -52,8 +72,10 @@ struct xr_wave_format_ex
char *data;
};
-static char g_pmc_22050_data[] = { 0 };
-static struct xr_wave_format_ex g_pmc_22050 =
+/* output formats */
+
+static char g_pcm_22050_data[] = { 0 };
+static struct xr_wave_format_ex g_pcm_22050 =
{
1, /* wFormatTag - WAVE_FORMAT_PCM */
2, /* num of channels */
@@ -62,11 +84,11 @@ static struct xr_wave_format_ex g_pmc_22050 =
4, /* block align */
16, /* bits per sample */
0, /* data size */
- g_pmc_22050_data /* data */
+ g_pcm_22050_data /* data */
};
-static char g_pmc_44100_data[] = { 0 };
-static struct xr_wave_format_ex g_pmc_44100 =
+static char g_pcm_44100_data[] = { 0 };
+static struct xr_wave_format_ex g_pcm_44100 =
{
1, /* wFormatTag - WAVE_FORMAT_PCM */
2, /* num of channels */
@@ -75,24 +97,63 @@ static struct xr_wave_format_ex g_pmc_44100 =
4, /* block align */
16, /* bits per sample */
0, /* data size */
- g_pmc_44100_data /* data */
+ g_pcm_44100_data /* data */
};
-#define NUM_BUILT_IN 2
-static struct xr_wave_format_ex *g_wave_formats[NUM_BUILT_IN] =
+#define SND_NUM_OUTP_FORMATS 2
+static struct xr_wave_format_ex *g_wave_outp_formats[SND_NUM_OUTP_FORMATS] =
{
- &g_pmc_44100,
- &g_pmc_22050
+ &g_pcm_44100,
+ &g_pcm_22050
};
/* index into list from client */
static int g_current_client_format_index = 0;
+
/* index into list from server */
static int g_current_server_format_index = 0;
+/* input formats */
+
+static char g_pcm_inp_22050_data[] = { 0 };
+static struct xr_wave_format_ex g_pcm_inp_22050 =
+{
+ 1, /* wFormatTag - WAVE_FORMAT_PCM */
+ 2, /* num of channels */
+ 22050, /* samples per sec */
+ 88200, /* avg bytes per sec */
+ 4, /* block align */
+ 16, /* bits per sample */
+ 0, /* data size */
+ g_pcm_inp_22050_data /* data */
+};
+
+static char g_pcm_inp_44100_data[] = { 0 };
+static struct xr_wave_format_ex g_pcm_inp_44100 =
+{
+ 1, /* wFormatTag - WAVE_FORMAT_PCM */
+ 2, /* num of channels */
+ 44100, /* samples per sec */
+ 176400, /* avg bytes per sec */
+ 4, /* block align */
+ 16, /* bits per sample */
+ 0, /* data size */
+ g_pcm_inp_44100_data /* data */
+};
+
+#define SND_NUM_INP_FORMATS 2
+static struct xr_wave_format_ex *g_wave_inp_formats[SND_NUM_INP_FORMATS] =
+{
+ &g_pcm_inp_22050,
+ &g_pcm_inp_44100
+};
+
+static int g_client_input_format_index = 0;
+static int g_server_input_format_index = 0;
+
/*****************************************************************************/
static int APP_CC
-sound_send_server_formats(void)
+sound_send_server_output_formats(void)
{
struct stream *s;
int bytes;
@@ -103,15 +164,15 @@ sound_send_server_formats(void)
init_stream(s, 8182);
out_uint16_le(s, SNDC_FORMATS);
size_ptr = s->p;
- out_uint16_le(s, 0); /* size, set later */
- out_uint32_le(s, 0); /* dwFlags */
- out_uint32_le(s, 0); /* dwVolume */
- out_uint32_le(s, 0); /* dwPitch */
- out_uint16_le(s, 0); /* wDGramPort */
- out_uint16_le(s, NUM_BUILT_IN); /* wNumberOfFormats */
- out_uint8(s, g_cBlockNo); /* cLastBlockConfirmed */
- out_uint16_le(s, 2); /* wVersion */
- out_uint8(s, 0); /* bPad */
+ out_uint16_le(s, 0); /* size, set later */
+ out_uint32_le(s, 0); /* dwFlags */
+ out_uint32_le(s, 0); /* dwVolume */
+ out_uint32_le(s, 0); /* dwPitch */
+ out_uint16_le(s, 0); /* wDGramPort */
+ out_uint16_le(s, SND_NUM_OUTP_FORMATS); /* wNumberOfFormats */
+ out_uint8(s, g_cBlockNo); /* cLastBlockConfirmed */
+ out_uint16_le(s, 2); /* wVersion */
+ out_uint8(s, 0); /* bPad */
/* sndFormats */
/*
@@ -132,19 +193,19 @@ sound_send_server_formats(void)
00 00
*/
- for (index = 0; index < NUM_BUILT_IN; index++)
+ for (index = 0; index < SND_NUM_OUTP_FORMATS; index++)
{
- out_uint16_le(s, g_wave_formats[index]->wFormatTag);
- out_uint16_le(s, g_wave_formats[index]->nChannels);
- out_uint32_le(s, g_wave_formats[index]->nSamplesPerSec);
- out_uint32_le(s, g_wave_formats[index]->nAvgBytesPerSec);
- out_uint16_le(s, g_wave_formats[index]->nBlockAlign);
- out_uint16_le(s, g_wave_formats[index]->wBitsPerSample);
- bytes = g_wave_formats[index]->cbSize;
+ out_uint16_le(s, g_wave_outp_formats[index]->wFormatTag);
+ out_uint16_le(s, g_wave_outp_formats[index]->nChannels);
+ out_uint32_le(s, g_wave_outp_formats[index]->nSamplesPerSec);
+ out_uint32_le(s, g_wave_outp_formats[index]->nAvgBytesPerSec);
+ out_uint16_le(s, g_wave_outp_formats[index]->nBlockAlign);
+ out_uint16_le(s, g_wave_outp_formats[index]->wBitsPerSample);
+ bytes = g_wave_outp_formats[index]->cbSize;
out_uint16_le(s, bytes);
if (bytes > 0)
{
- out_uint8p(s, g_wave_formats[index]->data, bytes);
+ out_uint8p(s, g_wave_outp_formats[index]->data, bytes);
}
}
@@ -159,6 +220,7 @@ sound_send_server_formats(void)
}
/*****************************************************************************/
+
static int
sound_send_training(void)
{
@@ -189,26 +251,29 @@ sound_send_training(void)
/*****************************************************************************/
static int APP_CC
-sound_process_format(int aindex, int wFormatTag, int nChannels,
- int nSamplesPerSec, int nAvgBytesPerSec,
- int nBlockAlign, int wBitsPerSample,
- int cbSize, char *data)
-{
- LOG(0, ("sound_process_format:"));
- LOG(0, (" wFormatTag %d", wFormatTag));
- LOG(0, (" nChannels %d", nChannels));
- LOG(0, (" nSamplesPerSec %d", nSamplesPerSec));
- LOG(0, (" nAvgBytesPerSec %d", nAvgBytesPerSec));
- LOG(0, (" nBlockAlign %d", nBlockAlign));
- LOG(0, (" wBitsPerSample %d", wBitsPerSample));
- LOG(0, (" cbSize %d", cbSize));
+sound_process_output_format(int aindex, int wFormatTag, int nChannels,
+ int nSamplesPerSec, int nAvgBytesPerSec,
+ int nBlockAlign, int wBitsPerSample,
+ int cbSize, char *data)
+{
+ LOG(1, ("sound_process_output_format:"));
+ LOG(1, (" wFormatTag %d", wFormatTag));
+ LOG(1, (" nChannels %d", nChannels));
+ LOG(1, (" nSamplesPerSec %d", nSamplesPerSec));
+ LOG(1, (" nAvgBytesPerSec %d", nAvgBytesPerSec));
+ LOG(1, (" nBlockAlign %d", nBlockAlign));
+ LOG(1, (" wBitsPerSample %d", wBitsPerSample));
+ LOG(1, (" cbSize %d", cbSize));
+
g_hexdump(data, cbSize);
- if (wFormatTag == g_pmc_44100.wFormatTag &&
- nChannels == g_pmc_44100.nChannels &&
- nSamplesPerSec == g_pmc_44100.nSamplesPerSec &&
- nAvgBytesPerSec == g_pmc_44100.nAvgBytesPerSec &&
- nBlockAlign == g_pmc_44100.nBlockAlign &&
- wBitsPerSample == g_pmc_44100.wBitsPerSample)
+
+ /* select CD quality audio */
+ if (wFormatTag == g_pcm_44100.wFormatTag &&
+ nChannels == g_pcm_44100.nChannels &&
+ nSamplesPerSec == g_pcm_44100.nSamplesPerSec &&
+ nAvgBytesPerSec == g_pcm_44100.nAvgBytesPerSec &&
+ nBlockAlign == g_pcm_44100.nBlockAlign &&
+ wBitsPerSample == g_pcm_44100.wBitsPerSample)
{
g_current_client_format_index = aindex;
g_current_server_format_index = 0;
@@ -239,7 +304,7 @@ sound_process_format(int aindex, int wFormatTag, int nChannels,
*/
static int APP_CC
-sound_process_formats(struct stream *s, int size)
+sound_process_output_formats(struct stream *s, int size)
{
int num_formats;
int index;
@@ -252,12 +317,8 @@ sound_process_formats(struct stream *s, int size)
int cbSize;
char *data;
- LOG(0, ("sound_process_formats:"));
-
if (size < 16)
- {
return 1;
- }
in_uint8s(s, 14);
in_uint16_le(s, num_formats);
@@ -275,9 +336,9 @@ sound_process_formats(struct stream *s, int size)
in_uint16_le(s, wBitsPerSample);
in_uint16_le(s, cbSize);
in_uint8p(s, data, cbSize);
- sound_process_format(index, wFormatTag, nChannels, nSamplesPerSec,
- nAvgBytesPerSec, nBlockAlign, wBitsPerSample,
- cbSize, data);
+ sound_process_output_format(index, wFormatTag, nChannels, nSamplesPerSec,
+ nAvgBytesPerSec, nBlockAlign, wBitsPerSample,
+ cbSize, data);
}
sound_send_training();
}
@@ -458,16 +519,18 @@ process_pcm_message(int id, int size, struct stream *s)
sound_send_close();
break;
default:
- LOG(0, ("process_pcm_message: unknown id %d", id));
+ LOG(10, ("process_pcm_message: unknown id %d", id));
break;
}
return 0;
}
/*****************************************************************************/
-/* data in from audio source, eg pulse, alsa */
+
+/* data in from sound_server_sink */
+
static int DEFAULT_CC
-sound_trans_audio_data_in(struct trans *trans)
+sound_sndsrvr_sink_data_in(struct trans *trans)
{
struct stream *s;
int id;
@@ -475,14 +538,10 @@ sound_trans_audio_data_in(struct trans *trans)
int error;
if (trans == 0)
- {
return 0;
- }
- if (trans != g_audio_c_trans)
- {
+ if (trans != g_audio_c_trans_out)
return 1;
- }
s = trans_get_in_s(trans);
in_uint32_le(s, id);
@@ -490,10 +549,11 @@ sound_trans_audio_data_in(struct trans *trans)
if ((id & ~3) || (size > 128 * 1024 + 8) || (size < 8))
{
- LOG(0, ("sound_trans_audio_data_in: bad message id %d size %d", id, size));
+ LOG(0, ("sound_sndsrvr_sink_data_in: bad message id %d size %d", id, size));
return 1;
}
- LOG(10, ("sound_trans_audio_data_in: good message id %d size %d", id, size));
+
+ LOG(10, ("sound_sndsrvr_sink_data_in: good message id %d size %d", id, size));
error = trans_force_read(trans, size - 8);
@@ -507,37 +567,62 @@ sound_trans_audio_data_in(struct trans *trans)
}
/*****************************************************************************/
-/* this is a connection in on the unix domain socket */
+
+/* incoming connection on unix domain socket - sound_server_sink -> xrdp */
+
static int DEFAULT_CC
-sound_trans_audio_conn_in(struct trans *trans, struct trans *new_trans)
+sound_sndsrvr_sink_conn_in(struct trans *trans, struct trans *new_trans)
{
- LOG(0, ("sound_trans_audio_conn_in:"));
+ LOG(0, ("sound_sndsrvr_sink_conn_in:"));
if (trans == 0)
- {
return 1;
- }
- if (trans != g_audio_l_trans)
- {
+ if (trans != g_audio_l_trans_out)
return 1;
- }
- if (g_audio_c_trans != 0) /* if already set, error */
- {
+ if (g_audio_c_trans_out != 0) /* if already set, error */
return 1;
- }
if (new_trans == 0)
- {
return 1;
- }
- g_audio_c_trans = new_trans;
- g_audio_c_trans->trans_data_in = sound_trans_audio_data_in;
- g_audio_c_trans->header_size = 8;
- trans_delete(g_audio_l_trans);
- g_audio_l_trans = 0;
+ g_audio_c_trans_out = new_trans;
+ g_audio_c_trans_out->trans_data_in = sound_sndsrvr_sink_data_in;
+ g_audio_c_trans_out->header_size = 8;
+ trans_delete(g_audio_l_trans_out);
+ g_audio_l_trans_out = 0;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/* incoming connection on unix domain socket - sound_server_source -> xrdp */
+
+static int DEFAULT_CC
+sound_sndsrvr_source_conn_in(struct trans *trans, struct trans *new_trans)
+{
+ LOG(0, ("sound_sndsrvr_source_conn_in: client connected"));
+
+ if (trans == 0)
+ return 1;
+
+ if (trans != g_audio_l_trans_in)
+ return 1;
+
+ if (g_audio_c_trans_in != 0) /* if already set, error */
+ return 1;
+
+ if (new_trans == 0)
+ return 1;
+
+ g_audio_c_trans_in = new_trans;
+ g_audio_c_trans_in->trans_data_in = sound_sndsrvr_source_data_in;
+ g_audio_c_trans_in->header_size = 8;
+ trans_delete(g_audio_l_trans_in);
+ g_audio_l_trans_in = 0;
+
return 0;
}
@@ -546,21 +631,39 @@ int APP_CC
sound_init(void)
{
char port[256];
- int error;
LOG(0, ("sound_init:"));
- sound_send_server_formats();
- g_audio_l_trans = trans_create(TRANS_MODE_UNIX, 128 * 1024, 8192);
- g_audio_l_trans->is_term = g_is_term;
- g_snprintf(port, 255, CHANSRV_PORT_STR, g_display_num);
- g_audio_l_trans->trans_conn_in = sound_trans_audio_conn_in;
- error = trans_listen(g_audio_l_trans, port);
- if (error != 0)
- {
+#ifdef XRDP_LOAD_PULSE_MODULES
+ if (load_pulse_modules())
+ LOG(0, ("Audio and microphone redirection will not work!"));
+#endif
+
+ /* init sound output */
+ sound_send_server_output_formats();
+
+ g_audio_l_trans_out = trans_create(TRANS_MODE_UNIX, 128 * 1024, 8192);
+ g_audio_l_trans_out->is_term = g_is_term;
+ g_snprintf(port, 255, CHANSRV_PORT_OUT_STR, g_display_num);
+ g_audio_l_trans_out->trans_conn_in = sound_sndsrvr_sink_conn_in;
+
+ if (trans_listen(g_audio_l_trans_out, port) != 0)
+ LOG(0, ("sound_init: trans_listen failed"));
+
+ /* init sound input */
+ sound_send_server_input_formats();
+
+ g_audio_l_trans_in = trans_create(TRANS_MODE_UNIX, 128 * 1024, 8192);
+ g_audio_l_trans_in->is_term = g_is_term;
+ g_snprintf(port, 255, CHANSRV_PORT_IN_STR, g_display_num);
+ g_audio_l_trans_in->trans_conn_in = sound_sndsrvr_source_conn_in;
+
+ if (trans_listen(g_audio_l_trans_in, port) != 0)
LOG(0, ("sound_init: trans_listen failed"));
- }
+
+ /* save data from sound_server_source */
+ fifo_init(&in_fifo, 100);
#if defined(XRDP_SIMPLESOUND)
@@ -576,25 +679,43 @@ sound_init(void)
int APP_CC
sound_deinit(void)
{
- LOG(0, ("sound_deinit:"));
+ if (g_audio_l_trans_out != 0)
+ {
+ trans_delete(g_audio_l_trans_out);
+ g_audio_l_trans_out = 0;
+ }
+
+ if (g_audio_c_trans_out != 0)
+ {
+ trans_delete(g_audio_c_trans_out);
+ g_audio_c_trans_out = 0;
+ }
- if (g_audio_l_trans != 0)
+ if (g_audio_l_trans_in != 0)
{
- trans_delete(g_audio_l_trans);
- g_audio_l_trans = 0;
+ trans_delete(g_audio_l_trans_in);
+ g_audio_l_trans_in = 0;
}
- if (g_audio_c_trans != 0)
+ if (g_audio_c_trans_in != 0)
{
- trans_delete(g_audio_c_trans);
- g_audio_c_trans = 0;
+ trans_delete(g_audio_c_trans_in);
+ g_audio_c_trans_in = 0;
}
+ fifo_deinit(&in_fifo);
+
+#ifdef XRDP_LOAD_PULSE_MODULES
+ system("pulseaudio --kill");
+#endif
+
return 0;
}
/*****************************************************************************/
+
/* data in from client ( client -> xrdp -> chansrv ) */
+
int APP_CC
sound_data_in(struct stream *s, int chan_id, int chan_flags, int length,
int total_length)
@@ -617,11 +738,19 @@ sound_data_in(struct stream *s, int chan_id, int chan_flags, int length,
break;
case SNDC_FORMATS:
- sound_process_formats(s, size);
+ sound_process_output_formats(s, size);
+ break;
+
+ case SNDC_REC_NEGOTIATE:
+ sound_process_input_formats(s, size);
+ break;
+
+ case SNDC_REC_DATA:
+ sound_process_input_data(s, size);
break;
default:
- LOG(0, ("sound_data_in: unknown code %d size %d", code, size));
+ LOG(10, ("sound_data_in: unknown code %d size %d", code, size));
break;
}
@@ -636,15 +765,27 @@ sound_get_wait_objs(tbus *objs, int *count, int *timeout)
lcount = *count;
- if (g_audio_l_trans != 0)
+ if (g_audio_l_trans_out != 0)
{
- objs[lcount] = g_audio_l_trans->sck;
+ objs[lcount] = g_audio_l_trans_out->sck;
lcount++;
}
- if (g_audio_c_trans != 0)
+ if (g_audio_c_trans_out != 0)
{
- objs[lcount] = g_audio_c_trans->sck;
+ objs[lcount] = g_audio_c_trans_out->sck;
+ lcount++;
+ }
+
+ if (g_audio_l_trans_in != 0)
+ {
+ objs[lcount] = g_audio_l_trans_in->sck;
+ lcount++;
+ }
+
+ if (g_audio_c_trans_in != 0)
+ {
+ objs[lcount] = g_audio_c_trans_in->sck;
lcount++;
}
@@ -656,19 +797,517 @@ sound_get_wait_objs(tbus *objs, int *count, int *timeout)
int APP_CC
sound_check_wait_objs(void)
{
- if (g_audio_l_trans != 0)
+ if (g_audio_l_trans_out != 0)
+ {
+ trans_check_wait_objs(g_audio_l_trans_out);
+ }
+
+ if (g_audio_c_trans_out != 0)
+ {
+ trans_check_wait_objs(g_audio_c_trans_out);
+ }
+
+ if (g_audio_l_trans_in != 0)
+ {
+ trans_check_wait_objs(g_audio_l_trans_in);
+ }
+
+ if (g_audio_c_trans_in != 0)
+ {
+ trans_check_wait_objs(g_audio_c_trans_in);
+ }
+
+ return 0;
+}
+
+/**
+ * Load xrdp pulseaudio sink and source modules
+ *
+ * @return 0 on success, -1 on failure
+ *****************************************************************************/
+
+#ifdef XRDP_LOAD_PULSE_MODULES
+
+static int APP_CC
+load_pulse_modules()
+{
+ struct sockaddr_un sa;
+
+ pid_t pid;
+ char* cli;
+ int fd;
+ int i;
+ int rv;
+ char buf[1024];
+
+ /* is pulse audio daemon running? */
+ if (pa_pid_file_check_running(&pid, "pulseaudio") < 0)
+ {
+ LOG(0, ("load_pulse_modules: No PulseAudio daemon running, "
+ "or not running as session daemon"));
+ }
+
+ /* get name of unix domain socket used by pulseaudio for CLI */
+ if ((cli = (char *) pa_runtime_path("cli")) == NULL)
+ {
+ LOG(0, ("load_pulse_modules: Error getting PulesAudio runtime path"));
+ return -1;
+ }
+
+ /* open a socket */
+ if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
+ {
+ pa_xfree(cli);
+ LOG(0, ("load_pulse_modules: Socket open error"));
+ return -1;
+ }
+
+ /* set it up */
+ memset(&sa, 0, sizeof(struct sockaddr_un));
+ sa.sun_family = AF_UNIX;
+ pa_strlcpy(sa.sun_path, cli, sizeof(sa.sun_path));
+ pa_xfree(cli);
+
+ for (i = 0; i < 20; i++)
+ {
+ if (pa_pid_file_kill(SIGUSR2, NULL, "pulseaudio") < 0)
+ LOG(0, ("load_pulse_modules: Failed to kill PulseAudio daemon"));
+
+ if ((rv = connect(fd, (struct sockaddr*) &sa, sizeof(sa))) < 0 &&
+ (errno != ECONNREFUSED && errno != ENOENT))
+ {
+ LOG(0, ("load_pulse_modules: connect() failed with error: %s",
+ strerror(errno)));
+ return -1;
+ }
+
+ if (rv >= 0)
+ break;
+
+ pa_msleep(300);
+ }
+
+ if (i >= 20)
+ {
+ LOG(0, ("load_pulse_modules: Daemon not responding"));
+ return -1;
+ }
+
+ LOG(0, ("load_pulse_modules: connected to pulseaudio daemon"));
+
+ /* read back PulseAudio sign on message */
+ memset(buf, 0, 1024);
+ recv(fd, buf, 1024, 0);
+
+ /* send cmd to load source module */
+ memset(buf, 0, 1024);
+ sprintf(buf, "load-module module-xrdp-source\n");
+ send(fd, buf, strlen(buf), 0);
+
+ /* read back response */
+ memset(buf, 0, 1024);
+ recv(fd, buf, 1024, 0);
+ if (strcasestr(buf, "Module load failed") != 0)
+ {
+ LOG(0, ("load_pulse_modules: Error loading module-xrdp-source"));
+ }
+ else
+ {
+ LOG(0, ("load_pulse_modules: Loaded module-xrdp-source"));
+
+ /* success, set it as the default source */
+ memset(buf, 0, 1024);
+ sprintf(buf, "set-default-source xrdp-source\n");
+ send(fd, buf, strlen(buf), 0);
+
+ memset(buf, 0, 1024);
+ recv(fd, buf, 1024, 0);
+
+ if (strcasestr(buf, "does not exist") != 0)
+ {
+ LOG(0, ("load_pulse_modules: Error setting default source"));
+ }
+ else
+ {
+ LOG(0, ("load_pulse_modules: set default source"));
+ }
+ }
+
+ /* send cmd to load sink module */
+ memset(buf, 0, 1024);
+ sprintf(buf, "load-module module-xrdp-sink\n");
+ send(fd, buf, strlen(buf), 0);
+
+ /* read back response */
+ memset(buf, 0, 1024);
+ recv(fd, buf, 1024, 0);
+ if (strcasestr(buf, "Module load failed") != 0)
+ {
+ LOG(0, ("load_pulse_modules: Error loading module-xrdp-sink"));
+ }
+ else
+ {
+ LOG(0, ("load_pulse_modules: Loaded module-xrdp-sink"));
+
+ /* success, set it as the default sink */
+ memset(buf, 0, 1024);
+ sprintf(buf, "set-default-sink xrdp-sink\n");
+ send(fd, buf, strlen(buf), 0);
+
+ memset(buf, 0, 1024);
+ recv(fd, buf, 1024, 0);
+
+ if (strcasestr(buf, "does not exist") != 0)
+ {
+ LOG(0, ("load_pulse_modules: Error setting default sink"));
+ }
+ else
+ {
+ LOG(0, ("load_pulse_modules: set default sink"));
+ }
+ }
+
+ close(fd);
+ return 0;
+}
+#endif
+
+/******************************************************************************
+ ** **
+ ** Microphone releated code **
+ ** **
+ ******************************************************************************/
+
+/**
+ *
+ *****************************************************************************/
+
+static int APP_CC
+sound_send_server_input_formats(void)
+{
+ struct stream* s;
+ int bytes;
+ int index;
+ char* size_ptr;
+
+ make_stream(s);
+ init_stream(s, 8182);
+ out_uint16_le(s, SNDC_REC_NEGOTIATE);
+ size_ptr = s->p;
+ out_uint16_le(s, 0); /* size, set later */
+ out_uint32_le(s, 0); /* unused */
+ out_uint32_le(s, 0); /* unused */
+ out_uint16_le(s, SND_NUM_INP_FORMATS); /* wNumberOfFormats */
+ out_uint16_le(s, 2); /* wVersion */
+
+ /*
+ wFormatTag 2 byte offset 0
+ nChannels 2 byte offset 2
+ nSamplesPerSec 4 byte offset 4
+ nAvgBytesPerSec 4 byte offset 8
+ nBlockAlign 2 byte offset 12
+ wBitsPerSample 2 byte offset 14
+ cbSize 2 byte offset 16
+ data variable offset 18
+ */
+
+ for (index = 0; index < SND_NUM_INP_FORMATS; index++)
{
- trans_check_wait_objs(g_audio_l_trans);
+ out_uint16_le(s, g_wave_inp_formats[index]->wFormatTag);
+ out_uint16_le(s, g_wave_inp_formats[index]->nChannels);
+ out_uint32_le(s, g_wave_inp_formats[index]->nSamplesPerSec);
+ out_uint32_le(s, g_wave_inp_formats[index]->nAvgBytesPerSec);
+ out_uint16_le(s, g_wave_inp_formats[index]->nBlockAlign);
+ out_uint16_le(s, g_wave_inp_formats[index]->wBitsPerSample);
+ bytes = g_wave_inp_formats[index]->cbSize;
+ out_uint16_le(s, bytes);
+ if (bytes > 0)
+ {
+ out_uint8p(s, g_wave_inp_formats[index]->data, bytes);
+ }
}
- if (g_audio_c_trans != 0)
+ s_mark_end(s);
+ bytes = (int)((s->end - s->data) - 4);
+ size_ptr[0] = bytes;
+ size_ptr[1] = bytes >> 8;
+ bytes = (int)(s->end - s->data);
+ send_channel_data(g_rdpsnd_chan_id, s->data, bytes);
+ free_stream(s);
+ return 0;
+}
+
+/**
+ *
+ *****************************************************************************/
+
+static int APP_CC
+sound_process_input_format(int aindex, int wFormatTag, int nChannels,
+ int nSamplesPerSec, int nAvgBytesPerSec,
+ int nBlockAlign, int wBitsPerSample,
+ int cbSize, char *data)
+{
+ LOG(10, ("sound_process_input_format:"));
+ LOG(10, (" wFormatTag %d", wFormatTag));
+ LOG(10, (" nChannels %d", nChannels));
+ LOG(10, (" nSamplesPerSec %d", nSamplesPerSec));
+ LOG(10, (" nAvgBytesPerSec %d", nAvgBytesPerSec));
+ LOG(10, (" nBlockAlign %d", nBlockAlign));
+ LOG(10, (" wBitsPerSample %d", wBitsPerSample));
+ LOG(10, (" cbSize %d", cbSize));
+
+#if 0
+ /* select CD quality audio */
+ if (wFormatTag == g_pcm_inp_44100.wFormatTag &&
+ nChannels == g_pcm_inp_44100.nChannels &&
+ nSamplesPerSec == g_pcm_inp_44100.nSamplesPerSec &&
+ nAvgBytesPerSec == g_pcm_inp_44100.nAvgBytesPerSec &&
+ nBlockAlign == g_pcm_inp_44100.nBlockAlign &&
+ wBitsPerSample == g_pcm_inp_44100.wBitsPerSample)
+ {
+ g_client_input_format_index = aindex;
+ g_server_input_format_index = 0;
+ }
+#else
+ /* select half of CD quality audio */
+ if (wFormatTag == g_pcm_inp_22050.wFormatTag &&
+ nChannels == g_pcm_inp_22050.nChannels &&
+ nSamplesPerSec == g_pcm_inp_22050.nSamplesPerSec &&
+ nAvgBytesPerSec == g_pcm_inp_22050.nAvgBytesPerSec &&
+ nBlockAlign == g_pcm_inp_22050.nBlockAlign &&
+ wBitsPerSample == g_pcm_inp_22050.wBitsPerSample)
{
- trans_check_wait_objs(g_audio_c_trans);
+ g_client_input_format_index = aindex;
+ g_server_input_format_index = 0;
}
+#endif
return 0;
}
+/**
+ *
+ *****************************************************************************/
+
+static int APP_CC
+sound_process_input_formats(struct stream *s, int size)
+{
+ int num_formats;
+ int index;
+ int wFormatTag;
+ int nChannels;
+ int nSamplesPerSec;
+ int nAvgBytesPerSec;
+ int nBlockAlign;
+ int wBitsPerSample;
+ int cbSize;
+ char *data;
+
+ LOG(10, ("sound_process_input_formats: size=%d", size));
+
+ in_uint8s(s, 8); /* skip 8 bytes */
+ in_uint16_le(s, num_formats);
+ in_uint8s(s, 2); /* skip version */
+
+ if (num_formats > 0)
+ {
+ for (index = 0; index < num_formats; index++)
+ {
+ in_uint16_le(s, wFormatTag);
+ in_uint16_le(s, nChannels);
+ in_uint32_le(s, nSamplesPerSec);
+ in_uint32_le(s, nAvgBytesPerSec);
+ in_uint16_le(s, nBlockAlign);
+ in_uint16_le(s, wBitsPerSample);
+ in_uint16_le(s, cbSize);
+ in_uint8p(s, data, cbSize);
+ sound_process_input_format(index, wFormatTag, nChannels, nSamplesPerSec,
+ nAvgBytesPerSec, nBlockAlign, wBitsPerSample,
+ cbSize, data);
+ }
+ }
+
+ return 0;
+}
+
+/**
+ *
+ *****************************************************************************/
+
+static int APP_CC
+sound_input_start_recording()
+{
+ struct stream* s;
+
+ /* if there is any data in FIFO, discard it */
+ while ((s = (struct stream *) fifo_remove(&in_fifo)) != NULL)
+ xstream_free(s);
+
+ xstream_new(s, 1024);
+
+ /*
+ * command format
+ *
+ * 02 bytes command SNDC_REC_START
+ * 02 bytes length
+ * 02 bytes data format received earlier
+ */
+
+ out_uint16_le(s, SNDC_REC_START);
+ out_uint16_le(s, 2);
+ out_uint16_le(s, g_client_input_format_index);
+
+ s_mark_end(s);
+ send_channel_data(g_rdpsnd_chan_id, s->data, 6);
+ xstream_free(s);
+
+ return 0;
+}
+
+/**
+ *
+ *****************************************************************************/
+
+static int APP_CC
+sound_input_stop_recording()
+{
+ struct stream* s;
+
+ xstream_new(s, 1024);
+
+ /*
+ * command format
+ *
+ * 02 bytes command SNDC_REC_STOP
+ * 02 bytes length (zero)
+ */
+
+ out_uint16_le(s, SNDC_REC_STOP);
+ out_uint16_le(s, 0);
+
+ s_mark_end(s);
+ send_channel_data(g_rdpsnd_chan_id, s->data, 4);
+ xstream_free(s);
+
+ return 0;
+}
+
+/**
+ * Process data: xrdp <- client
+ *****************************************************************************/
+
+static unsigned char data = 0;
+
+static int APP_CC
+sound_process_input_data(struct stream *s, int bytes)
+{
+ struct stream *ls;
+
+ xstream_new(ls, bytes);
+ memcpy(ls->data, s->p, bytes);
+ ls->p += bytes;
+ s_mark_end(ls);
+
+ fifo_insert(&in_fifo, (void *) ls);
+
+ return 0;
+}
+
+/**
+ * Got a command from sound_server_source
+ *****************************************************************************/
+
+static int DEFAULT_CC
+sound_sndsrvr_source_data_in(struct trans *trans)
+{
+ struct stream *ts = NULL;
+ struct stream *s = NULL;
+
+ tui16 bytes_req = 0;
+ int bytes_read = 0;
+ int cmd;
+ int i;
+
+ if (trans == 0)
+ return 0;
+
+ if (trans != g_audio_c_trans_in)
+ return 1;
+
+ ts = trans_get_in_s(trans);
+ trans_force_read(trans, 3);
+
+ ts->p = ts->data + 8;
+ in_uint8(ts, cmd);
+ in_uint16_le(ts, bytes_req);
+
+ if (bytes_req != 0)
+ xstream_new(s, bytes_req + 2);
+
+ if (cmd == PA_CMD_SEND_DATA)
+ {
+ /* set real len later */
+ out_uint16_le(s, 0);
+
+ while (bytes_read < bytes_req)
+ {
+ if (g_stream_inp == NULL)
+ g_stream_inp = (struct stream *) fifo_remove(&in_fifo);
+
+ if (g_stream_inp == NULL)
+ {
+ /* no more data, send what we have */
+ break;
+ }
+ else
+ {
+ if (g_bytes_in_stream == 0)
+ g_bytes_in_stream = g_stream_inp->size;
+
+ i = bytes_req - bytes_read;
+
+ if (i < g_bytes_in_stream)
+ {
+ xstream_copyin(s, &g_stream_inp->data[g_stream_inp->size - g_bytes_in_stream], i);
+ bytes_read += i;
+ g_bytes_in_stream -= i;
+ }
+ else
+ {
+ xstream_copyin(s, &g_stream_inp->data[g_stream_inp->size - g_bytes_in_stream], g_bytes_in_stream);
+ bytes_read += g_bytes_in_stream;
+ g_bytes_in_stream = 0;
+ xstream_free(g_stream_inp);
+ g_stream_inp = NULL;
+ }
+ }
+ }
+
+ if (bytes_read)
+ {
+ s->data[0] = (char) (bytes_read & 0xff);
+ s->data[1] = (char) ((bytes_read >> 8) & 0xff);
+ }
+
+ s_mark_end(s);
+
+ trans_force_write_s(trans, s);
+ xstream_free(s);
+ }
+ else if (cmd == PA_CMD_START_REC)
+ {
+ sound_input_start_recording();
+ }
+ else if (cmd == PA_CMD_STOP_REC)
+ {
+ sound_input_stop_recording();
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
#if defined(XRDP_SIMPLESOUND)
#define AUDIO_BUF_SIZE 2048
diff --git a/sesman/chansrv/sound.h b/sesman/chansrv/sound.h
index 0666ea00..c26d0913 100644
--- a/sesman/chansrv/sound.h
+++ b/sesman/chansrv/sound.h
@@ -43,16 +43,37 @@
#define SNDC_UDPWAVELAST 0x0B
#define SNDC_QUALITYMODE 0x0C
-int APP_CC
-sound_init(void);
-int APP_CC
-sound_deinit(void);
-int APP_CC
-sound_get_wait_objs(tbus* objs, int* count, int* timeout);
-int APP_CC
-sound_check_wait_objs(void);
-int APP_CC
-sound_data_in(struct stream* s, int chan_id, int chan_flags,
- int length, int total_length);
+/* used for sound input (mic) */
+#define SNDC_REC_NEGOTIATE 39
+#define SNDC_REC_START 40
+#define SNDC_REC_STOP 41
+#define SNDC_REC_DATA 42
+#define SNDC_REC_SET_VOLUME 43
+/* commands recvd from pulseaudio source */
+#define PA_CMD_START_REC 1
+#define PA_CMD_STOP_REC 2
+#define PA_CMD_SEND_DATA 3
+
+int APP_CC sound_init(void);
+int APP_CC sound_deinit(void);
+int APP_CC sound_get_wait_objs(tbus* objs, int* count, int* timeout);
+int APP_CC sound_check_wait_objs(void);
+
+int APP_CC sound_data_in(struct stream* s, int chan_id, int chan_flags,
+ int length, int total_length);
+
+/* microphone related */
+static int APP_CC sound_send_server_input_formats(void);
+
+static int APP_CC sound_process_input_format(int aindex, int wFormatTag,
+ int nChannels, int nSamplesPerSec, int nAvgBytesPerSec,
+ int nBlockAlign, int wBitsPerSample, int cbSize, char *data);
+
+static int APP_CC sound_process_input_formats(struct stream *s, int size);
+static int APP_CC sound_input_start_recording();
+static int APP_CC sound_input_stop_recording();
+static int APP_CC sound_process_input_data(struct stream *s, int bytes);
+static int DEFAULT_CC sound_sndsrvr_source_data_in(struct trans *trans);
+static int APP_CC load_pulse_modules();
#endif