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";
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 buffer = (BYTE*)calloc((buffer_size +
sizeof(
void*)),
sizeof(BYTE));
197 OSS_LOG_ERR(
"malloc() fail", errno);
198 error = ERROR_NOT_ENOUGH_MEMORY;
205 status = WaitForSingleObject(oss->stopEvent, 0);
207 if (status == WAIT_FAILED)
209 error = GetLastError();
210 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
214 if (status == WAIT_OBJECT_0)
217 stmp = read(pcm_handle, buffer, buffer_size);
222 OSS_LOG_ERR(
"read() error", errno);
226 if ((
size_t)stmp < buffer_size)
229 if ((error = oss->receive(&oss->format, buffer, buffer_size, oss->user_data)))
231 WLog_ERR(TAG,
"oss->receive failed with error %" PRIu32
"", error);
238 if (error && oss && oss->rdpcontext)
239 setChannelError(oss->rdpcontext, error,
"audin_oss_thread_func reported an error");
241 if (pcm_handle != -1)
243 WLog_INFO(TAG,
"close: %s", dev_name);
257static UINT audin_oss_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
259 AudinOSSDevice* oss = (AudinOSSDevice*)device;
260 oss->receive = receive;
261 oss->user_data = user_data;
263 if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
265 WLog_ERR(TAG,
"CreateEvent failed!");
266 return ERROR_INTERNAL_ERROR;
269 if (!(oss->thread = CreateThread(NULL, 0, audin_oss_thread_func, oss, 0, NULL)))
271 WLog_ERR(TAG,
"CreateThread failed!");
272 (void)CloseHandle(oss->stopEvent);
273 oss->stopEvent = NULL;
274 return ERROR_INTERNAL_ERROR;
277 return CHANNEL_RC_OK;
285static UINT audin_oss_close(IAudinDevice* device)
288 AudinOSSDevice* oss = (AudinOSSDevice*)device;
291 return ERROR_INVALID_PARAMETER;
293 if (oss->stopEvent != NULL)
295 (void)SetEvent(oss->stopEvent);
297 if (WaitForSingleObject(oss->thread, INFINITE) == WAIT_FAILED)
299 error = GetLastError();
300 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
304 (void)CloseHandle(oss->stopEvent);
305 oss->stopEvent = NULL;
306 (void)CloseHandle(oss->thread);
311 oss->user_data = NULL;
312 return CHANNEL_RC_OK;
320static UINT audin_oss_free(IAudinDevice* device)
322 AudinOSSDevice* oss = (AudinOSSDevice*)device;
326 return ERROR_INVALID_PARAMETER;
328 if ((error = audin_oss_close(device)))
330 WLog_ERR(TAG,
"audin_oss_close failed with error code %" PRIu32
"!", error);
334 return CHANNEL_RC_OK;
342static UINT audin_oss_parse_addin_args(AudinOSSDevice* device,
const ADDIN_ARGV* args)
345 char* str_num = NULL;
349 AudinOSSDevice* oss = device;
351 NULL, NULL, -1, NULL,
"audio device name" },
352 { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
355 COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
357 CommandLineParseArgumentsA(args->argc, args->argv, audin_oss_args, flags, oss, NULL, NULL);
360 return ERROR_INVALID_PARAMETER;
362 arg = audin_oss_args;
367 if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
370 CommandLineSwitchStart(arg) CommandLineSwitchCase(arg,
"dev")
372 str_num = _strdup(arg->Value);
376 WLog_ERR(TAG,
"_strdup failed!");
377 return CHANNEL_RC_NO_MEMORY;
381 long val = strtol(str_num, &eptr, 10);
383 if ((errno != 0) || (val < INT32_MIN) || (val > INT32_MAX))
386 return CHANNEL_RC_NULL_DATA;
389 oss->dev_unit = (INT32)val;
392 if (oss->dev_unit < 0 || *eptr !=
'\0')
397 CommandLineSwitchEnd(arg)
398 }
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
400 return CHANNEL_RC_OK;
408FREERDP_ENTRY_POINT(UINT VCAPITYPE oss_freerdp_audin_client_subsystem_entry(
412 AudinOSSDevice* oss = NULL;
414 oss = (AudinOSSDevice*)calloc(1,
sizeof(AudinOSSDevice));
418 WLog_ERR(TAG,
"calloc failed!");
419 return CHANNEL_RC_NO_MEMORY;
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;
429 args = pEntryPoints->args;
431 if ((error = audin_oss_parse_addin_args(oss, args)))
433 WLog_ERR(TAG,
"audin_oss_parse_addin_args failed with errorcode %" PRIu32
"!", error);
437 if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss)))
439 WLog_ERR(TAG,
"RegisterAudinDevice failed with error %" PRIu32
"!", error);
443 return CHANNEL_RC_OK;