21 #include <freerdp/config.h>
27 #include <winpr/crt.h>
28 #include <winpr/synch.h>
29 #include <winpr/string.h>
30 #include <winpr/thread.h>
31 #include <winpr/debug.h>
32 #include <winpr/cmdline.h>
34 #import <AVFoundation/AVFoundation.h>
36 #define __COREFOUNDATION_CFPLUGINCOM__ 1
37 #define IUNKNOWN_C_GUTS \
39 void *QueryInterface; \
43 #include <CoreAudio/CoreAudioTypes.h>
44 #include <CoreAudio/CoreAudio.h>
45 #include <AudioToolbox/AudioToolbox.h>
46 #include <AudioToolbox/AudioQueue.h>
48 #include <freerdp/addin.h>
49 #include <freerdp/channels/rdpsnd.h>
51 #include "audin_main.h"
53 #define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
59 typedef UInt32 AudioFormatID;
62 #ifndef AudioFormatFlags
63 typedef UInt32 AudioFormatFlags;
71 UINT32 FramesPerPacket;
77 rdpContext *rdpcontext;
81 AudioQueueRef audioQueue;
82 AudioStreamBasicDescription audioFormat;
83 AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
86 static AudioFormatID audin_mac_get_format(
const AUDIO_FORMAT *format)
88 switch (format->wFormatTag)
91 return kAudioFormatLinearPCM;
98 static AudioFormatFlags audin_mac_get_flags_for_format(
const AUDIO_FORMAT *format)
100 switch (format->wFormatTag)
102 case WAVE_FORMAT_PCM:
103 return kAudioFormatFlagIsSignedInteger;
110 static BOOL audin_mac_format_supported(IAudinDevice *device,
const AUDIO_FORMAT *format)
112 AudinMacDevice *mac = (AudinMacDevice *)device;
113 AudioFormatID req_fmt = 0;
115 if (!mac->isAuthorized)
118 if (device == NULL || format == NULL)
121 if (format->nChannels != 2)
124 req_fmt = audin_mac_get_format(format);
137 static UINT audin_mac_set_format(IAudinDevice *device,
const AUDIO_FORMAT *format,
138 UINT32 FramesPerPacket)
140 AudinMacDevice *mac = (AudinMacDevice *)device;
142 if (!mac->isAuthorized)
143 return ERROR_INTERNAL_ERROR;
145 if (device == NULL || format == NULL)
146 return ERROR_INVALID_PARAMETER;
148 mac->FramesPerPacket = FramesPerPacket;
149 mac->format = *format;
150 WLog_INFO(TAG,
"Audio Format %s [channels=%d, samples=%d, bits=%d]",
151 audio_format_get_tag_string(format->wFormatTag), format->nChannels,
152 format->nSamplesPerSec, format->wBitsPerSample);
153 mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
155 if (format->wBitsPerSample == 0)
156 mac->audioFormat.mBitsPerChannel = 16;
158 mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
159 mac->audioFormat.mFramesPerPacket = 1;
161 mac->audioFormat.mBytesPerFrame =
162 mac->audioFormat.mChannelsPerFrame * (mac->audioFormat.mBitsPerChannel / 8);
163 mac->audioFormat.mBytesPerPacket =
164 mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
166 mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
167 mac->audioFormat.mFormatID = audin_mac_get_format(format);
168 mac->audioFormat.mReserved = 0;
169 mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
170 return CHANNEL_RC_OK;
173 static void mac_audio_queue_input_cb(
void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
174 const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
175 const AudioStreamPacketDescription *inPacketDesc)
177 AudinMacDevice *mac = (AudinMacDevice *)aqData;
178 UINT error = CHANNEL_RC_OK;
179 const BYTE *buffer = inBuffer->mAudioData;
180 int buffer_size = inBuffer->mAudioDataByteSize;
187 error = mac->receive(&mac->format, buffer, buffer_size, mac->user_data);
189 AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
193 WLog_ERR(TAG,
"mac->receive failed with error %" PRIu32
"", error);
194 SetLastError(ERROR_INTERNAL_ERROR);
198 static UINT audin_mac_close(IAudinDevice *device)
200 UINT errCode = CHANNEL_RC_OK;
201 char errString[1024];
203 AudinMacDevice *mac = (AudinMacDevice *)device;
205 if (!mac->isAuthorized)
206 return ERROR_INTERNAL_ERROR;
209 return ERROR_INVALID_PARAMETER;
213 devStat = AudioQueueStop(mac->audioQueue,
true);
217 errCode = GetLastError();
218 WLog_ERR(TAG,
"AudioQueueStop failed with %s [%" PRIu32
"]",
219 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
227 devStat = AudioQueueDispose(mac->audioQueue,
true);
231 errCode = GetLastError();
232 WLog_ERR(TAG,
"AudioQueueDispose failed with %s [%" PRIu32
"]",
233 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
236 mac->audioQueue = NULL;
240 mac->user_data = NULL;
244 static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive,
void *user_data)
246 AudinMacDevice *mac = (AudinMacDevice *)device;
248 char errString[1024];
251 if (!mac->isAuthorized)
252 return ERROR_INTERNAL_ERROR;
254 mac->receive = receive;
255 mac->user_data = user_data;
256 devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb, mac, NULL,
257 kCFRunLoopCommonModes, 0, &(mac->audioQueue));
261 errCode = GetLastError();
262 WLog_ERR(TAG,
"AudioQueueNewInput failed with %s [%" PRIu32
"]",
263 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
267 for (
size_t index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
269 devStat = AudioQueueAllocateBuffer(mac->audioQueue,
270 mac->FramesPerPacket * 2 * mac->format.nChannels,
271 &mac->audioBuffers[index]);
275 errCode = GetLastError();
276 WLog_ERR(TAG,
"AudioQueueAllocateBuffer failed with %s [%" PRIu32
"]",
277 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
281 devStat = AudioQueueEnqueueBuffer(mac->audioQueue, mac->audioBuffers[index], 0, NULL);
285 errCode = GetLastError();
286 WLog_ERR(TAG,
"AudioQueueEnqueueBuffer failed with %s [%" PRIu32
"]",
287 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
292 devStat = AudioQueueStart(mac->audioQueue, NULL);
296 errCode = GetLastError();
297 WLog_ERR(TAG,
"AudioQueueStart failed with %s [%" PRIu32
"]",
298 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
303 return CHANNEL_RC_OK;
305 audin_mac_close(device);
306 return CHANNEL_RC_INITIALIZATION_ERROR;
309 static UINT audin_mac_free(IAudinDevice *device)
311 AudinMacDevice *mac = (AudinMacDevice *)device;
315 return ERROR_INVALID_PARAMETER;
317 if ((error = audin_mac_close(device)))
319 WLog_ERR(TAG,
"audin_oss_close failed with error code %d!", error);
323 return CHANNEL_RC_OK;
326 static UINT audin_mac_parse_addin_args(AudinMacDevice *device,
const ADDIN_ARGV *args)
329 char errString[1024];
331 char *str_num, *eptr;
335 NULL, NULL, -1, NULL, "audio device name" },
336 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
338 AudinMacDevice *mac = (AudinMacDevice *)device;
341 return CHANNEL_RC_OK;
344 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
346 CommandLineParseArgumentsA(args->argc, args->argv, audin_mac_args, flags, mac, NULL, NULL);
349 return ERROR_INVALID_PARAMETER;
351 arg = audin_mac_args;
355 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
358 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
360 str_num = _strdup(arg->Value);
364 errCode = GetLastError();
365 WLog_ERR(TAG,
"_strdup failed with %s [%d]",
366 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
367 return CHANNEL_RC_NO_MEMORY;
370 mac->dev_unit = strtol(str_num, &eptr, 10);
372 if (mac->dev_unit < 0 || *eptr !=
'\0')
377 CommandLineSwitchEnd(arg)
378 }
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
380 return CHANNEL_RC_OK;
383 FREERDP_ENTRY_POINT(UINT VCAPITYPE mac_freerdp_audin_client_subsystem_entry(
387 char errString[1024];
391 mac = (AudinMacDevice *)calloc(1,
sizeof(AudinMacDevice));
395 errCode = GetLastError();
396 WLog_ERR(TAG,
"calloc failed with %s [%" PRIu32
"]",
397 winpr_strerror(errCode, errString,
sizeof(errString)), errCode);
398 return CHANNEL_RC_NO_MEMORY;
401 mac->iface.Open = audin_mac_open;
402 mac->iface.FormatSupported = audin_mac_format_supported;
403 mac->iface.SetFormat = audin_mac_set_format;
404 mac->iface.Close = audin_mac_close;
405 mac->iface.Free = audin_mac_free;
406 mac->rdpcontext = pEntryPoints->rdpcontext;
408 args = pEntryPoints->args;
410 if ((error = audin_mac_parse_addin_args(mac, args)))
412 WLog_ERR(TAG,
"audin_mac_parse_addin_args failed with %" PRIu32
"!", error);
416 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)mac)))
418 WLog_ERR(TAG,
"RegisterAudinDevice failed with error %" PRIu32
"!", error);
422 #if defined(MAC_OS_X_VERSION_10_14)
423 if (@available(macOS 10.14, *))
427 AVAuthorizationStatus status =
428 [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio];
431 case AVAuthorizationStatusAuthorized:
432 mac->isAuthorized = TRUE;
434 case AVAuthorizationStatusNotDetermined:
436 requestAccessForMediaType:AVMediaTypeAudio
437 completionHandler:^(BOOL granted) {
440 mac->isAuthorized = TRUE;
443 WLog_WARN(TAG, "Microphone access denied by user");
446 case AVAuthorizationStatusRestricted:
447 WLog_WARN(TAG,
"Microphone access restricted by policy");
449 case AVAuthorizationStatusDenied:
450 WLog_WARN(TAG,
"Microphone access denied by policy");
459 return CHANNEL_RC_OK;