20#include <freerdp/config.h>
26#include <winpr/assert.h>
27#include <winpr/cast.h>
29#include <winpr/synch.h>
30#include <winpr/thread.h>
32#include <freerdp/freerdp.h>
33#include <freerdp/addin.h>
34#include <freerdp/channels/rdpsnd.h>
35#include <freerdp/client/audin.h>
37#include <aaudio/AAudio.h>
39#include "audin_main.h"
42#define AAUDIO_READ_TIMEOUT_NS (200LL * 1000LL * 1000LL)
44#define AAUDIO_MAX_RATE 48000
59 UINT32 frames_per_packet;
64 rdpContext* rdpcontext;
69static UINT audin_aaudio_close(IAudinDevice* device);
72static BOOL audin_aaudio_pcm_valid(
const AUDIO_FORMAT* format)
76 return (format->wFormatTag == WAVE_FORMAT_PCM) && (format->cbSize == 0) &&
77 (format->wBitsPerSample == 16) && (format->nSamplesPerSec > 0) &&
78 (format->nSamplesPerSec <= AAUDIO_MAX_RATE) &&
79 ((format->nChannels == 1) || (format->nChannels == 2));
82static void audin_aaudio_stop_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
87 const aaudio_result_t rc = AAudioStream_requestStop(stream);
89 WLog_Print(aaudio->log, WLOG_WARN,
"AAudioStream_requestStop: %s",
90 AAudio_convertResultToText(rc));
93static void audin_aaudio_close_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
98 const aaudio_result_t rc = AAudioStream_close(stream);
100 WLog_Print(aaudio->log, WLOG_WARN,
"AAudioStream_close: %s",
101 AAudio_convertResultToText(rc));
105static BOOL audin_aaudio_open_stream(AudinAAudioDevice* aaudio)
107 WINPR_ASSERT(aaudio);
109 AAudioStreamBuilder* builder =
nullptr;
111 aaudio_result_t rc = AAudio_createStreamBuilder(&builder);
114 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudio_createStreamBuilder: %s",
115 AAudio_convertResultToText(rc));
119 AAudioStreamBuilder_setDirection(builder, AAUDIO_DIRECTION_INPUT);
120 AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_SHARED);
121 AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_NONE);
122 AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_I16);
123 AAudioStreamBuilder_setSampleRate(
124 builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->format.nSamplesPerSec));
125 AAudioStreamBuilder_setChannelCount(
126 builder, WINPR_ASSERTING_INT_CAST(int32_t, aaudio->format.nChannels));
127#if __ANDROID_API__ >= 28
128 AAudioStreamBuilder_setInputPreset(builder, AAUDIO_INPUT_PRESET_VOICE_COMMUNICATION);
131 AAudioStream* stream =
nullptr;
132 rc = AAudioStreamBuilder_openStream(builder, &stream);
133 AAudioStreamBuilder_delete(builder);
137 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStreamBuilder_openStream: %s",
138 AAudio_convertResultToText(rc));
145 const int32_t actualRate = AAudioStream_getSampleRate(stream);
146 const int32_t actualChannels = AAudioStream_getChannelCount(stream);
147 const aaudio_format_t actualFormat = AAudioStream_getFormat(stream);
149 if ((actualChannels < 0) || ((UINT32)actualChannels != aaudio->format.nChannels) ||
150 (actualFormat != AAUDIO_FORMAT_PCM_I16))
152 WLog_Print(aaudio->log, WLOG_ERROR,
153 "AAudio input opened as %dch/fmt%d, need %" PRIu16
"ch/I16", actualChannels,
154 (
int)actualFormat, aaudio->format.nChannels);
155 audin_aaudio_close_stream(aaudio, stream);
159 if ((actualRate < 0) || ((UINT32)actualRate != aaudio->format.nSamplesPerSec))
161 WLog_Print(aaudio->log, WLOG_WARN,
162 "AAudio input rate adjusted: requested %" PRIu32
"Hz, got %dHz",
163 aaudio->format.nSamplesPerSec, actualRate);
166 rc = AAudioStream_requestStart(stream);
169 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_requestStart: %s",
170 AAudio_convertResultToText(rc));
171 audin_aaudio_close_stream(aaudio, stream);
175 EnterCriticalSection(&aaudio->lock);
176 aaudio->stream = stream;
177 LeaveCriticalSection(&aaudio->lock);
182static void audin_aaudio_drop_stream(AudinAAudioDevice* aaudio, AAudioStream* stream)
184 WINPR_ASSERT(aaudio);
186 EnterCriticalSection(&aaudio->lock);
187 if (aaudio->stream && (!stream || (aaudio->stream == stream)))
189 audin_aaudio_stop_stream(aaudio, aaudio->stream);
190 audin_aaudio_close_stream(aaudio, aaudio->stream);
191 aaudio->stream =
nullptr;
193 LeaveCriticalSection(&aaudio->lock);
197static DWORD WINAPI audin_aaudio_thread(LPVOID arg)
199 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)arg;
200 WINPR_ASSERT(aaudio);
201 WINPR_ASSERT(aaudio->receive);
202 WINPR_ASSERT(aaudio->stopEvent);
204 const size_t bytesPerFrame = aaudio->format.nChannels *
sizeof(INT16);
205 const size_t framesPerPacket = aaudio->frames_per_packet;
206 UINT error = CHANNEL_RC_OK;
208 if ((bytesPerFrame == 0) || (framesPerPacket == 0) || (framesPerPacket > INT32_MAX) ||
209 (framesPerPacket > SIZE_MAX / bytesPerFrame))
211 audin_aaudio_drop_stream(aaudio,
nullptr);
212 return ERROR_INVALID_DATA;
215 const size_t accumulationBytes = framesPerPacket * bytesPerFrame;
216 BYTE* accumulationBuffer = (BYTE*)calloc(framesPerPacket, bytesPerFrame);
217 if (!accumulationBuffer)
219 audin_aaudio_drop_stream(aaudio,
nullptr);
220 return CHANNEL_RC_NO_MEMORY;
223 size_t currentFrames = 0;
225 while (WaitForSingleObject(aaudio->stopEvent, 0) != WAIT_OBJECT_0)
227 EnterCriticalSection(&aaudio->lock);
228 AAudioStream* stream = aaudio->stream;
229 LeaveCriticalSection(&aaudio->lock);
234 if (!audin_aaudio_open_stream(aaudio))
236 if (WaitForSingleObject(aaudio->stopEvent, 500) == WAIT_OBJECT_0)
242 const int32_t framesToRead =
243 WINPR_ASSERTING_INT_CAST(int32_t, framesPerPacket - currentFrames);
244 BYTE* writePtr = accumulationBuffer + (currentFrames * bytesPerFrame);
249 AAudioStream_read(stream, writePtr, framesToRead, AAUDIO_READ_TIMEOUT_NS);
253 if (WaitForSingleObject(aaudio->stopEvent, 0) == WAIT_OBJECT_0)
256 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_read: %s",
257 AAudio_convertResultToText(r));
261 if (r != AAUDIO_ERROR_TIMEOUT)
263 audin_aaudio_drop_stream(aaudio, stream);
269 if (WaitForSingleObject(aaudio->stopEvent, 100) == WAIT_OBJECT_0)
277 if (r > framesToRead)
279 WLog_Print(aaudio->log, WLOG_ERROR,
"AAudioStream_read returned %d > %d frames", r,
281 audin_aaudio_drop_stream(aaudio, stream);
286 currentFrames += (size_t)r;
289 if (currentFrames == framesPerPacket)
291 error = aaudio->receive(&aaudio->format, accumulationBuffer, accumulationBytes,
296 if (aaudio->rdpcontext)
297 setChannelError(aaudio->rdpcontext, error,
298 "audin_aaudio receive reported an error");
304 audin_aaudio_drop_stream(aaudio,
nullptr);
305 free(accumulationBuffer);
310static BOOL audin_aaudio_format_supported(IAudinDevice* device,
const AUDIO_FORMAT* format)
312 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
314 if (!aaudio || !format)
317 return audin_aaudio_pcm_valid(format);
321static UINT audin_aaudio_set_format(IAudinDevice* device,
const AUDIO_FORMAT* format,
322 UINT32 FramesPerPacket)
324 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
326 if (!aaudio || !format)
327 return ERROR_INVALID_PARAMETER;
329 if (!audin_aaudio_pcm_valid(format))
330 return ERROR_UNSUPPORTED_TYPE;
332 const size_t bytesPerFrame = format->nChannels *
sizeof(INT16);
333 const UINT32 frames = (FramesPerPacket > 0) ? FramesPerPacket : 1;
334 if ((frames > INT32_MAX) || (frames > SIZE_MAX / bytesPerFrame))
336 WLog_Print(aaudio->log, WLOG_ERROR,
"invalid FramesPerPacket %" PRIu32, FramesPerPacket);
337 return ERROR_INVALID_DATA;
340 aaudio->format = *format;
341 aaudio->frames_per_packet = frames;
342 return CHANNEL_RC_OK;
346static UINT audin_aaudio_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
348 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
350 if (!aaudio || !receive || !user_data)
351 return ERROR_INVALID_PARAMETER;
355 if (aaudio->thread && (WaitForSingleObject(aaudio->thread, 0) == WAIT_OBJECT_0))
357 if (!CloseHandle(aaudio->thread))
358 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle on the exited capture thread failed");
359 aaudio->thread =
nullptr;
362 if (aaudio->stream || aaudio->thread)
363 return CHANNEL_RC_OK;
366 if ((aaudio->format.nSamplesPerSec == 0) ||
367 ((aaudio->format.nChannels != 1) && (aaudio->format.nChannels != 2)))
369 WLog_Print(aaudio->log, WLOG_ERROR,
"SetFormat must run with a valid PCM format first");
370 return ERROR_INVALID_PARAMETER;
373 aaudio->receive = receive;
374 aaudio->user_data = user_data;
376 if (!audin_aaudio_open_stream(aaudio))
379 if (aaudio->stopEvent)
381 if (!ResetEvent(aaudio->stopEvent))
383 WLog_Print(aaudio->log, WLOG_ERROR,
"ResetEvent failed");
389 aaudio->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr);
390 if (!aaudio->stopEvent)
392 WLog_Print(aaudio->log, WLOG_ERROR,
"CreateEvent failed");
397 aaudio->thread = CreateThread(
nullptr, 0, audin_aaudio_thread, aaudio, 0,
nullptr);
400 WLog_Print(aaudio->log, WLOG_ERROR,
"CreateThread failed");
404 return CHANNEL_RC_OK;
406 if (audin_aaudio_close(device) != CHANNEL_RC_OK)
407 WLog_Print(aaudio->log, WLOG_WARN,
"audin_aaudio_close failed");
408 return ERROR_INTERNAL_ERROR;
412static UINT audin_aaudio_close(IAudinDevice* device)
414 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
417 return ERROR_INVALID_PARAMETER;
419 if (aaudio->stopEvent && !SetEvent(aaudio->stopEvent))
420 WLog_Print(aaudio->log, WLOG_WARN,
"SetEvent failed");
423 EnterCriticalSection(&aaudio->lock);
425 audin_aaudio_stop_stream(aaudio, aaudio->stream);
426 LeaveCriticalSection(&aaudio->lock);
430 if (WaitForSingleObject(aaudio->thread, INFINITE) != WAIT_OBJECT_0)
431 WLog_Print(aaudio->log, WLOG_WARN,
"WaitForSingleObject failed");
432 if (!CloseHandle(aaudio->thread))
433 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle failed");
434 aaudio->thread =
nullptr;
437 EnterCriticalSection(&aaudio->lock);
440 audin_aaudio_close_stream(aaudio, aaudio->stream);
441 aaudio->stream =
nullptr;
443 LeaveCriticalSection(&aaudio->lock);
445 if (aaudio->stopEvent)
447 if (!CloseHandle(aaudio->stopEvent))
448 WLog_Print(aaudio->log, WLOG_WARN,
"CloseHandle failed");
449 aaudio->stopEvent =
nullptr;
452 aaudio->receive =
nullptr;
453 aaudio->user_data =
nullptr;
454 return CHANNEL_RC_OK;
458static UINT audin_aaudio_free(IAudinDevice* device)
460 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)device;
463 return ERROR_INVALID_PARAMETER;
465 if (audin_aaudio_close(device) != CHANNEL_RC_OK)
466 WLog_Print(aaudio->log, WLOG_WARN,
"audin_aaudio_close failed");
467 DeleteCriticalSection(&aaudio->lock);
469 return CHANNEL_RC_OK;
472FREERDP_ENTRY_POINT(UINT VCAPITYPE aaudio_freerdp_audin_client_subsystem_entry(
475 WINPR_ASSERT(pEntryPoints);
476 WINPR_ASSERT(pEntryPoints->pRegisterAudinDevice);
478 UINT error = ERROR_INTERNAL_ERROR;
479 AudinAAudioDevice* aaudio = (AudinAAudioDevice*)calloc(1,
sizeof(AudinAAudioDevice));
483 WLog_ERR(TAG,
"calloc failed!");
484 return CHANNEL_RC_NO_MEMORY;
487 InitializeCriticalSection(&aaudio->lock);
489 aaudio->log = WLog_Get(TAG);
490 aaudio->iface.Open = audin_aaudio_open;
491 aaudio->iface.FormatSupported = audin_aaudio_format_supported;
492 aaudio->iface.SetFormat = audin_aaudio_set_format;
493 aaudio->iface.Close = audin_aaudio_close;
494 aaudio->iface.Free = audin_aaudio_free;
495 aaudio->rdpcontext = pEntryPoints->rdpcontext;
497 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)aaudio)))
499 WLog_Print(aaudio->log, WLOG_ERROR,
"RegisterAudinDevice failed with error %" PRIu32
"!",
504 return CHANNEL_RC_OK;
506 DeleteCriticalSection(&aaudio->lock);