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] = WINPR_C_ARRAY_INIT;
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 ==
nullptr || format ==
nullptr)
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 ==
nullptr || format ==
nullptr)
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";
145 BYTE* buffer =
nullptr;
146 unsigned long tmp = 0;
147 size_t buffer_size = 0;
148 AudinOSSDevice* oss = (AudinOSSDevice*)arg;
154 error = ERROR_INVALID_PARAMETER;
158 if (oss->dev_unit != -1)
159 (void)sprintf_s(dev_name, (PATH_MAX - 1),
"/dev/dsp%i", oss->dev_unit);
161 WLog_INFO(TAG,
"open: %s", dev_name);
163 if ((pcm_handle = open(dev_name, O_RDONLY)) < 0)
165 OSS_LOG_ERR(
"sound dev open failed", errno);
166 error = ERROR_INTERNAL_ERROR;
171 tmp = audin_oss_get_format(&oss->format);
173 if (ioctl(pcm_handle, SNDCTL_DSP_SETFMT, &tmp) == -1)
174 OSS_LOG_ERR(
"SNDCTL_DSP_SETFMT failed", errno);
176 tmp = oss->format.nChannels;
178 if (ioctl(pcm_handle, SNDCTL_DSP_CHANNELS, &tmp) == -1)
179 OSS_LOG_ERR(
"SNDCTL_DSP_CHANNELS failed", errno);
181 tmp = oss->format.nSamplesPerSec;
183 if (ioctl(pcm_handle, SNDCTL_DSP_SPEED, &tmp) == -1)
184 OSS_LOG_ERR(
"SNDCTL_DSP_SPEED failed", errno);
186 tmp = oss->format.nBlockAlign;
188 if (ioctl(pcm_handle, SNDCTL_DSP_SETFRAGMENT, &tmp) == -1)
189 OSS_LOG_ERR(
"SNDCTL_DSP_SETFRAGMENT failed", errno);
192 (1ull * oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8ull));
193 if (buffer_size > INT32_MAX)
196 buffer = (BYTE*)calloc((buffer_size +
sizeof(
void*)),
sizeof(BYTE));
198 if (
nullptr == buffer)
200 OSS_LOG_ERR(
"malloc() fail", errno);
201 error = ERROR_NOT_ENOUGH_MEMORY;
208 status = WaitForSingleObject(oss->stopEvent, 0);
210 if (status == WAIT_FAILED)
212 error = GetLastError();
213 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
217 if (status == WAIT_OBJECT_0)
220 stmp = read(pcm_handle, buffer, buffer_size);
225 OSS_LOG_ERR(
"read() error", errno);
229 if ((
size_t)stmp < buffer_size)
232 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
234 WLog_ERR(TAG,
"oss->receive failed with error %" PRIu32
"", error);
241 if (error && oss && oss->rdpcontext)
242 setChannelError(oss->rdpcontext, error,
"audin_oss_thread_func reported an error");
244 if (pcm_handle != -1)
246 WLog_INFO(TAG,
"close: %s", dev_name);
260static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
262 AudinOSSDevice* oss = (AudinOSSDevice*)device;
263 oss->receive = receive;
264 oss->user_data = user_data;
266 if (!(oss->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
268 WLog_ERR(TAG,
"CreateEvent failed!");
269 return ERROR_INTERNAL_ERROR;
272 if (!(oss->thread = CreateThread(
nullptr, 0, audin_oss_thread_func, oss, 0,
nullptr)))
274 WLog_ERR(TAG,
"CreateThread failed!");
275 (void)CloseHandle(oss->stopEvent);
276 oss->stopEvent =
nullptr;
277 return ERROR_INTERNAL_ERROR;
280 return CHANNEL_RC_OK;
288static UINT audin_oss_close(IAudinDevice* device)
291 AudinOSSDevice* oss = (AudinOSSDevice*)device;
293 if (device ==
nullptr)
294 return ERROR_INVALID_PARAMETER;
296 if (oss->stopEvent !=
nullptr)
298 (void)SetEvent(oss->stopEvent);
300 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
302 error = GetLastError();
303 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
307 (void)CloseHandle(oss->stopEvent);
308 oss->stopEvent =
nullptr;
309 (void)CloseHandle(oss->thread);
310 oss->thread =
nullptr;
313 oss->receive =
nullptr;
314 oss->user_data =
nullptr;
315 return CHANNEL_RC_OK;
323static UINT audin_oss_free(IAudinDevice* device)
325 AudinOSSDevice* oss = (AudinOSSDevice*)device;
328 if (device ==
nullptr)
329 return ERROR_INVALID_PARAMETER;
331 if ((error = audin_oss_close(device)))
333 WLog_ERR(TAG,
"audin_oss_close failed with error code %" PRIu32
"!", error);
337 return CHANNEL_RC_OK;
345static UINT audin_oss_parse_addin_args(AudinOSSDevice* device,
const ADDIN_ARGV* args)
348 char* str_num =
nullptr;
349 char* eptr =
nullptr;
352 AudinOSSDevice* oss = device;
354 {
"dev", COMMAND_LINE_VALUE_REQUIRED,
"<device>",
nullptr,
nullptr, -1,
nullptr,
355 "audio device name" },
356 {
nullptr, 0,
nullptr,
nullptr,
nullptr, -1,
nullptr,
nullptr }
360 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
361 status = CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss,
nullptr,
365 return ERROR_INVALID_PARAMETER;
367 arg = audin_oss_args;
372 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
375 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
377 str_num = _strdup(arg->Value);
381 WLog_ERR(TAG,
"_strdup failed!");
382 return CHANNEL_RC_NO_MEMORY;
386 long val = strtol(str_num, &eptr, 10);
388 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
391 return CHANNEL_RC_NULL_DATA;
394 oss->dev_unit = (INT32)val;
397 if (oss->dev_unit < 0 || *eptr !=
'\0')
402 CommandLineSwitchEnd(arg)
403 }
while ((arg = CommandLineFindNextArgumentA(arg)) !=
nullptr);
405 return CHANNEL_RC_OK;
413FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
417 AudinOSSDevice* oss =
nullptr;
419 oss = (AudinOSSDevice*)calloc(1,
sizeof(AudinOSSDevice));
423 WLog_ERR(TAG,
"calloc failed!");
424 return CHANNEL_RC_NO_MEMORY;
427 oss->iface.Open = audin_oss_open;
428 oss->iface.FormatSupported = audin_oss_format_supported;
429 oss->iface.SetFormat = audin_oss_set_format;
430 oss->iface.Close = audin_oss_close;
431 oss->iface.Free = audin_oss_free;
432 oss->rdpcontext = pEntryPoints->rdpcontext;
434 args = pEntryPoints->args;
436 if ((error = audin_oss_parse_addin_args(oss, args)))
438 WLog_ERR(TAG,
"audin_oss_parse_addin_args failed with errorcode %" PRIu32
"!", error);
442 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
444 WLog_ERR(TAG,
"RegisterAudinDevice failed with error %" PRIu32
"!", error);
448 return CHANNEL_RC_OK;