GUAC-1389: Restore libguac's high-level convenience functions for audio streaming.

This commit is contained in:
Michael Jumper 2016-03-01 09:39:34 -08:00
parent fbe4d53fb1
commit 76e3b2ebb8
6 changed files with 173 additions and 53 deletions

View File

@ -230,9 +230,6 @@ AC_ARG_WITH([pulse],
[], [],
[with_pulse=check]) [with_pulse=check])
# FIXME: Temporarily disabled
with_pulse=no
if test "x$with_pulse" != "xno" if test "x$with_pulse" != "xno"
then then
have_pulse=yes have_pulse=yes

View File

@ -73,8 +73,8 @@ noinst_HEADERS = \
user-handlers.h \ user-handlers.h \
raw_encoder.h raw_encoder.h
# Temporarily removed audio.c and raw_encoder.c for testing of others
libguac_la_SOURCES = \ libguac_la_SOURCES = \
audio.c \
client.c \ client.c \
encode-jpeg.c \ encode-jpeg.c \
encode-png.c \ encode-png.c \
@ -85,6 +85,7 @@ libguac_la_SOURCES = \
parser.c \ parser.c \
pool.c \ pool.c \
protocol.c \ protocol.c \
raw_encoder.c \
socket.c \ socket.c \
socket-fd.c \ socket-fd.c \
socket-nest.c \ socket-nest.c \

View File

@ -28,51 +28,74 @@
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/stream.h> #include <guacamole/stream.h>
#include <guacamole/user.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
/**
* 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_stream* guac_audio_stream_alloc(guac_client* client,
guac_audio_encoder* encoder, int rate, int channels, int bps) { guac_audio_encoder* encoder, int rate, int channels, int bps) {
guac_audio_stream* audio; 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 */ /* Allocate stream */
audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream)); audio = (guac_audio_stream*) calloc(1, sizeof(guac_audio_stream));
audio->client = client; audio->client = client;
/* Assign encoder */
audio->encoder = encoder;
audio->stream = guac_client_alloc_stream(client); audio->stream = guac_client_alloc_stream(client);
/* Load PCM properties */ /* Load PCM properties */
@ -80,6 +103,13 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client,
audio->channels = channels; audio->channels = channels;
audio->bps = bps; 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 */ /* Call handler, if defined */
if (audio->encoder->begin_handler) if (audio->encoder->begin_handler)
audio->encoder->begin_handler(audio); 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) { void guac_audio_stream_free(guac_audio_stream* audio) {
/* Flush stream encoding */ /* Flush stream encoding */

View File

@ -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 * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@ -20,8 +20,8 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#ifndef __GUAC_AUDIO_FNTYPES_H #ifndef GUAC_AUDIO_FNTYPES_H
#define __GUAC_AUDIO_FNTYPES_H #define GUAC_AUDIO_FNTYPES_H
/** /**
* Function type definitions related to simple streaming audio. * Function type definitions related to simple streaming audio.
@ -30,24 +30,59 @@
*/ */
#include "audio-types.h" #include "audio-types.h"
#include "user-types.h"
/** /**
* Handler which is called when the audio stream is opened. * 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); typedef void guac_audio_encoder_begin_handler(guac_audio_stream* audio);
/** /**
* Handler which is called when the audio stream needs to be flushed. * 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); typedef void guac_audio_encoder_flush_handler(guac_audio_stream* audio);
/** /**
* Handler which is called when the audio stream is closed. * 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); 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, typedef void guac_audio_encoder_write_handler(guac_audio_stream* audio,
const unsigned char* pcm_data, int length); const unsigned char* pcm_data, int length);

View File

@ -64,6 +64,12 @@ struct guac_audio_encoder {
*/ */
guac_audio_encoder_end_handler* end_handler; 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 { struct guac_audio_stream {
@ -108,15 +114,24 @@ struct guac_audio_stream {
}; };
/** /**
* Allocates a new audio stream which encodes audio data using the given * Allocates a new audio stream at the client level which encodes audio data
* encoder. If NULL is specified for the encoder, an appropriate encoder * using the given encoder. If NULL is specified for the encoder, an
* will be selected based on the encoders built into libguac and the level * appropriate encoder will be selected based on the encoders built into
* of client support. The PCM format specified here (via rate, channels, and * 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. * 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(). * 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 * @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 * @param encoder
* The guac_audio_encoder to use when encoding audio, or NULL if libguac * The guac_audio_encoder to use when encoding audio, or NULL if libguac
@ -135,7 +150,8 @@ struct guac_audio_stream {
* *
* @return * @return
* The newly allocated guac_audio_stream, or NULL if no audio stream could * 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_stream* guac_audio_stream_alloc(guac_client* client,
guac_audio_encoder* encoder, int rate, int channels, int bps); 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, void guac_audio_stream_reset(guac_audio_stream* audio,
guac_audio_encoder* encoder, int rate, int channels, int bps); 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. * Closes and frees the given audio stream.
* *

View File

@ -29,14 +29,15 @@
#include <guacamole/client.h> #include <guacamole/client.h>
#include <guacamole/protocol.h> #include <guacamole/protocol.h>
#include <guacamole/socket.h> #include <guacamole/socket.h>
#include <guacamole/user.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
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]; char mimetype[256];
/* Produce mimetype string from format info */ /* 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); audio->bps, audio->rate, audio->channels);
/* Associate stream */ /* 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 */ /* Allocate and init encoder state */
audio->data = state = malloc(sizeof(raw_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); 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, .begin_handler = raw_encoder_begin_handler,
.write_handler = raw_encoder_write_handler, .write_handler = raw_encoder_write_handler,
.flush_handler = raw_encoder_flush_handler, .flush_handler = raw_encoder_flush_handler,
.join_handler = raw_encoder_join_handler,
.end_handler = raw_encoder_end_handler .end_handler = raw_encoder_end_handler
}; };
@ -152,6 +167,7 @@ guac_audio_encoder _raw16_encoder = {
.begin_handler = raw_encoder_begin_handler, .begin_handler = raw_encoder_begin_handler,
.write_handler = raw_encoder_write_handler, .write_handler = raw_encoder_write_handler,
.flush_handler = raw_encoder_flush_handler, .flush_handler = raw_encoder_flush_handler,
.join_handler = raw_encoder_join_handler,
.end_handler = raw_encoder_end_handler .end_handler = raw_encoder_end_handler
}; };