20#include <freerdp/config.h>
22#include <winpr/assert.h>
29#include <winpr/cast.h>
31#include <winpr/cmdline.h>
32#include <winpr/synch.h>
34#include <freerdp/types.h>
35#include <freerdp/channels/log.h>
37#include <aaudio/AAudio.h>
39#include "rdpsnd_main.h"
42#define AAUDIO_DEFAULT_LATENCY_MS 100
45#define AAUDIO_WRITE_TIMEOUT_NS (10LL * 1000LL * 1000LL)
47#define AAUDIO_MAX_RATE 48000
51 rdpsndDevicePlugin device;
69static BOOL rdpsnd_aaudio_pcm_valid(
const AUDIO_FORMAT* format)
73 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->cbSize == 0) &&
74 (format->wBitsPerSample == 16) && (format->nSamplesPerSec > 0) &&
75 (format->nSamplesPerSec <= AAUDIO_MAX_RATE) &&
76 ((format->nChannels == 1) || (format->nChannels == 2));
79static void rdpsnd_aaudio_close_stream(rdpsndAAudioPlugin* aaudio)
86 aaudio_result_t rc = AAudioStream_requestStop(aaudio->stream);
88 WLog_WARN(TAG,
"AAudioStream_requestStop: %s", AAudio_convertResultToText(rc));
89 rc = AAudioStream_close(aaudio->stream);
91 WLog_WARN(TAG,
"AAudioStream_close: %s", AAudio_convertResultToText(rc));
92 aaudio->stream =
nullptr;
96static BOOL rdpsnd_aaudio_open_stream(rdpsndAAudioPlugin* aaudio)
100 AAudioStreamBuilder* builder =
nullptr;
105 aaudio_result_t rc = AAudio_createStreamBuilder(&builder);
108 WLog_ERR(TAG,
"AAudio_createStreamBuilder: %s", AAudio_convertResultToText(rc));
112 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
113 AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
114 AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_NONE);
115 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
116 AAudioStreamBuilder_setSampleRate(builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->rate));
117 AAudioStreamBuilder_setChannelCount(builder,
118 WINPR_ASSERTING_INT_CAST(int32_t, aaudio->channels));
119#if __ANDROID_API__ >= 28
120 AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_MEDIA);
121 AAudioStreamBuilder_setContentType(builder, AAUDIO_CONTENT_TYPE_MUSIC);
124 const UINT32 targetMs = (aaudio->latency > 0) ? aaudio->latency : AAUDIO_DEFAULT_LATENCY_MS;
125 const UINT64 targetFrames = (UINT64)targetMs * aaudio->rate / 1000;
126 const int32_t targetCapacity =
127 (targetFrames > INT32_MAX) ? INT32_MAX : WINPR_ASSERTING_INT_CAST(int32_t, targetFrames);
128 AAudioStreamBuilder_setBufferCapacityInFrames(builder, targetCapacity);
130 rc = AAudioStreamBuilder_openStream(builder, &aaudio->stream);
131 AAudioStreamBuilder_delete(builder);
135 WLog_ERR(TAG,
"AAudioStreamBuilder_openStream: %s", AAudio_convertResultToText(rc));
136 aaudio->stream =
nullptr;
140 const int32_t actualRate = AAudioStream_getSampleRate(aaudio->stream);
141 const int32_t actualChannels = AAudioStream_getChannelCount(aaudio->stream);
142 const aaudio_format_t actualFormat = AAudioStream_getFormat(aaudio->stream);
147 if ((actualChannels < 0) || ((UINT32)actualChannels != aaudio->channels) ||
148 (actualFormat != AAUDIO_FORMAT_PCM_I16))
150 WLog_ERR(TAG,
"AAudio opened as %dch/fmt%d, need %" PRIu32
"ch/I16", actualChannels,
151 (
int)actualFormat, aaudio->channels);
152 rdpsnd_aaudio_close_stream(aaudio);
156 if ((actualRate < 0) || ((UINT32)actualRate != aaudio->rate))
158 WLog_WARN(TAG,
"AAudio rate adjusted: requested %" PRIu32
"Hz, got %dHz", aaudio->rate,
163 const int32_t burst = AAudioStream_getFramesPerBurst(aaudio->stream);
164 const int32_t capacity = AAudioStream_getBufferCapacityInFrames(aaudio->stream);
165 int32_t target = targetCapacity;
167 if ((burst > 0) && (burst <= INT32_MAX / 2) && (target < burst * 2))
169 if ((capacity > 0) && (target > capacity))
172 const aaudio_result_t size = AAudioStream_setBufferSizeInFrames(aaudio->stream, target);
174 WLog_WARN(TAG,
"AAudioStream_setBufferSizeInFrames(%d): %s", target,
175 AAudio_convertResultToText(size));
177 rc = AAudioStream_requestStart(aaudio->stream);
180 WLog_ERR(TAG,
"AAudioStream_requestStart: %s", AAudio_convertResultToText(rc));
181 rdpsnd_aaudio_close_stream(aaudio);
189static BOOL rdpsnd_aaudio_format_supported(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format)
191 WINPR_ASSERT(device);
192 WINPR_ASSERT(format);
194 return rdpsnd_aaudio_pcm_valid(format);
198static BOOL rdpsnd_aaudio_default_format(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* desired,
201 WINPR_ASSERT(device);
202 WINPR_ASSERT(defaultFormat);
205 *defaultFormat = *desired;
207 defaultFormat->wFormatTag = WAVE_FORMAT_PCM;
208 defaultFormat->wBitsPerSample = 16;
209 defaultFormat->cbSize = 0;
210 defaultFormat->data =
nullptr;
212 if ((defaultFormat->nChannels != 1) && (defaultFormat->nChannels != 2))
213 defaultFormat->nChannels = 2;
214 if ((defaultFormat->nSamplesPerSec == 0) || (defaultFormat->nSamplesPerSec > AAUDIO_MAX_RATE))
215 defaultFormat->nSamplesPerSec = AAUDIO_MAX_RATE;
217 defaultFormat->nBlockAlign = defaultFormat->nChannels * defaultFormat->wBitsPerSample / 8;
218 defaultFormat->nAvgBytesPerSec = defaultFormat->nBlockAlign * defaultFormat->nSamplesPerSec;
223static BOOL rdpsnd_aaudio_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
226 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
227 WINPR_ASSERT(aaudio);
229 if (format && !rdpsnd_aaudio_pcm_valid(format))
231 WLog_ERR(TAG,
"unsupported format %s", audio_format_get_tag_string(format->wFormatTag));
235 EnterCriticalSection(&aaudio->lock);
239 if (aaudio->stream &&
240 ((aaudio->rate != format->nSamplesPerSec) || (aaudio->channels != format->nChannels)))
241 rdpsnd_aaudio_close_stream(aaudio);
243 aaudio->rate = format->nSamplesPerSec;
244 aaudio->channels = format->nChannels;
247 aaudio->latency = latency;
248 const BOOL rc = rdpsnd_aaudio_open_stream(aaudio);
250 LeaveCriticalSection(&aaudio->lock);
254static void rdpsnd_aaudio_close(rdpsndDevicePlugin* device)
256 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
257 WINPR_ASSERT(aaudio);
258 EnterCriticalSection(&aaudio->lock);
259 aaudio->opened = FALSE;
260 rdpsnd_aaudio_close_stream(aaudio);
261 LeaveCriticalSection(&aaudio->lock);
264static void rdpsnd_aaudio_free(rdpsndDevicePlugin* device)
266 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
270 EnterCriticalSection(&aaudio->lock);
271 aaudio->opened = FALSE;
272 rdpsnd_aaudio_close_stream(aaudio);
273 free(aaudio->gainbuf);
274 aaudio->gainbuf =
nullptr;
275 aaudio->gainbuf_len = 0;
276 LeaveCriticalSection(&aaudio->lock);
278 DeleteCriticalSection(&aaudio->lock);
283static UINT32 rdpsnd_aaudio_get_volume(rdpsndDevicePlugin* device)
285 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
286 WINPR_ASSERT(aaudio);
287 EnterCriticalSection(&aaudio->lock);
288 const UINT32 volume = aaudio->volume;
289 LeaveCriticalSection(&aaudio->lock);
294static BOOL rdpsnd_aaudio_set_volume(rdpsndDevicePlugin* device, UINT32 value)
296 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
297 WINPR_ASSERT(aaudio);
298 EnterCriticalSection(&aaudio->lock);
299 aaudio->volume = value;
300 LeaveCriticalSection(&aaudio->lock);
306static const short* rdpsnd_aaudio_apply_volume(rdpsndAAudioPlugin* aaudio,
const short* src,
309 WINPR_ASSERT(aaudio);
312 const UINT32 left = aaudio->volume & 0xFFFF;
313 const UINT32 right = (aaudio->volume >> 16) & 0xFFFF;
315 if ((left == 0xFFFF) && (right == 0xFFFF))
318 if (samples > SIZE_MAX /
sizeof(
short))
321 if (aaudio->gainbuf_len < samples)
323 short* tmp = (
short*)realloc(aaudio->gainbuf, samples *
sizeof(
short));
326 aaudio->gainbuf = tmp;
327 aaudio->gainbuf_len = samples;
330 for (
size_t i = 0; i < samples; i++)
332 const UINT32 gain = ((aaudio->channels == 2) && (i & 1)) ? right : left;
333 aaudio->gainbuf[i] = (short)((int64_t)src[i] * gain / 0xFFFF);
336 return aaudio->gainbuf;
340static UINT rdpsnd_aaudio_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
342 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
343 WINPR_ASSERT(aaudio);
345 if (!data || (size < 2))
348 EnterCriticalSection(&aaudio->lock);
353 LeaveCriticalSection(&aaudio->lock);
359 if (!rdpsnd_aaudio_open_stream(aaudio))
361 LeaveCriticalSection(&aaudio->lock);
366 const size_t bytesPerFrame = aaudio->channels *
sizeof(INT16);
367 if (bytesPerFrame == 0)
369 LeaveCriticalSection(&aaudio->lock);
373 const short* samples =
374 rdpsnd_aaudio_apply_volume(aaudio, (
const short*)data, size /
sizeof(
short));
375 size_t frames = size / bytesPerFrame;
376 const BYTE* pos = (
const BYTE*)samples;
380 const int32_t chunk =
381 (frames > INT32_MAX) ? INT32_MAX : WINPR_ASSERTING_INT_CAST(int32_t, frames);
382 aaudio_result_t written =
383 AAudioStream_write(aaudio->stream, pos, chunk, AAUDIO_WRITE_TIMEOUT_NS);
390 if (written != AAUDIO_ERROR_TIMEOUT)
392 WLog_WARN(TAG,
"AAudio stream unusable (%s), closing",
393 AAudio_convertResultToText(written));
394 rdpsnd_aaudio_close_stream(aaudio);
398 WLog_ERR(TAG,
"AAudioStream_write: %s", AAudio_convertResultToText(written));
401 LeaveCriticalSection(&aaudio->lock);
405 if ((written == 0) || (written > chunk))
408 pos += (size_t)written * bytesPerFrame;
409 frames -= (size_t)written;
412 if (aaudio->rate == 0)
414 LeaveCriticalSection(&aaudio->lock);
419 int64_t framePosition = 0;
420 int64_t timeNanoseconds = 0;
423 if (AAudioStream_getTimestamp(aaudio->stream, CLOCK_MONOTONIC, &framePosition,
424 &timeNanoseconds) == AAUDIO_OK)
426 struct timespec ts = { 0 };
427 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
429 const int64_t nowNanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec;
430 int64_t elapsedNanos = nowNanos - timeNanoseconds;
431 if (elapsedNanos < 0)
433 if (elapsedNanos > INT64_MAX / AAUDIO_MAX_RATE)
434 elapsedNanos = INT64_MAX / AAUDIO_MAX_RATE;
435 const int64_t framesPlayed = elapsedNanos * aaudio->rate / 1000000000LL;
436 queued = AAudioStream_getFramesWritten(aaudio->stream) - (framePosition + framesPlayed);
440 const int64_t written = AAudioStream_getFramesWritten(aaudio->stream);
441 const int64_t read = AAudioStream_getFramesRead(aaudio->stream);
442 queued = written - read;
447 const int64_t written = AAudioStream_getFramesWritten(aaudio->stream);
448 const int64_t read = AAudioStream_getFramesRead(aaudio->stream);
449 queued = written - read;
454 const UINT64 latencyMs = (UINT64)queued / aaudio->rate * 1000 +
455 ((UINT64)queued % aaudio->rate) * 1000 / aaudio->rate;
456 const UINT latency = (latencyMs > UINT32_MAX) ? UINT32_MAX : (UINT)latencyMs;
457 LeaveCriticalSection(&aaudio->lock);
461FREERDP_ENTRY_POINT(UINT VCAPITYPE aaudio_freerdp_rdpsnd_client_subsystem_entry(
464 WINPR_ASSERT(pEntryPoints);
465 WINPR_ASSERT(pEntryPoints->pRegisterRdpsndDevice);
467 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)calloc(1,
sizeof(rdpsndAAudioPlugin));
469 return CHANNEL_RC_NO_MEMORY;
471 InitializeCriticalSection(&aaudio->lock);
473 aaudio->device.Open = rdpsnd_aaudio_open;
474 aaudio->device.FormatSupported = rdpsnd_aaudio_format_supported;
475 aaudio->device.GetVolume = rdpsnd_aaudio_get_volume;
476 aaudio->device.SetVolume = rdpsnd_aaudio_set_volume;
477 aaudio->device.Play = rdpsnd_aaudio_play;
478 aaudio->device.Close = rdpsnd_aaudio_close;
479 aaudio->device.Free = rdpsnd_aaudio_free;
480 aaudio->device.DefaultFormat = rdpsnd_aaudio_default_format;
482 aaudio->rate = 48000;
483 aaudio->channels = 2;
484 aaudio->volume = 0xFFFFFFFF;
486 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)aaudio);
487 return CHANNEL_RC_OK;