Commit 429a8ed3844b04dd5d8a8eb88f021ed335dff1fa

Authored by malc
1 parent 8da3ff18

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
Showing 1 changed file with 131 additions and 1 deletions
... ... @@ -28,6 +28,7 @@
28 28 #include "sysemu.h"
29 29 #include "qemu_socket.h"
30 30 #include "qemu-timer.h"
  31 +#include "audio/audio.h"
31 32  
32 33 #define VNC_REFRESH_INTERVAL (1000 / 30)
33 34  
... ... @@ -169,6 +170,9 @@ struct VncState
169 170 int client_green_shift, client_green_max, server_green_shift, server_green_max;
170 171 int client_blue_shift, client_blue_max, server_blue_shift, server_blue_max;
171 172  
  173 + CaptureVoiceOut *audio_cap;
  174 + audsettings_t as;
  175 +
172 176 VncReadEvent *read_handler;
173 177 size_t read_handler_expect;
174 178 /* input */
... ... @@ -592,7 +596,7 @@ static void vnc_update_client(void *opaque)
592 596 old_row += ds_get_linesize(vs->ds);
593 597 }
594 598  
595   - if (!has_dirty) {
  599 + if (!has_dirty && !vs->audio_cap) {
596 600 qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL);
597 601 return;
598 602 }
... ... @@ -681,6 +685,71 @@ static void buffer_append(Buffer *buffer, const void *data, size_t len)
681 685 buffer->offset += len;
682 686 }
683 687  
  688 +/* audio */
  689 +static void audio_capture_notify(void *opaque, audcnotification_e cmd)
  690 +{
  691 + VncState *vs = opaque;
  692 +
  693 + switch (cmd) {
  694 + case AUD_CNOTIFY_DISABLE:
  695 + vnc_write_u8(vs, 255);
  696 + vnc_write_u8(vs, 1);
  697 + vnc_write_u16(vs, 0);
  698 + vnc_flush(vs);
  699 + break;
  700 +
  701 + case AUD_CNOTIFY_ENABLE:
  702 + vnc_write_u8(vs, 255);
  703 + vnc_write_u8(vs, 1);
  704 + vnc_write_u16(vs, 1);
  705 + vnc_flush(vs);
  706 + break;
  707 + }
  708 +}
  709 +
  710 +static void audio_capture_destroy(void *opaque)
  711 +{
  712 +}
  713 +
  714 +static void audio_capture(void *opaque, void *buf, int size)
  715 +{
  716 + VncState *vs = opaque;
  717 +
  718 + vnc_write_u8(vs, 255);
  719 + vnc_write_u8(vs, 1);
  720 + vnc_write_u16(vs, 2);
  721 + vnc_write_u32(vs, size);
  722 + vnc_write(vs, buf, size);
  723 + vnc_flush(vs);
  724 +}
  725 +
  726 +static void audio_add(VncState *vs)
  727 +{
  728 + struct audio_capture_ops ops;
  729 +
  730 + if (vs->audio_cap) {
  731 + term_printf ("audio already running\n");
  732 + return;
  733 + }
  734 +
  735 + ops.notify = audio_capture_notify;
  736 + ops.destroy = audio_capture_destroy;
  737 + ops.capture = audio_capture;
  738 +
  739 + vs->audio_cap = AUD_add_capture(NULL, &vs->as, &ops, vs);
  740 + if (!vs->audio_cap) {
  741 + term_printf ("Failed to add audio capture\n");
  742 + }
  743 +}
  744 +
  745 +static void audio_del(VncState *vs)
  746 +{
  747 + if (vs->audio_cap) {
  748 + AUD_del_capture(vs->audio_cap, vs);
  749 + vs->audio_cap = NULL;
  750 + }
  751 +}
  752 +
684 753 static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
685 754 {
686 755 if (ret == 0 || ret == -1) {
... ... @@ -712,6 +781,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno)
712 781 }
713 782 vs->wiremode = VNC_WIREMODE_CLEAR;
714 783 #endif /* CONFIG_VNC_TLS */
  784 + audio_del(vs);
