22 #include <freerdp/config.h>
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>
40 #include <oss-includes.h>
41 #include <sys/ioctl.h>
43 #include <freerdp/freerdp.h>
44 #include <freerdp/addin.h>
45 #include <freerdp/channels/rdpsnd.h>
47 #include "audin_main.h"
57 UINT32 FramesPerPacket;
63 rdpContext* rdpcontext;
66 static void OSS_LOG_ERR(
const char* _text,
int _error)
70 char buffer[256] = { 0 };
71 WLog_ERR(TAG,
"%s: %i - %s\n", (_text), (_error),
72 winpr_strerror((_error), buffer,
sizeof(buffer)));
76 static UINT32 audin_oss_get_format(
const AUDIO_FORMAT* format)
78 switch (format->wFormatTag)
81 switch (format->wBitsPerSample)
101 static BOOL audin_oss_format_supported(IAudinDevice* device,
const AUDIO_FORMAT* format)
103 if (device == NULL || format == NULL)
106 switch (format->wFormatTag)
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))
128 static UINT audin_oss_set_format(IAudinDevice* device,
const AUDIO_FORMAT* format,
129 UINT32 FramesPerPacket)
131 AudinOSSDevice* oss = (AudinOSSDevice*)device;
133 if (device == NULL || format == NULL)
134 return ERROR_INVALID_PARAMETER;
136 oss->FramesPerPacket = FramesPerPacket;
137 oss->format = *format;
138 return CHANNEL_RC_OK;
141 static DWORD WINAPI audin_oss_thread_func(LPVOID arg)
143 char dev_name[PATH_MAX] =
"/dev/dsp";
144 char mixer_name[PATH_MAX] =
"/dev/mixer";
146 int mixer_handle = 0;
148 unsigned long tmp = 0;
149 size_t buffer_size = 0;
150 AudinOSSDevice* oss = (AudinOSSDevice*)arg;
156 error = ERROR_INVALID_PARAMETER;
160 if (oss->dev_unit != -1)
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);
166 WLog_INFO(TAG,
"open: %s", dev_name);
168 if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
170 OSS_LOG_ERR(
"sound dev open failed", errno);
171 error = ERROR_INTERNAL_ERROR;
176 if ((mixer_handle = open(mixer_name, O_RDWR)) < 0)
178 OSS_LOG_ERR(
"mixer open failed, not critical", errno);
182 tmp = (100 | (100 << 8));
184 if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_MIC), &tmp) == -1)
185 OSS_LOG_ERR(
"WRITE_MIXER - SOUND_MIXER_MIC, not critical", errno);
187 tmp = (100 | (100 << 8));
189 if (ioctl(mixer_handle, MIXER_WRITE(SOUND_MIXER_RECLEV), &tmp) == -1)
190 OSS_LOG_ERR(
"WRITE_MIXER - SOUND_MIXER_RECLEV, not critical", errno);
198 if (ioctl(pcm_handle, SNDCTL_DSP_GETCAPS, &tmp) == -1)
200 OSS_LOG_ERR(
"SNDCTL_DSP_GETCAPS failed, try ignory", errno);
202 else if ((tmp & PCM_CAP_INPUT) == 0)
204 OSS_LOG_ERR(
"Device does not supports playback", EOPNOTSUPP);
210 tmp = audin_oss_get_format(&oss->format);
212 if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
213 OSS_LOG_ERR(
"SNDCTL_DSP_SETFMT failed", errno);
215 tmp = oss->format.nChannels;
217 if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
218 OSS_LOG_ERR(
"SNDCTL_DSP_CHANNELS failed", errno);
220 tmp = oss->format.nSamplesPerSec;
222 if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
223 OSS_LOG_ERR(
"SNDCTL_DSP_SPEED failed", errno);
225 tmp = oss->format.nBlockAlign;
227 if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
228 OSS_LOG_ERR(
"SNDCTL_DSP_SETFRAGMENT failed", errno);
231 (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
232 buffer = (BYTE*)calloc((buffer_size +
sizeof(
void*)),
sizeof(BYTE));
236 OSS_LOG_ERR(
"malloc() fail", errno);
237 error = ERROR_NOT_ENOUGH_MEMORY;
244 status = WaitForSingleObject(oss->stopEvent, 0);
246 if (status == WAIT_FAILED)
248 error = GetLastError();
249 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
253 if (status == WAIT_OBJECT_0)
256 stmp = read(pcm_handle, buffer, buffer_size);
261 OSS_LOG_ERR(
"read() error", errno);
265 if ((
size_t)stmp < buffer_size)
268 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
270 WLog_ERR(TAG,
"oss->receive failed with error %" PRIu32
"", error);
277 if (error && oss && oss->rdpcontext)
278 setChannelError(oss->rdpcontext, error,
"audin_oss_thread_func reported an error");
280 if (pcm_handle != -1)
282 WLog_INFO(TAG,
"close: %s", dev_name);
296 static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
298 AudinOSSDevice* oss = (AudinOSSDevice*)device;
299 oss->receive = receive;
300 oss->user_data = user_data;
302 if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
304 WLog_ERR(TAG,
"CreateEvent failed!");
305 return ERROR_INTERNAL_ERROR;
308 if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
310 WLog_ERR(TAG,
"CreateThread failed!");
311 (void)CloseHandle(oss->stopEvent);
312 oss->stopEvent = NULL;
313 return ERROR_INTERNAL_ERROR;
316 return CHANNEL_RC_OK;
324 static UINT audin_oss_close(IAudinDevice* device)
327 AudinOSSDevice* oss = (AudinOSSDevice*)device;
330 return ERROR_INVALID_PARAMETER;
332 if (oss->stopEvent != NULL)
334 (void)SetEvent(oss->stopEvent);
336 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
338 error = GetLastError();
339 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
343 (void)CloseHandle(oss->stopEvent);
344 oss->stopEvent = NULL;
345 (void)CloseHandle(oss->thread);
350 oss->user_data = NULL;
351 return CHANNEL_RC_OK;
359 static UINT audin_oss_free(IAudinDevice* device)
361 AudinOSSDevice* oss = (AudinOSSDevice*)device;
365 return ERROR_INVALID_PARAMETER;
367 if ((error = audin_oss_close(device)))
369 WLog_ERR(TAG,
"audin_oss_close failed with error code %" PRIu32
"!", error);
373 return CHANNEL_RC_OK;
381 static UINT audin_oss_parse_addin_args(AudinOSSDevice* device,
const ADDIN_ARGV* args)
384 char* str_num = NULL;
388 AudinOSSDevice* oss = device;
390 NULL, NULL, -1, NULL,
"audio device name" },
391 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
394 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
396 CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
399 return ERROR_INVALID_PARAMETER;
401 arg = audin_oss_args;
406 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
409 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
411 str_num = _strdup(arg->Value);
415 WLog_ERR(TAG,
"_strdup failed!");
416 return CHANNEL_RC_NO_MEMORY;
420 long val = strtol(str_num, &eptr, 10);
422 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
425 return CHANNEL_RC_NULL_DATA;
428 oss->dev_unit = (INT32)val;
431 if (oss->dev_unit < 0 || *eptr !=
'\0')
436 CommandLineSwitchEnd(arg)
437 }
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
439 return CHANNEL_RC_OK;
447 FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
451 AudinOSSDevice* oss = NULL;
453 oss = (AudinOSSDevice*)calloc(1,
sizeof(AudinOSSDevice));
457 WLog_ERR(TAG,
"calloc failed!");
458 return CHANNEL_RC_NO_MEMORY;
461 oss->iface.Open = audin_oss_open;
462 oss->iface.FormatSupported = audin_oss_format_supported;
463 oss->iface.SetFormat = audin_oss_set_format;
464 oss->iface.Close = audin_oss_close;
465 oss->iface.Free = audin_oss_free;
466 oss->rdpcontext = pEntryPoints->rdpcontext;
468 args = pEntryPoints->args;
470 if ((error = audin_oss_parse_addin_args(oss, args)))
472 WLog_ERR(TAG,
"audin_oss_parse_addin_args failed with errorcode %" PRIu32
"!", error);
476 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
478 WLog_ERR(TAG,
"RegisterAudinDevice failed with error %" PRIu32
"!", error);
482 return CHANNEL_RC_OK;