hardend channel audin

This commit is contained in:
Martin Haimberger 2015-06-02 00:50:38 -07:00
parent d06da4f1bd
commit 156f86e487
10 changed files with 929 additions and 314 deletions

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel - ALSA implementation
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -98,11 +100,11 @@ static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_hand
return TRUE;
}
static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size)
static WIN32ERROR audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size)
{
int frames;
int cframes;
int ret = 0;
WIN32ERROR ret = CHANNEL_RC_OK;
int encoded_size;
BYTE* encoded_data;
int rbytes_per_frame;
@ -174,7 +176,7 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
alsa->buffer_frames = 0;
if (!ret)
if (ret != CHANNEL_RC_OK)
break;
}
@ -182,12 +184,12 @@ static BOOL audin_alsa_thread_receive(AudinALSADevice* alsa, BYTE* src, int size
frames -= cframes;
}
return (ret) ? TRUE : FALSE;
return ret;
}
static void* audin_alsa_thread_func(void* arg)
{
int error;
long error;
BYTE* buffer;
int rbytes_per_frame;
int tbytes_per_frame;
@ -198,8 +200,15 @@ static void* audin_alsa_thread_func(void* arg)
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
buffer = (BYTE*) malloc(rbytes_per_frame * alsa->frames_per_packet);
ZeroMemory(buffer, rbytes_per_frame * alsa->frames_per_packet);
buffer = (BYTE*) calloc(1, rbytes_per_frame * alsa->frames_per_packet);
if (!buffer)
{
WLog_ERR(TAG, "calloc failed!");
//TODO: signal error to freerdp
ExitThread((DWORD)CHANNEL_RC_NO_MEMORY);
return NULL;
}
freerdp_dsp_context_reset_adpcm(alsa->dsp_context);
do
@ -230,9 +239,13 @@ static void* audin_alsa_thread_func(void* arg)
break;
}
if (!audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame))
if ((error = audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame)))
{
WLog_ERR(TAG, "audin_alsa_thread_receive failed with error %lu", error);
break;
}
}
}
while (0);
@ -242,12 +255,12 @@ static void* audin_alsa_thread_func(void* arg)
snd_pcm_close(capture_handle);
DEBUG_DVC("out");
ExitThread(0);
ExitThread((DWORD)error);
return NULL;
}
static void audin_alsa_free(IAudinDevice* device)
static WIN32ERROR audin_alsa_free(IAudinDevice* device)
{
AudinALSADevice* alsa = (AudinALSADevice*) device;
@ -256,6 +269,7 @@ static void audin_alsa_free(IAudinDevice* device)
free(alsa->device_name);
free(alsa);
return CHANNEL_RC_OK;
}
static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* format)
@ -285,7 +299,7 @@ static BOOL audin_alsa_format_supported(IAudinDevice* device, audinFormat* forma
return FALSE;
}
static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
static WIN32ERROR audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
AudinALSADevice* alsa = (AudinALSADevice*) device;
@ -324,36 +338,51 @@ static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, UIN
alsa->wformat = format->wFormatTag;
alsa->block_size = format->nBlockAlign;
return CHANNEL_RC_OK;
}
static void audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
static WIN32ERROR audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
int rbytes_per_frame;
int tbytes_per_frame;
AudinALSADevice* alsa = (AudinALSADevice*) device;
DEBUG_DVC("");
alsa->receive = receive;
alsa->user_data = user_data;
rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
alsa->buffer = (BYTE*) malloc(tbytes_per_frame * alsa->frames_per_packet);
ZeroMemory(alsa->buffer, tbytes_per_frame * alsa->frames_per_packet);
alsa->buffer = (BYTE*) calloc(1, tbytes_per_frame * alsa->frames_per_packet);
if (!alsa->buffer)
{
WLog_ERR(TAG, "calloc failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
alsa->buffer_frames = 0;
alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
alsa->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_alsa_thread_func, alsa, 0, NULL);
if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
goto error_out;
}
// TODO: add mechanism that threads can signal failure
if (!(alsa->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_alsa_thread_func, alsa, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(alsa->buffer);
alsa->buffer = NULL;
CloseHandle(alsa->stopEvent);
alsa->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
static void audin_alsa_close(IAudinDevice* device)
static WIN32ERROR audin_alsa_close(IAudinDevice* device)
{
AudinALSADevice* alsa = (AudinALSADevice*) device;
DEBUG_DVC("");
if (alsa->stopEvent)
{
SetEvent(alsa->stopEvent);
@ -371,6 +400,8 @@ static void audin_alsa_close(IAudinDevice* device)
alsa->receive = NULL;
alsa->user_data = NULL;
return CHANNEL_RC_OK;
}
COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
@ -379,7 +410,7 @@ COMMAND_LINE_ARGUMENT_A audin_alsa_args[] =
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static void audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* args)
static WIN32ERROR audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
@ -402,24 +433,36 @@ static void audin_alsa_parse_addin_args(AudinALSADevice* device, ADDIN_ARGV* arg
CommandLineSwitchCase(arg, "dev")
{
alsa->device_name = _strdup(arg->Value);
if(!alsa->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry alsa_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
WIN32ERROR freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinALSADevice* alsa;
WIN32ERROR error;
alsa = (AudinALSADevice*) malloc(sizeof(AudinALSADevice));
ZeroMemory(alsa, sizeof(AudinALSADevice));
alsa = (AudinALSADevice*) calloc(1, sizeof(AudinALSADevice));
if (!alsa)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
alsa->iface.Open = audin_alsa_open;
alsa->iface.FormatSupported = audin_alsa_format_supported;
@ -429,10 +472,22 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
args = pEntryPoints->args;
audin_alsa_parse_addin_args(alsa, args);
if ((error = audin_alsa_parse_addin_args(alsa, args)))
{
WLog_ERR(TAG, "audin_alsa_parse_addin_args failed with errorcode %lu!", error);
goto error_out;
}
if (!alsa->device_name)
{
alsa->device_name = _strdup("default");
if (!alsa->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
alsa->frames_per_packet = 128;
alsa->target_rate = 22050;
@ -443,8 +498,23 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
alsa->bytes_per_channel = 2;
alsa->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa);
return 0;
if (!alsa->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(alsa->dsp_context);
free(alsa->device_name);
free(alsa);
return error;
}

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +34,7 @@
#include <freerdp/addin.h>
#include <winpr/stream.h>
#include <winpr/win32error.h>
#include "audin_main.h"
#define MSG_SNDIN_VERSION 0x01
@ -88,9 +90,9 @@ struct _AUDIN_PLUGIN
IAudinDevice* device;
};
static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
static WIN32ERROR audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
int error;
WIN32ERROR error;
wStream* out;
UINT32 Version;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
@ -100,6 +102,13 @@ static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, w
DEBUG_DVC("process_version: Version=%d", Version);
out = Stream_New(NULL, 5);
if (!out)
{
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_OUTOFMEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_VERSION);
Stream_Write_UINT32(out, Version);
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(out), Stream_Buffer(out), NULL);
@ -108,7 +117,7 @@ static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, w
return error;
}
static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
static WIN32ERROR audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
{
BYTE out_data[1];
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
@ -117,13 +126,13 @@ static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCall
return callback->channel->Write(callback->channel, 1, out_data, NULL);
}
static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
static WIN32ERROR audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
UINT32 i;
BYTE* fm;
int error;
WIN32ERROR error;
wStream* out;
UINT32 NumFormats;
audinFormat format;
@ -134,14 +143,26 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
if ((NumFormats < 1) || (NumFormats > 1000))
{
WLog_ERR(TAG, "bad NumFormats %d", NumFormats);
return 1;
return ERROR_INVALID_DATA;
}
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket */
callback->formats = (audinFormat*) malloc(NumFormats * sizeof(audinFormat));
ZeroMemory(callback->formats, NumFormats * sizeof(audinFormat));
callback->formats = (audinFormat*) calloc(1, NumFormats * sizeof(audinFormat));
if (!callback->formats)
{
WLog_ERR(TAG, "calloc failed!");
return ERROR_INVALID_DATA;
}
out = Stream_New(NULL, 9);
if (!out)
{
error = CHANNEL_RC_NO_MEMORY;
WLog_ERR(TAG, "Stream_New failed!");
goto out;
}
Stream_Seek(out, 9);
/* SoundFormats (variable) */
@ -178,9 +199,9 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
/* Put the format to output buffer */
if (!Stream_EnsureRemainingCapacity(out, 18 + format.cbSize))
{
free(callback->formats);
Stream_Free(out, TRUE);
return 1;
error = CHANNEL_RC_NO_MEMORY;
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
goto out;
}
@ -188,7 +209,11 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
}
}
audin_send_incoming_data_pdu(pChannelCallback);
if ((error = audin_send_incoming_data_pdu(pChannelCallback)))
{
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
goto out;
}
cbSizeFormatsPacket = (UINT32) Stream_GetPosition(out);
Stream_SetPosition(out, 0);
@ -198,18 +223,31 @@ static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, w
Stream_Write_UINT32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
error = callback->channel->Write(callback->channel, cbSizeFormatsPacket, Stream_Buffer(out), NULL);
out:
if (error != CHANNEL_RC_OK)
{
free(callback->formats);
callback->formats = NULL;
}
Stream_Free(out, TRUE);
return error;
}
static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 NewFormat)
static WIN32ERROR audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 NewFormat)
{
int error;
WIN32ERROR error;
wStream* out;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
out = Stream_New(NULL, 5);
if (!out)
{
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_OK;
}
Stream_Write_UINT8(out, MSG_SNDIN_FORMATCHANGE);
Stream_Write_UINT32(out, NewFormat);
error = callback->channel->Write(callback->channel, 5, Stream_Buffer(out), NULL);
@ -218,13 +256,20 @@ static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCall
return error;
}
static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 Result)
static WIN32ERROR audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, UINT32 Result)
{
int error;
WIN32ERROR error;
wStream* out;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
out = Stream_New(NULL, 5);
if (!out)
{
WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_OPEN_REPLY);
Stream_Write_UINT32(out, Result);
error = callback->channel->Write(callback->channel, 5, Stream_Buffer(out), NULL);
@ -233,33 +278,42 @@ static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallbac
return error;
}
static BOOL audin_receive_wave_data(BYTE* data, int size, void* user_data)
static WIN32ERROR audin_receive_wave_data(BYTE* data, int size, void* user_data)
{
int error;
WIN32ERROR error;
wStream* out;
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback);
if (error != 0)
return FALSE;
if ((error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback)))
{
WLog_ERR(TAG, "audin_send_incoming_data_pdu failed!");
return error;
}
out = Stream_New(NULL, size + 1);
if (!out)
{
WLog_ERR(TAG, "Stream_New failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
Stream_Write_UINT8(out, MSG_SNDIN_DATA);
Stream_Write(out, data, size);
error = callback->channel->Write(callback->channel, (UINT32) Stream_GetPosition(out), Stream_Buffer(out), NULL);
Stream_Free(out, TRUE);
return (error == 0 ? TRUE : FALSE);
return error;
}
static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
static WIN32ERROR audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
audinFormat* format;
UINT32 initialFormat;
UINT32 FramesPerPacket;
WIN32ERROR error = CHANNEL_RC_OK;
Stream_Read_UINT32(s, FramesPerPacket);
Stream_Read_UINT32(s, initialFormat);
@ -271,28 +325,45 @@ static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, wStr
{
WLog_ERR(TAG, "invalid format index %d (total %d)",
initialFormat, callback->formats_count);
return 1;
return ERROR_INVALID_DATA;
}
format = &callback->formats[initialFormat];
if (audin->device)
{
IFCALL(audin->device->SetFormat, audin->device, format, FramesPerPacket);
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
IFCALLRET(audin->device->SetFormat, error, audin->device, format, FramesPerPacket);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "SetFormat failed with errorcode %lu", error);
return error;
}
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "Open failed with errorcode %lu", error);
return error;
}
}
audin_send_format_change_pdu(pChannelCallback, initialFormat);
audin_send_open_reply_pdu(pChannelCallback, 0);
return 0;
if ((error = audin_send_format_change_pdu(pChannelCallback, initialFormat)))
{
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
return error;
}
static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
if ((error = audin_send_open_reply_pdu(pChannelCallback, 0)))
WLog_ERR(TAG, "audin_send_open_reply_pdu failed!");
return error;
}
static WIN32ERROR audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN * audin = (AUDIN_PLUGIN*) callback->plugin;
UINT32 NewFormat;
audinFormat* format;
WIN32ERROR error = CHANNEL_RC_OK;
Stream_Read_UINT32(s, NewFormat);
@ -302,26 +373,42 @@ static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallb
{
WLog_ERR(TAG, "invalid format index %d (total %d)",
NewFormat, callback->formats_count);
return 1;
return ERROR_INVALID_DATA;
}
format = &callback->formats[NewFormat];
if (audin->device)
{
IFCALL(audin->device->Close, audin->device);
IFCALL(audin->device->SetFormat, audin->device, format, 0);
IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
}
audin_send_format_change_pdu(pChannelCallback, NewFormat);
return 0;
}
static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
IFCALLRET(audin->device->Close, error, audin->device);
if (error != CHANNEL_RC_OK)
{
int error;
WLog_ERR(TAG, "Close failed with errorcode %lu", error);
return error;
}
IFCALLRET(audin->device->SetFormat, error, audin->device, format, 0);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "SetFormat failed with errorcode %lu", error);
return error;
}
IFCALLRET(audin->device->Open, error, audin->device, audin_receive_wave_data, callback);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "Open failed with errorcode %lu", error);
return error;
}
}
if ((error = audin_send_format_change_pdu(pChannelCallback, NewFormat)))
WLog_ERR(TAG, "audin_send_format_change_pdu failed!");
return error;
}
static WIN32ERROR audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data)
{
WIN32ERROR error;
BYTE MessageId;
Stream_Read_UINT8(data, MessageId);
@ -348,30 +435,38 @@ static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
default:
WLog_ERR(TAG, "unknown MessageId=0x%x", MessageId);
error = 1;
error = ERROR_INVALID_DATA;
break;
}
return error;
}
static int audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
static WIN32ERROR audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
{
AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
WIN32ERROR error = CHANNEL_RC_OK;
DEBUG_DVC("on_close");
if (audin->device)
IFCALL(audin->device->Close, audin->device);
{
IFCALLRET(audin->device->Close, error, audin->device);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "Close failed with errorcode %lu", error);
return error;
}
}
free(callback->formats);
free(callback);
return 0;
return error;
}
static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
static WIN32ERROR audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
IWTSVirtualChannelCallback** ppCallback)
{
@ -380,8 +475,12 @@ static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
DEBUG_DVC("on_new_channel_connection");
callback = (AUDIN_CHANNEL_CALLBACK*) malloc(sizeof(AUDIN_CHANNEL_CALLBACK));
ZeroMemory(callback, sizeof(AUDIN_CHANNEL_CALLBACK));
callback = (AUDIN_CHANNEL_CALLBACK*) calloc(1, sizeof(AUDIN_CHANNEL_CALLBACK));
if (!callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
callback->iface.OnDataReceived = audin_on_data_received;
callback->iface.OnClose = audin_on_close;
@ -391,17 +490,21 @@ static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallba
*ppCallback = (IWTSVirtualChannelCallback*) callback;
return 0;
return CHANNEL_RC_OK;
}
static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
static WIN32ERROR audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
{
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
DEBUG_DVC("plugin_initialize");
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) malloc(sizeof(AUDIN_LISTENER_CALLBACK));
ZeroMemory(audin->listener_callback, sizeof(AUDIN_LISTENER_CALLBACK));
audin->listener_callback = (AUDIN_LISTENER_CALLBACK*) calloc(1, sizeof(AUDIN_LISTENER_CALLBACK));
if (!audin->listener_callback)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
audin->listener_callback->plugin = pPlugin;
@ -411,15 +514,21 @@ static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
(IWTSListenerCallback*) audin->listener_callback, NULL);
}
static int audin_plugin_terminated(IWTSPlugin* pPlugin)
static WIN32ERROR audin_plugin_terminated(IWTSPlugin* pPlugin)
{
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
WIN32ERROR error = CHANNEL_RC_OK;
DEBUG_DVC("plugin_terminated");
if (audin->device)
{
IFCALL(audin->device->Free, audin->device);
IFCALLRET(audin->device->Free, error, audin->device);
if (error != CHANNEL_RC_OK)
{
WLog_ERR(TAG, "Free failed with errorcode %lu", error);
// dont stop on error
}
audin->device = NULL;
}
@ -432,57 +541,74 @@ static int audin_plugin_terminated(IWTSPlugin* pPlugin)
free(audin->listener_callback);
free(audin);
return 0;
return CHANNEL_RC_OK;
}
static void audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
static WIN32ERROR audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
{
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
if (audin->device)
{
WLog_ERR(TAG, "existing device, abort.");
return;
return ERROR_ALREADY_EXISTS;
}
DEBUG_DVC("register_device_plugin: device registered.");
audin->device = device;
return CHANNEL_RC_OK;
}
static BOOL audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
static WIN32ERROR audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDIN_ARGV* args)
{
PFREERDP_AUDIN_DEVICE_ENTRY entry;
FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
WIN32ERROR error;
entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_channel_addin_entry("audin", (LPSTR) name, NULL, 0);
if (entry == NULL)
return FALSE;
{
WLog_ERR(TAG, "freerdp_load_channel_addin_entry did not return any function pointers for %s ", name);
return ERROR_INVALID_FUNCTION;
}
entryPoints.plugin = pPlugin;
entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
entryPoints.args = args;
if (entry(&entryPoints) != 0)
if ((error = entry(&entryPoints)))
{
WLog_ERR(TAG, "%s entry returns error.", name);
return FALSE;
WLog_ERR(TAG, "%s entry returned error %lu.", name, error);
return error;
}
return TRUE;
return CHANNEL_RC_OK;
}
void audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
WIN32ERROR audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
{
free(audin->subsystem);
audin->subsystem = _strdup(subsystem);
if (!audin->subsystem)
{
WLog_ERR(TAG, "_strdup failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
return CHANNEL_RC_OK;
}
void audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
WIN32ERROR audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
{
free(audin->device_name);
audin->device_name = _strdup(device_name);
if (!audin->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return ERROR_NOT_ENOUGH_MEMORY;
}
return CHANNEL_RC_OK;
}
COMMAND_LINE_ARGUMENT_A audin_args[] =
@ -501,6 +627,7 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
WIN32ERROR error;
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON;
@ -518,11 +645,19 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
CommandLineSwitchCase(arg, "sys")
{
audin_set_subsystem(audin, arg->Value);
if ((error = audin_set_subsystem(audin, arg->Value)))
{
WLog_ERR(TAG, "audin_set_subsystem failed with error %lu!", error);
return FALSE;
}
}
CommandLineSwitchCase(arg, "dev")
{
audin_set_device_name(audin, arg->Value);
if ((error = audin_set_device_name(audin, arg->Value)))
{
WLog_ERR(TAG, "audin_set_device_name failed with error %lu!", error);
return FALSE;
}
}
CommandLineSwitchCase(arg, "format")
{
@ -552,9 +687,9 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
#define DVCPluginEntry audin_DVCPluginEntry
#endif
int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
WIN32ERROR DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
{
int error = 0;
WIN32ERROR error = CHANNEL_RC_OK;
ADDIN_ARGV* args;
AUDIN_PLUGIN* audin;
@ -564,8 +699,12 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
if (audin == NULL)
{
audin = (AUDIN_PLUGIN*) malloc(sizeof(AUDIN_PLUGIN));
ZeroMemory(audin, sizeof(AUDIN_PLUGIN));
audin = (AUDIN_PLUGIN*) calloc(1, sizeof(AUDIN_PLUGIN));
if (!audin)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
audin->iface.Initialize = audin_plugin_initialize;
audin->iface.Connected = NULL;
@ -577,54 +716,121 @@ int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
args = pEntryPoints->GetPluginData(pEntryPoints);
if (error == 0)
if (error == CHANNEL_RC_OK)
audin_process_addin_args((IWTSPlugin*) audin, args);
else
{
WLog_ERR(TAG, "RegisterPlugin failed with error %lu!", error);
return error;
}
if (audin->subsystem)
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if (audin->subsystem && (error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args))) {
WLog_ERR(TAG, "audin_load_device_plugin failed!");
return error;
}
#if defined(WITH_PULSE)
if (!audin->device)
{
audin_set_subsystem(audin, "pulse");
audin_set_device_name(audin, "");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if ((error = audin_set_subsystem(audin, "pulse")))
{
WLog_ERR(TAG, "audin_set_subsystem for pulse failed with error %lu!", error);
return error;
}
if ((error = audin_set_device_name(audin, "")))
{
WLog_ERR(TAG, "audin_set_device_name for pulse failed with error %lu!", error);
return error;
}
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_ERR(TAG, "audin_load_device_plugin for pulse failed with error %lu!", error);
return error;
}
}
#endif
#if defined(WITH_OSS)
if (!audin->device)
{
audin_set_subsystem(audin, "oss");
audin_set_device_name(audin, "default");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if ((error = audin_set_subsystem(audin, "oss")))
{
WLog_ERR(TAG, "audin_set_subsystem for oss failed with error %lu!", error);
return error;
}
if ((error = audin_set_device_name(audin, "default")))
{
WLog_ERR(TAG, "audin_set_device_name for oss failed with error %lu!", error);
return error;
}
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_ERR(TAG, "audin_load_device_plugin oss pulse failed with error %lu!", error);
return error;
}
}
#endif
#if defined(WITH_ALSA)
if (!audin->device)
{
audin_set_subsystem(audin, "alsa");
audin_set_device_name(audin, "default");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if ((error = audin_set_subsystem(audin, "alsa")))
{
WLog_ERR(TAG, "audin_set_subsystem for alsa failed with error %lu!", error);
return error;
}
if ((error = audin_set_device_name(audin, "default")))
{
WLog_ERR(TAG, "audin_set_device_name for alsa failed with error %lu!", error);
return error;
}
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_ERR(TAG, "audin_load_device_plugin oss alsa failed with error %lu!", error);
return error;
}
}
#endif
#if defined(WITH_OPENSLES)
if (!audin->device)
{
audin_set_subsystem(audin, "opensles");
audin_set_device_name(audin, "default");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if ((error = audin_set_subsystem(audin, "opensles")))
{
WLog_ERR(TAG, "audin_set_subsystem for opensles failed with error %lu!", error);
return error;
}
if ((error = audin_set_device_name(audin, "default")))
{
WLog_ERR(TAG, "audin_set_device_name for opensles failed with error %lu!", error);
return error;
}
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_ERR(TAG, "audin_load_device_plugin oss opensles failed with error %lu!", error);
return error;
}
}
#endif
#if defined(WITH_WINMM)
if (!audin->device)
{
audin_set_subsystem(audin, "winmm");
audin_set_device_name(audin, "default");
audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args);
if ((error = audin_set_subsystem(audin, "winmm")))
{
WLog_ERR(TAG, "audin_set_subsystem for winmm failed with error %lu!", error);
return error;
}
if ((error = audin_set_device_name(audin, "default")))
{
WLog_ERR(TAG, "audin_set_device_name for winmm failed with error %lu!", error);
return error;
}
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
{
WLog_ERR(TAG, "audin_load_device_plugin oss winmm failed with error %lu!", error);
return error;
}
}
#endif

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel - OpenSL ES implementation
*
* Copyright 2013 Armin Novak <armin.novak@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,6 +32,7 @@
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <winpr/win32error.h>
#include <freerdp/addin.h>
#include <freerdp/codec/dsp.h>
@ -75,6 +78,7 @@ static void* audin_opensles_thread_func(void* arg)
} buffer;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) arg;
const size_t raw_size = opensles->frames_per_packet * opensles->bytes_per_channel;
int rc = CHANNEL_RC_OK;
DEBUG_DVC("opensles=%p", opensles);
@ -84,19 +88,26 @@ static void* audin_opensles_thread_func(void* arg)
assert(opensles->stopEvent);
assert(opensles->stream);
buffer.v = malloc(raw_size);
ZeroMemory(buffer.v, raw_size);
buffer.v = calloc(1, raw_size);
if (!buffer.v)
{
WLog_ERR(TAG, "calloc failed!");
//TODO: signal error to freerdp
ExitThread((DWORD)CHANNEL_RC_NO_MEMORY);
return NULL;
}
freerdp_dsp_context_reset_adpcm(opensles->dsp_context);
while (!(WaitForSingleObject(opensles->stopEvent, 0) == WAIT_OBJECT_0))
while (WAIT_OBJECT_0 != WaitForSingleObject(opensles->stopEvent, 0))
{
size_t encoded_size;
void *encoded_data;
int rc = android_RecIn(opensles->stream, buffer.s, raw_size);
rc = android_RecIn(opensles->stream, buffer.s, raw_size);
if (rc < 0)
{
WLog_ERR(TAG, "android_RecIn %d", rc);
WLog_ERR(TAG, "android_RecIn %lu", rc);
continue;
}
@ -125,7 +136,7 @@ static void* audin_opensles_thread_func(void* arg)
}
rc = opensles->receive(encoded_data, encoded_size, opensles->user_data);
if (!rc)
if (rc)
break;
}
@ -133,11 +144,11 @@ static void* audin_opensles_thread_func(void* arg)
DEBUG_DVC("thread shutdown.");
ExitThread(0);
ExitThread((DWORD)rc);
return NULL;
}
static void audin_opensles_free(IAudinDevice* device)
static WIN32ERROR audin_opensles_free(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
@ -146,7 +157,7 @@ static void audin_opensles_free(IAudinDevice* device)
/* The function may have been called out of order,
* ignore duplicate requests. */
if (!opensles)
return;
return CHANNEL_RC_OK;
assert(opensles);
assert(opensles->dsp_context);
@ -157,6 +168,8 @@ static void audin_opensles_free(IAudinDevice* device)
free(opensles->device_name);
free(opensles);
return CHANNEL_RC_OK;
}
static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* format)
@ -201,7 +214,7 @@ static BOOL audin_opensles_format_supported(IAudinDevice* device, audinFormat* f
return FALSE;
}
static void audin_opensles_set_format(IAudinDevice* device,
static WIN32ERROR audin_opensles_set_format(IAudinDevice* device,
audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
@ -215,7 +228,7 @@ static void audin_opensles_set_format(IAudinDevice* device,
/* The function may have been called out of order, ignore
* requests before the device is available. */
if (!opensles)
return;
return CHANNEL_RC_OK;
switch (format->wFormatTag)
{
@ -252,7 +265,7 @@ static void audin_opensles_set_format(IAudinDevice* device,
WLog_ERR(TAG, "Encoding '%d' [%08X] not supported",
(format->wFormatTag),
format->wFormatTag);
return;
return ERROR_UNSUPPORTED_TYPE;
}
opensles->rate = format->nSamplesPerSec;
@ -263,9 +276,10 @@ static void audin_opensles_set_format(IAudinDevice* device,
DEBUG_DVC("aligned frames_per_packet=%d, block_size=%d",
opensles->frames_per_packet, opensles->block_size);
return CHANNEL_RC_OK;
}
static void audin_opensles_open(IAudinDevice* device, AudinReceive receive,
static WIN32ERROR audin_opensles_open(IAudinDevice* device, AudinReceive receive,
void* user_data)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
@ -277,26 +291,45 @@ static void audin_opensles_open(IAudinDevice* device, AudinReceive receive,
/* The function may have been called out of order,
* ignore duplicate open requests. */
if(opensles->stream)
return;
return CHANNEL_RC_OK;
opensles->stream = android_OpenRecDevice(
if(!(opensles->stream = android_OpenRecDevice(
opensles->device_name,
opensles->rate,
opensles->channels,
opensles->frames_per_packet,
opensles->bytes_per_channel * 8);
assert(opensles->stream);
opensles->bytes_per_channel * 8)))
{
WLog_ERR(TAG, "android_OpenRecDevice failed!");
return ERROR_INTERNAL_ERROR;
}
opensles->receive = receive;
opensles->user_data = user_data;
opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
opensles->thread = CreateThread(NULL, 0,
if (!(opensles->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
goto error_out;
}
// TODO: add mechanism that threads can signal failure
if (!(opensles->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_opensles_thread_func,
opensles, 0, NULL);
opensles, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
android_CloseRecDevice(opensles->stream);
opensles->stream = NULL;
CloseHandle(opensles->stopEvent);
opensles->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
static void audin_opensles_close(IAudinDevice* device)
static WIN32ERROR audin_opensles_close(IAudinDevice* device)
{
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
@ -309,7 +342,7 @@ static void audin_opensles_close(IAudinDevice* device)
if (!opensles->stopEvent)
{
WLog_ERR(TAG, "[ERROR] function called without matching open.");
return;
return ERROR_REQUEST_OUT_OF_SEQUENCE;
}
assert(opensles->stopEvent);
@ -328,6 +361,8 @@ static void audin_opensles_close(IAudinDevice* device)
opensles->receive = NULL;
opensles->user_data = NULL;
opensles->stream = NULL;
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
@ -337,10 +372,10 @@ static COMMAND_LINE_ARGUMENT_A audin_opensles_args[] =
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
static WIN32ERROR audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
ADDIN_ARGV* args)
{
int status;
WIN32ERROR status;
DWORD flags;
COMMAND_LINE_ARGUMENT_A* arg;
AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*) device;
@ -366,13 +401,18 @@ static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
CommandLineSwitchCase(arg, "dev")
{
opensles->device_name = _strdup(arg->Value);
if (!opensles->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return status;
return CHANNEL_RC_OK;
}
#ifdef STATIC_CHANNELS
@ -380,16 +420,21 @@ static int audin_opensles_parse_addin_args(AudinOpenSLESDevice* device,
opensles_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(
WIN32ERROR freerdp_audin_client_subsystem_entry(
PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinOpenSLESDevice* opensles;
WIN32ERROR error;
DEBUG_DVC("pEntryPoints=%p", pEntryPoints);
opensles = (AudinOpenSLESDevice*) malloc(sizeof(AudinOpenSLESDevice));
ZeroMemory(opensles, sizeof(AudinOpenSLESDevice));
opensles = (AudinOpenSLESDevice*) calloc(1, sizeof(AudinOpenSLESDevice));
if (!opensles)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
opensles->iface.Open = audin_opensles_open;
opensles->iface.FormatSupported = audin_opensles_format_supported;
@ -399,12 +444,29 @@ int freerdp_audin_client_subsystem_entry(
args = pEntryPoints->args;
audin_opensles_parse_addin_args(opensles, args);
if ((error = audin_opensles_parse_addin_args(opensles, args)))
{
WLog_ERR(TAG, "audin_opensles_parse_addin_args failed with errorcode %d!", error);
goto error_out;
}
opensles->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin,
(IAudinDevice*) opensles);
return 0;
if (!opensles->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) opensles)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %d!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(opensles->dsp_context);
free(opensles);
return error;
}

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel - OSS implementation
*
* Copyright (c) 2015 Rozhuk Ivan <rozhuk.im@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,6 +31,7 @@
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/cmdline.h>
#include <winpr/win32error.h>
#include <err.h>
#include <errno.h>
@ -125,11 +128,11 @@ static BOOL audin_oss_format_supported(IAudinDevice *device, audinFormat *format
return TRUE;
}
static void audin_oss_set_format(IAudinDevice *device, audinFormat *format, UINT32 FramesPerPacket) {
static WIN32ERROR audin_oss_set_format(IAudinDevice *device, audinFormat *format, UINT32 FramesPerPacket) {
AudinOSSDevice *oss = (AudinOSSDevice*)device;
if (device == NULL || format == NULL)
return;
return ERROR_INVALID_PARAMETER;
oss->FramesPerPacket = FramesPerPacket;
CopyMemory(&(oss->format), format, sizeof(audinFormat));
@ -140,6 +143,7 @@ static void audin_oss_set_format(IAudinDevice *device, audinFormat *format, UINT
oss->format.wBitsPerSample *= 4;
break;
}
return CHANNEL_RC_OK;
}
static void *audin_oss_thread_func(void *arg)
@ -149,6 +153,7 @@ static void *audin_oss_thread_func(void *arg)
BYTE *buffer = NULL, *encoded_data;
int tmp, buffer_size, encoded_size;
AudinOSSDevice *oss = (AudinOSSDevice*)arg;
WIN32ERROR error;
if (arg == NULL)
goto err_out;
@ -184,12 +189,11 @@ static void *audin_oss_thread_func(void *arg)
OSS_LOG_ERR("SNDCTL_DSP_SETFRAGMENT failed", errno);
buffer_size = (oss->FramesPerPacket * oss->format.nChannels * (oss->format.wBitsPerSample / 8));
buffer = (BYTE*)malloc((buffer_size + sizeof(void*)));
buffer = (BYTE*)calloc(1, (buffer_size + sizeof(void*)));
if (NULL == buffer) {
OSS_LOG_ERR("malloc() fail", errno);
goto err_out;
}
ZeroMemory(buffer, buffer_size);
freerdp_dsp_context_reset_adpcm(oss->dsp_context);
@ -220,10 +224,14 @@ static void *audin_oss_thread_func(void *arg)
encoded_size = buffer_size;
break;
}
if (0 != oss->receive(encoded_data, encoded_size, oss->user_data))
if ((error = oss->receive(encoded_data, encoded_size, oss->user_data)))
{
WLog_ERR(TAG, "oss->receive failed with error %lu", error);
break;
}
}
err_out:
if (pcm_handle != -1)
close(pcm_handle);
@ -234,21 +242,35 @@ err_out:
return NULL;
}
static void audin_oss_open(IAudinDevice *device, AudinReceive receive, void *user_data) {
static WIN32ERROR audin_oss_open(IAudinDevice *device, AudinReceive receive, void *user_data) {
AudinOSSDevice *oss = (AudinOSSDevice*)device;
oss->receive = receive;
oss->user_data = user_data;
oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
oss->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)audin_oss_thread_func, oss, 0, NULL);
if (!(oss->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
// TODO: add mechanism that threads can signal failure
if (!(oss->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE)audin_oss_thread_func, oss, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(oss->stopEvent);
oss->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
static void audin_oss_close(IAudinDevice *device) {
return CHANNEL_RC_OK;
}
static WIN32ERROR audin_oss_close(IAudinDevice *device) {
AudinOSSDevice *oss = (AudinOSSDevice*)device;
if (device == NULL)
return;
return ERROR_INVALID_PARAMETER;
if (oss->stopEvent != NULL) {
SetEvent(oss->stopEvent);
@ -263,18 +285,27 @@ static void audin_oss_close(IAudinDevice *device) {
oss->receive = NULL;
oss->user_data = NULL;
return CHANNEL_RC_OK;
}
static void audin_oss_free(IAudinDevice *device) {
static WIN32ERROR audin_oss_free(IAudinDevice *device) {
AudinOSSDevice *oss = (AudinOSSDevice*)device;
if (device == NULL)
return;
int error;
audin_oss_close(device);
if (device == NULL)
return ERROR_INVALID_PARAMETER;
if ((error = audin_oss_close(device)))
{
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
}
freerdp_dsp_context_free(oss->dsp_context);
free(oss);
return CHANNEL_RC_OK;
}
COMMAND_LINE_ARGUMENT_A audin_oss_args[] = {
@ -282,7 +313,7 @@ COMMAND_LINE_ARGUMENT_A audin_oss_args[] = {
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static void audin_oss_parse_addin_args(AudinOSSDevice *device, ADDIN_ARGV *args) {
static WIN32ERROR audin_oss_parse_addin_args(AudinOSSDevice *device, ADDIN_ARGV *args) {
int status;
char *str_num, *eptr;
DWORD flags;
@ -293,7 +324,7 @@ static void audin_oss_parse_addin_args(AudinOSSDevice *device, ADDIN_ARGV *args)
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_oss_args, flags, oss, NULL, NULL);
if (status < 0)
return;
return ERROR_INVALID_PARAMETER;
arg = audin_oss_args;
@ -305,6 +336,11 @@ static void audin_oss_parse_addin_args(AudinOSSDevice *device, ADDIN_ARGV *args)
CommandLineSwitchCase(arg, "dev") {
str_num = _strdup(arg->Value);
if (!str_num)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
oss->dev_unit = strtol(str_num, &eptr, 10);
if (oss->dev_unit < 0 || *eptr != '\0')
oss->dev_unit = -1;
@ -313,18 +349,25 @@ static void audin_oss_parse_addin_args(AudinOSSDevice *device, ADDIN_ARGV *args)
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry oss_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints) {
WIN32ERROR freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints) {
ADDIN_ARGV *args;
AudinOSSDevice *oss;
WIN32ERROR error;
oss = (AudinOSSDevice*)malloc(sizeof(AudinOSSDevice));
ZeroMemory(oss, sizeof(AudinOSSDevice));
oss = (AudinOSSDevice*)calloc(1, sizeof(AudinOSSDevice));
if (!oss)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
oss->iface.Open = audin_oss_open;
oss->iface.FormatSupported = audin_oss_format_supported;
@ -335,11 +378,31 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
oss->dev_unit = -1;
args = pEntryPoints->args;
audin_oss_parse_addin_args(oss, args);
if ((error = audin_oss_parse_addin_args(oss, args)))
{
WLog_ERR(TAG, "audin_oss_parse_addin_args failed with errorcode %lu!", error);
goto error_out;
}
oss->dsp_context = freerdp_dsp_context_new();
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)oss);
return 0;
if (!oss->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) oss)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
freerdp_dsp_context_free(oss->dsp_context);
free(oss);
return error;
}

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel - PulseAudio implementation
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,6 +29,7 @@
#include <winpr/crt.h>
#include <winpr/cmdline.h>
#include <winpr/win32error.h>
#include <pulse/pulseaudio.h>
@ -84,19 +87,19 @@ static void audin_pulse_context_state_callback(pa_context* context, void* userda
}
}
static BOOL audin_pulse_connect(IAudinDevice* device)
static WIN32ERROR audin_pulse_connect(IAudinDevice* device)
{
pa_context_state_t state;
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return FALSE;
return ERROR_INVALID_PARAMETER;
if (pa_context_connect(pulse->context, NULL, 0, NULL))
{
WLog_ERR(TAG, "pa_context_connect failed (%d)",
pa_context_errno(pulse->context));
return FALSE;
return ERROR_INTERNAL_ERROR;
}
pa_threaded_mainloop_lock(pulse->mainloop);
if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
@ -104,7 +107,7 @@ static BOOL audin_pulse_connect(IAudinDevice* device)
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_ERR(TAG, "pa_threaded_mainloop_start failed (%d)",
pa_context_errno(pulse->context));
return FALSE;
return ERROR_INTERNAL_ERROR;
}
for (;;)
{
@ -115,31 +118,22 @@ static BOOL audin_pulse_connect(IAudinDevice* device)
{
WLog_ERR(TAG, "bad context state (%d)",
pa_context_errno(pulse->context));
break;
pa_context_disconnect(pulse->context);
return ERROR_INVALID_STATE;
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
if (state == PA_CONTEXT_READY)
{
DEBUG_DVC("connected");
return TRUE;
}
else
{
pa_context_disconnect(pulse->context);
return FALSE;
}
return CHANNEL_RC_OK;
}
static void audin_pulse_free(IAudinDevice* device)
static WIN32ERROR audin_pulse_free(IAudinDevice* device)
{
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
DEBUG_DVC("");
if (!pulse)
return;
return ERROR_INVALID_PARAMETER;
if (pulse->mainloop)
{
pa_threaded_mainloop_stop(pulse->mainloop);
@ -157,6 +151,8 @@ static void audin_pulse_free(IAudinDevice* device)
}
freerdp_dsp_context_free(pulse->dsp_context);
free(pulse);
return CHANNEL_RC_OK;
}
static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
@ -201,14 +197,14 @@ static BOOL audin_pulse_format_supported(IAudinDevice* device, audinFormat* form
return FALSE;
}
static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
static WIN32ERROR audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
int bs;
pa_sample_spec sample_spec = { 0 };
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return;
return ERROR_INVALID_PARAMETER;
if (FramesPerPacket > 0)
{
@ -252,6 +248,7 @@ static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, UI
pulse->sample_spec = sample_spec;
pulse->format = format->wFormatTag;
pulse->block_size = format->nBlockAlign;
return CHANNEL_RC_OK;
}
static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
@ -347,14 +344,12 @@ static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length
}
static void audin_pulse_close(IAudinDevice* device)
static WIN32ERROR audin_pulse_close(IAudinDevice* device)
{
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context || !pulse->stream)
return;
DEBUG_DVC("");
return ERROR_INVALID_PARAMETER;
pa_threaded_mainloop_lock(pulse->mainloop);
pa_stream_disconnect(pulse->stream);
@ -370,20 +365,19 @@ static void audin_pulse_close(IAudinDevice* device)
pulse->buffer = NULL;
pulse->buffer_frames = 0;
}
return CHANNEL_RC_OK;
}
static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
static WIN32ERROR audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
pa_stream_state_t state;
pa_buffer_attr buffer_attr = { 0 };
AudinPulseDevice* pulse = (AudinPulseDevice*) device;
if (!pulse->context)
return;
return ERROR_INVALID_PARAMETER;
if (!pulse->sample_spec.rate || pulse->stream)
return;
DEBUG_DVC("");
return ERROR_INVALID_PARAMETER;
pulse->buffer = NULL;
pulse->receive = receive;
@ -397,7 +391,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
pa_threaded_mainloop_unlock(pulse->mainloop);
DEBUG_DVC("pa_stream_new failed (%d)",
pa_context_errno(pulse->context));
return;
return pa_context_errno(pulse->context);
}
pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
pa_stream_set_state_callback(pulse->stream,
@ -417,7 +411,7 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
pa_threaded_mainloop_unlock(pulse->mainloop);
WLog_ERR(TAG, "pa_stream_connect_playback failed (%d)",
pa_context_errno(pulse->context));
return;
return pa_context_errno(pulse->context);
}
for (;;)
@ -427,25 +421,24 @@ static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* u
break;
if (!PA_STREAM_IS_GOOD(state))
{
audin_pulse_close(device);
WLog_ERR(TAG, "bad stream state (%d)",
pa_context_errno(pulse->context));
break;
pa_threaded_mainloop_unlock(pulse->mainloop);
return pa_context_errno(pulse->context);
}
pa_threaded_mainloop_wait(pulse->mainloop);
}
pa_threaded_mainloop_unlock(pulse->mainloop);
if (state == PA_STREAM_READY)
{
freerdp_dsp_context_reset_adpcm(pulse->dsp_context);
pulse->buffer = malloc(pulse->bytes_per_frame * pulse->frames_per_packet);
ZeroMemory(pulse->buffer, pulse->bytes_per_frame * pulse->frames_per_packet);
pulse->buffer = calloc(1, pulse->bytes_per_frame * pulse->frames_per_packet);
if (!pulse->buffer) {
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_OK;
}
pulse->buffer_frames = 0;
DEBUG_DVC("connected");
}
else
{
audin_pulse_close(device);
}
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_pulse_args[] =
@ -454,7 +447,7 @@ static COMMAND_LINE_ARGUMENT_A audin_pulse_args[] =
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static void audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* args)
static WIN32ERROR audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
@ -477,24 +470,36 @@ static void audin_pulse_parse_addin_args(AudinPulseDevice* device, ADDIN_ARGV* a
CommandLineSwitchCase(arg, "dev")
{
pulse->device_name = _strdup(arg->Value);
if (!pulse->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry pulse_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
WIN32ERROR freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinPulseDevice* pulse;
WIN32ERROR error;
pulse = (AudinPulseDevice*) malloc(sizeof(AudinPulseDevice));
ZeroMemory(pulse, sizeof(AudinPulseDevice));
pulse = (AudinPulseDevice*) calloc(1, sizeof(AudinPulseDevice));
if (!pulse)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
pulse->iface.Open = audin_pulse_open;
pulse->iface.FormatSupported = audin_pulse_format_supported;
@ -504,17 +509,27 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
args = pEntryPoints->args;
audin_pulse_parse_addin_args(pulse, args);
if ((error = audin_pulse_parse_addin_args(pulse, args)))
{
WLog_ERR(TAG, "audin_pulse_parse_addin_args failed with error %lu!", error);
goto error_out;
}
pulse->dsp_context = freerdp_dsp_context_new();
if (!pulse->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
pulse->mainloop = pa_threaded_mainloop_new();
if (!pulse->mainloop)
{
WLog_ERR(TAG, "pa_threaded_mainloop_new failed");
audin_pulse_free((IAudinDevice*) pulse);
return 1;
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
@ -522,20 +537,27 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
if (!pulse->context)
{
WLog_ERR(TAG, "pa_context_new failed");
audin_pulse_free((IAudinDevice*) pulse);
return 1;
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
if (!audin_pulse_connect((IAudinDevice*) pulse))
if ((error = audin_pulse_connect((IAudinDevice*) pulse)))
{
WLog_ERR(TAG, "audin_pulse_connect failed");
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
audin_pulse_free((IAudinDevice*)pulse);
return 1;
}
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
return 0;
return error;
}

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel - WinMM implementation
*
* Copyright 2013 Zhang Zhaolong <zhangzl2013@126.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -103,6 +105,8 @@ static DWORD audin_winmm_thread_func(void* arg)
for (i = 0; i < 4; i++)
{
buffer = (char *) malloc(size);
if (!buffer)
return CHANNEL_RC_NO_MEMORY;
waveHdr[i].dwBufferLength = size;
waveHdr[i].dwFlags = 0;
waveHdr[i].lpData = buffer;
@ -136,7 +140,7 @@ static DWORD audin_winmm_thread_func(void* arg)
return 0;
}
static void audin_winmm_free(IAudinDevice* device)
static WIN32ERROR audin_winmm_free(IAudinDevice* device)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
@ -149,14 +153,14 @@ static void audin_winmm_free(IAudinDevice* device)
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return CHANNEL_RC_OK;
}
static void audin_winmm_close(IAudinDevice* device)
static WIN32ERROR audin_winmm_close(IAudinDevice* device)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
DEBUG_DVC("");
SetEvent(winmm->stopEvent);
WaitForSingleObject(winmm->thread, INFINITE);
@ -168,9 +172,11 @@ static void audin_winmm_close(IAudinDevice* device)
winmm->stopEvent = NULL;
winmm->receive = NULL;
winmm->user_data = NULL;
return CHANNEL_RC_OK;
}
static void audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
static WIN32ERROR audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
{
UINT32 i;
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
@ -187,6 +193,7 @@ static void audin_winmm_set_format(IAudinDevice* device, audinFormat* format, UI
break;
}
}
return CHANNEL_RC_OK;
}
static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* format)
@ -196,6 +203,8 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* form
BYTE *data;
pwfx = (PWAVEFORMATEX)malloc(sizeof(WAVEFORMATEX) + format->cbSize);
if (!pwfx)
return FALSE;
pwfx->cbSize = format->cbSize;
pwfx->wFormatTag = format->wFormatTag;
pwfx->nChannels = format->nChannels;
@ -216,32 +225,43 @@ static BOOL audin_winmm_format_supported(IAudinDevice* device, audinFormat* form
PWAVEFORMATEX *tmp_ppwfx;
tmp_ppwfx = realloc(winmm->ppwfx, sizeof(PWAVEFORMATEX) * winmm->ppwfx_size * 2);
if (!tmp_ppwfx)
return 0;
return FALSE;
winmm->ppwfx_size *= 2;
winmm->ppwfx = tmp_ppwfx;
}
winmm->ppwfx[winmm->cFormats++] = pwfx;
return 1;
return TRUE;
}
}
free(pwfx);
return 0;
return FALSE;
}
static void audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
static WIN32ERROR audin_winmm_open(IAudinDevice* device, AudinReceive receive, void* user_data)
{
AudinWinmmDevice* winmm = (AudinWinmmDevice*) device;
DEBUG_DVC("");
winmm->receive = receive;
winmm->user_data = user_data;
winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
winmm->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) audin_winmm_thread_func, winmm, 0, NULL);
if (!(winmm->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return ERROR_INTERNAL_ERROR;
}
// TODO: add mechanism that threads can signal failure
if (!(winmm->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_winmm_thread_func, winmm, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(winmm->stopEvent);
winmm->stopEvent = NULL;
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
@ -250,7 +270,7 @@ static COMMAND_LINE_ARGUMENT_A audin_winmm_args[] =
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
};
static void audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
static WIN32ERROR audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* args)
{
int status;
DWORD flags;
@ -273,24 +293,36 @@ static void audin_winmm_parse_addin_args(AudinWinmmDevice* device, ADDIN_ARGV* a
CommandLineSwitchCase(arg, "dev")
{
winmm->device_name = _strdup(arg->Value);
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
return CHANNEL_RC_NO_MEMORY;
}
}
CommandLineSwitchEnd(arg)
}
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
return CHANNEL_RC_OK;
}
#ifdef STATIC_CHANNELS
#define freerdp_audin_client_subsystem_entry winmm_freerdp_audin_client_subsystem_entry
#endif
int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
WIN32ERROR freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
{
ADDIN_ARGV* args;
AudinWinmmDevice* winmm;
WIN32ERROR error;
winmm = (AudinWinmmDevice*) malloc(sizeof(AudinWinmmDevice));
ZeroMemory(winmm, sizeof(AudinWinmmDevice));
winmm = (AudinWinmmDevice*) calloc(sizeof(AudinWinmmDevice));
if (!winmm)
{
WLog_ERR(TAG, "calloc failed!");
return CHANNEL_RC_NO_MEMORY;
}
winmm->iface.Open = audin_winmm_open;
winmm->iface.FormatSupported = audin_winmm_format_supported;
@ -300,15 +332,42 @@ int freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEnt
args = pEntryPoints->args;
audin_winmm_parse_addin_args(winmm, args);
if ((error = audin_winmm_parse_addin_args(winmm, args)))
{
WLog_ERR(TAG, "audin_winmm_parse_addin_args failed with error %d!", error);
goto error_out;
}
if (!winmm->device_name)
{
winmm->device_name = _strdup("default");
if (!winmm->device_name)
{
WLog_ERR(TAG, "_strdup failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
}
winmm->ppwfx_size = 10;
winmm->ppwfx = malloc(sizeof(PWAVEFORMATEX) * winmm->ppwfx_size);
pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm);
return 0;
if (!winmm->ppwfx)
{
WLog_ERR(TAG, "malloc failed!");
error = CHANNEL_RC_NO_MEMORY;
goto error_out;
}
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) winmm)))
{
WLog_ERR(TAG, "RegisterAudinDevice failed with error %d!", error);
goto error_out;
}
return CHANNEL_RC_OK;
error_out:
free(winmm->ppwfx);
free(winmm->device_name);
free(winmm);
return error;
}

View File

@ -3,6 +3,8 @@
* Server Audio Input Virtual Channel
*
* Copyright 2012 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -29,6 +31,7 @@
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/win32error.h>
#include <freerdp/codec/dsp.h>
#include <freerdp/codec/audio.h>
@ -62,12 +65,15 @@ typedef struct _audin_server
} audin_server;
static void audin_server_select_format(audin_server_context* context, int client_format_index)
static WIN32ERROR audin_server_select_format(audin_server_context* context, int client_format_index)
{
audin_server* audin = (audin_server*) context;
if (client_format_index >= context->num_client_formats)
return;
{
WLog_ERR(TAG, "error in protocol: client_format_index >= context->num_client_formats!");
return ERROR_INVALID_DATA;
}
context->selected_client_format = client_format_index;
@ -75,33 +81,45 @@ static void audin_server_select_format(audin_server_context* context, int client
{
/* TODO: send MSG_SNDIN_FORMATCHANGE */
}
return CHANNEL_RC_OK;
}
static void audin_server_send_version(audin_server* audin, wStream* s)
static WIN32ERROR audin_server_send_version(audin_server* audin, wStream* s)
{
ULONG written;
Stream_Write_UINT8(s, MSG_SNDIN_VERSION);
Stream_Write_UINT32(s, 1); /* Version (4 bytes) */
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
if (!WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written))
{
WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
return ERROR_INTERNAL_ERROR;
}
return CHANNEL_RC_OK;
}
static BOOL audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length)
static WIN32ERROR audin_server_recv_version(audin_server* audin, wStream* s, UINT32 length)
{
UINT32 Version;
if (length < 4)
return FALSE;
{
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %d", length);
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, Version);
if (Version < 1)
return FALSE;
return TRUE;
{
WLog_ERR(TAG, "expected Version > 0 but got %d", Version);
return ERROR_INVALID_DATA;
}
static void audin_server_send_formats(audin_server* audin, wStream* s)
return CHANNEL_RC_OK;
}
static WIN32ERROR audin_server_send_formats(audin_server* audin, wStream* s)
{
int i;
UINT32 nAvgBytesPerSec;
@ -119,7 +137,10 @@ static void audin_server_send_formats(audin_server* audin, wStream* s)
audin->context.server_formats[i].wBitsPerSample / 8;
if (!Stream_EnsureRemainingCapacity(s, 18))
return;
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write_UINT16(s, audin->context.server_formats[i].wFormatTag);
Stream_Write_UINT16(s, audin->context.server_formats[i].nChannels);
@ -132,29 +153,39 @@ static void audin_server_send_formats(audin_server* audin, wStream* s)
if (audin->context.server_formats[i].cbSize)
{
if (!Stream_EnsureRemainingCapacity(s, audin->context.server_formats[i].cbSize))
return;
{
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
return CHANNEL_RC_NO_MEMORY;
}
Stream_Write(s, audin->context.server_formats[i].data,
audin->context.server_formats[i].cbSize);
}
}
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length)
static WIN32ERROR audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 length)
{
int i;
WIN32ERROR success = CHANNEL_RC_OK;
if (length < 8)
return FALSE;
{
WLog_ERR(TAG, "error parsing rec formats: expected at least 8 bytes, got %d", length);
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, audin->context.num_client_formats); /* NumFormats (4 bytes) */
Stream_Seek_UINT32(s); /* cbSizeFormatsPacket (4 bytes) */
length -= 8;
if (audin->context.num_client_formats <= 0)
return FALSE;
{
WLog_ERR(TAG, "num_client_formats expected > 0 but got %d", audin->context.num_client_formats);
return ERROR_INVALID_DATA;
}
audin->context.client_formats = malloc(audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
ZeroMemory(audin->context.client_formats, audin->context.num_client_formats * sizeof(AUDIO_FORMAT));
@ -165,7 +196,8 @@ static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 le
{
free(audin->context.client_formats);
audin->context.client_formats = NULL;
return FALSE;
WLog_ERR(TAG, "expected length at least 18, but got %d", length);
return ERROR_INVALID_DATA;
}
Stream_Read_UINT16(s, audin->context.client_formats[i].wFormatTag);
@ -182,17 +214,20 @@ static BOOL audin_server_recv_formats(audin_server* audin, wStream* s, UINT32 le
}
}
IFCALL(audin->context.Opening, &audin->context);
return TRUE;
IFCALLRET(audin->context.Opening, success, &audin->context);
return success;
}
static void audin_server_send_open(audin_server* audin, wStream* s)
static WIN32ERROR audin_server_send_open(audin_server* audin, wStream* s)
{
ULONG written;
if (audin->context.selected_client_format < 0)
return;
{
WLog_ERR(TAG, "audin->context.selected_client_format = %d",
audin->context.selected_client_format);
return ERROR_INVALID_DATA;
}
audin->opened = TRUE;
@ -213,24 +248,29 @@ static void audin_server_send_open(audin_server* audin, wStream* s)
Stream_Write_UINT16(s, 16); /* wBitsPerSample */
Stream_Write_UINT16(s, 0); /* cbSize */
WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written);
return WTSVirtualChannelWrite(audin->audin_channel, (PCHAR) Stream_Buffer(s), Stream_GetPosition(s), &written) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR;
}
static BOOL audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length)
static WIN32ERROR audin_server_recv_open_reply(audin_server* audin, wStream* s, UINT32 length)
{
UINT32 Result;
WIN32ERROR success = CHANNEL_RC_OK;
if (length < 4)
return FALSE;
{
WLog_ERR(TAG, "error parsing version info: expected at least 4 bytes, got %d", length);
return ERROR_INVALID_DATA;
}
Stream_Read_UINT32(s, Result);
IFCALL(audin->context.OpenResult, &audin->context, Result);
IFCALLRET(audin->context.OpenResult, success, &audin->context, Result);
return TRUE;
return success;
}
static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length)
static WIN32ERROR audin_server_recv_data(audin_server* audin, wStream* s, UINT32 length)
{
AUDIO_FORMAT* format;
int sbytes_per_sample;
@ -238,9 +278,14 @@ static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 lengt
BYTE* src;
int size;
int frames;
WIN32ERROR success = CHANNEL_RC_OK;
if (audin->context.selected_client_format < 0)
return FALSE;
{
WLog_ERR(TAG, "audin->context.selected_client_format = %d",
audin->context.selected_client_format);
return ERROR_INVALID_DATA;
}
format = &audin->context.client_formats[audin->context.selected_client_format];
@ -283,9 +328,9 @@ static BOOL audin_server_recv_data(audin_server* audin, wStream* s, UINT32 lengt
src = audin->dsp_context->resampled_buffer;
}
IFCALL(audin->context.ReceiveSamples, &audin->context, src, frames);
IFCALLRET(audin->context.ReceiveSamples, success, &audin->context, src, frames);
return TRUE;
return success;
}
static void* audin_server_thread_func(void* arg)
@ -299,6 +344,7 @@ static void* audin_server_thread_func(void* arg)
HANDLE ChannelEvent;
DWORD BytesReturned = 0;
audin_server* audin = (audin_server*) arg;
WIN32ERROR error = CHANNEL_RC_OK;
buffer = NULL;
BytesReturned = 0;
@ -311,6 +357,12 @@ static void* audin_server_thread_func(void* arg)
WTSFreeMemory(buffer);
}
else
{
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
error = ERROR_INTERNAL_ERROR;
goto out;
}
nCount = 0;
events[nCount++] = audin->stopEvent;
@ -321,10 +373,14 @@ static void* audin_server_thread_func(void* arg)
while (1)
{
if (WaitForMultipleObjects(nCount, events, FALSE, 100) == WAIT_OBJECT_0)
break;
goto out;
if (WTSVirtualChannelQuery(audin->audin_channel, WTSVirtualChannelReady, &buffer, &BytesReturned) == FALSE)
break;
{
WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
error = ERROR_INTERNAL_ERROR;
goto out;
}
ready = *((BOOL*) buffer);
@ -336,11 +392,20 @@ static void* audin_server_thread_func(void* arg)
s = Stream_New(NULL, 4096);
if (!s)
{
WLog_ERR(TAG, "Stream_New failed!");
error = CHANNEL_RC_NO_MEMORY;
goto out;
}
if (ready)
{
audin_server_send_version(audin, s);
if ((error = audin_server_send_version(audin, s)))
{
WLog_ERR(TAG, "audin_server_send_version failed with error %lu!", error);
goto out_capacity;
}
}
while (ready)
@ -350,14 +415,21 @@ static void* audin_server_thread_func(void* arg)
Stream_SetPosition(s, 0);
WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned);
if (!WTSVirtualChannelRead(audin->audin_channel, 0, NULL, 0, &BytesReturned))
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
if (BytesReturned < 1)
continue;
if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
goto out_capacity;
break;
if (WTSVirtualChannelRead(audin->audin_channel, 0, (PCHAR) Stream_Buffer(s),
Stream_Capacity(s), &BytesReturned) == FALSE)
{
WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
error = ERROR_INTERNAL_ERROR;
break;
}
@ -367,24 +439,48 @@ static void* audin_server_thread_func(void* arg)
switch (MessageId)
{
case MSG_SNDIN_VERSION:
if (audin_server_recv_version(audin, s, BytesReturned))
audin_server_send_formats(audin, s);
if ((error = audin_server_recv_version(audin, s, BytesReturned)))
{
WLog_ERR(TAG, "audin_server_recv_version failed with error %lu!", error);
goto out_capacity;
}
if ((error = audin_server_send_formats(audin, s)))
{
WLog_ERR(TAG, "audin_server_send_formats failed with error %lu!", error);
goto out_capacity;
}
break;
case MSG_SNDIN_FORMATS:
if (audin_server_recv_formats(audin, s, BytesReturned))
audin_server_send_open(audin, s);
if ((error = audin_server_recv_formats(audin, s, BytesReturned)))
{
WLog_ERR(TAG, "audin_server_recv_formats failed with error %lu!", error);
goto out_capacity;
}
if ((error = audin_server_send_open(audin, s)))
{
WLog_ERR(TAG, "audin_server_send_open failed with error %lu!", error);
goto out_capacity;
}
break;
case MSG_SNDIN_OPEN_REPLY:
audin_server_recv_open_reply(audin, s, BytesReturned);
if ((error = audin_server_recv_open_reply(audin, s, BytesReturned)))
{
WLog_ERR(TAG, "audin_server_recv_open_reply failed with error %lu!", error);
goto out_capacity;
}
break;
case MSG_SNDIN_DATA_INCOMING:
break;
case MSG_SNDIN_DATA:
audin_server_recv_data(audin, s, BytesReturned);
if ((error = audin_server_recv_data(audin, s, BytesReturned)))
{
WLog_ERR(TAG, "audin_server_recv_data failed with error %lu!", error);
goto out_capacity;
};
break;
case MSG_SNDIN_FORMATCHANGE:
@ -401,7 +497,7 @@ out_capacity:
out:
WTSVirtualChannelClose(audin->audin_channel);
audin->audin_channel = NULL;
ExitThread((DWORD)error);
return NULL;
}
@ -427,16 +523,30 @@ static BOOL audin_server_open(audin_server_context* context)
"AUDIO_INPUT", WTS_CHANNEL_OPTION_DYNAMIC);
if (!audin->audin_channel)
{
WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
return FALSE;
}
audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!(audin->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
WLog_ERR(TAG, "CreateEvent failed!");
return FALSE;
}
audin->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_server_thread_func, (void*) audin, 0, NULL);
// TODO: implement signaling an error
if (!(audin->thread = CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) audin_server_thread_func, (void*) audin, 0, NULL)))
{
WLog_ERR(TAG, "CreateThread failed!");
CloseHandle(audin->stopEvent);
audin->stopEvent = NULL;
return FALSE;
}
return TRUE;
}
WLog_ERR(TAG, "thread already running!");
return FALSE;
}
@ -470,6 +580,11 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
audin_server* audin;
audin = (audin_server *)calloc(1, sizeof(audin_server));
if (!audin)
{
WLog_ERR(TAG, "calloc failed!");
return NULL;
}
audin->context.vcm = vcm;
audin->context.selected_client_format = -1;
@ -480,6 +595,13 @@ audin_server_context* audin_server_context_new(HANDLE vcm)
audin->dsp_context = freerdp_dsp_context_new();
if (!audin->dsp_context)
{
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
free(audin);
return NULL;
}
return (audin_server_context*) audin;
}