715 785 return 0;
716 786 }
717 787 return ret;
... ... @@ -1138,6 +1208,15 @@ static void send_ext_key_event_ack(VncState *vs)
1138 1208 vnc_flush(vs);
1139 1209 }
1140 1210  
  1211 +static void send_ext_audio_ack(VncState *vs)
  1212 +{
  1213 + vnc_write_u8(vs, 0);
  1214 + vnc_write_u8(vs, 0);
  1215 + vnc_write_u16(vs, 1);
  1216 + vnc_framebuffer_update(vs, 0, 0, ds_get_width(vs->ds), ds_get_height(vs->ds), -259);
  1217 + vnc_flush(vs);
  1218 +}
  1219 +
1141 1220 static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
1142 1221 {
1143 1222 int i;
... ... @@ -1169,6 +1248,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
1169 1248 case -258:
1170 1249 send_ext_key_event_ack(vs);
1171 1250 break;
  1251 + case -259:
  1252 + send_ext_audio_ack(vs);
  1253 + break;
1172 1254 case 0x574D5669:
1173 1255 vs->has_WMVi = 1;
1174 1256 break;
... ... @@ -1476,6 +1558,48 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
1476 1558 ext_key_event(vs, read_u16(data, 2),
1477 1559 read_u32(data, 4), read_u32(data, 8));
1478 1560 break;
  1561 + case 1:
  1562 + if (len == 2)
  1563 + return 4;
  1564 +
  1565 + switch (read_u16 (data, 2)) {
  1566 + case 0:
  1567 + audio_add(vs);
  1568 + break;
  1569 + case 1:
  1570 + audio_del(vs);
  1571 + break;
  1572 + case 2:
  1573 + if (len == 4)
  1574 + return 10;
  1575 + switch (read_u8(data, 4)) {
  1576 + case 0: vs->as.fmt = AUD_FMT_U8; break;
  1577 + case 1: vs->as.fmt = AUD_FMT_S8; break;
  1578 + case 2: vs->as.fmt = AUD_FMT_U16; break;
  1579 + case 3: vs->as.fmt = AUD_FMT_S16; break;
  1580 + case 4: vs->as.fmt = AUD_FMT_U32; break;
  1581 + case 5: vs->as.fmt = AUD_FMT_S32; break;
  1582 + default:
  1583 + printf("Invalid audio format %d\n", read_u8(data, 4));
  1584 + vnc_client_error(vs);
  1585 + break;
  1586 + }
  1587 + vs->as.nchannels = read_u8(data, 5);
  1588 + if (vs->as.nchannels != 1 && vs->as.nchannels != 2) {
  1589 + printf("Invalid audio channel coount %d\n",
  1590 + read_u8(data, 5));
  1591 + vnc_client_error(vs);
  1592 + break;
  1593 + }
  1594 + vs->as.freq = read_u32(data, 6);
  1595 + break;
  1596 + default:
  1597 + printf ("Invalid audio message %d\n", read_u8(data, 4));
  1598 + vnc_client_error(vs);
  1599 + break;
  1600 + }
  1601 + break;
  1602 +
1479 1603 default:
1480 1604 printf("Msg: %d\n", read_u16(data, 0));
1481 1605 vnc_client_error(vs);
... ... @@ -2177,6 +2301,11 @@ void vnc_display_init(DisplayState *ds)
2177 2301  
2178 2302 vnc_colordepth(vs->ds, 32);
2179 2303 vnc_dpy_resize(vs->ds, 640, 400);
  2304 +
  2305 + vs->as.freq = 44100;
  2306 + vs->as.nchannels = 2;
  2307 + vs->as.fmt = AUD_FMT_S16;
  2308 + vs->as.endianness = 0;
2180 2309 }
2181 2310  
2182 2311 #ifdef CONFIG_VNC_TLS
... ... @@ -2269,6 +2398,7 @@ void vnc_display_close(DisplayState *ds)
2269 2398 vs->subauth = VNC_AUTH_INVALID;
2270 2399 vs->x509verify = 0;
2271 2400 #endif
  2401 + audio_del(vs);
2272 2402 }
2273 2403  
2274 2404 int vnc_display_password(DisplayState *ds, const char *password)
... ...