23#define __ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__
25#include <freerdp/config.h>
31#include <winpr/assert.h>
32#include <winpr/cast.h>
34#include <winpr/synch.h>
35#include <winpr/thread.h>
37#include <freerdp/freerdp.h>
38#include <freerdp/addin.h>
39#include <freerdp/channels/rdpsnd.h>
40#include <freerdp/client/audin.h>
42#include <aaudio/AAudio.h>
44#include "audin_main.h"
47#define AAUDIO_READ_TIMEOUT_NS (200LL * 1000LL * 1000LL)
49#define AAUDIO_MAX_RATE 48000
64 UINT32 frames_per_packet;
69 rdpContext* rdpcontext;
74static UINT audin_aaudio_close(IAudinDevice* device);
77static BOOL audin_aaudio_pcm_valid(
const AUDIO_FORMAT* format)
81 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->cbSize == 0) &&
82 (format->wBitsPerSample == 16) && (format->nSamplesPerSec > 0) &&
83 (format->nSamplesPerSec <= AAUDIO_MAX_RATE) &&
84 ((format->nChannels == 1) || (format->nChannels == 2));
87static void audin_aaudio_stop_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
92 if (__builtin_available(android 26, *))
94 const aaudio_result_t rc = AAudioStream_requestStop(stream);
96 WLog_Print(aaudio->log, WLOG_WARN,
"AAudioStream_requestStop: %s",
97 AAudio_convertResultToText(rc));
101static void audin_aaudio_close_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
103 WINPR_ASSERT(aaudio);
104 WINPR_ASSERT(stream);
106 if (__builtin_available(android 26, *))
108 const aaudio_result_t rc = AAudioStream_close(stream);
110 WLog_Print(aaudio->log, WLOG_WARN,
"AAudioStream_close: %s",
111 AAudio_convertResultToText(rc));
116static BOOL audin_aaudio_open_stream(AudinAAudioDevice* aaudio)
118 WINPR_ASSERT(aaudio);
120 if (__builtin_available(android 26, *))
122 AAudioStreamBuilder* builder =
nullptr;
124 aaudio_result_t rc = AAudio_createStreamBuilder(&builder);
127 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudio_createStreamBuilder: %s",
128 AAudio_convertResultToText(rc));
132 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
133 AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
134 AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_NONE);
135 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
136 AAudioStreamBuilder_setSampleRate(
137 builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->format.nSamplesPerSec));
138 AAudioStreamBuilder_setChannelCount(
139 builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->format.nChannels));
140#if __ANDROID_API__ >= 28
141 AAudioStreamBuilder_setInputPreset(builder, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION);
144 AAudioStream* stream =
nullptr;
145 rc = AAudioStreamBuilder_openStream(builder, &stream);
146 AAudioStreamBuilder_delete(builder);
150 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStreamBuilder_openStream: %s",
151 AAudio_convertResultToText(rc));
158 const int32_t actualRate = AAudioStream_getSampleRate(stream);
159 const int32_t actualChannels = AAudioStream_getChannelCount(stream);
160 const aaudio_format_t actualFormat = AAudioStream_getFormat(stream);
162 if ((actualChannels < 0) || ((UINT32)actualChannels != aaudio->format.nChannels) ||
163 (actualFormat != AAUDIO_FORMAT_PCM_I16))
165 WLog_Print(aaudio->log, WLOG_ERROR,
166 "AAudio input opened as %dch/fmt%d, need %" PRIu16
"ch/I16", actualChannels,
167 (
int)actualFormat, aaudio->format.nChannels);
168 audin_aaudio_close_stream(aaudio, stream);
172 if ((actualRate < 0) || ((UINT32)actualRate != aaudio->format.nSamplesPerSec))
174 WLog_Print(aaudio->log, WLOG_WARN,
175 "AAudio input rate adjusted: requested %" PRIu32
"Hz, got %dHz",
176 aaudio->format.nSamplesPerSec, actualRate);
179 rc = AAudioStream_requestStart(stream);
182 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_requestStart: %s",
183 AAudio_convertResultToText(rc));
184 audin_aaudio_close_stream(aaudio, stream);
188 EnterCriticalSection(&aaudio->lock);
189 aaudio->stream = stream;
190 LeaveCriticalSection(&aaudio->lock);
194 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudio requires Android 26 or newer");
199static void audin_aaudio_drop_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
201 WINPR_ASSERT(aaudio);
203 EnterCriticalSection(&aaudio->lock);
204 if (aaudio->stream && (!stream || (aaudio->stream == stream)))
206 audin_aaudio_stop_stream(aaudio, aaudio->stream);
207 audin_aaudio_close_stream(aaudio, aaudio->stream);
208 aaudio->stream =
nullptr;
210 LeaveCriticalSection(&aaudio->lock);
214static DWORD WINAPI audin_aaudio_thread(LPVOID arg)
216 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)arg;
217 WINPR_ASSERT(aaudio);
218 WINPR_ASSERT(aaudio->receive);
219 WINPR_ASSERT(aaudio->stopEvent);
221 const size_t bytesPerFrame = aaudio->format.nChannels *
sizeof(INT16);
222 const size_t framesPerPacket = aaudio->frames_per_packet;
223 UINT error = CHANNEL_RC_OK;
225 if ((bytesPerFrame == 0) || (framesPerPacket == 0) || (framesPerPacket > INT32_MAX) ||
226 (framesPerPacket > SIZE_MAX / bytesPerFrame))
228 audin_aaudio_drop_stream(aaudio,
nullptr);
229 return ERROR_INVALID_DATA;
232 const size_t accumulationBytes = framesPerPacket * bytesPerFrame;
233 BYTE* accumulationBuffer = (BYTE*)calloc(framesPerPacket, bytesPerFrame);
234 if (!accumulationBuffer)
236 audin_aaudio_drop_stream(aaudio,
nullptr);
237 return CHANNEL_RC_NO_MEMORY;
240 size_t currentFrames = 0;
242 while (WaitForSingleObject(aaudio->stopEvent, 0) != WAIT_OBJECT_0)
244 EnterCriticalSection(&aaudio->lock);
245 AAudioStream* stream = aaudio->stream;
246 LeaveCriticalSection(&aaudio->lock);
251 if (!audin_aaudio_open_stream(aaudio))
253 if (WaitForSingleObject(aaudio->stopEvent, 500) == WAIT_OBJECT_0)
259 const int32_t framesToRead =
260 WINPR_ASSERTING_INT_CAST(int32_t, framesPerPacket - currentFrames);
261 BYTE* writePtr = accumulationBuffer + (currentFrames * bytesPerFrame);
267 aaudio_result_t r = AAUDIO_ERROR_TIMEOUT;
268 if (__builtin_available(android 26, *))
269 r = AAudioStream_read(stream, writePtr, framesToRead, AAUDIO_READ_TIMEOUT_NS);
273 if (WaitForSingleObject(aaudio->stopEvent, 0) == WAIT_OBJECT_0)
276 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_read: %s",
277 AAudio_convertResultToText(r));
281 if (r != AAUDIO_ERROR_TIMEOUT)
283 audin_aaudio_drop_stream(aaudio, stream);
289 if (WaitForSingleObject(aaudio->stopEvent, 100) == WAIT_OBJECT_0)
297 if (r > framesToRead)
299 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_read returned %d > %d frames", r,
301 audin_aaudio_drop_stream(aaudio, stream);
306 currentFrames += (size_t)r;
309 if (currentFrames == framesPerPacket)
311 error = aaudio->receive(&aaudio->format, accumulationBuffer, accumulationBytes,
316 if (aaudio->rdpcontext)
317 setChannelError(aaudio->rdpcontext, error,
318 "audin_aaudio receive reported an error");
324 audin_aaudio_drop_stream(aaudio,
nullptr);
325 free(accumulationBuffer);
330static BOOL audin_aaudio_format_supported(IAudinDevice* device,
const AUDIO_FORMAT* format)
332 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
334 if (!aaudio || !format)
337 return audin_aaudio_pcm_valid(format);
341static UINT audin_aaudio_set_format(IAudinDevice* device,
const AUDIO_FORMAT* format,
342 UINT32 FramesPerPacket)
344 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
346 if (!aaudio || !format)
347 return ERROR_INVALID_PARAMETER;
349 if (!audin_aaudio_pcm_valid(format))
350 return ERROR_UNSUPPORTED_TYPE;
352 const size_t bytesPerFrame = format->nChannels *
sizeof(INT16);
353 const UINT32 frames = (FramesPerPacket > 0) ? FramesPerPacket : 1;
354 if ((frames > INT32_MAX) || (frames > SIZE_MAX / bytesPerFrame))
356 WLog_Print(aaudio->log, WLOG_ERROR,
"invalid FramesPerPacket %" PRIu32, FramesPerPacket);
357 return ERROR_INVALID_DATA;
360 aaudio->format = *format;
361 aaudio->frames_per_packet = frames;
362 return CHANNEL_RC_OK;
366static UINT audin_aaudio_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
368 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
370 if (!aaudio || !receive || !user_data)
371 return ERROR_INVALID_PARAMETER;
375 if (aaudio->thread && (WaitForSingleObject(aaudio->thread, 0) == WAIT_OBJECT_0))
377 if (!CloseHandle(aaudio->thread))
378 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle on the exited capture thread failed");
379 aaudio->thread =
nullptr;
382 if (aaudio->stream || aaudio->thread)
383 return CHANNEL_RC_OK;
386 if ((aaudio->format.nSamplesPerSec == 0) ||
387 ((aaudio->format.nChannels != 1) && (aaudio->format.nChannels != 2)))
389 WLog_Print(aaudio->log, WLOG_ERROR,
"SetFormat must run with a valid PCM format first");
390 return ERROR_INVALID_PARAMETER;
393 aaudio->receive = receive;
394 aaudio->user_data = user_data;
396 if (!audin_aaudio_open_stream(aaudio))
399 if (aaudio->stopEvent)
401 if (!ResetEvent(aaudio->stopEvent))
403 WLog_Print(aaudio->log, WLOG_ERROR,
"ResetEvent failed");
409 aaudio->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
410 if (!aaudio->stopEvent)
412 WLog_Print(aaudio->log, WLOG_ERROR,
"CreateEvent failed");
417 aaudio->thread = CreateThread(
nullptr, 0, audin_aaudio_thread, aaudio, 0,
nullptr);
420 WLog_Print(aaudio->log, WLOG_ERROR,
"CreateThread failed");
424 return CHANNEL_RC_OK;
426 if (audin_aaudio_close(device) != CHANNEL_RC_OK)
427 WLog_Print(aaudio->log, WLOG_WARN,
"audin_aaudio_close failed");
428 return ERROR_INTERNAL_ERROR;
432static UINT audin_aaudio_close(IAudinDevice* device)
434 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
437 return ERROR_INVALID_PARAMETER;
439 if (aaudio->stopEvent && !SetEvent(aaudio->stopEvent))
440 WLog_Print(aaudio->log, WLOG_WARN,
"SetEvent failed");
443 EnterCriticalSection(&aaudio->lock);
445 audin_aaudio_stop_stream(aaudio, aaudio->stream);
446 LeaveCriticalSection(&aaudio->lock);
450 if (WaitForSingleObject(aaudio->thread, INFINITE) != WAIT_OBJECT_0)
451 WLog_Print(aaudio->log, WLOG_WARN,
"WaitForSingleObject failed");
452 if (!CloseHandle(aaudio->thread))
453 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle failed");
454 aaudio->thread =
nullptr;
457 EnterCriticalSection(&aaudio->lock);
460 audin_aaudio_close_stream(aaudio, aaudio->stream);
461 aaudio->stream =
nullptr;
463 LeaveCriticalSection(&aaudio->lock);
465 if (aaudio->stopEvent)
467 if (!CloseHandle(aaudio->stopEvent))
468 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle failed");
469 aaudio->stopEvent =
nullptr;
472 aaudio->receive =
nullptr;
473 aaudio->user_data =
nullptr;
474 return CHANNEL_RC_OK;
478static UINT audin_aaudio_free(IAudinDevice* device)
480 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
483 return ERROR_INVALID_PARAMETER;
485 if (audin_aaudio_close(device) != CHANNEL_RC_OK)
486 WLog_Print(aaudio->log, WLOG_WARN,
"audin_aaudio_close failed");
487 DeleteCriticalSection(&aaudio->lock);
489 return CHANNEL_RC_OK;
492FREERDP_ENTRY_POINT(UINT VCAPITYPE aaudio_freerdp_audin_client_subsystem_entry(
495 WINPR_ASSERT(pEntryPoints);
496 WINPR_ASSERT(pEntryPoints->pRegisterAudinDevice);
498 UINT error = ERROR_INTERNAL_ERROR;
499 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)calloc(1,
sizeof(AudinAAudioDevice));
503 WLog_ERR(TAG,
"calloc failed!");
504 return CHANNEL_RC_NO_MEMORY;
507 InitializeCriticalSection(&aaudio->lock);
509 aaudio->log = WLog_Get(TAG);
510 aaudio->iface.Open = audin_aaudio_open;
511 aaudio->iface.FormatSupported = audin_aaudio_format_supported;
512 aaudio->iface.SetFormat = audin_aaudio_set_format;
513 aaudio->iface.Close = audin_aaudio_close;
514 aaudio->iface.Free = audin_aaudio_free;
515 aaudio->rdpcontext = pEntryPoints->rdpcontext;
517 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)aaudio)))
519 WLog_Print(aaudio->log, WLOG_ERROR,
"RegisterAudinDevice failed with error %" PRIu32
"!",
524 return CHANNEL_RC_OK;
526 DeleteCriticalSection(&aaudio->lock);