212 lines
6.5 KiB
C
212 lines
6.5 KiB
C
|
|
/* ***** 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.
|
|
*
|
|
* 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/audio.h>
|
|
#include <guacamole/client.h>
|
|
#include <guacamole/protocol.h>
|
|
|
|
#include <vorbis/vorbisenc.h>
|
|
|
|
#include "ogg_encoder.h"
|
|
|
|
void ogg_encoder_begin_handler(guac_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 */
|
|
guac_audio_stream_write_encoded(audio,
|
|
state->ogg_page.header,
|
|
state->ogg_page.header_len);
|
|
|
|
/* Write packet body */
|
|
guac_audio_stream_write_encoded(audio,
|
|
state->ogg_page.body,
|
|
state->ogg_page.body_len);
|
|
}
|
|
|
|
}
|
|
|
|
audio->data = state;
|
|
|
|
}
|
|
|
|
void ogg_encoder_write_blocks(guac_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 */
|
|
guac_audio_stream_write_encoded(audio,
|
|
state->ogg_page.header,
|
|
state->ogg_page.header_len);
|
|
|
|
/* Write packet body */
|
|
guac_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(guac_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(guac_audio_stream* audio,
|
|
const 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 */
|
|
guac_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 */
|
|
guac_audio_encoder* ogg_encoder = &_ogg_encoder;
|
|
|