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 == nullptr || format == nullptr)
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 == nullptr || format == nullptr)
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, nullptr);
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 == nullptr)
184 {
185 WLog_ERR(TAG, "device == nullptr");
186 return ERROR_INVALID_PARAMETER;
187 }
188
189 if (ios->isOpen)
190 {
191 devStat = AudioQueueStop(ios->audioQueue, true);
192
193 if (devStat != 0)
194 {
195 errCode = GetLastError();
196 WLog_ERR(TAG, "AudioQueueStop failed with %s [%" PRIu32 "]",
197 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
198 }
199
200 ios->isOpen = false;
201 }
202
203 if (ios->audioQueue)
204 {
205 devStat = AudioQueueDispose(ios->audioQueue, true);
206
207 if (devStat != 0)
208 {
209 errCode = GetLastError();
210 WLog_ERR(TAG, "AudioQueueDispose failed with %s [%" PRIu32 "]",
211 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
212 }
213
214 ios->audioQueue = nullptr;
215 }
216
217 ios->receive = nullptr;
218 ios->user_data = nullptr;
219 return errCode;
220}
221
222static UINT audin_ios_open(IAudinDevice *device, AudinReceive receive, void *user_data)
223{
224 AudinIosDevice *ios = (AudinIosDevice *)device;
225 DWORD errCode;
226 char errString[1024];
227 OSStatus devStat;
228
229 ios->receive = receive;
230 ios->user_data = user_data;
231 devStat = AudioQueueNewInput(&(ios->audioFormat), ios_audio_queue_input_cb, ios, nullptr,
232 kCFRunLoopCommonModes, 0, &(ios->audioQueue));
233
234 if (devStat != 0)
235 {
236 errCode = GetLastError();
237 WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%" PRIu32 "]",
238 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
239 goto err_out;
240 }
241
242 for (size_t index = 0; index < IOS_AUDIO_QUEUE_NUM_BUFFERS; index++)
243 {
244 devStat = AudioQueueAllocateBuffer(ios->audioQueue,
245 ios->FramesPerPacket * 2 * ios->format.nChannels,
246 &ios->audioBuffers[index]);
247
248 if (devStat != 0)
249 {
250 errCode = GetLastError();
251 WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
252 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
253 goto err_out;
254 }
255
256 devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, nullptr);
257
258 if (devStat != 0)
259 {
260 errCode = GetLastError();
261 WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
262 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
263 goto err_out;
264 }
265 }
266
267 devStat = AudioQueueStart(ios->audioQueue, nullptr);
268
269 if (devStat != 0)
270 {
271 errCode = GetLastError();
272 WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
273 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
274 goto err_out;
275 }
276
277 ios->isOpen = true;
278 return CHANNEL_RC_OK;
279err_out:
280 (void)audin_ios_close(device);
281 return CHANNEL_RC_INITIALIZATION_ERROR;
282}
283
284static UINT audin_ios_free(IAudinDevice *device)
285{
286 AudinIosDevice *ios = (AudinIosDevice *)device;
287 int error;
288
289 if (device == nullptr)
290 return ERROR_INVALID_PARAMETER;
291
292 (void)audin_ios_close(device);
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}