22#include <freerdp/config.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>
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;
66static 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)));
76static UINT32 audin_oss_get_format(
const AUDIO_FORMAT* format)
78 switch (format->wFormatTag)
81 switch (format->wBitsPerSample)
101static 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))
128static 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;
141static 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);
196 tmp = audin_oss_get_format(&oss->format);
198 if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
199 OSS_LOG_ERR(
"SNDCTL_DSP_SETFMT failed", errno);
201 tmp = oss->format.nChannels;
203 if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
204 OSS_LOG_ERR(
"SNDCTL_DSP_CHANNELS failed", errno);
206 tmp = oss->format.nSamplesPerSec;
208 if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
209 OSS_LOG_ERR(
"SNDCTL_DSP_SPEED failed", errno);
211 tmp = oss->format.nBlockAlign;
213 if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
214 OSS_LOG_ERR(
"SNDCTL_DSP_SETFRAGMENT failed", errno);
217 (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
218 buffer = (BYTE*)calloc((buffer_size +
sizeof(
void*)),
sizeof(BYTE));
222 OSS_LOG_ERR(
"malloc() fail", errno);
223 error = ERROR_NOT_ENOUGH_MEMORY;
230 status = WaitForSingleObject(oss->stopEvent, 0);
232 if (status == WAIT_FAILED)
234 error = GetLastError();
235 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
239 if (status == WAIT_OBJECT_0)
242 stmp = read(pcm_handle, buffer, buffer_size);
247 OSS_LOG_ERR(
"read() error", errno);
251 if ((
size_t)stmp < buffer_size)
254 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
256 WLog_ERR(TAG,
"oss->receive failed with error %" PRIu32
"", error);
263 if (error && oss && oss->rdpcontext)
264 setChannelError(oss->rdpcontext, error,
"audin_oss_thread_func reported an error");
266 if (pcm_handle != -1)
268 WLog_INFO(TAG,
"close: %s", dev_name);
282static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
284 AudinOSSDevice* oss = (AudinOSSDevice*)device;
285 oss->receive = receive;
286 oss->user_data = user_data;
288 if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
290 WLog_ERR(TAG,
"CreateEvent failed!");
291 return ERROR_INTERNAL_ERROR;
294 if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
296 WLog_ERR(TAG,
"CreateThread failed!");
297 (void)CloseHandle(oss->stopEvent);
298 oss->stopEvent = NULL;
299 return ERROR_INTERNAL_ERROR;
302 return CHANNEL_RC_OK;
310static UINT audin_oss_close(IAudinDevice* device)
313 AudinOSSDevice* oss = (AudinOSSDevice*)device;
316 return ERROR_INVALID_PARAMETER;
318 if (oss->stopEvent != NULL)
320 (void)SetEvent(oss->stopEvent);
322 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
324 error = GetLastError();
325 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
329 (void)CloseHandle(oss->stopEvent);
330 oss->stopEvent = NULL;
331 (void)CloseHandle(oss->thread);
336 oss->user_data = NULL;
337 return CHANNEL_RC_OK;
345static UINT audin_oss_free(IAudinDevice* device)
347 AudinOSSDevice* oss = (AudinOSSDevice*)device;
351 return ERROR_INVALID_PARAMETER;
353 if ((error = audin_oss_close(device)))
355 WLog_ERR(TAG,
"audin_oss_close failed with error code %" PRIu32
"!", error);
359 return CHANNEL_RC_OK;
367static UINT audin_oss_parse_addin_args(AudinOSSDevice* device,
const ADDIN_ARGV* args)
370 char* str_num = NULL;
374 AudinOSSDevice* oss = device;
376 NULL, NULL, -1, NULL,
"audio device name" },
377 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
380 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
382 CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
385 return ERROR_INVALID_PARAMETER;
387 arg = audin_oss_args;
392 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
395 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
397 str_num = _strdup(arg->Value);
401 WLog_ERR(TAG,
"_strdup failed!");
402 return CHANNEL_RC_NO_MEMORY;
406 long val = strtol(str_num, &eptr, 10);
408 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
411 return CHANNEL_RC_NULL_DATA;
414 oss->dev_unit = (INT32)val;
417 if (oss->dev_unit < 0 || *eptr !=
'\0')
422 CommandLineSwitchEnd(arg)
423 }
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
425 return CHANNEL_RC_OK;
433FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
437 AudinOSSDevice* oss = NULL;
439 oss = (AudinOSSDevice*)calloc(1,
sizeof(AudinOSSDevice));
443 WLog_ERR(TAG,
"calloc failed!");
444 return CHANNEL_RC_NO_MEMORY;
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;
454 args = pEntryPoints->args;
456 if ((error = audin_oss_parse_addin_args(oss, args)))
458 WLog_ERR(TAG,
"audin_oss_parse_addin_args failed with errorcode %" PRIu32
"!", error);
462 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
464 WLog_ERR(TAG,
"RegisterAudinDevice failed with error %" PRIu32
"!", error);
468 return CHANNEL_RC_OK;