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
This commit is contained in:
malc 2008-12-01 20:57:48 +00:00
parent 8da3ff1809
commit 429a8ed384

132
vnc.c
View File

@ -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)