audio-input.c (5779B)
1/* 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, 13 * software distributed under the License is distributed on an 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 * KIND, either express or implied. See the License for the 16 * specific language governing permissions and limitations 17 * under the License. 18 */ 19 20#include "channels/audio-input/audio-buffer.h" 21#include "channels/audio-input/audio-input.h" 22#include "plugins/channels.h" 23#include "plugins/ptr-string.h" 24#include "rdp.h" 25 26#include <freerdp/freerdp.h> 27#include <guacamole/client.h> 28#include <guacamole/protocol.h> 29#include <guacamole/socket.h> 30#include <guacamole/stream.h> 31#include <guacamole/user.h> 32 33#include <errno.h> 34#include <stdint.h> 35#include <stdlib.h> 36#include <string.h> 37 38/** 39 * Parses the given raw audio mimetype, producing the corresponding rate, 40 * number of channels, and bytes per sample. 41 * 42 * @param mimetype 43 * The raw audio mimetype to parse. 44 * 45 * @param rate 46 * A pointer to an int where the sample rate for the PCM format described 47 * by the given mimetype should be stored. 48 * 49 * @param channels 50 * A pointer to an int where the number of channels used by the PCM format 51 * described by the given mimetype should be stored. 52 * 53 * @param bps 54 * A pointer to an int where the number of bytes used the PCM format for 55 * each sample (independent of number of channels) described by the given 56 * mimetype should be stored. 57 * 58 * @return 59 * Zero if the given mimetype is a raw audio mimetype and has been parsed 60 * successfully, non-zero otherwise. 61 */ 62static int guac_rdp_audio_parse_mimetype(const char* mimetype, 63 int* rate, int* channels, int* bps) { 64 65 int parsed_rate = -1; 66 int parsed_channels = 1; 67 int parsed_bps; 68 69 /* PCM audio with one byte per sample */ 70 if (strncmp(mimetype, "audio/L8;", 9) == 0) { 71 mimetype += 8; /* Advance to semicolon ONLY */ 72 parsed_bps = 1; 73 } 74 75 /* PCM audio with two bytes per sample */ 76 else if (strncmp(mimetype, "audio/L16;", 10) == 0) { 77 mimetype += 9; /* Advance to semicolon ONLY */ 78 parsed_bps = 2; 79 } 80 81 /* Unsupported mimetype */ 82 else 83 return 1; 84 85 /* Parse each parameter name/value pair within the mimetype */ 86 do { 87 88 /* Advance to first character of parameter (current is either a 89 * semicolon or a comma) */ 90 mimetype++; 91 92 /* Parse number of channels */ 93 if (strncmp(mimetype, "channels=", 9) == 0) { 94 95 mimetype += 9; 96 parsed_channels = strtol(mimetype, (char**) &mimetype, 10); 97 98 /* Fail if value invalid / out of range */ 99 if (errno == EINVAL || errno == ERANGE) 100 return 1; 101 102 } 103 104 /* Parse number of rate */ 105 else if (strncmp(mimetype, "rate=", 5) == 0) { 106 107 mimetype += 5; 108 parsed_rate = strtol(mimetype, (char**) &mimetype, 10); 109 110 /* Fail if value invalid / out of range */ 111 if (errno == EINVAL || errno == ERANGE) 112 return 1; 113 114 } 115 116 /* Advance to next parameter */ 117 mimetype = strchr(mimetype, ','); 118 119 } while (mimetype != NULL); 120 121 /* Mimetype is invalid if rate was not specified */ 122 if (parsed_rate == -1) 123 return 1; 124 125 /* Parse success */ 126 *rate = parsed_rate; 127 *channels = parsed_channels; 128 *bps = parsed_bps; 129 130 return 0; 131 132} 133 134int guac_rdp_audio_handler(guac_user* user, guac_stream* stream, 135 char* mimetype) { 136 137 guac_client* client = user->client; 138 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 139 140 int rate; 141 int channels; 142 int bps; 143 144 /* Parse mimetype, abort on parse error */ 145 if (guac_rdp_audio_parse_mimetype(mimetype, &rate, &channels, &bps)) { 146 guac_user_log(user, GUAC_LOG_WARNING, "Denying user audio stream with " 147 "unsupported mimetype: \"%s\"", mimetype); 148 guac_protocol_send_ack(user->socket, stream, "Unsupported audio " 149 "mimetype", GUAC_PROTOCOL_STATUS_CLIENT_BAD_TYPE); 150 return 0; 151 } 152 153 /* Init stream data */ 154 stream->blob_handler = guac_rdp_audio_blob_handler; 155 stream->end_handler = guac_rdp_audio_end_handler; 156 157 /* Associate stream with audio buffer */ 158 guac_rdp_audio_buffer_set_stream(rdp_client->audio_input, user, stream, 159 rate, channels, bps); 160 161 return 0; 162 163} 164 165int guac_rdp_audio_blob_handler(guac_user* user, guac_stream* stream, 166 void* data, int length) { 167 168 guac_client* client = user->client; 169 guac_rdp_client* rdp_client = (guac_rdp_client*) client->data; 170 171 /* Write blob to audio stream, buffering if necessary */ 172 guac_rdp_audio_buffer_write(rdp_client->audio_input, data, length); 173 174 return 0; 175 176} 177 178int guac_rdp_audio_end_handler(guac_user* user, guac_stream* stream) { 179 180 /* Ignore - the AUDIO_INPUT channel will simply not receive anything */ 181 return 0; 182 183} 184 185void guac_rdp_audio_load_plugin(rdpContext* context) { 186 187 guac_client* client = ((rdp_freerdp_context*) context)->client; 188 char client_ref[GUAC_RDP_PTR_STRING_LENGTH]; 189 190 /* Add "AUDIO_INPUT" channel */ 191 guac_rdp_ptr_to_string(client, client_ref); 192 guac_freerdp_dynamic_channel_collection_add(context->settings, "guacai", client_ref, NULL); 193 194} 195