diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 91593cbb..44f248a3 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -24,6 +24,7 @@ lib_LTLIBRARIES = libguac-client-rdp.la libguac_client_rdp_la_SOURCES = \ _generated_keymaps.c \ + audio_input.c \ client.c \ input.c \ rdp.c \ @@ -44,6 +45,9 @@ libguac_client_rdp_la_SOURCES = \ unicode.c \ user.c +guacai_sources = \ + guac_ai/ai_service.c + guacsvc_sources = \ guac_svc/svc_service.c \ rdp_svc.c @@ -68,6 +72,7 @@ guacdr_sources = \ noinst_HEADERS = \ compat/client-cliprdr.h \ compat/rail.h \ + guac_ai/ai_service.h \ guac_rdpdr/rdpdr_fs_messages.h \ guac_rdpdr/rdpdr_fs_messages_dir_info.h \ guac_rdpdr/rdpdr_fs_messages_file_info.h \ @@ -79,6 +84,7 @@ noinst_HEADERS = \ guac_rdpsnd/rdpsnd_messages.h \ guac_rdpsnd/rdpsnd_service.h \ guac_svc/svc_service.h \ + audio_input.h \ client.h \ input.h \ rdp.h \ @@ -104,6 +110,7 @@ noinst_HEADERS = \ if ! ENABLE_WINPR noinst_HEADERS += compat/winpr-stream.h compat/winpr-wtypes.h libguac_client_rdp_la_SOURCES += compat/winpr-stream.c +guacai_sources += compat/winpr-stream.c guacsvc_sources += compat/winpr-stream.c guacsnd_sources += compat/winpr-stream.c guacdr_sources += compat/winpr-stream.c @@ -148,6 +155,25 @@ guacdr_libadd = \ @COMMON_LTLIB@ \ @LIBGUAC_LTLIB@ +# +# Audio Input +# + +guacai_cflags = \ + -Werror -Wall -Iinclude \ + @COMMON_INCLUDE@ \ + @COMMON_SSH_INCLUDE@ \ + @LIBGUAC_INCLUDE@ + +guacai_ldflags = \ + -module -avoid-version -shared \ + @PTHREAD_LIBS@ \ + @RDP_LIBS@ + +guacai_libadd = \ + @COMMON_LTLIB@ \ + @LIBGUAC_LTLIB@ + # # RDPSND # @@ -224,10 +250,16 @@ if LEGACY_FREERDP_EXTENSIONS # FreeRDP 1.0-style extensions freerdp_LTLIBRARIES = \ + guacai.la \ guacdr.la \ guacsnd.la \ guacsvc.la +guacai_la_SOURCES = ${guacai_sources} +guacai_la_CFLAGS = ${guacai_cflags} +guacai_la_LDFLAGS = ${guacai_ldflags} +guacai_la_LIBADD = ${guacai_libadd} + guacdr_la_SOURCES = ${guacdr_sources} guacdr_la_CFLAGS = ${guacdr_cflags} guacdr_la_LDFLAGS = ${guacdr_ldflags} @@ -247,10 +279,16 @@ else # FreeRDP 1.1 (and hopefully onward) extensions freerdp_LTLIBRARIES = \ + guacai-client.la \ guacdr-client.la \ guacsnd-client.la \ guacsvc-client.la +guacai_client_la_SOURCES = ${guacai_sources} +guacai_client_la_CFLAGS = ${guacai_cflags} +guacai_client_la_LDFLAGS = ${guacai_ldflags} +guacai_client_la_LIBADD = ${guacai_libadd} + guacdr_client_la_SOURCES = ${guacdr_sources} guacdr_client_la_CFLAGS = ${guacdr_cflags} guacdr_client_la_LDFLAGS = ${guacdr_ldflags} diff --git a/src/protocols/rdp/audio_input.c b/src/protocols/rdp/audio_input.c new file mode 100644 index 00000000..8233184a --- /dev/null +++ b/src/protocols/rdp/audio_input.c @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" +#include "audio_input.h" + +#include +#include +#include +#include +#include +#include + +#include + +int guac_rdp_audio_handler(guac_user* user, guac_stream* stream, + char* mimetype) { + + /* FIXME: Assuming mimetype of "audio/L16;rate=44100,channels=2" */ + + /* Init stream data */ + stream->blob_handler = guac_rdp_audio_blob_handler; + stream->end_handler = guac_rdp_audio_end_handler; + + guac_protocol_send_ack(user->socket, stream, + "OK", GUAC_PROTOCOL_STATUS_SUCCESS); + guac_socket_flush(user->socket); + + return 0; + +} + +int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream, + void* data, int length) { + + /* STUB */ + return 0; + +} + +int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) { + + /* STUB */ + return 0; + +} + +void guac_rdp_audio_load_plugin(rdpContext* context) { + + /* Add "AUDIO_INPUT" channel */ + ADDIN_ARGV* args = malloc(sizeof(ADDIN_ARGV)); + args->argc = 1; + args->argv = malloc(sizeof(char**) * 1); + args->argv[0] = strdup("guacai"); + freerdp_dynamic_channel_collection_add(context->settings, args); + +} + diff --git a/src/protocols/rdp/audio_input.h b/src/protocols/rdp/audio_input.h new file mode 100644 index 00000000..d32cda1e --- /dev/null +++ b/src/protocols/rdp/audio_input.h @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#ifndef GUAC_RDP_AUDIO_INPUT_H +#define GUAC_RDP_AUDIO_INPUT_H + +#include "config.h" + +#include +#include + +/** + * Handler for inbound audio data (audio input). + */ +guac_user_audio_handler guac_rdp_audio_handler; + +/** + * Handler for stream data related to audio input. + */ +guac_user_blob_handler guac_rdp_audio_blob_handler; + +/** + * Handler for end-of-stream related to audio input. + */ +guac_user_end_handler guac_rdp_audio_end_handler; + +/** + * Loads Guacamole's "guacai" plugin for FreeRDP, adding support for the + * "AUDIO_INPUT" dynamic virtual channel. This function must ONLY be called + * after FreeRDP's "drdynvc" virtual channel plugin has been loaded. + * + * @param context + * The rdpContext associated with the active RDP session. + */ +void guac_rdp_audio_load_plugin(rdpContext* context); + +#endif + diff --git a/src/protocols/rdp/guac_ai/ai_service.c b/src/protocols/rdp/guac_ai/ai_service.c new file mode 100644 index 00000000..15490a09 --- /dev/null +++ b/src/protocols/rdp/guac_ai/ai_service.c @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +#include "config.h" + +#include "ai_service.h" +#include "rdp.h" + +#include +#include + +#include +#include +#include +#include + +#ifdef ENABLE_WINPR +#include +#else +#include "compat/winpr-stream.h" +#endif + +/** + * Entry point for AUDIO_INPUT dynamic virtual channel. + */ +int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) { + + rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints); + freerdp* instance = settings->instance; + rdpContext* context = instance->context; + guac_client* client = ((rdp_freerdp_context*) context)->client; + + /* STUB */ + guac_client_log(client, GUAC_LOG_DEBUG, + "STUB: AUDIO_INPUT DVC (entry point)"); + + return 1; + +} + diff --git a/src/protocols/rdp/guac_ai/ai_service.h b/src/protocols/rdp/guac_ai/ai_service.h new file mode 100644 index 00000000..caa62adb --- /dev/null +++ b/src/protocols/rdp/guac_ai/ai_service.h @@ -0,0 +1,29 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + + +#ifndef GUAC_RDP_AI_SERVICE_H +#define GUAC_RDP_AI_SERVICE_H + +#include "config.h" + +/* STUB */ + +#endif + diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index cd3ee231..7859e209 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -19,6 +19,7 @@ #include "config.h" +#include "audio_input.h" #include "client.h" #include "guac_cursor.h" #include "guac_display.h" @@ -223,24 +224,33 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) { (pChannelConnectedEventHandler) guac_rdp_channel_connected); #endif -#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT - /* Load required plugins if display update is enabled */ - if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) { + /* Load DRDYNVC plugin if required */ + if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE + || settings->enable_audio_input) { - /* Load virtual channel management plugin (needed by display update) */ + /* Load virtual channel management plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "drdynvc", instance->settings)) guac_client_log(client, GUAC_LOG_WARNING, - "Failed to load drdynvc plugin. Display update support " - "will be disabled."); + "Failed to load drdynvc plugin. Display update and audio " + "input support will be disabled."); /* Init display update plugin if "drdynvc" was loaded successfully */ - else - guac_rdp_disp_load_plugin(instance->context); - - } + else { +#ifdef HAVE_FREERDP_DISPLAY_UPDATE_SUPPORT + /* Load "disp" plugin for display update */ + if (settings->resize_method == GUAC_RESIZE_DISPLAY_UPDATE) + guac_rdp_disp_load_plugin(instance->context); #endif + /* Load "AUDIO_INPUT" plugin for audio input*/ + if (settings->enable_audio_input) + guac_rdp_audio_load_plugin(instance->context); + + } + + } /* end if drdynvc required */ + /* Load clipboard plugin */ if (freerdp_channels_load_plugin(channels, instance->settings, "cliprdr", NULL)) diff --git a/src/protocols/rdp/rdp_settings.c b/src/protocols/rdp/rdp_settings.c index bc1d831b..4f9343c0 100644 --- a/src/protocols/rdp/rdp_settings.c +++ b/src/protocols/rdp/rdp_settings.c @@ -90,6 +90,7 @@ const char* GUAC_RDP_CLIENT_ARGS[] = { "recording-name", "create-recording-path", "resize-method", + "enable-audio-input", NULL }; @@ -378,6 +379,12 @@ enum RDP_ARGS_IDX { */ IDX_RESIZE_METHOD, + /** + * "true" if audio input (microphone) should be enabled for the RDP + * connection, "false" or blank otherwise. + */ + IDX_ENABLE_AUDIO_INPUT, + RDP_ARGS_COUNT }; @@ -739,6 +746,11 @@ guac_rdp_settings* guac_rdp_parse_args(guac_user* user, settings->resize_method = GUAC_RESIZE_NONE; } + /* Audio input enable/disable */ + settings->enable_audio_input = + guac_user_parse_args_boolean(user, GUAC_RDP_CLIENT_ARGS, argv, + IDX_ENABLE_AUDIO_INPUT, 0); + /* Success */ return settings; diff --git a/src/protocols/rdp/rdp_settings.h b/src/protocols/rdp/rdp_settings.h index 7e3fec94..90c79cee 100644 --- a/src/protocols/rdp/rdp_settings.h +++ b/src/protocols/rdp/rdp_settings.h @@ -378,6 +378,11 @@ typedef struct guac_rdp_settings { */ guac_rdp_resize_method resize_method; + /** + * Whether audio input (microphone) is enabled. + */ + int enable_audio_input; + } guac_rdp_settings; /** diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index af597dbd..ef13205f 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -19,6 +19,7 @@ #include "config.h" +#include "audio_input.h" #include "input.h" #include "guac_display.h" #include "user.h" @@ -65,6 +66,10 @@ int guac_rdp_user_join_handler(guac_user* user, int argc, char** argv) { return 1; } + /* Handle inbound audio streams if audio input is enabled */ + if (settings->enable_audio_input) + user->audio_handler = guac_rdp_audio_handler; + } /* If not owner, synchronize with current state */