Re: QEMU VNC Audio Patch
- From: Steven Carr <Steven Carr scaa-usa com>
- To: "Daniel P. Berrange" <dan berrange com>
- Cc: jwendell gnome org, gtk-vnc-list gnome org
- Subject: Re: QEMU VNC Audio Patch
- Date: Fri, 2 Dec 2011 00:16:22 -0500
On Wednesday 30 November 2011 11:44:46 Daniel P. Berrange wrote:
> Moving the message to the gtk-vnc-devel mailing list....
>
> On Tue, Nov 29, 2011 at 04:50:22PM -0500, Steven Carr wrote:
> > Hello,
> > My name is Steven Carr. I have written a patch to gtk-vnc to support
> > QEMU/KVM Audio over VNC (outputs to PulseAudio). As with all things
> > Open Source, I had a need and scratched it. I needed audio supported
> > for virt- manager. I use RHEL 6.1, so audio output uses PulseAudio.
> > The patch is based on the contents of the git repository as of
> > 2011-11-29. This was the easiest approach to transparently enabling
> > audio on virt-manager.
>
> Thanks for hacking on this. I too have hacked on audio support myself but
> I always ended up down a rathole trying to wire it up to gstreamer, so
> never ended up finishing it. What you have done with pulseaudio is much
> simpler and shows me a nicer direction for us to take :-)
>
> > Taking into consideration the various distributions and sound outputs,
> > it might be better to simply have a callback to deliver the audio data
> > packets to the user application. This way, it is up to the calling
> > application to implement proper audio output (ESD, Pulse, written to a
> > mpeg file, whatever).
> >
> > int do_audio(void *private, int state, int bytes, char *data)
> > {
> > my_private_t *priv = (my_private_t *)private;
> >
> > switch(state) {
> > case VNC_QEMU_AUDIO_START:
> > case VNC_QEMU_AUDIO_STOP:
> > case VNC_QEMU_AUDIO_DATA:
> > /* magic happens here */
> > }
> > }
> >
> > /* register audio handler */
> > void vnc_connection_audio_callback(conn, do_audio, (void
> > *)private_struct); void vnc_connection_audio_enable(conn, sample_size,
> > channels, rate); void vnc_connection_audio_disable(conn);
>
> I think what I'd do is to create a new GObject interface
>
> VncAudioSink *
>
> which have a couple of methods
>
> vnc_audio_sink_start(VncAudioSink *sink, VncAudioFormat *format);;
> vnc_audio_sink_write(VncAudioSink *sink, void *data, gszie len)
> vnc_audio_sink_stop(VncAudioSink *sink);
>
> VncAudiobuffer is a simple struct, much like VncPixelformat, just for
> holding the sample size / channels / rate
>
> Then, create an implementation of that interface for pulseaudio
>
> VncAudioSinkPulse *
>
> To complete things we just need an API to allow attaching of
> an VncAudioSink * to a VncConnection *.
>
> vnc_connection_set_audio_sink(VncConnection *conn, VncAudiSink *sink);
>
>
> Is this something you have more time to work on ? If not I'll use you
> patch as the basis for doing this myself in the future.
>
> Regards,
> Daniel
>
Is there a reason you want the audio as a separate GObject instead of simply
adding 3 new emitters on the existing GObject? I am new to the gnome
development methodology and do not understand the benefit of having the audio
broken out as a separate GObject.
Attached is a diff that extends gtk-vnc's existing GObject to add the three new
QEMU Audio messages (Begin, End, and Data). 3 new functions (both display
and connection) are added to support (Enable, Disable, and SetFormat). Since
the format for the audio is only 3 parameters, I simply made them a part of
the function call instead of a separate structure for them. I think it makes
the code easier to read. The names were chosen to align with the QEMU code
that describes the messages (Begin/End/Data).
Also, Pulse Audio output support is added to the example gvncviewer
application and has been tested against qemu-kvm on RHEL 6.1 x86-64.
Comments are welcomed,
~Steven
diff --git a/configure.ac b/configure.ac
index d072173..26a0d8f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -204,6 +204,15 @@ AC_DEFINE_UNQUOTED(WITH_LIBVIEW,[$WITH_LIBVIEW], [Whether to use libview])
AC_SUBST(VIEW_CFLAGS)
AC_SUBST(VIEW_LIBS)
+
+PKG_CHECK_MODULES(PULSEAUDIO, libpulse-simple, WANT_PULSEAUDIO="yes", AC_MSG_NOTICE([Not building against libpulse]))
+if test "x$WANT_PULSEAUDIO" = xyes; then
+ AC_DEFINE_UNQUOTED([HAVE_PULSEAUDIO], 1,[Define if we have and want pulseaudio.])
+fi
+AC_SUBST(PULSEAUDIO_CFLAGS)
+AC_SUBST(PULSEAUDIO_LIBS)
+
+
PKG_CHECK_MODULES(GNUTLS, gnutls >= $GNUTLS_REQUIRED)
dnl Not all versions of gnutls include -lgcrypt and -lgpg-error, and we need it
dnl explicitly for building MS Logon extension which calls
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 12aa20b..e7f7399 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -10,9 +10,10 @@ gvncviewer_LDADD = ../src/libgtk-vnc-$(GTK_VNC_API_VERSION).la \
../src/libgvnc-1.0.la \
@GTK_CFLAGS@ \
@GTK_LIBS@ \
- @VIEW_LIBS@
+ @VIEW_LIBS@ \
+ @PULSEAUDIO_LIBS@
gvncviewer_CFLAGS = @GTK_CFLAGS@ @WARNING_CFLAGS@ \
- @VIEW_CFLAGS@ -I$(top_srcdir)/src/
+ @VIEW_CFLAGS@ @PULSEAUDIO_CFLAGS@ -I$(top_srcdir)/src/
EXTRA_DIST = gvncviewer.py gvncviewer.js
diff --git a/examples/gvncviewer.c b/examples/gvncviewer.c
index 84f36e0..1b128e4 100644
--- a/examples/gvncviewer.c
+++ b/examples/gvncviewer.c
@@ -28,6 +28,12 @@
#include "config.h"
+#ifdef HAVE_PULSEAUDIO
+#include <pulse/simple.h>
+#include <pulse/error.h>
+#include <pulse/gccmacro.h>
+#endif
+
#if WITH_LIBVIEW
#include <libview/autoDrawer.h>
#endif
@@ -88,6 +94,9 @@ static const GOptionEntry options [] =
{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, NULL, 0 }
};
+#ifdef HAVE_PULSEAUDIO
+static pa_simple *pulse=NULL;
+#endif
static GtkWidget *vnc;
@@ -155,6 +164,12 @@ static void vnc_initialized(GtkWidget *vncdisplay, GtkWidget *window)
printf("Connection initialized\n");
set_title(VNC_DISPLAY(vncdisplay), window, FALSE);
gtk_widget_show_all(window);
+
+#ifdef HAVE_PULSEAUDIO
+ /* look at do_audio_begin() for the actual Pulse Audio setup */
+ vnc_display_set_audio_format(VNC_DISPLAY(vncdisplay), 3, 2, 44100);
+ vnc_display_audio_enable(VNC_DISPLAY(vncdisplay));
+#endif
}
static void vnc_auth_failure(GtkWidget *vncdisplay G_GNUC_UNUSED,
@@ -178,6 +193,40 @@ static void vnc_disconnected(GtkWidget *vncdisplay G_GNUC_UNUSED)
gtk_main_quit();
}
+#ifdef HAVE_PULSEAUDIO
+static void do_audio_data(GtkWidget *vncdisplay G_GNUC_UNUSED,
+ const char *data, unsigned int len)
+{
+ int paerror;
+
+ if (pulse && pa_simple_write(pulse, data, len, &paerror)<0)
+ {
+ printf("pa_simple_write() failed with: %s\n", pa_strerror(paerror));
+ pa_simple_free(pulse);
+ pulse=NULL;
+ }
+}
+
+
+static void do_audio_begin(GtkWidget *vncdisplay G_GNUC_UNUSED)
+{
+ pa_sample_spec pulse_spec;
+
+ pulse_spec.format=PA_SAMPLE_S16LE;
+ pulse_spec.channels=2;
+ pulse_spec.rate=44100;
+
+ pulse = pa_simple_new(NULL, "gtk-vnc", PA_STREAM_PLAYBACK, NULL, "gvncviewer", &pulse_spec, NULL, NULL, NULL);
+
+}
+
+static void do_audio_end(GtkWidget *vncdisplay G_GNUC_UNUSED)
+{
+ if (pulse) pa_simple_free(pulse);
+ pulse=NULL;
+}
+#endif
+
static void send_caf1(GtkWidget *menu G_GNUC_UNUSED, GtkWidget *vncdisplay)
{
guint keys[] = { GDK_Control_L, GDK_Alt_L, GDK_F1 };
@@ -739,6 +788,15 @@ int main(int argc, char **argv)
G_CALLBACK(window_state_event), layout);
#endif
+#ifdef HAVE_PULSEAUDIO
+ g_signal_connect(vnc, "vnc-audio-begin",
+ G_CALLBACK(do_audio_begin), NULL);
+ g_signal_connect(vnc, "vnc-audio-end",
+ G_CALLBACK(do_audio_end), NULL);
+ g_signal_connect(vnc, "vnc-audio-data",
+ G_CALLBACK(do_audio_data), NULL);
+#endif
+
gtk_main();
return 0;
diff --git a/src/libgtk-vnc_sym.version b/src/libgtk-vnc_sym.version
index 7b641ec..a5e2726 100644
--- a/src/libgtk-vnc_sym.version
+++ b/src/libgtk-vnc_sym.version
@@ -82,6 +82,10 @@
vnc_grab_sequence_as_string;
vnc_grab_sequence_get_type;
+# Audio Support!
+ vnc_display_set_audio_format;
+ vnc_display_audio_enable;
+ vnc_display_audio_disable;
local:
*;
};
diff --git a/src/libgvnc_sym.version b/src/libgvnc_sym.version
index 63ff90f..4e54d7c 100644
--- a/src/libgvnc_sym.version
+++ b/src/libgvnc_sym.version
@@ -74,6 +74,10 @@
vnc_pixel_format_free;
vnc_pixel_format_get_type;
+ vnc_connection_audio_enable;
+ vnc_connection_audio_disable;
+ vnc_connection_set_audio_format;
+
local:
*;
};
diff --git a/src/vncconnection.c b/src/vncconnection.c
index 0a947f0..5f25e35 100644
--- a/src/vncconnection.c
+++ b/src/vncconnection.c
@@ -180,6 +180,14 @@ struct _VncConnectionPrivate
guint16 width;
guint16 height;
} lastUpdateRequest;
+
+ gboolean has_audio;
+ gboolean audio_format_pending;
+ gboolean audio_enable_pending;
+ gboolean audio_disable_pending;
+ guint8 audio_format;
+ guint8 audio_channels;
+ guint32 audio_frequency;
};
G_DEFINE_TYPE(VncConnection, vnc_connection, G_TYPE_OBJECT);
@@ -204,13 +212,18 @@ enum {
VNC_INITIALIZED,
VNC_DISCONNECTED,
+ VNC_AUDIO_BEGIN,
+ VNC_AUDIO_END,
+ VNC_AUDIO_DATA,
+
VNC_LAST_SIGNAL,
};
static guint signals[VNC_LAST_SIGNAL] = { 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
- 0, 0, 0 };
+ 0, 0, 0, 0,
+ 0, 0 };
#define nibhi(a) (((a) >> 4) & 0x0F)
#define niblo(a) ((a) & 0x0F)
@@ -404,6 +417,10 @@ struct signal_data
int width;
int height;
} size;
+ struct {
+ int length;
+ char *data;
+ } audio;
VncPixelFormat *pixelFormat;
const char *authReason;
unsigned int authUnsupported;
@@ -506,6 +523,16 @@ static gboolean do_vnc_connection_emit_main_context(gpointer opaque)
data->params.authTypes);
break;
+ case VNC_AUDIO_DATA:
+ g_signal_emit(G_OBJECT(data->conn),
+ signals[data->signum],
+ 0,
+ data->params.audio.data,
+ data->params.audio.length);
+ break;
+
+ case VNC_AUDIO_BEGIN:
+ case VNC_AUDIO_END:
case VNC_CONNECTED:
case VNC_INITIALIZED:
case VNC_DISCONNECTED:
@@ -1486,6 +1513,69 @@ gboolean vnc_connection_set_pixel_format(VncConnection *conn,
}
+gboolean vnc_connection_set_audio_format(VncConnection *conn, guint32 format, guint32 channels, guint32 frequency)
+{
+ VncConnectionPrivate *priv = conn->priv;
+
+ if (priv->has_audio)
+ {
+ vnc_connection_buffered_write_u8(conn, 255);
+ vnc_connection_buffered_write_u8(conn, 1); /* VNC_CONNECTION_QEMU_AUDIO */
+ vnc_connection_buffered_write_u16(conn, 2); /* VNC_CONNECTION_QEMU_AUDIO_SET_FORMAT */
+
+ vnc_connection_buffered_write_u8(conn, format);
+ vnc_connection_buffered_write_u8(conn, channels);
+ vnc_connection_buffered_write_u32(conn, frequency);
+ vnc_connection_buffered_flush(conn);
+ priv->audio_format_pending=FALSE;
+ }
+ else
+ {
+ priv->audio_format_pending=TRUE;
+ priv->audio_format=format;
+ priv->audio_channels=channels;
+ priv->audio_frequency=frequency;
+ }
+
+ return !vnc_connection_has_error(conn);
+}
+
+gboolean vnc_connection_audio_enable(VncConnection *conn)
+{
+ VncConnectionPrivate *priv = conn->priv;
+
+ if (priv->has_audio)
+ {
+ vnc_connection_buffered_write_u8(conn, 255); /* QEMU message */
+ vnc_connection_buffered_write_u8(conn, 1); /* VNC_CONNECTION_QEMU_AUDIO */
+ vnc_connection_buffered_write_u16(conn, 0); /* VNC_CONNECTION_QEMU_AUDIO_ENABLE */
+ vnc_connection_buffered_flush(conn);
+ priv->audio_enable_pending=FALSE;
+ }
+ else
+ priv->audio_enable_pending=TRUE;
+ return !vnc_connection_has_error(conn);
+}
+
+gboolean vnc_connection_audio_disable(VncConnection *conn)
+{
+ VncConnectionPrivate *priv = conn->priv;
+ priv->audio_disable_pending=TRUE;
+
+ if (priv->has_audio)
+ {
+ vnc_connection_buffered_write_u8(conn, 255); /* QEMU message */
+ vnc_connection_buffered_write_u8(conn, 1); /* VNC_CONNECTION_QEMU_AUDIO */
+ vnc_connection_buffered_write_u16(conn, 1); /* VNC_CONNECTION_QEMU_AUDIO_ENABLE */
+ vnc_connection_buffered_flush(conn);
+ priv->audio_disable_pending=FALSE;
+ }
+ else
+ priv->audio_disable_pending=TRUE;
+ return !vnc_connection_has_error(conn);
+}
+
+
gboolean vnc_connection_set_encodings(VncConnection *conn, int n_encoding, gint32 *encoding)
{
VncConnectionPrivate *priv = conn->priv;
@@ -1515,6 +1605,7 @@ gboolean vnc_connection_set_encodings(VncConnection *conn, int n_encoding, gint3
}
priv->has_ext_key_event = FALSE;
+ priv->has_audio = FALSE;
vnc_connection_buffered_write_u8(conn, 2);
vnc_connection_buffered_write(conn, pad, 1);
vnc_connection_buffered_write_u16(conn, n_encoding - skip_zrle);
@@ -2667,6 +2758,41 @@ static gboolean vnc_connection_validate_boundary(VncConnection *conn,
}
+static void vnc_audio_sink_begin(VncConnection *conn)
+{
+ VncConnectionPrivate *priv = conn->priv;
+ struct signal_data sigdata;
+
+ if (priv->has_error)
+ return;
+
+ vnc_connection_emit_main_context(conn, VNC_AUDIO_BEGIN, &sigdata);
+}
+
+static void vnc_audio_sink_end(VncConnection *conn)
+{
+ VncConnectionPrivate *priv = conn->priv;
+ struct signal_data sigdata;
+
+ if (priv->has_error)
+ return;
+
+ vnc_connection_emit_main_context(conn, VNC_AUDIO_END, &sigdata);
+}
+
+static void vnc_audio_sink_data(VncConnection *conn, char *data, guint32 n_length)
+{
+ VncConnectionPrivate *priv = conn->priv;
+ struct signal_data sigdata;
+
+ if (priv->has_error)
+ return;
+
+ sigdata.params.audio.data = data;
+ sigdata.params.audio.length = n_length;
+ vnc_connection_emit_main_context(conn, VNC_AUDIO_DATA, &sigdata);
+}
+
static gboolean vnc_connection_framebuffer_update(VncConnection *conn, gint32 etype,
guint16 x, guint16 y,
guint16 width, guint16 height)
@@ -2739,6 +2865,14 @@ static gboolean vnc_connection_framebuffer_update(VncConnection *conn, gint32 et
vnc_connection_ext_key_event(conn);
vnc_connection_resend_framebuffer_update_request(conn);
break;
+ case VNC_CONNECTION_ENCODING_AUDIO:
+ priv->has_audio=TRUE;
+
+ if (priv->audio_disable_pending) vnc_connection_audio_disable(conn);
+ if (priv->audio_format_pending) vnc_connection_set_audio_format(conn, priv->audio_format, priv->audio_channels, priv->audio_frequency);
+ if (priv->audio_enable_pending) vnc_connection_audio_enable(conn);
+
+ break;
default:
VNC_DEBUG("Received an unknown encoding type: %d", etype);
priv->has_error = TRUE;
@@ -2854,6 +2988,30 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
vnc_connection_server_cut_text(conn, data, n_text);
g_free(data);
} break;
+ case 255: { /* QEMU Messages */
+ guint8 n_type;
+ guint16 n_subtype;
+ guint32 n_length;
+ char *data;
+
+ vnc_connection_read(conn, &n_type, 1);
+ n_subtype = vnc_connection_read_u16(conn);
+ if (n_type==1) /* QEMU audio */
+ {
+ switch (n_subtype)
+ {
+ case 2:
+ n_length = vnc_connection_read_u32(conn);
+ data = g_new(char, n_length);
+ vnc_connection_read(conn, data, n_length);
+ vnc_audio_sink_data(conn, data, n_length);
+ g_free(data);
+ break;
+ case 1: vnc_audio_sink_begin(conn); break;
+ case 0: vnc_audio_sink_end(conn); break;
+ }
+ }
+ } break;
default:
VNC_DEBUG("Received an unknown message: %u", msg);
priv->has_error = TRUE;
@@ -4333,6 +4491,37 @@ static void vnc_connection_class_init(VncConnectionClass *klass)
G_TYPE_NONE,
0);
+ signals[VNC_AUDIO_BEGIN] =
+ g_signal_new ("vnc-audio-begin",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (VncConnectionClass, vnc_audio_begin),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_AUDIO_END] =
+ g_signal_new ("vnc-audio-end",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (VncConnectionClass, vnc_audio_end),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_AUDIO_DATA] =
+ g_signal_new ("vnc-audio-data",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (VncConnectionClass, vnc_audio_data),
+ NULL, NULL,
+ g_cclosure_user_marshal_VOID__POINTER_UINT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_POINTER,
+ G_TYPE_UINT);
g_type_class_add_private(klass, sizeof(VncConnectionPrivate));
}
diff --git a/src/vncconnection.h b/src/vncconnection.h
index e189351..f8054ee 100644
--- a/src/vncconnection.h
+++ b/src/vncconnection.h
@@ -72,6 +72,10 @@ struct _VncConnectionClass
void (*vnc_initialized)(VncConnection *conn);
void (*vnc_disconnected)(VncConnection *conn);
+ void (*vnc_audio_begin)(VncConnection *conn);
+ void (*vnc_audio_end)(VncConnection *conn);
+ void (*vnc_audio_data)(VncConnection *conn, const char *data, unsigned int length);
+
/*
* If adding fields to this struct, remove corresponding
* amount of padding to avoid changing overall struct size
@@ -111,6 +115,7 @@ typedef enum {
VNC_CONNECTION_ENCODING_POINTER_CHANGE = -257,
VNC_CONNECTION_ENCODING_EXT_KEY_EVENT = -258,
+ VNC_CONNECTION_ENCODING_AUDIO = -259,
} VncConnectionEncoding;
typedef enum {
@@ -147,7 +152,6 @@ typedef enum
VNC_CONNECTION_CREDENTIAL_CLIENTNAME,
} VncConnectionCredential;
-
GType vnc_connection_get_type(void) G_GNUC_CONST;
VncConnection *vnc_connection_new(void);
@@ -201,6 +205,11 @@ VncCursor *vnc_connection_get_cursor(VncConnection *conn);
gboolean vnc_connection_get_abs_pointer(VncConnection *conn);
gboolean vnc_connection_get_ext_key_event(VncConnection *conn);
+gboolean vnc_connection_set_audio_format(VncConnection *conn, guint32 format, guint32 channels, guint32 frequency);
+gboolean vnc_connection_audio_enable(VncConnection *conn);
+gboolean vnc_connection_audio_disable(VncConnection *conn);
+
+
G_END_DECLS
#endif /* VNC_CONNECTION_H */
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index f9dedad..cdc62c3 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -126,6 +126,10 @@ typedef enum
VNC_SERVER_CUT_TEXT,
VNC_BELL,
+ VNC_AUDIO_BEGIN,
+ VNC_AUDIO_END,
+ VNC_AUDIO_DATA,
+
LAST_SIGNAL
} vnc_display_signals;
@@ -147,7 +151,8 @@ static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh)
static guint signals[LAST_SIGNAL] = { 0, 0, 0, 0,
0, 0, 0, 0,
- 0, 0, 0, 0, 0,};
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,};
static gboolean vnc_debug_option_arg(const gchar *option_name G_GNUC_UNUSED,
const gchar *value G_GNUC_UNUSED,
@@ -1235,6 +1240,34 @@ static void on_bell(VncConnection *conn G_GNUC_UNUSED,
g_signal_emit(G_OBJECT(obj), signals[VNC_BELL], 0);
}
+
+
+static void on_audio_begin(VncConnection *conn G_GNUC_UNUSED,
+ gpointer opaque)
+{
+ VncDisplay *obj = VNC_DISPLAY(opaque);
+
+ g_signal_emit(G_OBJECT(obj), signals[VNC_AUDIO_BEGIN], 0);
+}
+
+static void on_audio_end(VncConnection *conn G_GNUC_UNUSED,
+ gpointer opaque)
+{
+ VncDisplay *obj = VNC_DISPLAY(opaque);
+
+ g_signal_emit(G_OBJECT(obj), signals[VNC_AUDIO_END], 0);
+}
+
+static void on_audio_data(VncConnection *conn G_GNUC_UNUSED,
+ const char *data,
+ unsigned int len,
+ gpointer opaque)
+{
+ VncDisplay *obj = VNC_DISPLAY(opaque);
+
+ g_signal_emit(G_OBJECT(obj), signals[VNC_AUDIO_DATA], 0, data, len);
+}
+
static void on_cursor_changed(VncConnection *conn G_GNUC_UNUSED,
VncCursor *cursor,
gpointer opaque)
@@ -1325,6 +1358,7 @@ static void on_initialized(VncConnection *conn G_GNUC_UNUSED,
VNC_CONNECTION_ENCODING_EXT_KEY_EVENT,
VNC_CONNECTION_ENCODING_DESKTOP_RESIZE,
VNC_CONNECTION_ENCODING_WMVi,
+ VNC_CONNECTION_ENCODING_AUDIO,
VNC_CONNECTION_ENCODING_RICH_CURSOR,
VNC_CONNECTION_ENCODING_XCURSOR,
VNC_CONNECTION_ENCODING_POINTER_CHANGE,
@@ -1894,6 +1928,41 @@ static void vnc_display_class_init(VncDisplayClass *klass)
G_TYPE_NONE,
0);
+ signals[VNC_AUDIO_BEGIN] =
+ g_signal_new ("vnc-audio-begin",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_AUDIO_END] =
+ g_signal_new ("vnc-audio-end",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ signals[VNC_AUDIO_DATA] =
+ g_signal_new ("vnc-audio-data",
+ G_TYPE_FROM_CLASS(klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_user_marshal_VOID__POINTER_UINT,
+ G_TYPE_NONE,
+ 2,
+ G_TYPE_POINTER,
+ G_TYPE_UINT);
+
g_type_class_add_private(klass, sizeof(VncDisplayPrivate));
}
@@ -2010,6 +2079,12 @@ static void vnc_display_init(VncDisplay *display)
G_CALLBACK(on_initialized), display);
g_signal_connect(G_OBJECT(priv->conn), "vnc-disconnected",
G_CALLBACK(on_disconnected), display);
+ g_signal_connect(G_OBJECT(priv->conn), "vnc-audio-begin",
+ G_CALLBACK(on_audio_begin), display);
+ g_signal_connect(G_OBJECT(priv->conn), "vnc-audio-end",
+ G_CALLBACK(on_audio_end), display);
+ g_signal_connect(G_OBJECT(priv->conn), "vnc-audio-data",
+ G_CALLBACK(on_audio_data), display);
priv->keycode_map = vnc_display_keymap_gdk2rfb_table(&priv->keycode_maplen);
}
@@ -2316,6 +2391,40 @@ vnc_display_request_update(VncDisplay *obj)
vnc_connection_get_width(obj->priv->conn));
}
+gboolean
+vnc_display_set_audio_format(VncDisplay *obj, guint32 format, guint32 channels, guint32 frequency)
+{
+ g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
+
+ if (!obj->priv->conn || !vnc_connection_is_initialized(obj->priv->conn))
+ return FALSE;
+
+ return vnc_connection_set_audio_format(obj->priv->conn, format, channels, frequency);
+}
+
+gboolean
+vnc_display_audio_enable(VncDisplay *obj)
+{
+ g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
+
+ if (!obj->priv->conn || !vnc_connection_is_initialized(obj->priv->conn))
+ return FALSE;
+
+ return vnc_connection_audio_enable(obj->priv->conn);
+}
+
+
+gboolean
+vnc_display_audio_disable(VncDisplay *obj)
+{
+ g_return_val_if_fail (VNC_IS_DISPLAY (obj), FALSE);
+
+ if (!obj->priv->conn || !vnc_connection_is_initialized(obj->priv->conn))
+ return FALSE;
+
+ return vnc_connection_audio_disable(obj->priv->conn);
+}
+
/*
* Local variables:
* c-indent-level: 8
diff --git a/src/vncdisplay.h b/src/vncdisplay.h
index 76f44d4..2466bc6 100644
--- a/src/vncdisplay.h
+++ b/src/vncdisplay.h
@@ -148,6 +148,10 @@ const GOptionEntry * vnc_display_get_option_entries(void);
gboolean vnc_display_request_update(VncDisplay *obj);
+gboolean vnc_display_set_audio_format(VncDisplay *obj, guint32 format, guint32 channels, guint32 frequency);
+gboolean vnc_display_audio_enable(VncDisplay *obj);
+gboolean vnc_display_audio_disable(VncDisplay *obj);
+
G_END_DECLS
#endif /* VNC_DISPLAY_H */
diff --git a/src/vncmarshal.txt b/src/vncmarshal.txt
index b60bcaf..23248cc 100644
--- a/src/vncmarshal.txt
+++ b/src/vncmarshal.txt
@@ -1,3 +1,4 @@
VOID:INT,INT
VOID:INT,INT,INT,INT
VOID:UINT,BOXED
+VOID:POINTER,UINT
[Date Prev][
Date Next] [Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]