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])
|
[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
|
||||||
|
@ -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 \
|
||||||
|
@ -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 */
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
*
|
*
|
||||||
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user