Hey, On Wed, Mar 18, 2015 at 01:27:54PM -0400, Marc-André Lureau wrote: > Hi > > ----- Original Message ----- > > This patch includes the vdagentd-audio.[ch] files in order to > > communicate with backend audio server. > > > > The two functions provide a way to set volume and mute in the guest by > > creating a temporary mainloop and requesting the changes to pulse > > server. > > Not sure PulseAudio API is best choice for modifying device volume, I would have used ALSA mixer API here (PulseAudio is fine synchronizing with ALSA changes) I'll give it a try this afternoon, indeed it looks way simpler. Thanks for the review/suggestion. > > --- > > Makefile.am | 6 +- > > configure.ac | 1 + > > src/vdagentd-audio.c | 250 > > +++++++++++++++++++++++++++++++++++++++++++++++++++ > > src/vdagentd-audio.h | 27 ++++++ > > 4 files changed, 282 insertions(+), 2 deletions(-) > > create mode 100644 src/vdagentd-audio.c > > create mode 100644 src/vdagentd-audio.h > > > > diff --git a/Makefile.am b/Makefile.am > > index 510f460..abd5b59 100644 > > --- a/Makefile.am > > +++ b/Makefile.am > > @@ -9,11 +9,12 @@ src_spice_vdagent_LDADD = $(X_LIBS) $(SPICE_LIBS) > > $(GLIB2_LIBS) > > src_spice_vdagent_SOURCES = src/vdagent.c src/vdagent-x11.c > > src/vdagent-x11-randr.c src/vdagent-file-xfers.c src/udscs.c > > > > src_spice_vdagentd_CFLAGS = $(DBUS_CFLAGS) $(LIBSYSTEMD_LOGIN_CFLAGS) \ > > - $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS) > > + $(PCIACCESS_CFLAGS) $(SPICE_CFLAGS) $(GLIB2_CFLAGS) $(PIE_CFLAGS) > > $(PULSE_CFLAGS) > > src_spice_vdagentd_LDADD = $(DBUS_LIBS) $(LIBSYSTEMD_LOGIN_LIBS) \ > > - $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS) > > + $(PCIACCESS_LIBS) $(SPICE_LIBS) $(GLIB2_LIBS) $(PIE_LDFLAGS) $(PULSE_LIBS) > > src_spice_vdagentd_SOURCES = src/vdagentd.c \ > > src/vdagentd-uinput.c \ > > + src/vdagentd-audio.c \ > > src/vdagentd-xorg-conf.c \ > > src/vdagent-virtio-port.c \ > > src/udscs.c > > @@ -37,6 +38,7 @@ noinst_HEADERS = src/glib-compat.h \ > > src/vdagentd-proto.h \ > > src/vdagentd-proto-strings.h \ > > src/vdagentd-uinput.h \ > > + src/vdagentd-audio.h \ > > src/vdagentd-xorg-conf.h > > > > xdgautostartdir = $(sysconfdir)/xdg/autostart > > diff --git a/configure.ac b/configure.ac > > index 79905a8..6eb3e44 100644 > > --- a/configure.ac > > +++ b/configure.ac > > @@ -79,6 +79,7 @@ AC_ARG_ENABLE([static-uinput], > > PKG_CHECK_MODULES([GLIB2], [glib-2.0 >= 2.12]) > > PKG_CHECK_MODULES(X, [xfixes xrandr >= 1.3 xinerama x11]) > > PKG_CHECK_MODULES(SPICE, [spice-protocol >= 0.12.5]) > > +PKG_CHECK_MODULES(PULSE, [libpulse]) > > > > if test "$with_session_info" = "auto" || test "$with_session_info" = > > "systemd"; then > > PKG_CHECK_MODULES([LIBSYSTEMD_LOGIN], > > diff --git a/src/vdagentd-audio.c b/src/vdagentd-audio.c > > new file mode 100644 > > index 0000000..ca2a174 > > --- /dev/null > > +++ b/src/vdagentd-audio.c > > @@ -0,0 +1,250 @@ > > +/* vdagentd-audio.c vdagentd audio handling code > > + > > + Copyright 2015 Red Hat, Inc. > > + > > + This program is free software: you can redistribute it and/or modify > > + it under the terms of the GNU General Public License as published by > > + the Free Software Foundation, either version 3 of the License, or > > + (at your option) any later version. > > + > > + This program 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 General Public License > > + along with this program. If not, see <http://www.gnu.org/licenses/>. > > +*/ > > + > > +#ifdef HAVE_CONFIG_H > > +#include <config.h> > > +#endif > > + > > +#include <syslog.h> > > +#include <pulse/pulseaudio.h> > > +#include <stdbool.h> > > +#include "vdagentd-audio.h" > > + > > +typedef enum { > > + OP_PLAYBACK_SYNC, > > + OP_RECORD_SYNC, > > +} operation; > > + > > +struct OperationSpec { > > + pa_mainloop *mainloop; > > + pa_context *context; > > + operation op_type; > > + bool mute; > > + uint16_t nchannels; > > + uint16_t *volume; > > + > > + /* Only quit mainloop after all callbacks are called */ > > + int num_cb_operations; > > +}; > > + > > +#define VDAGENT_APP "vdagentd-audio" > > + > > +static void context_success_cb (pa_context *c, int success, void *userdata) > > +{ > > + struct OperationSpec *os = userdata; > > + int retval = (success) ? 0 : -1; > > + > > + os->num_cb_operations--; > > + syslog(LOG_DEBUG, "Num callback left: %d", os->num_cb_operations); > > + > > + if (!success) { > > + int err = pa_context_errno(os->context); > > + syslog(LOG_WARNING, "Operation failed: %s", pa_strerror(err)); > > + } > > + > > + if (os->num_cb_operations == 0) > > + pa_mainloop_quit (os->mainloop, retval); > > +} > > + > > +static void vdagent_handle_operation (struct OperationSpec *os, > > + uint8_t nchannels, > > + const char *sink, > > + const char *source) > > +{ > > + int i; > > + pa_operation *op; > > + pa_cvolume vol; > > + > > + if (nchannels != os->nchannels) > > + syslog (LOG_WARNING, > > + "Number of channels in the guest (%u) and client (%u) > > differs", > > + nchannels, os->nchannels); > > + > > + pa_cvolume_init(&vol); > > + vol.channels = nchannels; > > + for (i = 0; i < nchannels; i++) { > > + vol.values[i] = os->volume[i]; > > + syslog(LOG_DEBUG, "Setting channel %d to volume %d (%0.2f)", > > + i, os->volume[i], (float) (100 * os->volume[i]) / > > UINT16_MAX); > > + } > > + > > + switch (os->op_type) { > > + case OP_PLAYBACK_SYNC: > > + os->num_cb_operations = 2; > > + op = pa_context_set_sink_volume_by_name(os->context, sink, &vol, > > + context_success_cb, os); > > + if (!op) { > > + int err = pa_context_errno(os->context); > > + syslog(LOG_WARNING, "Fail to set sink volume: %s", > > pa_strerror(err)); > > + pa_mainloop_quit (os->mainloop, -1); > > + } > > + pa_operation_unref(op); > > + > > + op = pa_context_set_sink_mute_by_name(os->context, sink, os->mute, > > + context_success_cb, os); > > + if (!op) { > > + int err = pa_context_errno(os->context); > > + syslog(LOG_WARNING, "Fail to set sink mute: %s", > > pa_strerror(err)); > > + pa_mainloop_quit (os->mainloop, -1); > > + } > > + pa_operation_unref(op); > > + break; > > + > > + case OP_RECORD_SYNC: > > + os->num_cb_operations = 2; > > + op = pa_context_set_source_volume_by_name(os->context, source, &vol, > > + context_success_cb, os); > > + if (!op) { > > + int err = pa_context_errno(os->context); > > + syslog(LOG_WARNING, "Fail to set source volume: %s", > > pa_strerror(err)); > > + pa_mainloop_quit (os->mainloop, -1); > > + } > > + pa_operation_unref(op); > > + > > + op = pa_context_set_source_mute_by_name(os->context, source, > > os->mute, > > + context_success_cb, os); > > + if (!op) { > > + int err = pa_context_errno(os->context); > > + syslog(LOG_WARNING, "Fail to set source mute: %s", > > pa_strerror(err)); > > + pa_mainloop_quit (os->mainloop, -1); > > + } > > + pa_operation_unref(op); > > + break; > > + > > + default: > > + syslog(LOG_WARNING, "Unknown operation type %d, ignoring", > > os->op_type); > > + } > > +} > > + > > +static void vdagent_server_info_cb (pa_context *context, > > + const pa_server_info *info, > > + void *userdata) > > +{ > > + vdagent_handle_operation (userdata, > > + info->channel_map.channels, > > + info->default_sink_name, > > + info->default_source_name); > > +} > > + > > +static void vdagent_context_state_cb(pa_context *context, void *userdata) > > +{ > > + struct OperationSpec *os = userdata; > > + switch (pa_context_get_state(context)) { > > + case PA_CONTEXT_UNCONNECTED: > > + case PA_CONTEXT_CONNECTING: > > + case PA_CONTEXT_AUTHORIZING: > > + case PA_CONTEXT_SETTING_NAME: > > + break; > > + > > + case PA_CONTEXT_FAILED: > > + syslog(LOG_WARNING, "The connection failed or was disconnected"); > > + goto fail; > > + break; > > + > > + case PA_CONTEXT_TERMINATED: > > + syslog(LOG_WARNING, "The connection was terminated cleanly"); > > + goto fail; > > + break; > > + > > + case PA_CONTEXT_READY: { > > + pa_operation *op; > > + op = pa_context_get_server_info(context, vdagent_server_info_cb, > > userdata); > > + if (!op) { > > + int err = pa_context_errno(context); > > + syslog(LOG_WARNING, "Fail to get server info: %s", > > pa_strerror(err)); > > + goto fail; > > + } > > + pa_operation_unref(op); > > + break; > > + } > > + default: > > + break; > > + } > > + return; > > +fail: > > + if (os->mainloop != NULL) { > > + syslog(LOG_WARNING, "Cancel operation and quit pulse mainloop"); > > + pa_mainloop_quit (os->mainloop, -1); > > + } > > +} > > + > > +static bool vdagent_pulse_run (struct OperationSpec *os) > > +{ > > + pa_mainloop *loop; > > + pa_mainloop_api *api; > > + pa_context *context; > > + int retval; > > + > > + os->mainloop = NULL; > > + loop = pa_mainloop_new(); > > + if (loop == NULL) { > > + syslog(LOG_WARNING, "Fail to alloc pa_mainloop"); > > + return false; > > + } > > + > > + api = pa_mainloop_get_api(loop); > > + if (api == NULL) { > > + syslog(LOG_WARNING, "Fail to get mainloop_api"); > > + return false; > > + } > > + > > + context = pa_context_new(api, VDAGENT_APP); > > + if (context == NULL) { > > + syslog(LOG_WARNING, "Fail to alloc pa_context"); > > + return false; > > + } > > + > > + pa_context_set_state_callback(context, vdagent_context_state_cb, os); > > + if (pa_context_connect(context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) { > > + syslog(LOG_WARNING, "Fail to connect to default server"); > > + pa_context_unref(context); > > + return false; > > + } > > + > > + os->mainloop = loop; > > + os->context = context; > > + pa_mainloop_run(loop, &retval); > > + pa_context_unref(context); > > + return (retval == 0) ? true : false; > > +} > > + > > +void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t > > *volume) > > +{ > > + struct OperationSpec os; > > + syslog(LOG_DEBUG, "%s mute=%s nchannels=%u", __func__, > > + (mute) ? "yes" : "no", nchannels); > > + os.op_type = OP_PLAYBACK_SYNC; > > + os.mute = (mute) ? true : false; > > + os.nchannels = nchannels; > > + os.volume = volume; > > + if (vdagent_pulse_run (&os) == false) > > + syslog(LOG_WARNING, "Fail to sync playback volume"); > > +} > > + > > +void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t > > *volume) > > +{ > > + struct OperationSpec os; > > + syslog(LOG_DEBUG, "%s mute=%s nchannels=%u", __func__, > > + (mute) ? "yes" : "no", nchannels); > > + os.op_type = OP_RECORD_SYNC; > > + os.mute = (mute) ? true : false; > > + os.nchannels = nchannels; > > + os.volume = volume; > > + if (vdagent_pulse_run (&os) == false) > > + syslog(LOG_WARNING, "Fail to sync record volume"); > > +} > > diff --git a/src/vdagentd-audio.h b/src/vdagentd-audio.h > > new file mode 100644 > > index 0000000..950827a > > --- /dev/null > > +++ b/src/vdagentd-audio.h > > @@ -0,0 +1,27 @@ > > +/* vdagentd-audio.h vdagentd audio handling header > > + > > + Copyright 2015 Red Hat, Inc. > > + > > + This program is free software: you can redistribute it and/or modify > > + it under the terms of the GNU General Public License as published by > > + the Free Software Foundation, either version 3 of the License, or > > + (at your option) any later version. > > + > > + This program 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 General Public License > > + along with this program. If not, see <http://www.gnu.org/licenses/>. > > +*/ > > +#ifndef __VDAGENTD_AUDIO_H > > +#define __VDAGENTD_AUDIO_H > > + > > +#include <stdio.h> > > +#include <stdint.h> > > + > > +void vdagent_audio_playback_sync(uint8_t mute, uint8_t nchannels, uint16_t > > *volume); > > +void vdagent_audio_record_sync(uint8_t mute, uint8_t nchannels, uint16_t > > *volume); > > + > > +#endif > > -- > > 2.1.0 > > > > _______________________________________________ > > Spice-devel mailing list > > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > > http://lists.freedesktop.org/mailman/listinfo/spice-devel > > _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel