Compare commits

..

111 Commits
v0.1 ... devel

Author SHA1 Message Date
matt335672
2d62018e58
Merge pull request #135 from matt335672/support_dot_users
Support usernames with dots in them
2025-04-17 11:39:27 +01:00
matt335672
96a8427e89 Support usernames with dots in them
The '.' character is supported in POSIX usernames, although
not recommended for Linux. We should not use the username as part of
the filename for a sudoers file, as usernames containing dots are
not scanned by sudo.
2025-04-17 11:33:12 +01:00
metalefty
f36911ee49
Merge pull request #132 from metalefty/release
bump version to v0.8
2025-04-15 18:02:47 +09:00
Koichiro Iwao
347e647bf0 bump version to v0.8 2025-04-15 17:33:24 +09:00
matt335672
bd1913f5a5
Merge pull request #131 from matt335672/fix_pa_sink_state
Fix accessing state within sink_process_msg()
2025-04-14 17:31:40 +01:00
matt335672
c8f820ce77 Fix accessing state within sink_process_msg()
Later versions of pulseaudio pass the state in for a
PA_SINK_MESSAGE_SET_STATE in a different way from earlier versions.
2025-04-14 14:38:52 +01:00
matt335672
1910e49dcc
Merge pull request #126 from oToToT/ubuntu-2404
Specify uid when creating the user via useradd
2025-01-04 16:54:40 +00:00
Tommy Chiang
b818c44cbd
Specify uid when creating the user via useradd
On my ubuntu24.04, the user created via useradd always has uid=1000.
However, the uid I observed entering chroot is the same as my own uid,
which is not 1000. Therefore, tools like sudo will fail because it
cannot recognize what user it is.

This commit fixes the issue by forcing the uid to be the same as the
outer one when creating the user via useradd.
2025-01-04 05:05:09 +08:00
matt335672
dba98d948b
Merge pull request #127 from matt335672/fix_missing_src
Support DEB822 .sources file format
2025-01-03 17:00:44 +00:00
matt335672
1f86d2d58d Add CI dependency for building pulseaudio module
Need libltdl-dev for ltdl.h

This has been checked back to ubuntu16.04
2025-01-03 16:51:06 +00:00
matt335672
c942f6f4ae Only enable sources for correct suite
When working with deb822 format sources files, only enable deb-src
for the suite we are working with.
2025-01-03 16:51:06 +00:00
matt335672
81668f56c9 Support .sources files in the apt build script
Later versions of Ubuntu use DEB822 format files for listing package
sources. The apt build script needs updating to handle these files.
2025-01-03 11:52:18 +00:00
matt335672
8c76a1d798
Merge pull request #120 from matt335672/support_bookworm_cloud_img
Support bookworm cloud img
2024-09-09 11:50:41 +01:00
matt335672
f3d8f98893 Copy trusted keys to chroot
If the user has installed additional sources (for example dbgsym
sources), and the trusted keys are not available in the chroot, the
updates in the chroot can fail as some keys aren't available.
2024-09-09 11:34:02 +01:00
matt335672
2e083f6a18 Support wrapper script on Debian 12 Cloud image
- Support *.source files in /etc/apt/sources
- Copy over /etc/apt/mirrors
- Install ca-certificates package for https repositories
- Don't rely on the local /etc/groups file being complete enough
  for the chroot
2024-09-09 11:34:02 +01:00
matt335672
791dc3f95c
Merge pull request #106 from miklcct/devel
update apt before installing sudo and lsb_release
2023-11-20 09:14:34 +00:00
Michael Tsang
d0a4c12221 update apt before installing sudo and lsb_release 2023-11-17 22:13:32 +00:00
matt335672
14bee582e7
Merge pull request #103 from peanutbutterandcrackers/fix-scripts
scripts: further update scripts to work better with derivative distros.
2023-10-12 10:00:02 +01:00
Prafulla Giri
a862b9dce4 scripts: incorporate refinements by @matt335672. 2023-10-11 22:17:35 +05:45
Prafulla Giri
1000ea6600 scripts: add comments. 2023-10-10 20:44:10 +05:45
Prafulla Giri
746fe3fe60 scripts: combine *.list files into a single one to avoid duplicate entries. 2023-10-10 20:26:33 +05:45
Prafulla Giri
f72c5080b4 scripts: revise previous changes as per review. 2023-10-10 15:40:22 +05:45
Prafulla Giri
4da77685db scripts: further update scripts to work better with derivative distros.
- copy *.list files from /etc/apt/sources.list.d into the chroot
- inside the chroot, filter out references to the derivative distro's
  package sources.
2023-10-09 21:47:27 +05:45
matt335672
e7cb3273b0
Merge pull request #99 from rch-W/devel
Update install_pulseaudio_sources_apt.sh for Debian 12
2023-07-31 11:29:24 +01:00
rch-W
c1b9d8fb59
Update install_pulseaudio_sources_apt.sh
Updated $RELEASE value for Debian 12 release
2023-07-05 22:28:59 +02:00
metalefty
f4b5d145c9
Merge pull request #95 from metalefty/prepare-release
bump version to v0.7
2023-02-28 09:03:50 +09:00
Koichiro IWAO
c3307ab670
bump version to v0.7 2023-02-27 17:11:26 +09:00
matt335672
f6ab4e62a6
Merge pull request #93 from matt335672/support_debian_testing
Support Debian bookworm in wrapped build script
2023-02-13 10:24:12 +00:00
matt335672
4e98356c41 Support Debian bookworm in wrapped build script 2023-02-13 10:22:02 +00:00
matt335672
afd3820b5a
Merge pull request #91 from akarl10/soundcard-class
plasma 5.26 does not like PA_PROP_DEVICE_CLASS abstract
2023-02-06 11:01:50 +00:00
Michael Saxl
0a1fd9a749 plasma 5.26 does not like PA_PROP_DEVICE_CLASS abstract
according to doxygen only
  "sound", "modem", "monitor", "filter"
are allowed. Use sound as this is the most appropriate

also plasma-pa hides virtual devices by default.

flag them as hardware since to the user this virtual card behaves like
that. It is not some kind of filter or secondary output
2023-02-02 15:09:44 +01:00
matt335672
0e9c1dd23a
Merge pull request #92 from matt335672/fix_kali_2022
Install Doxygen for Kali 2022 for building
2023-01-31 09:17:56 +00:00
matt335672
a54adc9027 Install Doxygen for Kali 2022 2023-01-31 08:48:15 +00:00
matt335672
83d88be127
Merge pull request #88 from matt335672/ci_fix
Update CI build script
2022-10-20 14:15:25 +01:00
matt335672
af40e18723 Update CI build script
The code to remove incompatible libunwind development packages
works, but only by chance.

