From 76e3b2ebb8478cffb0b6989753634b214b87a494 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Tue, 1 Mar 2016 09:39:34 -0800 Subject: [PATCH] GUAC-1389: Restore libguac's high-level convenience functions for audio streaming. --- configure.ac | 3 - src/libguac/Makefile.am | 3 +- src/libguac/audio.c | 106 ++++++++++++++++++-------- src/libguac/guacamole/audio-fntypes.h | 43 ++++++++++- src/libguac/guacamole/audio.h | 43 +++++++++-- src/libguac/raw_encoder.c | 28 +++++-- 6 files changed, 173 insertions(+), 53 deletions(-) diff --git a/configure.ac b/configure.ac index 0a680267..1617c295 100644 --- a/configure.ac +++ b/configure.ac @@ -230,9 +230,6 @@ AC_ARG_WITH([pulse], [], [with_pulse=check]) -# FIXME: Temporarily disabled -with_pulse=no - if test "x$with_pulse" != "xno" then have_pulse=yes diff --git a/src/libguac/Makefile.am b/src/libguac/Makefile.am index ffb6663a..e18ff338 100644 --- a/src/libguac/Makefile.am +++ b/src/libguac/Makefile.am @@ -73,8 +73,8 @@ noinst_HEADERS = \ user-handlers.h \ raw_encoder.h -# Temporarily removed audio.c and raw_encoder.c for testing of others libguac_la_SOURCES = \ + audio.c \ client.c \ encode-jpeg.c \ encode-png.c \ @@ -85,6 +85,7 @@ libguac_la_SOURCES = \ parser.c \ pool.c \ protocol.c \ + raw_encoder.c \ socket.c \ socket-fd.c \ socket-nest.c \ diff --git a/src/libguac/audio.c b/src/libguac/audio.c index 8d340be8..3ff7615e 100644 --- a/src/libguac/audio.c +++ b/src/libguac/audio.c @@ -28,51 +28,74 @@ #include #include #include +#include #include #include +/** + * Assigns a new audio encoder to the given guac_audio_stream based on the + * audio mimetypes declared as supported by the given user. If no audio encoder + * can be found, no new audio encoder is assigned, and the existing encoder is + * left untouched (if any). + * + * @param owner + * The user whose supported audio mimetypes should determine the audio + * encoder selected. It is expected that this user will be the owner of + * the connection. + * + * @param data + * The guac_audio_stream to which the new encoder should be assigned. + * Existing properties set on this audio stream, such as the bits per + * sample, may affect the encoder chosen. + * + * @return + * The assigned audio encoder. If no new audio encoder can be assigned, + * this will be the currently-assigned audio encoder (which may be NULL). + */ +static void* guac_audio_assign_encoder(guac_user* owner, void* data) { + + int i; + + guac_audio_stream* audio = (guac_audio_stream*) data; + int bps = audio->bps; + + /* If there is no owner, do not attempt to assign a new encoder */ + if (owner == NULL) + return audio->encoder; + + /* For each supported mimetype, check for an associated encoder */ + for (i=0; owner->info.audio_mimetypes[i] != NULL; i++) { + + const char* mimetype = owner->info.audio_mimetypes[i]; + + /* If 16-bit raw audio is supported, done. */ + if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) { + audio->encoder = raw16_encoder; + break; + } + + /* If 8-bit raw audio is supported, done. */ + if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) { + audio->encoder = raw8_encoder; + break; + } + + } /* end for each mimetype */ + + /* Return assigned encoder, if any */ + return audio->encoder; + +} + guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encoder* encoder, int rate, int channels, int bps) { guac_audio_stream* audio; - /* Choose an encoding if not specified */ - if (encoder == NULL) { - - int i; - - /* For each supported mimetype, check for an associated encoder */ - for (i=0; client->info.audio_mimetypes[i] != NULL; i++) { - - const char* mimetype = client->info.audio_mimetypes[i]; - - /* If 16-bit raw audio is supported, done. */ - if (bps == 16 && strcmp(mimetype, raw16_encoder->mimetype) == 0) { - encoder = raw16_encoder; - break; - } - - /* If 8-bit raw audio is supported, done. */ - if (bps == 8 && strcmp(mimetype, raw8_encoder->mimetype) == 0) { - encoder = raw8_encoder; - break; - } - - } /* end for each mimetype */ - - /* If still no encoder could be found, fail */ - if (encoder == NULL) - return NULL; - - } - /* Allocate stream */ audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream)); audio->client = client; - - /* Assign encoder */ - audio->encoder = encoder; audio->stream = guac_client_alloc_stream(client); /* Load PCM properties */ @@ -80,6 +103,13 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, audio->channels = channels; audio->bps = bps; + /* Assign encoder for owner, abort if no encoder can be found */ + if (!guac_client_for_owner(client, guac_audio_assign_encoder, audio)) { + guac_client_free_stream(client, audio->stream); + free(audio); + return NULL; + } + /* Call handler, if defined */ if (audio->encoder->begin_handler) audio->encoder->begin_handler(audio); @@ -118,6 +148,16 @@ void guac_audio_stream_reset(guac_audio_stream* audio, } +void guac_audio_stream_add_user(guac_audio_stream* audio, guac_user* user) { + + guac_audio_encoder* encoder = audio->encoder; + + /* Notify encoder that a new user is present */ + if (encoder->join_handler) + encoder->join_handler(audio, user); + +} + void guac_audio_stream_free(guac_audio_stream* audio) { /* Flush stream encoding */ diff --git a/src/libguac/guacamole/audio-fntypes.h b/src/libguac/guacamole/audio-fntypes.h index 7880eac7..718120da 100644 --- a/src/libguac/guacamole/audio-fntypes.h +++ b/src/libguac/guacamole/audio-fntypes.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Glyptodon LLC + * Copyright (C) 2016 Glyptodon LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,8 +20,8 @@ * THE SOFTWARE. */ -#ifndef __GUAC_AUDIO_FNTYPES_H -#define __GUAC_AUDIO_FNTYPES_H +#ifndef GUAC_AUDIO_FNTYPES_H +#define GUAC_AUDIO_FNTYPES_H /** * Function type definitions related to simple streaming audio. @@ -30,24 +30,59 @@ */ #include "audio-types.h" +#include "user-types.h" /** * Handler which is called when the audio stream is opened. + * + * @param audio + * The audio stream being opened. */ typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio); /** * Handler which is called when the audio stream needs to be flushed. + * + * @param audio + * The audio stream being flushed. */ typedef void guac_audio_encoder_flush_handler(guac_audio_stream* audio); /** * Handler which is called when the audio stream is closed. + * + * @param audio + * The audio stream being closed. */ typedef void guac_audio_encoder_end_handler(guac_audio_stream* audio); /** - * Handler which is called when PCM data is written to the audio stream. + * Handler which is called when a new user has joined the Guacamole + * connection associated with the audio stream. + * + * @param audio + * The audio stream associated with the Guacamole connection being + * joined. + * + * @param user + * The user that joined the connection. + */ +typedef void guac_audio_encoder_join_handler(guac_audio_stream* audio, + guac_user* user); + +/** + * Handler which is called when PCM data is written to the audio stream. The + * format of the PCM data is dictated by the properties of the audio stream. + * + * @param audio + * The audio stream to which data is being written. + * + * @param pcm_data + * A buffer containing the raw PCM data to be written. + * + * @param length + * The number of bytes within the buffer that should be written to the + * audio stream. */ typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio, const unsigned char* pcm_data, int length); diff --git a/src/libguac/guacamole/audio.h b/src/libguac/guacamole/audio.h index 6b6696fd..4d28a9cf 100644 --- a/src/libguac/guacamole/audio.h +++ b/src/libguac/guacamole/audio.h @@ -64,6 +64,12 @@ struct guac_audio_encoder { */ guac_audio_encoder_end_handler* end_handler; + /** + * Handler which will be called when a new user joins the Guacamole + * connection associated with an audio stream. + */ + guac_audio_encoder_join_handler* join_handler; + }; struct guac_audio_stream { @@ -108,15 +114,24 @@ struct guac_audio_stream { }; /** - * Allocates a new audio stream which encodes audio data using the given - * encoder. If NULL is specified for the encoder, an appropriate encoder - * will be selected based on the encoders built into libguac and the level - * of client support. The PCM format specified here (via rate, channels, and + * Allocates a new audio stream at the client level which encodes audio data + * using the given encoder. If NULL is specified for the encoder, an + * appropriate encoder will be selected based on the encoders built into + * libguac and the level of support declared by the owner associated with the + * given guac_client. The PCM format specified here (via rate, channels, and * bps) must be the format used for all PCM data provided to the audio stream. * The format may only be changed using guac_audio_stream_reset(). * + * If a new user joins the connection after the audio stream is created, that + * user will not be aware of the existence of the audio stream, and + * guac_audio_stream_add_user() will need to be invoked to recreate the stream + * for the new user. + * * @param client - * The guac_client for which this audio stream is being allocated. + * The guac_client for which this audio stream is being allocated. Only the + * connection owner is used to determine the level of audio support, and it + * is currently assumed that all other joining users on the connection will + * have the same level of audio support. * * @param encoder * The guac_audio_encoder to use when encoding audio, or NULL if libguac @@ -135,7 +150,8 @@ struct guac_audio_stream { * * @return * The newly allocated guac_audio_stream, or NULL if no audio stream could - * be allocated due to lack of client support. + * be allocated due to lack of support on the part of the connecting + * Guacamole client. */ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encoder* encoder, int rate, int channels, int bps); @@ -168,6 +184,21 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, void guac_audio_stream_reset(guac_audio_stream* audio, guac_audio_encoder* encoder, int rate, int channels, int bps); +/** + * Notifies the given audio stream that a user has joined the connection. The + * audio stream itself may need to be restarted. and the audio stream will need + * to be created for the new user to ensure they can properly handle future + * data received along the stream. + * + * @param audio + * The guac_audio_stream associated with the Guacamole connection being + * joined. + * + * @param user + * The user that has joined the Guacamole connection. + */ +void guac_audio_stream_add_user(guac_audio_stream* audio, guac_user* user); + /** * Closes and frees the given audio stream. * diff --git a/src/libguac/raw_encoder.c b/src/libguac/raw_encoder.c index 91efdc0f..5de3ad1d 100644 --- a/src/libguac/raw_encoder.c +++ b/src/libguac/raw_encoder.c @@ -29,14 +29,15 @@ #include #include #include +#include #include #include #include -static void raw_encoder_begin_handler(guac_audio_stream* audio) { +static void raw_encoder_send_audio(guac_audio_stream* audio, + guac_socket* socket) { - raw_encoder_state* state; char mimetype[256]; /* Produce mimetype string from format info */ @@ -44,7 +45,16 @@ static void raw_encoder_begin_handler(guac_audio_stream* audio) { audio->bps, audio->rate, audio->channels); /* Associate stream */ - guac_protocol_send_audio(audio->client->socket, audio->stream, mimetype); + guac_protocol_send_audio(socket, audio->stream, mimetype); + +} + +static void raw_encoder_begin_handler(guac_audio_stream* audio) { + + raw_encoder_state* state; + + /* Broadcast existence of stream */ + raw_encoder_send_audio(audio, audio->client->socket); /* Allocate and init encoder state */ audio->data = state = malloc(sizeof(raw_encoder_state)); @@ -55,9 +65,13 @@ static void raw_encoder_begin_handler(guac_audio_stream* audio) { state->buffer = malloc(state->length); - guac_client_log(audio->client, GUAC_LOG_DEBUG, - "Using raw encoder (%s) with a %i byte buffer.", - mimetype, state->length); +} + +static void raw_encoder_join_handler(guac_audio_stream* audio, + guac_user* user) { + + /* Notify user of existence of stream */ + raw_encoder_send_audio(audio, user->socket); } @@ -143,6 +157,7 @@ guac_audio_encoder _raw8_encoder = { .begin_handler = raw_encoder_begin_handler, .write_handler = raw_encoder_write_handler, .flush_handler = raw_encoder_flush_handler, + .join_handler = raw_encoder_join_handler, .end_handler = raw_encoder_end_handler }; @@ -152,6 +167,7 @@ guac_audio_encoder _raw16_encoder = { .begin_handler = raw_encoder_begin_handler, .write_handler = raw_encoder_write_handler, .flush_handler = raw_encoder_flush_handler, + .join_handler = raw_encoder_join_handler, .end_handler = raw_encoder_end_handler };