23#define __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
25#include <freerdp/config.h>
27#include <winpr/assert.h>
34#include <winpr/cast.h>
36#include <winpr/cmdline.h>
37#include <winpr/synch.h>
39#include <freerdp/types.h>
40#include <freerdp/channels/log.h>
42#include <aaudio/AAudio.h>
44#include "rdpsnd_main.h"
47#define AAUDIO_DEFAULT_LATENCY_MS 100
50#define AAUDIO_WRITE_TIMEOUT_NS (10LL * 1000LL * 1000LL)
52#define AAUDIO_MAX_RATE 48000
56 rdpsndDevicePlugin device;
74static BOOL rdpsnd_aaudio_pcm_valid(
const AUDIO_FORMAT* format)
78 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->cbSize == 0) &&
79 (format->wBitsPerSample == 16) && (format->nSamplesPerSec > 0) &&
80 (format->nSamplesPerSec <= AAUDIO_MAX_RATE) &&
81 ((format->nChannels == 1) || (format->nChannels == 2));
84static void rdpsnd_aaudio_close_stream(rdpsndAAudioPlugin* aaudio)
91 if (__builtin_available(android 26, *))
93 aaudio_result_t rc = AAudioStream_requestStop(aaudio->stream);
95 WLog_WARN(TAG,
"AAudioStream_requestStop: %s", AAudio_convertResultToText(rc));
96 rc = AAudioStream_close(aaudio->stream);
98 WLog_WARN(TAG,
"AAudioStream_close: %s", AAudio_convertResultToText(rc));
100 aaudio->stream =
nullptr;
104static BOOL rdpsnd_aaudio_open_stream(rdpsndAAudioPlugin* aaudio)
106 WINPR_ASSERT(aaudio);
111 if (__builtin_available(android 26, *))
113 AAudioStreamBuilder* builder =
nullptr;
115 aaudio_result_t rc = AAudio_createStreamBuilder(&builder);
118 WLog_ERR(TAG,
"AAudio_createStreamBuilder: %s", AAudio_convertResultToText(rc));
122 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_OUTPUT);
123 AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
124 AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_NONE);
125 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
126 AAudioStreamBuilder_setSampleRate(builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->rate));
127 AAudioStreamBuilder_setChannelCount(builder,
128 WINPR_ASSERTING_INT_CAST(int32_t, aaudio->channels));
129#if __ANDROID_API__ >= 28
130 AAudioStreamBuilder_setUsage(builder, AAUDIO_USAGE_MEDIA);
131 AAudioStreamBuilder_setContentType(builder, AAUDIO_CONTENT_TYPE_MUSIC);
134 const UINT32 targetMs = (aaudio->latency > 0) ? aaudio->latency : AAUDIO_DEFAULT_LATENCY_MS;
135 const UINT64 targetFrames = (UINT64)targetMs * aaudio->rate / 1000;
136 const int32_t targetCapacity = (targetFrames > INT32_MAX)
138 : WINPR_ASSERTING_INT_CAST(int32_t, targetFrames);
139 AAudioStreamBuilder_setBufferCapacityInFrames(builder, targetCapacity);
141 rc = AAudioStreamBuilder_openStream(builder, &aaudio->stream);
142 AAudioStreamBuilder_delete(builder);
146 WLog_ERR(TAG,
"AAudioStreamBuilder_openStream: %s", AAudio_convertResultToText(rc));
147 aaudio->stream =
nullptr;
151 const int32_t actualRate = AAudioStream_getSampleRate(aaudio->stream);
152 const int32_t actualChannels = AAudioStream_getChannelCount(aaudio->stream);
153 const aaudio_format_t actualFormat = AAudioStream_getFormat(aaudio->stream);
158 if ((actualChannels < 0) || ((UINT32)actualChannels != aaudio->channels) ||
159 (actualFormat != AAUDIO_FORMAT_PCM_I16))
161 WLog_ERR(TAG,
"AAudio opened as %dch/fmt%d, need %" PRIu32
"ch/I16", actualChannels,
162 (
int)actualFormat, aaudio->channels);
163 rdpsnd_aaudio_close_stream(aaudio);
167 if ((actualRate < 0) || ((UINT32)actualRate != aaudio->rate))
169 WLog_WARN(TAG,
"AAudio rate adjusted: requested %" PRIu32
"Hz, got %dHz", aaudio->rate,
174 const int32_t burst = AAudioStream_getFramesPerBurst(aaudio->stream);
175 const int32_t capacity = AAudioStream_getBufferCapacityInFrames(aaudio->stream);
176 int32_t target = targetCapacity;
178 if ((burst > 0) && (burst <= INT32_MAX / 2) && (target < burst * 2))
180 if ((capacity > 0) && (target > capacity))
183 const aaudio_result_t size = AAudioStream_setBufferSizeInFrames(aaudio->stream, target);
185 WLog_WARN(TAG,
"AAudioStream_setBufferSizeInFrames(%d): %s", target,
186 AAudio_convertResultToText(size));
188 rc = AAudioStream_requestStart(aaudio->stream);
191 WLog_ERR(TAG,
"AAudioStream_requestStart: %s", AAudio_convertResultToText(rc));
192 rdpsnd_aaudio_close_stream(aaudio);
199 WLog_ERR(TAG,
"AAudio requires Android 26 or newer");
204static BOOL rdpsnd_aaudio_format_supported(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format)
206 WINPR_ASSERT(device);
207 WINPR_ASSERT(format);
209 return rdpsnd_aaudio_pcm_valid(format);
213static BOOL rdpsnd_aaudio_default_format(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* desired,
216 WINPR_ASSERT(device);
217 WINPR_ASSERT(defaultFormat);
220 *defaultFormat = *desired;
222 defaultFormat->wFormatTag = WAVE_FORMAT_PCM;
223 defaultFormat->wBitsPerSample = 16;
224 defaultFormat->cbSize = 0;
225 defaultFormat->data =
nullptr;
227 if ((defaultFormat->nChannels != 1) && (defaultFormat->nChannels != 2))
228 defaultFormat->nChannels = 2;
229 if ((defaultFormat->nSamplesPerSec == 0) || (defaultFormat->nSamplesPerSec > AAUDIO_MAX_RATE))
230 defaultFormat->nSamplesPerSec = AAUDIO_MAX_RATE;
232 defaultFormat->nBlockAlign = defaultFormat->nChannels * defaultFormat->wBitsPerSample / 8;
233 defaultFormat->nAvgBytesPerSec = defaultFormat->nBlockAlign * defaultFormat->nSamplesPerSec;
238static BOOL rdpsnd_aaudio_open(rdpsndDevicePlugin* device,
const AUDIO_FORMAT* format,
241 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
242 WINPR_ASSERT(aaudio);
244 if (format && !rdpsnd_aaudio_pcm_valid(format))
246 WLog_ERR(TAG,
"unsupported format %s", audio_format_get_tag_string(format->wFormatTag));
250 EnterCriticalSection(&aaudio->lock);
254 if (aaudio->stream &&
255 ((aaudio->rate != format->nSamplesPerSec) || (aaudio->channels != format->nChannels)))
256 rdpsnd_aaudio_close_stream(aaudio);
258 aaudio->rate = format->nSamplesPerSec;
259 aaudio->channels = format->nChannels;
262 aaudio->latency = latency;
263 const BOOL rc = rdpsnd_aaudio_open_stream(aaudio);
265 LeaveCriticalSection(&aaudio->lock);
269static void rdpsnd_aaudio_close(rdpsndDevicePlugin* device)
271 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
272 WINPR_ASSERT(aaudio);
273 EnterCriticalSection(&aaudio->lock);
274 aaudio->opened = FALSE;
275 rdpsnd_aaudio_close_stream(aaudio);
276 LeaveCriticalSection(&aaudio->lock);
279static void rdpsnd_aaudio_free(rdpsndDevicePlugin* device)
281 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
285 EnterCriticalSection(&aaudio->lock);
286 aaudio->opened = FALSE;
287 rdpsnd_aaudio_close_stream(aaudio);
288 free(aaudio->gainbuf);
289 aaudio->gainbuf =
nullptr;
290 aaudio->gainbuf_len = 0;
291 LeaveCriticalSection(&aaudio->lock);
293 DeleteCriticalSection(&aaudio->lock);
298static UINT32 rdpsnd_aaudio_get_volume(rdpsndDevicePlugin* device)
300 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
301 WINPR_ASSERT(aaudio);
302 EnterCriticalSection(&aaudio->lock);
303 const UINT32 volume = aaudio->volume;
304 LeaveCriticalSection(&aaudio->lock);
309static BOOL rdpsnd_aaudio_set_volume(rdpsndDevicePlugin* device, UINT32 value)
311 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
312 WINPR_ASSERT(aaudio);
313 EnterCriticalSection(&aaudio->lock);
314 aaudio->volume = value;
315 LeaveCriticalSection(&aaudio->lock);
321static const short* rdpsnd_aaudio_apply_volume(rdpsndAAudioPlugin* aaudio,
const short* src,
324 WINPR_ASSERT(aaudio);
327 const UINT32 left = aaudio->volume & 0xFFFF;
328 const UINT32 right = (aaudio->volume >> 16) & 0xFFFF;
330 if ((left == 0xFFFF) && (right == 0xFFFF))
333 if (samples > SIZE_MAX /
sizeof(
short))
336 if (aaudio->gainbuf_len < samples)
338 short* tmp = (
short*)realloc(aaudio->gainbuf, samples *
sizeof(
short));
341 aaudio->gainbuf = tmp;
342 aaudio->gainbuf_len = samples;
345 for (
size_t i = 0; i < samples; i++)
347 const UINT32 gain = ((aaudio->channels == 2) && (i & 1)) ? right : left;
348 aaudio->gainbuf[i] = (short)((int64_t)src[i] * gain / 0xFFFF);
351 return aaudio->gainbuf;
355static UINT rdpsnd_aaudio_play(rdpsndDevicePlugin* device,
const BYTE* data,
size_t size)
357 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)device;
358 WINPR_ASSERT(aaudio);
360 if (!data || (size < 2))
363 EnterCriticalSection(&aaudio->lock);
368 LeaveCriticalSection(&aaudio->lock);
374 if (!rdpsnd_aaudio_open_stream(aaudio))
376 LeaveCriticalSection(&aaudio->lock);
382 if (__builtin_available(android 26, *))
384 const size_t bytesPerFrame = aaudio->channels *
sizeof(INT16);
385 if (bytesPerFrame == 0)
387 LeaveCriticalSection(&aaudio->lock);
391 const short* samples =
392 rdpsnd_aaudio_apply_volume(aaudio, (
const short*)data, size /
sizeof(
short));
393 size_t frames = size / bytesPerFrame;
394 const BYTE* pos = (
const BYTE*)samples;
398 const int32_t chunk =
399 (frames > INT32_MAX) ? INT32_MAX : WINPR_ASSERTING_INT_CAST(int32_t, frames);
400 aaudio_result_t written =
401 AAudioStream_write(aaudio->stream, pos, chunk, AAUDIO_WRITE_TIMEOUT_NS);
408 if (written != AAUDIO_ERROR_TIMEOUT)
410 WLog_WARN(TAG,
"AAudio stream unusable (%s), closing",
411 AAudio_convertResultToText(written));
412 rdpsnd_aaudio_close_stream(aaudio);
416 WLog_ERR(TAG,
"AAudioStream_write: %s", AAudio_convertResultToText(written));
419 LeaveCriticalSection(&aaudio->lock);
423 if ((written == 0) || (written > chunk))
426 pos += (size_t)written * bytesPerFrame;
427 frames -= (size_t)written;
430 if (aaudio->rate == 0)
432 LeaveCriticalSection(&aaudio->lock);
437 int64_t framePosition = 0;
438 int64_t timeNanoseconds = 0;
441 if (AAudioStream_getTimestamp(aaudio->stream, CLOCK_MONOTONIC, &framePosition,
442 &timeNanoseconds) == AAUDIO_OK)
444 struct timespec ts = { 0 };
445 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0)
447 const int64_t nowNanos = (int64_t)ts.tv_sec * 1000000000LL + ts.tv_nsec;
448 int64_t elapsedNanos = nowNanos - timeNanoseconds;
449 if (elapsedNanos < 0)
451 if (elapsedNanos > INT64_MAX / AAUDIO_MAX_RATE)
452 elapsedNanos = INT64_MAX / AAUDIO_MAX_RATE;
453 const int64_t framesPlayed = elapsedNanos * aaudio->rate / 1000000000LL;
455 AAudioStream_getFramesWritten(aaudio->stream) - (framePosition + framesPlayed);
459 const int64_t written = AAudioStream_getFramesWritten(aaudio->stream);
460 const int64_t read = AAudioStream_getFramesRead(aaudio->stream);
461 queued = written - read;
466 const int64_t written = AAudioStream_getFramesWritten(aaudio->stream);
467 const int64_t read = AAudioStream_getFramesRead(aaudio->stream);
468 queued = written - read;
473 const UINT64 latencyMs = (UINT64)queued / aaudio->rate * 1000 +
474 ((UINT64)queued % aaudio->rate) * 1000 / aaudio->rate;
475 latency = (latencyMs > UINT32_MAX) ? UINT32_MAX : (UINT)latencyMs;
477 LeaveCriticalSection(&aaudio->lock);
481FREERDP_ENTRY_POINT(UINT VCAPITYPE aaudio_freerdp_rdpsnd_client_subsystem_entry(
484 WINPR_ASSERT(pEntryPoints);
485 WINPR_ASSERT(pEntryPoints->pRegisterRdpsndDevice);
487 rdpsndAAudioPlugin* aaudio = (rdpsndAAudioPlugin*)calloc(1,
sizeof(rdpsndAAudioPlugin));
489 return CHANNEL_RC_NO_MEMORY;
491 InitializeCriticalSection(&aaudio->lock);
493 aaudio->device.Open = rdpsnd_aaudio_open;
494 aaudio->device.FormatSupported = rdpsnd_aaudio_format_supported;
495 aaudio->device.GetVolume = rdpsnd_aaudio_get_volume;
496 aaudio->device.SetVolume = rdpsnd_aaudio_set_volume;
497 aaudio->device.Play = rdpsnd_aaudio_play;
498 aaudio->device.Close = rdpsnd_aaudio_close;
499 aaudio->device.Free = rdpsnd_aaudio_free;
500 aaudio->device.DefaultFormat = rdpsnd_aaudio_default_format;
502 aaudio->rate = 48000;
503 aaudio->channels = 2;
504 aaudio->volume = 0xFFFFFFFF;
506 pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*)aaudio);
507 return CHANNEL_RC_OK;