summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc-André Lureau <marcandre.lureau@redhat.com>2010-12-01 03:06:48 +0100
committerMarc-André Lureau <marcandre.lureau@redhat.com>2010-12-01 03:07:31 +0100
commit77d7ae295f47ebf100c016eb75bf59990405c94d (patch)
tree0402d9822f0ce1a4ae24925b6774a0bc325c2767
parentd0cda284602914fa308e39f054e35b4a8ccf2495 (diff)
downloadspice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.tar.gz
spice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.tar.xz
spice-gtk-77d7ae295f47ebf100c016eb75bf59990405c94d.zip
gtk: add CELT recording
-rw-r--r--TODO2
-rw-r--r--gtk/channel-record.c111
2 files changed, 104 insertions, 9 deletions
diff --git a/TODO b/TODO
index a2c304fe..87f5268 100644
--- a/TODO
+++ b/TODO
@@ -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;