This update searches explicitly for incompatible packages and
then removes them.
2022-10-20 14:06:34 +01:00
matt335672
279fa8ef73
Merge pull request #87 from matt335672/update_actions
Update github actions to address warnings
2022-10-20 11:52:58 +01:00
matt335672
8e46b8f7b5 Fix CI build on github jammy VMs
Remove the libunwind-14-dev package before installing the PA
build dependencies, as this is incompatible with libunwind-dev
2022-10-20 11:33:25 +01:00
matt335672
8421b30054 Update github actions to address warnings
- Update github actions version for node.js 16
- Update github ::set-output usage
2022-10-20 10:21:13 +01:00
matt335672
308b6f22ce
Merge pull request #83 from matt335672/update_actions
github actions: use canonical way to get ImageOS
2022-05-27 12:43:20 +01:00
matt335672
1b2b6c66a3 github actions: use canonical way to get ImageOS 2022-05-27 12:37:16 +01:00
matt335672
221b287387
Merge pull request #77 from matt335672/remove_home_dependency
Remove dependency on mounted /home from the wrapper script
2022-05-12 17:04:22 +01:00
matt335672
6ebbdc632f Remove dependency on mounted /home from the wrapper script 2022-05-12 17:02:34 +01:00
matt335672
c610894c13
Merge pull request #81 from matt335672/restrictive_umask_support
Fix broken wrapper with restrictive root umask
2022-05-12 15:49:27 +01:00
matt335672
5a1e458228 Fix broken wrapper with restrictive root umask 2022-05-12 15:43:01 +01:00
matt335672
93307a6b0b
Merge pull request #78 from matt335672/suite_support
Add suite support to the wrapper script
2022-03-31 10:21:28 +01:00
matt335672
39ed564d0c Add suite support to the wrapper script 2022-03-14 12:25:41 +00:00
matt335672
1905083e71
Merge pull request #74 from matt335672/specify_mirror
Add mirror and keyring support to install_pulseaudio_sources_apt_wrapper.sh
2022-02-28 11:41:32 +00:00
matt335672
08f0ae1224 Add mirror and keyring support to install_pulseaudio_sources_apt_wrapper.sh 2022-02-25 11:15:41 +00:00
metalefty
4d572a7549
Merge pull request #70 from metalefty/prepare-release
bump version to v0.6
2021-11-19 14:39:56 +09:00
Koichiro IWAO
4ab0fed65a
bump version to v0.6 2021-11-19 14:21:50 +09:00
matt335672
0b30713223
Merge pull request #69 from matt335672/remote_tabs_from_scripts
Remove unnecessary tabs from scripts
2021-11-18 10:20:35 +00:00
matt335672
7fa46fde52 Remove unnecessary tabs from scripts 2021-11-18 10:16:08 +00:00
matt335672
27f51ee718
Merge pull request #68 from matt335672/pa15_support
Add support for Meson build system used by Pulseaudio 15
2021-11-18 10:12:33 +00:00
matt335672
c4a4e89be2 Support Meson in Ubuntu build scripts 2021-11-10 11:21:07 +00:00
matt335672
21dae611a4 Backward compatible support for PA15
- config.h is now search for under PULSE_DIR
- Location of config.h can be specified explicitly with PULSE_CONFIG_DIR
- Extra warning if PULSE_DIR appears to be invalid
2021-11-10 11:17:47 +00:00
matt335672
fc9fc663b9
Merge pull request #67 from matt335672/update_readme
Update README.md
2021-11-09 10:52:09 +00:00
matt335672
6ac122ac48
Merge pull request #64 from matt335672/instfiles
Add install files to the build
2021-11-09 10:48:45 +00:00
matt335672
63f37b0795
Merge pull request #62 from matt335672/fix_ubuntu16
Improve Debian/Ubuntu build process for users
2021-11-09 10:48:05 +00:00
matt335672
ef3cc29016
Merge pull request #59 from matt335672/module_params
Module load and unload support
2021-11-09 10:44:52 +00:00
matt335672
2a326d7411 Changes following review and retest
- Find pactl in PATH
- Set default source/sink for xrdp sessions
- Exit status returned from instfiles/load_pa_modules.sh
2021-11-09 08:58:08 +00:00
matt335672
d37b40dc07 Make xdgautostartdir configurable 2021-11-04 13:55:53 +00:00
matt335672
9611bbf50b Update README.md
- Refer to the wiki for build instructions
- Update CI and support links
- Small tidy-ups
2021-11-03 11:22:47 +00:00
matt335672
91c239ee58 Add install files to the build 2021-10-30 15:10:57 +01:00
matt335672
d2284f11fc Fixed compiler warning with -Wunused-parameter 2021-10-30 12:26:52 +01:00
matt335672
22b84e312e
Merge pull request #63 from matt335672/version_info
Add version logging to modules
2021-10-20 11:26:57 +01:00
matt335672
9fd159e88e Add version logging to modules 2021-10-20 11:01:59 +01:00
matt335672
b63e6fda01 Rename scripts to include _apt 2021-10-15 12:00:22 +01:00
matt335672
3a1ef90637 Improvements to usability for PA source build script 2021-10-15 11:25:46 +01:00
matt335672
b4d03c5545
Merge pull request #61 from matt335672/extra_actions
CI Improvements
2021-10-13 09:35:37 +01:00
matt335672
e447bc4cff Fix extra compiler warnings 2021-10-11 20:34:05 +01:00
matt335672
345ed66fab Add extra compiler and separate out cache stage 2021-10-11 20:34:05 +01:00
matt335672
95c96f86d6
Merge pull request #60 from matt335672/github_actions
Replace Travis-CI with Github Actions for build
2021-10-07 10:16:39 +01:00
matt335672
60a2ba77e3 Replace Travis-CI with Github Actions for build 2021-10-06 15:11:58 +01:00
matt335672
6f1857357d Latency improvements to the sink 2021-09-30 12:34:16 +01:00
matt335672
127a7e27ae Close file socket on module unload 2021-09-30 12:34:16 +01:00
matt335672
2d4cb1ea96 Added module params to xrdp-sink.c 2021-09-30 12:34:16 +01:00
jsorg71
8b3c7f30cc
Merge pull request #56 from jsorg71/readme
update README.md for MS-RDPEAI
2021-04-13 21:49:28 -07:00
Jay Sorg
52e0f92420 update README.md for MS-RDPEAI 2021-04-13 12:00:14 -07:00
metalefty
098806384f
Merge pull request #50 from metalefty/prepare-release
bump version to v0.5
2021-01-07 11:55:59 +09:00
Koichiro IWAO
7c11508b06
bump version to v0.5 2021-01-07 11:45:28 +09:00
metalefty
1cde8f8f68
Merge pull request #49 from metalefty/typo
README: fix typo
2021-01-05 23:10:50 +09:00
Koichiro IWAO
5a4b5a1f4f README: fix typo
Closes: #45
2021-01-05 21:21:06 +09:00
metalefty
5a8679f9b9
Merge pull request #48 from metalefty/license
Switch LICENSE to LGPLv2.1
2021-01-04 21:22:17 +09:00
Koichiro IWAO
e0a3fc3935
Switch LICENSE to LGPLv2.1
Closes: #36
2021-01-04 15:32:28 +09:00
jsorg71
e9f78c4f3a
Merge pull request #39 from jsorg71/mofd
use fd -1 not 0 for invalid fd
2019-11-20 21:34:00 -08:00
Jay Sorg
0bc53f5b40 use fd -1 not 0 for invalid fd 2019-11-19 01:42:48 -08:00
jsorg71
1d4947dc34
Merge pull request #38 from jsorg71/d10fix
fix for delay in sink
2019-11-18 21:48:34 -08:00
Jay Sorg
c63041f5c7 fix for delay in sink 2019-11-16 10:18:02 -08:00
metalefty
22d270b283
Merge pull request #35 from metalefty/prepare-release
bump version to v0.4
2019-10-21 14:33:12 +09:00
Koichiro IWAO
9be32b2cfd bump version to v0.4 2019-10-21 14:20:52 +09:00
jsorg71
5d36b870a7
Merge pull request #34 from jsorg71/sink1
source: move pa_memblock_new to data_get
2019-10-16 20:45:16 -07:00
Jay Sorg
1c983fa112 source: move pa_memblock_new to data_get 2019-10-15 22:26:56 -07:00
metalefty
254f598771
Merge pull request #29 from metalefty/prepare-release
Bump version to v0.3
2019-06-05 13:37:29 +09:00
Koichiro IWAO
345beb2287 Bump version to v0.3 2019-06-05 13:22:00 +09:00
jsorg71
48bddf21cb
Merge pull request #28 from jsorg71/env1
Fix some typos in source, change some logging to match sink
2019-06-03 17:19:40 -07:00
Jay Sorg
cc2453d55d source: use pa_log_debug instead of pa_log, a little bit too noisy 2019-06-02 20:57:51 -07:00
Jay Sorg
009e39f071 source: sink => source typos 2019-06-02 20:55:35 -07:00
Koichiro IWAO
3a24c3f77a Bump version to v0.2 2018-09-25 15:49:19 +09:00
metalefty
113c2523da
Merge pull request #20 from metalefty/sockname
Fix segfault when socket names not given via env #19
2018-09-08 23:49:18 +09:00
Koichiro IWAO
452cb29164 Fix segfault when socket names not given via env #19
snprintf requires malloc before copying strings. I didn't do enough test the case
when socket names were not given via environment variable.
2018-09-07 11:06:52 +09:00
metalefty
b88ff01348
Merge pull request #18 from metalefty/README
README: sync to the wiki
2018-09-05 09:20:58 +09:00
Koichiro IWAO
3eecd70907 README: sync to the wiki
CentOS 7.5 or later requires pulse modules to be built with distro's RPM
source.
2018-09-04 16:49:34 +09:00
metalefty
ce110ead22
Merge pull request #15 from metalefty/remove-xrdp-dependency
Remove xrdp dependency
2018-09-04 16:45:06 +09:00
Koichiro IWAO
389667c3e3 use pa_log_debug instead of pa_log, a little bit too noisy 2018-09-04 16:23:27 +09:00
Koichiro IWAO
0bc8be7243 add XRDP_ prefix to xrdp related environment variables
and remove CHANSRV, use the shorter name
2018-09-04 16:23:27 +09:00
Koichiro IWAO
80ee64dbc9 add some log, fix indent 2018-09-04 16:23:27 +09:00
Koichiro IWAO
6509fea778 pass sockets names via environment variable
now this module no longer depends on xrdp headers.
2018-09-04 16:23:27 +09:00
Koichiro IWAO
b049cec8db configure: remove xrdp dependency 2018-09-04 16:23:27 +09:00
Koichiro IWAO
f8859e3124 remove xrdp dependency from travis build 2018-09-04 16:23:27 +09:00
metalefty
7bf2c41925
Merge pull request #17 from metalefty/travis
build with distro's pulseaudio source
2018-08-29 17:57:15 +09:00
Koichiro IWAO
5a2232244f build with distro's pulseaudio source 2018-08-29 17:15:37 +09:00
16 changed files with 1757 additions and 320 deletions

60
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,60 @@
name: build
on:
push:
pull_request:
jobs:
install_pa_sources:
name: install PA sources
runs-on: ubuntu-latest
steps:
# Set steps.os.outputs.image to the specific OS (e.g. 'ubuntu20')
- name: Get operating system name and version.
id: os
run: echo "image=$ImageOS" >>$GITHUB_OUTPUT
shell: bash
- uses: actions/checkout@v3
- name: Cache pulseaudio source
uses: actions/cache@v3
env:
cache-name: cache-pulseaudio-src
with:
path: ~/pulseaudio.src
key: ${{ steps.os.outputs.image }}-build-${{ env.cache-name }}
- run: scripts/install_pulseaudio_sources_apt.sh
build:
strategy:
fail-fast: false
matrix:
include:
- CC: gcc
- CC: clang
name: build with ${{ matrix.CC }}
runs-on: ubuntu-latest
needs: install_pa_sources
env:
CC: ${{ matrix.CC }}
CFLAGS: -Wall -Wextra -Werror
steps:
- name: Get operating system name and version.
id: os
run: echo "image=$ImageOS" >>$GITHUB_OUTPUT
shell: bash
- name: Fetch pulseaudio sources
uses: actions/cache@v3
env:
cache-name: cache-pulseaudio-src
with:
path: ~/pulseaudio.src
key: ${{ steps.os.outputs.image }}-build-${{ env.cache-name }}
- uses: actions/checkout@v3
- run: sudo apt-get update
- run: sudo apt-get -yq install build-essential libpulse-dev libltdl-dev
- run: ./bootstrap
- run: ./configure PULSE_DIR=~/pulseaudio.src
- run: make

View File

@ -1,21 +0,0 @@
#!/usr/bin/env bash
SRC_DIR=${PWD}
cd /tmp
yum install -y epel-release
yum install -y xrdp xrdp-devel xrdp-selinux wget
yum install -y pulseaudio pulseaudio-libs pulseaudio-libs-devel
yum-builddep -y pulseaudio
yum groupinstall -y "Development Tools"
PULSE_VER=$(pkg-config --modversion libpulse)
# not to make traffic on upstream server
wget http://distcache.freebsd.org/ports-distfiles/pulseaudio-${PULSE_VER}.tar.xz
tar xf pulseaudio-${PULSE_VER}.tar.xz
cd pulseaudio-${PULSE_VER}
./configure || exit 1
cd ${SRC_DIR}
./bootstrap && ./configure PULSE_DIR=/tmp/pulseaudio-${PULSE_VER} && make

View File

@ -1,22 +0,0 @@
#!/usr/bin/env bash
SRC_DIR=${PWD}
cd /tmp
sed -i.bak -e 's|^# deb-src|deb-src|' /etc/apt/sources.list
apt update
apt install -y build-essential dpkg-dev libpulse-dev pulseaudio pkg-config xrdp
apt install -y g++ clang
apt install -y pulseaudio
apt build-dep -y pulseaudio
apt source pulseaudio
PULSE_VER=$(pkg-config --modversion libpulse)
cd pulseaudio-${PULSE_VER}
./configure || exit 1
cd ${SRC_DIR}
./bootstrap && ./configure PULSE_DIR=/tmp/pulseaudio-${PULSE_VER} && make

View File

