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:
parent
8da3ff1809
commit
429a8ed384
132
vnc.c
132
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)
|
||||
|
Loading…
Reference in New Issue
Block a user