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, const 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 DWORD WINAPI audin_sndio_thread_func(LPVOID arg)
81{
82 struct sio_hdl* hdl = nullptr;
83 struct sio_par par = WINPR_C_ARRAY_INIT;
84 BYTE* buffer = nullptr;
85 AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
86 UINT error = CHANNEL_RC_OK;
87
88 if (arg == nullptr)
89 {
90 error = ERROR_INVALID_PARAMETER;
91 goto err_out;
92 }
93
94 hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
95 if (hdl == nullptr)
96 {
97 WLog_ERR(TAG, "could not open audio device");
98 error = ERROR_INTERNAL_ERROR;
99 goto err_out;
100 }
101
102 sio_initpar(&par);
103 par.bits = sndio->format.wBitsPerSample;
104 par.rchan = sndio->format.nChannels;
105 par.rate = sndio->format.nSamplesPerSec;
106 if (!sio_setpar(hdl, &par))
107 {
108 WLog_ERR(TAG, "could not set audio parameters");
109 error = ERROR_INTERNAL_ERROR;
110 goto err_out;
111 }
112 if (!sio_getpar(hdl, &par))
113 {
114 WLog_ERR(TAG, "could not get audio parameters");
115 error = ERROR_INTERNAL_ERROR;
116 goto err_out;
117 }
118
119 if (!sio_start(hdl))
120 {
121 WLog_ERR(TAG, "could not start audio device");
122 error = ERROR_INTERNAL_ERROR;
123 goto err_out;
124 }
125
126 const size_t nbytes = (1ull * sndio->FramesPerPacket * sndio->format.nChannels *
127 (sndio->format.wBitsPerSample / 8));
128 if (nbytes > INT32_MAX)
129 goto err_out;
130
131 buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
132
133 if (buffer == nullptr)
134 {
135 error = ERROR_NOT_ENOUGH_MEMORY;
136 goto err_out;
137 }
138
139 while (1)
140 {
141 const DWORD status = WaitForSingleObject(sndio->stopEvent, 0);
142
143 if (status == WAIT_FAILED)
144 {
145 error = GetLastError();
146 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
147 goto err_out;
148 }
149
150 if (status == WAIT_OBJECT_0)
151 break;
152
153 const size_t n = sio_read(hdl, buffer, nbytes);
154
155 if (n == 0)
156 {
157 WLog_ERR(TAG, "could not read");
158 continue;
159 }
160
161 if (n < nbytes)
162 continue;
163
164 if ((error = sndio->receive(&sndio->format, buffer, nbytes, sndio->user_data)))
165 {
166 WLog_ERR(TAG, "sndio->receive failed with error %" PRIu32 "", error);
167 break;
168 }
169 }
170
171err_out:
172 if (error && sndio && sndio->rdpcontext)
173 setChannelError(sndio->rdpcontext, error, "audin_sndio_thread_func reported an error");
174
175 if (hdl != nullptr)
176 {
177 WLog_INFO(TAG, "sio_close");
178 sio_stop(hdl);
179 sio_close(hdl);
180 }
181
182 free(buffer);
183 return 0;
184}
185
191static UINT audin_sndio_open(IAudinDevice* device, AudinReceive receive, void* user_data)
192{
193 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
194 sndio->receive = receive;
195 sndio->user_data = user_data;
196
197 if (!(sndio->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
198 {
199 WLog_ERR(TAG, "CreateEvent failed");
200 return ERROR_INTERNAL_ERROR;
201 }
202
203 if (!(sndio->thread = CreateThread(nullptr, 0, audin_sndio_thread_func, sndio, 0, nullptr)))
204 {
205 WLog_ERR(TAG, "CreateThread failed");
206 (void)CloseHandle(sndio->stopEvent);
207 sndio->stopEvent = nullptr;
208 return ERROR_INTERNAL_ERROR;
209 }
210
211 return CHANNEL_RC_OK;
212}
213
219static UINT audin_sndio_close(IAudinDevice* device)
220{
221 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
222
223 if (device == nullptr)
224 return ERROR_INVALID_PARAMETER;
225
226 if (sndio->stopEvent != nullptr)
227 {
228 (void)SetEvent(sndio->stopEvent);
229
230 if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
231 {
232 const UINT error = GetLastError();
233 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
234 return error;
235 }
236
237 (void)CloseHandle(sndio->stopEvent);
238 sndio->stopEvent = nullptr;
239 (void)CloseHandle(sndio->thread);
240 sndio->thread = nullptr;
241 }
242
243 sndio->receive = nullptr;
244 sndio->user_data = nullptr;
245
246 return CHANNEL_RC_OK;
247}
248
254static UINT audin_sndio_free(IAudinDevice* device)
255{
256 AudinSndioDevice* sndio = (AudinSndioDevice*)device;
257
258 if (device == nullptr)
259 return ERROR_INVALID_PARAMETER;
260
261 const UINT error = audin_sndio_close(device);
262 if (error != CHANNEL_RC_OK)
263 {
264 WLog_ERR(TAG, "audin_sndio_close failed with error code %" PRIu32, error);
265 }
266
267 free(sndio);
268
269 return CHANNEL_RC_OK;
270}
271
277static UINT audin_sndio_parse_addin_args(AudinSndioDevice* sndio, const ADDIN_ARGV* args)
278{
279 WINPR_ASSERT(sndio);
280
281 COMMAND_LINE_ARGUMENT_A audin_sndio_args[] = { { nullptr, 0, nullptr, nullptr, nullptr, -1,
282 nullptr, nullptr } };
283 const DWORD flags =
284 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
285 const int status = CommandLineParseArgumentsA(args->argc, args->argv, audin_sndio_args, flags,
286 sndio, nullptr, nullptr);
287
288 if (status < 0)
289 return ERROR_INVALID_PARAMETER;
290
291 const COMMAND_LINE_ARGUMENT_A* arg = audin_sndio_args;
292
293 do
294 {
295 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
296 continue;
297
298 CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
299 } while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
300
301 return CHANNEL_RC_OK;
302}
303
309FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
311{
312 UINT ret = CHANNEL_RC_OK;
313 AudinSndioDevice* sndio = (AudinSndioDevice*)calloc(1, sizeof(AudinSndioDevice));
314
315 if (sndio == nullptr)
316 return CHANNEL_RC_NO_MEMORY;
317
318 sndio->device.Open = audin_sndio_open;
319 sndio->device.FormatSupported = audin_sndio_format_supported;
320 sndio->device.SetFormat = audin_sndio_set_format;
321 sndio->device.Close = audin_sndio_close;
322 sndio->device.Free = audin_sndio_free;
323 sndio->rdpcontext = pEntryPoints->rdpcontext;
324 const ADDIN_ARGV* args = pEntryPoints->args;
325
326 if (args->argc > 1)
327 {
328 ret = audin_sndio_parse_addin_args(sndio, args);
329
330 if (ret != CHANNEL_RC_OK)
331 {
332 WLog_ERR(TAG, "error parsing arguments");
333 goto error;
334 }
335 }
336
337 if ((ret = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)sndio)))
338 {
339 WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "", ret);
340 goto error;
341 }
342
343 return ret;
344error:
345 audin_sndio_free(&sndio->device);
346 return ret;
347}