22#include <freerdp/config.h> 
   29#include <winpr/synch.h> 
   30#include <winpr/thread.h> 
   31#include <winpr/cmdline.h> 
   32#include <winpr/wlog.h> 
   34#include <alsa/asoundlib.h> 
   36#include <freerdp/freerdp.h> 
   37#include <freerdp/addin.h> 
   38#include <freerdp/channels/rdpsnd.h> 
   40#include "audin_main.h" 
   47  UINT32 frames_per_packet;
 
   56  rdpContext* rdpcontext;
 
   58  size_t bytes_per_frame;
 
   61static snd_pcm_format_t audin_alsa_format(UINT32 wFormatTag, UINT32 bitPerChannel)
 
   66      switch (bitPerChannel)
 
   69          return SND_PCM_FORMAT_S16_LE;
 
   72          return SND_PCM_FORMAT_S8;
 
   75          return SND_PCM_FORMAT_UNKNOWN;
 
   79      return SND_PCM_FORMAT_UNKNOWN;
 
   83static BOOL audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
 
   87  UINT32 channels = alsa->aformat.nChannels;
 
   88  snd_pcm_hw_params_t* hw_params = NULL;
 
   89  snd_pcm_format_t format =
 
   90      audin_alsa_format(alsa->aformat.wFormatTag, alsa->aformat.wBitsPerSample);
 
   92  if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
 
   94    WLog_Print(alsa->log, WLOG_ERROR, 
"snd_pcm_hw_params_malloc (%s)", snd_strerror(error));
 
   98  snd_pcm_hw_params_any(capture_handle, hw_params);
 
   99  snd_pcm_hw_params_set_access(capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
 
  100  snd_pcm_hw_params_set_format(capture_handle, hw_params, format);
 
  101  snd_pcm_hw_params_set_rate_near(capture_handle, hw_params, &alsa->aformat.nSamplesPerSec, NULL);
 
  102  snd_pcm_hw_params_set_channels_near(capture_handle, hw_params, &channels);
 
  103  snd_pcm_hw_params(capture_handle, hw_params);
 
  104  snd_pcm_hw_params_free(hw_params);
 
  105  snd_pcm_prepare(capture_handle);
 
  106  if (channels > UINT16_MAX)
 
  108  s = snd_pcm_format_size(format, 1);
 
  109  if ((s < 0) || (s > UINT16_MAX))
 
  111  alsa->aformat.nChannels = (UINT16)channels;
 
  112  alsa->bytes_per_frame = (size_t)s * channels;
 
  116static DWORD WINAPI audin_alsa_thread_func(LPVOID arg)
 
  118  DWORD error = CHANNEL_RC_OK;
 
  120  AudinALSADevice* alsa = (AudinALSADevice*)arg;
 
  124  WLog_Print(alsa->log, WLOG_DEBUG, 
"in");
 
  126  snd_pcm_t* capture_handle = NULL;
 
  127  const int rc = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0);
 
  130    WLog_Print(alsa->log, WLOG_ERROR, 
"snd_pcm_open (%s)", snd_strerror(rc));
 
  131    error = CHANNEL_RC_INITIALIZATION_ERROR;
 
  135  if (!audin_alsa_set_params(alsa, capture_handle))
 
  137    WLog_Print(alsa->log, WLOG_ERROR, 
"audin_alsa_set_params failed");
 
  142      (BYTE*)calloc(alsa->frames_per_packet + alsa->aformat.nBlockAlign, alsa->bytes_per_frame);
 
  146    WLog_Print(alsa->log, WLOG_ERROR, 
"calloc failed!");
 
  147    error = CHANNEL_RC_NO_MEMORY;
 
  153    size_t frames = alsa->frames_per_packet;
 
  154    const DWORD status = WaitForSingleObject(alsa->stopEvent, 0);
 
  156    if (status == WAIT_FAILED)
 
  158      error = GetLastError();
 
  159      WLog_Print(alsa->log, WLOG_ERROR, 
"WaitForSingleObject failed with error %" PRIu32 
"!",
 
  164    if (status == WAIT_OBJECT_0)
 
  166      WLog_Print(alsa->log, WLOG_DEBUG, 
"alsa->stopEvent requests termination");
 
  170    snd_pcm_sframes_t framesRead = snd_pcm_readi(capture_handle, buffer, frames);
 
  175    if (framesRead == -EPIPE)
 
  177      const int res = snd_pcm_recover(capture_handle, (
int)framesRead, 0);
 
  179        WLog_Print(alsa->log, WLOG_WARN, 
"snd_pcm_recover (%s)", snd_strerror(res));
 
  183    else if (framesRead < 0)
 
  185      WLog_Print(alsa->log, WLOG_ERROR, 
"snd_pcm_readi (%s)", snd_strerror((
int)framesRead));
 
  186      error = ERROR_INTERNAL_ERROR;
 
  190    error = alsa->receive(&alsa->aformat, buffer,
 
  191                          WINPR_ASSERTING_INT_CAST(
size_t, framesRead) * alsa->bytes_per_frame,
 
  196      WLog_Print(alsa->log, WLOG_ERROR,
 
  197                 "audin_alsa_thread_receive failed with error %" PRIu32, error);
 
  206    const int res = snd_pcm_close(capture_handle);
 
  208      WLog_Print(alsa->log, WLOG_WARN, 
"snd_pcm_close (%s)", snd_strerror(res));
 
  212  WLog_Print(alsa->log, WLOG_DEBUG, 
"out");
 
  214  if (error && alsa->rdpcontext)
 
  215    setChannelError(alsa->rdpcontext, error, 
"audin_alsa_thread_func reported an error");
 
  226static UINT audin_alsa_free(IAudinDevice* device)
 
  228  AudinALSADevice* alsa = (AudinALSADevice*)device;
 
  231    free(alsa->device_name);
 
  234  return CHANNEL_RC_OK;
 
  237static BOOL audin_alsa_format_supported(IAudinDevice* device, 
const AUDIO_FORMAT* format)
 
  239  if (!device || !format)
 
  242  switch (format->wFormatTag)
 
  244    case WAVE_FORMAT_PCM:
 
  245      if (format->cbSize == 0 && (format->nSamplesPerSec <= 48000) &&
 
  246          (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
 
  247          (format->nChannels == 1 || format->nChannels == 2))
 
  266static UINT audin_alsa_set_format(IAudinDevice* device, 
const AUDIO_FORMAT* format,
 
  267                                  UINT32 FramesPerPacket)
 
  269  AudinALSADevice* alsa = (AudinALSADevice*)device;
 
  271  if (!alsa || !format)
 
  272    return ERROR_INVALID_PARAMETER;
 
  274  alsa->aformat = *format;
 
  275  alsa->frames_per_packet = FramesPerPacket;
 
  277  if (audin_alsa_format(format->wFormatTag, format->wBitsPerSample) == SND_PCM_FORMAT_UNKNOWN)
 
  278    return ERROR_INTERNAL_ERROR;
 
  280  return CHANNEL_RC_OK;
 
  288static UINT audin_alsa_open(IAudinDevice* device, AudinReceive receive, 
void* user_data)
 
  290  AudinALSADevice* alsa = (AudinALSADevice*)device;
 
  292  if (!device || !receive || !user_data)
 
  293    return ERROR_INVALID_PARAMETER;
 
  295  alsa->receive = receive;
 
  296  alsa->user_data = user_data;
 
  298  if (!(alsa->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
 
  300    WLog_Print(alsa->log, WLOG_ERROR, 
"CreateEvent failed!");
 
  304  if (!(alsa->thread = CreateThread(NULL, 0, audin_alsa_thread_func, alsa, 0, NULL)))
 
  306    WLog_Print(alsa->log, WLOG_ERROR, 
"CreateThread failed!");
 
  310  return CHANNEL_RC_OK;
 
  312  (void)CloseHandle(alsa->stopEvent);
 
  313  alsa->stopEvent = NULL;
 
  314  return ERROR_INTERNAL_ERROR;
 
  322static UINT audin_alsa_close(IAudinDevice* device)
 
  324  UINT error = CHANNEL_RC_OK;
 
  325  AudinALSADevice* alsa = (AudinALSADevice*)device;
 
  328    return ERROR_INVALID_PARAMETER;
 
  332    (void)SetEvent(alsa->stopEvent);
 
  334    if (WaitForSingleObject(alsa->thread, INFINITE) == WAIT_FAILED)
 
  336      error = GetLastError();
 
  337      WLog_Print(alsa->log, WLOG_ERROR, 
"WaitForSingleObject failed with error %" PRIu32 
"",
 
  342    (void)CloseHandle(alsa->stopEvent);
 
  343    alsa->stopEvent = NULL;
 
  344    (void)CloseHandle(alsa->thread);
 
  348  alsa->receive = NULL;
 
  349  alsa->user_data = NULL;
 
  358static UINT audin_alsa_parse_addin_args(AudinALSADevice* device, 
const ADDIN_ARGV* args)
 
  363  AudinALSADevice* alsa = device;
 
  365                                                NULL, NULL, -1, NULL, 
"audio device name" },
 
  366                                              { NULL, 0, NULL, NULL, NULL, -1, NULL, NULL } };
 
  368      COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
 
  369  status = CommandLineParseArgumentsA(args->argc, args->argv, audin_alsa_args, flags, alsa, NULL,
 
  373    return ERROR_INVALID_PARAMETER;
 
  375  arg = audin_alsa_args;
 
  379    if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
 
  382    CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, 
"dev")
 
  384      alsa->device_name = _strdup(arg->Value);
 
  386      if (!alsa->device_name)
 
  388        WLog_Print(alsa->log, WLOG_ERROR, 
"_strdup failed!");
 
  389        return CHANNEL_RC_NO_MEMORY;
 
  392    CommandLineSwitchEnd(arg)
 
  393  } 
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
 
  395  return CHANNEL_RC_OK;
 
  403FREERDP_ENTRY_POINT(UINT VCAPITYPE alsa_freerdp_audin_client_subsystem_entry(
 
  407  AudinALSADevice* alsa = NULL;
 
  409  alsa = (AudinALSADevice*)calloc(1, 
sizeof(AudinALSADevice));
 
  413    WLog_ERR(TAG, 
"calloc failed!");
 
  414    return CHANNEL_RC_NO_MEMORY;
 
  417  alsa->log = WLog_Get(TAG);
 
  418  alsa->iface.Open = audin_alsa_open;
 
  419  alsa->iface.FormatSupported = audin_alsa_format_supported;
 
  420  alsa->iface.SetFormat = audin_alsa_set_format;
 
  421  alsa->iface.Close = audin_alsa_close;
 
  422  alsa->iface.Free = audin_alsa_free;
 
  423  alsa->rdpcontext = pEntryPoints->rdpcontext;
 
  424  args = pEntryPoints->args;
 
  426  if ((error = audin_alsa_parse_addin_args(alsa, args)))
 
  428    WLog_Print(alsa->log, WLOG_ERROR,
 
  429               "audin_alsa_parse_addin_args failed with errorcode %" PRIu32 
"!", error);
 
  433  if (!alsa->device_name)
 
  435    alsa->device_name = _strdup(
"default");
 
  437    if (!alsa->device_name)
 
  439      WLog_Print(alsa->log, WLOG_ERROR, 
"_strdup failed!");
 
  440      error = CHANNEL_RC_NO_MEMORY;
 
  445  alsa->frames_per_packet = 128;
 
  446  alsa->aformat.nChannels = 2;
 
  447  alsa->aformat.wBitsPerSample = 16;
 
  448  alsa->aformat.wFormatTag = WAVE_FORMAT_PCM;
 
  449  alsa->aformat.nSamplesPerSec = 44100;
 
  451  if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*)alsa)))
 
  453    WLog_Print(alsa->log, WLOG_ERROR, 
"RegisterAudinDevice failed with error %" PRIu32 
"!",
 
  458  return CHANNEL_RC_OK;
 
  460  free(alsa->device_name);