Disable the audio feature in VNC when QEMU is configured with
`--disable-audio`. Do not compile the corresponding audio-related
code.
Signed-off-by: Sergei Heifetz <heifetz@yandex-team.com>
---
ui/vnc.c | 31 ++++++++++++++++++++++++++++---
1 file changed, 28 insertions(+), 3 deletions(-)
diff --git a/ui/vnc.c b/ui/vnc.c
index daf5b01d34..e6b6a9f4f9 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -1072,6 +1072,7 @@ static void vnc_update_throttle_offset(VncState *vs)
size_t offset =
vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
+#ifdef CONFIG_AUDIO
if (vs->audio_cap) {
int bps;
switch (vs->as.fmt) {
@@ -1091,6 +1092,7 @@ static void vnc_update_throttle_offset(VncState *vs)
}
offset += vs->as.freq * bps * vs->as.nchannels;
}
+#endif
/* Put a floor of 1MB on offset, so that if we have a large pending
* buffer and the display is resized to a small size & back again
@@ -1214,6 +1216,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
return n;
}
+#ifdef CONFIG_AUDIO
/* audio */
static void audio_capture_notify(void *opaque, audcnotification_e cmd)
{
@@ -1293,6 +1296,7 @@ static void audio_del(VncState *vs)
vs->audio_cap = NULL;
}
}
+#endif
static void vnc_disconnect_start(VncState *vs)
{
@@ -1332,7 +1336,9 @@ void vnc_disconnect_finish(VncState *vs)
#ifdef CONFIG_VNC_SASL
vnc_sasl_client_cleanup(vs);
#endif /* CONFIG_VNC_SASL */
+#ifdef CONFIG_AUDIO
audio_del(vs);
+#endif
qkbd_state_lift_all_keys(vs->vd->kbd);
if (vs->mouse_mode_notifier.notify != NULL) {
@@ -2097,6 +2103,7 @@ static void send_ext_key_event_ack(VncState *vs)
vnc_flush(vs);
}
+#ifdef CONFIG_AUDIO
static void send_ext_audio_ack(VncState *vs)
{
vnc_lock_output(vs);
@@ -2110,6 +2117,7 @@ static void send_ext_audio_ack(VncState *vs)
vnc_unlock_output(vs);
vnc_flush(vs);
}
+#endif
static void send_xvp_message(VncState *vs, int code)
{
@@ -2197,10 +2205,15 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
send_ext_key_event_ack(vs);
break;
case VNC_ENCODING_AUDIO:
+#ifdef CONFIG_AUDIO
if (vs->vd->audio_be) {
vnc_set_feature(vs, VNC_FEATURE_AUDIO);
send_ext_audio_ack(vs);
}
+#else
+ VNC_DEBUG("Audio encoding received with audio subsystem "
+ "disabled\n");
+#endif
break;
case VNC_ENCODING_WMVi:
vnc_set_feature(vs, VNC_FEATURE_WMVI);
@@ -2394,7 +2407,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
{
int i;
uint16_t limit;
- uint32_t freq;
+ #ifdef CONFIG_AUDIO
+ uint32_t freq;
+ #endif
VncDisplay *vd = vs->vd;
if (data[0] > 3) {
@@ -2571,7 +2586,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
vnc_client_error(vs);
break;
}
-
+#ifndef CONFIG_AUDIO
+ abort();
+#else
if (len == 2)
return 4;
@@ -2626,7 +2643,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
break;
}
break;
-
+#endif
default:
VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
vnc_client_error(vs);
@@ -3369,10 +3386,12 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
vs->last_x = -1;
vs->last_y = -1;
+#ifdef CONFIG_AUDIO
vs->as.freq = 44100;
vs->as.nchannels = 2;
vs->as.fmt = AUDIO_FORMAT_S16;
vs->as.big_endian = false;
+#endif
qemu_mutex_init(&vs->output_mutex);
vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
@@ -3645,9 +3664,11 @@ static QemuOptsList qemu_vnc_opts = {
},{
.name = "non-adaptive",
.type = QEMU_OPT_BOOL,
+#ifdef CONFIG_AUDIO
},{
.name = "audiodev",
.type = QEMU_OPT_STRING,
+#endif
},{
.name = "power-control",
.type = QEMU_OPT_BOOL,
@@ -4080,7 +4101,9 @@ void vnc_display_open(const char *id, Error **errp)
const char *saslauthz;
int lock_key_sync = 1;
int key_delay_ms;
+#ifdef CONFIG_AUDIO
const char *audiodev;
+#endif
const char *passwordSecret;
if (!vd) {
@@ -4238,6 +4261,7 @@ void vnc_display_open(const char *id, Error **errp)
}
vd->ledstate = 0;
+#ifdef CONFIG_AUDIO
audiodev = qemu_opt_get(opts, "audiodev");
if (audiodev) {
vd->audio_be = audio_be_by_name(audiodev, errp);
@@ -4247,6 +4271,7 @@ void vnc_display_open(const char *id, Error **errp)
} else {
vd->audio_be = audio_get_default_audio_be(NULL);
}
+#endif
device_id = qemu_opt_get(opts, "display");
if (device_id) {
--
2.34.1
Hi
On Mon, Feb 23, 2026 at 9:26 PM Sergei Heifetz <heifetz@yandex-team.com> wrote:
>
> Disable the audio feature in VNC when QEMU is configured with
> `--disable-audio`. Do not compile the corresponding audio-related
> code.
Users will have to call query-command-line-options to figure out if
audio support was disabled. Since this is an optional build option, I
don't know if we treat it as a breaking change.
>
> Signed-off-by: Sergei Heifetz <heifetz@yandex-team.com>
> ---
> ui/vnc.c | 31 ++++++++++++++++++++++++++++---
> 1 file changed, 28 insertions(+), 3 deletions(-)
>
> diff --git a/ui/vnc.c b/ui/vnc.c
> index daf5b01d34..e6b6a9f4f9 100644
> --- a/ui/vnc.c
> +++ b/ui/vnc.c
> @@ -1072,6 +1072,7 @@ static void vnc_update_throttle_offset(VncState *vs)
> size_t offset =
> vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
>
> +#ifdef CONFIG_AUDIO
> if (vs->audio_cap) {
> int bps;
> switch (vs->as.fmt) {
> @@ -1091,6 +1092,7 @@ static void vnc_update_throttle_offset(VncState *vs)
> }
> offset += vs->as.freq * bps * vs->as.nchannels;
> }
> +#endif
>
> /* Put a floor of 1MB on offset, so that if we have a large pending
> * buffer and the display is resized to a small size & back again
> @@ -1214,6 +1216,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
> return n;
> }
>
> +#ifdef CONFIG_AUDIO
> /* audio */
> static void audio_capture_notify(void *opaque, audcnotification_e cmd)
> {
> @@ -1293,6 +1296,7 @@ static void audio_del(VncState *vs)
> vs->audio_cap = NULL;
> }
> }
> +#endif
>
> static void vnc_disconnect_start(VncState *vs)
> {
> @@ -1332,7 +1336,9 @@ void vnc_disconnect_finish(VncState *vs)
> #ifdef CONFIG_VNC_SASL
> vnc_sasl_client_cleanup(vs);
> #endif /* CONFIG_VNC_SASL */
> +#ifdef CONFIG_AUDIO
> audio_del(vs);
> +#endif
> qkbd_state_lift_all_keys(vs->vd->kbd);
>
> if (vs->mouse_mode_notifier.notify != NULL) {
> @@ -2097,6 +2103,7 @@ static void send_ext_key_event_ack(VncState *vs)
> vnc_flush(vs);
> }
>
> +#ifdef CONFIG_AUDIO
> static void send_ext_audio_ack(VncState *vs)
> {
> vnc_lock_output(vs);
> @@ -2110,6 +2117,7 @@ static void send_ext_audio_ack(VncState *vs)
> vnc_unlock_output(vs);
> vnc_flush(vs);
> }
> +#endif
>
> static void send_xvp_message(VncState *vs, int code)
> {
> @@ -2197,10 +2205,15 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
> send_ext_key_event_ack(vs);
> break;
> case VNC_ENCODING_AUDIO:
> +#ifdef CONFIG_AUDIO
> if (vs->vd->audio_be) {
> vnc_set_feature(vs, VNC_FEATURE_AUDIO);
> send_ext_audio_ack(vs);
> }
> +#else
> + VNC_DEBUG("Audio encoding received with audio subsystem "
> + "disabled\n");
> +#endif
> break;
> case VNC_ENCODING_WMVi:
> vnc_set_feature(vs, VNC_FEATURE_WMVI);
> @@ -2394,7 +2407,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
> {
> int i;
> uint16_t limit;
> - uint32_t freq;
> + #ifdef CONFIG_AUDIO
> + uint32_t freq;
> + #endif
Indentation is off
> VncDisplay *vd = vs->vd;
>
> if (data[0] > 3) {
> @@ -2571,7 +2586,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
> vnc_client_error(vs);
> break;
> }
> -
> +#ifndef CONFIG_AUDIO
> + abort();
I'd keep the if AUDIO case first, for consistency
> +#else
> if (len == 2)
> return 4;
>
> @@ -2626,7 +2643,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
> break;
> }
> break;
> -
> +#endif
> default:
> VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
> vnc_client_error(vs);
> @@ -3369,10 +3386,12 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
> vs->last_x = -1;
> vs->last_y = -1;
>
> +#ifdef CONFIG_AUDIO
> vs->as.freq = 44100;
> vs->as.nchannels = 2;
> vs->as.fmt = AUDIO_FORMAT_S16;
> vs->as.big_endian = false;
> +#endif
>
> qemu_mutex_init(&vs->output_mutex);
> vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
> @@ -3645,9 +3664,11 @@ static QemuOptsList qemu_vnc_opts = {
> },{
> .name = "non-adaptive",
> .type = QEMU_OPT_BOOL,
> +#ifdef CONFIG_AUDIO
> },{
> .name = "audiodev",
> .type = QEMU_OPT_STRING,
> +#endif
> },{
> .name = "power-control",
> .type = QEMU_OPT_BOOL,
> @@ -4080,7 +4101,9 @@ void vnc_display_open(const char *id, Error **errp)
> const char *saslauthz;
> int lock_key_sync = 1;
> int key_delay_ms;
> +#ifdef CONFIG_AUDIO
> const char *audiodev;
> +#endif
> const char *passwordSecret;
>
> if (!vd) {
> @@ -4238,6 +4261,7 @@ void vnc_display_open(const char *id, Error **errp)
> }
> vd->ledstate = 0;
>
> +#ifdef CONFIG_AUDIO
> audiodev = qemu_opt_get(opts, "audiodev");
> if (audiodev) {
> vd->audio_be = audio_be_by_name(audiodev, errp);
> @@ -4247,6 +4271,7 @@ void vnc_display_open(const char *id, Error **errp)
> } else {
> vd->audio_be = audio_get_default_audio_be(NULL);
> }
> +#endif
>
> device_id = qemu_opt_get(opts, "display");
> if (device_id) {
> --
> 2.34.1
>
>
--
Marc-André Lureau
Hi, Marc-André.
On 2/24/26 16:05, Marc-André Lureau wrote:
> Hi
>
> On Mon, Feb 23, 2026 at 9:26 PM Sergei Heifetz<heifetz@yandex-team.com> wrote:
>> Disable the audio feature in VNC when QEMU is configured with
>> `--disable-audio`. Do not compile the corresponding audio-related
>> code.
> Users will have to call query-command-line-options to figure out if
> audio support was disabled. Since this is an optional build option, I
> don't know if we treat it as a breaking change.
1. I don’t think it makes sense to treat this as a breaking change,
because it’s impossible that anything would break after an update unless
QEMU has been deliberately reconfigured with `--disable-audio`. If it
has, then the consequences are expected.
2. More importantly, I think it should be assumed that a client can
handle the situation where QEMU does not support the audio feature.
See this excerpt from Section 6 of the RFB protocol specification (RFC
6143):
"A server that does not support the extension will simply ignore the
pseudo-encoding. Note that this means the client must assume that the
server does not support the extension until it gets some
extension-specific confirmation from the server."
In QEMU, use of the audio feature is requested by the client by adding
VNC_ENCODING_AUDIO to a SetEncodings message. For confirmation, there is
a send_ext_audio_ack function that sends a FrameBufferUpdate message to
the client with VNC_ENCODING_AUDIO. The VNCDoTool (0.8.0) documentation,
for example, explicitly says that this is used as confirmation for the
relevant custom feature (see 1.7.4.12, "QEMU Client Message"):
"This message may only be sent if the client has previously received a
FrameBufferUpdate that confirms support for the intended submessage-type
[...]" (where the submessage type is "Audio" in our case).
With this patch, the audio feature is disabled in a way that makes a
QEMU server behave like a non-QEMU server (as far as audio is
concerned). The client sends the audio pseudo-encoding, but the server
does not respond with confirmation, because send_ext_audio_ack is now
behind an #ifdef. A well-behaved client should handle this accordingly
and continue using VNC without sending any audio-related messages to the
server. If a client misbehaves and sends VNC_MSG_CLIENT_QEMU_AUDIO while
the audio feature is disabled, the server will close the connection with
an error.
Also note that the behaviour described above is already present when
there is no audiodev (`vs->vd->audio_be` is NULL).
>> Signed-off-by: Sergei Heifetz<heifetz@yandex-team.com>
>> ---
>> ui/vnc.c | 31 ++++++++++++++++++++++++++++---
>> 1 file changed, 28 insertions(+), 3 deletions(-)
>>
>> diff --git a/ui/vnc.c b/ui/vnc.c
>> index daf5b01d34..e6b6a9f4f9 100644
>> --- a/ui/vnc.c
>> +++ b/ui/vnc.c
>> @@ -1072,6 +1072,7 @@ static void vnc_update_throttle_offset(VncState *vs)
>> size_t offset =
>> vs->client_width * vs->client_height * vs->client_pf.bytes_per_pixel;
>>
>> +#ifdef CONFIG_AUDIO
>> if (vs->audio_cap) {
>> int bps;
>> switch (vs->as.fmt) {
>> @@ -1091,6 +1092,7 @@ static void vnc_update_throttle_offset(VncState *vs)
>> }
>> offset += vs->as.freq * bps * vs->as.nchannels;
>> }
>> +#endif
>>
>> /* Put a floor of 1MB on offset, so that if we have a large pending
>> * buffer and the display is resized to a small size & back again
>> @@ -1214,6 +1216,7 @@ static int vnc_update_client(VncState *vs, int has_dirty)
>> return n;
>> }
>>
>> +#ifdef CONFIG_AUDIO
>> /* audio */
>> static void audio_capture_notify(void *opaque, audcnotification_e cmd)
>> {
>> @@ -1293,6 +1296,7 @@ static void audio_del(VncState *vs)
>> vs->audio_cap = NULL;
>> }
>> }
>> +#endif
>>
>> static void vnc_disconnect_start(VncState *vs)
>> {
>> @@ -1332,7 +1336,9 @@ void vnc_disconnect_finish(VncState *vs)
>> #ifdef CONFIG_VNC_SASL
>> vnc_sasl_client_cleanup(vs);
>> #endif /* CONFIG_VNC_SASL */
>> +#ifdef CONFIG_AUDIO
>> audio_del(vs);
>> +#endif
>> qkbd_state_lift_all_keys(vs->vd->kbd);
>>
>> if (vs->mouse_mode_notifier.notify != NULL) {
>> @@ -2097,6 +2103,7 @@ static void send_ext_key_event_ack(VncState *vs)
>> vnc_flush(vs);
>> }
>>
>> +#ifdef CONFIG_AUDIO
>> static void send_ext_audio_ack(VncState *vs)
>> {
>> vnc_lock_output(vs);
>> @@ -2110,6 +2117,7 @@ static void send_ext_audio_ack(VncState *vs)
>> vnc_unlock_output(vs);
>> vnc_flush(vs);
>> }
>> +#endif
>>
>> static void send_xvp_message(VncState *vs, int code)
>> {
>> @@ -2197,10 +2205,15 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
>> send_ext_key_event_ack(vs);
>> break;
>> case VNC_ENCODING_AUDIO:
>> +#ifdef CONFIG_AUDIO
>> if (vs->vd->audio_be) {
>> vnc_set_feature(vs, VNC_FEATURE_AUDIO);
>> send_ext_audio_ack(vs);
>> }
>> +#else
>> + VNC_DEBUG("Audio encoding received with audio subsystem "
>> + "disabled\n");
>> +#endif
>> break;
>> case VNC_ENCODING_WMVi:
>> vnc_set_feature(vs, VNC_FEATURE_WMVI);
>> @@ -2394,7 +2407,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
>> {
>> int i;
>> uint16_t limit;
>> - uint32_t freq;
>> + #ifdef CONFIG_AUDIO
>> + uint32_t freq;
>> + #endif
> Indentation is off
Oh. I'll fix it.
>> VncDisplay *vd = vs->vd;
>>
>> if (data[0] > 3) {
>> @@ -2571,7 +2586,9 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
>> vnc_client_error(vs);
>> break;
>> }
>> -
>> +#ifndef CONFIG_AUDIO
>> + abort();
> I'd keep the if AUDIO case first, for consistency
Sure.
>> +#else
>> if (len == 2)
>> return 4;
>>
>> @@ -2626,7 +2643,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
>> break;
>> }
>> break;
>> -
>> +#endif
>> default:
>> VNC_DEBUG("Msg: %d\n", read_u16(data, 0));
>> vnc_client_error(vs);
>> @@ -3369,10 +3386,12 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc,
>> vs->last_x = -1;
>> vs->last_y = -1;
>>
>> +#ifdef CONFIG_AUDIO
>> vs->as.freq = 44100;
>> vs->as.nchannels = 2;
>> vs->as.fmt = AUDIO_FORMAT_S16;
>> vs->as.big_endian = false;
>> +#endif
>>
>> qemu_mutex_init(&vs->output_mutex);
>> vs->bh = qemu_bh_new(vnc_jobs_bh, vs);
>> @@ -3645,9 +3664,11 @@ static QemuOptsList qemu_vnc_opts = {
>> },{
>> .name = "non-adaptive",
>> .type = QEMU_OPT_BOOL,
>> +#ifdef CONFIG_AUDIO
>> },{
>> .name = "audiodev",
>> .type = QEMU_OPT_STRING,
>> +#endif
>> },{
>> .name = "power-control",
>> .type = QEMU_OPT_BOOL,
>> @@ -4080,7 +4101,9 @@ void vnc_display_open(const char *id, Error **errp)
>> const char *saslauthz;
>> int lock_key_sync = 1;
>> int key_delay_ms;
>> +#ifdef CONFIG_AUDIO
>> const char *audiodev;
>> +#endif
>> const char *passwordSecret;
>>
>> if (!vd) {
>> @@ -4238,6 +4261,7 @@ void vnc_display_open(const char *id, Error **errp)
>> }
>> vd->ledstate = 0;
>>
>> +#ifdef CONFIG_AUDIO
>> audiodev = qemu_opt_get(opts, "audiodev");
>> if (audiodev) {
>> vd->audio_be = audio_be_by_name(audiodev, errp);
>> @@ -4247,6 +4271,7 @@ void vnc_display_open(const char *id, Error **errp)
>> } else {
>> vd->audio_be = audio_get_default_audio_be(NULL);
>> }
>> +#endif
>>
>> device_id = qemu_opt_get(opts, "display");
>> if (device_id) {
>> --
>> 2.34.1
>>
>>
> --
> Marc-André Lureau
© 2016 - 2026 Red Hat, Inc.