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