FreeRDP
Loading...
Searching...
No Matches
audin_oss.c
1
22#include <freerdp/config.h>
23
24#include <stdio.h>
25#include <stdlib.h>
26#include <string.h>
27
28#include <winpr/crt.h>
29#include <winpr/synch.h>
30#include <winpr/string.h>
31#include <winpr/thread.h>
32#include <winpr/cmdline.h>
33
34#include <err.h>
35#include <errno.h>
36#include <fcntl.h>
37#include <libgen.h>
38#include <limits.h>
39#include <unistd.h>
40#include <oss-includes.h>
41#include <sys/ioctl.h>
42
43#include <freerdp/freerdp.h>
44#include <freerdp/addin.h>
45#include <freerdp/channels/rdpsnd.h>
46
47#include "audin_main.h"
48
49typedef struct
50{
51 IAudinDevice iface;
52
53 HANDLE thread;
54 HANDLE stopEvent;
55
56 AUDIO_FORMAT format;
57 UINT32 FramesPerPacket;
58 int dev_unit;
59
60 AudinReceive receive;
61 void* user_data;
62
63 rdpContext* rdpcontext;
64} AudinOSSDevice;
65
66static void OSS_LOG_ERR(const char* _text, int _error)
67{
68 if ((_error) != 0)
69 {
70 char buffer[256] = { 0 };
71 WLog_ERR(TAG, "%s: %i - %s\n", (_text), (_error),
72 winpr_strerror((_error), buffer, sizeof(buffer)));
73 }
74}
75
76static UINT32 audin_oss_get_format(const AUDIO_FORMAT* format)
77{
78 switch (format->wFormatTag)
79 {
80 case WAVE_FORMAT_PCM:
81 switch (format->wBitsPerSample)
82 {
83 case 8:
84 return AFMT_S8;
85
86 case 16:
87 return AFMT_S16_LE;
88
89 default:
90 break;
91 }
92
93 break;
94 default:
95 break;
96 }
97
98 return 0;
99}
100
101static BOOL audin_oss_format_supported(IAudinDevice* device, const AUDIO_FORMAT* format)
102{
103 if (device == NULL || format == NULL)
104 return FALSE;
105
106 switch (format->wFormatTag)
107 {
108 case WAVE_FORMAT_PCM:
109 if (format->cbSize != 0 || format->nSamplesPerSec > 48000 ||
110 (format->wBitsPerSample != 8 && format->wBitsPerSample != 16) ||
111 (format->nChannels != 1 && format->nChannels != 2))
112 return FALSE;
113
114 break;
115
116 default:
117 return FALSE;
118 }
119
120 return TRUE;
121}
122
128static UINT audin_oss_set_format(IAudinDevice* device, const AUDIO_FORMAT* format,
129 UINT32 FramesPerPacket)
130{
131 AudinOSSDevice* oss = (AudinOSSDevice*)device;
132
133 if (device == NULL || format == NULL)
134 return ERROR_INVALID_PARAMETER;
135
136 oss->FramesPerPacket = FramesPerPacket;
137 oss->format = *format;
138 return CHANNEL_RC_OK;
139}
140
141static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
142{
143 char dev_name[PATH_MAX] = "/dev/dsp";
144 int pcm_handle = -1;
145 BYTE* buffer = NULL;
146 unsigned long tmp = 0;
147 size_t buffer_size = 0;
148 AudinOSSDevice* oss = (AudinOSSDevice*)arg;
149 UINT error = 0;
150 DWORD status = 0;
151
152 if (oss == NULL)
153 {
154 error = ERROR_INVALID_PARAMETER;
155 goto err_out;
156 }
157
158 if (oss->dev_unit != -1)
159 (void)sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
160
161 WLog_INFO(TAG, "open: %s", dev_name);
162
163 if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
164 {
165 OSS_LOG_ERR("sound dev open failed", errno);
166 error = ERROR_INTERNAL_ERROR;
167 goto err_out;
168 }
169
170 /* Set format. */
171 tmp = audin_oss_get_format(&oss->format);
172
173 if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
174 OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
175
176 tmp = oss->format.nChannels;
177
178 if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
179 OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
180
181 tmp = oss->format.nSamplesPerSec;
182
183 if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
184 OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
185
186 tmp = oss->format.nBlockAlign;
187
188 if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
189 OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
190
191 buffer_size =
192 (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
193 buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
194
195 if (NULL == buffer)
196 {
197 OSS_LOG_ERR("malloc() fail", errno);
198 error = ERROR_NOT_ENOUGH_MEMORY;
199 goto err_out;
200 }
201
202 while (1)
203 {
204 SSIZE_T stmp = -1;
205 status = WaitForSingleObject(oss->stopEvent, 0);
206
207 if (status == WAIT_FAILED)
208 {
209 error = GetLastError();
210 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
211 goto err_out;
212 }
213
214 if (status == WAIT_OBJECT_0)
215 break;
216
217 stmp = read(pcm_handle, buffer, buffer_size);
218
219 /* Error happen. */
220 if (stmp < 0)
221 {
222 OSS_LOG_ERR("read() error", errno);
223 continue;
224 }
225
226 if ((size_t)stmp < buffer_size) /* Not enough data. */
227 continue;
228
229 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
230 {
231 WLog_ERR(TAG, "oss->receive failed with error %" PRIu32 "", error);
232 break;
233 }
234 }
235
236err_out:
237
238 if (error && oss && oss->rdpcontext)
239 setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
240
241 if (pcm_handle != -1)
242 {
243 WLog_INFO(TAG, "close: %s", dev_name);
244 close(pcm_handle);
245 }
246
247 free(buffer);
248 ExitThread(error);
249 return error;
250}
251
257static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
258{
259 AudinOSSDevice* oss = (AudinOSSDevice*)device;
260 oss->receive = receive;
261 oss->user_data = user_data;
262
263 if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
264 {
265 WLog_ERR(TAG, "CreateEvent failed!");
266 return ERROR_INTERNAL_ERROR;
267 }
268
269 if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
270 {
271 WLog_ERR(TAG, "CreateThread failed!");
272 (void)CloseHandle(oss->stopEvent);
273 oss->stopEvent = NULL;
274 return ERROR_INTERNAL_ERROR;
275 }
276
277 return CHANNEL_RC_OK;
278}
279
285static UINT audin_oss_close(IAudinDevice* device)
286{
287 UINT error = 0;
288 AudinOSSDevice* oss = (AudinOSSDevice*)device;
289
290 if (device == NULL)
291 return ERROR_INVALID_PARAMETER;
292
293 if (oss->stopEvent != NULL)
294 {
295 (void)SetEvent(oss->stopEvent);
296
297 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
298 {
299 error = GetLastError();
300 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
301 return error;
302 }
303
304 (void)CloseHandle(oss->stopEvent);
305 oss->stopEvent = NULL;
306 (void)CloseHandle(oss->thread);
307 oss->thread = NULL;
308 }
309
310 oss->receive = NULL;
311 oss->user_data = NULL;
312 return CHANNEL_RC_OK;
313}
314
320static UINT audin_oss_free(IAudinDevice* device)
321{
322 AudinOSSDevice* oss = (AudinOSSDevice*)device;
323 UINT error = 0;
324
325 if (device == NULL)
326 return ERROR_INVALID_PARAMETER;
327
328 if ((error = audin_oss_close(device)))
329 {
330 WLog_ERR(TAG, "audin_oss_close failed with error code %" PRIu32 "!", error);
331 }
332
333 free(oss);
334 return CHANNEL_RC_OK;
335}
336
342static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV* args)
343{
344 int status = 0;
345 char* str_num = NULL;
346 char* eptr = NULL;
347 DWORD flags = 0;
348 const COMMAND_LINE_ARGUMENT_A* arg = NULL;
349 AudinOSSDevice* oss = device;
350 COMMAND_LINE_ARGUMENT_A audin_oss_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
351 NULL, NULL, -1, NULL, "audio device name" },
352 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
353
354 flags =
355 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
356 status =
357 CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
358
359 if (status < 0)
360 return ERROR_INVALID_PARAMETER;
361
362 arg = audin_oss_args;
363 errno = 0;
364
365 do
366 {
367 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
368 continue;
369
370 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
371 {
372 str_num = _strdup(arg->Value);
373
374 if (!str_num)
375 {
376 WLog_ERR(TAG, "_strdup failed!");
377 return CHANNEL_RC_NO_MEMORY;
378 }
379
380 {
381 long val = strtol(str_num, &eptr, 10);
382
383 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
384 {
385 free(str_num);
386 return CHANNEL_RC_NULL_DATA;
387 }
388
389 oss->dev_unit = (INT32)val;
390 }
391
392 if (oss->dev_unit < 0 || *eptr != '\0')
393 oss->dev_unit = -1;
394
395 free(str_num);
396 }
397 CommandLineSwitchEnd(arg)
398 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
399
400 return CHANNEL_RC_OK;
401}
402
408FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
410{
411 const ADDIN_ARGV* args = NULL;
412 AudinOSSDevice* oss = NULL;
413 UINT error = 0;
414 oss = (AudinOSSDevice*)calloc(1, sizeof(AudinOSSDevice));
415
416 if (!oss)
417 {
418 WLog_ERR(TAG, "calloc failed!");
419 return CHANNEL_RC_NO_MEMORY;
420 }
421
422 oss->iface.Open = audin_oss_open;
423 oss->iface.FormatSupported = audin_oss_format_supported;
424 oss->iface.SetFormat = audin_oss_set_format;
425 oss->iface.Close = audin_oss_close;
426 oss->iface.Free = audin_oss_free;
427 oss->rdpcontext = pEntryPoints->rdpcontext;
428 oss->dev_unit = -1;
429 args = pEntryPoints->args;
430
431 if ((error = audin_oss_parse_addin_args(oss, args)))
432 {
433 WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %" PRIu32 "!", error);
434 goto error_out;
435 }
436
437 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
438 {
439 WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
440 goto error_out;
441 }
442
443 return CHANNEL_RC_OK;
444error_out:
445 free(oss);
446 return error;
447}