FreeRDP
Loading...
Searching...
No Matches
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
54typedef 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
73static 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
85static 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
97static 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
118static 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
151static 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
176static 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
219static 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;
276err_out:
277 audin_ios_close(device);
278 return CHANNEL_RC_INITIALIZATION_ERROR;
279}
280
281static 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
298FREERDP_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;
332error_out:
333 free(ios);
334 return error;
335}