From c1a495136344bbd778a91871d5735e347663c271 Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 1 Nov 2012 14:25:34 -0700 Subject: [PATCH] Implement wav encoder. --- protocols/rdp/Makefile.am | 22 +-- protocols/rdp/include/wav_encoder.h | 144 ++++++++++++++++++++ protocols/rdp/src/wav_encoder.c | 201 ++++++++++++++++++++++++++++ 3 files changed, 357 insertions(+), 10 deletions(-) create mode 100644 protocols/rdp/include/wav_encoder.h create mode 100644 protocols/rdp/src/wav_encoder.c diff --git a/protocols/rdp/Makefile.am b/protocols/rdp/Makefile.am index 618b4b72..f41f66ac 100644 --- a/protocols/rdp/Makefile.am +++ b/protocols/rdp/Makefile.am @@ -45,24 +45,27 @@ freerdp_LTLIBRARIES = guac_rdpsnd.la libguac_client_rdp_la_SOURCES = \ src/audio.c \ src/client.c \ - src/rdp_bitmap.c \ - src/rdp_glyph.c \ - src/rdp_pointer.c \ - src/rdp_gdi.c \ + src/default_pointer.c \ src/guac_handlers.c \ src/ogg_encoder.c \ + src/rdp_bitmap.c \ src/rdp_cliprdr.c \ - src/rdp_keymap.c \ + src/rdp_gdi.c \ + src/rdp_glyph.c \ src/rdp_keymap_base.c \ + src/rdp_keymap.c \ src/rdp_keymap_en_us.c \ - src/default_pointer.c + src/rdp_pointer.c \ + src/wav_encoder.c guac_rdpsnd_la_SOURCES = \ - src/audio.c \ guac_rdpsnd/messages.c \ - guac_rdpsnd/service.c + guac_rdpsnd/service.c \ + src/audio.c noinst_HEADERS = \ + guac_rdpsnd/messages.h \ + guac_rdpsnd/service.h \ include/audio.h \ include/client.h \ include/default_pointer.h \ @@ -74,8 +77,7 @@ noinst_HEADERS = \ include/rdp_glyph.h \ include/rdp_keymap.h \ include/rdp_pointer.h \ - guac_rdpsnd/messages.h \ - guac_rdpsnd/service.h + include/wav_encoder.h libguac_client_rdp_la_LDFLAGS = -version-info 0:0:0 guac_rdpsnd_la_LDFLAGS = -module -avoid-version -shared diff --git a/protocols/rdp/include/wav_encoder.h b/protocols/rdp/include/wav_encoder.h new file mode 100644 index 00000000..ce575ae9 --- /dev/null +++ b/protocols/rdp/include/wav_encoder.h @@ -0,0 +1,144 @@ + +/* ***** 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 + diff --git a/protocols/rdp/src/wav_encoder.c b/protocols/rdp/src/wav_encoder.c new file mode 100644 index 00000000..0a306675 --- /dev/null +++ b/protocols/rdp/src/wav_encoder.c @@ -0,0 +1,201 @@ + +/* ***** 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 +#include + +#include +#include + +#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>= 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; +