FreeRDP
audin_ios.m
1 
21 #include <freerdp/config.h>
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
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>
33 
34 #import <AVFoundation/AVFoundation.h>
35 
36 #define __COREFOUNDATION_CFPLUGINCOM__ 1
37 #define IUNKNOWN_C_GUTS \
38  void *_reserved; \
39  void *QueryInterface; \
40  void *AddRef; \
41  void *Release
42 
43 #include <CoreAudio/CoreAudioTypes.h>
44 #include <AudioToolbox/AudioToolbox.h>
45 #include <AudioToolbox/AudioQueue.h>
46 
47 #include <freerdp/addin.h>
48 #include <freerdp/channels/rdpsnd.h>
49 
50 #include "audin_main.h"
51 
52 #define IOS_AUDIO_QUEUE_NUM_BUFFERS 100
53 
54 typedef struct
55 {
56  IAudinDevice iface;
57 
58  AUDIO_FORMAT format;
59  UINT32 FramesPerPacket;
60  int dev_unit;
61 
62  AudinReceive receive;
63  void *user_data;
64 
65  rdpContext *rdpcontext;
66 
67  bool isOpen;
68  AudioQueueRef audioQueue;
69  AudioStreamBasicDescription audioFormat;
70  AudioQueueBufferRef audioBuffers[IOS_AUDIO_QUEUE_NUM_BUFFERS];
71 } AudinIosDevice;
72 
73 static AudioFormatID audin_ios_get_format(const AUDIO_FORMAT *format)
74 {
75  switch (format->wFormatTag)
76  {
77  case WAVE_FORMAT_PCM:
78  return kAudioFormatLinearPCM;
79 
80  default:
81  return 0;
82  }
83 }
84 
85 static AudioFormatFlags audin_ios_get_flags_for_format(const AUDIO_FORMAT *format)
86 {
87  switch (format->wFormatTag)
88  {
89  case WAVE_FORMAT_PCM:
90  return kAudioFormatFlagIsSignedInteger;
91 
92  default:
93  return 0;
94  }
95 }
96 
97 static BOOL audin_ios_format_supported(IAudinDevice *device, const AUDIO_FORMAT *format)
98 {
99  AudinIosDevice *ios = (AudinIosDevice *)device;
100  AudioFormatID req_fmt = 0;
101 
102  if (device == NULL || format == NULL)
103  return FALSE;
104 
105  req_fmt = audin_ios_get_format(format);
106 
107  if (req_fmt == 0)
108  return FALSE;
109 
110  return TRUE;
111 }
112 
118 static UINT audin_ios_set_format(IAudinDevice *device, const AUDIO_FORMAT *format,
119  UINT32 FramesPerPacket)
120 {
121  AudinIosDevice *ios = (AudinIosDevice *)device;
122 
123  if (device == NULL || format == NULL)
124  return ERROR_INVALID_PARAMETER;
125 
126  ios->FramesPerPacket = FramesPerPacket;
127  ios->format = *format;
128  WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
129  audio_format_get_tag_string(format->wFormatTag), format->nChannels,
130  format->nSamplesPerSec, format->wBitsPerSample);
131  ios->audioFormat.mBitsPerChannel = format->wBitsPerSample;
132 
133  if (format->wBitsPerSample == 0)
134  ios->audioFormat.mBitsPerChannel = 16;
135 
136  ios->audioFormat.mChannelsPerFrame = ios->format.nChannels;
137  ios->audioFormat.mFramesPerPacket = 1;
138 
139  ios->audioFormat.mBytesPerFrame =
140  ios->audioFormat.mChannelsPerFrame * (ios->audioFormat.mBitsPerChannel / 8);
141  ios->audioFormat.mBytesPerPacket =
142  ios->audioFormat.mBytesPerFrame * ios->audioFormat.mFramesPerPacket;
143 
144  ios->audioFormat.mFormatFlags = audin_ios_get_flags_for_format(format);
145  ios->audioFormat.mFormatID = audin_ios_get_format(format);
146  ios->audioFormat.mReserved = 0;
147  ios->audioFormat.mSampleRate = ios->format.nSamplesPerSec;
148  return CHANNEL_RC_OK;
149 }
150 
151 static void ios_audio_queue_input_cb(void *aqData, AudioQueueRef inAQ, AudioQueueBufferRef inBuffer,
152  const AudioTimeStamp *inStartTime, UInt32 inNumPackets,
153  const AudioStreamPacketDescription *inPacketDesc)
154 {
155  AudinIosDevice *ios = (AudinIosDevice *)aqData;
156  UINT error = CHANNEL_RC_OK;
157  const BYTE *buffer = inBuffer->mAudioData;
158  int buffer_size = inBuffer->mAudioDataByteSize;
159  (void)inAQ;
160  (void)inStartTime;
161  (void)inNumPackets;
162  (void)inPacketDesc;
163 
164  if (buffer_size > 0)
165  error = ios->receive(&ios->format, buffer, buffer_size, ios->user_data);
166 
167  AudioQueueEnqueueBuffer(inAQ, inBuffer, 0, NULL);
168 
169  if (error)
170  {
171  WLog_ERR(TAG, "ios->receive failed with error %" PRIu32 "", error);
172  SetLastError(ERROR_INTERNAL_ERROR);
173  }
174 }
175 
176 static UINT audin_ios_close(IAudinDevice *device)
177 {
178  UINT errCode = CHANNEL_RC_OK;
179  char errString[1024];
180  OSStatus devStat;
181  AudinIosDevice *ios = (AudinIosDevice *)device;
182 
183  if (device == NULL)
184  return ERROR_INVALID_PARAMETER;
185 
186  if (ios->isOpen)
187  {
188  devStat = AudioQueueStop(ios->audioQueue, true);
189 
190  if (devStat != 0)
191  {
192  errCode = GetLastError();
193  WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
194  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
195  }
196 
197  ios->isOpen = false;
198  }
199 
200  if (ios->audioQueue)
201  {
202  devStat = AudioQueueDispose(ios->audioQueue, true);
203 
204  if (devStat != 0)
205  {
206  errCode = GetLastError();
207  WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
208  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
209  }
210 
211  ios->audioQueue = NULL;
212  }
213 
214  ios->receive = NULL;
215  ios->user_data = NULL;
216  return errCode;
217 }
218 
219 static UINT audin_ios_open(IAudinDevice *device, AudinReceive receive, void *user_data)
220 {
221  AudinIosDevice *ios = (AudinIosDevice *)device;
222  DWORD errCode;
223  char errString[1024];
224  OSStatus devStat;
225 
226  ios->receive = receive;
227  ios->user_data = user_data;
228  devStat = AudioQueueNewInput(&(ios->audioFormat), ios_audio_queue_input_cb, ios, NULL,
229  kCFRunLoopCommonModes, 0, &(ios->audioQueue));
230 
231  if (devStat != 0)
232  {
233  errCode = GetLastError();
234  WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
235  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
236  goto err_out;
237  }
238 
239  for (size_t index = 0; index < IOS_AUDIO_QUEUE_NUM_BUFFERS; index++)
240  {
241  devStat = AudioQueueAllocateBuffer(ios->audioQueue,
242  ios->FramesPerPacket * 2 * ios->format.nChannels,
243  &ios->audioBuffers[index]);
244 
245  if (devStat != 0)
246  {
247  errCode = GetLastError();
248  WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
249  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
250  goto err_out;
251  }
252 
253  devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, NULL);
254 
255  if (devStat != 0)
256  {
257  errCode = GetLastError();
258  WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
259  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
260  goto err_out;
261  }
262  }
263 
264  devStat = AudioQueueStart(ios->audioQueue, NULL);
265 
266  if (devStat != 0)
267  {
268  errCode = GetLastError();
269  WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
270  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
271  goto err_out;
272  }
273 
274  ios->isOpen = true;
275  return CHANNEL_RC_OK;
276 err_out:
277  audin_ios_close(device);
278  return CHANNEL_RC_INITIALIZATION_ERROR;
279 }
280 
281 static UINT audin_ios_free(IAudinDevice *device)
282 {
283  AudinIosDevice *ios = (AudinIosDevice *)device;
284  int error;
285 
286  if (device == NULL)
287  return ERROR_INVALID_PARAMETER;
288 
289  if ((error = audin_ios_close(device)))
290  {
291  WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
292  }
293 
294  free(ios);
295  return CHANNEL_RC_OK;
296 }
297 
298 FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_audin_client_subsystem_entry(
300 {
301  DWORD errCode;
302  char errString[1024];
303  const ADDIN_ARGV *args;
304  AudinIosDevice *ios;
305  UINT error;
306  ios = (AudinIosDevice *)calloc(1, sizeof(AudinIosDevice));
307 
308  if (!ios)
309  {
310  errCode = GetLastError();
311  WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
312  winpr_strerror(errCode, errString, sizeof(errString)), errCode);
313  return CHANNEL_RC_NO_MEMORY;
314  }
315 
316  ios->iface.Open = audin_ios_open;
317  ios->iface.FormatSupported = audin_ios_format_supported;
318  ios->iface.SetFormat = audin_ios_set_format;
319  ios->iface.Close = audin_ios_close;
320  ios->iface.Free = audin_ios_free;
321  ios->rdpcontext = pEntryPoints->rdpcontext;
322  ios->dev_unit = -1;
323  args = pEntryPoints->args;
324 
325  if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)ios)))
326  {
327  WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
328  goto error_out;
329  }
330 
331  return CHANNEL_RC_OK;
332 error_out:
333  free(ios);
334  return error;
335 }
Definition: client/audin.h:59