GUACAMOLE-179: Move PulseAudio support into separate utility library.

This commit is contained in:
Michael Jumper 2016-12-25 01:13:40 -08:00
parent 5d2c9676f4
commit 48ebbe95ea
11 changed files with 241 additions and 151 deletions

View File

@ -28,6 +28,7 @@ DIST_SUBDIRS = \
src/terminal \
src/guacd \
src/guacenc \
src/pulse \
src/protocols/rdp \
src/protocols/ssh \
src/protocols/telnet \
@ -48,6 +49,10 @@ if ENABLE_TERMINAL
SUBDIRS += src/terminal
endif
if ENABLE_PULSE
SUBDIRS += src/pulse
endif
if ENABLE_RDP
SUBDIRS += src/protocols/rdp
endif

View File

@ -119,6 +119,10 @@ AC_SUBST([LIBGUAC_INCLUDE], '-I$(top_srcdir)/src/libguac')
AC_SUBST([COMMON_LTLIB], '$(top_builddir)/src/common/libguac_common.la')
AC_SUBST([COMMON_INCLUDE], '-I$(top_srcdir)/src/common')
# Common PulseAudio utility library
AC_SUBST([PULSE_LTLIB], '$(top_builddir)/src/pulse/libguac_pulse.la')
AC_SUBST([PULSE_INCLUDE], '-I$(top_srcdir)/src/pulse')
# Common utility library for guacd implementations
AC_SUBST([LIBGUACD_LTLIB], '$(top_builddir)/src/libguacd/libguacd.la')
AC_SUBST([LIBGUACD_INCLUDE], '-I$(top_srcdir)/src/libguacd')
@ -1108,6 +1112,7 @@ AC_CONFIG_FILES([Makefile
src/libguacd/Makefile
src/guacd/Makefile
src/guacenc/Makefile
src/pulse/Makefile
src/protocols/rdp/Makefile
src/protocols/ssh/Makefile
src/protocols/telnet/Makefile

View File

@ -46,22 +46,16 @@ noinst_HEADERS = \
user.h \
vnc.h
# Optional PulseAudio support
if ENABLE_PULSE
libguac_client_vnc_la_SOURCES += pulse.c
noinst_HEADERS += pulse.h
endif
libguac_client_vnc_la_CFLAGS = \
-Werror -Wall -pedantic -Iinclude \
@COMMON_INCLUDE@ \
@COMMON_SSH_INCLUDE@ \
@LIBGUAC_INCLUDE@
@LIBGUAC_INCLUDE@ \
@PULSE_INCLUDE@
libguac_client_vnc_la_LDFLAGS = \
-version-info 0:0:0 \
@CAIRO_LIBS@ \
@PULSE_LIBS@ \
@VNC_LIBS@
libguac_client_vnc_la_LIBADD = \
@ -75,3 +69,8 @@ noinst_HEADERS += sftp.h
libguac_client_vnc_la_LIBADD += @COMMON_SSH_LTLIB@
endif
# Optional PulseAudio support
if ENABLE_PULSE
libguac_client_vnc_la_LIBADD += @PULSE_LTLIB@
endif

View File

@ -30,7 +30,7 @@
#endif
#ifdef ENABLE_PULSE
#include "pulse.h"
#include "pulse/pulse.h"
#endif
#include <guacamole/client.h>
@ -110,20 +110,16 @@ int guac_vnc_client_free_handler(guac_client* client) {
if (vnc_client->display != NULL)
guac_common_display_free(vnc_client->display);
/* Free settings-dependend data */
if (settings != NULL) {
#ifdef ENABLE_PULSE
/* If audio enabled, stop streaming */
if (settings->audio_enabled)
guac_pa_stop_stream(client);
/* If audio enabled, stop streaming */
if (vnc_client->audio)
guac_pa_stream_free(vnc_client->audio);
#endif
/* Free parsed settings */
/* Free parsed settings */
if (settings != NULL)
guac_vnc_settings_free(settings);
}
/* Free generic data struct */
free(client->data);

View File

@ -1,70 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef __GUAC_VNC_PULSE_H
#define __GUAC_VNC_PULSE_H
#include "config.h"
#include <guacamole/client.h>
/**
* The number of bytes to request for the audio fragments received from
* PulseAudio.
*/
#define GUAC_VNC_AUDIO_FRAGMENT_SIZE 8192
/**
* The minimum number of PCM bytes to wait for before flushing an audio
* packet. The current value is 48K, which works out to be around 280ms.
*/
#define GUAC_VNC_PCM_WRITE_RATE 49152
/**
* Rate of audio to stream, in Hz.
*/
#define GUAC_VNC_AUDIO_RATE 44100
/**
* The number of channels to stream.
*/
#define GUAC_VNC_AUDIO_CHANNELS 2
/**
* The number of bits per sample.
*/
#define GUAC_VNC_AUDIO_BPS 16
/**
* Starts streaming audio from PulseAudio to the given Guacamole client.
*
* @param client The client to stream data to.
*/
void guac_pa_start_stream(guac_client* client);
/**
* Stops streaming audio from PulseAudio to the given Guacamole client.
*
* @param client The client to stream data to.
*/
void guac_pa_stop_stream(guac_client* client);
#endif

View File

@ -28,6 +28,10 @@
#include "sftp.h"
#include "vnc.h"
#ifdef ENABLE_PULSE
#include "pulse/pulse.h"
#endif
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/socket.h>
@ -75,7 +79,7 @@ int guac_vnc_user_join_handler(guac_user* user, int argc, char** argv) {
#ifdef ENABLE_PULSE
/* Synchronize an audio stream */
if (vnc_client->audio)
guac_audio_stream_add_user(vnc_client->audio, user);
guac_pa_stream_add_user(vnc_client->audio, user);
#endif
/* Synchronize with current display */

View File

@ -33,7 +33,7 @@
#include "vnc.h"
#ifdef ENABLE_PULSE
#include "pulse.h"
#include "pulse/pulse.h"
#endif
#ifdef ENABLE_COMMON_SSH
@ -207,32 +207,10 @@ void* guac_vnc_client_thread(void* data) {
}
#ifdef ENABLE_PULSE
/* If an encoding is available, load an audio stream */
if (settings->audio_enabled) {
vnc_client->audio = guac_audio_stream_alloc(client, NULL,
GUAC_VNC_AUDIO_RATE,
GUAC_VNC_AUDIO_CHANNELS,
GUAC_VNC_AUDIO_BPS);
/* If successful, init audio system */
if (vnc_client->audio != NULL) {
guac_client_log(client, GUAC_LOG_INFO,
"Audio will be encoded as %s",
vnc_client->audio->encoder->mimetype);
/* Start audio stream */
guac_pa_start_stream(client);
}
/* Otherwise, audio loading failed */
else
guac_client_log(client, GUAC_LOG_INFO,
"No available audio encoding. Sound disabled.");
} /* end if audio enabled */
/* If audio is enabled, start streaming via PulseAudio */
if (settings->audio_enabled)
vnc_client->audio = guac_pa_stream_alloc(client,
settings->pa_servername);
#endif
#ifdef ENABLE_COMMON_SSH

View File

@ -33,8 +33,7 @@
#include <rfb/rfbclient.h>
#ifdef ENABLE_PULSE
#include <guacamole/audio.h>
#include <pulse/pulseaudio.h>
#include "pulse/pulse.h"
#endif
#ifdef ENABLE_COMMON_SSH
@ -89,14 +88,9 @@ typedef struct guac_vnc_client {
#ifdef ENABLE_PULSE
/**
* Audio output, if any.
* PulseAudio output, if any.
*/
guac_audio_stream* audio;
/**
* PulseAudio event loop.
*/
pa_threaded_mainloop* pa_mainloop;
guac_pa_stream* audio;
#endif
#ifdef ENABLE_COMMON_SSH

40
src/pulse/Makefile.am Normal file
View File

@ -0,0 +1,40 @@
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.
#
AUTOMAKE_OPTIONS = foreign
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libguac_pulse.la
noinst_HEADERS = \
pulse/pulse.h
libguac_pulse_la_SOURCES = \
pulse.c
libguac_pulse_la_CFLAGS = \
-Werror -Wall -pedantic \
@LIBGUAC_INCLUDE@
libguac_pulse_la_LIBADD = \
@LIBGUAC_LTLIB@
libguac_pulse_la_LDFLAGS = \
@PULSE_LIBS@

View File

@ -19,12 +19,11 @@
#include "config.h"
#include "pulse.h"
#include "vnc.h"
#include "pulse/pulse.h"
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <pulse/pulseaudio.h>
/**
@ -62,9 +61,8 @@ static int guac_pa_is_silence(const void* buffer, size_t length) {
static void __stream_read_callback(pa_stream* stream, size_t length,
void* data) {
guac_client* client = (guac_client*) data;
guac_vnc_client* vnc_client = (guac_vnc_client*) client->data;
guac_audio_stream* audio = vnc_client->audio;
guac_pa_stream* guac_stream = (guac_pa_stream*) data;
guac_audio_stream* audio = guac_stream->audio;
const void* buffer;
@ -86,7 +84,8 @@ static void __stream_read_callback(pa_stream* stream, size_t length,
static void __stream_state_callback(pa_stream* stream, void* data) {
guac_client* client = (guac_client*) data;
guac_pa_stream* guac_stream = (guac_pa_stream*) data;
guac_client* client = guac_stream->client;
switch (pa_stream_get_state(stream)) {
@ -137,11 +136,11 @@ static void __context_get_sink_info_callback(pa_context* context,
/* Set format */
spec.format = PA_SAMPLE_S16LE;
spec.rate = GUAC_VNC_AUDIO_RATE;
spec.channels = GUAC_VNC_AUDIO_CHANNELS;
spec.rate = GUAC_PULSE_AUDIO_RATE;
spec.channels = GUAC_PULSE_AUDIO_CHANNELS;
attr.maxlength = -1;
attr.fragsize = GUAC_VNC_AUDIO_FRAGMENT_SIZE;
attr.fragsize = GUAC_PULSE_AUDIO_FRAGMENT_SIZE;
/* Create stream */
stream = pa_stream_new(context, "Guacamole Audio", &spec, NULL);
@ -226,41 +225,57 @@ static void __context_state_callback(pa_context* context, void* data) {
}
void guac_pa_start_stream(guac_client* client) {
guac_pa_stream* guac_pa_stream_alloc(guac_client* client,
const char* server_name) {
guac_vnc_client* vnc_client = (guac_vnc_client*) client->data;
guac_vnc_settings* settings = vnc_client->settings;
guac_audio_stream* audio = guac_audio_stream_alloc(client, NULL,
GUAC_PULSE_AUDIO_RATE, GUAC_PULSE_AUDIO_CHANNELS,
GUAC_PULSE_AUDIO_BPS);
pa_context* context;
/* Abort if audio stream cannot be created */
if (audio == NULL)
return NULL;
guac_client_log(client, GUAC_LOG_INFO, "Starting audio stream");
guac_client_log(client, GUAC_LOG_INFO, "Audio will be encoded as %s",
audio->encoder->mimetype);
/* Init main loop */
vnc_client->pa_mainloop = pa_threaded_mainloop_new();
guac_pa_stream* stream = malloc(sizeof(guac_pa_stream));
stream->client = client;
stream->audio = audio;
stream->pa_mainloop = pa_threaded_mainloop_new();
/* Create context */
context = pa_context_new(
pa_threaded_mainloop_get_api(vnc_client->pa_mainloop),
pa_context* context = pa_context_new(
pa_threaded_mainloop_get_api(stream->pa_mainloop),
"Guacamole Audio");
/* Set up context */
pa_context_set_state_callback(context, __context_state_callback, client);
pa_context_connect(context, settings->pa_servername,
PA_CONTEXT_NOAUTOSPAWN, NULL);
pa_context_set_state_callback(context, __context_state_callback, stream);
pa_context_connect(context, server_name, PA_CONTEXT_NOAUTOSPAWN, NULL);
/* Start loop */
pa_threaded_mainloop_start(vnc_client->pa_mainloop);
pa_threaded_mainloop_start(stream->pa_mainloop);
return stream;
}
void guac_pa_stop_stream(guac_client* client) {
void guac_pa_stream_add_user(guac_pa_stream* stream, guac_user* user) {
guac_audio_stream_add_user(stream->audio, user);
}
guac_vnc_client* vnc_client = (guac_vnc_client*) client->data;
void guac_pa_stream_free(guac_pa_stream* stream) {
/* Stop loop */
pa_threaded_mainloop_stop(vnc_client->pa_mainloop);
pa_threaded_mainloop_stop(stream->pa_mainloop);
guac_client_log(client, GUAC_LOG_INFO, "Audio stream finished");
/* Free underlying audio stream */
guac_audio_stream_free(stream->audio);
/* Stream now ended */
guac_client_log(stream->client, GUAC_LOG_INFO, "Audio stream finished");
free(stream);
}

124
src/pulse/pulse/pulse.h Normal file
View File

@ -0,0 +1,124 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
#ifndef GUAC_PULSE_H
#define GUAC_PULSE_H
#include "config.h"
#include <guacamole/audio.h>
#include <guacamole/client.h>
#include <guacamole/user.h>
#include <pulse/pulseaudio.h>
/**
* The number of bytes to request for the audio fragments received from
* PulseAudio.
*/
#define GUAC_PULSE_AUDIO_FRAGMENT_SIZE 8192
/**
* The minimum number of PCM bytes to wait for before flushing an audio
* packet. The current value is 48K, which works out to be around 280ms.
*/
#define GUAC_PULSE_PCM_WRITE_RATE 49152
/**
* Rate of audio to stream, in Hz.
*/
#define GUAC_PULSE_AUDIO_RATE 44100
/**
* The number of channels to stream.
*/
#define GUAC_PULSE_AUDIO_CHANNELS 2
/**
* The number of bits per sample.
*/
#define GUAC_PULSE_AUDIO_BPS 16
/**
* An audio stream which connects to a PulseAudio server and streams the
* received audio through a guac_client.
*/
typedef struct guac_pa_stream {
/**
* The client associated with the audio stream.
*/
guac_client* client;
/**
* Audio output stream.
*/
guac_audio_stream* audio;
/**
* PulseAudio event loop.
*/
pa_threaded_mainloop* pa_mainloop;
} guac_pa_stream;
/**
* Allocates a new PulseAudio audio stream for the given Guacamole client and
* begins streaming.
*
* @param client
* The client to stream audio to.
*
* @param server_name
* The hostname of the PulseAudio server to connect to, or NULL to connect
* to the default (local) server.
*
* @return
* A newly-allocated PulseAudio stream, or NULL if audio cannot be
* streamed.
*/
guac_pa_stream* guac_pa_stream_alloc(guac_client* client,
const char* server_name);
/**
* Notifies the given PulseAudio stream that a user has joined the connection.
* The audio stream itself may need to be restarted. and the audio stream will
* need to be created for the new user to ensure they can properly handle
* future data received along the stream.
*
* @param stream
* The guac_pa_stream associated with the Guacamole connection being
* joined.
*
* @param user
* The user that has joined the Guacamole connection.
*/
void guac_pa_stream_add_user(guac_pa_stream* stream, guac_user* user);
/**
* Stops streaming audio from the given PulseAudio stream, freeing all
* associated resources.
*
* @param stream
* The PulseAudio stream to free.
*/
void guac_pa_stream_free(guac_pa_stream* stream);
#endif