Commit 429a8ed3844b04dd5d8a8eb88f021ed335dff1fa
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
vnc.c
@@ -28,6 +28,7 @@ | @@ -28,6 +28,7 @@ | ||
28 | #include "sysemu.h" | 28 | #include "sysemu.h" |
29 | #include "qemu_socket.h" | 29 | #include "qemu_socket.h" |
30 | #include "qemu-timer.h" | 30 | #include "qemu-timer.h" |
31 | +#include "audio/audio.h" | ||
31 | 32 | ||
32 | #define VNC_REFRESH_INTERVAL (1000 / 30) | 33 | #define VNC_REFRESH_INTERVAL (1000 / 30) |
33 | 34 | ||
@@ -169,6 +170,9 @@ struct VncState | @@ -169,6 +170,9 @@ struct VncState | ||
169 | int client_green_shift, client_green_max, server_green_shift, server_green_max; | 170 | int client_green_shift, client_green_max, server_green_shift, server_green_max; |
170 | int client_blue_shift, client_blue_max, server_blue_shift, server_blue_max; | 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 | VncReadEvent *read_handler; | 176 | VncReadEvent *read_handler; |
173 | size_t read_handler_expect; | 177 | size_t read_handler_expect; |
174 | /* input */ | 178 | /* input */ |
@@ -592,7 +596,7 @@ static void vnc_update_client(void *opaque) | @@ -592,7 +596,7 @@ static void vnc_update_client(void *opaque) | ||
592 | old_row += ds_get_linesize(vs->ds); | 596 | old_row += ds_get_linesize(vs->ds); |
593 | } | 597 | } |
594 | 598 | ||
595 | - if (!has_dirty) { | 599 | + if (!has_dirty && !vs->audio_cap) { |
596 | qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); | 600 | qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); |
597 | return; | 601 | return; |
598 | } | 602 | } |
@@ -681,6 +685,71 @@ static void buffer_append(Buffer *buffer, const void *data, size_t len) | @@ -681,6 +685,71 @@ static void buffer_append(Buffer *buffer, const void *data, size_t len) | ||
681 | buffer->offset += len; | 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 | static int vnc_client_io_error(VncState *vs, int ret, int last_errno) | 753 | static int vnc_client_io_error(VncState *vs, int ret, int last_errno) |
685 | { | 754 | { |
686 | if (ret == 0 || ret == -1) { | 755 | if (ret == 0 || ret == -1) { |
@@ -712,6 +781,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno) | @@ -712,6 +781,7 @@ static int vnc_client_io_error(VncState *vs, int ret, int last_errno) | ||
712 | } | 781 | } |
713 | vs->wiremode = VNC_WIREMODE_CLEAR; | 782 | vs->wiremode = VNC_WIREMODE_CLEAR; |
714 | #endif /* CONFIG_VNC_TLS */ | 783 | #endif /* CONFIG_VNC_TLS */ |
784 | + audio_del(vs); | ||
715 | return 0; | 785 | return 0; |
716 | } | 786 | } |
717 | return ret; | 787 | return ret; |
@@ -1138,6 +1208,15 @@ static void send_ext_key_event_ack(VncState *vs) | @@ -1138,6 +1208,15 @@ static void send_ext_key_event_ack(VncState *vs) | ||
1138 | vnc_flush(vs); | 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 | static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) | 1220 | static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) |
1142 | { | 1221 | { |
1143 | int i; | 1222 | int i; |
@@ -1169,6 +1248,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) | @@ -1169,6 +1248,9 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) | ||
1169 | case -258: | 1248 | case -258: |
1170 | send_ext_key_event_ack(vs); | 1249 | send_ext_key_event_ack(vs); |
1171 | break; | 1250 | break; |
1251 | + case -259: | ||
1252 | + send_ext_audio_ack(vs); | ||
1253 | + break; | ||
1172 | case 0x574D5669: | 1254 | case 0x574D5669: |
1173 | vs->has_WMVi = 1; | 1255 | vs->has_WMVi = 1; |
1174 | break; | 1256 | break; |
@@ -1476,6 +1558,48 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) | @@ -1476,6 +1558,48 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) | ||
1476 | ext_key_event(vs, read_u16(data, 2), | 1558 | ext_key_event(vs, read_u16(data, 2), |
1477 | read_u32(data, 4), read_u32(data, 8)); | 1559 | read_u32(data, 4), read_u32(data, 8)); |
1478 | break; | 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 | default: | 1603 | default: |
1480 | printf("Msg: %d\n", read_u16(data, 0)); | 1604 | printf("Msg: %d\n", read_u16(data, 0)); |
1481 | vnc_client_error(vs); | 1605 | vnc_client_error(vs); |
@@ -2177,6 +2301,11 @@ void vnc_display_init(DisplayState *ds) | @@ -2177,6 +2301,11 @@ void vnc_display_init(DisplayState *ds) | ||
2177 | 2301 | ||
2178 | vnc_colordepth(vs->ds, 32); | 2302 | vnc_colordepth(vs->ds, 32); |
2179 | vnc_dpy_resize(vs->ds, 640, 400); | 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 | #ifdef CONFIG_VNC_TLS | 2311 | #ifdef CONFIG_VNC_TLS |
@@ -2269,6 +2398,7 @@ void vnc_display_close(DisplayState *ds) | @@ -2269,6 +2398,7 @@ void vnc_display_close(DisplayState *ds) | ||
2269 | vs->subauth = VNC_AUTH_INVALID; | 2398 | vs->subauth = VNC_AUTH_INVALID; |
2270 | vs->x509verify = 0; | 2399 | vs->x509verify = 0; |
2271 | #endif | 2400 | #endif |
2401 | + audio_del(vs); | ||
2272 | } | 2402 | } |
2273 | 2403 | ||
2274 | int vnc_display_password(DisplayState *ds, const char *password) | 2404 | int vnc_display_password(DisplayState *ds, const char *password) |