FreeRDP
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 
34 typedef 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 
50 static BOOL audin_sndio_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
51 {
52  if (device == NULL || format == NULL)
53  return FALSE;
54 
55  return (format->wFormatTag == WAVE_FORMAT_PCM);
56 }
57 
63 static UINT audin_sndio_set_format(IAudinDevice* device, AUDIO_FORMAT* format,
64  UINT32 FramesPerPacket)
65 {
66  AudinSndioDevice* sndio = (AudinSndioDevice*)device;
67 
68  if (device == NULL || format == NULL)
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 
80 static void* audin_sndio_thread_func(void* arg)
81 {
82  struct sio_hdl* hdl;
83  struct sio_par par;
84  BYTE* buffer = NULL;
85  size_t n, nbytes;
86  AudinSndioDevice* sndio = (AudinSndioDevice*)arg;
87  UINT error = 0;
88  DWORD status;
89 
90  if (arg == NULL)
91  {
92  error = ERROR_INVALID_PARAMETER;
93  goto err_out;
94  }
95 
96  hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
97  if (hdl == NULL)
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  nbytes =
129  (sndio->FramesPerPacket * sndio->format.nChannels * (sndio->format.wBitsPerSample / 8));
130  buffer = (BYTE*)calloc((nbytes + sizeof(void*)), sizeof(BYTE));
131 
132  if (buffer == NULL)
133  {
134  error = ERROR_NOT_ENOUGH_MEMORY;
135  goto err_out;
136  }
137 
138  while (1)
139  {
140  status = WaitForSingleObject(sndio->stopEvent, 0);
141 
142  if (status == WAIT_FAILED)
143  {
144  error = GetLastError();
145  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
146  goto err_out;
147  }
148 
149  if (status == WAIT_OBJECT_0)
150  break;
151 
152  n = sio_read(hdl, buffer, nbytes);
153 
154  if (n == 0)
155  {
156  WLog_ERR(TAG, "could not read");
157  continue;
158  }
159 
160  if (n < nbytes)
161  continue;
162 
163  if ((error = sndio->receive(&sndio->format, buffer, nbytes, sndio->user_data)))
164  {
165  WLog_ERR(TAG, "sndio->receive failed with error %" PRIu32 "", error);
166  break;
167  }
168  }
169 
170 err_out:
171  if (error && sndio->rdpcontext)
172  setChannelError(sndio->rdpcontext, error, "audin_sndio_thread_func reported an error");
173 
174  if (hdl != NULL)
175  {
176  WLog_INFO(TAG, "sio_close");
177  sio_stop(hdl);
178  sio_close(hdl);
179  }
180 
181  free(buffer);
182  ExitThread(0);
183  return NULL;
184 }
185 
191 static 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(NULL, TRUE, FALSE, NULL)))
198  {
199  WLog_ERR(TAG, "CreateEvent failed");
200  return ERROR_INTERNAL_ERROR;
201  }
202 
203  if (!(sndio->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)audin_sndio_thread_func,
204  sndio, 0, NULL)))
205  {
206  WLog_ERR(TAG, "CreateThread failed");
207  (void)CloseHandle(sndio->stopEvent);
208  sndio->stopEvent = NULL;
209  return ERROR_INTERNAL_ERROR;
210  }
211 
212  return CHANNEL_RC_OK;
213 }
214 
220 static UINT audin_sndio_close(IAudinDevice* device)
221 {
222  UINT error;
223  AudinSndioDevice* sndio = (AudinSndioDevice*)device;
224 
225  if (device == NULL)
226  return ERROR_INVALID_PARAMETER;
227 
228  if (sndio->stopEvent != NULL)
229  {
230  (void)SetEvent(sndio->stopEvent);
231 
232  if (WaitForSingleObject(sndio->thread, INFINITE) == WAIT_FAILED)
233  {
234  error = GetLastError();
235  WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
236  return error;
237  }
238 
239  (void)CloseHandle(sndio->stopEvent);
240  sndio->stopEvent = NULL;
241  (void)CloseHandle(sndio->thread);
242  sndio->thread = NULL;
243  }
244 
245  sndio->receive = NULL;
246  sndio->user_data = NULL;
247 
248  return CHANNEL_RC_OK;
249 }
250 
256 static UINT audin_sndio_free(IAudinDevice* device)
257 {
258  AudinSndioDevice* sndio = (AudinSndioDevice*)device;
259  int error;
260 
261  if (device == NULL)
262  return ERROR_INVALID_PARAMETER;
263 
264  if ((error = audin_sndio_close(device)))
265  {
266  WLog_ERR(TAG, "audin_sndio_close failed with error code %d", error);
267  }
268 
269  free(sndio);
270 
271  return CHANNEL_RC_OK;
272 }
273 
279 static UINT audin_sndio_parse_addin_args(AudinSndioDevice* device, ADDIN_ARGV* args)
280 {
281  int status;
282  DWORD flags;
284  AudinSndioDevice* sndio = (AudinSndioDevice*)device;
285  COMMAND_LINE_ARGUMENT_A audin_sndio_args[] = { { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
286  flags =
287  COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
288  status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_sndio_args,
289  flags, sndio, NULL, NULL);
290 
291  if (status < 0)
292  return ERROR_INVALID_PARAMETER;
293 
294  arg = audin_sndio_args;
295 
296  do
297  {
298  if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
299  continue;
300 
301  CommandLineSwitchStart(arg) CommandLineSwitchEnd(arg)
302  } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
303 
304  return CHANNEL_RC_OK;
305 }
306 
312 FREERDP_ENTRY_POINT(UINT VCAPITYPE sndio_freerdp_audin_client_subsystem_entry(
314 {
315  ADDIN_ARGV* args;
316  AudinSndioDevice* sndio;
317  UINT ret = CHANNEL_RC_OK;
318  sndio = (AudinSndioDevice*)calloc(1, sizeof(AudinSndioDevice));
319 
320  if (sndio == NULL)
321  return CHANNEL_RC_NO_MEMORY;
322 
323  sndio->device.Open = audin_sndio_open;
324  sndio->device.FormatSupported = audin_sndio_format_supported;
325  sndio->device.SetFormat = audin_sndio_set_format;
326  sndio->device.Close = audin_sndio_close;
327  sndio->device.Free = audin_sndio_free;
328  sndio->rdpcontext = pEntryPoints->rdpcontext;
329  args = pEntryPoints->args;
330 
331  if (args->argc > 1)
332  {
333  ret = audin_sndio_parse_addin_args(sndio, args);
334 
335  if (ret != CHANNEL_RC_OK)
336  {
337  WLog_ERR(TAG, "error parsing arguments");
338  goto error;
339  }
340  }
341 
342  if ((ret = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)sndio)))
343  {
344  WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "", ret);
345  goto error;
346  }
347 
348  return ret;
349 error:
350  audin_sndio_free(&sndio->device);
351  return ret;
352 }
Definition: client/audin.h:59