aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormalc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>2008-12-01 20:57:48 +0000
committermalc <malc@c046a42c-6fe2-441c-8c8c-71466251a162>2008-12-01 20:57:48 +0000
commit429a8ed3844b04dd5d8a8eb88f021ed335dff1fa (patch)
treec9e011a4535351e769a0c1a900143a033698c6c5
parent8da3ff180974732fc4272cb4433fef85c1822961 (diff)
Add basic audio functionality to vnc.c
This allows among other things to capturing A/V of running guests. Ad-hoc client that does this (via script that invokes ffmpeg) can be found at: http://repo.or.cz/w/qemu/malc.git?a=tree;f=vcap;hb=capture3 Thanks to Anthony Liguori for comments and review. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5850 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--vnc.c132
1 files changed, 131 insertions, 1 deletions
diff --git a/vnc.c b/vnc.c
index 1d9c113539..99d7534fae 100644
--- a/vnc.c
+++ b/vnc.c
@@ -28,6 +28,7 @@
#include "sysemu.h"
#include "qemu_socket.h"
#include "qemu-timer.h"
+#include "audio/audio.h"
#define VNC_REFRESH_INTERVAL (1000 / 30)
@@ -169,6 +170,9 @@ struct VncState
int client_green_shift, client_green_max, server_green_shift, server_green_max;
int client_blue_shift, client_blue_max, server_blue_shift, server_blue_max;
+ CaptureVoiceOut *audio_cap;
+ audsettings_t as;
+
VncReadEvent *read_handler;
size_t read_handler_expect;
/* input */
@@ -592,7 +596,7 @@ static void vnc_update_client(void *opaque)
old_row += ds_get_linesize(vs->ds);
}
- if (!has_dirty) {
+ if (!has_dirty && !vs->audio_cap) {
qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
return;
}
@@ -681,6 +685,71 @@ static void buffer_append(Buffer *buffer, const void *data, size_t len)
buffer->offset += len;
}
+/* audio */
+static void audio_capture_notify(void *opaque, audcnotification_e cmd)
+{
+ VncState *vs = opaque;
+
+ switch (cmd) {
+ case AUD_CNOTIFY_DISABLE:
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 1);
+ vnc_write_u16(vs, 0);
+ vnc_flush(vs);
+ break;
+
+ case AUD_CNOTIFY_ENABLE:
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 1);
+ vnc_write_u16(vs, 1);
+ vnc_flush(vs);
+ break;
+ }
+}
+
+static void audio_capture_destroy(void *opaque)
+{
+}
+
+static void audio_capture(void *opaque, void *buf, int size)
+{
+ VncState *vs = opaque;
+
+ vnc_write_u8(vs, 255);
+ vnc_write_u8(vs, 1);
+ vnc_write_u16(vs, 2);
+ vnc_write_u32(vs, size);
+ vnc_write(vs, buf, size);
+ vnc_flush(vs);
+}
+
+static void audio_add(VncState *vs)
+{
+ struct audio_capture_ops ops;
+
+ if (vs->audio_cap) {
+ term_printf ("audio already running\n");
+ return;
+ }
+
+ ops.notify = audio_capture_notify;
+ ops.destroy = audio_capture_destroy;
+ ops.capture = audio_capture;
+
+ vs->audio_cap = AUD_add_capture(NULL, &vs->as, &ops, vs);
+ if (!vs->audio_cap) {
+ term_printf ("Failed to add audio capture\n");
+ }
+}
+
+static void audio_del(VncState *vs)
+{
+ if (vs->audio_cap) {
+ AUD_del_capture(vs->audio_cap, vs);
+ vs->audio_cap = NULL;
+ }
+}
+
static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
{
if (ret == 0 || ret == -1) {
@@ -712,6 +781,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
}
vs->wiremode = VNC_WIREMODE_CLEAR;
#endif /* CONFIG_VNC_TLS */
+ audio_del(vs);
return 0;
}
return ret;
@@ -1138,6 +1208,15 @@ static void send_ext_key_event_ack(VncState *vs)
vnc_flush(vs);
}
+static void send_ext_audio_ack(VncState *vs)
+{
+ vnc_write_u8(vs, 0);
+ vnc_write_u8(vs, 0);
+ vnc_write_u16(vs, 1);
+ vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), -259);
+ vnc_flush(vs);
+}
+
static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
{
int i;
@@ -1169,6 +1248,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
case -258:
send_ext_key_event_ack(vs);
break;
+ case -259:
+ send_ext_audio_ack(vs);
+ break;
case 0x574D5669:
vs->has_WMVi = 1;
break;
@@ -1476,6 +1558,48 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
ext_key_event(vs, read_u16(data, 2),
read_u32(data, 4), read_u32(data, 8));
break;
+ case 1:
+ if (len == 2)
+ return 4;
+
+ switch (read_u16 (data, 2)) {
+ case 0:
+ audio_add(vs);
+ break;
+ case 1:
+ audio_del(vs);
+ break;
+ case 2:
+ if (len == 4)
+ return 10;
+ switch (read_u8(data, 4)) {
+ case 0: vs->as.fmt = AUD_FMT_U8; break;
+ case 1: vs->as.fmt = AUD_FMT_S8; break;
+ case 2: vs->as.fmt = AUD_FMT_U16; break;
+ case 3: vs->as.fmt = AUD_FMT_S16; break;
+ case 4: vs->as.fmt = AUD_FMT_U32; break;
+ case 5: vs->as.fmt = AUD_FMT_S32; break;
+ default:
+ printf("Invalid audio format %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.nchannels = read_u8(data, 5);
+ if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
+ printf("Invalid audio channel coount %d\n",
+ read_u8(data, 5));
+ vnc_client_error(vs);
+ break;
+ }
+ vs->as.freq = read_u32(data, 6);
+ break;
+ default:
+ printf ("Invalid audio message %d\n", read_u8(data, 4));
+ vnc_client_error(vs);
+ break;
+ }
+ break;
+
default:
printf("Msg: %d\n", read_u16(data, 0));
vnc_client_error(vs);
@@ -2177,6 +2301,11 @@ void vnc_display_init(DisplayState *ds)
vnc_colordepth(vs->ds, 32);
vnc_dpy_resize(vs->ds, 640, 400);
+
+ vs->as.freq = 44100;
+ vs->as.nchannels = 2;
+ vs->as.fmt = AUD_FMT_S16;
+ vs->as.endianness = 0;
}
#ifdef CONFIG_VNC_TLS
@@ -2269,6 +2398,7 @@ void vnc_display_close(DisplayState *ds)
vs->subauth = VNC_AUTH_INVALID;
vs->x509verify = 0;
#endif
+ audio_del(vs);
}
int vnc_display_password(DisplayState *ds, const char *password)