GUACAMOLE-249: Restructure audio input such that audio buffer can be separately linked.

On some platforms, the libguacai-client.so plugin for FreeRDP reports an
unlinked symbol:

    undefined symbol: guac_freerdp_dynamic_channel_collection_add	(/usr/local/lib/freerdp2/libguacai-client.so)

This symbol is actually unused within the plugin, but may be referenced
due to being defined within a function in a common piece of source
shared between the plugin and the RDP support.

Separating the actual common components such that they can be included
by both the RDP support and the libguacai-client.so plugin removes the
potential for unused pieces being flagged as missing.
This commit is contained in:
Michael Jumper 2020-01-12 21:55:58 -08:00
parent e9846945c7
commit 4282da662f
12 changed files with 267 additions and 206 deletions

View File

@ -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 = \

View File

@ -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 <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
@ -30,169 +27,9 @@
#include <guacamole/user.h>
#include <assert.h>
#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* 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));

View File

@ -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 <freerdp/freerdp.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <pthread.h>
@ -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

View File

@ -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 <freerdp/freerdp.h>
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
/**
* 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);
}

View File

@ -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 <freerdp/freerdp.h>
#include <guacamole/user.h>
/**
* 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

View File

@ -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"

View File

@ -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"

View File

@ -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 <freerdp/dvc.h>
#include <guacamole/client.h>

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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"