Migrate to libguac audio. Migrate to improved stream protocol.
This commit is contained in:
parent
157828ef18
commit
fbdb6876d9
@ -104,9 +104,6 @@ guac_audio_stream* guac_audio_stream_alloc(guac_client* client, guac_audio_encod
|
||||
audio->encoder = encoder;
|
||||
audio->stream = guac_client_alloc_stream(client);
|
||||
|
||||
/* Ensure socket within new stream is threadsafe */
|
||||
guac_socket_require_threadsafe(audio->stream->socket);
|
||||
|
||||
return audio;
|
||||
}
|
||||
|
||||
@ -138,9 +135,13 @@ void guac_audio_stream_end(guac_audio_stream* audio) {
|
||||
/ audio->rate / audio->channels / audio->bps;
|
||||
|
||||
/* Send audio */
|
||||
guac_protocol_send_audio(audio->stream->socket,
|
||||
0, audio->encoder->mimetype,
|
||||
duration, audio->encoded_data, audio->encoded_data_used);
|
||||
guac_protocol_send_audio(audio->client->socket, audio->stream,
|
||||
audio->encoder->mimetype, duration);
|
||||
|
||||
guac_protocol_send_blob(audio->client->socket, audio->stream,
|
||||
audio->encoded_data, audio->encoded_data_used);
|
||||
|
||||
guac_protocol_send_end(audio->client->socket, audio->stream);
|
||||
|
||||
/* Clear data */
|
||||
audio->encoded_data_used = 0;
|
||||
|
@ -101,12 +101,6 @@ guac_stream* guac_client_alloc_stream(guac_client* client) {
|
||||
guac_stream* allocd_stream = malloc(sizeof(guac_stream));
|
||||
allocd_stream->index = guac_pool_next_int(client->__stream_pool);
|
||||
|
||||
/* Nest socket */
|
||||
allocd_stream->socket = guac_socket_nest(
|
||||
client->socket,
|
||||
allocd_stream->index
|
||||
);
|
||||
|
||||
return allocd_stream;
|
||||
|
||||
}
|
||||
@ -116,9 +110,6 @@ void guac_client_free_stream(guac_client* client, guac_stream* stream) {
|
||||
/* Release index to pool */
|
||||
guac_pool_free_int(client->__stream_pool, stream->index - 1);
|
||||
|
||||
/* Release socket */
|
||||
guac_socket_free(stream->socket);
|
||||
|
||||
/* Free stream */
|
||||
free(stream);
|
||||
|
||||
|
@ -42,6 +42,7 @@
|
||||
|
||||
#include "layer.h"
|
||||
#include "socket.h"
|
||||
#include "stream.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
/**
|
||||
@ -280,78 +281,13 @@ int guac_protocol_send_sync(guac_socket* socket, guac_timestamp timestamp);
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param channel The index of the audio channel the sound should play on.
|
||||
* @param stream The stream to use.
|
||||
* @param mimetype The mimetype of the data being sent.
|
||||
* @param duration The duration of the sound being sent, in milliseconds.
|
||||
* @param data The audio data to be sent.
|
||||
* @param size The number of bytes of audio data to send.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_audio(guac_socket* socket, int channel,
|
||||
const char* mimetype, double duration, void* data, int size);
|
||||
|
||||
/**
|
||||
* Begins a audio instruction over the given guac_socket connection. Only the
|
||||
* initial non-data part of the instruction and the length of the data part
|
||||
* of the instruction are sent. The actual contents of the data must be
|
||||
* sent with guac_protocol_send_audio_data(), and the instruction must be
|
||||
* completed with guac_protocol_send_audio_end().
|
||||
*
|
||||
* Note that the size of the audio to be sent MUST be known ahead of time,
|
||||
* even though the data of the audio may be sent in chunks.
|
||||
*
|
||||
* No further instruction data may be sent along the givven guac_socket
|
||||
* except via guac_protocol_send_audio_data() until the audio instruction
|
||||
* is completed with guac_protocol_send_audio_end().
|
||||
*
|
||||
* Note that if you send this instruction over a threadsafe socket, you
|
||||
* MUST also call guac_protocol_send_audio_end() or the socket will be
|
||||
* left in an unsafe state.
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param channel The index of the audio channel the sound should play on.
|
||||
* @param mimetype The mimetype of the data being sent.
|
||||
* @param duration The duration of the audio being sent, in milliseconds.
|
||||
* @param size The number of bytes of audio data to send.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_audio_header(guac_socket* socket,
|
||||
int channel, const char* mimetype, double duration, int size);
|
||||
|
||||
/**
|
||||
* Writes a block of audio data to the currently in-progress audio instruction
|
||||
* which was started with guac_protocol_send_audio_header(). Exactly the
|
||||
* number of requested bytes are written unless an error occurs. This function
|
||||
* may be called multiple times per audio instruction for each chunk of audio
|
||||
* data being written, allowing the potentially huge audio instruction to be
|
||||
* split across multiple writes.
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param data The audio data to write.
|
||||
* @param count The number of bytes within the given buffer of audio data
|
||||
* that must be written.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_audio_data(guac_socket* socket, void* data, int count);
|
||||
|
||||
/**
|
||||
* Completes the audio instruction which was started with
|
||||
* guac_protocol_send_audio_header(), and whose data has been written with
|
||||
* guac_protocol_send_audio_data().
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_audio_end(guac_socket* socket);
|
||||
int guac_protocol_send_audio(guac_socket* socket, const guac_stream* stream,
|
||||
const char* mimetype, double duration);
|
||||
|
||||
/**
|
||||
* Sends a file instruction over the given guac_socket connection.
|
||||
@ -360,13 +296,13 @@ int guac_protocol_send_audio_end(guac_socket* socket);
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param index The index of the blob that will contain the contents
|
||||
* of this file.
|
||||
* @param stream The stream to use.
|
||||
* @param mimetype The mimetype of the data being sent.
|
||||
* @param name A name describing the file being sent.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype, const char* name);
|
||||
int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
|
||||
const char* mimetype, const char* name);
|
||||
|
||||
/**
|
||||
* Writes a block of data to the currently in-progress blob which was already
|
||||
@ -376,13 +312,14 @@ int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param index The index of the blob to append data to.
|
||||
* @param stream The stream to use.
|
||||
* @param data The file data to write.
|
||||
* @param count The number of bytes within the given buffer of file data
|
||||
* that must be written.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int count);
|
||||
int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
|
||||
void* data, int count);
|
||||
|
||||
/**
|
||||
* Sends an end instruction over the given guac_socket connection.
|
||||
@ -391,10 +328,10 @@ int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int coun
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param index The index of the blob which is now complete.
|
||||
* @param stream The stream to use.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_end(guac_socket* socket, int index);
|
||||
int guac_protocol_send_end(guac_socket* socket, const guac_stream* stream);
|
||||
|
||||
/**
|
||||
* Sends a video instruction over the given guac_socket connection.
|
||||
@ -403,78 +340,14 @@ int guac_protocol_send_end(guac_socket* socket, int index);
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param stream The stream to use.
|
||||
* @param layer The destination layer.
|
||||
* @param mimetype The mimetype of the data being sent.
|
||||
* @param duration The duration of the video being sent, in milliseconds.
|
||||
* @param data The video data to be sent.
|
||||
* @param size The number of bytes of video data to send.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_video(guac_socket* socket, const guac_layer* layer,
|
||||
const char* mimetype, double duration, void* data, int size);
|
||||
|
||||
/**
|
||||
* Begins a video instruction over the given guac_socket connection. Only the
|
||||
* initial non-data part of the instruction and the length of the data part
|
||||
* of the instruction are sent. The actual contents of the data must be
|
||||
* sent with guac_protocol_send_video_data(), and the instruction must be
|
||||
* completed with guac_protocol_send_video_end().
|
||||
*
|
||||
* Note that the size of the video to be sent MUST be known ahead of time,
|
||||
* even though the data of the video may be sent in chunks.
|
||||
*
|
||||
* No further instruction data may be sent along the givven guac_socket
|
||||
* except via guac_protocol_send_video_data() until the video instruction
|
||||
* is completed with guac_protocol_send_video_end().
|
||||
*
|
||||
* Note that if you send this instruction over a threadsafe socket, you
|
||||
* MUST also call guac_protocol_send_video_end() or the socket will be
|
||||
* left in an unsafe state.
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param layer The destination layer.
|
||||
* @param mimetype The mimetype of the data being sent.
|
||||
* @param duration The duration of the video being sent, in milliseconds.
|
||||
* @param size The number of bytes of video data to send.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_video_header(guac_socket* socket,
|
||||
const guac_layer* layer, const char* mimetype, double duration, int size);
|
||||
|
||||
/**
|
||||
* Writes a block of video data to the currently in-progress video instruction
|
||||
* which was started with guac_protocol_send_video_header(). Exactly the
|
||||
* number of requested bytes are written unless an error occurs. This function
|
||||
* may be called multiple times per video instruction for each chunk of video
|
||||
* data being written, allowing the potentially huge video instruction to be
|
||||
* split across multiple writes.
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @param data The video data to write.
|
||||
* @param count The number of bytes within the given buffer of video data
|
||||
* that must be written.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_video_data(guac_socket* socket, void* data, int count);
|
||||
|
||||
/**
|
||||
* Completes the video instruction which was started with
|
||||
* guac_protocol_send_video_header(), and whose data has been written with
|
||||
* guac_protocol_send_video_data().
|
||||
*
|
||||
* If an error occurs sending the instruction, a non-zero value is
|
||||
* returned, and guac_error is set appropriately.
|
||||
*
|
||||
* @param socket The guac_socket connection to use.
|
||||
* @return Zero on success, non-zero on error.
|
||||
*/
|
||||
int guac_protocol_send_video_end(guac_socket* socket);
|
||||
int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
|
||||
const guac_layer* layer, const char* mimetype, double duration);
|
||||
|
||||
/* DRAWING INSTRUCTIONS */
|
||||
|
||||
|
@ -39,8 +39,7 @@
|
||||
#define _GUAC_STREAM_H
|
||||
|
||||
/**
|
||||
* Provides functions and structures required for allocating and using nested
|
||||
* streams.
|
||||
* Provides functions and structures required for allocating and using streams.
|
||||
*
|
||||
* @file stream.h
|
||||
*/
|
||||
@ -48,20 +47,15 @@
|
||||
typedef struct guac_stream guac_stream;
|
||||
|
||||
/**
|
||||
* Represents a single nested stream within the Guacamole protocol.
|
||||
* Represents a single stream within the Guacamole protocol.
|
||||
*/
|
||||
struct guac_stream {
|
||||
|
||||
/**
|
||||
* The index of this layer.
|
||||
* The index of this stream.
|
||||
*/
|
||||
int index;
|
||||
|
||||
/**
|
||||
* A guac_socket which writes to this stream.
|
||||
*/
|
||||
guac_socket* socket;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -416,55 +416,28 @@ int guac_protocol_send_arc(guac_socket* socket, const guac_layer* layer,
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_audio(guac_socket* socket, int channel,
|
||||
const char* mimetype, double duration, void* data, int size) {
|
||||
int guac_protocol_send_audio(guac_socket* socket, const guac_stream* stream,
|
||||
const char* mimetype, double duration) {
|
||||
|
||||
/* By the spec, guac_protocol_send_audio_end() must be called */
|
||||
return
|
||||
(guac_protocol_send_audio_header(socket, channel,
|
||||
mimetype, duration, size)
|
||||
|| guac_protocol_send_audio_data(socket, data, size))
|
||||
| guac_protocol_send_audio_end(socket);
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_audio_header(guac_socket* socket,
|
||||
int channel, const char* mimetype, double duration, int size) {
|
||||
|
||||
int base64_length = (size + 2) / 3 * 4;
|
||||
int ret_val;
|
||||
|
||||
guac_socket_instruction_begin(socket);
|
||||
return
|
||||
ret_val =
|
||||
guac_socket_write_string(socket, "5.audio,")
|
||||
|| __guac_socket_write_length_int(socket, channel)
|
||||
|| __guac_socket_write_length_int(socket, stream->index)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_string(socket, mimetype)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_double(socket, duration)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| guac_socket_write_int(socket, base64_length)
|
||||
|| guac_socket_write_string(socket, ".");
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_audio_data(guac_socket* socket, void* data, int count) {
|
||||
|
||||
return guac_socket_write_base64(socket, data, count);
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_audio_end(guac_socket* socket) {
|
||||
|
||||
int ret_val =
|
||||
guac_socket_flush_base64(socket)
|
||||
|| guac_socket_write_string(socket, ";");
|
||||
|
||||
guac_socket_instruction_end(socket);
|
||||
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int count) {
|
||||
int guac_protocol_send_blob(guac_socket* socket, const guac_stream* stream,
|
||||
void* data, int count) {
|
||||
|
||||
int base64_length = (count + 2) / 3 * 4;
|
||||
|
||||
@ -473,7 +446,7 @@ int guac_protocol_send_blob(guac_socket* socket, int index, void* data, int coun
|
||||
guac_socket_instruction_begin(socket);
|
||||
ret_val =
|
||||
guac_socket_write_string(socket, "4.blob,")
|
||||
|| __guac_socket_write_length_int(socket, index)
|
||||
|| __guac_socket_write_length_int(socket, stream->index)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| guac_socket_write_int(socket, base64_length)
|
||||
|| guac_socket_write_string(socket, ".")
|
||||
@ -777,14 +750,14 @@ int guac_protocol_send_distort(guac_socket* socket, const guac_layer* layer,
|
||||
}
|
||||
|
||||
|
||||
int guac_protocol_send_end(guac_socket* socket, int index) {
|
||||
int guac_protocol_send_end(guac_socket* socket, const guac_stream* stream) {
|
||||
|
||||
int ret_val;
|
||||
|
||||
guac_socket_instruction_begin(socket);
|
||||
ret_val =
|
||||
guac_socket_write_string(socket, "3.end,")
|
||||
|| __guac_socket_write_length_int(socket, index)
|
||||
|| __guac_socket_write_length_int(socket, stream->index)
|
||||
|| guac_socket_write_string(socket, ";");
|
||||
|
||||
guac_socket_instruction_end(socket);
|
||||
@ -809,14 +782,15 @@ int guac_protocol_send_error(guac_socket* socket, const char* error) {
|
||||
}
|
||||
|
||||
|
||||
int guac_protocol_send_file(guac_socket* socket, int index, const char* mimetype, const char* name) {
|
||||
int guac_protocol_send_file(guac_socket* socket, const guac_stream* stream,
|
||||
const char* mimetype, const char* name) {
|
||||
|
||||
int ret_val;
|
||||
|
||||
guac_socket_instruction_begin(socket);
|
||||
ret_val =
|
||||
guac_socket_write_string(socket, "4.file,")
|
||||
|| __guac_socket_write_length_int(socket, index)
|
||||
|| __guac_socket_write_length_int(socket, stream->index)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_string(socket, mimetype)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
@ -1250,50 +1224,24 @@ int guac_protocol_send_transform(guac_socket* socket, const guac_layer* layer,
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_video(guac_socket* socket, const guac_layer* layer,
|
||||
const char* mimetype, double duration, void* data, int size) {
|
||||
int guac_protocol_send_video(guac_socket* socket, const guac_stream* stream,
|
||||
const guac_layer* layer, const char* mimetype, double duration) {
|
||||
|
||||
/* By the spec, guac_protocol_send_video_end() must be called */
|
||||
return
|
||||
(guac_protocol_send_video_header(socket, layer,
|
||||
mimetype, duration, size)
|
||||
|| guac_protocol_send_video_data(socket, data, size))
|
||||
| guac_protocol_send_video_end(socket);
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_video_header(guac_socket* socket,
|
||||
const guac_layer* layer, const char* mimetype, double duration, int size) {
|
||||
|
||||
int base64_length = (size + 2) / 3 * 4;
|
||||
int ret_val;
|
||||
|
||||
guac_socket_instruction_begin(socket);
|
||||
return
|
||||
ret_val =
|
||||
guac_socket_write_string(socket, "5.video,")
|
||||
|| __guac_socket_write_length_int(socket, stream->index)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_int(socket, layer->index)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_string(socket, mimetype)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| __guac_socket_write_length_double(socket, duration)
|
||||
|| guac_socket_write_string(socket, ",")
|
||||
|| guac_socket_write_int(socket, base64_length)
|
||||
|| guac_socket_write_string(socket, ".");
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_video_data(guac_socket* socket, void* data, int count) {
|
||||
|
||||
return guac_socket_write_base64(socket, data, count);
|
||||
|
||||
}
|
||||
|
||||
int guac_protocol_send_video_end(guac_socket* socket) {
|
||||
|
||||
int ret_val =
|
||||
guac_socket_flush_base64(socket)
|
||||
|| guac_socket_write_string(socket, ";");
|
||||
|
||||
guac_socket_instruction_end(socket);
|
||||
|
||||
return ret_val;
|
||||
|
||||
}
|
||||
|
@ -42,7 +42,6 @@ AM_CFLAGS = -Werror -Wall -Iinclude @LIBGUAC_INCLUDE@
|
||||
lib_LTLIBRARIES = libguac-client-rdp.la
|
||||
|
||||
libguac_client_rdp_la_SOURCES = \
|
||||
audio.c \
|
||||
client.c \
|
||||
default_pointer.c \
|
||||
guac_handlers.c \
|
||||
@ -57,13 +56,11 @@ libguac_client_rdp_la_SOURCES = \
|
||||
rdp_keymap_fr_fr.c \
|
||||
rdp_keymap_en_us.c \
|
||||
rdp_pointer.c \
|
||||
rdp_settings.c \
|
||||
wav_encoder.c
|
||||
rdp_settings.c
|
||||
|
||||
guacsnd_sources = \
|
||||
guac_rdpsnd/rdpsnd_messages.c \
|
||||
guac_rdpsnd/rdpsnd_service.c \
|
||||
audio.c
|
||||
guac_rdpsnd/rdpsnd_service.c
|
||||
|
||||
guacdr_sources = \
|
||||
guac_rdpdr/rdpdr_messages.c \
|
||||
@ -77,7 +74,6 @@ noinst_HEADERS = \
|
||||
guac_rdpdr/rdpdr_service.h \
|
||||
guac_rdpsnd/rdpsnd_messages.h \
|
||||
guac_rdpsnd/rdpsnd_service.h \
|
||||
audio.h \
|
||||
client.h \
|
||||
config.h \
|
||||
default_pointer.h \
|
||||
@ -88,8 +84,7 @@ noinst_HEADERS = \
|
||||
rdp_glyph.h \
|
||||
rdp_keymap.h \
|
||||
rdp_pointer.h \
|
||||
rdp_settings.h \
|
||||
wav_encoder.h
|
||||
rdp_settings.h
|
||||
|
||||
# Add compatibility layer for WinPR if not available
|
||||
if ! ENABLE_WINPR
|
||||
@ -98,14 +93,8 @@ if ! ENABLE_WINPR
|
||||
guacdr_sources += compat/winpr-stream.c
|
||||
endif
|
||||
|
||||
# Compile OGG support if available
|
||||
if ENABLE_OGG
|
||||
libguac_client_rdp_la_SOURCES += ogg_encoder.c
|
||||
noinst_HEADERS += ogg_encoder.h
|
||||
endif
|
||||
|
||||
libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@ @CAIRO_LIBS@
|
||||
guacsnd_ldflags = -module -avoid-version -shared @RDP_LIBS@ @VORBIS_LIBS@ @PTHREAD_LIBS@
|
||||
libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 @RDP_LIBS@ @PTHREAD_LIBS@ @CAIRO_LIBS@
|
||||
guacsnd_ldflags = -module -avoid-version -shared @RDP_LIBS@ @PTHREAD_LIBS@
|
||||
guacdr_ldflags = -module -avoid-version -shared @RDP_LIBS@ @PTHREAD_LIBS@
|
||||
|
||||
libguac_client_rdp_la_LIBADD = @LIBGUAC_LTLIB@
|
||||
|
@ -1,176 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "client.h"
|
||||
|
||||
audio_stream* audio_stream_alloc(guac_client* client, audio_encoder* encoder) {
|
||||
|
||||
/* Allocate stream */
|
||||
audio_stream* audio = (audio_stream*) malloc(sizeof(audio_stream));
|
||||
audio->client = client;
|
||||
|
||||
/* Reset buffer stats */
|
||||
audio->used = 0;
|
||||
audio->length = 0x40000;
|
||||
|
||||
audio->encoded_data_used = 0;
|
||||
audio->encoded_data_length = 0x40000;
|
||||
|
||||
/* Allocate buffers */
|
||||
audio->pcm_data = malloc(audio->length);
|
||||
audio->encoded_data = malloc(audio->encoded_data_length);
|
||||
|
||||
/* Assign encoder */
|
||||
audio->encoder = encoder;
|
||||
audio->stream = guac_client_alloc_stream(client);
|
||||
|
||||
/* Ensure socket within new stream is threadsafe */
|
||||
guac_socket_require_threadsafe(audio->stream->socket);
|
||||
|
||||
return audio;
|
||||
}
|
||||
|
||||
void audio_stream_begin(audio_stream* audio, int rate, int channels, int bps) {
|
||||
|
||||
/* Load PCM properties */
|
||||
audio->rate = rate;
|
||||
audio->channels = channels;
|
||||
audio->bps = bps;
|
||||
|
||||
/* Reset write counter */
|
||||
audio->pcm_bytes_written = 0;
|
||||
|
||||
/* Call handler */
|
||||
audio->encoder->begin_handler(audio);
|
||||
|
||||
}
|
||||
|
||||
void audio_stream_end(audio_stream* audio) {
|
||||
|
||||
double duration;
|
||||
|
||||
/* Flush stream and finish encoding */
|
||||
audio_stream_flush(audio);
|
||||
audio->encoder->end_handler(audio);
|
||||
|
||||
/* Calculate duration of PCM data */
|
||||
duration = ((double) (audio->pcm_bytes_written * 1000 * 8))
|
||||
/ audio->rate / audio->channels / audio->bps;
|
||||
|
||||
/* Send audio */
|
||||
guac_protocol_send_audio(audio->stream->socket,
|
||||
0, audio->encoder->mimetype,
|
||||
duration, audio->encoded_data, audio->encoded_data_used);
|
||||
|
||||
/* Clear data */
|
||||
audio->encoded_data_used = 0;
|
||||
|
||||
}
|
||||
|
||||
void audio_stream_free(audio_stream* audio) {
|
||||
free(audio->pcm_data);
|
||||
free(audio);
|
||||
}
|
||||
|
||||
void audio_stream_write_pcm(audio_stream* audio,
|
||||
unsigned char* data, int length) {
|
||||
|
||||
/* Update counter */
|
||||
audio->pcm_bytes_written += length;
|
||||
|
||||
/* Resize audio buffer if necessary */
|
||||
if (length > audio->length) {
|
||||
|
||||
/* Resize to double provided length */
|
||||
audio->length = length*2;
|
||||
audio->pcm_data = realloc(audio->pcm_data, audio->length);
|
||||
|
||||
}
|
||||
|
||||
/* Flush if necessary */
|
||||
if (audio->used + length > audio->length)
|
||||
audio_stream_flush(audio);
|
||||
|
||||
/* Append to buffer */
|
||||
memcpy(&(audio->pcm_data[audio->used]), data, length);
|
||||
audio->used += length;
|
||||
|
||||
}
|
||||
|
||||
void audio_stream_flush(audio_stream* audio) {
|
||||
|
||||
/* If data in buffer */
|
||||
if (audio->used != 0) {
|
||||
|
||||
/* Write data */
|
||||
audio->encoder->write_handler(audio,
|
||||
audio->pcm_data, audio->used);
|
||||
|
||||
/* Reset buffer */
|
||||
audio->used = 0;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void audio_stream_write_encoded(audio_stream* audio,
|
||||
unsigned char* data, int length) {
|
||||
|
||||
/* Resize audio buffer if necessary */
|
||||
if (audio->encoded_data_used + length > audio->encoded_data_length) {
|
||||
|
||||
/* Increase to double concatenated size to accomodate */
|
||||
audio->encoded_data_length = (audio->encoded_data_length + length)*2;
|
||||
audio->encoded_data = realloc(audio->encoded_data,
|
||||
audio->encoded_data_length);
|
||||
|
||||
}
|
||||
|
||||
/* Append to buffer */
|
||||
memcpy(&(audio->encoded_data[audio->encoded_data_used]), data, length);
|
||||
audio->encoded_data_used += length;
|
||||
|
||||
}
|
||||
|
@ -1,212 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __GUAC_TEST_AUDIO_H
|
||||
#define __GUAC_TEST_AUDIO_H
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/stream.h>
|
||||
|
||||
typedef struct audio_stream audio_stream;
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream is opened.
|
||||
*/
|
||||
typedef void audio_encoder_begin_handler(audio_stream* audio);
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream is closed.
|
||||
*/
|
||||
typedef void audio_encoder_end_handler(audio_stream* audio);
|
||||
|
||||
/**
|
||||
* Handler which is called when the audio stream is flushed.
|
||||
*/
|
||||
typedef void audio_encoder_write_handler(audio_stream* audio,
|
||||
unsigned char* pcm_data, int length);
|
||||
|
||||
/**
|
||||
* Arbitrary audio codec encoder.
|
||||
*/
|
||||
typedef struct audio_encoder {
|
||||
|
||||
/**
|
||||
* The mimetype of the audio data encoded by this audio
|
||||
* encoder.
|
||||
*/
|
||||
const char* mimetype;
|
||||
|
||||
/**
|
||||
* Handler which will be called when the audio stream is opened.
|
||||
*/
|
||||
audio_encoder_begin_handler* begin_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called when the audio stream is flushed.
|
||||
*/
|
||||
audio_encoder_write_handler* write_handler;
|
||||
|
||||
/**
|
||||
* Handler which will be called when the audio stream is closed.
|
||||
*/
|
||||
audio_encoder_end_handler* end_handler;
|
||||
|
||||
} audio_encoder;
|
||||
|
||||
/**
|
||||
* Basic audio stream. PCM data is added to the stream. When the stream is
|
||||
* flushed, a write handler receives PCM data packets and, presumably, streams
|
||||
* them to the guac_stream provided.
|
||||
*/
|
||||
struct audio_stream {
|
||||
|
||||
/**
|
||||
* PCM data buffer, 16-bit samples, 2-channel, 44100 Hz.
|
||||
*/
|
||||
unsigned char* pcm_data;
|
||||
|
||||
/**
|
||||
* Number of bytes in buffer.
|
||||
*/
|
||||
int used;
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in buffer.
|
||||
*/
|
||||
int length;
|
||||
|
||||
/**
|
||||
* Encoded audio data buffer, as written by the encoder.
|
||||
*/
|
||||
unsigned char* encoded_data;
|
||||
|
||||
/**
|
||||
* Number of bytes in the encoded data buffer.
|
||||
*/
|
||||
int encoded_data_used;
|
||||
|
||||
/**
|
||||
* Maximum number of bytes in the encoded data buffer.
|
||||
*/
|
||||
int encoded_data_length;
|
||||
|
||||
/**
|
||||
* Arbitrary codec encoder. When the PCM buffer is flushed, PCM data will
|
||||
* be sent to this encoder.
|
||||
*/
|
||||
audio_encoder* encoder;
|
||||
|
||||
/**
|
||||
* The client associated with this audio stream.
|
||||
*/
|
||||
guac_client* client;
|
||||
|
||||
/**
|
||||
* The actual stream associated with this audio stream.
|
||||
*/
|
||||
guac_stream* stream;
|
||||
|
||||
/**
|
||||
* The number of samples per second of PCM data sent to this stream.
|
||||
*/
|
||||
int rate;
|
||||
|
||||
/**
|
||||
* The number of audio channels per sample of PCM data. Legal values are
|
||||
* 1 or 2.
|
||||
*/
|
||||
int channels;
|
||||
|
||||
/**
|
||||
* The number of bits per sample per channel for PCM data. Legal values are
|
||||
* 8 or 16.
|
||||
*/
|
||||
int bps;
|
||||
|
||||
/**
|
||||
* The number of PCM bytes written since the audio chunk began.
|
||||
*/
|
||||
int pcm_bytes_written;
|
||||
|
||||
/**
|
||||
* Encoder-specific state data.
|
||||
*/
|
||||
void* data;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Allocates a new audio stream.
|
||||
*/
|
||||
audio_stream* audio_stream_alloc(guac_client* client,
|
||||
audio_encoder* encoder);
|
||||
|
||||
/**
|
||||
* Frees the given audio stream.
|
||||
*/
|
||||
void audio_stream_free(audio_stream* stream);
|
||||
|
||||
/**
|
||||
* Begins a new audio stream.
|
||||
*/
|
||||
void audio_stream_begin(audio_stream* stream, int rate, int channels, int bps);
|
||||
|
||||
/**
|
||||
* Ends the current audio stream.
|
||||
*/
|
||||
void audio_stream_end(audio_stream* stream);
|
||||
|
||||
/**
|
||||
* Writes PCM data to the given audio stream.
|
||||
*/
|
||||
void audio_stream_write_pcm(audio_stream* stream,
|
||||
unsigned char* data, int length);
|
||||
|
||||
/**
|
||||
* Flushes the given audio stream.
|
||||
*/
|
||||
void audio_stream_flush(audio_stream* stream);
|
||||
|
||||
/**
|
||||
* Appends arbitrarily-encoded data to the encoded_data buffer
|
||||
* within the given audio stream.
|
||||
*/
|
||||
void audio_stream_write_encoded(audio_stream* audio,
|
||||
unsigned char* data, int length);
|
||||
|
||||
#endif
|
||||
|
@ -75,15 +75,9 @@
|
||||
#include <guacamole/socket.h>
|
||||
#include <guacamole/protocol.h>
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/error.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "wav_encoder.h"
|
||||
|
||||
#ifdef ENABLE_OGG
|
||||
#include "ogg_encoder.h"
|
||||
#endif
|
||||
|
||||
#include "client.h"
|
||||
#include "guac_handlers.h"
|
||||
#include "rdp_keymap.h"
|
||||
@ -151,7 +145,6 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
rdpPointer* pointer;
|
||||
rdpPrimaryUpdate* primary;
|
||||
CLRCONV* clrconv;
|
||||
int i;
|
||||
|
||||
rdp_guac_client_data* guac_client_data =
|
||||
(rdp_guac_client_data*) client->data;
|
||||
@ -169,30 +162,7 @@ BOOL rdp_freerdp_pre_connect(freerdp* instance) {
|
||||
/* If audio enabled, choose an encoder */
|
||||
if (guac_client_data->settings.audio_enabled) {
|
||||
|
||||
/* Choose an encoding */
|
||||
for (i=0; client->info.audio_mimetypes[i] != NULL; i++) {
|
||||
|
||||
const char* mimetype = client->info.audio_mimetypes[i];
|
||||
|
||||
#ifdef ENABLE_OGG
|
||||
/* If Ogg is supported, done. */
|
||||
if (strcmp(mimetype, ogg_encoder->mimetype) == 0) {
|
||||
guac_client_log_info(client, "Loading Ogg Vorbis encoder.");
|
||||
guac_client_data->audio = audio_stream_alloc(client,
|
||||
ogg_encoder);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If wav is supported, done. */
|
||||
if (strcmp(mimetype, wav_encoder->mimetype) == 0) {
|
||||
guac_client_log_info(client, "Loading wav encoder.");
|
||||
guac_client_data->audio = audio_stream_alloc(client,
|
||||
wav_encoder);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
guac_client_data->audio = guac_audio_stream_alloc(client, NULL);
|
||||
|
||||
/* If an encoding is available, load the sound plugin */
|
||||
if (guac_client_data->audio != NULL) {
|
||||
|
@ -46,8 +46,8 @@
|
||||
#include <freerdp/codec/color.h>
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/audio.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "rdp_keymap.h"
|
||||
#include "rdp_settings.h"
|
||||
|
||||
@ -166,7 +166,7 @@ typedef struct rdp_guac_client_data {
|
||||
/**
|
||||
* Audio output, if any.
|
||||
*/
|
||||
audio_stream* audio;
|
||||
guac_audio_stream* audio;
|
||||
|
||||
/**
|
||||
* Lock which is locked and unlocked for each RDP message.
|
||||
|
@ -247,10 +247,6 @@ int rdp_guac_client_handle_messages(guac_client* client) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Flush any audio */
|
||||
if (guac_client_data->audio != NULL)
|
||||
guac_socket_flush(guac_client_data->audio->stream->socket);
|
||||
|
||||
pthread_mutex_unlock(&(guac_client_data->rdp_lock));
|
||||
|
||||
/* Calculate time remaining in frame */
|
||||
|
@ -49,9 +49,9 @@
|
||||
#include "compat/winpr-wtypes.h"
|
||||
#endif
|
||||
|
||||
#include <guacamole/audio.h>
|
||||
#include <guacamole/client.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "rdpsnd_service.h"
|
||||
#include "rdpsnd_messages.h"
|
||||
#include "client.h"
|
||||
@ -59,7 +59,7 @@
|
||||
/* MESSAGE HANDLERS */
|
||||
|
||||
void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
int server_format_count;
|
||||
@ -209,7 +209,7 @@ void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
|
||||
/* server is getting a feel of the round trip time */
|
||||
void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
int data_size;
|
||||
@ -237,7 +237,7 @@ void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
}
|
||||
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
unsigned char buffer[4];
|
||||
@ -261,18 +261,18 @@ void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
rdpsnd->next_pdu_is_wave = TRUE;
|
||||
|
||||
/* Init stream with requested format */
|
||||
audio_stream_begin(audio,
|
||||
guac_audio_stream_begin(audio,
|
||||
rdpsnd->formats[format].rate,
|
||||
rdpsnd->formats[format].channels,
|
||||
rdpsnd->formats[format].bps);
|
||||
|
||||
/* Write initial 4 bytes of data */
|
||||
audio_stream_write_pcm(audio, buffer, 4);
|
||||
guac_audio_stream_write_pcm(audio, buffer, 4);
|
||||
|
||||
}
|
||||
|
||||
void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
rdpSvcPlugin* plugin = (rdpSvcPlugin*)rdpsnd;
|
||||
@ -287,8 +287,8 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
unsigned char* buffer = Stream_Buffer(input_stream) + 4;
|
||||
|
||||
/* Write rest of audio packet */
|
||||
audio_stream_write_pcm(audio, buffer, rdpsnd->incoming_wave_size);
|
||||
audio_stream_end(audio);
|
||||
guac_audio_stream_write_pcm(audio, buffer, rdpsnd->incoming_wave_size);
|
||||
guac_audio_stream_end(audio);
|
||||
|
||||
/* Write Wave Confirmation PDU */
|
||||
Stream_Write_UINT8(output_stream, SNDC_WAVECONFIRM);
|
||||
@ -309,7 +309,7 @@ void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
}
|
||||
|
||||
void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header) {
|
||||
|
||||
/* STUB: Do nothing for now */
|
||||
|
@ -38,6 +38,8 @@
|
||||
#ifndef __GUAC_RDPSND_MESSAGES_H
|
||||
#define __GUAC_RDPSND_MESSAGES_H
|
||||
|
||||
#include <guacamole/audio.h>
|
||||
|
||||
/*
|
||||
* PDU Message Types
|
||||
*/
|
||||
@ -127,35 +129,35 @@ typedef struct guac_rdpsnd_pdu_header {
|
||||
* Handler for the SNDC_FORMATS (Server Audio Formats and Version) PDU.
|
||||
*/
|
||||
void guac_rdpsnd_formats_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
* Handler for the SNDC_TRAINING (Training) PDU.
|
||||
*/
|
||||
void guac_rdpsnd_training_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
* Handler for the SNDC_WAVE (WaveInfo) PDU.
|
||||
*/
|
||||
void guac_rdpsnd_wave_info_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
* Handler for the SNDWAV (Wave) PDU which follows any WaveInfo PDU.
|
||||
*/
|
||||
void guac_rdpsnd_wave_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header);
|
||||
|
||||
/**
|
||||
* Handler for the SNDC_CLOSE (Close) PDU.
|
||||
*/
|
||||
void guac_rdpsnd_close_handler(guac_rdpsndPlugin* rdpsnd,
|
||||
audio_stream* audio, wStream* input_stream,
|
||||
guac_audio_stream* audio, wStream* input_stream,
|
||||
guac_rdpsnd_pdu_header* header);
|
||||
|
||||
#endif
|
||||
|
@ -51,8 +51,8 @@
|
||||
#endif
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/audio.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "rdpsnd_service.h"
|
||||
#include "rdpsnd_messages.h"
|
||||
|
||||
@ -91,10 +91,10 @@ void guac_rdpsnd_process_connect(rdpSvcPlugin* plugin) {
|
||||
guac_rdpsndPlugin* rdpsnd = (guac_rdpsndPlugin*) plugin;
|
||||
|
||||
/* Get audio stream from plugin parameters */
|
||||
audio_stream* audio = rdpsnd->audio =
|
||||
(audio_stream*) plugin->channel_entry_points.pExtendedData;
|
||||
guac_audio_stream* audio = rdpsnd->audio =
|
||||
(guac_audio_stream*) plugin->channel_entry_points.pExtendedData;
|
||||
|
||||
/* NULL out pExtendedData so we don't lose our audio_stream due to an
|
||||
/* NULL out pExtendedData so we don't lose our guac_audio_stream due to an
|
||||
* automatic free() within libfreerdp */
|
||||
plugin->channel_entry_points.pExtendedData = NULL;
|
||||
|
||||
@ -123,7 +123,7 @@ void guac_rdpsnd_process_receive(rdpSvcPlugin* plugin,
|
||||
guac_rdpsnd_pdu_header header;
|
||||
|
||||
/* Get audio stream from plugin */
|
||||
audio_stream* audio = rdpsnd->audio;
|
||||
guac_audio_stream* audio = rdpsnd->audio;
|
||||
|
||||
/* Read RDPSND PDU header */
|
||||
Stream_Read_UINT8(input_stream, header.message_type);
|
||||
|
@ -38,6 +38,8 @@
|
||||
#ifndef __GUAC_RDPSND_SERVICE_H
|
||||
#define __GUAC_RDPSND_SERVICE_H
|
||||
|
||||
#include <guacamole/audio.h>
|
||||
|
||||
/**
|
||||
* The maximum number of PCM formats to accept during the initial RDPSND
|
||||
* handshake with the RDP server.
|
||||
@ -87,7 +89,7 @@ typedef struct guac_rdpsndPlugin {
|
||||
/**
|
||||
* The current audio stream.
|
||||
*/
|
||||
audio_stream* audio;
|
||||
guac_audio_stream* audio;
|
||||
|
||||
/**
|
||||
* The block number of the last SNDC_WAVE (WaveInfo) PDU received.
|
||||
|
@ -1,211 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
|
||||
#include <vorbis/vorbisenc.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "ogg_encoder.h"
|
||||
|
||||
void ogg_encoder_begin_handler(audio_stream* audio) {
|
||||
|
||||
/* Allocate stream state */
|
||||
ogg_encoder_state* state = (ogg_encoder_state*)
|
||||
malloc(sizeof(ogg_encoder_state));
|
||||
|
||||
/* Init state */
|
||||
vorbis_info_init(&(state->info));
|
||||
vorbis_encode_init_vbr(&(state->info), audio->channels, audio->rate, 0.4);
|
||||
|
||||
vorbis_analysis_init(&(state->vorbis_state), &(state->info));
|
||||
vorbis_block_init(&(state->vorbis_state), &(state->vorbis_block));
|
||||
|
||||
vorbis_comment_init(&(state->comment));
|
||||
vorbis_comment_add_tag(&(state->comment), "ENCODER", "libguac-client-rdp");
|
||||
|
||||
ogg_stream_init(&(state->ogg_state), rand());
|
||||
|
||||
/* Write headers */
|
||||
{
|
||||
ogg_packet header;
|
||||
ogg_packet header_comm;
|
||||
ogg_packet header_code;
|
||||
|
||||
vorbis_analysis_headerout(
|
||||
&(state->vorbis_state),
|
||||
&(state->comment),
|
||||
&header, &header_comm, &header_code);
|
||||
|
||||
ogg_stream_packetin(&(state->ogg_state), &header);
|
||||
ogg_stream_packetin(&(state->ogg_state), &header_comm);
|
||||
ogg_stream_packetin(&(state->ogg_state), &header_code);
|
||||
|
||||
/* For each packet */
|
||||
while (ogg_stream_flush(&(state->ogg_state), &(state->ogg_page)) != 0) {
|
||||
|
||||
/* Write packet header */
|
||||
audio_stream_write_encoded(audio,
|
||||
state->ogg_page.header,
|
||||
state->ogg_page.header_len);
|
||||
|
||||
/* Write packet body */
|
||||
audio_stream_write_encoded(audio,
|
||||
state->ogg_page.body,
|
||||
state->ogg_page.body_len);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
audio->data = state;
|
||||
|
||||
}
|
||||
|
||||
void ogg_encoder_write_blocks(audio_stream* audio) {
|
||||
|
||||
/* Get state */
|
||||
ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
|
||||
|
||||
while (vorbis_analysis_blockout(&(state->vorbis_state),
|
||||
&(state->vorbis_block)) == 1) {
|
||||
|
||||
/* Analyze */
|
||||
vorbis_analysis(&(state->vorbis_block), NULL);
|
||||
vorbis_bitrate_addblock(&(state->vorbis_block));
|
||||
|
||||
/* Flush Ogg pages */
|
||||
while (vorbis_bitrate_flushpacket(&(state->vorbis_state),
|
||||
&(state->ogg_packet))) {
|
||||
|
||||
/* Weld packet into bitstream */
|
||||
ogg_stream_packetin(&(state->ogg_state), &(state->ogg_packet));
|
||||
|
||||
/* Write out pages */
|
||||
while (ogg_stream_pageout(&(state->ogg_state),
|
||||
&(state->ogg_page)) != 0) {
|
||||
|
||||
/* Write packet header */
|
||||
audio_stream_write_encoded(audio,
|
||||
state->ogg_page.header,
|
||||
state->ogg_page.header_len);
|
||||
|
||||
/* Write packet body */
|
||||
audio_stream_write_encoded(audio,
|
||||
state->ogg_page.body,
|
||||
state->ogg_page.body_len);
|
||||
|
||||
if (ogg_page_eos(&(state->ogg_page)))
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ogg_encoder_end_handler(audio_stream* audio) {
|
||||
|
||||
/* Get state */
|
||||
ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
|
||||
|
||||
/* Write end-of-stream */
|
||||
vorbis_analysis_wrote(&(state->vorbis_state), 0);
|
||||
ogg_encoder_write_blocks(audio);
|
||||
|
||||
/* Clean up encoder */
|
||||
ogg_stream_clear(&(state->ogg_state));
|
||||
vorbis_block_clear(&(state->vorbis_block));
|
||||
vorbis_dsp_clear(&(state->vorbis_state));
|
||||
vorbis_comment_clear(&(state->comment));
|
||||
vorbis_info_clear(&(state->info));
|
||||
|
||||
/* Free stream state */
|
||||
free(audio->data);
|
||||
|
||||
}
|
||||
|
||||
void ogg_encoder_write_handler(audio_stream* audio,
|
||||
unsigned char* pcm_data, int length) {
|
||||
|
||||
/* Get state */
|
||||
ogg_encoder_state* state = (ogg_encoder_state*) audio->data;
|
||||
|
||||
/* Calculate samples */
|
||||
int samples = length / audio->channels * 8 / audio->bps;
|
||||
int i;
|
||||
|
||||
/* Get buffer */
|
||||
float** buffer = vorbis_analysis_buffer(&(state->vorbis_state), samples);
|
||||
|
||||
signed char* readbuffer = (signed char*) pcm_data;
|
||||
|
||||
for (i=0; i<samples; i++) {
|
||||
|
||||
/* FIXME: For now, assume 2 channels, 16-bit */
|
||||
int left = ((readbuffer[i*4+1]<<8)|(0x00ff&(int)readbuffer[i*4]));
|
||||
int right = ((readbuffer[i*4+3]<<8)|(0x00ff&(int)readbuffer[i*4+2]));
|
||||
|
||||
/* Store sample in buffer */
|
||||
buffer[0][i] = left / 32768.f;
|
||||
buffer[1][i] = right / 32768.f;
|
||||
|
||||
}
|
||||
|
||||
/* Submit data */
|
||||
vorbis_analysis_wrote(&(state->vorbis_state), samples);
|
||||
|
||||
/* Write data */
|
||||
ogg_encoder_write_blocks(audio);
|
||||
|
||||
}
|
||||
|
||||
/* Encoder handlers */
|
||||
audio_encoder _ogg_encoder = {
|
||||
.mimetype = "audio/ogg",
|
||||
.begin_handler = ogg_encoder_begin_handler,
|
||||
.write_handler = ogg_encoder_write_handler,
|
||||
.end_handler = ogg_encoder_end_handler
|
||||
};
|
||||
|
||||
/* Actual encoder */
|
||||
audio_encoder* ogg_encoder = &_ogg_encoder;
|
||||
|
@ -1,67 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __GUAC_OGG_ENCODER_H
|
||||
#define __GUAC_OGG_ENCODER_H
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
#include <vorbis/vorbisenc.h>
|
||||
|
||||
typedef struct ogg_encoder_state {
|
||||
|
||||
/**
|
||||
* Ogg state
|
||||
*/
|
||||
ogg_stream_state ogg_state;
|
||||
ogg_page ogg_page;
|
||||
ogg_packet ogg_packet;
|
||||
|
||||
/**
|
||||
* Vorbis state
|
||||
*/
|
||||
vorbis_info info;
|
||||
vorbis_comment comment;
|
||||
vorbis_dsp_state vorbis_state;
|
||||
vorbis_block vorbis_block;
|
||||
|
||||
} ogg_encoder_state;
|
||||
|
||||
extern audio_encoder* ogg_encoder;
|
||||
|
||||
#endif
|
||||
|
@ -1,201 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#define WAV_BUFFER_SIZE 0x4000
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <guacamole/client.h>
|
||||
#include <guacamole/protocol.h>
|
||||
|
||||
#include "audio.h"
|
||||
#include "wav_encoder.h"
|
||||
|
||||
void wav_encoder_begin_handler(audio_stream* audio) {
|
||||
|
||||
/* Allocate stream state */
|
||||
wav_encoder_state* state = (wav_encoder_state*)
|
||||
malloc(sizeof(wav_encoder_state));
|
||||
|
||||
/* Initialize buffer */
|
||||
state->length = WAV_BUFFER_SIZE;
|
||||
state->used = 0;
|
||||
state->data_buffer = (unsigned char*) malloc(state->length);
|
||||
|
||||
audio->data = state;
|
||||
|
||||
}
|
||||
|
||||
void _wav_encoder_write_le(unsigned char* buffer, int value, int length) {
|
||||
|
||||
int offset;
|
||||
|
||||
/* Write all bytes in the given value in little-endian byte order */
|
||||
for (offset=0; offset<length; offset++) {
|
||||
|
||||
/* Store byte */
|
||||
*buffer = value & 0xFF;
|
||||
|
||||
/* Move to next byte */
|
||||
value >>= 8;
|
||||
buffer++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void wav_encoder_end_handler(audio_stream* audio) {
|
||||
|
||||
/*
|
||||
* Static header init
|
||||
*/
|
||||
|
||||
wav_encoder_riff_header riff_header = {
|
||||
.chunk_id = "RIFF",
|
||||
.chunk_format = "WAVE"
|
||||
};
|
||||
|
||||
wav_encoder_fmt_header fmt_header = {
|
||||
.subchunk_id = "fmt ",
|
||||
.subchunk_size = {0x10, 0x00, 0x00, 0x00}, /* 16 */
|
||||
.subchunk_format = {0x01, 0x00} /* 1 = PCM */
|
||||
};
|
||||
|
||||
wav_encoder_data_header data_header = {
|
||||
.subchunk_id = "data"
|
||||
};
|
||||
|
||||
/* Get state */
|
||||
wav_encoder_state* state = (wav_encoder_state*) audio->data;
|
||||
|
||||
/*
|
||||
* RIFF HEADER
|
||||
*/
|
||||
|
||||
/* Chunk size */
|
||||
_wav_encoder_write_le(riff_header.chunk_size,
|
||||
4 + sizeof(fmt_header) + sizeof(data_header) + state->used,
|
||||
sizeof(riff_header.chunk_size));
|
||||
|
||||
audio_stream_write_encoded(audio,
|
||||
(unsigned char*) &riff_header,
|
||||
sizeof(riff_header));
|
||||
|
||||
/*
|
||||
* FMT HEADER
|
||||
*/
|
||||
|
||||
/* Channels */
|
||||
_wav_encoder_write_le(fmt_header.subchunk_channels,
|
||||
audio->channels, sizeof(fmt_header.subchunk_channels));
|
||||
|
||||
/* Sample rate */
|
||||
_wav_encoder_write_le(fmt_header.subchunk_sample_rate,
|
||||
audio->rate, sizeof(fmt_header.subchunk_sample_rate));
|
||||
|
||||
/* Byte rate */
|
||||
_wav_encoder_write_le(fmt_header.subchunk_byte_rate,
|
||||
audio->rate * audio->channels * audio->bps / 8,
|
||||
sizeof(fmt_header.subchunk_byte_rate));
|
||||
|
||||
/* Block align */
|
||||
_wav_encoder_write_le(fmt_header.subchunk_block_align,
|
||||
audio->channels * audio->bps / 8,
|
||||
sizeof(fmt_header.subchunk_block_align));
|
||||
|
||||
/* Bits per second */
|
||||
_wav_encoder_write_le(fmt_header.subchunk_bps,
|
||||
audio->bps, sizeof(fmt_header.subchunk_bps));
|
||||
|
||||
audio_stream_write_encoded(audio,
|
||||
(unsigned char*) &fmt_header,
|
||||
sizeof(fmt_header));
|
||||
|
||||
/*
|
||||
* DATA HEADER
|
||||
*/
|
||||
|
||||
/* PCM data size */
|
||||
_wav_encoder_write_le(data_header.subchunk_size,
|
||||
state->used, sizeof(data_header.subchunk_size));
|
||||
|
||||
audio_stream_write_encoded(audio,
|
||||
(unsigned char*) &data_header,
|
||||
sizeof(data_header));
|
||||
|
||||
/* Write .wav data */
|
||||
audio_stream_write_encoded(audio, state->data_buffer, state->used);
|
||||
|
||||
/* Free stream state */
|
||||
free(state);
|
||||
|
||||
}
|
||||
|
||||
void wav_encoder_write_handler(audio_stream* audio,
|
||||
unsigned char* pcm_data, int length) {
|
||||
|
||||
/* Get state */
|
||||
wav_encoder_state* state = (wav_encoder_state*) audio->data;
|
||||
|
||||
/* Increase size of buffer if necessary */
|
||||
if (state->used + length > state->length) {
|
||||
|
||||
/* Increase to double concatenated size to accomodate */
|
||||
state->length = (state->length + length)*2;
|
||||
state->data_buffer = realloc(state->data_buffer,
|
||||
state->length);
|
||||
|
||||
}
|
||||
|
||||
/* Append to buffer */
|
||||
memcpy(&(state->data_buffer[state->used]), pcm_data, length);
|
||||
state->used += length;
|
||||
|
||||
}
|
||||
|
||||
/* Encoder handlers */
|
||||
audio_encoder _wav_encoder = {
|
||||
.mimetype = "audio/wav",
|
||||
.begin_handler = wav_encoder_begin_handler,
|
||||
.write_handler = wav_encoder_write_handler,
|
||||
.end_handler = wav_encoder_end_handler
|
||||
};
|
||||
|
||||
/* Actual encoder */
|
||||
audio_encoder* wav_encoder = &_wav_encoder;
|
||||
|
@ -1,144 +0,0 @@
|
||||
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is libguac-client-rdp.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Michael Jumper.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#ifndef __GUAC_WAV_ENCODER_H
|
||||
#define __GUAC_WAV_ENCODER_H
|
||||
|
||||
#include "audio.h"
|
||||
|
||||
typedef struct wav_encoder_riff_header {
|
||||
|
||||
/**
|
||||
* The RIFF chunk header, normally the string "RIFF".
|
||||
*/
|
||||
unsigned char chunk_id[4];
|
||||
|
||||
/**
|
||||
* Size of the entire file, not including chunk_id or chunk_size.
|
||||
*/
|
||||
unsigned char chunk_size[4];
|
||||
|
||||
/**
|
||||
* The format of this file, normally the string "WAVE".
|
||||
*/
|
||||
unsigned char chunk_format[4];
|
||||
|
||||
} wav_encoder_riff_header;
|
||||
|
||||
typedef struct wav_encoder_fmt_header {
|
||||
|
||||
/**
|
||||
* ID of this subchunk. For the fmt subchunk, this should be "fmt ".
|
||||
*/
|
||||
unsigned char subchunk_id[4];
|
||||
|
||||
/**
|
||||
* The size of the rest of this subchunk. For PCM, this will be 16.
|
||||
*/
|
||||
unsigned char subchunk_size[4];
|
||||
|
||||
/**
|
||||
* Format of this subchunk. For PCM, this will be 1.
|
||||
*/
|
||||
unsigned char subchunk_format[2];
|
||||
|
||||
/**
|
||||
* The number of channels in the PCM data.
|
||||
*/
|
||||
unsigned char subchunk_channels[2];
|
||||
|
||||
/**
|
||||
* The sample rate of the PCM data.
|
||||
*/
|
||||
unsigned char subchunk_sample_rate[4];
|
||||
|
||||
/**
|
||||
* The sample rate of the PCM data in bytes per second.
|
||||
*/
|
||||
unsigned char subchunk_byte_rate[4];
|
||||
|
||||
/**
|
||||
* The number of bytes per sample.
|
||||
*/
|
||||
unsigned char subchunk_block_align[2];
|
||||
|
||||
/**
|
||||
* The number of bits per sample.
|
||||
*/
|
||||
unsigned char subchunk_bps[2];
|
||||
|
||||
} wav_encoder_fmt_header;
|
||||
|
||||
typedef struct wav_encoder_state {
|
||||
|
||||
/**
|
||||
* Arbitrary PCM data available for writing when the overall WAV is
|
||||
* flushed.
|
||||
*/
|
||||
unsigned char* data_buffer;
|
||||
|
||||
/**
|
||||
* The number of bytes currently present in the data buffer.
|
||||
*/
|
||||
int used;
|
||||
|
||||
/**
|
||||
* The total number of bytes that can be written into the data buffer
|
||||
* without requiring resizing.
|
||||
*/
|
||||
int length;
|
||||
|
||||
} wav_encoder_state;
|
||||
|
||||
typedef struct wav_encoder_data_header {
|
||||
|
||||
/**
|
||||
* ID of this subchunk. For the data subchunk, this should be "data".
|
||||
*/
|
||||
unsigned char subchunk_id[4];
|
||||
|
||||
/**
|
||||
* The number of bytes in the PCM data.
|
||||
*/
|
||||
unsigned char subchunk_size[4];
|
||||
|
||||
} wav_encoder_data_header;
|
||||
|
||||
extern audio_encoder* wav_encoder;
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user