The RDP specification for the AUDIO_INPUT channel requires that all
audio be sent in packets of a specific size. Guacamole does correctly
limit itself to sending packets of this size to the RDP server, but will
send quite a few of these packets all at once if it has received more
audio data than the RDP packet size. This is OK in principle (the
Guacamole client should be able to send audio in packets of whatever
size it chooses), but may overwhelm the software running within the RDP
server if the amount of data received exceeds the available buffer
space, resulting in dropped samples.
As there is no way to know the size of the remote audio buffer, we need
to instead ensure that audio is streamed as close to real time as
possible, with each audio packet of N bytes not being sent until roughly
the amount of time represented by those N bytes has elapsed since the
last packet. This throttling ensures that software expecting to process
audio in real time should never run out of buffer space.
That said, if we never exceed the per-packet data rate and occasionally
send a packet earlier than real time would dictate, unavoidable latency
in sending/receiving audio data would accumulate over time. For example,
if each audio packet represents 10ms of audio data, but we receive that
audio packet 10.1ms after the previous packet, we need to adjust the
timing of the next audio packet(s) to account for that additional 0.1ms.
Simply waiting 10ms after sending each packet would cause that 0.1ms to
accumulate each time it occurs, eventually resulting in noticable
latency and finally running out of buffer space.
Thus, these changes:
1. Leverage a flush thread and per-packet scheduling to ensure that each
flushed audio packet does not exceed the equivalent real time rate.
2. Calculate the amount of additional latency from the amount of data
received beyond the required packet size, and amortize scheduling
corrections to account for that latency over the next several audio
packets.
This ensures that audio is streamed exactly as it is received if the
audio matches the packet size of the RDP server, and audio that is
received in a different size or varying sizes is buffered and throttled
to keep things within the expectations of software running within the
RDP server.
The buffer and data parameters of the guac_rdp_audio_buffer flush
handler are redundant now that the guac_rdp_audio_buffer is being passed
to the handler. They can instead be referenced as audio_buffer->packet
and audio_buffer->data respectively.
The FreeRDP library is intended to be threadsafe, but is not reliably so
with respect to legacy RDP encryption and outbound messages. When
outbound messages are sent by multiple threads, the encryption key used
for legacy RDP encryption may not be updated correctly, resulting in a
fatal connection error like:
"ERRINFO_DECRYPT_FAILED (0x00001192):(a) Decryption using Standard RDP
Security mechanisms (section 5.3.6) failed. (b) Session key creation
using Standard RDP Security mechanisms (section 5.3.5) failed."
The previous implementation passed `length - str_len` to `snprintf()`,
yet compared the return value to `length`. This is incorrect, as
`length` is not the buffer size provided to `snprintf()`.
When using the fr-be-azerty remote keyboard layout on an RDP connection,
the dash ('-') and underscore ('_') are swapped.
Underscore and dash are located on the same key on a Belgian azerty
layout. Dash should be the normal/unshifted character, and underscore
should be the shifted character. The current mapping has this the other
way around, so let's fix this.
Signed-off-by: Sander Vanheule <Sander.Vanheule@UGent.be>
Older versions of libvncclient did not free all memory within
rfbClientCleanup(), but this has been corrected as of their 0.9.12
release. As guacamole-server may well be built against older versions of
libvncclient, we can't simply remove the manual free() calls, but we
should be sure to set any memory that we free ourselves to NULL so that
rfbClientCleanup() does not attempt to free it again.
The changes introduced by GUACAMOLE-1181 (commit 2c86e20) were made
conditional as older versions of FreeRDP will automatically free the
wStream, resulting in a double-free if we attempt to do so ourselves.
The macro controlling that conditional code is defined within config.h,
which is missing here. Without that macro, the call to Stream_Free()
always occurs, and we get a double-free with older FreeRDP.
On German keyboards, Caps Lock behaves like Shift, affects all
characters except dead keys, "°", "<", ">", "-", "_", and any keys
requiring AltGr, and is undone by Shift.
On Swiss German keyboards, Caps Lock turns all letters uppercase and is
undone by Shift, except for letters which instead rely on Shift to
determine their identity (accented letters).
For example, the key directly to the right of "P" produces "ü" if no
modifiers or locks are active. With Shift pressed, the key changes
identity to "è", with the state of Caps Lock determining the case:
| Shift | Caps | Result |
|-------|-------|--------|
| | | "ü" |
| | X | "Ü" |
| X | | "è" |
| X | X | "È" |
This goes for all accented characters in the Swiss German layout.
On French keyboards, Caps Lock behaves like Shift, affects all
characters including dead keys but excluding "<", ">", "²", and any keys
requiring AltGr, and is undone by Shift.
On Belgian French keyboards, Caps Lock behaves like Shift, affects all
characters including dead keys but excluding "<", ">", "²", "³", and any
keys requiring AltGr, and is undone by Shift.