Skip to content
17 changes: 12 additions & 5 deletions compositor/rdpaudio.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
#include <libweston/libweston.h>
#include <shared/xalloc.h>

#include <freerdp/version.h>

static AUDIO_FORMAT rdp_audio_supported_audio_formats[] = {
{ WAVE_FORMAT_PCM, 2, 44100, 176400, 4, 16, 0, NULL },
};
Expand Down Expand Up @@ -656,8 +658,11 @@ rdp_audio_client_activated(RdpsndServerContext* context)
rdp_audio_debug(priv, "rdp_audio_server_activated: bytesPerFrame:%d, latency:%d\n",
priv->bytesPerFrame, context->latency);

context->SelectFormat(context, format);
context->SetVolume(context, 0x7FFF, 0x7FFF);
if (context->SelectFormat(context, format) != CHANNEL_RC_OK) {
weston_log("RDPAudio - SelectFormat failed; audio output disabled.\n");
return;
}
(void)context->SetVolume(context, 0x7FFF, 0x7FFF);

priv->pulseAudioSinkListenerFd = rdp_audio_setup_listener();
if (priv->pulseAudioSinkListenerFd < 0) {
Expand Down Expand Up @@ -706,7 +711,7 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm)
goto Error_Exit;
}
memcpy(audio_formats, rdp_audio_supported_audio_formats, sizeof rdp_audio_supported_audio_formats);

priv->rdpsnd_server_context->data = (void*)priv;
priv->rdpsnd_server_context->Activated = rdp_audio_client_activated;
priv->rdpsnd_server_context->ConfirmBlock = rdp_audio_client_confirm_block;
Expand All @@ -722,6 +727,8 @@ rdp_audio_out_init(struct weston_compositor *c, HANDLE vcm)
weston_log("RDPAudio - force static channel.\n");
}
}
#else
(void)s;
#endif // HAVE_RDPAUDIO_DYNAMIC_VIRTUAL_CHANNEL

/* Calling Initialize does Start as well */
Expand Down Expand Up @@ -784,8 +791,8 @@ rdp_audio_out_destroy(void *audio_out_private)
assert(priv->pulseAudioSinkFd < 0);
assert(priv->audioBuffer == NULL);

priv->rdpsnd_server_context->Close(priv->rdpsnd_server_context);
priv->rdpsnd_server_context->Stop(priv->rdpsnd_server_context);
(void)priv->rdpsnd_server_context->Close(priv->rdpsnd_server_context);
(void)priv->rdpsnd_server_context->Stop(priv->rdpsnd_server_context);

if (priv->audioSem != -1) {
close(priv->audioSem);
Expand Down
133 changes: 131 additions & 2 deletions compositor/rdpaudioin.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@
#include <libweston/libweston.h>
#include <shared/xalloc.h>

#include <freerdp/version.h>
#include <winpr/stream.h>

static AUDIO_FORMAT rdp_audioin_supported_audio_formats[] = {
{ WAVE_FORMAT_PCM, 1, 44100, 88200, 2, 16, 0, NULL },
};
Expand Down Expand Up @@ -308,6 +311,7 @@ rdp_audioin_setup_listener(struct audio_in_private *priv)
return fd;
}

