GUACAMOLE-25: Handle the server's Sound Formats PDU.

This commit is contained in:
Michael Jumper 2016-04-17 02:11:29 -07:00
parent 503ffb0d45
commit 86806a3759
2 changed files with 204 additions and 2 deletions

View File

@ -35,6 +35,126 @@
#include "compat/winpr-stream.h" #include "compat/winpr-stream.h"
#endif #endif
/**
* Reads AUDIO_FORMAT data from the given stream into the given struct.
*
* @param stream
* The stream to read AUDIO_FORMAT data from.
*
* @param format
* The structure to populate with data from the stream.
*/
static void guac_rdp_ai_read_format(wStream* stream,
guac_rdp_ai_format* format) {
/* Read audio format into structure */
Stream_Read_UINT16(stream, format->tag); /* wFormatTag */
Stream_Read_UINT16(stream, format->channels); /* nChannels */
Stream_Read_UINT32(stream, format->rate); /* nSamplesPerSec */
Stream_Read_UINT32(stream, format->bytes_per_sec); /* nAvgBytesPerSec */
Stream_Read_UINT16(stream, format->block_align); /* nBlockAlign */
Stream_Read_UINT16(stream, format->bps); /* wBitsPerSample */
Stream_Read_UINT16(stream, format->data_size); /* cbSize */
/* Read arbitrary data block (if applicable) */
if (format->data_size != 0) {
format->data = Stream_Pointer(stream); /* data */
Stream_Seek(stream, format->data_size);
}
}
/**
* Writes AUDIO_FORMAT data to the given stream from the given struct.
*
* @param stream
* The stream to write AUDIO_FORMAT data to.
*
* @param format
* The structure containing the data that should be written to the stream.
*/
static void guac_rdp_ai_write_format(wStream* stream,
guac_rdp_ai_format* format) {
/* Write audio format into structure */
Stream_Write_UINT16(stream, format->tag); /* wFormatTag */
Stream_Write_UINT16(stream, format->channels); /* nChannels */
Stream_Write_UINT32(stream, format->rate); /* nSamplesPerSec */
Stream_Write_UINT32(stream, format->bytes_per_sec); /* nAvgBytesPerSec */
Stream_Write_UINT16(stream, format->block_align); /* nBlockAlign */
Stream_Write_UINT16(stream, format->bps); /* wBitsPerSample */
Stream_Write_UINT16(stream, format->data_size); /* cbSize */
/* Write arbitrary data block (if applicable) */
if (format->data_size != 0)
Stream_Write(stream, format->data, format->data_size);
}
/**
* Sends a Data Incoming PDU along the given channel. A Data Incoming PDU is
* used by the client to indicate to the server that format or audio data is
* about to be sent.
*
* @param channel
* The channel along which the PDU should be sent.
*/
static void guac_rdp_ai_send_incoming_data(IWTSVirtualChannel* channel) {
/* Build response version PDU */
wStream* response = Stream_New(NULL, 1);
Stream_Write_UINT8(response, GUAC_RDP_MSG_SNDIN_DATA_INCOMING); /* MessageId */
/* Send response */
channel->Write(channel, (UINT32) Stream_GetPosition(response),
Stream_Buffer(response), NULL);
Stream_Free(response, TRUE);
}
/**
* Sends a Sound Formats PDU along the given channel. A Sound Formats PDU is
* used by the client to indicate to the server which formats of audio it
* supports (in response to the server sending exactly the same type of PDU).
* This PDU MUST be preceded by the Data Incoming PDU.
*
* @param channel
* The channel along which the PDU should be sent.
*
* @param formats
* An array of all supported formats.
*
* @param num_formats
* The number of entries in the formats array.
*/
static void guac_rdp_ai_send_formats(IWTSVirtualChannel* channel,
guac_rdp_ai_format* formats, int num_formats) {
int index;
int packet_size = 9;
/* Calculate packet size */
for (index = 0; index < num_formats; index++)
packet_size += 18 + formats[index].data_size;
wStream* stream = Stream_New(NULL, packet_size);
/* Write header */
Stream_Write_UINT8(stream, GUAC_RDP_MSG_SNDIN_FORMATS); /* MessageId */
Stream_Write_UINT32(stream, num_formats); /* NumFormats */
Stream_Write_UINT32(stream, packet_size); /* cbSizeFormatsPacket */
/* Write all formats */
for (index = 0; index < num_formats; index++)
guac_rdp_ai_write_format(stream, &(formats[index]));
/* Send PDU */
channel->Write(channel, (UINT32) Stream_GetPosition(stream),
Stream_Buffer(stream), NULL);
Stream_Free(stream, TRUE);
}
void guac_rdp_ai_process_version(guac_client* client, void guac_rdp_ai_process_version(guac_client* client,
IWTSVirtualChannel* channel, wStream* stream) { IWTSVirtualChannel* channel, wStream* stream) {
@ -61,8 +181,31 @@ void guac_rdp_ai_process_version(guac_client* client,
void guac_rdp_ai_process_formats(guac_client* client, void guac_rdp_ai_process_formats(guac_client* client,
IWTSVirtualChannel* channel, wStream* stream) { IWTSVirtualChannel* channel, wStream* stream) {
/* STUB */ UINT32 num_formats;
guac_client_log(client, GUAC_LOG_DEBUG, "AUDIO_INPUT: formats"); Stream_Read_UINT32(stream, num_formats); /* NumFormats */
Stream_Seek_UINT32(stream); /* cbSizeFormatsPacket (MUST BE IGNORED) */
UINT32 index;
for (index = 0; index < num_formats; index++) {
guac_rdp_ai_format format;
guac_rdp_ai_read_format(stream, &format);
/* Ignore anything but WAVE_FORMAT_PCM */
if (format.tag != GUAC_RDP_WAVE_FORMAT_PCM)
continue;
/* Accept single format */
guac_rdp_ai_send_incoming_data(channel);
guac_rdp_ai_send_formats(channel, &format, 1);
return;
}
/* No formats available */
guac_client_log(client, GUAC_LOG_WARNING, "AUDIO_INPUT: No WAVE format.");
guac_rdp_ai_send_incoming_data(channel);
guac_rdp_ai_send_formats(channel, NULL, 0);
} }

View File

@ -31,6 +31,12 @@
#include "compat/winpr-stream.h" #include "compat/winpr-stream.h"
#endif #endif
/**
* The format tag associated with raw wave audio (WAVE_FORMAT_PCM). This format
* is required to be supported by all RDP servers.
*/
#define GUAC_RDP_WAVE_FORMAT_PCM 0x01
/** /**
* The message ID associated with the AUDIO_INPUT Version PDU. The Version PDU * The message ID associated with the AUDIO_INPUT Version PDU. The Version PDU
* is sent by both the client and the server to indicate their version of the * is sent by both the client and the server to indicate their version of the
@ -79,6 +85,59 @@
*/ */
#define GUAC_RDP_MSG_SNDIN_FORMATCHANGE 0x07 #define GUAC_RDP_MSG_SNDIN_FORMATCHANGE 0x07
/**
* An AUDIO_INPUT format, analogous to the AUDIO_FORMAT structure defined
* within Microsoft's RDP documentation.
*/
typedef struct guac_rdp_ai_format {
/**
* The "format tag" denoting the overall format of audio data received,
* such as WAVE_FORMAT_PCM.
*/
UINT16 tag;
/**
* The number of audio channels.
*/
UINT16 channels;
/**
* The number of samples per second.
*/
UINT32 rate;
/**
* The average number of bytes required for one second of audio.
*/
UINT32 bytes_per_sec;
/**
* The absolute minimum number of bytes required to process audio in this
* format.
*/
UINT16 block_align;
/**
* The number of bits per sample.
*/
UINT16 bps;
/**
* The size of the arbitrary data block, if any. The meaning of the data
* within the arbitrary data block is determined by the format tag.
* WAVE_FORMAT_PCM audio has no associated arbitrary data.
*/
UINT16 data_size;
/**
* Optional arbitrary data whose meaning is determined by the format tag.
* WAVE_FORMAT_PCM audio has no associated arbitrary data.
*/
BYTE* data;
} guac_rdp_ai_format;
/** /**
* Processes a Version PDU received from the RDP server. The Version PDU is * Processes a Version PDU received from the RDP server. The Version PDU is
* sent by the server to indicate its version of the AUDIO_INPUT channel * sent by the server to indicate its version of the AUDIO_INPUT channel