diff options
author | Laxmikant Rashinkar <LK.Rashinkar@gmail.com> | 2014-09-25 06:19:31 +0400 |
---|---|---|
committer | Laxmikant Rashinkar <LK.Rashinkar@gmail.com> | 2014-09-25 06:19:31 +0400 |
commit | 7d66d0b359bc0ebdd15b3901d4035534fe7af92b (patch) | |
tree | 8eeafc32f1fde07b91d974a4bea05e28cb37df47 /channels | |
parent | 5c46c5a64de7f019ea96a11315a008dd5eb94532 (diff) |
developer checkin, phase one: added microphone redirection
Diffstat (limited to 'channels')
-rw-r--r-- | channels/rdpsnd/alsa/rdpsnd_alsa.c | 200 | ||||
-rw-r--r-- | channels/rdpsnd/rdpsnd_main.c | 187 | ||||
-rw-r--r-- | channels/rdpsnd/rdpsnd_main.h | 14 |
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 */ - |