21#include <freerdp/config.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 
   59typedef UInt32 AudioFormatID;
 
   62#ifndef AudioFormatFlags 
   63typedef UInt32 AudioFormatFlags;
 
   71  UINT32 FramesPerPacket;
 
   77  rdpContext *rdpcontext;
 
   81  AudioQueueRef audioQueue;
 
   82  AudioStreamBasicDescription audioFormat;
 
   83  AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
 
   86static AudioFormatID audin_mac_get_format(
const AUDIO_FORMAT *format)
 
   88  switch (format->wFormatTag)
 
   91      return kAudioFormatLinearPCM;
 
   98static AudioFormatFlags audin_mac_get_flags_for_format(
const AUDIO_FORMAT *format)
 
  100  switch (format->wFormatTag)
 
  102    case WAVE_FORMAT_PCM:
 
  103      return kAudioFormatFlagIsSignedInteger;
 
  110static 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);
 
  137static 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;
 
  173static 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);
 
  198static 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;
 
  244static 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;
 
  309static 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;
 
  326static 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;
 
  383FREERDP_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;