#if FREERDP_VERSION_MAJOR < 3
static UINT
rdp_audioin_client_opening(audin_server_context* context)
{
Expand Down Expand Up @@ -402,6 +406,112 @@ rdp_audioin_client_receive_samples(

return 0;
}
#else /* FREERDP_VERSION_MAJOR >= 3 */

/*
* In FreeRDP 3.x the audin server API was reworked into a PDU-callback
* model. The default ReceiveFormats handler in libfreerdp-server picks a
* compatible format from the client's list but then calls SendOpen with a
* hardcoded stereo 44.1kHz 16-bit captureFormat (see
* channels/audin/server/audin.c:send_open in FreeRDP). WSLg's pulse audio
* source pipe expects mono PCM, so we override ReceiveFormats to send the
* negotiated format (mono) back to the client in the SNDIN_OPEN PDU.
*/
static UINT
rdp_audioin_client_receive_formats(audin_server_context* context,
const SNDIN_FORMATS* formats)
{
struct audio_in_private *priv = context->userdata;
const AUDIO_FORMAT *server_format = &rdp_audioin_supported_audio_formats[0];
UINT32 matched_index = UINT32_MAX;
SNDIN_OPEN open = { 0 };

rdp_audio_debug(priv, "RDP AudioIn ReceiveFormats: %u client formats.\n",
formats->NumFormats);

for (UINT32 i = 0; i < formats->NumFormats; i++) {
const AUDIO_FORMAT *cf = &formats->SoundFormats[i];

rdp_audio_debug(priv, "\t[%u] - Format(%s) - Bits(%d), Channels(%d), Frequency(%d)\n",
i,
AUDIO_FORMAT_to_String(cf->wFormatTag),
cf->wBitsPerSample,
cf->nChannels,
cf->nSamplesPerSec);

if (cf->wFormatTag == server_format->wFormatTag &&
cf->nChannels == server_format->nChannels &&
cf->nSamplesPerSec == server_format->nSamplesPerSec &&
cf->wBitsPerSample == server_format->wBitsPerSample) {
matched_index = i;
rdp_audio_debug(priv, "RDPAudioIn - Agreed on format %u.\n", i);
break;
}
}

if (matched_index == UINT32_MAX) {
weston_log("RDPAudioIn - No agreeded format.\n");
return ERROR_INVALID_DATA;
}

/*
* 10ms of audio per packet — matches the 2.x frames_per_packet
* setting (nSamplesPerSec / 100).
*/
open.FramesPerPacket = server_format->nSamplesPerSec / 100;
open.initialFormat = matched_index;
open.captureFormat = *server_format;

priv->isAudioInStreamOpened = TRUE;

return context->SendOpen(context, &open);
}

static UINT
rdp_audioin_client_data(audin_server_context* context,
const SNDIN_DATA* data)
{
struct audio_in_private *priv = context->userdata;
const AUDIO_FORMAT *server_format = &rdp_audioin_supported_audio_formats[0];
const BYTE *buffer;
size_t bytes;
ssize_t sent;

if (!priv->isAudioInStreamOpened || priv->pulseAudioSourceFd == -1) {
weston_log("RDPAudioIn - audio stream is not opened.\n");
return CHANNEL_RC_OK;
}

bytes = Stream_Length(data->Data);
if (bytes == 0)
return CHANNEL_RC_OK;

buffer = Stream_Buffer(data->Data);
assert(buffer != NULL);
assert(server_format->wFormatTag == WAVE_FORMAT_PCM);
assert(server_format->nChannels == 1);
assert(server_format->nSamplesPerSec == 44100);
assert(server_format->wBitsPerSample == 16);

sent = send(priv->pulseAudioSourceFd, buffer, bytes, 0);
if (sent != (ssize_t)bytes) {
rdp_audio_debug(priv, "RDP AudioIn source send failed (sent:%zd, bytes:%zu) %s\n",
sent, bytes, strerror(errno));

/* Unblock worker thread to close pipe to pulseaudio */
uint64_t one = 1;
if (write(priv->closeAudioSourceFd, &one, sizeof(one)) != sizeof(uint64_t)) {
weston_log("RDP AudioIn error at Data while writing to closeAudioSourceFd (%s)\n", strerror(errno));
return ERROR_INTERNAL_ERROR;
}

if (sent <= 0)
return ERROR_INTERNAL_ERROR;
}

return CHANNEL_RC_OK;
}
#endif /* FREERDP_VERSION_MAJOR >= 3 */

static void*
rdp_audioin_source_thread(void *context)
Expand Down Expand Up @@ -441,7 +551,7 @@ rdp_audioin_source_thread(void *context)
weston_log("RDP AudioIn wait on eventfd failed. thread exiting. %s\n", strerror(errno));
break;
}
priv->audin_server_context->Close(priv->audin_server_context);
(void)priv->audin_server_context->Close(priv->audin_server_context);
rdp_audio_debug(priv, "RDP AudioIn closed.\n");
} else {
weston_log("Failed to open audio in connection with RDP client.\n");
Expand All @@ -453,7 +563,7 @@ rdp_audioin_source_thread(void *context)
}

if (priv->audin_server_context->IsOpen(priv->audin_server_context))
priv->audin_server_context->Close(priv->audin_server_context);
(void)priv->audin_server_context->Close(priv->audin_server_context);

if (priv->pulseAudioSourceFd != -1) {
close(priv->pulseAudioSourceFd);
Expand Down Expand Up @@ -484,6 +594,24 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm)
priv->pulseAudioSourceFd = -1;
priv->closeAudioSourceFd = -1;

#if FREERDP_VERSION_MAJOR >= 3
priv->audin_server_context->userdata = (void*)priv;
/*
* Override ReceiveFormats to send our negotiated (mono) captureFormat
* in the SNDIN_OPEN PDU instead of the default-handler's hardcoded
* stereo. The default ReceiveVersion / ReceiveFormatChange handlers
* installed by audin_server_context_new are fine to keep.
*/
priv->audin_server_context->ReceiveFormats = rdp_audioin_client_receive_formats;
priv->audin_server_context->Data = rdp_audioin_client_data;

if (!audin_server_set_formats(priv->audin_server_context,
ARRAYSIZE(rdp_audioin_supported_audio_formats),
rdp_audioin_supported_audio_formats)) {
weston_log("RDPAudioIn - audin_server_set_formats failed.\n");
goto Error_Exit;
}
#else
// this will be freed by FreeRDP at audin_server_context_free.
AUDIO_FORMAT *audio_formats = malloc(sizeof rdp_audioin_supported_audio_formats);
if (!audio_formats) {
Expand All @@ -500,6 +628,7 @@ rdp_audio_in_init(struct weston_compositor *c, HANDLE vcm)
priv->audin_server_context->server_formats = audio_formats;
priv->audin_server_context->dst_format = &rdp_audioin_supported_audio_formats[0];
priv->audin_server_context->frames_per_packet = rdp_audioin_supported_audio_formats[0].nSamplesPerSec / 100; // 10ms per packet
#endif /* FREERDP_VERSION_MAJOR >= 3 */

priv->closeAudioSourceFd = eventfd(0, EFD_CLOEXEC);
if (priv->closeAudioSourceFd < 0) {
Expand Down
Loading