cscg24-guacamole

CSCG 2024 Challenge 'Guacamole Mashup'
git clone https://git.sinitax.com/sinitax/cscg24-guacamole
Log | Files | Refs | sfeed.txt

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