FreeRDP
Loading...
Searching...
No Matches
audin_sndio.c
1
23#include <freerdp/config.h>
24
25#include <sndio.h>
26
27#include <winpr/cmdline.h>
28
29#include <freerdp/freerdp.h>
30#include <freerdp/channels/rdpsnd.h>
31
32#include "audin_main.h"
33
34typedef struct
35{
36 IAudinDevice device;
37
38 HANDLE thread;
39 HANDLE stopEvent;
40
41 AUDIO_FORMAT format;
42 UINT32 FramesPerPacket;
43
44 AudinReceive receive;
45 void* user_data;
46
47 rdpContext* rdpcontext;
48} AudinSndioDevice;
49
50static BOOL audin_sndio_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
51{
52 if (device == nullptr || format == nullptr)
53 return FALSE;
54
55 return (format->wFormatTag == WAVE_FORMAT_PCM);
56}
57
63static UINT audin_sndio_set_format(IAudinDevice* device, AUDIO_FORMAT* format,
64 UINT32 FramesPerPacket)
65{
66 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
67
68 if (device == nullptr || format == nullptr)
69 return ERROR_INVALID_PARAMETER;
70
71 if (format->wFormatTag != WAVE_FORMAT_PCM)
72 return ERROR_INTERNAL_ERROR;
73
74 sndio->format = *format;
75 sndio->FramesPerPacket = FramesPerPacket;
76
77 return CHANNEL_RC_OK;
78}
79
80static void* audin_sndio_thread_func(void* arg)
81{
82 struct sio_hdl* hdl;
83 struct sio_par par;
84 BYTE* buffer = nullptr;
85 size_t n;
86 AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
87 UINT error = 0;
88 DWORD status;
89
90 if (arg == nullptr)
91 {
92 error = ERROR_INVALID_PARAMETER;
93 goto err_out;
94 }
95
96 hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
97 if (hdl == nullptr)
98 {
99 WLog_ERR(TAG, "could not open audio device");
100 error = ERROR_INTERNAL_ERROR;
101 goto err_out;
102 }
103
104 sio_initpar(&par);
105 par.bits = sndio->format.wBitsPerSample;
106 par.rchan = sndio->format.nChannels;
107 par.rate = sndio->format.nSamplesPerSec;
108 if (!sio_setpar(hdl, &par))
109 {
110 WLog_ERR(TAG, "could not set audio parameters");
111 error = ERROR_INTERNAL_ERROR;
112 goto err_out;
113 }
114 if (!sio_getpar(hdl, &par))
115 {
116 WLog_ERR(TAG, "could not get audio parameters");
117 error = ERROR_INTERNAL_ERROR;
118 goto err_out;
119 }
120
121 if (!sio_start(hdl))
122 {
123 WLog_ERR(TAG, "could not start audio device");
124 error = ERROR_INTERNAL_ERROR;
125 goto err_out;
126 }
127
128 const size_t nbytes = (1ull * sndio->FramesPerPacket * sndio->format.nChannels *
129 (sndio->format.wBitsPerSample / 8));
130 if (nbytes > INT32_MAX)
131 goto err_out;
132
133 buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
134
135 if (buffer == nullptr)
136 {
137 error = ERROR_NOT_ENOUGH_MEMORY;
138 goto err_out;
139 }
140
141 while (1)
142 {
143 status = WaitForSingleObject(sndio->stopEvent, 0);
144
145 if (status == WAIT_FAILED)
146 {
147 error = GetLastError();
148 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
149 goto err_out;
150 }
151
152 if (status == WAIT_OBJECT_0)
153 break;
154
155 n = sio_read(hdl, buffer, nbytes);
156
157 if (n == 0)
158 {
159 WLog_ERR(TAG, "could not read");
160 continue;
161 }
162
163 if (n < nbytes)
164 continue;
165
166 if ((error = sndio->receive(&sndio->format, buffer, nbytes, sndio->user_data)))
167 {
168 WLog_ERR(TAG, "sndio->receive failed with error %" PRIu32 "", error);
169 break;
170 }
171 }
172
173err_out:
174 if (error && sndio && sndio->rdpcontext)
175 setChannelError(sndio->rdpcontext, error, "audin_sndio_thread_func reported an error");
176
177 if (hdl != nullptr)
178 {
179 WLog_INFO(TAG, "sio_close");
180 sio_stop(hdl);
181 sio_close(hdl);
182 }
183
184 free(buffer);
185 ExitThread(0);
186 return nullptr;
187}
188
194static UINT audin_sndio_open(IAudinDevice* device, AudinReceive receive, void* user_data)
195{
196 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
197 sndio->receive = receive;
198 sndio->user_data = user_data;
199
200 if (!(sndio->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
201 {
202 WLog_ERR(TAG, "CreateEvent failed");
203 return ERROR_INTERNAL_ERROR;
204 }
205
206 if (!(sndio->thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)audin_sndio_thread_func,
207 sndio, 0, nullptr)))
208 {
209 WLog_ERR(TAG, "CreateThread failed");
210 (void)CloseHandle(sndio->stopEvent);
211 sndio->stopEvent = nullptr;
212 return ERROR_INTERNAL_ERROR;
213 }
214
215 return CHANNEL_RC_OK;
216}
217
223static UINT audin_sndio_close(IAudinDevice* device)
224{
225 UINT error;
226 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
227
228 if (device == nullptr)
229 return ERROR_INVALID_PARAMETER;
230
231 if (sndio->stopEvent != nullptr)
232 {
233 (void)SetEvent(sndio->stopEvent);
234
235 if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
236 {
237 error = GetLastError();
238 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
239 return error;
240 }
241
242 (void)CloseHandle(sndio->stopEvent);
243 sndio->stopEvent = nullptr;
244 (void)CloseHandle(sndio->thread);
245 sndio->thread = nullptr;
246 }
247
248 sndio->receive = nullptr;
249 sndio->user_data = nullptr;
250
251 return CHANNEL_RC_OK;
252}
253
259static UINT audin_sndio_free(IAudinDevice* device)
260{
261 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
262 int error;
263
264 if (device == nullptr)
265 return ERROR_INVALID_PARAMETER;
266
267 if ((error = audin_sndio_close(device)))
268 {
269 WLog_ERR(TAG, "audin_sndio_close failed with error code %d", error);
270 }
271
272 free(sndio);
273
274 return CHANNEL_RC_OK;
275}
276
282static UINT audin_sndio_parse_addin_args(AudinSndioDevice* device, ADDIN_ARGV* args)
283{
284 int status;
285 DWORD flags;
287 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
288 COMMAND_LINE_ARGUMENT_A audin_sndio_args[] = { { nullptr, 0, nullptr, nullptr, nullptr, -1,
289 nullptr, nullptr } };
290 flags =
291 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
292 status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_sndio_args,
293 flags, sndio, nullptr, nullptr);
294
295 if (status < 0)
296 return ERROR_INVALID_PARAMETER;
297
298 arg = audin_sndio_args;
299
300 do
301 {
302 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
303 continue;
304
305 CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
306 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
307
308 return CHANNEL_RC_OK;
309}
310
316FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
318{
319 ADDIN_ARGV* args;
320 AudinSndioDevice* sndio;
321 UINT ret = CHANNEL_RC_OK;
322 sndio = (AudinSndioDevice*)calloc(1, sizeof(AudinSndioDevice));
323
324 if (sndio == nullptr)
325 return CHANNEL_RC_NO_MEMORY;
326
327 sndio->device.Open = audin_sndio_open;
328 sndio->device.FormatSupported = audin_sndio_format_supported;
329 sndio->device.SetFormat = audin_sndio_set_format;
330 sndio->device.Close = audin_sndio_close;
331 sndio->device.Free = audin_sndio_free;
332 sndio->rdpcontext = pEntryPoints->rdpcontext;
333 args = pEntryPoints->args;
334
335 if (args->argc > 1)
336 {
337 ret = audin_sndio_parse_addin_args(sndio, args);
338
339 if (ret != CHANNEL_RC_OK)
340 {
341 WLog_ERR(TAG, "error parsing arguments");
342 goto error;
343 }
344 }
345
346 if ((ret = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)sndio)))
347 {
348 WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "", ret);
349 goto error;
350 }
351
352 return ret;
353error:
354 audin_sndio_free(&sndio->device);
355 return ret;
356}