diff options
author | Marc-André Lureau <marcandre.lureau@redhat.com> | 2010-12-01 03:06:48 +0100 |
---|---|---|
committer | Marc-André Lureau <marcandre.lureau@redhat.com> | 2010-12-01 03:07:31 +0100 |
commit | 77d7ae295f47ebf100c016eb75bf59990405c94d (patch) | |
tree | 0402d9822f0ce1a4ae24925b6774a0bc325c2767 | |
parent | d0cda284602914fa308e39f054e35b4a8ccf2495 (diff) | |
download | spice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.tar.gz spice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.tar.xz spice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.zip |
gtk: add CELT recording
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | gtk/channel-record.c | 111 |
2 files changed, 104 insertions, 9 deletions
@@ -3,12 +3,10 @@ * use libspice-common instead of common/ * clipboard images * TunnelChannel? -* audio: CELT support * protocol <1 support? * win32 * i18 in libs * documentation. -* open session by fd (tunnel ssh) * make the IO fully async * use GSocket * use GStreamer diff --git a/gtk/channel-record.c b/gtk/channel-record.c index 5a23a13..f3f855b 100644 --- a/gtk/channel-record.c +++ b/gtk/channel-record.c @@ -15,6 +15,8 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <http://www.gnu.org/licenses/>. */ +#include <celt051/celt.h> + #include "spice-client.h" #include "spice-common.h" #include "spice-channel-priv.h" @@ -28,6 +30,11 @@ struct spice_record_channel { int mode; gboolean started; + CELTMode *celt_mode; + CELTEncoder *celt_encoder; + gsize frame_bytes; + guint8 *last_frame; + gsize last_frame_current; }; G_DEFINE_TYPE(SpiceRecordChannel, spice_record_channel, SPICE_TYPE_CHANNEL) @@ -44,6 +51,9 @@ static guint signals[SPICE_RECORD_LAST_SIGNAL]; static void spice_record_handle_msg(SpiceChannel *channel, spice_msg_in *msg); static void channel_up(SpiceChannel *channel); +#define FRAME_SIZE 256 +#define CELT_BIT_RATE (64 * 1024) + /* ------------------------------------------------------------------ */ static void spice_record_channel_init(SpiceRecordChannel *channel) @@ -52,10 +62,26 @@ static void spice_record_channel_init(SpiceRecordChannel *channel) c = channel->priv = SPICE_RECORD_CHANNEL_GET_PRIVATE(channel); memset(c, 0, sizeof(*c)); + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_RECORD_CAP_CELT_0_5_1); } static void spice_record_channel_finalize(GObject *obj) { + spice_record_channel *c = SPICE_RECORD_CHANNEL(obj)->priv; + + g_free(c->last_frame); + c->last_frame = NULL; + + if (c->celt_encoder) { + celt051_encoder_destroy(c->celt_encoder); + c->celt_encoder = NULL; + } + + if (c->celt_mode) { + celt051_mode_destroy(c->celt_mode); + c->celt_mode = NULL; + } + if (G_OBJECT_CLASS(spice_record_channel_parent_class)->finalize) G_OBJECT_CLASS(spice_record_channel_parent_class)->finalize(obj); } @@ -122,10 +148,10 @@ static void channel_up(SpiceChannel *channel) rc = SPICE_RECORD_CHANNEL(channel)->priv; if (spice_channel_test_capability(channel, SPICE_RECORD_CAP_CELT_0_5_1)) { - SPICE_DEBUG("record compatible CELT_0_5_1, TODO"); + rc->mode = SPICE_AUDIO_DATA_MODE_CELT_0_5_1; + } else { + rc->mode = SPICE_AUDIO_DATA_MODE_RAW; } - - rc->mode = SPICE_AUDIO_DATA_MODE_RAW; } static void spice_record_start_mark(SpiceRecordChannel *channel, uint32_t time) @@ -153,9 +179,11 @@ void spice_record_send_data(SpiceRecordChannel *channel, gpointer data, spice_record_channel *rc; spice_channel *c; SpiceMsgcRecordPacket p = {0, }; - spice_msg_out *msg; + int celt_compressed_frame_bytes = FRAME_SIZE * CELT_BIT_RATE / 44100 / 8; + uint8_t *celt_buf = NULL; g_return_if_fail(channel != NULL); + rc = channel->priv; c = SPICE_CHANNEL(channel)->priv; @@ -165,17 +193,59 @@ void spice_record_send_data(SpiceRecordChannel *channel, gpointer data, rc->started = TRUE; } + if (rc->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) + celt_buf = g_alloca(celt_compressed_frame_bytes); + p.time = time; while (bytes > 0) { - gsize n; + gsize n, frame_size; + spice_msg_out *msg; + uint8_t *frame; + + if (rc->last_frame_current > 0) { + /* complete previous frame */ + n = MIN(bytes, rc->frame_bytes - rc->last_frame_current); + memcpy(rc->last_frame + rc->last_frame_current, data, n); + rc->last_frame_current += n; + if (rc->last_frame_current < rc->frame_bytes) + /* if the frame is still incomplete, return */ + break; + frame = rc->last_frame; + frame_size = rc->frame_bytes; + } else { + n = MIN(bytes, rc->frame_bytes); + frame_size = n; + frame = data; + } + + if (rc->last_frame_current == 0 && + n < rc->frame_bytes) { + /* start a new frame */ + memcpy(rc->last_frame, data, n); + rc->last_frame_current = n; + break; + } + + if (rc->mode == SPICE_AUDIO_DATA_MODE_CELT_0_5_1) { + frame_size = celt051_encode(rc->celt_encoder, (celt_int16_t *)frame, NULL, celt_buf, + celt_compressed_frame_bytes); + if (frame_size < 0) { + g_warning("celt encode failed"); + return; + } + frame = celt_buf; + } - n = MIN(bytes, 1024); msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_RECORD_DATA); msg->marshallers->msgc_record_data(msg->marshaller, &p); - spice_marshaller_add(msg->marshaller, data, n); + spice_marshaller_add(msg->marshaller, frame, frame_size); spice_msg_out_send(msg); spice_msg_out_unref(msg); + + if (rc->last_frame_current == rc->frame_bytes) + rc->last_frame_current = 0; + bytes -= n; data = (guint8*)data + n; } @@ -191,11 +261,38 @@ static void record_handle_start(SpiceChannel *channel, spice_msg_in *in) SPICE_DEBUG("%s: fmt %d channels %d freq %d", __FUNCTION__, start->format, start->channels, start->frequency); + c->frame_bytes = FRAME_SIZE * 16 * start->channels / 8; + + g_free(c->last_frame); + c->last_frame = g_malloc(c->frame_bytes); + c->last_frame_current = 0; + switch (c->mode) { case SPICE_AUDIO_DATA_MODE_RAW: g_signal_emit(channel, signals[SPICE_RECORD_START], 0, start->format, start->channels, start->frequency); break; + case SPICE_AUDIO_DATA_MODE_CELT_0_5_1: { + int celt_mode_err; + + g_return_if_fail(start->format == SPICE_AUDIO_FMT_S16); + + if (!c->celt_mode) + c->celt_mode = celt051_mode_create(start->frequency, start->channels, FRAME_SIZE, + &celt_mode_err); + if (!c->celt_mode) + g_warning("Failed to create celt mode"); + + if (!c->celt_encoder) + c->celt_encoder = celt051_encoder_create(c->celt_mode); + + if (!c->celt_encoder) + g_warning("Failed to create celt encoder"); + + g_signal_emit(channel, signals[SPICE_RECORD_START], 0, + start->format, start->channels, start->frequency); + break; + } default: g_warning("%s: unhandled mode %d", __FUNCTION__, c->mode); break; |