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 const UINT64 bytes = 2ull * ios->FramesPerPacket * ios->format.nChannels;
245 if (bytes > UINT32_MAX)
246 goto err_out;
247
248 devStat = AudioQueueAllocateBuffer(ios->audioQueue, WINPR_ASSERTING_INT_CAST(UInt32, bytes),
249 &ios->audioBuffers[index]);
250
251 if (devStat != 0)
252 {
253 errCode = GetLastError();
254 WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%" PRIu32 "]",
255 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
256 goto err_out;
257 }
258
259 devStat = AudioQueueEnqueueBuffer(ios->audioQueue, ios->audioBuffers[index], 0, nullptr);
260
261 if (devStat != 0)
262 {
263 errCode = GetLastError();
264 WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%" PRIu32 "]",
265 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
266 goto err_out;
267 }
268 }
269
270 devStat = AudioQueueStart(ios->audioQueue, nullptr);
271
272 if (devStat != 0)
273 {
274 errCode = GetLastError();
275 WLog_ERR(TAG, "AudioQueueStart failed with %s [%" PRIu32 "]",
276 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
277 goto err_out;
278 }
279
280 ios->isOpen = true;
281 return CHANNEL_RC_OK;
282err_out:
283 (void)audin_ios_close(device);
284 return CHANNEL_RC_INITIALIZATION_ERROR;
285}
286
287static UINT audin_ios_free(IAudinDevice *device)
288{
289 AudinIosDevice *ios = (AudinIosDevice *)device;
290 int error;
291
292 if (device == nullptr)
293 return ERROR_INVALID_PARAMETER;
294
295 (void)audin_ios_close(device);
296
297 free(ios);
298 return CHANNEL_RC_OK;
299}
300
301FREERDP_ENTRY_POINT(UINT VCAPITYPE ios_freerdp_audin_client_subsystem_entry(
303{
304 DWORD errCode;
305 char errString[1024];
306 const ADDIN_ARGV *args;
307 AudinIosDevice *ios;
308 UINT error;
309 ios = (AudinIosDevice *)calloc(1, sizeof(AudinIosDevice));
310
311 if (!ios)
312 {
313 errCode = GetLastError();
314 WLog_ERR(TAG, "calloc failed with %s [%" PRIu32 "]",
315 winpr_strerror(errCode, errString, sizeof(errString)), errCode);
316 return CHANNEL_RC_NO_MEMORY;
317 }
318
319 ios->iface.Open = audin_ios_open;
320 ios->iface.FormatSupported = audin_ios_format_supported;
321 ios->iface.SetFormat = audin_ios_set_format;
322 ios->iface.Close = audin_ios_close;
323 ios->iface.Free = audin_ios_free;
324 ios->rdpcontext = pEntryPoints->rdpcontext;
325 ios->dev_unit = -1;
326 args = pEntryPoints->args;
327
328 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice *)ios)))
329 {
330 WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
331 goto error_out;
332 }
333
334 return CHANNEL_RC_OK;
335error_out:
336 free(ios);
337 return error;
338}