@ -1,18 +0,0 @@
# vim:ts=2:sw=2:sts=0:number:expandtab
language: c
env:
matrix:
- OS_TYPE=ubuntu OS_VERSION=18.04 BRANCH=devel
- OS_TYPE=ubuntu OS_VERSION=18.04 BRANCH=master
- OS_TYPE=centos OS_VERSION=7 BRANCH=devel
- OS_TYPE=centos OS_VERSION=7 BRANCH=master
services:
- docker
before_install:
- docker pull ${OS_TYPE}:${OS_VERSION}
script:
- docker run --rm --interactive --tty --volume=${PWD}:${PWD} ${OS_TYPE}:${OS_VERSION} bash -c "cd ${PWD} && bash .travis.${OS_TYPE}.sh"

502
LICENSE Normal file
View File

@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library 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.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

View File

@ -1,4 +1,4 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src
SUBDIRS = src instfiles
EXTRA_DIST = bootstrap.sh

134
README.md
View File

@ -1,9 +1,5 @@
[![Build Status](https://travis-ci.org/neutrinolabs/pulseaudio-module-xrdp.svg?branch=devel)](https://travis-ci.org/neutrinolabs/pulseaudio-module-xrdp)
The latest version of this document can be found at wiki.
* https://github.com/neutrinolabs/pulseaudio-module-xrdp/wiki/README
[![Build Status](https://github.com/neutrinolabs/pulseaudio-module-xrdp/actions/workflows/build.yml/badge.svg)](https://github.com/neutrinolabs/pulseaudio-module-xrdp/actions)
[![Gitter (xrdp)](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/neutrinolabs/xrdp-questions)
# Overview
xrdp implements Audio Output redirection using PulseAudio, which is a sound
@ -15,107 +11,59 @@ Protocol: Audio Output Virtual Channel Extension
which means it is interoperable with any RDP client which implements it
(most of them including: MS RDP clients, FreeRDP).
However, our Microphone redirection (client to server) implementation is
proprietary and doesn't implement the
[[MS-RDPEAI]](https://msdn.microsoft.com/en-us/library/dd342521.aspx)
specs. Its only supported by the following clients so far: NeutrinoRDP client
and rdesktop.
Here is how to build pulseaudio modules for your distro, so you can have audio
support through xrdp.
In this instruction, pulseaudio version is **11.1**. You need to **replace the
version number in this instruction** if your environment has different
versions. You can find out your pulseaudio version executing the following
command:
pulseaudio --version
or
pkg-config --modversion libpulse
The client to server audio redirection is implemented as per **Remote Desktop
Protocol: Audio Input Redirection Virtual Channel Extension
[[MS-RDPEAI]](https://msdn.microsoft.com/en-us/library/dd342521.aspx)**
which means it is interoperable with any RDP client which implements it
(most of them including: MS RDP clients, FreeRDP).
# How to build
These modules make use of the internal pulseaudio module API. To build
them you need access to the pulseaudio sources and configuration.
## Debian 9 / Ubuntu
*Be aware that the pulseaudio application development packages provided
with many distributions do not contain the files necessary to use the
pulseaudio module API*. Consequently, the preparation for building these
modules can be a little more involved than just installing development
tools and packages.
This instruction also should be applicable to the Ubuntu family.
Consult the Pulseaudio Wiki for instructions on building the modules
for your platform:-
### Prerequisites
Some build tools and package development tools are required. Make sure install
the tools.
apt install build-essential dpkg-dev
### Prepare & build
Install pulseaudio and requisite packages to build pulseaudio.
apt install pulseaudio
apt build-dep pulseaudio
Fetch the pulseaudio source. You'll see `pulseaudio-11.1` directory in your
current directory.
apt source pulseaudio
Enter into the directory and build the pulseaudio package.
cd pulseaudio-11.1
./configure
Finally, let's build xrdp source / sink modules. You'll have two .so files
`module-xrdp-sink.so` and `module-xrdp-source.so`.
git clone https://github.com/neutrinolabs/pulseaudio-module-xrdp.git
cd pulseaudio-module-xrdp
./bootstrap && ./configure PULSE_DIR=/path/to/pulseaudio-11.1
make
## Other distro
First off, find out your pulseaudio version using `pulseaudio --version`
command. Download the tarball of the pulseaudio version that you have.
* https://freedesktop.org/software/pulseaudio/releases/
After downloading the tarball, extract the tarball and `cd` into the source
directory, then run `./configure`.
wget https://freedesktop.org/software/pulseaudio/releases/pulseaudio-11.1.tar.xz
tar xf pulseaudio-11.1.tar.xz
cd pulseaudio-11.1
./configure
If additional packages are required to run `./configure`, install requisite
packages depending on your environment.
Finally, let's build xrdp source / sink modules. You'll have two .so files
`module-xrdp-sink.so` and `module-xrdp-source.so`.
git clone https://github.com/neutrinolabs/pulseaudio-module-xrdp.git
cd pulseaudio-module-xrdp
./bootstrap && ./configure PULSE_DIR=/path/to/pulseaudio-11.1
make
https://github.com/neutrinolabs/pulseaudio-module-xrdp/wiki
# Install
One the modules have been built, `sudo make install` should do the following:-
- Install the modules to the correct directory
- Install a script `load_pa_modules.sh` to load the modules when a
session is started.
On many systems this script is installed by default in
/usr/libexec/pulseaudio-module-xrdp/
- Install a desktop file `pulseaudio-xrdp.desktop` which will call the
`load_pa_modules.sh` script when a desktop is loaded.
On many systems this script is installed by default in
`/etc/xdg/autostart`
Just `make install` should install built modules to the correct directory.
Note that the modules will only be loaded automatically when the desktop
starts if your desktop supports the XDG autostart specification. Most modern
desktops support this.
You can confirm if the modules properly installed by following command:
You can confirm if the modules are properly installed by following command:
```
ls $(pkg-config --variable=modlibexecdir libpulse)
ls $(pkg-config --variable=modlibexecdir libpulse) | grep xrdp
```
If you can see lots of `module-*.so` and `module-xrdp-sink.so`,
`module-xrdp-source.so`, PulseAudio modules should be properly built and
installed.
If you can see `module-xrdp-sink.so` and `module-xrdp-source.so`,
PulseAudio modules are properly built and installed.
Enjoy!
# See if it works
The easiest way to test this is to use the `paplay` command to play an
audio file.
To see if it works, run `pavumeter` in the xrdp session. Playback any YouTube
video in Firefox. You'll see "Showing signal levels of **xrdp sink**" and
volume meter moving.
You can also do the following:-
- run `pavumeter` in the xrdp session-
- Playback any YouTube video in Firefox.
You'll see "Showing signal levels of **xrdp sink**" and volume meter moving.

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.65])
AC_INIT([pulseaudio-module-xrdp], [0.1], [xrdp-devel@googlegroups.com])
AC_INIT([pulseaudio-module-xrdp], [0.8], [xrdp-devel@googlegroups.com])
# specify config_ac.h so we don't collide with pulseaudio's config.h
AC_CONFIG_HEADERS([config_ac.h])
AC_CONFIG_SRCDIR([src])
@ -20,6 +20,16 @@ fi
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
dnl ------------------------------------------------------------------
dnl FIND_CONFIG_H(TOP_DIRECTORY)
dnl Find the config.h file under TOP_DIRECTORY
dnl
dnl Outputs the enclosing directory. Only the first match is returned.
dnl ------------------------------------------------------------------
m4_define([FIND_CONFIG_H],
[find $1 -type f -maxdepth 3 -type f -name config.h | \
sed -e 's#/config.h##' -e '2,$d'])
# get system's pulseaudio version
m4_define([pa_major], [`$PKG_CONFIG --modversion libpulse | cut -d. -f1`])
m4_define([pa_minor], [`$PKG_CONFIG --modversion libpulse | cut -d. -f2`])
@ -28,6 +38,9 @@ PA_LIBDIR=`$PKG_CONFIG --variable=libdir libpulse`
PA_MODDIR=`$PKG_CONFIG --variable=modlibexecdir libpulse`
PA_PREFIX=`$PKG_CONFIG --variable=prefix libpulse`
# Default system-wide autostart directory from XDG autostart specification
m4_define([XDG_AUTOSTART_DIR], /etc/xdg/autostart)
AC_SUBST([PA_MAJOR], [pa_major])
AC_SUBST([PA_MINOR], [pa_minor])
AC_SUBST([PA_MAJORMINOR], [pa_major].[pa_minor])
@ -45,12 +58,11 @@ AC_PROG_LN_S
AC_PROG_MAKE_SET
# Checks for libraries.
PKG_CHECK_MODULES([XRDP], [xrdp >= 0.9.0])
AC_SUBST(XRDP_CFLAGS)
PKG_CHECK_MODULES([LIBPULSE], [libpulse])
m4_define([PULSE_MSG], [PULSE_DIR not specified. Follow the instructions in README.md.])
# Check PULSE_DIR is specified
AC_ARG_VAR([PULSE_DIR], [pulseaudio source code directory])
AS_IF([test x"$PULSE_DIR" == x""],
cat <<__MSG__
@ -59,6 +71,31 @@ __MSG__
AC_MSG_ERROR([PULSE_DIR not specified])
)
# Does PULSE_DIR appear to be valid?
AS_IF([test -e "$PULSE_DIR/src/pulsecore/macro.h"],,
AC_MSG_WARN([PULSE_DIR may not be valid - can't find expected file]))
# Look for config.h, using PULSE_CONFIG_DIR if specified
AC_ARG_VAR([PULSE_CONFIG_DIR], [pulseaudio config.h source code directory (optional)])
AS_IF([test x"$PULSE_CONFIG_DIR" == x""],
AC_MSG_NOTICE([PULSE_CONFIG_DIR not defined])
AC_MSG_CHECKING([Searching for config.h under PULSE_DIR])
PULSE_CONFIG_DIR="`FIND_CONFIG_H(\"$PULSE_DIR\")`"
[AS_IF([test -e "$PULSE_CONFIG_DIR/config.h" ],
AC_MSG_RESULT([$PULSE_CONFIG_DIR/config.h])
,
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find config.h under PULSE_DIR. Define PULSE_CONFIG_DIR?]))]
,
AC_MSG_NOTICE([PULSE_CONFIG_DIR is defined])
AC_MSG_CHECKING([Looking for config.h in PULSE_CONFIG_DIR])
[AS_IF([test -e "$PULSE_CONFIG_DIR/config.h"],
AC_MSG_RESULT([$PULSE_CONFIG_DIR/config.h])
,
AC_MSG_RESULT([no])
AC_MSG_ERROR([Can't find config.h in PULSE_CONFIG_DIR.]))])
# use the prefix as same as pulseaudio
AC_PREFIX_PROGRAM(pulseaudio)
@ -70,6 +107,13 @@ AC_ARG_WITH(
[modlibexecdir=$withval], [modlibexecdir="${PA_MODDIR}"])
AC_SUBST(modlibexecdir)
AC_ARG_WITH(
[xdgautostart-dir],
[AS_HELP_STRING([--with-xdgautostart-dir],
Directory to install the desktop file (defaults to XDG_AUTOSTART_DIR))],
[xdgautostartdir=$withval], [xdgautostartdir=XDG_AUTOSTART_DIR])
AC_SUBST(xdgautostartdir)
# Checks for header files.
AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h stdlib.h string.h sys/ioctl.h sys/socket.h unistd.h])
@ -82,5 +126,6 @@ AC_TYPE_UINT32_T
AC_CHECK_FUNCS([memset socket])
AC_CONFIG_FILES([Makefile
src/Makefile])
src/Makefile
instfiles/Makefile])
AC_OUTPUT

29
instfiles/Makefile.am Normal file
View File

@ -0,0 +1,29 @@
EXTRA_DIST = \
load_pa_modules.sh \
pulseaudio-xrdp.desktop.in
#
# substitute directories in service file
#
CLEANFILES= \
pulseaudio-xrdp.desktop
SUBST_VARS = sed \
-e 's|@pkglibexecdir[@]|$(pkglibexecdir)|g'
subst_verbose = $(subst_verbose_@AM_V@)
subst_verbose_ = $(subst_verbose_@AM_DEFAULT_V@)
subst_verbose_0 = @echo " SUBST $@";
SUFFIXES = .in
.in:
$(subst_verbose)$(SUBST_VARS) $< > $@
#
# files for all platforms
#
xdgautostart_DATA = \
pulseaudio-xrdp.desktop
pkglibexec_SCRIPTS = \
load_pa_modules.sh

54
instfiles/load_pa_modules.sh Executable file
View File

@ -0,0 +1,54 @@
#!/bin/sh
status=0
if [ -n "$XRDP_SESSION" -a -n "$XRDP_SOCKET_PATH" ]; then
# These values are not present on xrdp versions before v0.9.8
if [ -z "$XRDP_PULSE_SINK_SOCKET" -o \
-z "$XRDP_PULSE_SOURCE_SOCKET" ]; then
displaynum=${DISPLAY##*:}
displaynum=${displaynum%.*}
XRDP_PULSE_SINK_SOCKET=xrdp_chansrv_audio_out_socket_$displaynum
XRDP_PULSE_SOURCE_SOCKET=xrdp_chansrv_audio_in_socket_$displaynum
fi
# Don't check for the presence of the sockets, as if the modules
# are loaded they won't be there
# Unload modules
pactl unload-module module-xrdp-sink >/dev/null 2>&1
pactl unload-module module-xrdp-source >/dev/null 2>&1
# Reload modules
if pactl load-module module-xrdp-sink \
xrdp_socket_path=$XRDP_SOCKET_PATH \
xrdp_pulse_sink_socket=$XRDP_PULSE_SINK_SOCKET
then
echo "- pulseaudio xrdp-sink loaded"
if pacmd set-default-sink xrdp-sink; then
echo "- pulseaudio xrdp-sink set as default"
else
echo "? Can't set pulseaudio xrdp-sink as default"
fi
else
echo "? Can't load pulseaudio xrdp-sink"
status=1
fi
if pactl load-module module-xrdp-source \
xrdp_socket_path=$XRDP_SOCKET_PATH \
xrdp_pulse_source_socket=$XRDP_PULSE_SOURCE_SOCKET
then
echo "- pulseaudio xrdp-source loaded"
if pacmd set-default-source xrdp-source; then
echo "- pulseaudio xrdp-source set as default"
else
echo "? Can't set pulseaudio xrdp-source as default"
fi
else
echo "? Can't load pulseaudio xrdp-source"
status=1
fi
fi
exit $status

View File

@ -0,0 +1,9 @@
[Desktop Entry]
Version=1.0
Name=PulseAudio xrdp modules
Comment=Load PulseAudio Modules for xrdp
Exec=@pkglibexecdir@/load_pa_modules.sh
Terminal=false
Type=Application
Categories=
GenericName=

View File

@ -0,0 +1,197 @@
#!/bin/sh
#
# xrdp: A Remote Desktop Protocol server.
#
# Copyright (C) 2021 Matt Burt, all xrdp contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Builds the pulseaudio sources on Debian/Ubuntu and writes the internal
# pulseaudio files needed to build the xrdp pulseaudio module into a
# single directory
# This script will pollute the machine with all the pulseaudio dependencies
# needed to build the pulseaudio dpkg. If this isn't acceptable, consider
# running this script in a schroot (or similar) wrapper.
# Use '-d <dir>' to specify an alternate directory
set -e ; # Exit on any error
# Target directory for output files
PULSE_DIR="$HOME/pulseaudio.src" ; # Default if nothing is specified
# Argument processing
while [ $# -gt 0 ]; do
arg="$1"
shift
case "$arg" in
-d) if [ $# -gt 0 ]; then
PULSE_DIR="$1"
shift
else
echo "** $arg needs an argument" >&2
fi
;;
*) echo "** Unrecognised argument '$arg'" >&2
esac
done
if [ ! -d "$PULSE_DIR" ]; then
# Operating system release ?
RELEASE="$(lsb_release -si)-$(lsb_release -sr)"
codename=$(lsb_release -cs)
echo "Building for : $RELEASE ($codename)"
# Do any special-case stuff related to repositories
case $(lsb_release -si) in
Ubuntu)
# Enable the universe repository. Don't use add-apt-repository
# as this has a huge number of dependencies.
if [ -f /etc/apt/sources.list ] && \
! grep -q '^ *[^#].* universe *' /etc/apt/sources.list; then
echo "- Adding 'universe' repository" >&2
cp /etc/apt/sources.list /tmp/sources.list
while read type url suite rest; do
if [ "$type" = deb -a "$rest" = main ]; then
case "$suite" in
$codename | $codename-updates | $codename-security)
echo "deb $url $suite universe"
;;
esac
fi
done </tmp/sources.list \
| sudo tee -a /etc/apt/sources.list >/dev/null
rm /tmp/sources.list
fi
;;
esac
# Scan the source repositories. Add sources for all repositories
# in this suite.
# Ignore other suites. This is needed when running the wrapper in a
# derivative-distro (like Linux Mint 21.2 'victoria') with --suite
# option (--suite=jammy).
echo "- Adding source repositories" >&2
SRCLIST=$(find /etc/apt/ /etc/apt/sources.list.d -maxdepth 1 -type f -name '*.list')
if [ -n "$SRCLIST" ]; then
# Older-style .list files have been detected
# Create a combined file for all .list sources, adding deb-src
# directives.
for srclst in $SRCLIST; do
while read type url suite rest; do
case "$suite" in
$codename | $codename-updates | $codename-security)
if [ "$type" = deb ]; then
echo "deb $url $suite $rest"
echo "deb-src $url $suite $rest"
fi
;;
esac
done <$srclst
done >/tmp/combined_sources.list
sudo rm $SRCLIST ;# Remove source respositories
# remove duplicates from the combined sources.list in order to prevent
# apt warnings/errors; this is useful in cases where the user has
# already configured source code repositories.
sort -u < /tmp/combined_sources.list | \
sudo tee /etc/apt/sources.list > /dev/null
fi
# Cater for DEB822 .sources files. These can appear alongside the
# older format.
for src in $(find /etc/apt/sources.list.d -maxdepth 1 -type f -name '*.sources'); do
# If we can find a match for the codename in the file, enable
# sources for all elements of the file. We assume that different
# codenames will be assigned to different files
if grep -iq "^suites:.* $codename" $src; then
sudo sed -i 's/^Types: deb/Types: deb deb-src/' "$src"
fi
done
sudo apt-get update
# For the CI build on 22.04, it was noted that an incompatible libunwind
# development package libunwind-14-dev was installed, which prevented
# installation of the default libunwind-dev package.
#
# Remove any libunwind-*-dev package
pkg_list=`dpkg-query -W -f '${Package} ' 'libunwind-*-dev' 2>/dev/null || :`
if [ -n "$pkg_list" ]; then
echo "- Removing package(s) $pkg_list"
sudo apt-get remove -y $pkg_list
fi
sudo apt-get build-dep -y pulseaudio
# Install any missing dependencies for this software release
case "$RELEASE" in
Ubuntu-16.04)
sudo apt-get install -y libjson-c-dev
;;
Kali-2022*)
sudo apt-get install -y doxygen
;;
Debian-12)
# Debian testing build
case "$codename" in
bookworm)
sudo apt-get install -y doxygen
;;
esac
;;
esac
cd "$(dirname $PULSE_DIR)"
apt-get source pulseaudio
build_dir="$(find . -maxdepth 1 -name pulseaudio-[0-9]\*)"
if [ -z "$build_dir" ]; then
echo "** Can't find build directory in $(ls)" >&2
exit 1
fi
cd "$build_dir"
if [ -x ./configure ]; then
# This version of PA uses autotools to build
# This command creates ./config.h
./configure
elif [ -f ./meson.build ]; then
# Meson only
rm -rf build
# This command creates ./build/config.h
meson build
else
echo "** Unable to configure pulseaudio from files in $(pwd)" >&2
false
fi
echo "- Removing unnecessary files"
# We only need .h files...
find . -type f \! -name \*.h -delete
# .. in src/ and /build directories
find . -mindepth 1 -maxdepth 1 \
-name src -o -name build -o -name config.h \
-o -exec rm -rf {} +
echo "- Renaming $(pwd)/$build_dir as $PULSE_DIR"
cd ..
mv "$build_dir" "$PULSE_DIR"
fi
exit 0

View File

@ -0,0 +1,276 @@
#!/bin/sh
#
# xrdp: A Remote Desktop Protocol server.
#
# Copyright (C) 2021 Matt Burt, all xrdp contributors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Wrapper to call install_pulseaudio_sources.sh and tidy up afterwards
#
# The following command line switches are supported, for systems based on
# Debian or Ubuntu:-
#
# 1) --mirror= Specify an alternative mirror for debootstrap
# 2) --keyring= Specify an alternative keyring for debootstrap
# 3) --suite= Specify an alternative suite for debootstrap
#
# The first two of these are are needed for systems with their own
# mirrors and signing keys (i.e. Raspberry PI OS).
#
# --suite is useful for systems which report their own codename for
# `lsb_release -c`, but are otherwise based on a standard distro. For
# example Linux Mint 20.04 reports 'una', but is largely based on
# Ubuntu 'focal'
# ---------------------------------------------------------------------------
# G L O B A L S
# ---------------------------------------------------------------------------
# Where the output files are going
PULSE_DIRNAME=pulseaudio.src
PULSE_DIR=$HOME/$PULSE_DIRNAME
# Absolute path to the script we're wrapping. This picks it up from
# the same directory this file is in
WRAPPED_SCRIPT=$(cd $(dirname $0) && pwd)/install_pulseaudio_sources_apt.sh
# The buildroot directory. Choose fast, temporary storage if available
BUILDROOT=/var/lib/pa-build/$USER
# Extra packages to install in the build root which the wrapped script
# may be using. These are packages available by default when using
# GitHub actions
#
# ca-certificates are needed to fetch updates over https
#
# This list is comma-separated
WRAPPED_SCRIPT_DEPS="ca-certificates,sudo,lsb-release"
# -----------------------------------------------------------------------------
# S U I T E E X I S T S
#
# Does the specified debootstrap suite exist?
# -----------------------------------------------------------------------------
SuiteExists()
{
[ -f "/usr/share/debootstrap/scripts/$1" ]
}
# -----------------------------------------------------------------------------
# I N S T A L L R E Q U I R E D P A C K A G E S
#
# Installs packages required for the build on the host machine
# -----------------------------------------------------------------------------
InstallRequiredPackages()
{
set -- \
/usr/sbin/debootstrap debootstrap \
/usr/bin/schroot schroot \
/usr/bin/lsb_release lsb-release
pkgs=
while [ $# -ge 2 ]; do
if [ ! -x $1 ]; then
pkgs="$pkgs $2"
fi
shift 2
done
if [ -n "$pkgs" ]; then
echo "- Need to install packages :$pkgs"
echo
echo " These can be removed when this script completes with:-"
echo " sudo apt-get purge$pkgs && apt-get autoremove"
echo
sudo apt-get install -y $pkgs
fi
}
# -----------------------------------------------------------------------------
# R U N W R A P P E D S C R I P T
#
# Runs the wrapped build script using schroot
#
# This function definition uses () rather than {} to create an extra
# sub-process where we can run 'set -e' without affecting the parent
#
# Parameters : <script> [<script params>...]
# -----------------------------------------------------------------------------
RunWrappedScript()
(
# In this sub-process, fail on error
set -e
# Define default args for running program
# -c : Define the schroot config to use
# -d : Directory to switch to before running command
schroot="schroot -c pa-build-$USER -d /build"
# Update the buildroot
$schroot -u root -- apt-get update
# Allow normal user to sudo without a password. We may need to add the
# normal user, as it probably isn't created by debootstrap
sudoers_filename=$(mktemp -u /etc/sudoers.d/nopasswd-XXXXXXXX)
$schroot -u root -- useradd -m $USER -u $(id -u) || :
$schroot -u root -- \
/bin/sh -c "echo '$USER ALL=(ALL) NOPASSWD:ALL'>$sudoers_filename"
$schroot -u root -- chmod 400 "$sudoers_filename"
# Call the wrapped script
$schroot -- "$@"
)
# -----------------------------------------------------------------------------
# M A I N
# -----------------------------------------------------------------------------
debootstrap_mirror=""
debootstrap_switches="--include=$WRAPPED_SCRIPT_DEPS"
debootstrap_suite=""
# Parse command line switches
while [ -n "$1" ]; do
case "$1" in
--mirror=*)
debootstrap_mirror="${1#--mirror=}"
;;
--keyring=*)
file="${1#--keyring=}"
if [ -f "$file" ]; then
debootstrap_switches="$debootstrap_switches $1"
else
echo "** Ignoring missing keyring $1" >&2
fi
;;
--suite=*)
debootstrap_suite="${1#--suite=}"
if ! SuiteExists "$debootstrap_suite"; then
echo "** Unsupported suite '$debootstrap_suite'" >&2
exit 1
fi
;;
*) echo "** Unrecognised parameter '$1'" >&2
exit 1
esac
shift
done
# Start with a few sanity checks
if [ -d $PULSE_DIR ]; then
echo "** Target directory $PULSE_DIR already exists" >&2
exit 0
fi
if [ ! -x $WRAPPED_SCRIPT ]; then
echo "** Can't find wrapped script $WRAPPED_SCRIPT" >&2
exit 1
fi
if [ -e $BUILDROOT ]; then
echo "** Remove old build root $BUILDROOT before running this script"
exit 1
fi
# Do we need extra packages?
InstallRequiredPackages || exit $?
# We should be able to determine the suite now, if it's not specified
if [ -z "$debootstrap_suite" ]; then
debootstrap_suite=$(lsb_release -cs) ; # e.g. 'bullseye'
if [ -z "$debootstrap_suite" ]; then
echo "** Can't determine current suite" >&2
exit 1
fi
if ! SuiteExists "$debootstrap_suite" ; then
echo "** Current distro '$debootstrap_suite' does not appear to be supported by debootstrap" >&2
echo " Need --suite switch?" >&2
exit 1
fi
fi
# Create the build root
log=/var/tmp/pa-build-$USER-debootstrap.log
echo "- Creating $debootstrap_suite build root. Log file in $log"
sudo debootstrap \
$debootstrap_switches \
$debootstrap_suite $BUILDROOT "$debootstrap_mirror" >$log 2>&1 || {
echo "** debootstrap failed. Check log file $log" >&2
exit 1
}
# Create the config file for schroot
schroot_conf=/etc/schroot/chroot.d/pa-build-$USER.conf
echo "- Creating schroot config file $schroot_conf"
{
echo "[pa-build-$USER]"
echo "description=Build PA on current system for $USER"
echo "directory=$BUILDROOT"
echo "root-users=$USER"
echo "users=$USER"
echo "type=directory"
# Make sure we don't clobber /etc/passwd, /etc/group (etc) which
# have been created by debootstrap
echo "setup.nssdatabases="
} | sudo tee $schroot_conf >/dev/null || exit $?
# Copy some files to the build root
for file in $(find /etc/apt/ /etc/apt/sources.list.d/ /etc/apt/mirrors/ \
-maxdepth 1 \
-type f \( -name '*.list' -o -name '*.sources' \) ); do
echo "- Copying $file to the root"
sudo install -Dm 0644 $file $BUILDROOT/$file || exit $?
done
for file in $(find /etc/apt/trusted.gpg.d/ \
-maxdepth 1 \
-type f -name '*.gpg' ); do
if [ ! -f "$BUILDROOT/$file" ]; then
echo "- Copying $file to the root"
sudo install -Dm 0644 $file $BUILDROOT/$file || exit $?
fi
done
# Create a separate directory in $BUILDROOT to hold the build
# artefacts.
#
# We used to do the build on the user's home directory, but this
# isn't supported by schroot out-of-the-box on all configurations -
# see https://github.com/neutrinolabs/pulseaudio-module-xrdp/issues/76
echo "- Creating the build directory /build"
sudo install -d --mode=0775 --owner=$USER --group=$(id -g) \
$BUILDROOT/build || exit $?
# Copy the wrapped script to the buildroot root
echo "- Copying the wrapped script to the build directory"
sudo install -m 755 $WRAPPED_SCRIPT $BUILDROOT/build/wrapped_script || exit $?
# Run the wrapped script
log=/var/tmp/pa-build-$USER-schroot.log
echo "- Building PA sources. Log file in $log"
RunWrappedScript /build/wrapped_script -d /build/$PULSE_DIRNAME >$log 2>&1 || {
echo "** schroot failed. Check log file $log" >&2
exit 1
}
# Done! Copy the resulting directory out of the build root
echo "- Copying sources out of the build root"
cp -pr $BUILDROOT/build/$PULSE_DIRNAME $PULSE_DIR || exit $?
# Remove the schroot config file as its no longer needed
echo "- Removing schroot config file and build root"
sudo rm -rf $schroot_conf $BUILDROOT
echo "- All done. Configure PA xrdp module with PULSE_DIR=$PULSE_DIR"
exit 0

