From 429a8ed3844b04dd5d8a8eb88f021ed335dff1fa Mon Sep 17 00:00:00 2001 From: malc Date: Mon, 1 Dec 2008 20:57:48 +0000 Subject: [PATCH] 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 --- vnc.c | 132 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) 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) -- 2.11.4.GIT