diff --git a/channels/rdpsnd/alsa/rdpsnd_alsa.c b/channels/rdpsnd/alsa/rdpsnd_alsa.c index bdd4ee0c7..05a4c2159 100644 --- a/channels/rdpsnd/alsa/rdpsnd_alsa.c +++ b/channels/rdpsnd/alsa/rdpsnd_alsa.c @@ -44,6 +44,7 @@ struct rdpsnd_alsa_plugin int bytes_per_channel; int wformat; int block_size; + int latency; ADPCM adpcm; }; @@ -53,6 +54,7 @@ static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa) snd_pcm_sw_params_t* sw_params; int error; snd_pcm_uframes_t frames; + snd_pcm_uframes_t start_threshold; snd_pcm_drop(alsa->out_handle); @@ -84,8 +86,13 @@ static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa) return; } snd_pcm_sw_params_current(alsa->out_handle, sw_params); - snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params, - frames / 2); + if (alsa->latency < 0) + start_threshold = frames / 2; + else + start_threshold = alsa->latency * alsa->actual_rate / 1000; + if (start_threshold > frames) + start_threshold = frames; + snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params, start_threshold); snd_pcm_sw_params(alsa->out_handle, sw_params); snd_pcm_sw_params_free(sw_params); @@ -101,7 +108,7 @@ static void rdpsnd_alsa_set_params(rdpsndAlsaPlugin* alsa) } } -static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format) +static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) { rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device; @@ -136,10 +143,12 @@ static void rdpsnd_alsa_set_format(rdpsndDevicePlugin* device, rdpsndFormat* for alsa->block_size = format->nBlockAlign; } + alsa->latency = latency; + rdpsnd_alsa_set_params(alsa); } -static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format) +static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) { rdpsndAlsaPlugin* alsa = (rdpsndAlsaPlugin*)device; int error; @@ -158,7 +167,7 @@ static void rdpsnd_alsa_open(rdpsndDevicePlugin* device, rdpsndFormat* format) else { memset(&alsa->adpcm, 0, sizeof(ADPCM)); - rdpsnd_alsa_set_format(device, format); + rdpsnd_alsa_set_format(device, format, latency); } } @@ -286,7 +295,7 @@ static void rdpsnd_alsa_play(rdpsndDevicePlugin* device, uint8* data, int size) DEBUG_WARN("error %d", error); snd_pcm_close(alsa->out_handle); alsa->out_handle = 0; - rdpsnd_alsa_open(device, NULL); + rdpsnd_alsa_open(device, NULL, alsa->latency); break; } pindex += error * rbytes_per_frame; diff --git a/channels/rdpsnd/pulse/rdpsnd_pulse.c b/channels/rdpsnd/pulse/rdpsnd_pulse.c index 8a6434398..d88673713 100644 --- a/channels/rdpsnd/pulse/rdpsnd_pulse.c +++ b/channels/rdpsnd/pulse/rdpsnd_pulse.c @@ -40,6 +40,7 @@ struct rdpsnd_pulse_plugin pa_stream *stream; int format; int block_size; + int latency; ADPCM adpcm; }; @@ -220,23 +221,26 @@ static void rdpsnd_pulse_set_format_spec(rdpsndPulsePlugin* pulse, rdpsndFormat* pulse->block_size = format->nBlockAlign; } -static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, rdpsndFormat* format) +static void rdpsnd_pulse_open(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; pa_stream_state_t state; char ss[PA_SAMPLE_SPEC_SNPRINT_MAX]; - if (!pulse->context || pulse->stream) { - DEBUG_WARN("pulse stream has been created."); + if (!pulse->context || pulse->stream) + { + DEBUG_WARN("pulse stream has been created."); return; } rdpsnd_pulse_set_format_spec(pulse, format); + pulse->latency = latency; - if (pa_sample_spec_valid(&pulse->sample_spec) == 0) { - pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec); - DEBUG_WARN("Invalid sample spec %s", ss); - return; + if (pa_sample_spec_valid(&pulse->sample_spec) == 0) + { + pa_sample_spec_snprint(ss, sizeof(ss), &pulse->sample_spec); + DEBUG_WARN("Invalid sample spec %s", ss); + return; } pa_threaded_mainloop_lock(pulse->mainloop); @@ -358,7 +362,7 @@ static boolean rdpsnd_pulse_format_supported(rdpsndDevicePlugin* device, rdpsndF return False; } -static void rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format) +static void rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, rdpsndFormat* format, int latency) { rdpsndPulsePlugin* pulse = (rdpsndPulsePlugin*)device; @@ -370,7 +374,7 @@ static void rdpsnd_pulse_set_format(rdpsndDevicePlugin* device, rdpsndFormat* fo pulse->stream = NULL; pa_threaded_mainloop_unlock(pulse->mainloop); } - rdpsnd_pulse_open(device, format); + rdpsnd_pulse_open(device, format, latency); } static void rdpsnd_pulse_set_volume(rdpsndDevicePlugin* device, uint32 value) @@ -458,8 +462,8 @@ int FreeRDPRdpsndDeviceEntry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS pEntryPoints) data = pEntryPoints->plugin_data; if (data && strcmp((char*)data->data[0], "pulse") == 0) { - if(strlen((char*)data->data[1]) > 0) - pulse->device_name = xstrdup((char*)data->data[1]); + if(strlen((char*)data->data[1]) > 0) + pulse->device_name = xstrdup((char*)data->data[1]); else pulse->device_name = NULL; } diff --git a/channels/rdpsnd/rdpsnd_main.c b/channels/rdpsnd/rdpsnd_main.c index a36664e56..856bf1663 100644 --- a/channels/rdpsnd/rdpsnd_main.c +++ b/channels/rdpsnd/rdpsnd_main.c @@ -79,6 +79,7 @@ struct rdpsnd_plugin uint16 fixed_format; uint16 fixed_channel; uint32 fixed_rate; + int latency; /* Device plugin */ rdpsndDevicePlugin* device; @@ -315,13 +316,15 @@ static void rdpsnd_process_message_wave_info(rdpsndPlugin* rdpsnd, STREAM* data_ rdpsnd->current_format = wFormatNo; rdpsnd->is_open = True; if (rdpsnd->device) - IFCALL(rdpsnd->device->Open, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo]); + IFCALL(rdpsnd->device->Open, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo], + rdpsnd->latency); } else if (wFormatNo != rdpsnd->current_format) { rdpsnd->current_format = wFormatNo; if (rdpsnd->device) - IFCALL(rdpsnd->device->SetFormat, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo]); + IFCALL(rdpsnd->device->SetFormat, rdpsnd->device, &rdpsnd->supported_formats[wFormatNo], + rdpsnd->latency); } } @@ -480,6 +483,10 @@ static void rdpsnd_process_plugin_data(rdpsndPlugin* rdpsnd, RDP_PLUGIN_DATA* da { rdpsnd->fixed_channel = atoi(data->data[1]); } + else if (strcmp((char*)data->data[0], "latency") == 0) + { + rdpsnd->latency = atoi(data->data[1]); + } else { rdpsnd_load_device_plugin(rdpsnd, (char*)data->data[0], data); @@ -497,6 +504,7 @@ static void rdpsnd_process_connect(rdpSvcPlugin* plugin) plugin->interval_callback = rdpsnd_process_interval; rdpsnd->data_out_list = list_new(); + rdpsnd->latency = -1; data = (RDP_PLUGIN_DATA*)plugin->channel_entry_points.pExtendedData; while (data && data->size > 0) diff --git a/channels/rdpsnd/rdpsnd_main.h b/channels/rdpsnd/rdpsnd_main.h index b6897b4fa..9c127479c 100644 --- a/channels/rdpsnd/rdpsnd_main.h +++ b/channels/rdpsnd/rdpsnd_main.h @@ -37,8 +37,8 @@ struct rdpsnd_format typedef struct rdpsnd_device_plugin rdpsndDevicePlugin; typedef boolean (*pcFormatSupported) (rdpsndDevicePlugin* device, rdpsndFormat* format); -typedef void (*pcOpen) (rdpsndDevicePlugin* device, rdpsndFormat* format); -typedef void (*pcSetFormat) (rdpsndDevicePlugin* device, rdpsndFormat* format); +typedef void (*pcOpen) (rdpsndDevicePlugin* device, rdpsndFormat* format, int latency); +typedef void (*pcSetFormat) (rdpsndDevicePlugin* device, rdpsndFormat* format, int latency); typedef void (*pcSetVolume) (rdpsndDevicePlugin* device, uint32 value); typedef void (*pcPlay) (rdpsndDevicePlugin* device, uint8* data, int size); typedef void (*pcStart) (rdpsndDevicePlugin* device);