Commit 429a8ed3 authored by malc's avatar malc
Browse files

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
parent 8da3ff18
Loading
Loading
Loading
Loading
+131 −1
Original line number Diff line number Diff line
@@ -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)