rdpsnd: Enhance server implementation
The current server sided channel handling of RDPSND/AUDIO_PLAYBACK_DVC is currently very constrained. So, solve this. This means: - Add the missing Training/Training Confirm PDUs - Stop overriding the average bytes per second values, when submitting the audio formats, as this currently makes the usage of codecs impossible - Add a way to send the server formats manually again, to be able to restart the protocol after a Close PDU was sent - Add a way to send already encoded audio data to let server implementations to take care of the encoding process and to set custom audio timestamps for the Video Optimized Remoting channel - Add public attributes to let server implementations know the initial volume and pitch values - Add public attribute to let server implementations know the quality mode setting
This commit is contained in:
parent
e1610a7524
commit
902727df5e
@ -36,12 +36,13 @@
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* Send Server Audio Formats and Version PDU (2.2.2.1)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
static UINT rdpsnd_server_send_formats(RdpsndServerContext* context)
|
||||
{
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
size_t pos;
|
||||
UINT16 i;
|
||||
BOOL status = FALSE;
|
||||
@ -61,9 +62,6 @@ static UINT rdpsnd_server_send_formats(RdpsndServerContext* context, wStream* s)
|
||||
for (i = 0; i < context->num_server_formats; i++)
|
||||
{
|
||||
AUDIO_FORMAT format = context->server_formats[i];
|
||||
// TODO: Eliminate this!!!
|
||||
format.nAvgBytesPerSec =
|
||||
format.nSamplesPerSec * format.nChannels * format.wBitsPerSample / 8;
|
||||
|
||||
if (!audio_format_write(s, &format))
|
||||
goto fail;
|
||||
@ -81,7 +79,7 @@ fail:
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* Read Wave Confirm PDU (2.2.3.8) and handle callback
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
@ -106,32 +104,58 @@ static UINT rdpsnd_server_recv_waveconfirm(RdpsndServerContext* context, wStream
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* Read Training Confirm PDU (2.2.3.2) and handle callback
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_recv_trainingconfirm(RdpsndServerContext* context, wStream* s)
|
||||
{
|
||||
UINT16 timestamp;
|
||||
UINT16 packsize;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, timestamp);
|
||||
Stream_Read_UINT16(s, packsize);
|
||||
|
||||
IFCALLRET(context->TrainingConfirm, error, context, timestamp, packsize);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "context->TrainingConfirm failed with error %" PRIu32 "", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read Quality Mode PDU (2.2.2.3)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_recv_quality_mode(RdpsndServerContext* context, wStream* s)
|
||||
{
|
||||
UINT16 quality;
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
{
|
||||
WLog_ERR(TAG, "not enough data in stream!");
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Read_UINT16(s, context->qualityMode); /* wQualityMode */
|
||||
Stream_Seek_UINT16(s); /* Reserved */
|
||||
|
||||
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", context->qualityMode);
|
||||
|
||||
Stream_Read_UINT16(s, quality);
|
||||
Stream_Seek_UINT16(s); // reserved
|
||||
WLog_DBG(TAG, "Client requested sound quality: 0x%04" PRIX16 "", quality);
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
* Read Client Audio Formats and Version PDU (2.2.2.2)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
||||
{
|
||||
UINT16 i, num_known_format = 0;
|
||||
UINT32 flags, vol, pitch;
|
||||
UINT16 udpPort;
|
||||
BYTE lastblock;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
@ -139,9 +163,9 @@ static UINT rdpsnd_server_recv_formats(RdpsndServerContext* context, wStream* s)
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 20))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, flags); /* dwFlags */
|
||||
Stream_Read_UINT32(s, vol); /* dwVolume */
|
||||
Stream_Read_UINT32(s, pitch); /* dwPitch */
|
||||
Stream_Read_UINT32(s, context->capsFlags); /* dwFlags */
|
||||
Stream_Read_UINT32(s, context->initialVolume); /* dwVolume */
|
||||
Stream_Read_UINT32(s, context->initialPitch); /* dwPitch */
|
||||
Stream_Read_UINT16(s, udpPort); /* wDGramPort */
|
||||
Stream_Read_UINT16(s, context->num_client_formats); /* wNumberOfFormats */
|
||||
Stream_Read_UINT8(s, lastblock); /* cLastBlockConfirmed */
|
||||
@ -359,6 +383,51 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send Training PDU (2.2.3.1)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_training(RdpsndServerContext* context, UINT16 timestamp, UINT16 packsize,
|
||||
BYTE* data)
|
||||
{
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
size_t end = 0;
|
||||
ULONG written;
|
||||
BOOL status;
|
||||
|
||||
if (!Stream_EnsureRemainingCapacity(s, 8))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
Stream_Write_UINT8(s, SNDC_TRAINING);
|
||||
Stream_Write_UINT8(s, 0);
|
||||
Stream_Seek_UINT16(s);
|
||||
Stream_Write_UINT16(s, timestamp);
|
||||
Stream_Write_UINT16(s, packsize);
|
||||
|
||||
if (packsize > 0)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, packsize))
|
||||
{
|
||||
Stream_SetPosition(s, 0);
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
Stream_Write(s, data, packsize);
|
||||
}
|
||||
|
||||
end = Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, 2);
|
||||
Stream_Write_UINT16(s, end - 4);
|
||||
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
||||
&written);
|
||||
|
||||
Stream_SetPosition(s, 0);
|
||||
|
||||
return status ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_server_align_wave_pdu(wStream* s, UINT32 alignment)
|
||||
{
|
||||
size_t size;
|
||||
@ -395,9 +464,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
format = &context->client_formats[context->selected_client_format];
|
||||
/* WaveInfo PDU */
|
||||
Stream_SetPosition(s, 0);
|
||||
@ -424,7 +490,6 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
||||
Stream_SetPosition(s, 2);
|
||||
Stream_Write_UINT16(s, end - start + 8);
|
||||
Stream_SetPosition(s, end);
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
|
||||
if (!WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s),
|
||||
start + 4, &written))
|
||||
@ -452,6 +517,8 @@ static UINT rdpsnd_server_send_wave_pdu(RdpsndServerContext* context, UINT16 wTi
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
|
||||
out:
|
||||
Stream_SetPosition(s, 0);
|
||||
context->priv->out_pending_frames = 0;
|
||||
@ -464,59 +531,80 @@ out:
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
|
||||
static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 formatNo,
|
||||
const BYTE* data, size_t size, BOOL encoded,
|
||||
UINT16 timestamp, UINT32 audioTimeStamp)
|
||||
{
|
||||
size_t length;
|
||||
size_t end = 0;
|
||||
const BYTE* src;
|
||||
AUDIO_FORMAT* format;
|
||||
ULONG written;
|
||||
wStream* s = context->priv->rdpsnd_pdu;
|
||||
size_t end = 0;
|
||||
ULONG written;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
BOOL status;
|
||||
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
format = &context->client_formats[context->selected_client_format];
|
||||
/* WaveInfo PDU */
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT16(s, 0); /* BodySize */
|
||||
Stream_Write_UINT16(s, wTimestamp); /* wTimeStamp */
|
||||
Stream_Write_UINT16(s, context->selected_client_format); /* wFormatNo */
|
||||
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
|
||||
Stream_Seek(s, 3); /* bPad */
|
||||
Stream_Write_UINT32(s, wTimestamp); /* dwAudioTimeStamp */
|
||||
src = context->priv->out_buffer;
|
||||
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
||||
|
||||
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, src, length, s))
|
||||
if (!Stream_EnsureRemainingCapacity(s, 16))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Wave2 PDU */
|
||||
Stream_SetPosition(s, 0);
|
||||
Stream_Write_UINT8(s, SNDC_WAVE2); /* msgType */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT16(s, 0); /* BodySize */
|
||||
Stream_Write_UINT16(s, timestamp); /* wTimeStamp */
|
||||
Stream_Write_UINT16(s, formatNo); /* wFormatNo */
|
||||
Stream_Write_UINT8(s, context->block_no); /* cBlockNo */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT8(s, 0); /* bPad */
|
||||
Stream_Write_UINT32(s, audioTimeStamp); /* dwAudioTimeStamp */
|
||||
|
||||
if (encoded)
|
||||
{
|
||||
if (!Stream_EnsureRemainingCapacity(s, size))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Stream_Write(s, data, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOL rc;
|
||||
AUDIO_FORMAT* format;
|
||||
|
||||
/* Set stream size */
|
||||
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
end = Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, 2);
|
||||
Stream_Write_UINT16(s, end - 4);
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
rc = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
||||
&written);
|
||||
|
||||
if (!rc || (end != written))
|
||||
if (!freerdp_dsp_encode(context->priv->dsp_context, context->src_format, data, size, s))
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
|
||||
end, written);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
format = &context->client_formats[formatNo];
|
||||
if (!rdpsnd_server_align_wave_pdu(s, format->nBlockAlign))
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
end = Stream_GetPosition(s);
|
||||
Stream_SetPosition(s, 2);
|
||||
Stream_Write_UINT16(s, end - 4);
|
||||
|
||||
status = WTSVirtualChannelWrite(context->priv->ChannelHandle, (PCHAR)Stream_Buffer(s), end,
|
||||
&written);
|
||||
|
||||
if (!status || (end != written))
|
||||
{
|
||||
WLog_ERR(TAG, "WTSVirtualChannelWrite failed! [stream length=%" PRIdz " - written=%" PRIu32,
|
||||
end, written);
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
}
|
||||
|
||||
context->block_no = (context->block_no + 1) % 256;
|
||||
|
||||
out:
|
||||
Stream_SetPosition(s, 0);
|
||||
context->priv->out_pending_frames = 0;
|
||||
return error;
|
||||
@ -525,8 +613,18 @@ static UINT rdpsnd_server_send_wave2_pdu(RdpsndServerContext* context, UINT16 wT
|
||||
/* Wrapper function to send WAVE or WAVE2 PDU depending on client connected */
|
||||
static UINT rdpsnd_server_send_audio_pdu(RdpsndServerContext* context, UINT16 wTimestamp)
|
||||
{
|
||||
const BYTE* src;
|
||||
size_t length;
|
||||
|
||||
if (context->selected_client_format >= context->num_client_formats)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
src = context->priv->out_buffer;
|
||||
length = context->priv->out_pending_frames * context->priv->src_bytes_per_frame;
|
||||
|
||||
if (context->clientVersion >= CHANNEL_VERSION_WIN_8)
|
||||
return rdpsnd_server_send_wave2_pdu(context, wTimestamp);
|
||||
return rdpsnd_server_send_wave2_pdu(context, context->selected_client_format, src, length,
|
||||
FALSE, wTimestamp, wTimestamp);
|
||||
else
|
||||
return rdpsnd_server_send_wave_pdu(context, wTimestamp);
|
||||
}
|
||||
@ -577,6 +675,30 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send encoded audio samples using a Wave2 PDU.
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpsnd_server_send_samples2(RdpsndServerContext* context, UINT16 formatNo,
|
||||
const void* buf, size_t size, UINT16 timestamp,
|
||||
UINT32 audioTimeStamp)
|
||||
{
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
if (context->clientVersion < CHANNEL_VERSION_WIN_8)
|
||||
return ERROR_INTERNAL_ERROR;
|
||||
|
||||
EnterCriticalSection(&context->priv->lock);
|
||||
|
||||
error =
|
||||
rdpsnd_server_send_wave2_pdu(context, formatNo, buf, size, TRUE, timestamp, audioTimeStamp);
|
||||
|
||||
LeaveCriticalSection(&context->priv->lock);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
@ -738,7 +860,7 @@ static UINT rdpsnd_server_start(RdpsndServerContext* context)
|
||||
goto out_pdu;
|
||||
}
|
||||
|
||||
if ((error = rdpsnd_server_send_formats(context, context->priv->rdpsnd_pdu)))
|
||||
if ((error = rdpsnd_server_send_formats(context)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpsnd_server_send_formats failed with error %" PRIu32 "", error);
|
||||
goto out_lock;
|
||||
@ -844,8 +966,11 @@ RdpsndServerContext* rdpsnd_server_context_new(HANDLE vcm)
|
||||
context->Stop = rdpsnd_server_stop;
|
||||
context->selected_client_format = 0xFFFF;
|
||||
context->Initialize = rdpsnd_server_initialize;
|
||||
context->SendFormats = rdpsnd_server_send_formats;
|
||||
context->SelectFormat = rdpsnd_server_select_format;
|
||||
context->Training = rdpsnd_server_training;
|
||||
context->SendSamples = rdpsnd_server_send_samples;
|
||||
context->SendSamples2 = rdpsnd_server_send_samples2;
|
||||
context->SetVolume = rdpsnd_server_set_volume;
|
||||
context->Close = rdpsnd_server_close;
|
||||
context->priv = priv = (RdpsndServerPrivate*)calloc(1, sizeof(RdpsndServerPrivate));
|
||||
@ -992,6 +1117,10 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
||||
ret = rdpsnd_server_recv_waveconfirm(context, s);
|
||||
break;
|
||||
|
||||
case SNDC_TRAINING:
|
||||
ret = rdpsnd_server_recv_trainingconfirm(context, s);
|
||||
break;
|
||||
|
||||
case SNDC_FORMATS:
|
||||
ret = rdpsnd_server_recv_formats(context, s);
|
||||
|
||||
@ -1002,8 +1131,6 @@ UINT rdpsnd_server_handle_messages(RdpsndServerContext* context)
|
||||
|
||||
case SNDC_QUALITYMODE:
|
||||
ret = rdpsnd_server_recv_quality_mode(context, s);
|
||||
Stream_SetPosition(s,
|
||||
0); /* in case the Activated callback tries to treat some messages */
|
||||
|
||||
if ((ret == CHANNEL_RC_OK) && (context->clientVersion >= CHANNEL_VERSION_WIN_7))
|
||||
IFCALL(context->Activated, context);
|
||||
|
@ -42,23 +42,23 @@ struct AUDIO_FORMAT
|
||||
};
|
||||
typedef struct AUDIO_FORMAT AUDIO_FORMAT;
|
||||
|
||||
#define SNDC_CLOSE 1
|
||||
#define SNDC_WAVE 2
|
||||
#define SNDC_SETVOLUME 3
|
||||
#define SNDC_SETPITCH 4
|
||||
#define SNDC_WAVECONFIRM 5
|
||||
#define SNDC_TRAINING 6
|
||||
#define SNDC_FORMATS 7
|
||||
#define SNDC_CRYPTKEY 8
|
||||
#define SNDC_WAVEENCRYPT 9
|
||||
#define SNDC_UDPWAVE 10
|
||||
#define SNDC_UDPWAVELAST 11
|
||||
#define SNDC_QUALITYMODE 12
|
||||
#define SNDC_WAVE2 13
|
||||
#define SNDC_CLOSE 0x01
|
||||
#define SNDC_WAVE 0x02
|
||||
#define SNDC_SETVOLUME 0x03
|
||||
#define SNDC_SETPITCH 0x04
|
||||
#define SNDC_WAVECONFIRM 0x05
|
||||
#define SNDC_TRAINING 0x06
|
||||
#define SNDC_FORMATS 0x07
|
||||
#define SNDC_CRYPTKEY 0x08
|
||||
#define SNDC_WAVEENCRYPT 0x09
|
||||
#define SNDC_UDPWAVE 0x0A
|
||||
#define SNDC_UDPWAVELAST 0x0B
|
||||
#define SNDC_QUALITYMODE 0x0C
|
||||
#define SNDC_WAVE2 0x0D
|
||||
|
||||
#define TSSNDCAPS_ALIVE 1
|
||||
#define TSSNDCAPS_VOLUME 2
|
||||
#define TSSNDCAPS_PITCH 4
|
||||
#define TSSNDCAPS_ALIVE 0x00000001
|
||||
#define TSSNDCAPS_VOLUME 0x00000002
|
||||
#define TSSNDCAPS_PITCH 0x00000004
|
||||
|
||||
#define DYNAMIC_QUALITY 0x0000
|
||||
#define MEDIUM_QUALITY 0x0001
|
||||
|
@ -33,10 +33,18 @@ typedef UINT (*psRdpsndStart)(RdpsndServerContext* context);
|
||||
typedef UINT (*psRdpsndStop)(RdpsndServerContext* context);
|
||||
|
||||
typedef UINT (*psRdpsndServerInitialize)(RdpsndServerContext* context, BOOL ownThread);
|
||||
typedef UINT (*psRdpsndServerSendFormats)(RdpsndServerContext* context);
|
||||
typedef UINT (*psRdpsndServerSelectFormat)(RdpsndServerContext* context,
|
||||
UINT16 client_format_index);
|
||||
typedef UINT (*psRdpsndServerTraining)(RdpsndServerContext* context, UINT16 timestamp,
|
||||
UINT16 packsize, BYTE* data);
|
||||
typedef UINT (*psRdpsndServerTrainingConfirm)(RdpsndServerContext* context, UINT16 timestamp,
|
||||
UINT16 packsize);
|
||||
typedef UINT (*psRdpsndServerSendSamples)(RdpsndServerContext* context, const void* buf,
|
||||
size_t nframes, UINT16 wTimestamp);
|
||||
typedef UINT (*psRdpsndServerSendSamples2)(RdpsndServerContext* context, UINT16 formatNo,
|
||||
const void* buf, size_t size, UINT16 timestamp,
|
||||
UINT32 audioTimeStamp);
|
||||
typedef UINT (*psRdpsndServerConfirmBlock)(RdpsndServerContext* context, BYTE confirmBlockNum,
|
||||
UINT16 wtimestamp);
|
||||
typedef UINT (*psRdpsndServerSetVolume)(RdpsndServerContext* context, UINT16 left, UINT16 right);
|
||||
@ -74,6 +82,15 @@ struct s_rdpsnd_server_context
|
||||
UINT16 num_client_formats;
|
||||
UINT16 selected_client_format;
|
||||
|
||||
/* dwFlags in CLIENT_AUDIO_VERSION_AND_FORMATS */
|
||||
UINT32 capsFlags;
|
||||
/* dwVolume in CLIENT_AUDIO_VERSION_AND_FORMATS */
|
||||
UINT32 initialVolume;
|
||||
/* dwPitch in CLIENT_AUDIO_VERSION_AND_FORMATS */
|
||||
UINT32 initialPitch;
|
||||
|
||||
UINT16 qualityMode;
|
||||
|
||||
/* Last sent audio block number. */
|
||||
UINT8 block_no;
|
||||
|
||||
@ -84,6 +101,16 @@ struct s_rdpsnd_server_context
|
||||
* will not be called and the server must not call any API on this context.
|
||||
*/
|
||||
psRdpsndServerInitialize Initialize;
|
||||
/**
|
||||
* Send server formats and version to the client. Automatically sent, when
|
||||
* opening the channel.
|
||||
* Also used to restart the protocol after sending the Close PDU.
|
||||
*/
|
||||
psRdpsndServerSendFormats SendFormats;
|
||||
/**
|
||||
* Send Training PDU.
|
||||
*/
|
||||
psRdpsndServerTraining Training;
|
||||
/**
|
||||
* Choose the audio format to be sent. The index argument is an index into
|
||||
* the client_formats array and must be smaller than num_client_formats.
|
||||
@ -95,9 +122,10 @@ struct s_rdpsnd_server_context
|
||||
*/
|
||||
psRdpsndServerSendSamples SendSamples;
|
||||
/**
|
||||
* Called when block confirm is received from the client
|
||||
* Send encoded audio samples using a Wave2 PDU.
|
||||
* When successful, the block_no member is incremented.
|
||||
*/
|
||||
psRdpsndServerConfirmBlock ConfirmBlock;
|
||||
psRdpsndServerSendSamples2 SendSamples2;
|
||||
/**
|
||||
* Set the volume level of the client. Valid range is between 0 and 0xFFFF.
|
||||
*/
|
||||
@ -115,6 +143,14 @@ struct s_rdpsnd_server_context
|
||||
* synchronization.
|
||||
*/
|
||||
psRdpsndServerActivated Activated;
|
||||
/**
|
||||
* Called when a TrainingConfirm PDU is received from the client.
|
||||
*/
|
||||
psRdpsndServerTrainingConfirm TrainingConfirm;
|
||||
/**
|
||||
* Called when block confirm is received from the client.
|
||||
*/
|
||||
psRdpsndServerConfirmBlock ConfirmBlock;
|
||||
|
||||
/**
|
||||
* MS-RDPEA channel version the client announces
|
||||
|
Loading…
x
Reference in New Issue
Block a user