View File

@ -1,6 +1,6 @@
AM_CFLAGS = -O2 \
-fPIC \
-I $(PULSE_DIR) \
-I $(PULSE_CONFIG_DIR) \
-I $(PULSE_DIR)/src \
$(XRDP_CFLAGS)

View File

@ -1,25 +1,25 @@
/**
* xrdp: A Remote Desktop Protocol server.
* pulse sink
*
* Copyright (C) Jay Sorg 2013
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/***
pulse sink module for xrdp
/*
* see pulse-notes.txt
*/
Copyright 2004-2008 Lennart Poettering
Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
Copyright (C) 2013-2021 Jay Sorg, Neutrino Labs, and all contributors.
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.
***/
// config.h from pulseaudio sources
#ifdef HAVE_CONFIG_H
@ -59,9 +59,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/rtpoll.h>
#include <xrdp_sockets.h>
/* defined in pulse/version.h */
#if PA_PROTOCOL_VERSION > 28
/* these used to be defined in pulsecore/macro.h */
@ -84,16 +81,48 @@ PA_MODULE_USAGE(
"format=<sample format> "
"rate=<sample rate> "
"channels=<number of channels> "
"channel_map=<channel map>");
"channel_map=<channel map> "
"description=<description for the sink> "
"xrdp_socket_path=<path to XRDP sockets> "
"xrdp_pulse_sink_socket=<name of sink socket>");
#define DEFAULT_SINK_NAME "xrdp-sink"
#define BLOCK_USEC 30000
//#define BLOCK_USEC (PA_USEC_PER_SEC * 2)
#define UNUSED_VAR(x) ((void) (x))
/* support for the set_state_in_io_thread callback was added in 11.99.1 */
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(11, 99, 1)
#define USE_SET_STATE_IN_IO_THREAD_CB
#else
#undef USE_SET_STATE_IN_IO_THREAD_CB
#endif
/*
* ** THIS IS A HACK **
*
* we need to access (struct set_state_data *)->state
* from our code, but the struct is currently private to pulsecore/sink.c
* The struct was introduced for 11.99.1, and hasn't changed since. Given
* the late development status of pulseaudio now (April '25), this is
* unlikely to change again */
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(11, 99, 1)
struct set_state_data { /* Copied from pulsecore/sink.c */
pa_sink_state_t state;
pa_suspend_cause_t suspend_cause;
};
#define GET_STATE_FROM_SET_STATE_DATA
#else
#undef GET_STATE_FROM_SET_STATE_DATA
#endif
struct userdata {
pa_core *core;
pa_module *module;
pa_sink *sink;
pa_device_port *port;
pa_card *card;
pa_thread *thread;
pa_thread_mq thread_mq;
@ -105,10 +134,9 @@ struct userdata {
pa_usec_t last_send_time;
int fd; /* unix domain socket connection to xrdp chansrv */
int display_num;
int skip_bytes;
int got_max_latency;
char *sink_socket;
};
static const char* const valid_modargs[] = {
@ -118,54 +146,207 @@ static const char* const valid_modargs[] = {
"rate",
"channels",
"channel_map",
"xrdp_socket_path",
"xrdp_pulse_sink_socket",
"description",
NULL
};
static int close_send(struct userdata *u);
static pa_device_port *xrdp_create_port(struct userdata *u) {
pa_device_port_new_data data;
pa_device_port *port;
pa_device_port_new_data_init(&data);
pa_device_port_new_data_set_name(&data, "xrdp-output");
pa_device_port_new_data_set_description(&data, "xrdp output");
pa_device_port_new_data_set_direction(&data, PA_DIRECTION_OUTPUT);
pa_device_port_new_data_set_available(&data, PA_AVAILABLE_YES);
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(14, 0, 0)
pa_device_port_new_data_set_type(&data, PA_DEVICE_PORT_TYPE_NETWORK);
#endif
port = pa_device_port_new(u->core, &data, 0);
pa_device_port_new_data_done(&data);
if (port == NULL)
{
return NULL;
}
pa_device_port_ref(port);
return port;
}
static pa_card_profile *xrdp_create_profile() {
pa_card_profile *profile;
profile = pa_card_profile_new("output:xrdp", "xrdp audio output", 0);
profile->priority = 10;
profile->n_sinks = 1;
profile->n_sources = 0;
profile->max_sink_channels = 2;
profile->max_source_channels = 0;
return profile;
}
static pa_card *xrdp_create_card(pa_module *m, pa_device_port *port, pa_card_profile *profile) {
pa_card_new_data data;
pa_card *card;
pa_card_new_data_init(&data);
data.driver = __FILE__;
pa_card_new_data_set_name(&data, "xrdp.sink");
pa_hashmap_put(data.ports, port->name, port);
pa_hashmap_put(data.profiles, profile->name, profile);
card = pa_card_new(m->core, &data);
pa_card_new_data_done(&data);
if (card == NULL)
{
return NULL;
}
pa_card_choose_initial_profile(card);
pa_card_put(card);
return card;
}
#if !defined(PA_CHECK_VERSION) || !PA_CHECK_VERSION(11, 99, 1)
/**
* Returns a string representation of the sink state
*
* Later versions of PA provide this function in pulsecore/sink.h
*
* @param state Sink state
* @return string representation of state
*/
static const char *
pa_sink_state_to_string(pa_sink_state_t state)
{
switch (state)
{
case PA_SINK_INIT:
return "INIT";
case PA_SINK_RUNNING:
return "RUNNING";
case PA_SINK_SUSPENDED:
return "SUSPENDED";
case PA_SINK_IDLE:
return "IDLE";
case PA_SINK_UNLINKED:
return "UNLINKED";
}
pa_assert_not_reached();
}
#endif
static int sink_process_msg(pa_msgobject *o, int code, void *data,
int64_t offset, pa_memchunk *chunk) {
struct userdata *u = PA_SINK(o)->userdata;
pa_usec_t now;
long lat;
pa_log_debug("sink_process_msg: code %d", code);
pa_sink_state_t sink_state;
switch (code) {
case PA_SINK_MESSAGE_SET_VOLUME: /* 3 */
case PA_SINK_MESSAGE_SET_VOLUME:
pa_log_debug("sink_process_msg: PA_SINK_MESSAGE_SET_VOLUME");
break;
case PA_SINK_MESSAGE_SET_MUTE: /* 6 */
case PA_SINK_MESSAGE_SET_MUTE:
pa_log_debug("sink_process_msg: PA_SINK_MESSAGE_SET_MUTE");
break;
case PA_SINK_MESSAGE_GET_LATENCY: /* 7 */
case PA_SINK_MESSAGE_GET_LATENCY:
pa_log_debug("sink_process_msg: PA_SINK_MESSAGE_GET_LATENCY");
now = pa_rtclock_now();
lat = u->timestamp > now ? u->timestamp - now : 0ULL;
pa_log_debug("sink_process_msg: lat %ld", lat);
*((pa_usec_t*) data) = lat;
return 0;
case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY: /* 8 */
case PA_SINK_MESSAGE_GET_REQUESTED_LATENCY:
pa_log_debug("sink_process_msg: PA_SINK_MESSAGE_GET_REQUESTED_LATENCY");
break;
case PA_SINK_MESSAGE_SET_STATE: /* 9 */
if (PA_PTR_TO_UINT(data) == PA_SINK_RUNNING) /* 0 */ {
pa_log("sink_process_msg: running");
case PA_SINK_MESSAGE_SET_STATE:
#ifdef GET_STATE_FROM_SET_STATE_DATA
sink_state = ((const struct set_state_data *)data)->state;
#else
sink_state = PA_PTR_TO_UINT(data);
#endif
pa_log("sink_process_msg: PA_SINK_MESSAGE_SET_STATE [%s]",
pa_sink_state_to_string(sink_state));
switch (sink_state)
{
case PA_SINK_INVALID_STATE:
case PA_SINK_INIT:
break;
u->timestamp = pa_rtclock_now();
} else {
pa_log("sink_process_msg: not running");
close_send(u);
case PA_SINK_RUNNING:
pa_log("sink_process_msg: running");
u->timestamp = pa_rtclock_now();
break;
case PA_SINK_IDLE:
case PA_SINK_SUSPENDED:
case PA_SINK_UNLINKED:
pa_log("sink_process_msg: not running");
close_send(u);
break;
default:
pa_assert_not_reached();
}
break;
default:
pa_log_debug("sink_process_msg: code %d", code);
}
return pa_sink_process_msg(o, code, data, offset, chunk);
}
#ifdef USE_SET_STATE_IN_IO_THREAD_CB
/* Called from the IO thread. */
static int sink_set_state_in_io_thread_cb(pa_sink *s,
pa_sink_state_t new_state,
pa_suspend_cause_t new_suspend_cause)
{
struct userdata *u;
UNUSED_VAR(new_suspend_cause);
pa_assert(s);
pa_assert_se(u = s->userdata);
if (s->thread_info.state == PA_SINK_SUSPENDED || s->thread_info.state == PA_SINK_INIT)
{
if (PA_SINK_IS_OPENED(new_state))
{
pa_log_debug("sink_set_state_in_io_thread_cb: set timestamp");
u->timestamp = pa_rtclock_now();
}
}
return 0;
}
#endif /* USE_SET_STATE_IN_IO_THREAD_CB */
static void sink_update_requested_latency_cb(pa_sink *s) {
struct userdata *u;
size_t nbytes;
@ -173,17 +354,11 @@ static void sink_update_requested_latency_cb(pa_sink *s) {
pa_sink_assert_ref(s);
pa_assert_se(u = s->userdata);
u->block_usec = BLOCK_USEC;
//u->block_usec = pa_sink_get_requested_latency_within_thread(s);
pa_log("1 block_usec %llu", (unsigned long long) u->block_usec);
u->block_usec = pa_sink_get_requested_latency_within_thread(s);
u->got_max_latency = 0;
if (u->block_usec == (pa_usec_t) -1) {
u->block_usec = s->thread_info.max_latency;
pa_log_debug("2 block_usec %llu", (unsigned long long) u->block_usec);
u->got_max_latency = 1;
}
nbytes = pa_usec_to_bytes(u->block_usec, &s->sample_spec);
pa_sink_set_max_rewind_within_thread(s, nbytes);
pa_sink_set_max_request_within_thread(s, nbytes);
@ -195,13 +370,12 @@ static void process_rewind(struct userdata *u, pa_usec_t now) {
pa_assert(u);
/* Figure out how much we shall rewind and reset the counter */
rewind_nbytes = u->sink->thread_info.rewind_nbytes;
u->sink->thread_info.rewind_nbytes = 0;
pa_assert(rewind_nbytes > 0);
pa_log_debug("Requested to rewind %lu bytes.",
(unsigned long) rewind_nbytes);
if (!PA_SINK_IS_OPENED(u->sink->thread_info.state) || rewind_nbytes <= 0)
goto do_nothing;
pa_log_debug("Requested to rewind %lu bytes.", (unsigned long) rewind_nbytes);
if (u->timestamp <= now)
goto do_nothing;
@ -217,7 +391,6 @@ static void process_rewind(struct userdata *u, pa_usec_t now) {
pa_sink_process_rewind(u->sink, rewind_nbytes);
u->timestamp -= pa_bytes_to_usec(rewind_nbytes, &u->sink->sample_spec);
u->skip_bytes += rewind_nbytes;
pa_log_debug("Rewound %lu bytes.", (unsigned long) rewind_nbytes);
return;
@ -232,7 +405,7 @@ struct header {
int bytes;
};
static int get_display_num_from_display(char *display_text) {
static int get_display_num_from_display(const char *display_text) {
int index;
int mode;
int host_index;
@ -296,14 +469,13 @@ static int lsend(int fd, char *data, int bytes) {
static int data_send(struct userdata *u, pa_memchunk *chunk) {
char *data;
char *socket_dir;
int bytes;
int sent;
int fd;
struct header h;
struct sockaddr_un s;
if (u->fd == 0) {
if (u->fd == -1) {
if (u->failed_connect_time != 0) {
if (pa_rtclock_now() - u->failed_connect_time < 1000000) {
return 0;
@ -312,15 +484,9 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) {
fd = socket(PF_LOCAL, SOCK_STREAM, 0);
memset(&s, 0, sizeof(s));
s.sun_family = AF_UNIX;
bytes = sizeof(s.sun_path) - 1;
socket_dir = getenv("XRDP_SOCKET_PATH");
if (socket_dir == NULL || socket_dir[0] == '\0')
{
socket_dir = "/tmp/.xrdp";
}
snprintf(s.sun_path, bytes, "%s/" CHANSRV_PORT_OUT_BASE_STR,
socket_dir, u->display_num);
pa_strlcpy(s.sun_path, u->sink_socket, sizeof(s.sun_path));
pa_log_debug("trying to connect to %s", s.sun_path);
if (connect(fd, (struct sockaddr *)&s,
sizeof(struct sockaddr_un)) != 0) {
u->failed_connect_time = pa_rtclock_now();
@ -336,23 +502,12 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) {
bytes = chunk->length;
pa_log_debug("bytes %d", bytes);
/* from rewind */
if (u->skip_bytes > 0) {
if (bytes > u->skip_bytes) {
bytes -= u->skip_bytes;
u->skip_bytes = 0;
} else {
u->skip_bytes -= bytes;
return bytes;
}
}
h.code = 0;
h.bytes = bytes + 8;
if (lsend(u->fd, (char*)(&h), 8) != 8) {
pa_log("data_send: send failed");
close(u->fd);
u->fd = 0;
u->fd = -1;
return 0;
} else {
pa_log_debug("data_send: sent header ok bytes %d", bytes);
@ -366,7 +521,7 @@ static int data_send(struct userdata *u, pa_memchunk *chunk) {
if (sent != bytes) {
pa_log("data_send: send failed sent %d bytes %d", sent, bytes);
close(u->fd);
u->fd = 0;
u->fd = -1;
return 0;
}
@ -377,7 +532,7 @@ static int close_send(struct userdata *u) {
struct header h;
pa_log("close_send:");
if (u->fd == 0) {
if (u->fd == -1) {
return 0;
}
h.code = 1;
@ -385,7 +540,7 @@ static int close_send(struct userdata *u) {
if (lsend(u->fd, (char*)(&h), 8) != 8) {
pa_log("close_send: send failed");
close(u->fd);
u->fd = 0;
u->fd = -1;
return 0;
} else {
pa_log_debug("close_send: sent header ok");
@ -398,15 +553,14 @@ static void process_render(struct userdata *u, pa_usec_t now) {
int request_bytes;
pa_assert(u);
if (u->got_max_latency) {
return;
}
pa_log_debug("process_render: u->block_usec %llu", (unsigned long long) u->block_usec);
while (u->timestamp < now + u->block_usec) {
request_bytes = u->sink->thread_info.max_request;
request_bytes = MIN(request_bytes, 16 * 1024);
pa_sink_render(u->sink, request_bytes, &chunk);
data_send(u, &chunk);
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
data_send(u, &chunk);
}
pa_memblock_unref(chunk.memblock);
u->timestamp += pa_bytes_to_usec(chunk.length, &u->sink->sample_spec);
}
@ -415,8 +569,6 @@ static void process_render(struct userdata *u, pa_usec_t now) {
static void thread_func(void *userdata) {
struct userdata *u = userdata;
int ret;
pa_usec_t now;
pa_assert(u);
@ -427,30 +579,26 @@ static void thread_func(void *userdata) {
u->timestamp = pa_rtclock_now();
for (;;) {
pa_usec_t now = 0;
int ret;
if (u->sink->thread_info.state == PA_SINK_RUNNING) {
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
now = pa_rtclock_now();
if (u->sink->thread_info.rewind_requested) {
if (u->sink->thread_info.rewind_nbytes > 0) {
process_rewind(u, now);
} else {
pa_sink_process_rewind(u->sink, 0);
}
}
}
if (PA_UNLIKELY(u->sink->thread_info.rewind_requested)) {
process_rewind(u, now);
}
/* Render some data and write it to the socket */
if (PA_SINK_IS_OPENED(u->sink->thread_info.state)) {
if (u->timestamp <= now) {
pa_log_debug("thread_func: calling process_render");
process_render(u, now);
}
pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp);
} else {
pa_rtpoll_set_timer_disabled(u->rtpoll);
}
/* Hmm, nothing to do. Let's sleep */
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(6, 0, 0)
if ((ret = pa_rtpoll_run(u->rtpoll)) < 0) {
#else
@ -463,7 +611,6 @@ static void thread_func(void *userdata) {
goto finish;
}
}
fail:
/* If this was no regular exit from the loop we have to continue
* processing messages until we received PA_MESSAGE_SHUTDOWN */
@ -476,8 +623,39 @@ finish:
pa_log_debug("Thread shutting down");
}
static void set_sink_socket(pa_modargs *ma, struct userdata *u) {
const char *socket_dir;
const char *socket_name;
char default_socket_name[64];
size_t nbytes;
socket_dir = pa_modargs_get_value(ma, "xrdp_socket_path",
getenv("XRDP_SOCKET_PATH"));
if (socket_dir == NULL || socket_dir[0] == '\0') {
socket_dir = "/tmp/.xrdp";
}
socket_name = pa_modargs_get_value(ma, "xrdp_pulse_sink_socket",
getenv("XRDP_PULSE_SINK_SOCKET"));
if (socket_name == NULL || socket_name[0] == '\0')
{
int display_num = get_display_num_from_display(getenv("DISPLAY"));
pa_log_debug("Could not obtain sink_socket from environment.");
snprintf(default_socket_name, sizeof(default_socket_name),
"xrdp_chansrv_audio_out_socket_%d", display_num);
socket_name = default_socket_name;
}
nbytes = strlen(socket_dir) + 1 + strlen(socket_name) + 1;
u->sink_socket = pa_xmalloc(nbytes);
snprintf(u->sink_socket, nbytes, "%s/%s", socket_dir, socket_name);
}
int pa__init(pa_module*m) {
struct userdata *u = NULL;
pa_device_port *port;
pa_card_profile *profile;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
@ -486,6 +664,17 @@ int pa__init(pa_module*m) {
pa_assert(m);
/* Check the runtime library version matches the build version */
if (strcmp(pa_get_library_version(), pa_get_headers_version()) == 0)
{
pa_log_notice("Build library version %s", pa_get_headers_version());
}
else
{
pa_log_warn("Runtime version '%s' differs from build version '%s'",
pa_get_library_version(), pa_get_headers_version());
}
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
@ -512,8 +701,11 @@ int pa__init(pa_module*m) {
pa_modargs_get_value(ma, "sink_name", DEFAULT_SINK_NAME));
pa_sink_new_data_set_sample_spec(&data, &ss);
pa_sink_new_data_set_channel_map(&data, &map);
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "xrdp sink");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "remote audio output"));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "computer");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, "xrdp");
if (pa_modargs_get_proplist(ma, "sink_properties", data.proplist,
PA_UPDATE_REPLACE) < 0) {
@ -521,9 +713,27 @@ int pa__init(pa_module*m) {
pa_sink_new_data_done(&data);
goto fail;
}
port = xrdp_create_port(u);
if (port == NULL) {
pa_log("Failed to create port object");
goto fail;
}
profile = xrdp_create_profile();
pa_hashmap_put(port->profiles, profile->name, profile);
u->card = xrdp_create_card(m, port, profile);
if (u->card == NULL) {
pa_log("Failed to create card object");
goto fail;
}
data.card = u->card;
pa_hashmap_put(data.ports, port->name, port);
u->sink = pa_sink_new(m->core, &data,
PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY);
PA_SINK_LATENCY | PA_SINK_DYNAMIC_LATENCY | PA_SINK_NETWORK | PA_SINK_HARDWARE);
pa_sink_new_data_done(&data);
if (!u->sink) {
@ -532,6 +742,9 @@ int pa__init(pa_module*m) {
}
u->sink->parent.process_msg = sink_process_msg;
#ifdef USE_SET_STATE_IN_IO_THREAD_CB
u->sink->set_state_in_io_thread = sink_set_state_in_io_thread_cb;
#endif
u->sink->update_requested_latency = sink_update_requested_latency_cb;
u->sink->userdata = u;
@ -544,7 +757,9 @@ int pa__init(pa_module*m) {
pa_sink_set_max_rewind(u->sink, nbytes);
pa_sink_set_max_request(u->sink, nbytes);
u->display_num = get_display_num_from_display(getenv("DISPLAY"));
set_sink_socket(ma, u);
u->fd = -1;
#if defined(PA_CHECK_VERSION)
#if PA_CHECK_VERSION(0, 9, 22)
@ -559,6 +774,8 @@ int pa__init(pa_module*m) {
goto fail;
}
pa_sink_set_latency_range(u->sink, 0, BLOCK_USEC);
pa_sink_put(u->sink);
pa_modargs_free(ma);
@ -613,5 +830,17 @@ void pa__done(pa_module*m) {
pa_rtpoll_free(u->rtpoll);
}
if (u->card)
{
pa_card_free(u->card);
}
if (u->fd >= 0)
{
close(u->fd);
u->fd = -1;
}
pa_xfree(u->sink_socket);
pa_xfree(u);
}

View File

@ -1,8 +1,9 @@
/***
This file is part of PulseAudio.
pulse source module for xrdp
Copyright 2004-2008 Lennart Poettering
Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
Copyright (C) 2013-2021 Jay Sorg, Neutrino Labs, and all contributors.
PulseAudio is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
@ -46,9 +47,6 @@
#include <pulsecore/thread-mq.h>
#include <pulsecore/thread.h>
#include <xrdp_sockets.h>
/* defined in pulse/version.h */
#if PA_PROTOCOL_VERSION > 28
/* these used to be defined in pulsecore/macro.h */
@ -71,7 +69,9 @@ PA_MODULE_USAGE(
"source_name=<name of source> "
"channel_map=<channel map> "
"description=<description for the source> "
"latency_time=<latency time in ms>");
"latency_time=<latency time in ms> "
"xrdp_socket_path=<path to XRDP sockets> "
"xrdp_pulse_source_socket=<name of source socket>");
#define DEFAULT_SOURCE_NAME "xrdp-source"
#define DEFAULT_LATENCY_TIME 10
@ -81,6 +81,8 @@ struct userdata {
pa_core *core;
pa_module *module;
pa_source *source;
pa_device_port *port;
pa_card *card;
pa_thread *thread;
pa_thread_mq thread_mq;
@ -94,7 +96,7 @@ struct userdata {
/* xrdp stuff */
int fd; /* UDS connection to xrdp chansrv */
int display_num; /* X display number */
char *source_socket;
int want_src_data;
};
@ -106,10 +108,81 @@ static const char* const valid_modargs[] = {
"channel_map",
"description",
"latency_time",
"xrdp_socket_path",
"xrdp_pulse_source_socket",
NULL
};
static int get_display_num_from_display(char *display_text) ;
static pa_device_port *xrdp_create_port(struct userdata *u) {
pa_device_port_new_data data;
pa_device_port *port;
pa_device_port_new_data_init(&data);
pa_device_port_new_data_set_name(&data, "xrdp-input");
pa_device_port_new_data_set_description(&data, "xrdp input");
pa_device_port_new_data_set_direction(&data, PA_DIRECTION_INPUT);
pa_device_port_new_data_set_available(&data, PA_AVAILABLE_YES);
#if defined(PA_CHECK_VERSION) && PA_CHECK_VERSION(14, 0, 0)
pa_device_port_new_data_set_type(&data, PA_DEVICE_PORT_TYPE_NETWORK);
#endif
port = pa_device_port_new(u->core, &data, 0);
pa_device_port_new_data_done(&data);
if (port == NULL)
{
return NULL;
}
pa_device_port_ref(port);
return port;
}
static pa_card_profile *xrdp_create_profile() {
pa_card_profile *profile;
profile = pa_card_profile_new("input:xrdp", "xrdp audio input", 0);
profile->priority = 10;
profile->n_sinks = 0;
profile->n_sources = 1;
profile->max_sink_channels = 0;
profile->max_source_channels = 2;
return profile;
}
static pa_card *xrdp_create_card(pa_module *m, pa_device_port *port, pa_card_profile *profile) {
pa_card_new_data data;
pa_card *card;
pa_card_new_data_init(&data);
data.driver = __FILE__;
pa_card_new_data_set_name(&data, "xrdp.source");
pa_hashmap_put(data.ports, port->name, port);
pa_hashmap_put(data.profiles, profile->name, profile);
card = pa_card_new(m->core, &data);
pa_card_new_data_done(&data);
if (card == NULL)
{
return NULL;
}
pa_card_choose_initial_profile(card);
pa_card_put(card);
return card;
}
static int get_display_num_from_display(const char *display_text) ;
static int source_process_msg(pa_msgobject *o, int code, void *data,
int64_t offset, pa_memchunk *chunk) {
@ -180,23 +253,15 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
int read_bytes;
struct sockaddr_un s;
char *data;
char *socket_dir;
char buf[11];
unsigned char ubuf[10];
if (u->fd == 0) {
if (u->fd == -1) {
/* 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;
socket_dir = getenv("XRDP_SOCKET_PATH");
if (socket_dir == NULL || socket_dir[0] == '\0')
{
socket_dir = "/tmp/.xrdp";
}
snprintf(s.sun_path, bytes, "%s/" CHANSRV_PORT_IN_BASE_STR,
socket_dir, u->display_num);
pa_strlcpy(s.sun_path, u->source_socket, sizeof(s.sun_path));
pa_log_debug("Trying to connect to %s", s.sun_path);
if (connect(fd, (struct sockaddr *) &s, sizeof(struct sockaddr_un)) != 0) {
@ -210,8 +275,6 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
u->fd = fd;
}
data = (char *) pa_memblock_acquire(chunk->memblock);
if (!u->want_src_data) {
char buf[12];
@ -229,8 +292,7 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
if (lsend(u->fd, buf, 11) != 11) {
close(u->fd);
u->fd = 0;
pa_memblock_release(chunk->memblock);
u->fd = -1;
return -1;
}
u->want_src_data = 1;
@ -252,8 +314,7 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
if (lsend(u->fd, buf, 11) != 11) {
close(u->fd);
u->fd = 0;
pa_memblock_release(chunk->memblock);
u->fd = -1;
u->want_src_data = 0;
return -1;
}
@ -261,15 +322,23 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
/* read length of data available */
if (lrecv(u->fd, (char *) ubuf, 2) != 2) {
close(u->fd);
u->fd = 0;
pa_memblock_release(chunk->memblock);
u->fd = -1;
u->want_src_data = 0;
return -1;
}
bytes = ((ubuf[1] << 8) & 0xff00) | (ubuf[0] & 0xff);
if (bytes == 0) {
pa_memblock_release(chunk->memblock);
return 0;
}
chunk->memblock = pa_memblock_new(u->core->mempool, bytes);
if (chunk->memblock == NULL) {
return 0;
}
data = (char *) pa_memblock_acquire(chunk->memblock);
if (data == NULL) {
return 0;
}
@ -277,11 +346,12 @@ static int data_get(struct userdata *u, pa_memchunk *chunk) {
read_bytes = lrecv(u->fd, data, bytes);
if (read_bytes != bytes) {
close(u->fd);
u->fd = 0;
u->fd = -1;
pa_memblock_release(chunk->memblock);
u->want_src_data = 0;
return -1;
}
pa_memblock_release(chunk->memblock);
return read_bytes;
@ -298,28 +368,26 @@ static void thread_func(void *userdata) {
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();
memset(&chunk, 0, sizeof(chunk));
if ((chunk.length = pa_usec_to_bytes(now - u->timestamp, &u->source->sample_spec)) > 0) {
chunk.length *= 4;
chunk.memblock = pa_memblock_new(u->core->mempool, chunk.length);
chunk.index = 0;
bytes = data_get(u, &chunk);
if (bytes > 0)
{
if (bytes > 0) {
chunk.length = bytes;
pa_source_post(u->source, &chunk);
pa_source_post(u->source, &chunk);
u->timestamp = now;
}
if (chunk.memblock) {
pa_memblock_unref(chunk.memblock);
}
pa_memblock_unref(chunk.memblock);
u->timestamp = now;
}
pa_rtpoll_set_timer_absolute(u->rtpoll, u->timestamp + u->latency_time * PA_USEC_PER_MSEC);
pa_rtpoll_set_timer_absolute(u->rtpoll, now + u->latency_time * PA_USEC_PER_MSEC);
} else {
if (u->want_src_data)
{
@ -340,7 +408,7 @@ static void thread_func(void *userdata) {
if (lsend(u->fd, buf, 11) != 11) {
close(u->fd);
u->fd = 0;
u->fd = -1;
}
u->want_src_data = 0;
pa_log_debug("###### stopped recording");
@ -371,8 +439,40 @@ finish:
pa_log_debug("###### thread shutting down");
}
static void set_source_socket(pa_modargs *ma, struct userdata *u) {
const char *socket_dir;
const char *socket_name;
char default_socket_name[64];
size_t nbytes;
socket_dir = pa_modargs_get_value(ma, "xrdp_socket_path",
getenv("XRDP_SOCKET_PATH"));
if (socket_dir == NULL || socket_dir[0] == '\0') {
socket_dir = "/tmp/.xrdp";
}
socket_name = pa_modargs_get_value(ma, "xrdp_pulse_source_socket",
getenv("XRDP_PULSE_SOURCE_SOCKET"));
if (socket_name == NULL || socket_name[0] == '\0')
{
int display_num = get_display_num_from_display(getenv("DISPLAY"));
pa_log_debug("Could not obtain source_socket from environment.");
snprintf(default_socket_name, sizeof(default_socket_name),
"xrdp_chansrv_audio_out_socket_%d", display_num);
socket_name = default_socket_name;
}
nbytes = strlen(socket_dir) + 1 + strlen(socket_name) + 1;
u->source_socket = pa_xmalloc(nbytes);
snprintf(u->source_socket, nbytes, "%s/%s", socket_dir, socket_name);
}
int pa__init(pa_module *m) {
struct userdata *u = NULL;
pa_device_port *port;
pa_card_profile *profile;
pa_sample_spec ss;
pa_channel_map map;
pa_modargs *ma = NULL;
@ -381,6 +481,21 @@ int pa__init(pa_module *m) {
pa_assert(m);
/* Check the runtime library version matches the build version */
if (strcmp(pa_get_library_version(), pa_get_headers_version()) == 0)
{
pa_log_notice("Build library version %s", pa_get_headers_version());
}
else
{
pa_log_warn("Runtime version '%s' differs from build version '%s'",
pa_get_library_version(), pa_get_headers_version());
}
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
}
if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
pa_log("Failed to parse module arguments.");
goto fail;
@ -412,10 +527,32 @@ int pa__init(pa_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", "xrdp source"));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "abstract");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_DESCRIPTION, pa_modargs_get_value(ma, "description", "remote audio input"));
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_CLASS, "sound");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_FORM_FACTOR, "microphone");
pa_proplist_sets(data.proplist, PA_PROP_DEVICE_PRODUCT_NAME, "xrdp");
u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY | PA_SOURCE_DYNAMIC_LATENCY);
port = xrdp_create_port(u);
if (port == NULL) {
pa_log("Failed to create port object");
goto fail;
}
profile = xrdp_create_profile();
pa_hashmap_put(port->profiles, profile->name, profile);
u->card = xrdp_create_card(m, port, profile);
if (u->card == NULL) {
pa_log("Failed to create card object");
goto fail;
}
data.card = u->card;
pa_hashmap_put(data.ports, port->name, port);
u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY |
PA_SOURCE_DYNAMIC_LATENCY | PA_SOURCE_NETWORK | PA_SOURCE_HARDWARE);
pa_source_new_data_done(&data);
if (!u->source) {
@ -443,15 +580,19 @@ int pa__init(pa_module *m) {
u->source->thread_info.max_rewind =
pa_usec_to_bytes(u->block_usec, &u->source->sample_spec);
#if defined(PA_CHECK_VERSION)
#if PA_CHECK_VERSION(0, 9, 22)
if (!(u->thread = pa_thread_new("xrdp-source", thread_func, u))) {
#else
if (!(u->thread = pa_thread_new(thread_func, u))) {
#endif
#else
if (!(u->thread = pa_thread_new(thread_func, u)))
#endif
set_source_socket(ma, u);
u->fd = -1;
#if defined(PA_CHECK_VERSION)
#if PA_CHECK_VERSION(0, 9, 22)
if (!(u->thread = pa_thread_new("xrdp-source", thread_func, u))) {
#else
if (!(u->thread = pa_thread_new(thread_func, u))) {
#endif
#else
if (!(u->thread = pa_thread_new(thread_func, u))) {
#endif
pa_log("Failed to create thread.");
goto fail;
}
@ -460,8 +601,6 @@ int pa__init(pa_module *m) {
pa_modargs_free(ma);
u->display_num = get_display_num_from_display(getenv("DISPLAY"));
return 0;
fail:
@ -484,6 +623,7 @@ void pa__done(pa_module*m) {
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);
@ -491,16 +631,25 @@ void pa__done(pa_module*m) {
pa_thread_mq_done(&u->thread_mq);
if (u->source)
pa_source_unref(u->source);
if (u->rtpoll)
pa_rtpoll_free(u->rtpoll);
if (u->fd >= 0)
{
close(u->fd);
u->fd = -1;
}
if (u->card)
{
pa_card_free(u->card);
}
pa_xfree(u->source_socket);
pa_xfree(u);
}
static int get_display_num_from_display(char *display_text) {
static int get_display_num_from_display(const char *display_text) {
int index;
int mode;
int host_index;