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 char mixer_name[PATH_MAX] = "/dev/mixer";
145 int pcm_handle = -1;
146 int mixer_handle = 0;
147 BYTE* buffer = NULL;
148 unsigned long tmp = 0;
149 size_t buffer_size = 0;
150 AudinOSSDevice* oss = (AudinOSSDevice*)arg;
151 UINT error = 0;
152 DWORD status = 0;
153
154 if (oss == NULL)
155 {
156 error = ERROR_INVALID_PARAMETER;
157 goto err_out;
158 }
159
160 if (oss->dev_unit != -1)
161 {
162 (void)sprintf_s(dev_name, (PATH_MAX - 1), "/dev/dsp%i", oss->dev_unit);
163 (void)sprintf_s(mixer_name, PATH_MAX - 1, "/dev/mixer%i", oss->dev_unit);
164 }
165
166 WLog_INFO(TAG, "open: %s", dev_name);
167
168 if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
169 {
170 OSS_LOG_ERR("sound dev open failed", errno);
171 error = ERROR_INTERNAL_ERROR;
172 goto err_out;
173 }
174
175 /* Set rec volume to 100%. */
176 if ((mixer_handle = open(mixer_name, O_RDWR)) < 0)
177 {
178 OSS_LOG_ERR("mixer open failed, not critical", errno);
179 }
180 else
181 {
182 tmp = (100 | (100 << 8));
183
184 if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_MIC), &tmp) == -1)
185 OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_MIC, not critical", errno);
186
187 tmp = (100 | (100 << 8));
188
189 if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_RECLEV), &tmp) == -1)
190 OSS_LOG_ERR("WRITE_MIXER - SOUND_MIXER_RECLEV, not critical", errno);
191
192 close(mixer_handle);
193 }
194
195 /* Set format. */
196 tmp = audin_oss_get_format(&oss->format);
197
198 if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
199 OSS_LOG_ERR("SNDCTL_DSP_SETFMT failed", errno);
200
201 tmp = oss->format.nChannels;
202
203 if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
204 OSS_LOG_ERR("SNDCTL_DSP_CHANNELS failed", errno);
205
206 tmp = oss->format.nSamplesPerSec;
207
208 if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
209 OSS_LOG_ERR("SNDCTL_DSP_SPEED failed", errno);
210
211 tmp = oss->format.nBlockAlign;
212
213 if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
214 OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
215
216 buffer_size =
217 (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
218 buffer = (BYTE*)calloc((buffer_size + sizeof(void*)), sizeof(BYTE));
219
220 if (NULL == buffer)
221 {
222 OSS_LOG_ERR("malloc() fail", errno);
223 error = ERROR_NOT_ENOUGH_MEMORY;
224 goto err_out;
225 }
226
227 while (1)
228 {
229 SSIZE_T stmp = -1;
230 status = WaitForSingleObject(oss->stopEvent, 0);
231
232 if (status == WAIT_FAILED)
233 {
234 error = GetLastError();
235 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
236 goto err_out;
237 }
238
239 if (status == WAIT_OBJECT_0)
240 break;
241
242 stmp = read(pcm_handle, buffer, buffer_size);
243
244 /* Error happen. */
245 if (stmp < 0)
246 {
247 OSS_LOG_ERR("read() error", errno);
248 continue;
249 }
250
251 if ((size_t)stmp < buffer_size) /* Not enough data. */
252 continue;
253
254 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
255 {
256 WLog_ERR(TAG, "oss->receive failed with error %" PRIu32 "", error);
257 break;
258 }
259 }
260
261err_out:
262
263 if (error && oss && oss->rdpcontext)
264 setChannelError(oss->rdpcontext, error, "audin_oss_thread_func reported an error");
265
266 if (pcm_handle != -1)
267 {
268 WLog_INFO(TAG, "close: %s", dev_name);
269 close(pcm_handle);
270 }
271
272 free(buffer);
273 ExitThread(error);
274 return error;
275}
276
282static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive, void* user_data)
283{
284 AudinOSSDevice* oss = (AudinOSSDevice*)device;
285 oss->receive = receive;
286 oss->user_data = user_data;
287
288 if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
289 {
290 WLog_ERR(TAG, "CreateEvent failed!");
291 return ERROR_INTERNAL_ERROR;
292 }
293
294 if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
295 {
296 WLog_ERR(TAG, "CreateThread failed!");
297 (void)CloseHandle(oss->stopEvent);
298 oss->stopEvent = NULL;
299 return ERROR_INTERNAL_ERROR;
300 }
301
302 return CHANNEL_RC_OK;
303}
304
310static UINT audin_oss_close(IAudinDevice* device)
311{
312 UINT error = 0;
313 AudinOSSDevice* oss = (AudinOSSDevice*)device;
314
315 if (device == NULL)
316 return ERROR_INVALID_PARAMETER;
317
318 if (oss->stopEvent != NULL)
319 {
320 (void)SetEvent(oss->stopEvent);
321
322 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
323 {
324 error = GetLastError();
325 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
326 return error;
327 }
328
329 (void)CloseHandle(oss->stopEvent);
330 oss->stopEvent = NULL;
331 (void)CloseHandle(oss->thread);
332 oss->thread = NULL;
333 }
334
335 oss->receive = NULL;
336 oss->user_data = NULL;
337 return CHANNEL_RC_OK;
338}
339
345static UINT audin_oss_free(IAudinDevice* device)
346{
347 AudinOSSDevice* oss = (AudinOSSDevice*)device;
348 UINT error = 0;
349
350 if (device == NULL)
351 return ERROR_INVALID_PARAMETER;
352
353 if ((error = audin_oss_close(device)))
354 {
355 WLog_ERR(TAG, "audin_oss_close failed with error code %" PRIu32 "!", error);
356 }
357
358 free(oss);
359 return CHANNEL_RC_OK;
360}
361
367static UINT audin_oss_parse_addin_args(AudinOSSDevice* device, const ADDIN_ARGV* args)
368{
369 int status = 0;
370 char* str_num = NULL;
371 char* eptr = NULL;
372 DWORD flags = 0;
373 const COMMAND_LINE_ARGUMENT_A* arg = NULL;
374 AudinOSSDevice* oss = device;
375 COMMAND_LINE_ARGUMENT_A audin_oss_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>",
376 NULL, NULL, -1, NULL, "audio device name" },
377 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
378
379 flags =
380 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
381 status =
382 CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
383
384 if (status < 0)
385 return ERROR_INVALID_PARAMETER;
386
387 arg = audin_oss_args;
388 errno = 0;
389
390 do
391 {
392 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
393 continue;
394
395 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "dev")
396 {
397 str_num = _strdup(arg->Value);
398
399 if (!str_num)
400 {
401 WLog_ERR(TAG, "_strdup failed!");
402 return CHANNEL_RC_NO_MEMORY;
403 }
404
405 {
406 long val = strtol(str_num, &eptr, 10);
407
408 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
409 {
410 free(str_num);
411 return CHANNEL_RC_NULL_DATA;
412 }
413
414 oss->dev_unit = (INT32)val;
415 }
416
417 if (oss->dev_unit < 0 || *eptr != '\0')
418 oss->dev_unit = -1;
419
420 free(str_num);
421 }
422 CommandLineSwitchEnd(arg)
423 } while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
424
425 return CHANNEL_RC_OK;
426}
427
433FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
435{
436 const ADDIN_ARGV* args = NULL;
437 AudinOSSDevice* oss = NULL;
438 UINT error = 0;
439 oss = (AudinOSSDevice*)calloc(1, sizeof(AudinOSSDevice));
440
441 if (!oss)
442 {
443 WLog_ERR(TAG, "calloc failed!");
444 return CHANNEL_RC_NO_MEMORY;
445 }
446
447 oss->iface.Open = audin_oss_open;
448 oss->iface.FormatSupported = audin_oss_format_supported;
449 oss->iface.SetFormat = audin_oss_set_format;
450 oss->iface.Close = audin_oss_close;
451 oss->iface.Free = audin_oss_free;
452 oss->rdpcontext = pEntryPoints->rdpcontext;
453 oss->dev_unit = -1;
454 args = pEntryPoints->args;
455
456 if ((error = audin_oss_parse_addin_args(oss, args)))
457 {
458 WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %" PRIu32 "!", error);
459 goto error_out;
460 }
461
462 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
463 {
464 WLog_ERR(TAG, "RegisterAudinDevice failed with error %" PRIu32 "!", error);
465 goto error_out;
466 }
467
468 return CHANNEL_RC_OK;
469error_out:
470 free(oss);
471 return error;
472}