Re: QEMU VNC Audio Patch



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]