diff --git a/src/protocols/rdp/Makefile.am b/src/protocols/rdp/Makefile.am index 8766b76a..d20d79cb 100644 --- a/src/protocols/rdp/Makefile.am +++ b/src/protocols/rdp/Makefile.am @@ -39,7 +39,8 @@ nodist_libguac_client_rdp_la_SOURCES = \ libguac_client_rdp_la_SOURCES = \ bitmap.c \ - channels/audio-input.c \ + channels/audio-input/audio-buffer.c \ + channels/audio-input/audio-input.c \ channels/cliprdr.c \ channels/common-svc.c \ channels/disp.c \ @@ -81,7 +82,8 @@ libguac_client_rdp_la_SOURCES = \ noinst_HEADERS = \ bitmap.h \ - channels/audio-input.h \ + channels/audio-input/audio-buffer.h \ + channels/audio-input/audio-input.h \ channels/cliprdr.h \ channels/common-svc.h \ channels/disp.h \ @@ -173,10 +175,10 @@ libguac_common_svc_client_la_LIBADD = \ # Audio Input # -libguacai_client_la_SOURCES = \ - channels/audio-input.c \ - plugins/guacai/guacai-messages.c \ - plugins/guacai/guacai.c \ +libguacai_client_la_SOURCES = \ + channels/audio-input/audio-buffer.c \ + plugins/guacai/guacai-messages.c \ + plugins/guacai/guacai.c \ plugins/ptr-string.c libguacai_client_la_CFLAGS = \ diff --git a/src/protocols/rdp/channels/audio-input.c b/src/protocols/rdp/channels/audio-input/audio-buffer.c similarity index 69% rename from src/protocols/rdp/channels/audio-input.c rename to src/protocols/rdp/channels/audio-input/audio-buffer.c index 29b48169..30513419 100644 --- a/src/protocols/rdp/channels/audio-input.c +++ b/src/protocols/rdp/channels/audio-input/audio-buffer.c @@ -17,12 +17,9 @@ * under the License. */ -#include "channels/audio-input.h" -#include "plugins/channels.h" -#include "plugins/ptr-string.h" +#include "channels/audio-input/audio-buffer.h" #include "rdp.h" -#include #include #include #include @@ -30,169 +27,9 @@ #include #include -#include #include #include #include -#include - -/** - * Parses the given raw audio mimetype, producing the corresponding rate, - * number of channels, and bytes per sample. - * - * @param mimetype - * The raw auduio mimetype to parse. - * - * @param rate - * A pointer to an int where the sample rate for the PCM format described - * by the given mimetype should be stored. - * - * @param channels - * A pointer to an int where the number of channels used by the PCM format - * described by the given mimetype should be stored. - * - * @param bps - * A pointer to an int where the number of bytes used the PCM format for - * each sample (independent of number of channels) described by the given - * mimetype should be stored. - * - * @return - * Zero if the given mimetype is a raw audio mimetype and has been parsed - * successfully, non-zero otherwise. - */ -static int guac_rdp_audio_parse_mimetype(const char* mimetype, - int* rate, int* channels, int* bps) { - - int parsed_rate = -1; - int parsed_channels = 1; - int parsed_bps; - - /* PCM audio with one byte per sample */ - if (strncmp(mimetype, "audio/L8;", 9) == 0) { - mimetype += 8; /* Advance to semicolon ONLY */ - parsed_bps = 1; - } - - /* PCM audio with two bytes per sample */ - else if (strncmp(mimetype, "audio/L16;", 10) == 0) { - mimetype += 9; /* Advance to semicolon ONLY */ - parsed_bps = 2; - } - - /* Unsupported mimetype */ - else - return 1; - - /* Parse each parameter name/value pair within the mimetype */ - do { - - /* Advance to first character of parameter (current is either a - * semicolon or a comma) */ - mimetype++; - - /* Parse number of channels */ - if (strncmp(mimetype, "channels=", 9) == 0) { - - mimetype += 9; - parsed_channels = strtol(mimetype, (char**) &mimetype, 10); - - /* Fail if value invalid / out of range */ - if (errno == EINVAL || errno == ERANGE) - return 1; - - } - - /* Parse number of rate */ - else if (strncmp(mimetype, "rate=", 5) == 0) { - - mimetype += 5; - parsed_rate = strtol(mimetype, (char**) &mimetype, 10); - - /* Fail if value invalid / out of range */ - if (errno == EINVAL || errno == ERANGE) - return 1; - - } - - /* Advance to next parameter */ - mimetype = strchr(mimetype, ','); - - } while (mimetype != NULL); - - /* Mimetype is invalid if rate was not specified */ - if (parsed_rate == -1) - return 1; - - /* Parse success */ - *rate = parsed_rate; - *channels = parsed_channels; - *bps = parsed_bps; - - return 0; - -} - -int guac_rdp_audio_handler(guac_user* user, guac_stream* stream, - char* mimetype) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - - int rate; - int channels; - int bps; - - /* Parse mimetype, abort on parse error */ - if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) { - guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with " - "unsupported mimetype: \"%s\"", mimetype); - guac_protocol_send_ack(user->socket, stream, "Unsupported audio " - "mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE); - return 0; - } - - /* Init stream data */ - stream->blob_handler = guac_rdp_audio_blob_handler; - stream->end_handler = guac_rdp_audio_end_handler; - - /* Associate stream with audio buffer */ - guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream, - rate, channels, bps); - - return 0; - -} - -int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream, - void* data, int length) { - - guac_client* client = user->client; - guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; - - /* Write blob to audio stream, buffering if necessary */ - guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length); - - return 0; - -} - -int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) { - - /* Ignore - the AUDIO_INPUT channel will simply not receive anything */ - return 0; - -} - -void guac_rdp_audio_load_plugin(rdpContext* context) { - - guac_client* client = ((rdp_freerdp_context*) context)->client; - char client_ref[GUAC_RDP_PTR_STRING_LENGTH]; - - /* Add "AUDIO_INPUT" channel */ - guac_rdp_ptr_to_string(client, client_ref); - guac_freerdp_dynamic_channel_collection_add(context->settings, "guacai", client_ref, NULL); - -} guac_rdp_audio_buffer* guac_rdp_audio_buffer_alloc() { guac_rdp_audio_buffer* buffer = calloc(1, sizeof(guac_rdp_audio_buffer)); diff --git a/src/protocols/rdp/channels/audio-input.h b/src/protocols/rdp/channels/audio-input/audio-buffer.h similarity index 90% rename from src/protocols/rdp/channels/audio-input.h rename to src/protocols/rdp/channels/audio-input/audio-buffer.h index 1ff76d58..32f7def8 100644 --- a/src/protocols/rdp/channels/audio-input.h +++ b/src/protocols/rdp/channels/audio-input/audio-buffer.h @@ -17,10 +17,9 @@ * under the License. */ -#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_H -#define GUAC_RDP_CHANNELS_AUDIO_INPUT_H +#ifndef GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H +#define GUAC_RDP_CHANNELS_AUDIO_INPUT_AUDIO_BUFFER_H -#include #include #include #include @@ -274,31 +273,5 @@ void guac_rdp_audio_buffer_end(guac_rdp_audio_buffer* audio_buffer); */ void guac_rdp_audio_buffer_free(guac_rdp_audio_buffer* audio_buffer); -/** - * 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; - -/** - * Adds Guacamole's "guacai" plugin to the list of dynamic virtual channel - * plugins to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only - * be loaded once the "drdynvc" plugin is loaded. The "guacai" plugin - * ultimately adds support for the "AUDIO_INPUT" dynamic virtual channel. - * - * @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/channels/audio-input/audio-input.c b/src/protocols/rdp/channels/audio-input/audio-input.c new file mode 100644 index 00000000..51fa1188 --- /dev/null +++ b/src/protocols/rdp/channels/audio-input/audio-input.c @@ -0,0 +1,195 @@ +/* + * 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 "channels/audio-input/audio-buffer.h" +#include "channels/audio-input/audio-input.h" +#include "plugins/channels.h" +#include "plugins/ptr-string.h" +#include "rdp.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * Parses the given raw audio mimetype, producing the corresponding rate, + * number of channels, and bytes per sample. + * + * @param mimetype + * The raw auduio mimetype to parse. + * + * @param rate + * A pointer to an int where the sample rate for the PCM format described + * by the given mimetype should be stored. + * + * @param channels + * A pointer to an int where the number of channels used by the PCM format + * described by the given mimetype should be stored. + * + * @param bps + * A pointer to an int where the number of bytes used the PCM format for + * each sample (independent of number of channels) described by the given + * mimetype should be stored. + * + * @return + * Zero if the given mimetype is a raw audio mimetype and has been parsed + * successfully, non-zero otherwise. + */ +static int guac_rdp_audio_parse_mimetype(const char* mimetype, + int* rate, int* channels, int* bps) { + + int parsed_rate = -1; + int parsed_channels = 1; + int parsed_bps; + + /* PCM audio with one byte per sample */ + if (strncmp(mimetype, "audio/L8;", 9) == 0) { + mimetype += 8; /* Advance to semicolon ONLY */ + parsed_bps = 1; + } + + /* PCM audio with two bytes per sample */ + else if (strncmp(mimetype, "audio/L16;", 10) == 0) { + mimetype += 9; /* Advance to semicolon ONLY */ + parsed_bps = 2; + } + + /* Unsupported mimetype */ + else + return 1; + + /* Parse each parameter name/value pair within the mimetype */ + do { + + /* Advance to first character of parameter (current is either a + * semicolon or a comma) */ + mimetype++; + + /* Parse number of channels */ + if (strncmp(mimetype, "channels=", 9) == 0) { + + mimetype += 9; + parsed_channels = strtol(mimetype, (char**) &mimetype, 10); + + /* Fail if value invalid / out of range */ + if (errno == EINVAL || errno == ERANGE) + return 1; + + } + + /* Parse number of rate */ + else if (strncmp(mimetype, "rate=", 5) == 0) { + + mimetype += 5; + parsed_rate = strtol(mimetype, (char**) &mimetype, 10); + + /* Fail if value invalid / out of range */ + if (errno == EINVAL || errno == ERANGE) + return 1; + + } + + /* Advance to next parameter */ + mimetype = strchr(mimetype, ','); + + } while (mimetype != NULL); + + /* Mimetype is invalid if rate was not specified */ + if (parsed_rate == -1) + return 1; + + /* Parse success */ + *rate = parsed_rate; + *channels = parsed_channels; + *bps = parsed_bps; + + return 0; + +} + +int guac_rdp_audio_handler(guac_user* user, guac_stream* stream, + char* mimetype) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + int rate; + int channels; + int bps; + + /* Parse mimetype, abort on parse error */ + if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) { + guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with " + "unsupported mimetype: \"%s\"", mimetype); + guac_protocol_send_ack(user->socket, stream, "Unsupported audio " + "mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE); + return 0; + } + + /* Init stream data */ + stream->blob_handler = guac_rdp_audio_blob_handler; + stream->end_handler = guac_rdp_audio_end_handler; + + /* Associate stream with audio buffer */ + guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream, + rate, channels, bps); + + return 0; + +} + +int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream, + void* data, int length) { + + guac_client* client = user->client; + guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; + + /* Write blob to audio stream, buffering if necessary */ + guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length); + + return 0; + +} + +int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) { + + /* Ignore - the AUDIO_INPUT channel will simply not receive anything */ + return 0; + +} + +void guac_rdp_audio_load_plugin(rdpContext* context) { + + guac_client* client = ((rdp_freerdp_context*) context)->client; + char client_ref[GUAC_RDP_PTR_STRING_LENGTH]; + + /* Add "AUDIO_INPUT" channel */ + guac_rdp_ptr_to_string(client, client_ref); + guac_freerdp_dynamic_channel_collection_add(context->settings, "guacai", client_ref, NULL); + +} + diff --git a/src/protocols/rdp/channels/audio-input/audio-input.h b/src/protocols/rdp/channels/audio-input/audio-input.h new file mode 100644 index 00000000..a3b705f7 --- /dev/null +++ b/src/protocols/rdp/channels/audio-input/audio-input.h @@ -0,0 +1,53 @@ +/* + * 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_CHANNELS_AUDIO_INPUT_H +#define GUAC_RDP_CHANNELS_AUDIO_INPUT_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; + +/** + * Adds Guacamole's "guacai" plugin to the list of dynamic virtual channel + * plugins to be loaded by FreeRDP's "drdynvc" plugin. The plugin will only + * be loaded once the "drdynvc" plugin is loaded. The "guacai" plugin + * ultimately adds support for the "AUDIO_INPUT" dynamic virtual channel. + * + * @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/client.c b/src/protocols/rdp/client.c index 8cbc1635..c79e59d8 100644 --- a/src/protocols/rdp/client.c +++ b/src/protocols/rdp/client.c @@ -18,7 +18,7 @@ */ #include "client.h" -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" #include "channels/cliprdr.h" #include "channels/disp.h" #include "common/recording.h" diff --git a/src/protocols/rdp/plugins/guacai/guacai-messages.c b/src/protocols/rdp/plugins/guacai/guacai-messages.c index 9e4b4142..38f7a7c4 100644 --- a/src/protocols/rdp/plugins/guacai/guacai-messages.c +++ b/src/protocols/rdp/plugins/guacai/guacai-messages.c @@ -17,7 +17,7 @@ * under the License. */ -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" #include "plugins/guacai/guacai-messages.h" #include "rdp.h" diff --git a/src/protocols/rdp/plugins/guacai/guacai-messages.h b/src/protocols/rdp/plugins/guacai/guacai-messages.h index 49fbd1cb..6a2333fc 100644 --- a/src/protocols/rdp/plugins/guacai/guacai-messages.h +++ b/src/protocols/rdp/plugins/guacai/guacai-messages.h @@ -20,7 +20,7 @@ #ifndef GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H #define GUAC_RDP_PLUGINS_GUACAI_MESSAGES_H -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" #include #include diff --git a/src/protocols/rdp/plugins/guacai/guacai.c b/src/protocols/rdp/plugins/guacai/guacai.c index 80dd87f3..15de8655 100644 --- a/src/protocols/rdp/plugins/guacai/guacai.c +++ b/src/protocols/rdp/plugins/guacai/guacai.c @@ -17,7 +17,7 @@ * under the License. */ -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" #include "plugins/guacai/guacai.h" #include "plugins/guacai/guacai-messages.h" #include "plugins/ptr-string.h" diff --git a/src/protocols/rdp/rdp.c b/src/protocols/rdp/rdp.c index 07475c56..5cf651ad 100644 --- a/src/protocols/rdp/rdp.c +++ b/src/protocols/rdp/rdp.c @@ -18,7 +18,8 @@ */ #include "bitmap.h" -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" +#include "channels/audio-input/audio-input.h" #include "channels/cliprdr.h" #include "channels/disp.h" #include "channels/pipe-svc.h" diff --git a/src/protocols/rdp/rdp.h b/src/protocols/rdp/rdp.h index 43f6dc2d..32f72df7 100644 --- a/src/protocols/rdp/rdp.h +++ b/src/protocols/rdp/rdp.h @@ -20,7 +20,7 @@ #ifndef GUAC_RDP_H #define GUAC_RDP_H -#include "channels/audio-input.h" +#include "channels/audio-input/audio-buffer.h" #include "channels/cliprdr.h" #include "channels/disp.h" #include "common/clipboard.h" diff --git a/src/protocols/rdp/user.c b/src/protocols/rdp/user.c index 2e96b9a2..38f2623b 100644 --- a/src/protocols/rdp/user.c +++ b/src/protocols/rdp/user.c @@ -17,7 +17,7 @@ * under the License. */ -#include "channels/audio-input.h" +#include "channels/audio-input/audio-input.h" #include "channels/cliprdr.h" #include "channels/pipe-svc.h" #include "common/cursor.h"