GUAC-1389: Restore libguac's high-level convenience functions for audio streaming.
This commit is contained in:
parent
fbe4d53fb1
commit
76e3b2ebb8
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user