Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/neutrinolabs/NeutrinoRDP.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLaxmikant Rashinkar <LK.Rashinkar@gmail.com>2014-09-25 06:19:31 +0400
committerLaxmikant Rashinkar <LK.Rashinkar@gmail.com>2014-09-25 06:19:31 +0400
commit7d66d0b359bc0ebdd15b3901d4035534fe7af92b (patch)
tree8eeafc32f1fde07b91d974a4bea05e28cb37df47 /channels
parent5c46c5a64de7f019ea96a11315a008dd5eb94532 (diff)
developer checkin, phase one: added microphone redirection
Diffstat (limited to 'channels')
-rw-r--r--channels/rdpsnd/alsa/rdpsnd_alsa.c200
-rw-r--r--channels/rdpsnd/rdpsnd_main.c187
-rw-r--r--channels/rdpsnd/rdpsnd_main.h14
3 files changed, 386 insertions, 15 deletions
diff --git a/channels/rdpsnd/alsa/rdpsnd_alsa.c b/channels/rdpsnd/alsa/rdpsnd_alsa.c
index 24dc91b..7c58ad2 100644
--- a/channels/rdpsnd/alsa/rdpsnd_alsa.c
+++ b/channels/rdpsnd/alsa/rdpsnd_alsa.c
@@ -29,23 +29,31 @@
#include "rdpsnd_main.h"
+static int rdpsnd_alsa_rec_close(rdpsndDevicePlugin* device);
+
typedef struct rdpsnd_alsa_plugin rdpsndAlsaPlugin;
struct rdpsnd_alsa_plugin
{
rdpsndDevicePlugin device;
- char* device_name;
- snd_pcm_t* out_handle;
- uint32 source_rate;
- uint32 actual_rate;
- snd_pcm_format_t format;
- uint32 source_channels;
- uint32 actual_channels;
- int bytes_per_channel;
- int wformat;
- int block_size;
- int latency;
- ADPCM adpcm;
+ char* device_name;
+ snd_pcm_t* out_handle;
+ snd_pcm_t* rec_handle;
+ uint32 source_rate;
+ uint32 actual_rate;
+ snd_pcm_format_t format;
+ uint32 source_channels;
+ uint32 actual_channels;
+ int bytes_per_channel;
+ int wformat;
+ int block_size;
+ int latency;
+ ADPCM adpcm;
+
+ /* for recording */
+ int rec_dev_opened;
+ int rec_bytes_per_sample;
+ int rec_num_channels;
};
static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa)
@@ -162,7 +170,7 @@ static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format, i
DEBUG_SVC("opening");
error = snd_pcm_open(&alsa->out_handle, alsa->device_name,
- SND_PCM_STREAM_PLAYBACK, 0);
+ SND_PCM_STREAM_PLAYBACK, 0);
if (error < 0)
{
DEBUG_WARN("snd_pcm_open failed");
@@ -188,6 +196,169 @@ static void rdpsnd_alsa_close(rdpsndDevicePlugin* device)
}
}
+static int rdpsnd_alsa_rec_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
+{
+ snd_pcm_hw_params_t* hwparams = NULL;
+ int err;
+ unsigned int buffertime;
+ short samplewidth;
+ int audiochannels;
+ unsigned int rate;
+
+ rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device;
+ snd_pcm_t* pcm = alsa->rec_handle;
+
+ samplewidth = format->wBitsPerSample / 8;
+
+ if ((err = snd_pcm_hw_params_malloc(&hwparams)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_malloc() failed");
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_any(pcm, hwparams)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_any() failed");
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params_set_access(pcm, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_access() failed");
+ return -1;
+ }
+
+ if (format->wBitsPerSample == 16)
+ {
+ if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S16_LE)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_format() failed");
+ return -1;
+ }
+ }
+ else
+ {
+ if ((err = snd_pcm_hw_params_set_format(pcm, hwparams, SND_PCM_FORMAT_S8)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_format() failed");
+ return -1;
+ }
+ }
+
+#if 0
+ if ((err = snd_pcm_hw_params_set_rate_resample(pcm, hwparams, 1)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_rate_resample() failed");
+ return -1;
+ }
+#endif
+
+ rate = format->nSamplesPerSec;
+ if ((err = snd_pcm_hw_params_set_rate_near(pcm, hwparams, &rate, 0)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_rate_near() failed");
+ return -1;
+ }
+
+ audiochannels = format->nChannels;
+ if ((err = snd_pcm_hw_params_set_channels(pcm, hwparams, format->nChannels)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_channels() failed");
+ return -1;
+ }
+
+ buffertime = 500000; /* microseconds */
+ if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, hwparams, &buffertime, 0)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params_set_buffer_time_near() failed");
+ return -1;
+ }
+
+ if ((err = snd_pcm_hw_params(pcm, hwparams)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_hw_params() failed");
+ return -1;
+ }
+
+ snd_pcm_hw_params_free(hwparams);
+
+ if ((err = snd_pcm_prepare(pcm)) < 0)
+ {
+ DEBUG_SVC("snd_pcm_prepare() failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rdpsnd_alsa_rec_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency)
+{
+ rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
+ int err;
+
+ /* if recording device already open, just return */
+ if (alsa->rec_handle != 0)
+ { DEBUG_WARN("Recording device already open");
+ return -1;
+ }
+
+ /* open recording device */
+ if (snd_pcm_open(&alsa->rec_handle, alsa->device_name,
+ SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0)
+ {
+ DEBUG_WARN("Error opening alsa device: %s", alsa->device_name);
+ return -1;
+ }
+
+ /* set recording format */
+ if (rdpsnd_alsa_rec_set_format(device, format, latency))
+ {
+ rdpsnd_alsa_rec_close(device);
+ DEBUG_WARN("Error setting recording format on alsa device: %s", alsa->device_name);
+ return -1;
+ }
+
+ /* start recording */
+ if ((err = snd_pcm_start(alsa->rec_handle)) < 0)
+ DEBUG_SVC("snd_pcm_start() failed");
+
+ alsa->rec_bytes_per_sample = format->wBitsPerSample / 8;
+ alsa->rec_num_channels = format->nChannels;
+
+ return 0;
+}
+
+static int rdpsnd_alsa_rec_close(rdpsndDevicePlugin* device)
+{
+ rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
+
+ if (alsa->rec_handle == 0)
+ return 0;
+
+ snd_pcm_drain(alsa->rec_handle);
+ snd_pcm_close(alsa->rec_handle);
+ alsa->rec_handle = 0;
+
+ return 0;
+}
+
+static int rdpsnd_alsa_rec_capture(rdpsndDevicePlugin* device, char* data_buffer, int buf_len)
+{
+ rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*) device;
+ int len;
+
+ len = snd_pcm_readi(alsa->rec_handle, data_buffer,
+ buf_len / (alsa->rec_bytes_per_sample * alsa->rec_num_channels));
+
+ if (len < 0)
+ {
+ snd_pcm_prepare(alsa->rec_handle);
+ len = 0;
+ }
+
+ return len * alsa->rec_bytes_per_sample * alsa->rec_num_channels;
+}
+
static void rdpsnd_alsa_free(rdpsndDevicePlugin* device)
{
rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device;
@@ -335,6 +506,9 @@ int FreeRDPRdpsndDeviceEntry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints)
alsa->device.Play = rdpsnd_alsa_play;
alsa->device.Start = rdpsnd_alsa_start;
alsa->device.Close = rdpsnd_alsa_close;
+ alsa->device.RecOpen = rdpsnd_alsa_rec_open;
+ alsa->device.RecClose = rdpsnd_alsa_rec_close;
+ alsa->device.RecCapture = rdpsnd_alsa_rec_capture;
alsa->device.Free = rdpsnd_alsa_free;
data = pEntryPoints->plugin_data;
diff --git a/channels/rdpsnd/rdpsnd_main.c b/channels/rdpsnd/rdpsnd_main.c
index cc4b115..324c686 100644
--- a/channels/rdpsnd/rdpsnd_main.c
+++ b/channels/rdpsnd/rdpsnd_main.c
@@ -67,6 +67,12 @@ struct rdpsnd_plugin
int n_supported_formats;
int current_format;
+ /* for audio recording */
+ rdpsndFormat* supported_formats_rec;
+ int n_supported_formats_rec;
+ int current_format_rec;
+ int rec_device_opened;
+
tbool expectingWave;
uint8 waveData[4];
uint16 waveDataSize;
@@ -103,10 +109,14 @@ static uint32 get_mstime(void)
/* process the linked list of data that has queued to be sent */
static void rdpsnd_process_interval(rdpSvcPlugin* plugin)
{
+ int len;
+
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)plugin;
struct data_out_item* item;
uint32 cur_time;
+ /* handle playback */
+
while (rdpsnd->data_out_list->head)
{
item = (struct data_out_item*)rdpsnd->data_out_list->head->data;
@@ -139,6 +149,25 @@ static void rdpsnd_process_interval(rdpSvcPlugin* plugin)
{
rdpsnd->plugin.interval_ms = 0;
}
+
+ /* handle recording */
+
+ if (rdpsnd->rec_device_opened)
+ {
+ STREAM* out = stream_new(4 + 32768);
+
+ len = rdpsnd->device->RecCapture(rdpsnd->device,
+ (char *) &out->data[4], 32768);
+ if (len)
+ {
+ stream_write_uint16(out, RDPSND_REC_DATA);
+ stream_write_uint16(out, len);
+ stream_set_pos(out, 4 + len);
+ svc_plugin_send((rdpSvcPlugin*) plugin, out);
+ }
+
+ rdpsnd->plugin.interval_ms = 10;
+ }
}
static void rdpsnd_free_supported_formats(rdpsndPlugin* rdpsnd)
@@ -386,6 +415,153 @@ static void rdpsnd_process_message_setvolume(rdpsndPlugin* rdpsnd, STREAM* data_
IFCALL(rdpsnd->device->SetVolume, rdpsnd->device, dwVolume);
}
+static void rdpsnd_process_message_rec_negotiate(rdpsndPlugin* rdpsnd, STREAM* data_in)
+{
+ STREAM* out;
+ uint16 numFormats;
+ uint16 wVersion;
+ rdpsndFormat* rec_formats;
+ rdpsndFormat* format;
+ int pos;
+ int i;
+
+ stream_seek_uint32(data_in); /* unused */
+ stream_seek_uint32(data_in); /* unused */
+ stream_read_uint16(data_in, numFormats);
+ stream_read_uint16(data_in, wVersion);
+
+ // RASH TODO this needs to be freed when plugin is freed
+ rec_formats = (rdpsndFormat *) xzalloc(numFormats * sizeof(rdpsndFormat));
+
+ for (i = 0; i < numFormats; i++)
+ {
+ format = &rec_formats[i];
+ stream_read_uint16(data_in, format->wFormatTag);
+ stream_read_uint16(data_in, format->nChannels);
+ stream_read_uint32(data_in, format->nSamplesPerSec);
+ stream_read_uint32(data_in, format->nAvgBytesPerSec);
+ stream_read_uint16(data_in, format->nBlockAlign);
+ stream_read_uint16(data_in, format->wBitsPerSample);
+ stream_read_uint16(data_in, format->cbSize);
+
+ if (format->cbSize > 0)
+ {
+ format->data = xmalloc(format->cbSize);
+ stream_read(data_in, format->data, format->cbSize);
+ }
+ }
+
+ rdpsnd->n_supported_formats_rec = numFormats;
+ if (numFormats > 0)
+ {
+ rdpsnd->supported_formats_rec = rec_formats;
+ }
+ else
+ {
+ xfree(rec_formats);
+ DEBUG_WARN("no recording formats supported");
+ }
+
+ /*
+ * let server know we support the same recording formats
+ */
+
+ out = stream_new(16 + (18 * numFormats));
+
+ stream_write_uint8(out, RDPSND_REC_NEGOTIATE);
+ stream_write_uint8(out, 1);
+ stream_write_uint16(out, 0); /* write real bytes to follow later */
+
+ stream_write_uint32(out, 0); /* flags - unused */
+ stream_write_uint32(out, 0); /* volume - unused */
+ stream_write_uint16(out, numFormats);
+ stream_write_uint16(out, 1); /* version */
+
+ for (i = 0; i < numFormats; i++)
+ {
+ format = &rec_formats[i];
+ stream_write_uint16(out, format->wFormatTag);
+ stream_write_uint16(out, format->nChannels);
+ stream_write_uint32(out, format->nSamplesPerSec);
+ stream_write_uint32(out, format->nAvgBytesPerSec);
+ stream_write_uint16(out, format->nBlockAlign);
+ stream_write_uint16(out, format->wBitsPerSample);
+ stream_write_uint16(out, 0); /* cbSize not used */
+ }
+
+ pos = stream_get_pos(out);
+ stream_set_pos(out, 2);
+ stream_write_uint16(out, pos - 4); /* real bytes to follow */
+ stream_set_pos(out, pos);
+
+ svc_plugin_send((rdpSvcPlugin*) rdpsnd, out);
+}
+
+static void
+rdpsnd_process_message_rec_start(rdpsndPlugin* rdpsnd, STREAM* data_in)
+{
+ int formatIndex;
+
+ /* if rec dev already opened, just return */
+ if (rdpsnd->rec_device_opened)
+ {
+ DEBUG_WARN("recording already in progress");
+ return;
+ }
+
+ /* if plugin not inited, cant continue */
+ if (!rdpsnd->device)
+ {
+ DEBUG_WARN("sound plugin in NULL");
+ return;
+ }
+
+ /* get recording format and save it */
+ stream_read_uint16(data_in, formatIndex);
+ if (formatIndex > rdpsnd->n_supported_formats_rec)
+ {
+ DEBUG_WARN("Unsupported recording format");
+ return;
+ }
+
+ rdpsnd->current_format_rec = formatIndex;
+
+ // RASH_TODO need to support latency
+ /* open recording device and set flag */
+ if (rdpsnd->device->RecOpen(rdpsnd->device, &rdpsnd->supported_formats_rec[formatIndex], 0))
+ {
+ DEBUG_WARN("Error opening recording device");
+ return;
+ }
+
+ rdpsnd->rec_device_opened = 1;
+ rdpsnd->plugin.interval_ms = 10;
+}
+
+static void
+rdpsnd_process_message_rec_stop(rdpsndPlugin* rdpsnd, STREAM* data_in)
+{
+ /* if rec dev not opened, just return */
+ if (!rdpsnd->rec_device_opened)
+ {
+ DEBUG_WARN("recording device not opened");
+ return;
+ }
+
+ /* if plugin not inited, cant continue */
+ if (!rdpsnd->device)
+ {
+ DEBUG_WARN("sound plugin in NULL");
+ return;
+ }
+
+ rdpsnd->device->RecClose(rdpsnd->device);
+ rdpsnd->rec_device_opened = 0;
+ rdpsnd->plugin.interval_ms = 0;
+
+ return;
+}
+
static void rdpsnd_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
{
rdpsndPlugin* rdpsnd = (rdpsndPlugin*)plugin;
@@ -422,6 +598,15 @@ static void rdpsnd_process_receive(rdpSvcPlugin* plugin, STREAM* data_in)
case SNDC_SETVOLUME:
rdpsnd_process_message_setvolume(rdpsnd, data_in);
break;
+ case RDPSND_REC_NEGOTIATE:
+ rdpsnd_process_message_rec_negotiate(rdpsnd, data_in);
+ break;
+ case RDPSND_REC_START:
+ rdpsnd_process_message_rec_start(rdpsnd, data_in);
+ break;
+ case RDPSND_REC_STOP:
+ rdpsnd_process_message_rec_stop(rdpsnd, data_in);
+ break;
default:
DEBUG_WARN("unknown msgType %d", msgType);
break;
@@ -520,6 +705,7 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin)
default_data[0].size = sizeof(RDP_PLUGIN_DATA);
default_data[0].data[0] = "pulse";
default_data[0].data[1] = "";
+
if (!rdpsnd_load_device_plugin(rdpsnd, "pulse", default_data))
{
default_data[0].data[0] = "alsa";
@@ -560,4 +746,3 @@ static void rdpsnd_process_terminate(rdpSvcPlugin* plugin)
DEFINE_SVC_PLUGIN(rdpsnd, "rdpsnd",
CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP)
-
diff --git a/channels/rdpsnd/rdpsnd_main.h b/channels/rdpsnd/rdpsnd_main.h
index 9c12747..df26e0a 100644
--- a/channels/rdpsnd/rdpsnd_main.h
+++ b/channels/rdpsnd/rdpsnd_main.h
@@ -20,6 +20,12 @@
#ifndef __RDPSND_MAIN_H
#define __RDPSND_MAIN_H
+#define RDPSND_REC_NEGOTIATE 39
+#define RDPSND_REC_START 40
+#define RDPSND_REC_STOP 41
+#define RDPSND_REC_DATA 42
+#define RDPSND_REC_SET_VOLUME 43
+
typedef struct rdpsnd_plugin rdpsndPlugin;
typedef struct rdpsnd_format rdpsndFormat;
@@ -28,6 +34,7 @@ struct rdpsnd_format
uint16 wFormatTag;
uint16 nChannels;
uint32 nSamplesPerSec;
+ uint32 nAvgBytesPerSec;
uint16 nBlockAlign;
uint16 wBitsPerSample;
uint16 cbSize;
@@ -44,6 +51,9 @@ typedef void (*pcPlay) (rdpsndDevicePlugin* device, uint8* data, int size);
typedef void (*pcStart) (rdpsndDevicePlugin* device);
typedef void (*pcClose) (rdpsndDevicePlugin* device);
typedef void (*pcFree) (rdpsndDevicePlugin* device);
+typedef int (*pcRecOpen) (rdpsndDevicePlugin* device, rdpsndFormat* format, int latency);
+typedef int (*pcRecClose) (rdpsndDevicePlugin* device);
+typedef int (*pcRecCapture) (rdpsndDevicePlugin* device, char* data_bufer, int buf_len);
struct rdpsnd_device_plugin
{
@@ -55,6 +65,9 @@ struct rdpsnd_device_plugin
pcStart Start;
pcClose Close;
pcFree Free;
+ pcRecOpen RecOpen;
+ pcRecClose RecClose;
+ pcRecCapture RecCapture;
};
#define RDPSND_DEVICE_EXPORT_FUNC_NAME "FreeRDPRdpsndDeviceEntry"
@@ -73,4 +86,3 @@ typedef FREERDP_RDPSND_DEVICE_ENTRY_POINTS* PFREERDP_RDPSND_DEVICE_ENTRY_POINTS;
typedef int (*PFREERDP_RDPSND_DEVICE_ENTRY)(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints);
#endif /* __RDPSND_MAIN_H */
-