View File

@ -3,6 +3,8 @@
* Audio Input Redirection Virtual Channel
*
* Copyright 2010-2011 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,7 +28,7 @@
* Subsystem Interface
*/
typedef BOOL (*AudinReceive) (BYTE* data, int size, void* userData);
typedef WIN32ERROR (*AudinReceive) (BYTE* data, int size, void* userData);
typedef struct audin_format audinFormat;
struct audin_format
@ -43,16 +45,16 @@ struct audin_format
typedef struct _IAudinDevice IAudinDevice;
struct _IAudinDevice
{
void (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* userData);
WIN32ERROR (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* userData);
BOOL (*FormatSupported) (IAudinDevice* devplugin, audinFormat* format);
void (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, UINT32 FramesPerPacket);
void (*Close) (IAudinDevice* devplugin);
void (*Free) (IAudinDevice* devplugin);
WIN32ERROR (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, UINT32 FramesPerPacket);
WIN32ERROR (*Close) (IAudinDevice* devplugin);
WIN32ERROR (*Free) (IAudinDevice* devplugin);
};
#define AUDIN_DEVICE_EXPORT_FUNC_NAME "freerdp_audin_client_subsystem_entry"
typedef void (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
typedef WIN32ERROR (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
{
@ -63,7 +65,7 @@ struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
typedef struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS FREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef FREERDP_AUDIN_DEVICE_ENTRY_POINTS* PFREERDP_AUDIN_DEVICE_ENTRY_POINTS;
typedef int (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
typedef WIN32ERROR (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
#endif /* FREERDP_CHANNEL_CLIENT_AUDIN_H */

View File

@ -3,6 +3,8 @@
* Server Audio Input Virtual Channel
*
* Copyright 2012 Vic Lee
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,16 +25,17 @@
#include <freerdp/codec/audio.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/rdpsnd.h>
#include <winpr/win32error.h>
typedef struct _audin_server_context audin_server_context;
typedef void (*psAudinServerSelectFormat)(audin_server_context* context, int client_format_index);
typedef WIN32ERROR (*psAudinServerSelectFormat)(audin_server_context* context, int client_format_index);
typedef BOOL (*psAudinServerOpen)(audin_server_context* context);
typedef BOOL (*psAudinServerClose)(audin_server_context* context);
typedef void (*psAudinServerOpening)(audin_server_context* context);
typedef void (*psAudinServerOpenResult)(audin_server_context* context, UINT32 result);
typedef void (*psAudinServerReceiveSamples)(audin_server_context* context, const void* buf, int nframes);
typedef WIN32ERROR (*psAudinServerOpening)(audin_server_context* context);
typedef WIN32ERROR (*psAudinServerOpenResult)(audin_server_context* context, UINT32 result);
typedef WIN32ERROR (*psAudinServerReceiveSamples)(audin_server_context* context, const void* buf, int nframes);
struct _audin_server_context
{

View File

@ -3,6 +3,8 @@
* FreeRDP Sample Server (Audio Input)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -28,6 +30,7 @@
#include "sf_audin.h"
#include <freerdp/log.h>
#include <winpr/win32error.h>
#define TAG SERVER_TAG("sample")
static const AUDIO_FORMAT test_audio_formats[] =
@ -36,21 +39,24 @@ static const AUDIO_FORMAT test_audio_formats[] =
{ WAVE_FORMAT_ALAW, 2, 22050, 44100, 2, 8, 0, NULL }
};
static void sf_peer_audin_opening(audin_server_context* context)
static WIN32ERROR sf_peer_audin_opening(audin_server_context* context)
{
WLog_DBG(TAG, "AUDIN opening.");
/* Simply choose the first format supported by the client. */
context->SelectFormat(context, 0);
return CHANNEL_RC_OK;
}
static void sf_peer_audin_open_result(audin_server_context* context, UINT32 result)
static WIN32ERROR sf_peer_audin_open_result(audin_server_context* context, UINT32 result)
{
WLog_DBG(TAG, "AUDIN open result %d.", result);
return CHANNEL_RC_OK;
}
static void sf_peer_audin_receive_samples(audin_server_context* context, const void* buf, int nframes)
static WIN32ERROR sf_peer_audin_receive_samples(audin_server_context* context, const void* buf, int nframes)
{
WLog_DBG(TAG, "AUDIN receive %d frames.", nframes);
return CHANNEL_RC_OK;
}
void sf_peer_audin_init(testPeerContext* context)