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])
# FIXME: Temporarily disabled
with_pulse=no
if test "x$with_pulse" != "xno"
then
have_pulse=yes

View File

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

View File

@ -28,51 +28,74 @@
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/stream.h>
#include <guacamole/user.h>
#include <stdlib.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_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 */

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
* 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);

View File

@ -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.
*

View File

@ -29,14 +29,15 @@
#include <guacamole/client.h>
#include <guacamole/protocol.h>
#include <guacamole/socket.h>
#include <guacamole/user.h>
#include <stdlib.h>
#include <stdio.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];
/* 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
};