[Spice-devel] [PATCH 05/16] server: rename files

Frediano Ziglio fziglio at redhat.com
Thu Nov 26 08:06:03 PST 2015


From: Marc-André Lureau <marcandre.lureau at gmail.com>

---
 NEWS                                      |    2 +-
 server/Makefile.am                        |   62 +-
 server/canvas.c                           |   24 +
 server/canvas.h                           |   27 +
 server/char-device.c                      | 1035 ++++++++++++++++++
 server/char-device.h                      |  216 ++++
 server/char_device.c                      | 1035 ------------------
 server/char_device.h                      |  216 ----
 server/dcc-encoders.h                     |    9 +-
 server/dispatcher.h                       |    3 +-
 server/display-channel.h                  |   16 +-
 server/glz-encoder-dict.c                 |  634 +++++++++++
 server/glz-encoder-dict.h                 |   71 ++
 server/glz-encoder-priv.h                 |  201 ++++
 server/glz-encoder.c                      |  311 ++++++
 server/glz-encoder.h                      |   74 ++
 server/glz_encoder.c                      |  311 ------
 server/glz_encoder.h                      |   55 -
 server/glz_encoder_config.h               |   59 --
 server/glz_encoder_dictionary.c           |  633 -----------
 server/glz_encoder_dictionary.h           |   69 --
 server/glz_encoder_dictionary_protected.h |  186 ----
 server/image-cache.c                      |  214 ++++
 server/image-cache.h                      |   65 ++
 server/inputs-channel.c                   |  679 ++++++++++++
 server/inputs-channel.h                   |   38 +
 server/inputs_channel.c                   |  679 ------------
 server/inputs_channel.h                   |   38 -
 server/jpeg-encoder.c                     |  249 +++++
 server/jpeg-encoder.h                     |   61 ++
 server/jpeg_encoder.c                     |  248 -----
 server/jpeg_encoder.h                     |   61 --
 server/main-channel.c                     | 1345 ++++++++++++++++++++++++
 server/main-channel.h                     |  103 ++
 server/main-dispatcher.c                  |  217 ++++
 server/main-dispatcher.h                  |   36 +
 server/main_channel.c                     | 1345 ------------------------
 server/main_channel.h                     |  103 --
 server/main_dispatcher.c                  |  217 ----
 server/main_dispatcher.h                  |   36 -
 server/memslot.c                          |  183 ++++
 server/memslot.h                          |   71 ++
 server/migration-protocol.h               |  213 ++++
 server/migration_protocol.h               |  213 ----
 server/mjpeg-encoder.c                    | 1375 ++++++++++++++++++++++++
 server/mjpeg-encoder.h                    |  100 ++
 server/mjpeg_encoder.c                    | 1375 ------------------------
 server/mjpeg_encoder.h                    |  100 --
 server/red_channel.c                      |    2 +-
 server/red_common.h                       |   17 +-
 server/red_dispatcher.c                   |    2 +-
 server/red_dispatcher.h                   |    2 -
 server/red_memslots.c                     |  184 ----
 server/red_memslots.h                     |   72 --
 server/red_parse_qxl.c                    |   62 +-
 server/red_parse_qxl.h                    |    2 +-
 server/red_record_qxl.c                   |   62 +-
 server/red_record_qxl.h                   |    2 +-
 server/red_replay_qxl.c                   |    2 +-
 server/red_worker.c                       |   16 +-
 server/red_worker.h                       |    3 -
 server/reds.c                             |   13 +-
 server/reds.h                             |    5 +-
 server/reds_stream.c                      |    2 +-
 server/reds_sw_canvas.c                   |   26 -
 server/reds_sw_canvas.h                   |   24 -
 server/smartcard.c                        |    4 +-
 server/snd_worker.c                       | 1625 -----------------------------
 server/snd_worker.h                       |   33 -
 server/sound.c                            | 1625 +++++++++++++++++++++++++++++
 server/sound.h                            |   33 +
 server/spice-bitmap-utils.c               |  162 +++
 server/spice-bitmap-utils.h               |   15 +-
 server/spice_bitmap_utils.c               |  188 ----
 server/spice_bitmap_utils.h               |    8 -
 server/spice_image_cache.c                |  214 ----
 server/spice_image_cache.h                |   65 --
 server/spicevmc.c                         |    4 +-
 server/stream.h                           |    4 +-
 server/utils.h                            |    6 +-
 server/zlib-encoder.c                     |  125 +++
 server/zlib-encoder.h                     |   47 +
 server/zlib_encoder.c                     |  125 ---
 server/zlib_encoder.h                     |   47 -
 84 files changed, 9695 insertions(+), 9746 deletions(-)
 create mode 100644 server/canvas.c
 create mode 100644 server/canvas.h
 create mode 100644 server/char-device.c
 create mode 100644 server/char-device.h
 delete mode 100644 server/char_device.c
 delete mode 100644 server/char_device.h
 create mode 100644 server/glz-encoder-dict.c
 create mode 100644 server/glz-encoder-dict.h
 create mode 100644 server/glz-encoder-priv.h
 create mode 100644 server/glz-encoder.c
 create mode 100644 server/glz-encoder.h
 delete mode 100644 server/glz_encoder.c
 delete mode 100644 server/glz_encoder.h
 delete mode 100644 server/glz_encoder_config.h
 delete mode 100644 server/glz_encoder_dictionary.c
 delete mode 100644 server/glz_encoder_dictionary.h
 delete mode 100644 server/glz_encoder_dictionary_protected.h
 create mode 100644 server/image-cache.c
 create mode 100644 server/image-cache.h
 create mode 100644 server/inputs-channel.c
 create mode 100644 server/inputs-channel.h
 delete mode 100644 server/inputs_channel.c
 delete mode 100644 server/inputs_channel.h
 create mode 100644 server/jpeg-encoder.c
 create mode 100644 server/jpeg-encoder.h
 delete mode 100644 server/jpeg_encoder.c
 delete mode 100644 server/jpeg_encoder.h
 create mode 100644 server/main-channel.c
 create mode 100644 server/main-channel.h
 create mode 100644 server/main-dispatcher.c
 create mode 100644 server/main-dispatcher.h
 delete mode 100644 server/main_channel.c
 delete mode 100644 server/main_channel.h
 delete mode 100644 server/main_dispatcher.c
 delete mode 100644 server/main_dispatcher.h
 create mode 100644 server/memslot.c
 create mode 100644 server/memslot.h
 create mode 100644 server/migration-protocol.h
 delete mode 100644 server/migration_protocol.h
 create mode 100644 server/mjpeg-encoder.c
 create mode 100644 server/mjpeg-encoder.h
 delete mode 100644 server/mjpeg_encoder.c
 delete mode 100644 server/mjpeg_encoder.h
 delete mode 100644 server/red_memslots.c
 delete mode 100644 server/red_memslots.h
 delete mode 100644 server/reds_sw_canvas.c
 delete mode 100644 server/reds_sw_canvas.h
 delete mode 100644 server/snd_worker.c
 delete mode 100644 server/snd_worker.h
 create mode 100644 server/sound.c
 create mode 100644 server/sound.h
 delete mode 100644 server/spice_bitmap_utils.c
 delete mode 100644 server/spice_bitmap_utils.h
 delete mode 100644 server/spice_image_cache.c
 delete mode 100644 server/spice_image_cache.h
 create mode 100644 server/zlib-encoder.c
 create mode 100644 server/zlib-encoder.h
 delete mode 100644 server/zlib_encoder.c
 delete mode 100644 server/zlib_encoder.h

diff --git a/NEWS b/NEWS
index a33f6cf..c05f62a 100644
--- a/NEWS
+++ b/NEWS
@@ -101,7 +101,7 @@ Major changes in 0.11.3:
  SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS
  SPICE_MAIN_CAP_SEAMLESS_MIGRATE
 * Misc:
- * char_device.c: Introducing shared flow control code for char devices
+ * char-device.c: Introducing shared flow control code for char devices
  * Enable build without client, cegui and slirp.
 
 Major changes in 0.11.0:
diff --git a/server/Makefile.am b/server/Makefile.am
index 6b45a42..7876e9c 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -4,6 +4,7 @@ SUBDIRS = . tests
 AM_CPPFLAGS =					\
 	-DSPICE_SERVER_INTERNAL			\
 	-DRED_STATISTICS			\
+	$(CELT051_CFLAGS)			\
 	$(COMMON_CFLAGS)			\
 	$(GLIB2_CFLAGS)				\
 	$(LZ4_CFLAGS)				\
@@ -66,25 +67,24 @@ libspice_server_la_SOURCES =			\
 	agent-msg-filter.c			\
 	agent-msg-filter.h			\
 	cache-item.h				\
-	char_device.c				\
-	char_device.h				\
+	char-device.c				\
+	char-device.h				\
 	demarshallers.h				\
-	glz_encoder.c				\
-	glz_encoder.h				\
-	glz_encoder_config.h			\
-	glz_encoder_dictionary.c		\
-	glz_encoder_dictionary.h		\
-	glz_encoder_dictionary_protected.h	\
-	inputs_channel.c			\
-	inputs_channel.h			\
-	jpeg_encoder.c				\
-	jpeg_encoder.h				\
+	glz-encoder.c				\
+	glz-encoder.h				\
+	glz-encoder-dict.c		\
+	glz-encoder-dict.h		\
+	glz-encoder-priv.h	\
+	inputs-channel.c			\
+	inputs-channel.h			\
+	jpeg-encoder.c				\
+	jpeg-encoder.h				\
 	lz4_encoder.c				\
 	lz4_encoder.h				\
-	main_channel.c				\
-	main_channel.h				\
-	mjpeg_encoder.c				\
-	mjpeg_encoder.h				\
+	main-channel.c				\
+	main-channel.h				\
+	mjpeg-encoder.c				\
+	mjpeg-encoder.h				\
 	red_channel.c				\
 	red_channel.h				\
 	red_common.h				\
@@ -92,11 +92,11 @@ libspice_server_la_SOURCES =			\
 	dispatcher.h				\
 	red_dispatcher.c			\
 	red_dispatcher.h			\
-	main_dispatcher.c			\
-	main_dispatcher.h			\
-	migration_protocol.h		\
-	red_memslots.c				\
-	red_memslots.h				\
+	main-dispatcher.c			\
+	main-dispatcher.h			\
+	migration-protocol.h		\
+	memslot.c				\
+	memslot.h				\
 	red_parse_qxl.c				\
 	red_record_qxl.c			\
 	red_record_qxl.h			\
@@ -114,20 +114,18 @@ libspice_server_la_SOURCES =			\
 	reds-private.h				\
 	reds_stream.c				\
 	reds_stream.h				\
-	reds_sw_canvas.c			\
-	reds_sw_canvas.h			\
-	snd_worker.c				\
-	snd_worker.h				\
+	sound.c				\
+	sound.h				\
+	spice-experimental.h			\
+	spice.h					\
 	stat.h					\
 	spicevmc.c				\
 	spice_timer_queue.c			\
 	spice_timer_queue.h			\
-	zlib_encoder.c				\
-	zlib_encoder.h				\
-	spice_bitmap_utils.h		\
-	spice_bitmap_utils.c		\
-	spice_image_cache.h			\
-	spice_image_cache.c			\
+	zlib-encoder.c				\
+	zlib-encoder.h				\
+	image-cache.h			\
+	image-cache.c			\
 	pixmap-cache.h				\
 	pixmap-cache.c				\
 	tree.h				\
@@ -144,6 +142,8 @@ libspice_server_la_SOURCES =			\
 	display-limits.h			\
 	dcc-encoders.c					\
 	dcc-encoders.h					\
+	canvas.c					\
+	canvas.h					\
 	$(NULL)
 
 if HAVE_SMARTCARD
diff --git a/server/canvas.c b/server/canvas.c
new file mode 100644
index 0000000..883fe25
--- /dev/null
+++ b/server/canvas.c
@@ -0,0 +1,24 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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 "canvas.h"
+
+#define SPICE_CANVAS_INTERNAL
+#define SW_CANVAS_IMAGE_CACHE
+#include "common/sw_canvas.c"
+#undef SW_CANVAS_IMAGE_CACHE
+#undef SPICE_CANVAS_INTERNAL
diff --git a/server/canvas.h b/server/canvas.h
new file mode 100644
index 0000000..d2835ec
--- /dev/null
+++ b/server/canvas.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2011 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifndef CANVAS_H_
+#define CANVAS_H_
+
+#define SPICE_CANVAS_INTERNAL
+#define SW_CANVAS_IMAGE_CACHE
+#include "common/sw_canvas.h"
+#undef SW_CANVAS_IMAGE_CACHE
+#undef SPICE_CANVAS_INTERNAL
+
+#endif /* CANVAS_H_ */
diff --git a/server/char-device.c b/server/char-device.c
new file mode 100644
index 0000000..3790fab
--- /dev/null
+++ b/server/char-device.c
@@ -0,0 +1,1035 @@
+/* spice-server char device flow control code
+
+   Copyright (C) 2012 Red Hat, Inc.
+
+   Red Hat Authors:
+   Yonit Halperin <yhalperi at redhat.com>
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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 <config.h>
+#include "char-device.h"
+#include "red_channel.h"
+#include "reds.h"
+
+#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
+#define SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT 30000
+#define MAX_POOL_SIZE (10 * 64 * 1024)
+
+typedef struct SpiceCharDeviceClientState SpiceCharDeviceClientState;
+struct SpiceCharDeviceClientState {
+    RingItem link;
+    SpiceCharDeviceState *dev;
+    RedClient *client;
+    int do_flow_control;
+    uint64_t num_client_tokens;
+    uint64_t num_client_tokens_free; /* client messages that were consumed by the device */
+    uint64_t num_send_tokens; /* send to client */
+    SpiceTimer *wait_for_tokens_timer;
+    int wait_for_tokens_started;
+    Ring send_queue;
+    uint32_t send_queue_size;
+    uint32_t max_send_queue_size;
+};
+
+struct SpiceCharDeviceState {
+    int running;
+    int active; /* has read/write been performed since the device was started */
+    int wait_for_migrate_data;
+    uint32_t refs;
+
+    Ring write_queue;
+    Ring write_bufs_pool;
+    uint64_t cur_pool_size;
+    SpiceCharDeviceWriteBuffer *cur_write_buf;
+    uint8_t *cur_write_buf_pos;
+    SpiceTimer *write_to_dev_timer;
+    uint64_t num_self_tokens;
+
+    Ring clients; /* list of SpiceCharDeviceClientState */
+    uint32_t num_clients;
+
+    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
+    SpiceCharDeviceInstance *sin;
+
+    int during_read_from_device;
+    int during_write_to_device;
+
+    SpiceCharDeviceCallbacks cbs;
+    void *opaque;
+};
+
+enum {
+    WRITE_BUFFER_ORIGIN_NONE,
+    WRITE_BUFFER_ORIGIN_CLIENT,
+    WRITE_BUFFER_ORIGIN_SERVER,
+    WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
+};
+
+/* Holding references for avoiding access violation if the char device was
+ * destroyed during a callback */
+static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev);
+static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev);
+static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf);
+
+static void spice_char_dev_write_retry(void *opaque);
+
+typedef struct SpiceCharDeviceMsgToClientItem {
+    RingItem link;
+    SpiceCharDeviceMsgToClient *msg;
+} SpiceCharDeviceMsgToClientItem;
+
+static void spice_char_device_write_buffer_free(SpiceCharDeviceWriteBuffer *buf)
+{
+    if (buf == NULL)
+        return;
+
+    free(buf->buf);
+    free(buf);
+}
+
+static void write_buffers_queue_free(Ring *write_queue)
+{
+    while (!ring_is_empty(write_queue)) {
+        RingItem *item = ring_get_tail(write_queue);
+        SpiceCharDeviceWriteBuffer *buf;
+
+        ring_remove(item);
+        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        spice_char_device_write_buffer_free(buf);
+    }
+}
+
+static void spice_char_device_write_buffer_pool_add(SpiceCharDeviceState *dev,
+                                                    SpiceCharDeviceWriteBuffer *buf)
+{
+    if (buf->refs == 1 &&
+        dev->cur_pool_size < MAX_POOL_SIZE) {
+        buf->buf_used = 0;
+        buf->origin = WRITE_BUFFER_ORIGIN_NONE;
+        buf->client = NULL;
+        dev->cur_pool_size += buf->buf_size;
+        ring_add(&dev->write_bufs_pool, &buf->link);
+        return;
+    }
+
+    /* Buffer still being used - just unref for the caller */
+    spice_char_device_write_buffer_unref(buf);
+}
+
+static void spice_char_device_client_send_queue_free(SpiceCharDeviceState *dev,
+                                                     SpiceCharDeviceClientState *dev_client)
+{
+    spice_debug("send_queue_empty %d", ring_is_empty(&dev_client->send_queue));
+    while (!ring_is_empty(&dev_client->send_queue)) {
+        RingItem *item = ring_get_tail(&dev_client->send_queue);
+        SpiceCharDeviceMsgToClientItem *msg_item = SPICE_CONTAINEROF(item,
+                                                                     SpiceCharDeviceMsgToClientItem,
+                                                                     link);
+
+        ring_remove(item);
+        dev->cbs.unref_msg_to_client(msg_item->msg, dev->opaque);
+        free(msg_item);
+    }
+    dev_client->num_send_tokens += dev_client->send_queue_size;
+    dev_client->send_queue_size = 0;
+}
+
+static void spice_char_device_client_free(SpiceCharDeviceState *dev,
+                                          SpiceCharDeviceClientState *dev_client)
+{
+    RingItem *item, *next;
+
+    if (dev_client->wait_for_tokens_timer) {
+        core->timer_remove(dev_client->wait_for_tokens_timer);
+    }
+
+    spice_char_device_client_send_queue_free(dev, dev_client);
+
+    /* remove write buffers that are associated with the client */
+    spice_debug("write_queue_is_empty %d", ring_is_empty(&dev->write_queue) && !dev->cur_write_buf);
+    RING_FOREACH_SAFE(item, next, &dev->write_queue) {
+        SpiceCharDeviceWriteBuffer *write_buf;
+
+        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+            write_buf->client == dev_client->client) {
+            ring_remove(item);
+            spice_char_device_write_buffer_pool_add(dev, write_buf);
+        }
+    }
+
+    if (dev->cur_write_buf && dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+        dev->cur_write_buf->client == dev_client->client) {
+        dev->cur_write_buf->origin = WRITE_BUFFER_ORIGIN_NONE;
+        dev->cur_write_buf->client = NULL;
+    }
+
+    dev->num_clients--;
+    ring_remove(&dev_client->link);
+    free(dev_client);
+}
+
+static void spice_char_device_handle_client_overflow(SpiceCharDeviceClientState *dev_client)
+{
+    SpiceCharDeviceState *dev = dev_client->dev;
+    spice_printerr("dev %p client %p ", dev, dev_client);
+    dev->cbs.remove_client(dev_client->client, dev->opaque);
+}
+
+static SpiceCharDeviceClientState *spice_char_device_client_find(SpiceCharDeviceState *dev,
+                                                                 RedClient *client)
+{
+    RingItem *item;
+
+    RING_FOREACH(item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        if (dev_client->client == client) {
+            return dev_client;
+        }
+    }
+    return NULL;
+}
+
+/***************************
+ * Reading from the device *
+ **************************/
+
+static void device_client_wait_for_tokens_timeout(void *opaque)
+{
+    SpiceCharDeviceClientState *dev_client = opaque;
+
+    spice_char_device_handle_client_overflow(dev_client);
+}
+
+static int spice_char_device_can_send_to_client(SpiceCharDeviceClientState *dev_client)
+{
+    return !dev_client->do_flow_control || dev_client->num_send_tokens;
+}
+
+static uint64_t spice_char_device_max_send_tokens(SpiceCharDeviceState *dev)
+{
+    RingItem *item;
+    uint64_t max = 0;
+
+    RING_FOREACH(item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+
+        if (!dev_client->do_flow_control) {
+            max = ~0;
+            break;
+        }
+
+        if (dev_client->num_send_tokens > max) {
+            max = dev_client->num_send_tokens;
+        }
+    }
+    return max;
+}
+
+static void spice_char_device_add_msg_to_client_queue(SpiceCharDeviceClientState *dev_client,
+                                                      SpiceCharDeviceMsgToClient *msg)
+{
+    SpiceCharDeviceState *dev = dev_client->dev;
+    SpiceCharDeviceMsgToClientItem *msg_item;
+
+    if (dev_client->send_queue_size >= dev_client->max_send_queue_size) {
+        spice_char_device_handle_client_overflow(dev_client);
+        return;
+    }
+
+    msg_item = spice_new0(SpiceCharDeviceMsgToClientItem, 1);
+    msg_item->msg = dev->cbs.ref_msg_to_client(msg, dev->opaque);
+    ring_add(&dev_client->send_queue, &msg_item->link);
+    dev_client->send_queue_size++;
+    if (!dev_client->wait_for_tokens_started) {
+        core->timer_start(dev_client->wait_for_tokens_timer,
+                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
+        dev_client->wait_for_tokens_started = TRUE;
+    }
+}
+
+static void spice_char_device_send_msg_to_clients(SpiceCharDeviceState *dev,
+                                                  SpiceCharDeviceMsgToClient *msg)
+{
+    RingItem *item, *next;
+
+    RING_FOREACH_SAFE(item, next, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        if (spice_char_device_can_send_to_client(dev_client)) {
+            dev_client->num_send_tokens--;
+            spice_assert(ring_is_empty(&dev_client->send_queue));
+            dev->cbs.send_msg_to_client(msg, dev_client->client, dev->opaque);
+
+            /* don't refer to dev_client anymore, it may have been released */
+        } else {
+            spice_char_device_add_msg_to_client_queue(dev_client, msg);
+        }
+    }
+}
+
+static int spice_char_device_read_from_device(SpiceCharDeviceState *dev)
+{
+    uint64_t max_send_tokens;
+    int did_read = FALSE;
+
+    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
+        return FALSE;
+    }
+
+    /* There are 2 scenarios where we can get called recursively:
+     * 1) spice-vmc vmc_read triggering flush of throttled data, recalling wakeup
+     * (virtio)
+     * 2) in case of sending messages to the client, and unreferencing the
+     * msg, we trigger another read.
+     */
+    if (dev->during_read_from_device++ > 0) {
+        return FALSE;
+    }
+
+    max_send_tokens = spice_char_device_max_send_tokens(dev);
+    spice_char_device_state_ref(dev);
+    /*
+     * Reading from the device only in case at least one of the clients have a free token.
+     * All messages will be discarded if no client is attached to the device
+     */
+    while ((max_send_tokens || ring_is_empty(&dev->clients)) && dev->running) {
+        SpiceCharDeviceMsgToClient *msg;
+
+        msg = dev->cbs.read_one_msg_from_device(dev->sin, dev->opaque);
+        if (!msg) {
+            if (dev->during_read_from_device > 1) {
+                dev->during_read_from_device = 1;
+                continue; /* a wakeup might have been called during the read -
+                             make sure it doesn't get lost */
+            }
+            break;
+        }
+        did_read = TRUE;
+        spice_char_device_send_msg_to_clients(dev, msg);
+        dev->cbs.unref_msg_to_client(msg, dev->opaque);
+        max_send_tokens--;
+    }
+    dev->during_read_from_device = 0;
+    if (dev->running) {
+        dev->active = dev->active || did_read;
+    }
+    spice_char_device_state_unref(dev);
+    return did_read;
+}
+
+static void spice_char_device_client_send_queue_push(SpiceCharDeviceClientState *dev_client)
+{
+    RingItem *item;
+    while ((item = ring_get_tail(&dev_client->send_queue)) &&
+           spice_char_device_can_send_to_client(dev_client)) {
+        SpiceCharDeviceMsgToClientItem *msg_item;
+
+        msg_item = SPICE_CONTAINEROF(item, SpiceCharDeviceMsgToClientItem, link);
+        ring_remove(item);
+
+        dev_client->num_send_tokens--;
+        dev_client->dev->cbs.send_msg_to_client(msg_item->msg,
+                                           dev_client->client,
+                                           dev_client->dev->opaque);
+        dev_client->dev->cbs.unref_msg_to_client(msg_item->msg, dev_client->dev->opaque);
+        dev_client->send_queue_size--;
+        free(msg_item);
+    }
+}
+
+static void spice_char_device_send_to_client_tokens_absorb(SpiceCharDeviceClientState *dev_client,
+                                                           uint32_t tokens)
+{
+    dev_client->num_send_tokens += tokens;
+
+    if (dev_client->send_queue_size) {
+        spice_assert(dev_client->num_send_tokens == tokens);
+        spice_char_device_client_send_queue_push(dev_client);
+    }
+
+    if (spice_char_device_can_send_to_client(dev_client)) {
+        core->timer_cancel(dev_client->wait_for_tokens_timer);
+        dev_client->wait_for_tokens_started = FALSE;
+        spice_char_device_read_from_device(dev_client->dev);
+    } else if (dev_client->send_queue_size) {
+        core->timer_start(dev_client->wait_for_tokens_timer,
+                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
+        dev_client->wait_for_tokens_started = TRUE;
+    }
+}
+
+void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found dev %p client %p", dev, client);
+        return;
+    }
+    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
+}
+
+void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found dev %p client %p", dev, client);
+        return;
+    }
+
+    dev_client->num_send_tokens = 0;
+    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
+}
+
+/**************************
+ * Writing to the device  *
+***************************/
+
+static void spice_char_device_client_tokens_add(SpiceCharDeviceState *dev,
+                                                SpiceCharDeviceClientState *dev_client,
+                                                uint32_t num_tokens)
+{
+    if (!dev_client->do_flow_control) {
+        return;
+    }
+    if (num_tokens > 1) {
+        spice_debug("#tokens > 1 (=%u)", num_tokens);
+    }
+    dev_client->num_client_tokens_free += num_tokens;
+    if (dev_client->num_client_tokens_free >= dev->client_tokens_interval) {
+        uint32_t tokens = dev_client->num_client_tokens_free;
+
+        dev_client->num_client_tokens += dev_client->num_client_tokens_free;
+        dev_client->num_client_tokens_free = 0;
+        dev->cbs.send_tokens_to_client(dev_client->client,
+                                       tokens,
+                                       dev->opaque);
+    }
+}
+
+static int spice_char_device_write_to_device(SpiceCharDeviceState *dev)
+{
+    SpiceCharDeviceInterface *sif;
+    int total = 0;
+    int n;
+
+    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
+        return 0;
+    }
+
+    /* protect against recursion with spice_char_device_wakeup */
+    if (dev->during_write_to_device++ > 0) {
+        return 0;
+    }
+
+    spice_char_device_state_ref(dev);
+
+    if (dev->write_to_dev_timer) {
+        core->timer_cancel(dev->write_to_dev_timer);
+    }
+
+    sif = SPICE_CONTAINEROF(dev->sin->base.sif, SpiceCharDeviceInterface, base);
+    while (dev->running) {
+        uint32_t write_len;
+
+        if (!dev->cur_write_buf) {
+            RingItem *item = ring_get_tail(&dev->write_queue);
+            if (!item) {
+                break;
+            }
+            dev->cur_write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+            dev->cur_write_buf_pos = dev->cur_write_buf->buf;
+            ring_remove(item);
+        }
+
+        write_len = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
+                    dev->cur_write_buf_pos;
+        n = sif->write(dev->sin, dev->cur_write_buf_pos, write_len);
+        if (n <= 0) {
+            if (dev->during_write_to_device > 1) {
+                dev->during_write_to_device = 1;
+                continue; /* a wakeup might have been called during the write -
+                             make sure it doesn't get lost */
+            }
+            break;
+        }
+        total += n;
+        write_len -= n;
+        if (!write_len) {
+            SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
+            dev->cur_write_buf = NULL;
+            spice_char_device_write_buffer_release(dev, release_buf);
+            continue;
+        }
+        dev->cur_write_buf_pos += n;
+    }
+    /* retry writing as long as the write queue is not empty */
+    if (dev->running) {
+        if (dev->cur_write_buf) {
+            if (dev->write_to_dev_timer) {
+                core->timer_start(dev->write_to_dev_timer,
+                                  CHAR_DEVICE_WRITE_TO_TIMEOUT);
+            }
+        } else {
+            spice_assert(ring_is_empty(&dev->write_queue));
+        }
+        dev->active = dev->active || total;
+    }
+    dev->during_write_to_device = 0;
+    spice_char_device_state_unref(dev);
+    return total;
+}
+
+static void spice_char_dev_write_retry(void *opaque)
+{
+    SpiceCharDeviceState *dev = opaque;
+
+    if (dev->write_to_dev_timer) {
+        core->timer_cancel(dev->write_to_dev_timer);
+    }
+    spice_char_device_write_to_device(dev);
+}
+
+static SpiceCharDeviceWriteBuffer *__spice_char_device_write_buffer_get(
+    SpiceCharDeviceState *dev, RedClient *client,
+    int size, int origin, int migrated_data_tokens)
+{
+    RingItem *item;
+    SpiceCharDeviceWriteBuffer *ret;
+
+    if (origin == WRITE_BUFFER_ORIGIN_SERVER && !dev->num_self_tokens) {
+        return NULL;
+    }
+
+    if ((item = ring_get_tail(&dev->write_bufs_pool))) {
+        ret = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        ring_remove(item);
+        dev->cur_pool_size -= ret->buf_size;
+    } else {
+        ret = spice_new0(SpiceCharDeviceWriteBuffer, 1);
+    }
+
+    spice_assert(!ret->buf_used);
+
+    if (ret->buf_size < size) {
+        ret->buf = spice_realloc(ret->buf, size);
+        ret->buf_size = size;
+    }
+    ret->origin = origin;
+
+    if (origin == WRITE_BUFFER_ORIGIN_CLIENT) {
+       spice_assert(client);
+       SpiceCharDeviceClientState *dev_client = spice_char_device_client_find(dev, client);
+       if (dev_client) {
+            if (!migrated_data_tokens &&
+                dev_client->do_flow_control && !dev_client->num_client_tokens) {
+                spice_printerr("token violation: dev %p client %p", dev, client);
+                spice_char_device_handle_client_overflow(dev_client);
+                goto error;
+            }
+            ret->client = client;
+            if (!migrated_data_tokens && dev_client->do_flow_control) {
+                dev_client->num_client_tokens--;
+            }
+        } else {
+            /* it is possible that the client was removed due to send tokens underflow, but
+             * the caller still receive messages from the client */
+            spice_printerr("client not found: dev %p client %p", dev, client);
+            goto error;
+        }
+    } else if (origin == WRITE_BUFFER_ORIGIN_SERVER) {
+        dev->num_self_tokens--;
+    }
+
+    ret->token_price = migrated_data_tokens ? migrated_data_tokens : 1;
+    ret->refs = 1;
+    return ret;
+error:
+    dev->cur_pool_size += ret->buf_size;
+    ring_add(&dev->write_bufs_pool, &ret->link);
+    return NULL;
+}
+
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
+                                                               RedClient *client,
+                                                               int size)
+{
+   return  __spice_char_device_write_buffer_get(dev, client, size,
+             client ? WRITE_BUFFER_ORIGIN_CLIENT : WRITE_BUFFER_ORIGIN_SERVER,
+             0);
+}
+
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
+    SpiceCharDeviceState *dev, int size)
+{
+   return  __spice_char_device_write_buffer_get(dev, NULL, size,
+             WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN, 0);
+}
+
+static SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_ref(SpiceCharDeviceWriteBuffer *write_buf)
+{
+    spice_assert(write_buf);
+
+    write_buf->refs++;
+    return write_buf;
+}
+
+static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf)
+{
+    spice_assert(write_buf);
+
+    write_buf->refs--;
+    if (write_buf->refs == 0)
+        spice_char_device_write_buffer_free(write_buf);
+}
+
+void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
+                                        SpiceCharDeviceWriteBuffer *write_buf)
+{
+    spice_assert(dev);
+    /* caller shouldn't add buffers for client that was removed */
+    if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
+        !spice_char_device_client_find(dev, write_buf->client)) {
+        spice_printerr("client not found: dev %p client %p", dev, write_buf->client);
+        spice_char_device_write_buffer_pool_add(dev, write_buf);
+        return;
+    }
+
+    ring_add(&dev->write_queue, &write_buf->link);
+    spice_char_device_write_to_device(dev);
+}
+
+void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
+                                            SpiceCharDeviceWriteBuffer *write_buf)
+{
+    int buf_origin = write_buf->origin;
+    uint32_t buf_token_price = write_buf->token_price;
+    RedClient *client = write_buf->client;
+
+    spice_assert(!ring_item_is_linked(&write_buf->link));
+    if (!dev) {
+        spice_printerr("no device. write buffer is freed");
+        spice_char_device_write_buffer_free(write_buf);
+        return;
+    }
+
+    spice_assert(dev->cur_write_buf != write_buf);
+
+    spice_char_device_write_buffer_pool_add(dev, write_buf);
+    if (buf_origin == WRITE_BUFFER_ORIGIN_CLIENT) {
+        SpiceCharDeviceClientState *dev_client;
+
+        spice_assert(client);
+        dev_client = spice_char_device_client_find(dev, client);
+        /* when a client is removed, we remove all the buffers that are associated with it */
+        spice_assert(dev_client);
+        spice_char_device_client_tokens_add(dev, dev_client, buf_token_price);
+    } else if (buf_origin == WRITE_BUFFER_ORIGIN_SERVER) {
+        dev->num_self_tokens++;
+        if (dev->cbs.on_free_self_token) {
+            dev->cbs.on_free_self_token(dev->opaque);
+        }
+    }
+}
+
+/********************************
+ * char_device_state management *
+ ********************************/
+
+SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
+                                                     uint32_t client_tokens_interval,
+                                                     uint32_t self_tokens,
+                                                     SpiceCharDeviceCallbacks *cbs,
+                                                     void *opaque)
+{
+    SpiceCharDeviceState *char_dev;
+    SpiceCharDeviceInterface *sif;
+
+    spice_assert(sin);
+    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
+                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
+                 cbs->send_tokens_to_client && cbs->remove_client);
+
+    char_dev = spice_new0(SpiceCharDeviceState, 1);
+    char_dev->sin = sin;
+    char_dev->cbs = *cbs;
+    char_dev->opaque = opaque;
+    char_dev->client_tokens_interval = client_tokens_interval;
+    char_dev->num_self_tokens = self_tokens;
+
+    ring_init(&char_dev->write_queue);
+    ring_init(&char_dev->write_bufs_pool);
+    ring_init(&char_dev->clients);
+
+    sif = SPICE_CONTAINEROF(char_dev->sin->base.sif, SpiceCharDeviceInterface, base);
+    if (sif->base.minor_version <= 2 ||
+        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
+        char_dev->write_to_dev_timer = core->timer_add(spice_char_dev_write_retry, char_dev);
+        if (!char_dev->write_to_dev_timer) {
+            spice_error("failed creating char dev write timer");
+        }
+    }
+
+    char_dev->refs = 1;
+    sin->st = char_dev;
+    spice_debug("sin %p dev_state %p", sin, char_dev);
+    return char_dev;
+}
+
+void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *state,
+                                                SpiceCharDeviceInstance *sin)
+{
+    spice_debug("sin %p dev_state %p", sin, state);
+    state->sin = sin;
+    sin->st = state;
+}
+
+void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev)
+{
+    return dev->opaque;
+}
+
+static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev)
+{
+    char_dev->refs++;
+}
+
+static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev)
+{
+    /* The refs field protects the char_dev from being deallocated in
+     * case spice_char_device_state_destroy has been called
+     * during a callabck, and we might still access the char_dev afterwards.
+     * spice_char_device_state_unref is always coupled with a preceding
+     * spice_char_device_state_ref. Here, refs can turn 0
+     * only when spice_char_device_state_destroy is called in between
+     * the calls to spice_char_device_state_ref and spice_char_device_state_unref.*/
+    if (!--char_dev->refs) {
+        free(char_dev);
+    }
+}
+
+void spice_char_device_state_destroy(SpiceCharDeviceState *char_dev)
+{
+    reds_on_char_device_state_destroy(char_dev);
+    if (char_dev->write_to_dev_timer) {
+        core->timer_remove(char_dev->write_to_dev_timer);
+        char_dev->write_to_dev_timer = NULL;
+    }
+    write_buffers_queue_free(&char_dev->write_queue);
+    write_buffers_queue_free(&char_dev->write_bufs_pool);
+    char_dev->cur_pool_size = 0;
+    spice_char_device_write_buffer_free(char_dev->cur_write_buf);
+    char_dev->cur_write_buf = NULL;
+
+    while (!ring_is_empty(&char_dev->clients)) {
+        RingItem *item = ring_get_tail(&char_dev->clients);
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
+        spice_char_device_client_free(char_dev, dev_client);
+    }
+    char_dev->running = FALSE;
+
+    spice_char_device_state_unref(char_dev);
+}
+
+int spice_char_device_client_add(SpiceCharDeviceState *dev,
+                                 RedClient *client,
+                                 int do_flow_control,
+                                 uint32_t max_send_queue_size,
+                                 uint32_t num_client_tokens,
+                                 uint32_t num_send_tokens,
+                                 int wait_for_migrate_data)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    spice_assert(dev);
+    spice_assert(client);
+
+    if (wait_for_migrate_data && (dev->num_clients > 0 || dev->active)) {
+        spice_warning("can't restore device %p from migration data. The device "
+                      "has already been active", dev);
+        return FALSE;
+    }
+
+    dev->wait_for_migrate_data = wait_for_migrate_data;
+
+    spice_debug("dev_state %p client %p", dev, client);
+    dev_client = spice_new0(SpiceCharDeviceClientState, 1);
+    dev_client->dev = dev;
+    dev_client->client = client;
+    ring_init(&dev_client->send_queue);
+    dev_client->send_queue_size = 0;
+    dev_client->max_send_queue_size = max_send_queue_size;
+    dev_client->do_flow_control = do_flow_control;
+    if (do_flow_control) {
+        dev_client->wait_for_tokens_timer = core->timer_add(device_client_wait_for_tokens_timeout,
+                                                            dev_client);
+        if (!dev_client->wait_for_tokens_timer) {
+            spice_error("failed to create wait for tokens timer");
+        }
+        dev_client->num_client_tokens = num_client_tokens;
+        dev_client->num_send_tokens = num_send_tokens;
+    } else {
+        dev_client->num_client_tokens = ~0;
+        dev_client->num_send_tokens = ~0;
+    }
+    ring_add(&dev->clients, &dev_client->link);
+    dev->num_clients++;
+    /* Now that we have a client, forward any pending device data */
+    spice_char_device_wakeup(dev);
+    return TRUE;
+}
+
+void spice_char_device_client_remove(SpiceCharDeviceState *dev,
+                                     RedClient *client)
+{
+    SpiceCharDeviceClientState *dev_client;
+
+    spice_debug("dev_state %p client %p", dev, client);
+    dev_client = spice_char_device_client_find(dev, client);
+
+    if (!dev_client) {
+        spice_error("client wasn't found");
+        return;
+    }
+    spice_char_device_client_free(dev, dev_client);
+    if (dev->wait_for_migrate_data) {
+        spice_assert(dev->num_clients == 0);
+        dev->wait_for_migrate_data  = FALSE;
+        spice_char_device_read_from_device(dev);
+    }
+
+    if (dev->num_clients == 0) {
+        spice_debug("client removed, memory pool will be freed (%lu bytes)", dev->cur_pool_size);
+        write_buffers_queue_free(&dev->write_bufs_pool);
+        dev->cur_pool_size = 0;
+    }
+}
+
+int spice_char_device_client_exists(SpiceCharDeviceState *dev,
+                                    RedClient *client)
+{
+    return (spice_char_device_client_find(dev, client) != NULL);
+}
+
+void spice_char_device_start(SpiceCharDeviceState *dev)
+{
+    spice_debug("dev_state %p", dev);
+    dev->running = TRUE;
+    spice_char_device_state_ref(dev);
+    while (spice_char_device_write_to_device(dev) ||
+           spice_char_device_read_from_device(dev));
+    spice_char_device_state_unref(dev);
+}
+
+void spice_char_device_stop(SpiceCharDeviceState *dev)
+{
+    spice_debug("dev_state %p", dev);
+    dev->running = FALSE;
+    dev->active = FALSE;
+    if (dev->write_to_dev_timer) {
+        core->timer_cancel(dev->write_to_dev_timer);
+    }
+}
+
+void spice_char_device_reset(SpiceCharDeviceState *dev)
+{
+    RingItem *client_item;
+
+    spice_char_device_stop(dev);
+    dev->wait_for_migrate_data = FALSE;
+    spice_debug("dev_state %p", dev);
+    while (!ring_is_empty(&dev->write_queue)) {
+        RingItem *item = ring_get_tail(&dev->write_queue);
+        SpiceCharDeviceWriteBuffer *buf;
+
+        ring_remove(item);
+        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        /* tracking the tokens */
+        spice_char_device_write_buffer_release(dev, buf);
+    }
+    if (dev->cur_write_buf) {
+        SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
+
+        dev->cur_write_buf = NULL;
+        spice_char_device_write_buffer_release(dev, release_buf);
+    }
+
+    RING_FOREACH(client_item, &dev->clients) {
+        SpiceCharDeviceClientState *dev_client;
+
+        dev_client = SPICE_CONTAINEROF(client_item, SpiceCharDeviceClientState, link);
+        spice_char_device_client_send_queue_free(dev, dev_client);
+    }
+    dev->sin = NULL;
+}
+
+void spice_char_device_wakeup(SpiceCharDeviceState *dev)
+{
+    spice_char_device_write_to_device(dev);
+    spice_char_device_read_from_device(dev);
+}
+
+/*************
+ * Migration *
+ * **********/
+
+void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m)
+{
+    SpiceMigrateDataCharDevice *mig_data;
+
+    spice_debug(NULL);
+    mig_data = (SpiceMigrateDataCharDevice *)spice_marshaller_reserve_space(m,
+                                                                            sizeof(*mig_data));
+    memset(mig_data, 0, sizeof(*mig_data));
+    mig_data->version = SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION;
+    mig_data->connected = FALSE;
+}
+
+static void migrate_data_marshaller_write_buffer_free(uint8_t *data, void *opaque)
+{
+    SpiceCharDeviceWriteBuffer *write_buf = (SpiceCharDeviceWriteBuffer *)opaque;
+
+    spice_char_device_write_buffer_unref(write_buf);
+}
+
+void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
+                                                   SpiceMarshaller *m)
+{
+    SpiceCharDeviceClientState *client_state;
+    RingItem *item;
+    uint32_t *write_to_dev_size_ptr;
+    uint32_t *write_to_dev_tokens_ptr;
+    SpiceMarshaller *m2;
+
+    /* multi-clients are not supported */
+    spice_assert(dev->num_clients == 1);
+    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
+                                     SpiceCharDeviceClientState,
+                                     link);
+    /* FIXME: if there were more than one client before the marshalling,
+     * it is possible that the send_queue_size > 0, and the send data
+     * should be migrated as well */
+    spice_assert(client_state->send_queue_size == 0);
+    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
+    spice_marshaller_add_uint8(m, 1); /* connected */
+    spice_marshaller_add_uint32(m, client_state->num_client_tokens);
+    spice_marshaller_add_uint32(m, client_state->num_send_tokens);
+    write_to_dev_size_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
+    write_to_dev_tokens_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
+    *write_to_dev_size_ptr = 0;
+    *write_to_dev_tokens_ptr = 0;
+
+    m2 = spice_marshaller_get_ptr_submarshaller(m, 0);
+    if (dev->cur_write_buf) {
+        uint32_t buf_remaining = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
+                                 dev->cur_write_buf_pos;
+        spice_marshaller_add_ref_full(m2, dev->cur_write_buf_pos, buf_remaining,
+                                      migrate_data_marshaller_write_buffer_free,
+                                      spice_char_device_write_buffer_ref(dev->cur_write_buf)
+                                      );
+        *write_to_dev_size_ptr += buf_remaining;
+        if (dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
+            spice_assert(dev->cur_write_buf->client == client_state->client);
+            (*write_to_dev_tokens_ptr) += dev->cur_write_buf->token_price;
+        }
+    }
+
+    RING_FOREACH_REVERSED(item, &dev->write_queue) {
+        SpiceCharDeviceWriteBuffer *write_buf;
+
+        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
+        spice_marshaller_add_ref_full(m2, write_buf->buf, write_buf->buf_used,
+                                      migrate_data_marshaller_write_buffer_free,
+                                      spice_char_device_write_buffer_ref(write_buf)
+                                      );
+        *write_to_dev_size_ptr += write_buf->buf_used;
+        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
+            spice_assert(write_buf->client == client_state->client);
+            (*write_to_dev_tokens_ptr) += write_buf->token_price;
+        }
+    }
+    spice_debug("migration data dev %p: write_queue size %u tokens %u",
+                dev, *write_to_dev_size_ptr, *write_to_dev_tokens_ptr);
+}
+
+int spice_char_device_state_restore(SpiceCharDeviceState *dev,
+                                    SpiceMigrateDataCharDevice *mig_data)
+{
+    SpiceCharDeviceClientState *client_state;
+    uint32_t client_tokens_window;
+
+    spice_assert(dev->num_clients == 1 && dev->wait_for_migrate_data);
+
+    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
+                                     SpiceCharDeviceClientState,
+                                     link);
+    if (mig_data->version > SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION) {
+        spice_error("dev %p error: migration data version %u is bigger than self %u",
+                    dev, mig_data->version, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
+        return FALSE;
+    }
+    spice_assert(!dev->cur_write_buf && ring_is_empty(&dev->write_queue));
+    spice_assert(mig_data->connected);
+
+    client_tokens_window = client_state->num_client_tokens; /* initial state of tokens */
+    client_state->num_client_tokens = mig_data->num_client_tokens;
+    /* assumption: client_tokens_window stays the same across severs */
+    client_state->num_client_tokens_free = client_tokens_window -
+                                           mig_data->num_client_tokens -
+                                           mig_data->write_num_client_tokens;
+    client_state->num_send_tokens = mig_data->num_send_tokens;
+
+    if (mig_data->write_size > 0) {
+        if (mig_data->write_num_client_tokens) {
+            dev->cur_write_buf =
+                __spice_char_device_write_buffer_get(dev, client_state->client,
+                    mig_data->write_size, WRITE_BUFFER_ORIGIN_CLIENT,
+                    mig_data->write_num_client_tokens);
+        } else {
+            dev->cur_write_buf =
+                __spice_char_device_write_buffer_get(dev, NULL,
+                    mig_data->write_size, WRITE_BUFFER_ORIGIN_SERVER, 0);
+        }
+        /* the first write buffer contains all the data that was saved for migration */
+        memcpy(dev->cur_write_buf->buf,
+               ((uint8_t *)mig_data) + mig_data->write_data_ptr - sizeof(SpiceMigrateDataHeader),
+               mig_data->write_size);
+        dev->cur_write_buf->buf_used = mig_data->write_size;
+        dev->cur_write_buf_pos = dev->cur_write_buf->buf;
+    }
+    dev->wait_for_migrate_data = FALSE;
+    spice_char_device_write_to_device(dev);
+    spice_char_device_read_from_device(dev);
+    return TRUE;
+}
diff --git a/server/char-device.h b/server/char-device.h
new file mode 100644
index 0000000..30b3d4a
--- /dev/null
+++ b/server/char-device.h
@@ -0,0 +1,216 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifndef __CHAR_DEVICE_H__
+#define __CHAR_DEVICE_H__
+
+#include "spice.h"
+#include "red_channel.h"
+#include "migration-protocol.h"
+
+/*
+ * Shared code for char devices, mainly for flow control.
+ *
+ * How to use the api:
+ * ==================
+ * device attached: call spice_char_device_state_create
+ * device detached: call spice_char_device_state_destroy/reset
+ *
+ * client connected and associated with a device: spice_char_device_client_add
+ * client disconnected: spice_char_device_client_remove
+ *
+ * Writing to the device
+ * ---------------------
+ * Write the data into SpiceCharDeviceWriteBuffer:
+ * call spice_char_device_write_buffer_get in order to get an appropriate buffer.
+ * call spice_char_device_write_buffer_add in order to push the buffer to the write queue.
+ * If you choose not to push the buffer to the device, call
+ * spice_char_device_write_buffer_release
+ *
+ * reading from the device
+ * -----------------------
+ *  The callback read_one_msg_from_device (see below) should be implemented
+ *  (using sif->read).
+ *  When the device is ready, this callback is called, and is expected to
+ *  return one message which is addressed to the client, or NULL if the read
+ *  hasn't completed.
+ *
+ * calls triggered from the device (qemu):
+ * --------------------------------------
+ * spice_char_device_start
+ * spice_char_device_stop
+ * spice_char_device_wakeup (for reading from the device)
+ */
+
+/*
+ * Note about multiple-clients:
+ * Multiclients are currently not supported in any of the character devices:
+ * spicevmc does not allow more than one client (and at least for usb, it should stay this way).
+ * smartcard code is not compatible with more than one reader.
+ * The server and guest agent code doesn't distinguish messages from different clients.
+ * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't
+ * take into account the different clients.
+ *
+ * Nonetheless, the following code introduces some support for multiple-clients:
+ * We track the number of tokens for all the clients, and we read from the device
+ * if one of the clients have enough tokens. For the clients that don't have tokens,
+ * we queue the messages, till they receive tokens, or till a timeout.
+ *
+ * TODO:
+ * At least for the agent, not all the messages from the device will be directed to all
+ * the clients (e.g., copy from guest to a specific client). Thus, support for
+ * client-specific-messages should be added.
+ * In addition, we should have support for clients that are being connected
+ * in the middle of a message transfer from the agent to the clients.
+ *
+ * */
+
+/* buffer that is used for writing to the device */
+typedef struct SpiceCharDeviceWriteBuffer {
+    RingItem link;
+    int origin;
+    RedClient *client; /* The client that sent the message to the device.
+                          NULL if the server created the message */
+
+    uint8_t *buf;
+    uint32_t buf_size;
+    uint32_t buf_used;
+    uint32_t token_price;
+    uint32_t refs;
+} SpiceCharDeviceWriteBuffer;
+
+typedef void SpiceCharDeviceMsgToClient;
+
+typedef struct SpiceCharDeviceCallbacks {
+    /*
+     * Messages that are addressed to the client can be queued in case we have
+     * multiple clients and some of them don't have enough tokens.
+     */
+
+    /* reads from the device till reaching a msg that should be sent to the client,
+     * or till the reading fails */
+    SpiceCharDeviceMsgToClient* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin,
+                                                            void *opaque);
+    SpiceCharDeviceMsgToClient* (*ref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                                                     void *opaque);
+    void (*unref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                                void *opaque);
+    void (*send_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
+                               RedClient *client,
+                               void *opaque); /* after this call, the message is unreferenced */
+
+    /* The cb is called when a predefined number of write buffers were consumed by the
+     * device */
+    void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque);
+
+    /* The cb is called when a server (self) message that was addressed to the device,
+     * has been completely written to it */
+    void (*on_free_self_token)(void *opaque);
+
+    /* This cb is called if it is recommanded that a client will be removed
+     * due to slow flow or due to some other error.
+     * The called instance should disconnect the client, or at least the corresponding channel */
+    void (*remove_client)(RedClient *client, void *opaque);
+} SpiceCharDeviceCallbacks;
+
+SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
+                                                     uint32_t client_tokens_interval,
+                                                     uint32_t self_tokens,
+                                                     SpiceCharDeviceCallbacks *cbs,
+                                                     void *opaque);
+
+void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *dev,
+                                                SpiceCharDeviceInstance *sin);
+void spice_char_device_state_destroy(SpiceCharDeviceState *dev);
+
+void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev);
+
+/* only one client is supported */
+void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
+                                                  SpiceMarshaller *m);
+void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m);
+
+int spice_char_device_state_restore(SpiceCharDeviceState *dev,
+                                    SpiceMigrateDataCharDevice *mig_data);
+
+/*
+ * Resets write/read queues, and moves that state to being stopped.
+ * This routine is a workaround for a bad tokens management in the vdagent
+ * protocol:
+ *  The client tokens' are set only once, when the main channel is initialized.
+ *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
+ *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
+ *  in order to be backwartd compatible with the client, we need to track the tokens
+ *  event when the agent is detached. We don't destroy the char_device state, and
+ *  instead we just reset it.
+ *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
+ *  overrides the amount of tokens, instead of adding the given amount.
+ *
+ *  todo: change AGENT_CONNECT msg to contain tokens count.
+ */
+void spice_char_device_reset(SpiceCharDeviceState *dev);
+
+/* max_send_queue_size = how many messages we can read from the device and enqueue for this client,
+ * when we have tokens for other clients and no tokens for this one */
+int spice_char_device_client_add(SpiceCharDeviceState *dev,
+                                 RedClient *client,
+                                 int do_flow_control,
+                                 uint32_t max_send_queue_size,
+                                 uint32_t num_client_tokens,
+                                 uint32_t num_send_tokens,
+                                 int wait_for_migrate_data);
+
+void spice_char_device_client_remove(SpiceCharDeviceState *dev,
+                                     RedClient *client);
+int spice_char_device_client_exists(SpiceCharDeviceState *dev,
+                                    RedClient *client);
+
+void spice_char_device_start(SpiceCharDeviceState *dev);
+void spice_char_device_stop(SpiceCharDeviceState *dev);
+
+/** Read from device **/
+
+void spice_char_device_wakeup(SpiceCharDeviceState *dev);
+
+void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens);
+
+
+void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
+                                                 RedClient *client,
+                                                 uint32_t tokens);
+/** Write to device **/
+
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
+                                                               RedClient *client, int size);
+SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
+    SpiceCharDeviceState *dev, int size);
+
+/* Either add the buffer to the write queue or release it */
+void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
+                                        SpiceCharDeviceWriteBuffer *write_buf);
+void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
+                                            SpiceCharDeviceWriteBuffer *write_buf);
+
+/* api for specific char devices */
+
+SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
+                                              uint8_t channel_type);
+void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
+
+#endif // __CHAR_DEVICE_H__
diff --git a/server/char_device.c b/server/char_device.c
deleted file mode 100644
index ae7cb98..0000000
--- a/server/char_device.c
+++ /dev/null
@@ -1,1035 +0,0 @@
-/* spice-server char device flow control code
-
-   Copyright (C) 2012 Red Hat, Inc.
-
-   Red Hat Authors:
-   Yonit Halperin <yhalperi at redhat.com>
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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 <config.h>
-#include "char_device.h"
-#include "red_channel.h"
-#include "reds.h"
-
-#define CHAR_DEVICE_WRITE_TO_TIMEOUT 100
-#define SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT 30000
-#define MAX_POOL_SIZE (10 * 64 * 1024)
-
-typedef struct SpiceCharDeviceClientState SpiceCharDeviceClientState;
-struct SpiceCharDeviceClientState {
-    RingItem link;
-    SpiceCharDeviceState *dev;
-    RedClient *client;
-    int do_flow_control;
-    uint64_t num_client_tokens;
-    uint64_t num_client_tokens_free; /* client messages that were consumed by the device */
-    uint64_t num_send_tokens; /* send to client */
-    SpiceTimer *wait_for_tokens_timer;
-    int wait_for_tokens_started;
-    Ring send_queue;
-    uint32_t send_queue_size;
-    uint32_t max_send_queue_size;
-};
-
-struct SpiceCharDeviceState {
-    int running;
-    int active; /* has read/write been performed since the device was started */
-    int wait_for_migrate_data;
-    uint32_t refs;
-
-    Ring write_queue;
-    Ring write_bufs_pool;
-    uint64_t cur_pool_size;
-    SpiceCharDeviceWriteBuffer *cur_write_buf;
-    uint8_t *cur_write_buf_pos;
-    SpiceTimer *write_to_dev_timer;
-    uint64_t num_self_tokens;
-
-    Ring clients; /* list of SpiceCharDeviceClientState */
-    uint32_t num_clients;
-
-    uint64_t client_tokens_interval; /* frequency of returning tokens to the client */
-    SpiceCharDeviceInstance *sin;
-
-    int during_read_from_device;
-    int during_write_to_device;
-
-    SpiceCharDeviceCallbacks cbs;
-    void *opaque;
-};
-
-enum {
-    WRITE_BUFFER_ORIGIN_NONE,
-    WRITE_BUFFER_ORIGIN_CLIENT,
-    WRITE_BUFFER_ORIGIN_SERVER,
-    WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN,
-};
-
-/* Holding references for avoiding access violation if the char device was
- * destroyed during a callback */
-static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev);
-static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev);
-static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf);
-
-static void spice_char_dev_write_retry(void *opaque);
-
-typedef struct SpiceCharDeviceMsgToClientItem {
-    RingItem link;
-    SpiceCharDeviceMsgToClient *msg;
-} SpiceCharDeviceMsgToClientItem;
-
-static void spice_char_device_write_buffer_free(SpiceCharDeviceWriteBuffer *buf)
-{
-    if (buf == NULL)
-        return;
-
-    free(buf->buf);
-    free(buf);
-}
-
-static void write_buffers_queue_free(Ring *write_queue)
-{
-    while (!ring_is_empty(write_queue)) {
-        RingItem *item = ring_get_tail(write_queue);
-        SpiceCharDeviceWriteBuffer *buf;
-
-        ring_remove(item);
-        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-        spice_char_device_write_buffer_free(buf);
-    }
-}
-
-static void spice_char_device_write_buffer_pool_add(SpiceCharDeviceState *dev,
-                                                    SpiceCharDeviceWriteBuffer *buf)
-{
-    if (buf->refs == 1 &&
-        dev->cur_pool_size < MAX_POOL_SIZE) {
-        buf->buf_used = 0;
-        buf->origin = WRITE_BUFFER_ORIGIN_NONE;
-        buf->client = NULL;
-        dev->cur_pool_size += buf->buf_size;
-        ring_add(&dev->write_bufs_pool, &buf->link);
-        return;
-    }
-
-    /* Buffer still being used - just unref for the caller */
-    spice_char_device_write_buffer_unref(buf);
-}
-
-static void spice_char_device_client_send_queue_free(SpiceCharDeviceState *dev,
-                                                     SpiceCharDeviceClientState *dev_client)
-{
-    spice_debug("send_queue_empty %d", ring_is_empty(&dev_client->send_queue));
-    while (!ring_is_empty(&dev_client->send_queue)) {
-        RingItem *item = ring_get_tail(&dev_client->send_queue);
-        SpiceCharDeviceMsgToClientItem *msg_item = SPICE_CONTAINEROF(item,
-                                                                     SpiceCharDeviceMsgToClientItem,
-                                                                     link);
-
-        ring_remove(item);
-        dev->cbs.unref_msg_to_client(msg_item->msg, dev->opaque);
-        free(msg_item);
-    }
-    dev_client->num_send_tokens += dev_client->send_queue_size;
-    dev_client->send_queue_size = 0;
-}
-
-static void spice_char_device_client_free(SpiceCharDeviceState *dev,
-                                          SpiceCharDeviceClientState *dev_client)
-{
-    RingItem *item, *next;
-
-    if (dev_client->wait_for_tokens_timer) {
-        core->timer_remove(dev_client->wait_for_tokens_timer);
-    }
-
-    spice_char_device_client_send_queue_free(dev, dev_client);
-
-    /* remove write buffers that are associated with the client */
-    spice_debug("write_queue_is_empty %d", ring_is_empty(&dev->write_queue) && !dev->cur_write_buf);
-    RING_FOREACH_SAFE(item, next, &dev->write_queue) {
-        SpiceCharDeviceWriteBuffer *write_buf;
-
-        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
-            write_buf->client == dev_client->client) {
-            ring_remove(item);
-            spice_char_device_write_buffer_pool_add(dev, write_buf);
-        }
-    }
-
-    if (dev->cur_write_buf && dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
-        dev->cur_write_buf->client == dev_client->client) {
-        dev->cur_write_buf->origin = WRITE_BUFFER_ORIGIN_NONE;
-        dev->cur_write_buf->client = NULL;
-    }
-
-    dev->num_clients--;
-    ring_remove(&dev_client->link);
-    free(dev_client);
-}
-
-static void spice_char_device_handle_client_overflow(SpiceCharDeviceClientState *dev_client)
-{
-    SpiceCharDeviceState *dev = dev_client->dev;
-    spice_printerr("dev %p client %p ", dev, dev_client);
-    dev->cbs.remove_client(dev_client->client, dev->opaque);
-}
-
-static SpiceCharDeviceClientState *spice_char_device_client_find(SpiceCharDeviceState *dev,
-                                                                 RedClient *client)
-{
-    RingItem *item;
-
-    RING_FOREACH(item, &dev->clients) {
-        SpiceCharDeviceClientState *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
-        if (dev_client->client == client) {
-            return dev_client;
-        }
-    }
-    return NULL;
-}
-
-/***************************
- * Reading from the device *
- **************************/
-
-static void device_client_wait_for_tokens_timeout(void *opaque)
-{
-    SpiceCharDeviceClientState *dev_client = opaque;
-
-    spice_char_device_handle_client_overflow(dev_client);
-}
-
-static int spice_char_device_can_send_to_client(SpiceCharDeviceClientState *dev_client)
-{
-    return !dev_client->do_flow_control || dev_client->num_send_tokens;
-}
-
-static uint64_t spice_char_device_max_send_tokens(SpiceCharDeviceState *dev)
-{
-    RingItem *item;
-    uint64_t max = 0;
-
-    RING_FOREACH(item, &dev->clients) {
-        SpiceCharDeviceClientState *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
-
-        if (!dev_client->do_flow_control) {
-            max = ~0;
-            break;
-        }
-
-        if (dev_client->num_send_tokens > max) {
-            max = dev_client->num_send_tokens;
-        }
-    }
-    return max;
-}
-
-static void spice_char_device_add_msg_to_client_queue(SpiceCharDeviceClientState *dev_client,
-                                                      SpiceCharDeviceMsgToClient *msg)
-{
-    SpiceCharDeviceState *dev = dev_client->dev;
-    SpiceCharDeviceMsgToClientItem *msg_item;
-
-    if (dev_client->send_queue_size >= dev_client->max_send_queue_size) {
-        spice_char_device_handle_client_overflow(dev_client);
-        return;
-    }
-
-    msg_item = spice_new0(SpiceCharDeviceMsgToClientItem, 1);
-    msg_item->msg = dev->cbs.ref_msg_to_client(msg, dev->opaque);
-    ring_add(&dev_client->send_queue, &msg_item->link);
-    dev_client->send_queue_size++;
-    if (!dev_client->wait_for_tokens_started) {
-        core->timer_start(dev_client->wait_for_tokens_timer,
-                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
-        dev_client->wait_for_tokens_started = TRUE;
-    }
-}
-
-static void spice_char_device_send_msg_to_clients(SpiceCharDeviceState *dev,
-                                                  SpiceCharDeviceMsgToClient *msg)
-{
-    RingItem *item, *next;
-
-    RING_FOREACH_SAFE(item, next, &dev->clients) {
-        SpiceCharDeviceClientState *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
-        if (spice_char_device_can_send_to_client(dev_client)) {
-            dev_client->num_send_tokens--;
-            spice_assert(ring_is_empty(&dev_client->send_queue));
-            dev->cbs.send_msg_to_client(msg, dev_client->client, dev->opaque);
-
-            /* don't refer to dev_client anymore, it may have been released */
-        } else {
-            spice_char_device_add_msg_to_client_queue(dev_client, msg);
-        }
-    }
-}
-
-static int spice_char_device_read_from_device(SpiceCharDeviceState *dev)
-{
-    uint64_t max_send_tokens;
-    int did_read = FALSE;
-
-    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
-        return FALSE;
-    }
-
-    /* There are 2 scenarios where we can get called recursively:
-     * 1) spice-vmc vmc_read triggering flush of throttled data, recalling wakeup
-     * (virtio)
-     * 2) in case of sending messages to the client, and unreferencing the
-     * msg, we trigger another read.
-     */
-    if (dev->during_read_from_device++ > 0) {
-        return FALSE;
-    }
-
-    max_send_tokens = spice_char_device_max_send_tokens(dev);
-    spice_char_device_state_ref(dev);
-    /*
-     * Reading from the device only in case at least one of the clients have a free token.
-     * All messages will be discarded if no client is attached to the device
-     */
-    while ((max_send_tokens || ring_is_empty(&dev->clients)) && dev->running) {
-        SpiceCharDeviceMsgToClient *msg;
-
-        msg = dev->cbs.read_one_msg_from_device(dev->sin, dev->opaque);
-        if (!msg) {
-            if (dev->during_read_from_device > 1) {
-                dev->during_read_from_device = 1;
-                continue; /* a wakeup might have been called during the read -
-                             make sure it doesn't get lost */
-            }
-            break;
-        }
-        did_read = TRUE;
-        spice_char_device_send_msg_to_clients(dev, msg);
-        dev->cbs.unref_msg_to_client(msg, dev->opaque);
-        max_send_tokens--;
-    }
-    dev->during_read_from_device = 0;
-    if (dev->running) {
-        dev->active = dev->active || did_read;
-    }
-    spice_char_device_state_unref(dev);
-    return did_read;
-}
-
-static void spice_char_device_client_send_queue_push(SpiceCharDeviceClientState *dev_client)
-{
-    RingItem *item;
-    while ((item = ring_get_tail(&dev_client->send_queue)) &&
-           spice_char_device_can_send_to_client(dev_client)) {
-        SpiceCharDeviceMsgToClientItem *msg_item;
-
-        msg_item = SPICE_CONTAINEROF(item, SpiceCharDeviceMsgToClientItem, link);
-        ring_remove(item);
-
-        dev_client->num_send_tokens--;
-        dev_client->dev->cbs.send_msg_to_client(msg_item->msg,
-                                           dev_client->client,
-                                           dev_client->dev->opaque);
-        dev_client->dev->cbs.unref_msg_to_client(msg_item->msg, dev_client->dev->opaque);
-        dev_client->send_queue_size--;
-        free(msg_item);
-    }
-}
-
-static void spice_char_device_send_to_client_tokens_absorb(SpiceCharDeviceClientState *dev_client,
-                                                           uint32_t tokens)
-{
-    dev_client->num_send_tokens += tokens;
-
-    if (dev_client->send_queue_size) {
-        spice_assert(dev_client->num_send_tokens == tokens);
-        spice_char_device_client_send_queue_push(dev_client);
-    }
-
-    if (spice_char_device_can_send_to_client(dev_client)) {
-        core->timer_cancel(dev_client->wait_for_tokens_timer);
-        dev_client->wait_for_tokens_started = FALSE;
-        spice_char_device_read_from_device(dev_client->dev);
-    } else if (dev_client->send_queue_size) {
-        core->timer_start(dev_client->wait_for_tokens_timer,
-                          SPICE_CHAR_DEVICE_WAIT_TOKENS_TIMEOUT);
-        dev_client->wait_for_tokens_started = TRUE;
-    }
-}
-
-void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
-                                                 RedClient *client,
-                                                 uint32_t tokens)
-{
-    SpiceCharDeviceClientState *dev_client;
-
-    dev_client = spice_char_device_client_find(dev, client);
-
-    if (!dev_client) {
-        spice_error("client wasn't found dev %p client %p", dev, client);
-        return;
-    }
-    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
-}
-
-void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
-                                                 RedClient *client,
-                                                 uint32_t tokens)
-{
-    SpiceCharDeviceClientState *dev_client;
-
-    dev_client = spice_char_device_client_find(dev, client);
-
-    if (!dev_client) {
-        spice_error("client wasn't found dev %p client %p", dev, client);
-        return;
-    }
-
-    dev_client->num_send_tokens = 0;
-    spice_char_device_send_to_client_tokens_absorb(dev_client, tokens);
-}
-
-/**************************
- * Writing to the device  *
-***************************/
-
-static void spice_char_device_client_tokens_add(SpiceCharDeviceState *dev,
-                                                SpiceCharDeviceClientState *dev_client,
-                                                uint32_t num_tokens)
-{
-    if (!dev_client->do_flow_control) {
-        return;
-    }
-    if (num_tokens > 1) {
-        spice_debug("#tokens > 1 (=%u)", num_tokens);
-    }
-    dev_client->num_client_tokens_free += num_tokens;
-    if (dev_client->num_client_tokens_free >= dev->client_tokens_interval) {
-        uint32_t tokens = dev_client->num_client_tokens_free;
-
-        dev_client->num_client_tokens += dev_client->num_client_tokens_free;
-        dev_client->num_client_tokens_free = 0;
-        dev->cbs.send_tokens_to_client(dev_client->client,
-                                       tokens,
-                                       dev->opaque);
-    }
-}
-
-static int spice_char_device_write_to_device(SpiceCharDeviceState *dev)
-{
-    SpiceCharDeviceInterface *sif;
-    int total = 0;
-    int n;
-
-    if (!dev->running || dev->wait_for_migrate_data || !dev->sin) {
-        return 0;
-    }
-
-    /* protect against recursion with spice_char_device_wakeup */
-    if (dev->during_write_to_device++ > 0) {
-        return 0;
-    }
-
-    spice_char_device_state_ref(dev);
-
-    if (dev->write_to_dev_timer) {
-        core->timer_cancel(dev->write_to_dev_timer);
-    }
-
-    sif = SPICE_CONTAINEROF(dev->sin->base.sif, SpiceCharDeviceInterface, base);
-    while (dev->running) {
-        uint32_t write_len;
-
-        if (!dev->cur_write_buf) {
-            RingItem *item = ring_get_tail(&dev->write_queue);
-            if (!item) {
-                break;
-            }
-            dev->cur_write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-            dev->cur_write_buf_pos = dev->cur_write_buf->buf;
-            ring_remove(item);
-        }
-
-        write_len = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
-                    dev->cur_write_buf_pos;
-        n = sif->write(dev->sin, dev->cur_write_buf_pos, write_len);
-        if (n <= 0) {
-            if (dev->during_write_to_device > 1) {
-                dev->during_write_to_device = 1;
-                continue; /* a wakeup might have been called during the write -
-                             make sure it doesn't get lost */
-            }
-            break;
-        }
-        total += n;
-        write_len -= n;
-        if (!write_len) {
-            SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
-            dev->cur_write_buf = NULL;
-            spice_char_device_write_buffer_release(dev, release_buf);
-            continue;
-        }
-        dev->cur_write_buf_pos += n;
-    }
-    /* retry writing as long as the write queue is not empty */
-    if (dev->running) {
-        if (dev->cur_write_buf) {
-            if (dev->write_to_dev_timer) {
-                core->timer_start(dev->write_to_dev_timer,
-                                  CHAR_DEVICE_WRITE_TO_TIMEOUT);
-            }
-        } else {
-            spice_assert(ring_is_empty(&dev->write_queue));
-        }
-        dev->active = dev->active || total;
-    }
-    dev->during_write_to_device = 0;
-    spice_char_device_state_unref(dev);
-    return total;
-}
-
-static void spice_char_dev_write_retry(void *opaque)
-{
-    SpiceCharDeviceState *dev = opaque;
-
-    if (dev->write_to_dev_timer) {
-        core->timer_cancel(dev->write_to_dev_timer);
-    }
-    spice_char_device_write_to_device(dev);
-}
-
-static SpiceCharDeviceWriteBuffer *__spice_char_device_write_buffer_get(
-    SpiceCharDeviceState *dev, RedClient *client,
-    int size, int origin, int migrated_data_tokens)
-{
-    RingItem *item;
-    SpiceCharDeviceWriteBuffer *ret;
-
-    if (origin == WRITE_BUFFER_ORIGIN_SERVER && !dev->num_self_tokens) {
-        return NULL;
-    }
-
-    if ((item = ring_get_tail(&dev->write_bufs_pool))) {
-        ret = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-        ring_remove(item);
-        dev->cur_pool_size -= ret->buf_size;
-    } else {
-        ret = spice_new0(SpiceCharDeviceWriteBuffer, 1);
-    }
-
-    spice_assert(!ret->buf_used);
-
-    if (ret->buf_size < size) {
-        ret->buf = spice_realloc(ret->buf, size);
-        ret->buf_size = size;
-    }
-    ret->origin = origin;
-
-    if (origin == WRITE_BUFFER_ORIGIN_CLIENT) {
-       spice_assert(client);
-       SpiceCharDeviceClientState *dev_client = spice_char_device_client_find(dev, client);
-       if (dev_client) {
-            if (!migrated_data_tokens &&
-                dev_client->do_flow_control && !dev_client->num_client_tokens) {
-                spice_printerr("token violation: dev %p client %p", dev, client);
-                spice_char_device_handle_client_overflow(dev_client);
-                goto error;
-            }
-            ret->client = client;
-            if (!migrated_data_tokens && dev_client->do_flow_control) {
-                dev_client->num_client_tokens--;
-            }
-        } else {
-            /* it is possible that the client was removed due to send tokens underflow, but
-             * the caller still receive messages from the client */
-            spice_printerr("client not found: dev %p client %p", dev, client);
-            goto error;
-        }
-    } else if (origin == WRITE_BUFFER_ORIGIN_SERVER) {
-        dev->num_self_tokens--;
-    }
-
-    ret->token_price = migrated_data_tokens ? migrated_data_tokens : 1;
-    ret->refs = 1;
-    return ret;
-error:
-    dev->cur_pool_size += ret->buf_size;
-    ring_add(&dev->write_bufs_pool, &ret->link);
-    return NULL;
-}
-
-SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
-                                                               RedClient *client,
-                                                               int size)
-{
-   return  __spice_char_device_write_buffer_get(dev, client, size,
-             client ? WRITE_BUFFER_ORIGIN_CLIENT : WRITE_BUFFER_ORIGIN_SERVER,
-             0);
-}
-
-SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
-    SpiceCharDeviceState *dev, int size)
-{
-   return  __spice_char_device_write_buffer_get(dev, NULL, size,
-             WRITE_BUFFER_ORIGIN_SERVER_NO_TOKEN, 0);
-}
-
-static SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_ref(SpiceCharDeviceWriteBuffer *write_buf)
-{
-    spice_assert(write_buf);
-
-    write_buf->refs++;
-    return write_buf;
-}
-
-static void spice_char_device_write_buffer_unref(SpiceCharDeviceWriteBuffer *write_buf)
-{
-    spice_assert(write_buf);
-
-    write_buf->refs--;
-    if (write_buf->refs == 0)
-        spice_char_device_write_buffer_free(write_buf);
-}
-
-void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
-                                        SpiceCharDeviceWriteBuffer *write_buf)
-{
-    spice_assert(dev);
-    /* caller shouldn't add buffers for client that was removed */
-    if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT &&
-        !spice_char_device_client_find(dev, write_buf->client)) {
-        spice_printerr("client not found: dev %p client %p", dev, write_buf->client);
-        spice_char_device_write_buffer_pool_add(dev, write_buf);
-        return;
-    }
-
-    ring_add(&dev->write_queue, &write_buf->link);
-    spice_char_device_write_to_device(dev);
-}
-
-void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
-                                            SpiceCharDeviceWriteBuffer *write_buf)
-{
-    int buf_origin = write_buf->origin;
-    uint32_t buf_token_price = write_buf->token_price;
-    RedClient *client = write_buf->client;
-
-    spice_assert(!ring_item_is_linked(&write_buf->link));
-    if (!dev) {
-        spice_printerr("no device. write buffer is freed");
-        spice_char_device_write_buffer_free(write_buf);
-        return;
-    }
-
-    spice_assert(dev->cur_write_buf != write_buf);
-
-    spice_char_device_write_buffer_pool_add(dev, write_buf);
-    if (buf_origin == WRITE_BUFFER_ORIGIN_CLIENT) {
-        SpiceCharDeviceClientState *dev_client;
-
-        spice_assert(client);
-        dev_client = spice_char_device_client_find(dev, client);
-        /* when a client is removed, we remove all the buffers that are associated with it */
-        spice_assert(dev_client);
-        spice_char_device_client_tokens_add(dev, dev_client, buf_token_price);
-    } else if (buf_origin == WRITE_BUFFER_ORIGIN_SERVER) {
-        dev->num_self_tokens++;
-        if (dev->cbs.on_free_self_token) {
-            dev->cbs.on_free_self_token(dev->opaque);
-        }
-    }
-}
-
-/********************************
- * char_device_state management *
- ********************************/
-
-SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
-                                                     uint32_t client_tokens_interval,
-                                                     uint32_t self_tokens,
-                                                     SpiceCharDeviceCallbacks *cbs,
-                                                     void *opaque)
-{
-    SpiceCharDeviceState *char_dev;
-    SpiceCharDeviceInterface *sif;
-
-    spice_assert(sin);
-    spice_assert(cbs->read_one_msg_from_device && cbs->ref_msg_to_client &&
-                 cbs->unref_msg_to_client && cbs->send_msg_to_client &&
-                 cbs->send_tokens_to_client && cbs->remove_client);
-
-    char_dev = spice_new0(SpiceCharDeviceState, 1);
-    char_dev->sin = sin;
-    char_dev->cbs = *cbs;
-    char_dev->opaque = opaque;
-    char_dev->client_tokens_interval = client_tokens_interval;
-    char_dev->num_self_tokens = self_tokens;
-
-    ring_init(&char_dev->write_queue);
-    ring_init(&char_dev->write_bufs_pool);
-    ring_init(&char_dev->clients);
-
-    sif = SPICE_CONTAINEROF(char_dev->sin->base.sif, SpiceCharDeviceInterface, base);
-    if (sif->base.minor_version <= 2 ||
-        !(sif->flags & SPICE_CHAR_DEVICE_NOTIFY_WRITABLE)) {
-        char_dev->write_to_dev_timer = core->timer_add(spice_char_dev_write_retry, char_dev);
-        if (!char_dev->write_to_dev_timer) {
-            spice_error("failed creating char dev write timer");
-        }
-    }
-
-    char_dev->refs = 1;
-    sin->st = char_dev;
-    spice_debug("sin %p dev_state %p", sin, char_dev);
-    return char_dev;
-}
-
-void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *state,
-                                                SpiceCharDeviceInstance *sin)
-{
-    spice_debug("sin %p dev_state %p", sin, state);
-    state->sin = sin;
-    sin->st = state;
-}
-
-void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev)
-{
-    return dev->opaque;
-}
-
-static void spice_char_device_state_ref(SpiceCharDeviceState *char_dev)
-{
-    char_dev->refs++;
-}
-
-static void spice_char_device_state_unref(SpiceCharDeviceState *char_dev)
-{
-    /* The refs field protects the char_dev from being deallocated in
-     * case spice_char_device_state_destroy has been called
-     * during a callabck, and we might still access the char_dev afterwards.
-     * spice_char_device_state_unref is always coupled with a preceding
-     * spice_char_device_state_ref. Here, refs can turn 0
-     * only when spice_char_device_state_destroy is called in between
-     * the calls to spice_char_device_state_ref and spice_char_device_state_unref.*/
-    if (!--char_dev->refs) {
-        free(char_dev);
-    }
-}
-
-void spice_char_device_state_destroy(SpiceCharDeviceState *char_dev)
-{
-    reds_on_char_device_state_destroy(char_dev);
-    if (char_dev->write_to_dev_timer) {
-        core->timer_remove(char_dev->write_to_dev_timer);
-        char_dev->write_to_dev_timer = NULL;
-    }
-    write_buffers_queue_free(&char_dev->write_queue);
-    write_buffers_queue_free(&char_dev->write_bufs_pool);
-    char_dev->cur_pool_size = 0;
-    spice_char_device_write_buffer_free(char_dev->cur_write_buf);
-    char_dev->cur_write_buf = NULL;
-
-    while (!ring_is_empty(&char_dev->clients)) {
-        RingItem *item = ring_get_tail(&char_dev->clients);
-        SpiceCharDeviceClientState *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(item, SpiceCharDeviceClientState, link);
-        spice_char_device_client_free(char_dev, dev_client);
-    }
-    char_dev->running = FALSE;
-
-    spice_char_device_state_unref(char_dev);
-}
-
-int spice_char_device_client_add(SpiceCharDeviceState *dev,
-                                 RedClient *client,
-                                 int do_flow_control,
-                                 uint32_t max_send_queue_size,
-                                 uint32_t num_client_tokens,
-                                 uint32_t num_send_tokens,
-                                 int wait_for_migrate_data)
-{
-    SpiceCharDeviceClientState *dev_client;
-
-    spice_assert(dev);
-    spice_assert(client);
-
-    if (wait_for_migrate_data && (dev->num_clients > 0 || dev->active)) {
-        spice_warning("can't restore device %p from migration data. The device "
-                      "has already been active", dev);
-        return FALSE;
-    }
-
-    dev->wait_for_migrate_data = wait_for_migrate_data;
-
-    spice_debug("dev_state %p client %p", dev, client);
-    dev_client = spice_new0(SpiceCharDeviceClientState, 1);
-    dev_client->dev = dev;
-    dev_client->client = client;
-    ring_init(&dev_client->send_queue);
-    dev_client->send_queue_size = 0;
-    dev_client->max_send_queue_size = max_send_queue_size;
-    dev_client->do_flow_control = do_flow_control;
-    if (do_flow_control) {
-        dev_client->wait_for_tokens_timer = core->timer_add(device_client_wait_for_tokens_timeout,
-                                                            dev_client);
-        if (!dev_client->wait_for_tokens_timer) {
-            spice_error("failed to create wait for tokens timer");
-        }
-        dev_client->num_client_tokens = num_client_tokens;
-        dev_client->num_send_tokens = num_send_tokens;
-    } else {
-        dev_client->num_client_tokens = ~0;
-        dev_client->num_send_tokens = ~0;
-    }
-    ring_add(&dev->clients, &dev_client->link);
-    dev->num_clients++;
-    /* Now that we have a client, forward any pending device data */
-    spice_char_device_wakeup(dev);
-    return TRUE;
-}
-
-void spice_char_device_client_remove(SpiceCharDeviceState *dev,
-                                     RedClient *client)
-{
-    SpiceCharDeviceClientState *dev_client;
-
-    spice_debug("dev_state %p client %p", dev, client);
-    dev_client = spice_char_device_client_find(dev, client);
-
-    if (!dev_client) {
-        spice_error("client wasn't found");
-        return;
-    }
-    spice_char_device_client_free(dev, dev_client);
-    if (dev->wait_for_migrate_data) {
-        spice_assert(dev->num_clients == 0);
-        dev->wait_for_migrate_data  = FALSE;
-        spice_char_device_read_from_device(dev);
-    }
-
-    if (dev->num_clients == 0) {
-        spice_debug("client removed, memory pool will be freed (%lu bytes)", dev->cur_pool_size);
-        write_buffers_queue_free(&dev->write_bufs_pool);
-        dev->cur_pool_size = 0;
-    }
-}
-
-int spice_char_device_client_exists(SpiceCharDeviceState *dev,
-                                    RedClient *client)
-{
-    return (spice_char_device_client_find(dev, client) != NULL);
-}
-
-void spice_char_device_start(SpiceCharDeviceState *dev)
-{
-    spice_debug("dev_state %p", dev);
-    dev->running = TRUE;
-    spice_char_device_state_ref(dev);
-    while (spice_char_device_write_to_device(dev) ||
-           spice_char_device_read_from_device(dev));
-    spice_char_device_state_unref(dev);
-}
-
-void spice_char_device_stop(SpiceCharDeviceState *dev)
-{
-    spice_debug("dev_state %p", dev);
-    dev->running = FALSE;
-    dev->active = FALSE;
-    if (dev->write_to_dev_timer) {
-        core->timer_cancel(dev->write_to_dev_timer);
-    }
-}
-
-void spice_char_device_reset(SpiceCharDeviceState *dev)
-{
-    RingItem *client_item;
-
-    spice_char_device_stop(dev);
-    dev->wait_for_migrate_data = FALSE;
-    spice_debug("dev_state %p", dev);
-    while (!ring_is_empty(&dev->write_queue)) {
-        RingItem *item = ring_get_tail(&dev->write_queue);
-        SpiceCharDeviceWriteBuffer *buf;
-
-        ring_remove(item);
-        buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-        /* tracking the tokens */
-        spice_char_device_write_buffer_release(dev, buf);
-    }
-    if (dev->cur_write_buf) {
-        SpiceCharDeviceWriteBuffer *release_buf = dev->cur_write_buf;
-
-        dev->cur_write_buf = NULL;
-        spice_char_device_write_buffer_release(dev, release_buf);
-    }
-
-    RING_FOREACH(client_item, &dev->clients) {
-        SpiceCharDeviceClientState *dev_client;
-
-        dev_client = SPICE_CONTAINEROF(client_item, SpiceCharDeviceClientState, link);
-        spice_char_device_client_send_queue_free(dev, dev_client);
-    }
-    dev->sin = NULL;
-}
-
-void spice_char_device_wakeup(SpiceCharDeviceState *dev)
-{
-    spice_char_device_write_to_device(dev);
-    spice_char_device_read_from_device(dev);
-}
-
-/*************
- * Migration *
- * **********/
-
-void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m)
-{
-    SpiceMigrateDataCharDevice *mig_data;
-
-    spice_debug(NULL);
-    mig_data = (SpiceMigrateDataCharDevice *)spice_marshaller_reserve_space(m,
-                                                                            sizeof(*mig_data));
-    memset(mig_data, 0, sizeof(*mig_data));
-    mig_data->version = SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION;
-    mig_data->connected = FALSE;
-}
-
-static void migrate_data_marshaller_write_buffer_free(uint8_t *data, void *opaque)
-{
-    SpiceCharDeviceWriteBuffer *write_buf = (SpiceCharDeviceWriteBuffer *)opaque;
-
-    spice_char_device_write_buffer_unref(write_buf);
-}
-
-void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
-                                                   SpiceMarshaller *m)
-{
-    SpiceCharDeviceClientState *client_state;
-    RingItem *item;
-    uint32_t *write_to_dev_size_ptr;
-    uint32_t *write_to_dev_tokens_ptr;
-    SpiceMarshaller *m2;
-
-    /* multi-clients are not supported */
-    spice_assert(dev->num_clients == 1);
-    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
-                                     SpiceCharDeviceClientState,
-                                     link);
-    /* FIXME: if there were more than one client before the marshalling,
-     * it is possible that the send_queue_size > 0, and the send data
-     * should be migrated as well */
-    spice_assert(client_state->send_queue_size == 0);
-    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
-    spice_marshaller_add_uint8(m, 1); /* connected */
-    spice_marshaller_add_uint32(m, client_state->num_client_tokens);
-    spice_marshaller_add_uint32(m, client_state->num_send_tokens);
-    write_to_dev_size_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
-    write_to_dev_tokens_ptr = (uint32_t *)spice_marshaller_reserve_space(m, sizeof(uint32_t));
-    *write_to_dev_size_ptr = 0;
-    *write_to_dev_tokens_ptr = 0;
-
-    m2 = spice_marshaller_get_ptr_submarshaller(m, 0);
-    if (dev->cur_write_buf) {
-        uint32_t buf_remaining = dev->cur_write_buf->buf + dev->cur_write_buf->buf_used -
-                                 dev->cur_write_buf_pos;
-        spice_marshaller_add_ref_full(m2, dev->cur_write_buf_pos, buf_remaining,
-                                      migrate_data_marshaller_write_buffer_free,
-                                      spice_char_device_write_buffer_ref(dev->cur_write_buf)
-                                      );
-        *write_to_dev_size_ptr += buf_remaining;
-        if (dev->cur_write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
-            spice_assert(dev->cur_write_buf->client == client_state->client);
-            (*write_to_dev_tokens_ptr) += dev->cur_write_buf->token_price;
-        }
-    }
-
-    RING_FOREACH_REVERSED(item, &dev->write_queue) {
-        SpiceCharDeviceWriteBuffer *write_buf;
-
-        write_buf = SPICE_CONTAINEROF(item, SpiceCharDeviceWriteBuffer, link);
-        spice_marshaller_add_ref_full(m2, write_buf->buf, write_buf->buf_used,
-                                      migrate_data_marshaller_write_buffer_free,
-                                      spice_char_device_write_buffer_ref(write_buf)
-                                      );
-        *write_to_dev_size_ptr += write_buf->buf_used;
-        if (write_buf->origin == WRITE_BUFFER_ORIGIN_CLIENT) {
-            spice_assert(write_buf->client == client_state->client);
-            (*write_to_dev_tokens_ptr) += write_buf->token_price;
-        }
-    }
-    spice_debug("migration data dev %p: write_queue size %u tokens %u",
-                dev, *write_to_dev_size_ptr, *write_to_dev_tokens_ptr);
-}
-
-int spice_char_device_state_restore(SpiceCharDeviceState *dev,
-                                    SpiceMigrateDataCharDevice *mig_data)
-{
-    SpiceCharDeviceClientState *client_state;
-    uint32_t client_tokens_window;
-
-    spice_assert(dev->num_clients == 1 && dev->wait_for_migrate_data);
-
-    client_state = SPICE_CONTAINEROF(ring_get_tail(&dev->clients),
-                                     SpiceCharDeviceClientState,
-                                     link);
-    if (mig_data->version > SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION) {
-        spice_error("dev %p error: migration data version %u is bigger than self %u",
-                    dev, mig_data->version, SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION);
-        return FALSE;
-    }
-    spice_assert(!dev->cur_write_buf && ring_is_empty(&dev->write_queue));
-    spice_assert(mig_data->connected);
-
-    client_tokens_window = client_state->num_client_tokens; /* initial state of tokens */
-    client_state->num_client_tokens = mig_data->num_client_tokens;
-    /* assumption: client_tokens_window stays the same across severs */
-    client_state->num_client_tokens_free = client_tokens_window -
-                                           mig_data->num_client_tokens -
-                                           mig_data->write_num_client_tokens;
-    client_state->num_send_tokens = mig_data->num_send_tokens;
-
-    if (mig_data->write_size > 0) {
-        if (mig_data->write_num_client_tokens) {
-            dev->cur_write_buf =
-                __spice_char_device_write_buffer_get(dev, client_state->client,
-                    mig_data->write_size, WRITE_BUFFER_ORIGIN_CLIENT,
-                    mig_data->write_num_client_tokens);
-        } else {
-            dev->cur_write_buf =
-                __spice_char_device_write_buffer_get(dev, NULL,
-                    mig_data->write_size, WRITE_BUFFER_ORIGIN_SERVER, 0);
-        }
-        /* the first write buffer contains all the data that was saved for migration */
-        memcpy(dev->cur_write_buf->buf,
-               ((uint8_t *)mig_data) + mig_data->write_data_ptr - sizeof(SpiceMigrateDataHeader),
-               mig_data->write_size);
-        dev->cur_write_buf->buf_used = mig_data->write_size;
-        dev->cur_write_buf_pos = dev->cur_write_buf->buf;
-    }
-    dev->wait_for_migrate_data = FALSE;
-    spice_char_device_write_to_device(dev);
-    spice_char_device_read_from_device(dev);
-    return TRUE;
-}
diff --git a/server/char_device.h b/server/char_device.h
deleted file mode 100644
index 55d1ee6..0000000
--- a/server/char_device.h
+++ /dev/null
@@ -1,216 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifndef __CHAR_DEVICE_H__
-#define __CHAR_DEVICE_H__
-
-#include "spice.h"
-#include "red_channel.h"
-#include "migration_protocol.h"
-
-/*
- * Shared code for char devices, mainly for flow control.
- *
- * How to use the api:
- * ==================
- * device attached: call spice_char_device_state_create
- * device detached: call spice_char_device_state_destroy/reset
- *
- * client connected and associated with a device: spice_char_device_client_add
- * client disconnected: spice_char_device_client_remove
- *
- * Writing to the device
- * ---------------------
- * Write the data into SpiceCharDeviceWriteBuffer:
- * call spice_char_device_write_buffer_get in order to get an appropriate buffer.
- * call spice_char_device_write_buffer_add in order to push the buffer to the write queue.
- * If you choose not to push the buffer to the device, call
- * spice_char_device_write_buffer_release
- *
- * reading from the device
- * -----------------------
- *  The callback read_one_msg_from_device (see below) should be implemented
- *  (using sif->read).
- *  When the device is ready, this callback is called, and is expected to
- *  return one message which is addressed to the client, or NULL if the read
- *  hasn't completed.
- *
- * calls triggered from the device (qemu):
- * --------------------------------------
- * spice_char_device_start
- * spice_char_device_stop
- * spice_char_device_wakeup (for reading from the device)
- */
-
-/*
- * Note about multiple-clients:
- * Multiclients are currently not supported in any of the character devices:
- * spicevmc does not allow more than one client (and at least for usb, it should stay this way).
- * smartcard code is not compatible with more than one reader.
- * The server and guest agent code doesn't distinguish messages from different clients.
- * In addition, its current flow control code (e.g., tokens handling) is wrong and doesn't
- * take into account the different clients.
- *
- * Nonetheless, the following code introduces some support for multiple-clients:
- * We track the number of tokens for all the clients, and we read from the device
- * if one of the clients have enough tokens. For the clients that don't have tokens,
- * we queue the messages, till they receive tokens, or till a timeout.
- *
- * TODO:
- * At least for the agent, not all the messages from the device will be directed to all
- * the clients (e.g., copy from guest to a specific client). Thus, support for
- * client-specific-messages should be added.
- * In addition, we should have support for clients that are being connected
- * in the middle of a message transfer from the agent to the clients.
- *
- * */
-
-/* buffer that is used for writing to the device */
-typedef struct SpiceCharDeviceWriteBuffer {
-    RingItem link;
-    int origin;
-    RedClient *client; /* The client that sent the message to the device.
-                          NULL if the server created the message */
-
-    uint8_t *buf;
-    uint32_t buf_size;
-    uint32_t buf_used;
-    uint32_t token_price;
-    uint32_t refs;
-} SpiceCharDeviceWriteBuffer;
-
-typedef void SpiceCharDeviceMsgToClient;
-
-typedef struct SpiceCharDeviceCallbacks {
-    /*
-     * Messages that are addressed to the client can be queued in case we have
-     * multiple clients and some of them don't have enough tokens.
-     */
-
-    /* reads from the device till reaching a msg that should be sent to the client,
-     * or till the reading fails */
-    SpiceCharDeviceMsgToClient* (*read_one_msg_from_device)(SpiceCharDeviceInstance *sin,
-                                                            void *opaque);
-    SpiceCharDeviceMsgToClient* (*ref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
-                                                     void *opaque);
-    void (*unref_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
-                                void *opaque);
-    void (*send_msg_to_client)(SpiceCharDeviceMsgToClient *msg,
-                               RedClient *client,
-                               void *opaque); /* after this call, the message is unreferenced */
-
-    /* The cb is called when a predefined number of write buffers were consumed by the
-     * device */
-    void (*send_tokens_to_client)(RedClient *client, uint32_t tokens, void *opaque);
-
-    /* The cb is called when a server (self) message that was addressed to the device,
-     * has been completely written to it */
-    void (*on_free_self_token)(void *opaque);
-
-    /* This cb is called if it is recommanded that a client will be removed
-     * due to slow flow or due to some other error.
-     * The called instance should disconnect the client, or at least the corresponding channel */
-    void (*remove_client)(RedClient *client, void *opaque);
-} SpiceCharDeviceCallbacks;
-
-SpiceCharDeviceState *spice_char_device_state_create(SpiceCharDeviceInstance *sin,
-                                                     uint32_t client_tokens_interval,
-                                                     uint32_t self_tokens,
-                                                     SpiceCharDeviceCallbacks *cbs,
-                                                     void *opaque);
-
-void spice_char_device_state_reset_dev_instance(SpiceCharDeviceState *dev,
-                                                SpiceCharDeviceInstance *sin);
-void spice_char_device_state_destroy(SpiceCharDeviceState *dev);
-
-void *spice_char_device_state_opaque_get(SpiceCharDeviceState *dev);
-
-/* only one client is supported */
-void spice_char_device_state_migrate_data_marshall(SpiceCharDeviceState *dev,
-                                                  SpiceMarshaller *m);
-void spice_char_device_state_migrate_data_marshall_empty(SpiceMarshaller *m);
-
-int spice_char_device_state_restore(SpiceCharDeviceState *dev,
-                                    SpiceMigrateDataCharDevice *mig_data);
-
-/*
- * Resets write/read queues, and moves that state to being stopped.
- * This routine is a workaround for a bad tokens management in the vdagent
- * protocol:
- *  The client tokens' are set only once, when the main channel is initialized.
- *  Instead, it would have been more appropriate to reset them upon AGEN_CONNECT.
- *  The client tokens are tracked as part of the SpiceCharDeviceClientState. Thus,
- *  in order to be backwartd compatible with the client, we need to track the tokens
- *  event when the agent is detached. We don't destroy the char_device state, and
- *  instead we just reset it.
- *  In addition, there is a misshandling of AGENT_TOKENS message in spice-gtk: it
- *  overrides the amount of tokens, instead of adding the given amount.
- *
- *  todo: change AGENT_CONNECT msg to contain tokens count.
- */
-void spice_char_device_reset(SpiceCharDeviceState *dev);
-
-/* max_send_queue_size = how many messages we can read from the device and enqueue for this client,
- * when we have tokens for other clients and no tokens for this one */
-int spice_char_device_client_add(SpiceCharDeviceState *dev,
-                                 RedClient *client,
-                                 int do_flow_control,
-                                 uint32_t max_send_queue_size,
-                                 uint32_t num_client_tokens,
-                                 uint32_t num_send_tokens,
-                                 int wait_for_migrate_data);
-
-void spice_char_device_client_remove(SpiceCharDeviceState *dev,
-                                     RedClient *client);
-int spice_char_device_client_exists(SpiceCharDeviceState *dev,
-                                    RedClient *client);
-
-void spice_char_device_start(SpiceCharDeviceState *dev);
-void spice_char_device_stop(SpiceCharDeviceState *dev);
-
-/** Read from device **/
-
-void spice_char_device_wakeup(SpiceCharDeviceState *dev);
-
-void spice_char_device_send_to_client_tokens_add(SpiceCharDeviceState *dev,
-                                                 RedClient *client,
-                                                 uint32_t tokens);
-
-
-void spice_char_device_send_to_client_tokens_set(SpiceCharDeviceState *dev,
-                                                 RedClient *client,
-                                                 uint32_t tokens);
-/** Write to device **/
-
-SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get(SpiceCharDeviceState *dev,
-                                                               RedClient *client, int size);
-SpiceCharDeviceWriteBuffer *spice_char_device_write_buffer_get_server_no_token(
-    SpiceCharDeviceState *dev, int size);
-
-/* Either add the buffer to the write queue or release it */
-void spice_char_device_write_buffer_add(SpiceCharDeviceState *dev,
-                                        SpiceCharDeviceWriteBuffer *write_buf);
-void spice_char_device_write_buffer_release(SpiceCharDeviceState *dev,
-                                            SpiceCharDeviceWriteBuffer *write_buf);
-
-/* api for specific char devices */
-
-SpiceCharDeviceState *spicevmc_device_connect(SpiceCharDeviceInstance *sin,
-                                              uint8_t channel_type);
-void spicevmc_device_disconnect(SpiceCharDeviceInstance *char_device);
-
-#endif // __CHAR_DEVICE_H__
diff --git a/server/dcc-encoders.h b/server/dcc-encoders.h
index 5de66f7..dc00ed1 100644
--- a/server/dcc-encoders.h
+++ b/server/dcc-encoders.h
@@ -23,14 +23,13 @@
 #include "common/quic.h"
 #include "red_channel.h"
 #include "red_parse_qxl.h"
-#include "spice_image_cache.h"
-#include "glz_encoder_dictionary.h"
-#include "glz_encoder.h"
-#include "jpeg_encoder.h"
+#include "image-cache.h"
+#include "glz-encoder.h"
+#include "jpeg-encoder.h"
 #ifdef USE_LZ4
 #include "lz4_encoder.h"
 #endif
-#include "zlib_encoder.h"
+#include "zlib-encoder.h"
 
 typedef struct RedCompressBuf RedCompressBuf;
 typedef struct GlzDrawableInstanceItem GlzDrawableInstanceItem;
diff --git a/server/dispatcher.h b/server/dispatcher.h
index 353744a..0d3175f 100644
--- a/server/dispatcher.h
+++ b/server/dispatcher.h
@@ -18,8 +18,7 @@
 #ifndef DISPATCHER_H
 #define DISPATCHER_H
 
-#include <spice.h>
-#include "utils.h"
+#include "red_common.h"
 
 typedef struct Dispatcher Dispatcher;
 
diff --git a/server/display-channel.h b/server/display-channel.h
index 2ac8173..e17381c 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -25,22 +25,22 @@
 #include "reds_stream.h"
 #include "cache-item.h"
 #include "pixmap-cache.h"
-#include "reds_sw_canvas.h"
+#include "canvas.h"
 #include "stat.h"
 #include "reds.h"
-#include "mjpeg_encoder.h"
-#include "red_memslots.h"
+#include "mjpeg-encoder.h"
+#include "memslot.h"
 #include "red_parse_qxl.h"
 #include "red_record_qxl.h"
 #include "demarshallers.h"
 #include "red_channel.h"
 #include "red_dispatcher.h"
 #include "dispatcher.h"
-#include "main_channel.h"
-#include "migration_protocol.h"
-#include "main_dispatcher.h"
-#include "spice_bitmap_utils.h"
-#include "spice_image_cache.h"
+#include "main-channel.h"
+#include "migration-protocol.h"
+#include "main-dispatcher.h"
+#include "spice-bitmap-utils.h"
+#include "image-cache.h"
 #include "utils.h"
 #include "tree.h"
 #include "stream.h"
diff --git a/server/glz-encoder-dict.c b/server/glz-encoder-dict.c
new file mode 100644
index 0000000..af74736
--- /dev/null
+++ b/server/glz-encoder-dict.c
@@ -0,0 +1,634 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <pthread.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "glz-encoder.h"
+#include "glz-encoder-dict.h"
+#include "glz-encoder-priv.h"
+
+/* turning all used images to free ones. If they are alive, calling the free_image callback for
+   each one */
+static inline void __glz_dictionary_window_reset_images(SharedDictionary *dict)
+{
+    WindowImage *tmp;
+
+    while (dict->window.used_images_head) {
+        tmp = dict->window.used_images_head;
+        dict->window.used_images_head = dict->window.used_images_head->next;
+        if (tmp->is_alive) {
+            dict->cur_usr->free_image(dict->cur_usr, tmp->usr_context);
+        }
+        tmp->next = dict->window.free_images;
+        tmp->is_alive = FALSE;
+        dict->window.free_images = tmp;
+    }
+    dict->window.used_images_tail = NULL;
+}
+
+/* allocate window fields (no reset)*/
+static int glz_dictionary_window_create(SharedDictionary *dict, uint32_t size)
+{
+    if (size > LZ_MAX_WINDOW_SIZE) {
+        return FALSE;
+    }
+
+    dict->window.size_limit = size;
+    dict->window.segs = (WindowImageSegment *)(
+            dict->cur_usr->malloc(dict->cur_usr, sizeof(WindowImageSegment) * INIT_IMAGE_SEGS_NUM));
+
+    if (!dict->window.segs) {
+        return FALSE;
+    }
+
+    dict->window.segs_quota = INIT_IMAGE_SEGS_NUM;
+
+    dict->window.encoders_heads = (uint32_t *)dict->cur_usr->malloc(dict->cur_usr,
+                                                            sizeof(uint32_t) * dict->max_encoders);
+
+    if (!dict->window.encoders_heads) {
+        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
+        return FALSE;
+    }
+
+    dict->window.used_images_head = NULL;
+    dict->window.used_images_tail = NULL;
+    dict->window.free_images = NULL;
+    dict->window.pixels_so_far = 0;
+
+    return TRUE;
+}
+
+/* initializes an empty window (segs and encoder_heads should be pre allocated.
+   resets the image infos, and calls the free_image usr callback*/
+static void glz_dictionary_window_reset(SharedDictionary *dict)
+{
+    uint32_t i;
+    WindowImageSegment *seg, *last_seg;
+
+    last_seg = dict->window.segs + dict->window.segs_quota;
+    /* reset free segs list */
+    dict->window.free_segs_head = 0;
+    for (seg = dict->window.segs, i = 0; seg < last_seg; seg++, i++) {
+        seg->next = i + 1;
+        seg->image = NULL;
+        seg->lines = NULL;
+        seg->lines_end = NULL;
+        seg->pixels_num = 0;
+        seg->pixels_so_far = 0;
+    }
+    dict->window.segs[dict->window.segs_quota - 1].next = NULL_IMAGE_SEG_ID;
+
+    dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
+    dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
+
+    // reset encoders heads
+    for (i = 0; i < dict->max_encoders; i++) {
+        dict->window.encoders_heads[i] = NULL_IMAGE_SEG_ID;
+    }
+
+    __glz_dictionary_window_reset_images(dict);
+}
+
+static inline void glz_dictionary_reset_hash(SharedDictionary *dict)
+{
+    memset(dict->htab, 0, sizeof(HashEntry) * HASH_SIZE * HASH_CHAIN_SIZE);
+#ifdef CHAINED_HASH
+    memset(dict->htab_counter, 0, HASH_SIZE * sizeof(uint8_t));
+#endif
+}
+
+static inline void glz_dictionary_window_destroy(SharedDictionary *dict)
+{
+    __glz_dictionary_window_reset_images(dict);
+
+    if (dict->window.segs) {
+        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
+        dict->window.segs = NULL;
+    }
+
+    while (dict->window.free_images) {
+        WindowImage *tmp = dict->window.free_images;
+        dict->window.free_images = tmp->next;
+
+        dict->cur_usr->free(dict->cur_usr, tmp);
+    }
+
+    if (dict->window.encoders_heads) {
+        dict->cur_usr->free(dict->cur_usr, dict->window.encoders_heads);
+        dict->window.encoders_heads = NULL;
+    }
+}
+
+/* logic removal only */
+static inline void glz_dictionary_window_kill_image(SharedDictionary *dict, WindowImage *image)
+{
+    image->is_alive = FALSE;
+}
+
+GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
+                                             GlzEncoderUsrContext *usr)
+{
+    SharedDictionary *dict;
+
+    if (!(dict = (SharedDictionary *)usr->malloc(usr,
+                                                 sizeof(SharedDictionary)))) {
+        return NULL;
+    }
+
+    dict->cur_usr = usr;
+    dict->last_image_id = 0;
+    dict->max_encoders = max_encoders;
+
+    pthread_mutex_init(&dict->lock, NULL);
+    pthread_rwlock_init(&dict->rw_alloc_lock, NULL);
+
+    dict->window.encoders_heads = NULL;
+
+    // alloc window fields and reset
+    if (!glz_dictionary_window_create(dict, size)) {
+        dict->cur_usr->free(usr, dict);
+        return NULL;
+    }
+
+    // reset window and hash
+    glz_enc_dictionary_reset((GlzEncDictContext *)dict, usr);
+
+    return (GlzEncDictContext *)dict;
+}
+
+void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
+                                         GlzEncDictRestoreData *out_data, GlzEncoderUsrContext *usr)
+{
+    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
+    dict->cur_usr = usr;
+    GLZ_ASSERT(dict->cur_usr, opaque_dict);
+    GLZ_ASSERT(dict->cur_usr, out_data);
+
+    out_data->last_image_id = dict->last_image_id;
+    out_data->max_encoders = dict->max_encoders;
+    out_data->size = dict->window.size_limit;
+}
+
+GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
+                                              GlzEncoderUsrContext *usr)
+{
+    if (!restore_data) {
+        return NULL;
+    }
+    SharedDictionary *ret = (SharedDictionary *)glz_enc_dictionary_create(
+            restore_data->size, restore_data->max_encoders, usr);
+    ret->last_image_id = restore_data->last_image_id;
+    return ((GlzEncDictContext *)ret);
+}
+
+void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
+{
+    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
+    dict->cur_usr = usr;
+    GLZ_ASSERT(dict->cur_usr, opaque_dict);
+
+    dict->last_image_id = 0;
+    glz_dictionary_window_reset(dict);
+    glz_dictionary_reset_hash(dict);
+}
+
+void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
+{
+    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
+
+    if (!opaque_dict) {
+        return;
+    }
+
+    dict->cur_usr = usr;
+    glz_dictionary_window_destroy(dict);
+
+    pthread_mutex_destroy(&dict->lock);
+    pthread_rwlock_destroy(&dict->rw_alloc_lock);
+
+    dict->cur_usr->free(dict->cur_usr, dict);
+}
+
+uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *opaque_dict)
+{
+    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
+
+    if (!opaque_dict) {
+        return 0;
+    }
+    return dict->window.size_limit;
+}
+
+/* doesn't call the remove image callback */
+void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
+                                     GlzEncDictImageContext *opaque_image,
+                                     GlzEncoderUsrContext *usr)
+{
+    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
+    WindowImage *image = (WindowImage *)opaque_image;
+    dict->cur_usr = usr;
+    GLZ_ASSERT(dict->cur_usr, opaque_image && opaque_dict);
+
+    glz_dictionary_window_kill_image(dict, image);
+}
+
+/***********************************************************************************
+ Mutators of the window. Should be called by the encoder before and after encoding.
+ ***********************************************************************************/
+
+static inline int __get_pixels_num(LzImageType image_type, unsigned int num_lines, int stride)
+{
+    if (IS_IMAGE_TYPE_RGB[image_type]) {
+        return num_lines * stride / RGB_BYTES_PER_PIXEL[image_type];
+    } else {
+        return num_lines * stride * PLT_PIXELS_PER_BYTE[image_type];
+    }
+}
+
+static void __glz_dictionary_window_segs_realloc(SharedDictionary *dict)
+{
+    WindowImageSegment *new_segs;
+    uint32_t new_quota = (MAX_IMAGE_SEGS_NUM < (dict->window.segs_quota * 2)) ?
+        MAX_IMAGE_SEGS_NUM : (dict->window.segs_quota * 2);
+    WindowImageSegment *seg;
+    uint32_t i;
+
+    pthread_rwlock_wrlock(&dict->rw_alloc_lock);
+
+    if (dict->window.segs_quota == MAX_IMAGE_SEGS_NUM) {
+        dict->cur_usr->error(dict->cur_usr, "overflow in image segments window\n");
+    }
+
+    new_segs = (WindowImageSegment*)dict->cur_usr->malloc(
+            dict->cur_usr, sizeof(WindowImageSegment) * new_quota);
+
+    if (!new_segs) {
+        dict->cur_usr->error(dict->cur_usr,
+                             "realloc of dictionary window failed\n");
+    }
+
+    memcpy(new_segs, dict->window.segs,
+           sizeof(WindowImageSegment) * dict->window.segs_quota);
+
+    // resetting the new elements
+    for (i = dict->window.segs_quota, seg = new_segs + i; i < new_quota; i++, seg++) {
+        seg->image = NULL;
+        seg->lines = NULL;
+        seg->lines_end = NULL;
+        seg->pixels_num = 0;
+        seg->pixels_so_far = 0;
+        seg->next = i + 1;
+    }
+    new_segs[new_quota - 1].next = dict->window.free_segs_head;
+    dict->window.free_segs_head = dict->window.segs_quota;
+
+    dict->cur_usr->free(dict->cur_usr, dict->window.segs);
+    dict->window.segs = new_segs;
+    dict->window.segs_quota = new_quota;
+
+    pthread_rwlock_unlock(&dict->rw_alloc_lock);
+}
+
+/* NOTE - it also updates the used_images_list*/
+static WindowImage *__glz_dictionary_window_alloc_image(SharedDictionary *dict)
+{
+    WindowImage *ret;
+
+    if (dict->window.free_images) {
+        ret = dict->window.free_images;
+        dict->window.free_images = ret->next;
+    } else {
+        if (!(ret = (WindowImage *)dict->cur_usr->malloc(dict->cur_usr,
+                                                         sizeof(*ret)))) {
+            return NULL;
+        }
+    }
+
+    ret->next = NULL;
+    if (dict->window.used_images_tail) {
+        dict->window.used_images_tail->next = ret;
+    }
+    dict->window.used_images_tail = ret;
+
+    if (!dict->window.used_images_head) {
+        dict->window.used_images_head = ret;
+    }
+    return ret;
+}
+
+/* NOTE - it doesn't update the used_segs list*/
+static uint32_t __glz_dictionary_window_alloc_image_seg(SharedDictionary *dict)
+{
+    uint32_t seg_id;
+    WindowImageSegment *seg;
+
+    // TODO: when is it best to realloc? when full or when half full?
+    if (dict->window.free_segs_head == NULL_IMAGE_SEG_ID) {
+        __glz_dictionary_window_segs_realloc(dict);
+    }
+
+    GLZ_ASSERT(dict->cur_usr, dict->window.free_segs_head != NULL_IMAGE_SEG_ID);
+
+    seg_id = dict->window.free_segs_head;
+    seg = dict->window.segs + seg_id;
+    dict->window.free_segs_head = seg->next;
+
+    return seg_id;
+}
+
+/* moves image to free list and "kill" it. Calls the free_image callback if was alive. */
+static inline void __glz_dictionary_window_free_image(SharedDictionary *dict, WindowImage *image)
+{
+    if (image->is_alive) {
+        dict->cur_usr->free_image(dict->cur_usr, image->usr_context);
+    }
+    image->is_alive = FALSE;
+    image->next = dict->window.free_images;
+    dict->window.free_images = image;
+}
+
+/* moves all the segments that were associated with the images to the free segments */
+static inline void __glz_dictionary_window_free_image_segs(SharedDictionary *dict,
+                                                           WindowImage *image)
+{
+    uint32_t old_free_head = dict->window.free_segs_head;
+    uint32_t seg_id, next_seg_id;
+
+    GLZ_ASSERT(dict->cur_usr, image->first_seg != NULL_IMAGE_SEG_ID);
+    dict->window.free_segs_head = image->first_seg;
+
+    // retrieving the last segment of the image
+    for (seg_id = image->first_seg, next_seg_id = dict->window.segs[seg_id].next;
+         (next_seg_id != NULL_IMAGE_SEG_ID) && (dict->window.segs[next_seg_id].image == image);
+         seg_id = next_seg_id, next_seg_id = dict->window.segs[seg_id].next) {
+    }
+
+    // concatenate the free list
+    dict->window.segs[seg_id].next = old_free_head;
+}
+
+/* Returns the logical head of the window after we add an image with the give size to its tail.
+   Returns NULL when the window is empty, of when we have to empty the window in order
+   to insert the new image. */
+static WindowImage *glz_dictionary_window_get_new_head(SharedDictionary *dict, int new_image_size)
+{
+    uint32_t cur_win_size;
+    WindowImage *cur_head;
+
+    if ((uint32_t)new_image_size > dict->window.size_limit) {
+        dict->cur_usr->error(dict->cur_usr, "image is bigger than window\n");
+    }
+
+    GLZ_ASSERT(dict->cur_usr, new_image_size < dict->window.size_limit)
+
+    // the window is empty
+    if (!dict->window.used_images_head) {
+        return NULL;
+    }
+
+    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_head != NULL_IMAGE_SEG_ID);
+    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_tail != NULL_IMAGE_SEG_ID);
+
+    // used_segs_head is the latest logical head (the physical head may preceed it)
+    cur_head = dict->window.segs[dict->window.used_segs_head].image;
+    cur_win_size = dict->window.segs[dict->window.used_segs_tail].pixels_num +
+        dict->window.segs[dict->window.used_segs_tail].pixels_so_far -
+        dict->window.segs[dict->window.used_segs_head].pixels_so_far;
+
+    while ((cur_win_size + new_image_size) > dict->window.size_limit) {
+        GLZ_ASSERT(dict->cur_usr, cur_head);
+        cur_win_size -= cur_head->size;
+        cur_head = cur_head->next;
+    }
+
+    return cur_head;
+}
+
+static inline int glz_dictionary_is_in_use(SharedDictionary *dict)
+{
+    uint32_t i = 0;
+    for (i = 0; i < dict->max_encoders; i++) {
+        if (dict->window.encoders_heads[i] != NULL_IMAGE_SEG_ID) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+/* remove from the window (and free relevant data) the images between the oldest physical head
+   (inclusive) and the end_image (exclusive). If end_image is NULL, empties the window*/
+static void glz_dictionary_window_remove_head(SharedDictionary *dict, uint32_t encoder_id,
+                                              WindowImage *end_image)
+{
+    // note that the segs list heads (one per encoder) may be different than the
+    // used_segs_head and it is updated somewhere else
+    while (dict->window.used_images_head != end_image) {
+        WindowImage *image = dict->window.used_images_head;
+
+        __glz_dictionary_window_free_image_segs(dict, image);
+        dict->window.used_images_head = image->next;
+        __glz_dictionary_window_free_image(dict, image);
+    }
+
+    if (!dict->window.used_images_head) {
+        dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
+        dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
+        dict->window.used_images_tail = NULL;
+    } else {
+        dict->window.used_segs_head = end_image->first_seg;
+    }
+}
+
+static uint32_t glz_dictionary_window_alloc_image_seg(SharedDictionary *dict, WindowImage* image,
+                                                      int size, int stride,
+                                                      uint8_t *lines, unsigned int num_lines)
+{
+    uint32_t seg_id = __glz_dictionary_window_alloc_image_seg(dict);
+    WindowImageSegment *seg = &dict->window.segs[seg_id];
+
+    seg->image = image;
+    seg->lines = lines;
+    seg->lines_end = lines + num_lines * stride;
+    seg->pixels_num = size;
+    seg->pixels_so_far = dict->window.pixels_so_far;
+    dict->window.pixels_so_far += seg->pixels_num;
+
+    seg->next = NULL_IMAGE_SEG_ID;
+
+    return seg_id;
+}
+
+static WindowImage *glz_dictionary_window_add_image(SharedDictionary *dict, LzImageType image_type,
+                                                    int image_size, int image_height,
+                                                    int image_stride, uint8_t *first_lines,
+                                                    unsigned int num_first_lines,
+                                                    GlzUsrImageContext *usr_image_context)
+{
+    unsigned int num_lines = num_first_lines;
+    unsigned int row;
+    uint32_t seg_id, prev_seg_id;
+    uint8_t* lines = first_lines;
+    // alloc image info,update used head tail,  if used_head null - update  head
+    WindowImage *image = __glz_dictionary_window_alloc_image(dict);
+    image->id = dict->last_image_id++;
+    image->size = image_size;
+    image->type = image_type;
+    image->usr_context = usr_image_context;
+
+    if (num_lines <= 0) {
+        num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
+        if (num_lines <= 0) {
+            dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
+        }
+    }
+
+    for (row = 0;;) {
+        seg_id = glz_dictionary_window_alloc_image_seg(dict, image,
+                                                       image_size * num_lines / image_height,
+                                                       image_stride,
+                                                       lines, num_lines);
+        if (row == 0) {
+            image->first_seg = seg_id;
+        } else {
+            dict->window.segs[prev_seg_id].next = seg_id;
+        }
+
+        row += num_lines;
+        if (row < (uint32_t)image_height) {
+            num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
+            if (num_lines <= 0) {
+                dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
+            }
+        } else {
+            break;
+        }
+        prev_seg_id = seg_id;
+    }
+
+    if (dict->window.used_segs_tail == NULL_IMAGE_SEG_ID) {
+        dict->window.used_segs_head = image->first_seg;
+        dict->window.used_segs_tail = seg_id;
+    } else {
+        int prev_tail = dict->window.used_segs_tail;
+
+        // The used segs may be in use by another thread which is during encoding
+        // (read-only use - when going over the segs of an image,
+        // see glz_encode_tmpl::compress).
+        // Thus, the 'next' field of the list's tail can be accessed only
+        // after all the new tail's data was set. Note that we are relying on
+        // an atomic assignment (32 bit variable).
+        // For the other thread that may read 'next' of the old tail, NULL_IMAGE_SEG_ID
+        // is equivalent to a segment with an image id that is different
+        // from the image id of the tail, so we don't need to further protect this field.
+        dict->window.segs[prev_tail].next = image->first_seg;
+        dict->window.used_segs_tail = seg_id;
+    }
+    image->is_alive = TRUE;
+
+    return image;
+}
+
+WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
+                                       SharedDictionary *dict, LzImageType image_type,
+                                       int image_width, int image_height, int image_stride,
+                                       uint8_t *first_lines, unsigned int num_first_lines,
+                                       GlzUsrImageContext *usr_image_context,
+                                       uint32_t *image_head_dist)
+{
+    WindowImage *new_win_head, *ret;
+    int image_size;
+
+
+    pthread_mutex_lock(&dict->lock);
+
+    dict->cur_usr = usr;
+    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] == NULL_IMAGE_SEG_ID);
+
+    image_size = __get_pixels_num(image_type, image_height, image_stride);
+    new_win_head = glz_dictionary_window_get_new_head(dict, image_size);
+
+    if (!glz_dictionary_is_in_use(dict)) {
+        glz_dictionary_window_remove_head(dict, encoder_id, new_win_head);
+    }
+
+    ret = glz_dictionary_window_add_image(dict, image_type, image_size, image_height, image_stride,
+                                          first_lines, num_first_lines, usr_image_context);
+
+    if (new_win_head) {
+        dict->window.encoders_heads[encoder_id] = new_win_head->first_seg;
+        *image_head_dist = (uint32_t)(ret->id - new_win_head->id); // shouldn't be greater than 32
+                                                                   // bit because the window size is
+                                                                   // limited to 2^25
+    } else {
+        dict->window.encoders_heads[encoder_id] = ret->first_seg;
+        *image_head_dist = 0;
+    }
+
+
+    // update encoders head  (the other heads were already updated)
+    pthread_mutex_unlock(&dict->lock);
+    pthread_rwlock_rdlock(&dict->rw_alloc_lock);
+    return ret;
+}
+
+void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
+                                SharedDictionary *dict)
+{
+    uint32_t i;
+    uint32_t early_head_seg = NULL_IMAGE_SEG_ID;
+    uint32_t this_encoder_head_seg;
+
+    pthread_rwlock_unlock(&dict->rw_alloc_lock);
+    pthread_mutex_lock(&dict->lock);
+    dict->cur_usr = usr;
+
+    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] != NULL_IMAGE_SEG_ID);
+    // get the earliest head in use (not including this encoder head)
+    for (i = 0; i < dict->max_encoders; i++) {
+        if (i != encoder_id) {
+            if (IMAGE_SEG_IS_EARLIER(dict, dict->window.encoders_heads[i], early_head_seg)) {
+                early_head_seg = dict->window.encoders_heads[i];
+            }
+        }
+    }
+
+    // possible only if early_head_seg == NULL
+    if (IMAGE_SEG_IS_EARLIER(dict, dict->window.used_segs_head, early_head_seg)) {
+        early_head_seg = dict->window.used_segs_head;
+    }
+
+    this_encoder_head_seg = dict->window.encoders_heads[encoder_id];
+
+    GLZ_ASSERT(dict->cur_usr, early_head_seg != NULL_IMAGE_SEG_ID);
+
+    if (IMAGE_SEG_IS_EARLIER(dict, this_encoder_head_seg, early_head_seg)) {
+        GLZ_ASSERT(dict->cur_usr,
+                   this_encoder_head_seg == dict->window.used_images_head->first_seg);
+        glz_dictionary_window_remove_head(dict, encoder_id,
+                                          dict->window.segs[early_head_seg].image);
+    }
+
+
+    dict->window.encoders_heads[encoder_id] = NULL_IMAGE_SEG_ID;
+    pthread_mutex_unlock(&dict->lock);
+}
diff --git a/server/glz-encoder-dict.h b/server/glz-encoder-dict.h
new file mode 100644
index 0000000..0a58b4b
--- /dev/null
+++ b/server/glz-encoder-dict.h
@@ -0,0 +1,71 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_GLZ_ENCODER_DICTIONARY
+#define _H_GLZ_ENCODER_DICTIONARY
+
+#include <stdint.h>
+
+/*
+    Interface for maintaining lz dictionary that is shared among several encoders.
+    The interface for accessing the dictionary for encoding purposes is located in
+    glz_encoder_dictionary_protected.h
+*/
+
+typedef void GlzEncDictContext;
+typedef void GlzEncDictImageContext;
+
+typedef void GlzUsrImageContext;
+typedef struct GlzEncoderUsrContext GlzEncoderUsrContext;
+
+/* NOTE: DISPLAY_MIGRATE_DATA_VERSION should change in case GlzEncDictRestoreData changes*/
+typedef struct GlzEncDictRestoreData {
+    uint32_t size;
+    uint32_t max_encoders;
+    uint64_t last_image_id;
+} GlzEncDictRestoreData;
+
+/* size        : maximal number of pixels occupying the window
+   max_encoders: maximal number of encoders that use the dictionary
+   usr         : callbacks */
+GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
+                                             GlzEncoderUsrContext *usr);
+
+void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
+
+/* returns the window capacity in pixels */
+uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *);
+
+/* returns the current state of the dictionary.
+   NOTE - you should use it only when no encoder uses the dictionary. */
+void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
+                                         GlzEncDictRestoreData *out_data,
+                                         GlzEncoderUsrContext *usr);
+
+/* creates a dictionary and initialized it by use the given info */
+GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
+                                              GlzEncoderUsrContext *usr);
+
+/*  NOTE - you should use this routine only when no encoder uses the dictionary. */
+void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
+
+/* image: the context returned by the encoder when the image was encoded.
+   NOTE - you should use this routine only when no encoder uses the dictionary.*/
+void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
+                                     GlzEncDictImageContext *image, GlzEncoderUsrContext *usr);
+
+#endif // _H_GLZ_ENCODER_DICTIONARY
diff --git a/server/glz-encoder-priv.h b/server/glz-encoder-priv.h
new file mode 100644
index 0000000..a14ec74
--- /dev/null
+++ b/server/glz-encoder-priv.h
@@ -0,0 +1,201 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_GLZ_ENCODER_PRIV
+#define _H_GLZ_ENCODER_PRIV
+
+#include "red_common.h"
+
+/* Interface for using the dictionary for encoding.
+   Data structures are exposed for the encoder for efficiency
+   purposes. */
+typedef struct WindowImage WindowImage;
+typedef struct WindowImageSegment WindowImageSegment;
+
+
+//#define CHAINED_HASH
+
+#ifdef CHAINED_HASH
+#define HASH_SIZE_LOG 16
+#define HASH_CHAIN_SIZE 4
+#else
+#define HASH_SIZE_LOG 20
+#define HASH_CHAIN_SIZE 1
+#endif
+
+#define HASH_SIZE (1 << HASH_SIZE_LOG)
+#define HASH_MASK (HASH_SIZE - 1)
+
+typedef struct HashEntry HashEntry;
+
+typedef struct SharedDictionary SharedDictionary;
+
+struct WindowImage {
+    uint64_t id;
+    LzImageType type;
+    int size;                    // in pixels
+    uint32_t first_seg;
+    GlzUsrImageContext  *usr_context;
+    WindowImage*       next;
+    uint8_t is_alive;
+};
+
+#define MAX_IMAGE_SEGS_NUM (0xffffffff)
+#define NULL_IMAGE_SEG_ID MAX_IMAGE_SEGS_NUM
+#define INIT_IMAGE_SEGS_NUM 1000
+
+/* Images can be separated into several chunks. The basic unit of the
+   dictionary window is one image segment. Each segment is encoded separately.
+   An encoded match can refer to only one segment.*/
+struct WindowImageSegment {
+    WindowImage     *image;
+    void            *lines;
+    void            *lines_end;
+    uint32_t pixels_num;            // Number of pixels in the segment
+    uint64_t pixels_so_far;         // Total no. pixels passed through the window till this segment.
+                                    // NOTE - never use size delta independently. It should
+                                    // always be used with respect to a previous size delta
+    uint32_t next;
+};
+
+
+struct  __attribute__ ((__packed__)) HashEntry {
+    uint32_t image_seg_idx;
+    uint32_t ref_pix_idx;
+};
+
+
+struct SharedDictionary {
+    struct {
+        /* The segments storage. A dynamic array.
+           By referring to a segment by its index, instead of address,
+           we save space in the hash entries (32bit instead of 64bit) */
+        WindowImageSegment  *segs;
+        uint32_t segs_quota;
+
+        /* The window is manged as a linked list rather than as a cyclic
+           array in order to keep the indices of the segments consistent
+           after reallocation */
+
+        /* the window in a resolution of image segments */
+        uint32_t used_segs_head;             // the latest head
+        uint32_t used_segs_tail;
+        uint32_t free_segs_head;
+
+        uint32_t            *encoders_heads; // Holds for each encoder (by id), the window head when
+                                             // it started the encoding.
+                                             // The head is NULL_IMAGE_SEG_ID when the encoder is
+                                             // not encoding.
+
+        /* the window in a resolution of images. But here the head contains the oldest head*/
+        WindowImage*        used_images_tail;
+        WindowImage*        used_images_head;
+        WindowImage*        free_images;
+
+        uint64_t pixels_so_far;
+        uint32_t size_limit;                 // max number of pixels in a window (per encoder)
+    } window;
+
+    /* Concurrency issues: the reading/writing of each entry field should be atomic.
+       It is allowed that the reading/writing of the whole entry won't be atomic,
+       since before we access a reference we check its validity*/
+#ifdef CHAINED_HASH
+    HashEntry htab[HASH_SIZE][HASH_CHAIN_SIZE];
+    uint8_t htab_counter[HASH_SIZE];  //cyclic counter for the next entry in a chain to be assigned
+#else
+    HashEntry htab[HASH_SIZE];
+#endif
+
+    uint64_t last_image_id;
+    uint32_t max_encoders;
+    pthread_mutex_t lock;
+    pthread_rwlock_t rw_alloc_lock;
+    GlzEncoderUsrContext       *cur_usr; // each encoder has other context.
+};
+
+/*
+    Add the image to the tail of the window.
+    If possible, release images from the head of the window.
+    Also perform concurrency related operations.
+
+    usr_image_context: when an image is released from the window due to capacity overflow,
+                       usr_image_context is given as a parameter to the free_image callback.
+
+    image_head_dist  : the number of images between the current image and the head of the
+                       window that is associated with the encoder.
+*/
+WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
+                                       SharedDictionary *dict, LzImageType image_type,
+                                       int image_width, int image_height, int image_stride,
+                                       uint8_t *first_lines, unsigned int num_first_lines,
+                                       GlzUsrImageContext *usr_image_context,
+                                       uint32_t *image_head_dist);
+
+/*
+   Performs concurrency related operations.
+   If possible, release images from the head of the window.
+*/
+void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
+                                SharedDictionary *dict);
+
+#define IMAGE_SEG_IS_EARLIER(dict, dst_seg, src_seg) (                     \
+    ((src_seg) == NULL_IMAGE_SEG_ID) || (((dst_seg) != NULL_IMAGE_SEG_ID)  \
+    && ((dict)->window.segs[(dst_seg)].pixels_so_far <                     \
+       (dict)->window.segs[(src_seg)].pixels_so_far)))
+
+
+#ifdef CHAINED_HASH
+#define UPDATE_HASH(dict, hval, seg, pix) {                \
+    uint8_t tmp_count = (dict)->htab_counter[hval];        \
+    (dict)->htab[hval][tmp_count].image_seg_idx = seg;     \
+    (dict)->htab[hval][tmp_count].ref_pix_idx = pix;       \
+    tmp_count = ((tmp_count) + 1) & (HASH_CHAIN_SIZE - 1); \
+    dict->htab_counter[hval] = tmp_count;                  \
+}
+#else
+#define UPDATE_HASH(dict, hval, seg, pix) { \
+    (dict)->htab[hval].image_seg_idx = seg; \
+    (dict)->htab[hval].ref_pix_idx = pix;   \
+}
+#endif
+
+/* checks if the reference segment is located in the range of the window
+   of the current encoder */
+#define REF_SEG_IS_VALID(dict, enc_id, ref_seg, src_seg) ( \
+    ((ref_seg) == (src_seg)) ||                            \
+    ((ref_seg)->image &&                                   \
+     (ref_seg)->image->is_alive &&                         \
+     (src_seg->image->type == ref_seg->image->type) &&     \
+     (ref_seg->pixels_so_far <= src_seg->pixels_so_far) && \
+     ((dict)->window.segs[                                 \
+        (dict)->window.encoders_heads[enc_id]].pixels_so_far <= \
+        ref_seg->pixels_so_far)))
+
+#ifdef DEBUG
+
+#define GLZ_ASSERT(usr, x)                                              \
+    if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
+
+#else
+
+#define GLZ_ASSERT(usr, x)
+
+#endif
+
+
+
+#endif // _H_GLZ_ENCODER_PRIV
diff --git a/server/glz-encoder.c b/server/glz-encoder.c
new file mode 100644
index 0000000..f761330
--- /dev/null
+++ b/server/glz-encoder.c
@@ -0,0 +1,311 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <pthread.h>
+#include <stdio.h>
+#include "glz-encoder.h"
+#include "glz-encoder-priv.h"
+
+
+/* Holds a specific data for one encoder, and data that is relevant for the current image encoded */
+typedef struct Encoder {
+    GlzEncoderUsrContext *usr;
+    uint8_t id;
+    SharedDictionary     *dict;
+
+    struct {
+        LzImageType type;
+        uint32_t id;
+        uint32_t first_win_seg;
+    } cur_image;
+
+    struct {
+        uint8_t            *start;
+        uint8_t            *now;
+        uint8_t            *end;
+        size_t bytes_count;
+        uint8_t            *last_copy;  // pointer to the last byte in which copy count was written
+    } io;
+} Encoder;
+
+
+/**************************************************************************
+* Handling writing the encoded image to the output buffer
+***************************************************************************/
+static inline int more_io_bytes(Encoder *encoder)
+{
+    uint8_t *io_ptr;
+    int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
+    encoder->io.bytes_count += num_io_bytes;
+    encoder->io.now = io_ptr;
+    encoder->io.end = encoder->io.now + num_io_bytes;
+    return num_io_bytes;
+}
+
+static inline void encode(Encoder *encoder, uint8_t byte)
+{
+    if (encoder->io.now == encoder->io.end) {
+        if (more_io_bytes(encoder) <= 0) {
+            encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
+        }
+        GLZ_ASSERT(encoder->usr, encoder->io.now);
+    }
+
+    GLZ_ASSERT(encoder->usr, encoder->io.now < encoder->io.end);
+    *(encoder->io.now++) = byte;
+}
+
+static inline void encode_32(Encoder *encoder, unsigned int word)
+{
+    encode(encoder, (uint8_t)(word >> 24));
+    encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
+    encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
+    encode(encoder, (uint8_t)(word & 0x0000ff));
+}
+
+static inline void encode_64(Encoder *encoder, uint64_t word)
+{
+    encode_32(encoder, (uint32_t)(word >> 32));
+    encode_32(encoder, (uint32_t)(word & 0xffffff));
+}
+
+static inline void encode_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+    encode(encoder, copy_count);
+    encoder->io.last_copy = encoder->io.now - 1; // io_now cannot be the first byte of the buffer
+}
+
+static inline void update_copy_count(Encoder *encoder, uint8_t copy_count)
+{
+    GLZ_ASSERT(encoder->usr, encoder->io.last_copy);
+    *(encoder->io.last_copy) = copy_count;
+}
+
+// decrease the io ptr by 1
+static inline void compress_output_prev(Encoder *encoder)
+{
+    // io_now cannot be the first byte of the buffer
+    encoder->io.now--;
+    // the function should be called only when copy count is written unnecessarily by glz_compress
+    GLZ_ASSERT(encoder->usr, encoder->io.now == encoder->io.last_copy)
+}
+
+static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
+{
+    GLZ_ASSERT(encoder->usr, io_ptr <= io_ptr_end);
+    encoder->io.bytes_count = io_ptr_end - io_ptr;
+    encoder->io.start = io_ptr;
+    encoder->io.now = io_ptr;
+    encoder->io.end = io_ptr_end;
+    encoder->io.last_copy = NULL;
+
+    return TRUE;
+}
+
+/**********************************************************
+*           Encoding
+***********************************************************/
+
+GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
+                                      GlzEncoderUsrContext *usr)
+{
+    Encoder *encoder;
+
+    if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
+        !usr->free || !usr->more_space) {
+        return NULL;
+    }
+
+    if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
+        return NULL;
+    }
+
+    encoder->id = id;
+    encoder->usr = usr;
+    encoder->dict = (SharedDictionary *)dictionary;
+
+    return (GlzEncoderContext *)encoder;
+}
+
+void glz_encoder_destroy(GlzEncoderContext *opaque_encoder)
+{
+    Encoder *encoder = (Encoder *)opaque_encoder;
+
+    if (!opaque_encoder) {
+        return;
+    }
+
+    encoder->usr->free(encoder->usr, encoder);
+}
+
+/*
+ * Give hints to the compiler for branch prediction optimization.
+ */
+#if defined(__GNUC__) && (__GNUC__ > 2)
+#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
+#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
+#else
+#define LZ_EXPECT_CONDITIONAL(c) (c)
+#define LZ_UNEXPECT_CONDITIONAL(c) (c)
+#endif
+
+
+typedef uint8_t BYTE;
+
+typedef struct __attribute__ ((__packed__)) one_byte_pixel_t {
+    BYTE a;
+} one_byte_pixel_t;
+
+typedef struct __attribute__ ((__packed__)) rgb32_pixel_t {
+    BYTE b;
+    BYTE g;
+    BYTE r;
+    BYTE pad;
+} rgb32_pixel_t;
+
+typedef struct __attribute__ ((__packed__)) rgb24_pixel_t {
+    BYTE b;
+    BYTE g;
+    BYTE r;
+} rgb24_pixel_t;
+
+typedef uint16_t rgb16_pixel_t;
+
+#define BOUND_OFFSET 2
+#define LIMIT_OFFSET 6
+#define MIN_FILE_SIZE 4
+
+#define MAX_PIXEL_SHORT_DISTANCE 4096       // (1 << 12)
+#define MAX_PIXEL_MEDIUM_DISTANCE 131072    // (1 << 17)  2 ^ (12 + 5)
+#define MAX_PIXEL_LONG_DISTANCE 33554432    // (1 << 25)  2 ^ (12 + 5 + 8)
+#define MAX_IMAGE_DIST 16777215             // (1 << 24 - 1)
+
+
+//#define DEBUG_ENCODE
+
+
+#define GLZ_ENCODE_SIZE
+#include "glz-encode-match.tmpl.c"
+#define GLZ_ENCODE_MATCH
+#include "glz-encode-match.tmpl.c"
+
+#define LZ_PLT
+#include "glz-encode.tmpl.c"
+
+#define LZ_RGB16
+#include "glz-encode.tmpl.c"
+
+#define LZ_RGB24
+#include "glz-encode.tmpl.c"
+
+#define LZ_RGB32
+#include "glz-encode.tmpl.c"
+
+#define LZ_RGB_ALPHA
+#include "glz-encode.tmpl.c"
+
+
+int glz_encode(GlzEncoderContext *opaque_encoder,
+               LzImageType type, int width, int height, int top_down,
+               uint8_t *lines, unsigned int num_lines, int stride,
+               uint8_t *io_ptr, unsigned int num_io_bytes,
+               GlzUsrImageContext *usr_context, GlzEncDictImageContext **o_enc_dict_context)
+{
+    Encoder *encoder = (Encoder *)opaque_encoder;
+    WindowImage *dict_image;
+    uint8_t *io_ptr_end = io_ptr + num_io_bytes;
+    uint32_t win_head_image_dist;
+
+    if (IS_IMAGE_TYPE_PLT[type]) {
+        if (stride > (width / PLT_PIXELS_PER_BYTE[type])) {
+            if (((width % PLT_PIXELS_PER_BYTE[type]) == 0) || (
+                    (stride - (width / PLT_PIXELS_PER_BYTE[type])) > 1)) {
+                encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
+            }
+        }
+    } else {
+        if (stride != width * RGB_BYTES_PER_PIXEL[type]) {
+            encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
+        }
+    }
+
+    // assign the output buffer
+    if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
+        encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
+    }
+
+    // first read the list of the image segments into the dictionary window
+    dict_image = glz_dictionary_pre_encode(encoder->id, encoder->usr,
+                                           encoder->dict, type, width, height, stride,
+                                           lines, num_lines, usr_context, &win_head_image_dist);
+    *o_enc_dict_context = (GlzEncDictImageContext *)dict_image;
+
+    encoder->cur_image.type = type;
+    encoder->cur_image.id = dict_image->id;
+    encoder->cur_image.first_win_seg = dict_image->first_seg;
+
+    encode_32(encoder, GUINT32_TO_LE(LZ_MAGIC));
+    encode_32(encoder, LZ_VERSION);
+    if (top_down) {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
+    } else {
+        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
+    }
+
+    encode_32(encoder, width);
+    encode_32(encoder, height);
+    encode_32(encoder, stride);
+    encode_64(encoder, dict_image->id);
+    encode_32(encoder, win_head_image_dist);
+
+    switch (encoder->cur_image.type) {
+    case LZ_IMAGE_TYPE_PLT1_BE:
+    case LZ_IMAGE_TYPE_PLT1_LE:
+    case LZ_IMAGE_TYPE_PLT4_BE:
+    case LZ_IMAGE_TYPE_PLT4_LE:
+    case LZ_IMAGE_TYPE_PLT8:
+        glz_plt_compress(encoder);
+        break;
+    case LZ_IMAGE_TYPE_RGB16:
+        glz_rgb16_compress(encoder);
+        break;
+    case LZ_IMAGE_TYPE_RGB24:
+        glz_rgb24_compress(encoder);
+        break;
+    case LZ_IMAGE_TYPE_RGB32:
+        glz_rgb32_compress(encoder);
+        break;
+    case LZ_IMAGE_TYPE_RGBA:
+        glz_rgb32_compress(encoder);
+        glz_rgb_alpha_compress(encoder);
+        break;
+    case LZ_IMAGE_TYPE_INVALID:
+    default:
+        encoder->usr->error(encoder->usr, "bad image type\n");
+    }
+
+    glz_dictionary_post_encode(encoder->id, encoder->usr, encoder->dict);
+
+    // move all the used segments to the free ones
+    encoder->io.bytes_count -= (encoder->io.end - encoder->io.now);
+
+    return encoder->io.bytes_count;
+}
diff --git a/server/glz-encoder.h b/server/glz-encoder.h
new file mode 100644
index 0000000..6de7513
--- /dev/null
+++ b/server/glz-encoder.h
@@ -0,0 +1,74 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_GLZ_ENCODER
+#define _H_GLZ_ENCODER
+
+/* Manging the lz encoding using a dictionary that is shared among encoders */
+
+#include "red_common.h"
+#include "common/lz_common.h"
+#include "glz-encoder-dict.h"
+
+struct GlzEncoderUsrContext {
+    SPICE_GNUC_PRINTF(2, 3) void (*error)(GlzEncoderUsrContext *usr, const char *fmt, ...);
+    SPICE_GNUC_PRINTF(2, 3) void (*warn)(GlzEncoderUsrContext *usr, const char *fmt, ...);
+    SPICE_GNUC_PRINTF(2, 3) void (*info)(GlzEncoderUsrContext *usr, const char *fmt, ...);
+    void    *(*malloc)(GlzEncoderUsrContext *usr, int size);
+    void (*free)(GlzEncoderUsrContext *usr, void *ptr);
+
+    // get the next chunk of the image which is entered to the dictionary. If the image is down to
+    // top, return it from the last line to the first one (stride should always be positive)
+    int (*more_lines)(GlzEncoderUsrContext *usr, uint8_t **lines);
+
+    // get the next chunk of the compressed buffer.return number of bytes in the chunk.
+    int (*more_space)(GlzEncoderUsrContext *usr, uint8_t **io_ptr);
+
+    // called when an image is removed from the dictionary, due to the window size limit
+    void (*free_image)(GlzEncoderUsrContext *usr, GlzUsrImageContext *image);
+
+};
+
+
+typedef void GlzEncoderContext;
+
+GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
+                                      GlzEncoderUsrContext *usr);
+
+void glz_encoder_destroy(GlzEncoderContext *opaque_encoder);
+
+/*
+        assumes width is in pixels and stride is in bytes
+    usr_context       : when an image is released from the window due to capacity overflow,
+                        usr_context is given as a parameter to the free_image callback.
+    o_enc_dict_context: if glz_enc_dictionary_remove_image is called, it should be
+                        called with the o_enc_dict_context that is associated with
+                        the image.
+
+        return: the number of bytes in the compressed data and sets o_enc_dict_context
+
+        NOTE  : currently supports only rgb images in which width*bytes_per_pixel = stride OR
+                palette images in which stride equals the min number of bytes to hold a line.
+                The stride should be > 0
+*/
+int glz_encode(GlzEncoderContext *opaque_encoder, LzImageType type, int width, int height,
+               int top_down, uint8_t *lines, unsigned int num_lines, int stride,
+               uint8_t *io_ptr, unsigned int num_io_bytes, GlzUsrImageContext *usr_context,
+               GlzEncDictImageContext **o_enc_dict_context);
+
+
+#endif // _H_GLZ_ENCODER
diff --git a/server/glz_encoder.c b/server/glz_encoder.c
deleted file mode 100644
index 65f4478..0000000
--- a/server/glz_encoder.c
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <glib.h>
-#include <pthread.h>
-#include <stdio.h>
-#include "glz_encoder.h"
-#include "glz_encoder_dictionary_protected.h"
-
-
-/* Holds a specific data for one encoder, and data that is relevant for the current image encoded */
-typedef struct Encoder {
-    GlzEncoderUsrContext *usr;
-    uint8_t id;
-    SharedDictionary     *dict;
-
-    struct {
-        LzImageType type;
-        uint32_t id;
-        uint32_t first_win_seg;
-    } cur_image;
-
-    struct {
-        uint8_t            *start;
-        uint8_t            *now;
-        uint8_t            *end;
-        size_t bytes_count;
-        uint8_t            *last_copy;  // pointer to the last byte in which copy count was written
-    } io;
-} Encoder;
-
-
-/**************************************************************************
-* Handling writing the encoded image to the output buffer
-***************************************************************************/
-static inline int more_io_bytes(Encoder *encoder)
-{
-    uint8_t *io_ptr;
-    int num_io_bytes = encoder->usr->more_space(encoder->usr, &io_ptr);
-    encoder->io.bytes_count += num_io_bytes;
-    encoder->io.now = io_ptr;
-    encoder->io.end = encoder->io.now + num_io_bytes;
-    return num_io_bytes;
-}
-
-static inline void encode(Encoder *encoder, uint8_t byte)
-{
-    if (encoder->io.now == encoder->io.end) {
-        if (more_io_bytes(encoder) <= 0) {
-            encoder->usr->error(encoder->usr, "%s: no more bytes\n", __FUNCTION__);
-        }
-        GLZ_ASSERT(encoder->usr, encoder->io.now);
-    }
-
-    GLZ_ASSERT(encoder->usr, encoder->io.now < encoder->io.end);
-    *(encoder->io.now++) = byte;
-}
-
-static inline void encode_32(Encoder *encoder, unsigned int word)
-{
-    encode(encoder, (uint8_t)(word >> 24));
-    encode(encoder, (uint8_t)(word >> 16) & 0x0000ff);
-    encode(encoder, (uint8_t)(word >> 8) & 0x0000ff);
-    encode(encoder, (uint8_t)(word & 0x0000ff));
-}
-
-static inline void encode_64(Encoder *encoder, uint64_t word)
-{
-    encode_32(encoder, (uint32_t)(word >> 32));
-    encode_32(encoder, (uint32_t)(word & 0xffffff));
-}
-
-static inline void encode_copy_count(Encoder *encoder, uint8_t copy_count)
-{
-    encode(encoder, copy_count);
-    encoder->io.last_copy = encoder->io.now - 1; // io_now cannot be the first byte of the buffer
-}
-
-static inline void update_copy_count(Encoder *encoder, uint8_t copy_count)
-{
-    GLZ_ASSERT(encoder->usr, encoder->io.last_copy);
-    *(encoder->io.last_copy) = copy_count;
-}
-
-// decrease the io ptr by 1
-static inline void compress_output_prev(Encoder *encoder)
-{
-    // io_now cannot be the first byte of the buffer
-    encoder->io.now--;
-    // the function should be called only when copy count is written unnecessarily by glz_compress
-    GLZ_ASSERT(encoder->usr, encoder->io.now == encoder->io.last_copy)
-}
-
-static int encoder_reset(Encoder *encoder, uint8_t *io_ptr, uint8_t *io_ptr_end)
-{
-    GLZ_ASSERT(encoder->usr, io_ptr <= io_ptr_end);
-    encoder->io.bytes_count = io_ptr_end - io_ptr;
-    encoder->io.start = io_ptr;
-    encoder->io.now = io_ptr;
-    encoder->io.end = io_ptr_end;
-    encoder->io.last_copy = NULL;
-
-    return TRUE;
-}
-
-/**********************************************************
-*           Encoding
-***********************************************************/
-
-GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
-                                      GlzEncoderUsrContext *usr)
-{
-    Encoder *encoder;
-
-    if (!usr || !usr->error || !usr->warn || !usr->info || !usr->malloc ||
-        !usr->free || !usr->more_space) {
-        return NULL;
-    }
-
-    if (!(encoder = (Encoder *)usr->malloc(usr, sizeof(Encoder)))) {
-        return NULL;
-    }
-
-    encoder->id = id;
-    encoder->usr = usr;
-    encoder->dict = (SharedDictionary *)dictionary;
-
-    return (GlzEncoderContext *)encoder;
-}
-
-void glz_encoder_destroy(GlzEncoderContext *opaque_encoder)
-{
-    Encoder *encoder = (Encoder *)opaque_encoder;
-
-    if (!opaque_encoder) {
-        return;
-    }
-
-    encoder->usr->free(encoder->usr, encoder);
-}
-
-/*
- * Give hints to the compiler for branch prediction optimization.
- */
-#if defined(__GNUC__) && (__GNUC__ > 2)
-#define LZ_EXPECT_CONDITIONAL(c) (__builtin_expect((c), 1))
-#define LZ_UNEXPECT_CONDITIONAL(c) (__builtin_expect((c), 0))
-#else
-#define LZ_EXPECT_CONDITIONAL(c) (c)
-#define LZ_UNEXPECT_CONDITIONAL(c) (c)
-#endif
-
-
-typedef uint8_t BYTE;
-
-typedef struct __attribute__ ((__packed__)) one_byte_pixel_t {
-    BYTE a;
-} one_byte_pixel_t;
-
-typedef struct __attribute__ ((__packed__)) rgb32_pixel_t {
-    BYTE b;
-    BYTE g;
-    BYTE r;
-    BYTE pad;
-} rgb32_pixel_t;
-
-typedef struct __attribute__ ((__packed__)) rgb24_pixel_t {
-    BYTE b;
-    BYTE g;
-    BYTE r;
-} rgb24_pixel_t;
-
-typedef uint16_t rgb16_pixel_t;
-
-#define BOUND_OFFSET 2
-#define LIMIT_OFFSET 6
-#define MIN_FILE_SIZE 4
-
-#define MAX_PIXEL_SHORT_DISTANCE 4096       // (1 << 12)
-#define MAX_PIXEL_MEDIUM_DISTANCE 131072    // (1 << 17)  2 ^ (12 + 5)
-#define MAX_PIXEL_LONG_DISTANCE 33554432    // (1 << 25)  2 ^ (12 + 5 + 8)
-#define MAX_IMAGE_DIST 16777215             // (1 << 24 - 1)
-
-
-//#define DEBUG_ENCODE
-
-
-#define GLZ_ENCODE_SIZE
-#include "glz-encode-match.tmpl.c"
-#define GLZ_ENCODE_MATCH
-#include "glz-encode-match.tmpl.c"
-
-#define LZ_PLT
-#include "glz-encode.tmpl.c"
-
-#define LZ_RGB16
-#include "glz-encode.tmpl.c"
-
-#define LZ_RGB24
-#include "glz-encode.tmpl.c"
-
-#define LZ_RGB32
-#include "glz-encode.tmpl.c"
-
-#define LZ_RGB_ALPHA
-#include "glz-encode.tmpl.c"
-
-
-int glz_encode(GlzEncoderContext *opaque_encoder,
-               LzImageType type, int width, int height, int top_down,
-               uint8_t *lines, unsigned int num_lines, int stride,
-               uint8_t *io_ptr, unsigned int num_io_bytes,
-               GlzUsrImageContext *usr_context, GlzEncDictImageContext **o_enc_dict_context)
-{
-    Encoder *encoder = (Encoder *)opaque_encoder;
-    WindowImage *dict_image;
-    uint8_t *io_ptr_end = io_ptr + num_io_bytes;
-    uint32_t win_head_image_dist;
-
-    if (IS_IMAGE_TYPE_PLT[type]) {
-        if (stride > (width / PLT_PIXELS_PER_BYTE[type])) {
-            if (((width % PLT_PIXELS_PER_BYTE[type]) == 0) || (
-                    (stride - (width / PLT_PIXELS_PER_BYTE[type])) > 1)) {
-                encoder->usr->error(encoder->usr, "stride overflows (plt)\n");
-            }
-        }
-    } else {
-        if (stride != width * RGB_BYTES_PER_PIXEL[type]) {
-            encoder->usr->error(encoder->usr, "stride != width*bytes_per_pixel (rgb)\n");
-        }
-    }
-
-    // assign the output buffer
-    if (!encoder_reset(encoder, io_ptr, io_ptr_end)) {
-        encoder->usr->error(encoder->usr, "lz encoder io reset failed\n");
-    }
-
-    // first read the list of the image segments into the dictionary window
-    dict_image = glz_dictionary_pre_encode(encoder->id, encoder->usr,
-                                           encoder->dict, type, width, height, stride,
-                                           lines, num_lines, usr_context, &win_head_image_dist);
-    *o_enc_dict_context = (GlzEncDictImageContext *)dict_image;
-
-    encoder->cur_image.type = type;
-    encoder->cur_image.id = dict_image->id;
-    encoder->cur_image.first_win_seg = dict_image->first_seg;
-
-    encode_32(encoder, GUINT32_TO_LE(LZ_MAGIC));
-    encode_32(encoder, LZ_VERSION);
-    if (top_down) {
-        encode(encoder, (type & LZ_IMAGE_TYPE_MASK) | (1 << LZ_IMAGE_TYPE_LOG));
-    } else {
-        encode(encoder, (type & LZ_IMAGE_TYPE_MASK));
-    }
-
-    encode_32(encoder, width);
-    encode_32(encoder, height);
-    encode_32(encoder, stride);
-    encode_64(encoder, dict_image->id);
-    encode_32(encoder, win_head_image_dist);
-
-    switch (encoder->cur_image.type) {
-    case LZ_IMAGE_TYPE_PLT1_BE:
-    case LZ_IMAGE_TYPE_PLT1_LE:
-    case LZ_IMAGE_TYPE_PLT4_BE:
-    case LZ_IMAGE_TYPE_PLT4_LE:
-    case LZ_IMAGE_TYPE_PLT8:
-        glz_plt_compress(encoder);
-        break;
-    case LZ_IMAGE_TYPE_RGB16:
-        glz_rgb16_compress(encoder);
-        break;
-    case LZ_IMAGE_TYPE_RGB24:
-        glz_rgb24_compress(encoder);
-        break;
-    case LZ_IMAGE_TYPE_RGB32:
-        glz_rgb32_compress(encoder);
-        break;
-    case LZ_IMAGE_TYPE_RGBA:
-        glz_rgb32_compress(encoder);
-        glz_rgb_alpha_compress(encoder);
-        break;
-    case LZ_IMAGE_TYPE_INVALID:
-    default:
-        encoder->usr->error(encoder->usr, "bad image type\n");
-    }
-
-    glz_dictionary_post_encode(encoder->id, encoder->usr, encoder->dict);
-
-    // move all the used segments to the free ones
-    encoder->io.bytes_count -= (encoder->io.end - encoder->io.now);
-
-    return encoder->io.bytes_count;
-}
diff --git a/server/glz_encoder.h b/server/glz_encoder.h
deleted file mode 100644
index e91f515..0000000
--- a/server/glz_encoder.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_GLZ_ENCODER
-#define _H_GLZ_ENCODER
-
-/* Manging the lz encoding using a dictionary that is shared among encoders */
-
-#include <stdint.h>
-#include "common/lz_common.h"
-#include "glz_encoder_dictionary.h"
-#include "glz_encoder_config.h"
-
-typedef void GlzEncoderContext;
-
-GlzEncoderContext *glz_encoder_create(uint8_t id, GlzEncDictContext *dictionary,
-                                      GlzEncoderUsrContext *usr);
-
-void glz_encoder_destroy(GlzEncoderContext *opaque_encoder);
-
-/*
-        assumes width is in pixels and stride is in bytes
-    usr_context       : when an image is released from the window due to capacity overflow,
-                        usr_context is given as a parameter to the free_image callback.
-    o_enc_dict_context: if glz_enc_dictionary_remove_image is called, it should be
-                        called with the o_enc_dict_context that is associated with
-                        the image.
-
-        return: the number of bytes in the compressed data and sets o_enc_dict_context
-
-        NOTE  : currently supports only rgb images in which width*bytes_per_pixel = stride OR
-                palette images in which stride equals the min number of bytes to hold a line.
-                The stride should be > 0
-*/
-int glz_encode(GlzEncoderContext *opaque_encoder, LzImageType type, int width, int height,
-               int top_down, uint8_t *lines, unsigned int num_lines, int stride,
-               uint8_t *io_ptr, unsigned int num_io_bytes, GlzUsrImageContext *usr_context,
-               GlzEncDictImageContext **o_enc_dict_context);
-
-
-#endif // _H_GLZ_ENCODER
diff --git a/server/glz_encoder_config.h b/server/glz_encoder_config.h
deleted file mode 100644
index 6472668..0000000
--- a/server/glz_encoder_config.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_GLZ_ENCODER_CONFIG
-#define _H_GLZ_ENCODER_CONFIG
-
-#include <spice/macros.h>
-#include "common/lz_common.h"
-
-typedef void GlzUsrImageContext;
-typedef struct GlzEncoderUsrContext GlzEncoderUsrContext;
-
-struct GlzEncoderUsrContext {
-    SPICE_GNUC_PRINTF(2, 3) void (*error)(GlzEncoderUsrContext *usr, const char *fmt, ...);
-    SPICE_GNUC_PRINTF(2, 3) void (*warn)(GlzEncoderUsrContext *usr, const char *fmt, ...);
-    SPICE_GNUC_PRINTF(2, 3) void (*info)(GlzEncoderUsrContext *usr, const char *fmt, ...);
-    void    *(*malloc)(GlzEncoderUsrContext *usr, int size);
-    void (*free)(GlzEncoderUsrContext *usr, void *ptr);
-
-    // get the next chunk of the image which is entered to the dictionary. If the image is down to
-    // top, return it from the last line to the first one (stride should always be positive)
-    int (*more_lines)(GlzEncoderUsrContext *usr, uint8_t **lines);
-
-    // get the next chunk of the compressed buffer.return number of bytes in the chunk.
-    int (*more_space)(GlzEncoderUsrContext *usr, uint8_t **io_ptr);
-
-    // called when an image is removed from the dictionary, due to the window size limit
-    void (*free_image)(GlzEncoderUsrContext *usr, GlzUsrImageContext *image);
-
-};
-
-
-#ifdef DEBUG
-
-#define GLZ_ASSERT(usr, x) \
-    if (!(x)) (usr)->error(usr, "%s: ASSERT %s failed\n", __FUNCTION__, #x);
-
-#else
-
-#define GLZ_ASSERT(usr, x)
-
-#endif
-
-
-#endif
diff --git a/server/glz_encoder_dictionary.c b/server/glz_encoder_dictionary.c
deleted file mode 100644
index 70226e1..0000000
--- a/server/glz_encoder_dictionary.c
+++ /dev/null
@@ -1,633 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <pthread.h>
-#include <string.h>
-#include <stdio.h>
-
-#include "glz_encoder_dictionary.h"
-#include "glz_encoder_dictionary_protected.h"
-
-/* turning all used images to free ones. If they are alive, calling the free_image callback for
-   each one */
-static inline void __glz_dictionary_window_reset_images(SharedDictionary *dict)
-{
-    WindowImage *tmp;
-
-    while (dict->window.used_images_head) {
-        tmp = dict->window.used_images_head;
-        dict->window.used_images_head = dict->window.used_images_head->next;
-        if (tmp->is_alive) {
-            dict->cur_usr->free_image(dict->cur_usr, tmp->usr_context);
-        }
-        tmp->next = dict->window.free_images;
-        tmp->is_alive = FALSE;
-        dict->window.free_images = tmp;
-    }
-    dict->window.used_images_tail = NULL;
-}
-
-/* allocate window fields (no reset)*/
-static int glz_dictionary_window_create(SharedDictionary *dict, uint32_t size)
-{
-    if (size > LZ_MAX_WINDOW_SIZE) {
-        return FALSE;
-    }
-
-    dict->window.size_limit = size;
-    dict->window.segs = (WindowImageSegment *)(
-            dict->cur_usr->malloc(dict->cur_usr, sizeof(WindowImageSegment) * INIT_IMAGE_SEGS_NUM));
-
-    if (!dict->window.segs) {
-        return FALSE;
-    }
-
-    dict->window.segs_quota = INIT_IMAGE_SEGS_NUM;
-
-    dict->window.encoders_heads = (uint32_t *)dict->cur_usr->malloc(dict->cur_usr,
-                                                            sizeof(uint32_t) * dict->max_encoders);
-
-    if (!dict->window.encoders_heads) {
-        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
-        return FALSE;
-    }
-
-    dict->window.used_images_head = NULL;
-    dict->window.used_images_tail = NULL;
-    dict->window.free_images = NULL;
-    dict->window.pixels_so_far = 0;
-
-    return TRUE;
-}
-
-/* initializes an empty window (segs and encoder_heads should be pre allocated.
-   resets the image infos, and calls the free_image usr callback*/
-static void glz_dictionary_window_reset(SharedDictionary *dict)
-{
-    uint32_t i;
-    WindowImageSegment *seg, *last_seg;
-
-    last_seg = dict->window.segs + dict->window.segs_quota;
-    /* reset free segs list */
-    dict->window.free_segs_head = 0;
-    for (seg = dict->window.segs, i = 0; seg < last_seg; seg++, i++) {
-        seg->next = i + 1;
-        seg->image = NULL;
-        seg->lines = NULL;
-        seg->lines_end = NULL;
-        seg->pixels_num = 0;
-        seg->pixels_so_far = 0;
-    }
-    dict->window.segs[dict->window.segs_quota - 1].next = NULL_IMAGE_SEG_ID;
-
-    dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
-    dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
-
-    // reset encoders heads
-    for (i = 0; i < dict->max_encoders; i++) {
-        dict->window.encoders_heads[i] = NULL_IMAGE_SEG_ID;
-    }
-
-    __glz_dictionary_window_reset_images(dict);
-}
-
-static inline void glz_dictionary_reset_hash(SharedDictionary *dict)
-{
-    memset(dict->htab, 0, sizeof(HashEntry) * HASH_SIZE * HASH_CHAIN_SIZE);
-#ifdef CHAINED_HASH
-    memset(dict->htab_counter, 0, HASH_SIZE * sizeof(uint8_t));
-#endif
-}
-
-static inline void glz_dictionary_window_destroy(SharedDictionary *dict)
-{
-    __glz_dictionary_window_reset_images(dict);
-
-    if (dict->window.segs) {
-        dict->cur_usr->free(dict->cur_usr, dict->window.segs);
-        dict->window.segs = NULL;
-    }
-
-    while (dict->window.free_images) {
-        WindowImage *tmp = dict->window.free_images;
-        dict->window.free_images = tmp->next;
-
-        dict->cur_usr->free(dict->cur_usr, tmp);
-    }
-
-    if (dict->window.encoders_heads) {
-        dict->cur_usr->free(dict->cur_usr, dict->window.encoders_heads);
-        dict->window.encoders_heads = NULL;
-    }
-}
-
-/* logic removal only */
-static inline void glz_dictionary_window_kill_image(SharedDictionary *dict, WindowImage *image)
-{
-    image->is_alive = FALSE;
-}
-
-GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
-                                             GlzEncoderUsrContext *usr)
-{
-    SharedDictionary *dict;
-
-    if (!(dict = (SharedDictionary *)usr->malloc(usr,
-                                                 sizeof(SharedDictionary)))) {
-        return NULL;
-    }
-
-    dict->cur_usr = usr;
-    dict->last_image_id = 0;
-    dict->max_encoders = max_encoders;
-
-    pthread_mutex_init(&dict->lock, NULL);
-    pthread_rwlock_init(&dict->rw_alloc_lock, NULL);
-
-    dict->window.encoders_heads = NULL;
-
-    // alloc window fields and reset
-    if (!glz_dictionary_window_create(dict, size)) {
-        dict->cur_usr->free(usr, dict);
-        return NULL;
-    }
-
-    // reset window and hash
-    glz_enc_dictionary_reset((GlzEncDictContext *)dict, usr);
-
-    return (GlzEncDictContext *)dict;
-}
-
-void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
-                                         GlzEncDictRestoreData *out_data, GlzEncoderUsrContext *usr)
-{
-    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
-    dict->cur_usr = usr;
-    GLZ_ASSERT(dict->cur_usr, opaque_dict);
-    GLZ_ASSERT(dict->cur_usr, out_data);
-
-    out_data->last_image_id = dict->last_image_id;
-    out_data->max_encoders = dict->max_encoders;
-    out_data->size = dict->window.size_limit;
-}
-
-GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
-                                              GlzEncoderUsrContext *usr)
-{
-    if (!restore_data) {
-        return NULL;
-    }
-    SharedDictionary *ret = (SharedDictionary *)glz_enc_dictionary_create(
-            restore_data->size, restore_data->max_encoders, usr);
-    ret->last_image_id = restore_data->last_image_id;
-    return ((GlzEncDictContext *)ret);
-}
-
-void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
-{
-    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
-    dict->cur_usr = usr;
-    GLZ_ASSERT(dict->cur_usr, opaque_dict);
-
-    dict->last_image_id = 0;
-    glz_dictionary_window_reset(dict);
-    glz_dictionary_reset_hash(dict);
-}
-
-void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr)
-{
-    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
-
-    if (!opaque_dict) {
-        return;
-    }
-
-    dict->cur_usr = usr;
-    glz_dictionary_window_destroy(dict);
-
-    pthread_mutex_destroy(&dict->lock);
-    pthread_rwlock_destroy(&dict->rw_alloc_lock);
-
-    dict->cur_usr->free(dict->cur_usr, dict);
-}
-
-uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *opaque_dict)
-{
-    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
-
-    if (!opaque_dict) {
-        return 0;
-    }
-    return dict->window.size_limit;
-}
-
-/* doesn't call the remove image callback */
-void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
-                                     GlzEncDictImageContext *opaque_image,
-                                     GlzEncoderUsrContext *usr)
-{
-    SharedDictionary *dict = (SharedDictionary *)opaque_dict;
-    WindowImage *image = (WindowImage *)opaque_image;
-    dict->cur_usr = usr;
-    GLZ_ASSERT(dict->cur_usr, opaque_image && opaque_dict);
-
-    glz_dictionary_window_kill_image(dict, image);
-}
-
-/***********************************************************************************
- Mutators of the window. Should be called by the encoder before and after encoding.
- ***********************************************************************************/
-
-static inline int __get_pixels_num(LzImageType image_type, unsigned int num_lines, int stride)
-{
-    if (IS_IMAGE_TYPE_RGB[image_type]) {
-        return num_lines * stride / RGB_BYTES_PER_PIXEL[image_type];
-    } else {
-        return num_lines * stride * PLT_PIXELS_PER_BYTE[image_type];
-    }
-}
-
-static void __glz_dictionary_window_segs_realloc(SharedDictionary *dict)
-{
-    WindowImageSegment *new_segs;
-    uint32_t new_quota = (MAX_IMAGE_SEGS_NUM < (dict->window.segs_quota * 2)) ?
-        MAX_IMAGE_SEGS_NUM : (dict->window.segs_quota * 2);
-    WindowImageSegment *seg;
-    uint32_t i;
-
-    pthread_rwlock_wrlock(&dict->rw_alloc_lock);
-
-    if (dict->window.segs_quota == MAX_IMAGE_SEGS_NUM) {
-        dict->cur_usr->error(dict->cur_usr, "overflow in image segments window\n");
-    }
-
-    new_segs = (WindowImageSegment*)dict->cur_usr->malloc(
-            dict->cur_usr, sizeof(WindowImageSegment) * new_quota);
-
-    if (!new_segs) {
-        dict->cur_usr->error(dict->cur_usr,
-                             "realloc of dictionary window failed\n");
-    }
-
-    memcpy(new_segs, dict->window.segs,
-           sizeof(WindowImageSegment) * dict->window.segs_quota);
-
-    // resetting the new elements
-    for (i = dict->window.segs_quota, seg = new_segs + i; i < new_quota; i++, seg++) {
-        seg->image = NULL;
-        seg->lines = NULL;
-        seg->lines_end = NULL;
-        seg->pixels_num = 0;
-        seg->pixels_so_far = 0;
-        seg->next = i + 1;
-    }
-    new_segs[new_quota - 1].next = dict->window.free_segs_head;
-    dict->window.free_segs_head = dict->window.segs_quota;
-
-    dict->cur_usr->free(dict->cur_usr, dict->window.segs);
-    dict->window.segs = new_segs;
-    dict->window.segs_quota = new_quota;
-
-    pthread_rwlock_unlock(&dict->rw_alloc_lock);
-}
-
-/* NOTE - it also updates the used_images_list*/
-static WindowImage *__glz_dictionary_window_alloc_image(SharedDictionary *dict)
-{
-    WindowImage *ret;
-
-    if (dict->window.free_images) {
-        ret = dict->window.free_images;
-        dict->window.free_images = ret->next;
-    } else {
-        if (!(ret = (WindowImage *)dict->cur_usr->malloc(dict->cur_usr,
-                                                         sizeof(*ret)))) {
-            return NULL;
-        }
-    }
-
-    ret->next = NULL;
-    if (dict->window.used_images_tail) {
-        dict->window.used_images_tail->next = ret;
-    }
-    dict->window.used_images_tail = ret;
-
-    if (!dict->window.used_images_head) {
-        dict->window.used_images_head = ret;
-    }
-    return ret;
-}
-
-/* NOTE - it doesn't update the used_segs list*/
-static uint32_t __glz_dictionary_window_alloc_image_seg(SharedDictionary *dict)
-{
-    uint32_t seg_id;
-    WindowImageSegment *seg;
-
-    // TODO: when is it best to realloc? when full or when half full?
-    if (dict->window.free_segs_head == NULL_IMAGE_SEG_ID) {
-        __glz_dictionary_window_segs_realloc(dict);
-    }
-
-    GLZ_ASSERT(dict->cur_usr, dict->window.free_segs_head != NULL_IMAGE_SEG_ID);
-
-    seg_id = dict->window.free_segs_head;
-    seg = dict->window.segs + seg_id;
-    dict->window.free_segs_head = seg->next;
-
-    return seg_id;
-}
-
-/* moves image to free list and "kill" it. Calls the free_image callback if was alive. */
-static inline void __glz_dictionary_window_free_image(SharedDictionary *dict, WindowImage *image)
-{
-    if (image->is_alive) {
-        dict->cur_usr->free_image(dict->cur_usr, image->usr_context);
-    }
-    image->is_alive = FALSE;
-    image->next = dict->window.free_images;
-    dict->window.free_images = image;
-}
-
-/* moves all the segments that were associated with the images to the free segments */
-static inline void __glz_dictionary_window_free_image_segs(SharedDictionary *dict,
-                                                           WindowImage *image)
-{
-    uint32_t old_free_head = dict->window.free_segs_head;
-    uint32_t seg_id, next_seg_id;
-
-    GLZ_ASSERT(dict->cur_usr, image->first_seg != NULL_IMAGE_SEG_ID);
-    dict->window.free_segs_head = image->first_seg;
-
-    // retrieving the last segment of the image
-    for (seg_id = image->first_seg, next_seg_id = dict->window.segs[seg_id].next;
-         (next_seg_id != NULL_IMAGE_SEG_ID) && (dict->window.segs[next_seg_id].image == image);
-         seg_id = next_seg_id, next_seg_id = dict->window.segs[seg_id].next) {
-    }
-
-    // concatenate the free list
-    dict->window.segs[seg_id].next = old_free_head;
-}
-
-/* Returns the logical head of the window after we add an image with the give size to its tail.
-   Returns NULL when the window is empty, of when we have to empty the window in order
-   to insert the new image. */
-static WindowImage *glz_dictionary_window_get_new_head(SharedDictionary *dict, int new_image_size)
-{
-    uint32_t cur_win_size;
-    WindowImage *cur_head;
-
-    if ((uint32_t)new_image_size > dict->window.size_limit) {
-        dict->cur_usr->error(dict->cur_usr, "image is bigger than window\n");
-    }
-
-    GLZ_ASSERT(dict->cur_usr, new_image_size < dict->window.size_limit)
-
-    // the window is empty
-    if (!dict->window.used_images_head) {
-        return NULL;
-    }
-
-    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_head != NULL_IMAGE_SEG_ID);
-    GLZ_ASSERT(dict->cur_usr, dict->window.used_segs_tail != NULL_IMAGE_SEG_ID);
-
-    // used_segs_head is the latest logical head (the physical head may preceed it)
-    cur_head = dict->window.segs[dict->window.used_segs_head].image;
-    cur_win_size = dict->window.segs[dict->window.used_segs_tail].pixels_num +
-        dict->window.segs[dict->window.used_segs_tail].pixels_so_far -
-        dict->window.segs[dict->window.used_segs_head].pixels_so_far;
-
-    while ((cur_win_size + new_image_size) > dict->window.size_limit) {
-        GLZ_ASSERT(dict->cur_usr, cur_head);
-        cur_win_size -= cur_head->size;
-        cur_head = cur_head->next;
-    }
-
-    return cur_head;
-}
-
-static inline int glz_dictionary_is_in_use(SharedDictionary *dict)
-{
-    uint32_t i = 0;
-    for (i = 0; i < dict->max_encoders; i++) {
-        if (dict->window.encoders_heads[i] != NULL_IMAGE_SEG_ID) {
-            return TRUE;
-        }
-    }
-    return FALSE;
-}
-
-/* remove from the window (and free relevant data) the images between the oldest physical head
-   (inclusive) and the end_image (exclusive). If end_image is NULL, empties the window*/
-static void glz_dictionary_window_remove_head(SharedDictionary *dict, uint32_t encoder_id,
-                                              WindowImage *end_image)
-{
-    // note that the segs list heads (one per encoder) may be different than the
-    // used_segs_head and it is updated somewhere else
-    while (dict->window.used_images_head != end_image) {
-        WindowImage *image = dict->window.used_images_head;
-
-        __glz_dictionary_window_free_image_segs(dict, image);
-        dict->window.used_images_head = image->next;
-        __glz_dictionary_window_free_image(dict, image);
-    }
-
-    if (!dict->window.used_images_head) {
-        dict->window.used_segs_head = NULL_IMAGE_SEG_ID;
-        dict->window.used_segs_tail = NULL_IMAGE_SEG_ID;
-        dict->window.used_images_tail = NULL;
-    } else {
-        dict->window.used_segs_head = end_image->first_seg;
-    }
-}
-
-static uint32_t glz_dictionary_window_alloc_image_seg(SharedDictionary *dict, WindowImage* image,
-                                                      int size, int stride,
-                                                      uint8_t *lines, unsigned int num_lines)
-{
-    uint32_t seg_id = __glz_dictionary_window_alloc_image_seg(dict);
-    WindowImageSegment *seg = &dict->window.segs[seg_id];
-
-    seg->image = image;
-    seg->lines = lines;
-    seg->lines_end = lines + num_lines * stride;
-    seg->pixels_num = size;
-    seg->pixels_so_far = dict->window.pixels_so_far;
-    dict->window.pixels_so_far += seg->pixels_num;
-
-    seg->next = NULL_IMAGE_SEG_ID;
-
-    return seg_id;
-}
-
-static WindowImage *glz_dictionary_window_add_image(SharedDictionary *dict, LzImageType image_type,
-                                                    int image_size, int image_height,
-                                                    int image_stride, uint8_t *first_lines,
-                                                    unsigned int num_first_lines,
-                                                    GlzUsrImageContext *usr_image_context)
-{
-    unsigned int num_lines = num_first_lines;
-    unsigned int row;
-    uint32_t seg_id, prev_seg_id;
-    uint8_t* lines = first_lines;
-    // alloc image info,update used head tail,  if used_head null - update  head
-    WindowImage *image = __glz_dictionary_window_alloc_image(dict);
-    image->id = dict->last_image_id++;
-    image->size = image_size;
-    image->type = image_type;
-    image->usr_context = usr_image_context;
-
-    if (num_lines <= 0) {
-        num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
-        if (num_lines <= 0) {
-            dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
-        }
-    }
-
-    for (row = 0;;) {
-        seg_id = glz_dictionary_window_alloc_image_seg(dict, image,
-                                                       image_size * num_lines / image_height,
-                                                       image_stride,
-                                                       lines, num_lines);
-        if (row == 0) {
-            image->first_seg = seg_id;
-        } else {
-            dict->window.segs[prev_seg_id].next = seg_id;
-        }
-
-        row += num_lines;
-        if (row < (uint32_t)image_height) {
-            num_lines = dict->cur_usr->more_lines(dict->cur_usr, &lines);
-            if (num_lines <= 0) {
-                dict->cur_usr->error(dict->cur_usr, "more lines failed\n");
-            }
-        } else {
-            break;
-        }
-        prev_seg_id = seg_id;
-    }
-
-    if (dict->window.used_segs_tail == NULL_IMAGE_SEG_ID) {
-        dict->window.used_segs_head = image->first_seg;
-        dict->window.used_segs_tail = seg_id;
-    } else {
-        int prev_tail = dict->window.used_segs_tail;
-
-        // The used segs may be in use by another thread which is during encoding
-        // (read-only use - when going over the segs of an image,
-        // see glz_encode_tmpl::compress).
-        // Thus, the 'next' field of the list's tail can be accessed only
-        // after all the new tail's data was set. Note that we are relying on
-        // an atomic assignment (32 bit variable).
-        // For the other thread that may read 'next' of the old tail, NULL_IMAGE_SEG_ID
-        // is equivalent to a segment with an image id that is different
-        // from the image id of the tail, so we don't need to further protect this field.
-        dict->window.segs[prev_tail].next = image->first_seg;
-        dict->window.used_segs_tail = seg_id;
-    }
-    image->is_alive = TRUE;
-
-    return image;
-}
-
-WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
-                                       SharedDictionary *dict, LzImageType image_type,
-                                       int image_width, int image_height, int image_stride,
-                                       uint8_t *first_lines, unsigned int num_first_lines,
-                                       GlzUsrImageContext *usr_image_context,
-                                       uint32_t *image_head_dist)
-{
-    WindowImage *new_win_head, *ret;
-    int image_size;
-
-
-    pthread_mutex_lock(&dict->lock);
-
-    dict->cur_usr = usr;
-    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] == NULL_IMAGE_SEG_ID);
-
-    image_size = __get_pixels_num(image_type, image_height, image_stride);
-    new_win_head = glz_dictionary_window_get_new_head(dict, image_size);
-
-    if (!glz_dictionary_is_in_use(dict)) {
-        glz_dictionary_window_remove_head(dict, encoder_id, new_win_head);
-    }
-
-    ret = glz_dictionary_window_add_image(dict, image_type, image_size, image_height, image_stride,
-                                          first_lines, num_first_lines, usr_image_context);
-
-    if (new_win_head) {
-        dict->window.encoders_heads[encoder_id] = new_win_head->first_seg;
-        *image_head_dist = (uint32_t)(ret->id - new_win_head->id); // shouldn't be greater than 32
-                                                                   // bit because the window size is
-                                                                   // limited to 2^25
-    } else {
-        dict->window.encoders_heads[encoder_id] = ret->first_seg;
-        *image_head_dist = 0;
-    }
-
-
-    // update encoders head  (the other heads were already updated)
-    pthread_mutex_unlock(&dict->lock);
-    pthread_rwlock_rdlock(&dict->rw_alloc_lock);
-    return ret;
-}
-
-void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
-                                SharedDictionary *dict)
-{
-    uint32_t i;
-    uint32_t early_head_seg = NULL_IMAGE_SEG_ID;
-    uint32_t this_encoder_head_seg;
-
-    pthread_rwlock_unlock(&dict->rw_alloc_lock);
-    pthread_mutex_lock(&dict->lock);
-    dict->cur_usr = usr;
-
-    GLZ_ASSERT(dict->cur_usr, dict->window.encoders_heads[encoder_id] != NULL_IMAGE_SEG_ID);
-    // get the earliest head in use (not including this encoder head)
-    for (i = 0; i < dict->max_encoders; i++) {
-        if (i != encoder_id) {
-            if (IMAGE_SEG_IS_EARLIER(dict, dict->window.encoders_heads[i], early_head_seg)) {
-                early_head_seg = dict->window.encoders_heads[i];
-            }
-        }
-    }
-
-    // possible only if early_head_seg == NULL
-    if (IMAGE_SEG_IS_EARLIER(dict, dict->window.used_segs_head, early_head_seg)) {
-        early_head_seg = dict->window.used_segs_head;
-    }
-
-    this_encoder_head_seg = dict->window.encoders_heads[encoder_id];
-
-    GLZ_ASSERT(dict->cur_usr, early_head_seg != NULL_IMAGE_SEG_ID);
-
-    if (IMAGE_SEG_IS_EARLIER(dict, this_encoder_head_seg, early_head_seg)) {
-        GLZ_ASSERT(dict->cur_usr,
-                   this_encoder_head_seg == dict->window.used_images_head->first_seg);
-        glz_dictionary_window_remove_head(dict, encoder_id,
-                                          dict->window.segs[early_head_seg].image);
-    }
-
-
-    dict->window.encoders_heads[encoder_id] = NULL_IMAGE_SEG_ID;
-    pthread_mutex_unlock(&dict->lock);
-}
diff --git a/server/glz_encoder_dictionary.h b/server/glz_encoder_dictionary.h
deleted file mode 100644
index eb57aa5..0000000
--- a/server/glz_encoder_dictionary.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_GLZ_ENCODER_DICTIONARY
-#define _H_GLZ_ENCODER_DICTIONARY
-
-#include <stdint.h>
-#include "glz_encoder_config.h"
-
-/*
-    Interface for maintaining lz dictionary that is shared among several encoders.
-    The interface for accessing the dictionary for encoding purposes is located in
-    glz_encoder_dictionary_protected.h
-*/
-
-typedef void GlzEncDictContext;
-typedef void GlzEncDictImageContext;
-
-/* NOTE: DISPLAY_MIGRATE_DATA_VERSION should change in case GlzEncDictRestoreData changes*/
-typedef struct GlzEncDictRestoreData {
-    uint32_t size;
-    uint32_t max_encoders;
-    uint64_t last_image_id;
-} GlzEncDictRestoreData;
-
-/* size        : maximal number of pixels occupying the window
-   max_encoders: maximal number of encoders that use the dictionary
-   usr         : callbacks */
-GlzEncDictContext *glz_enc_dictionary_create(uint32_t size, uint32_t max_encoders,
-                                             GlzEncoderUsrContext *usr);
-
-void glz_enc_dictionary_destroy(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
-
-/* returns the window capacity in pixels */
-uint32_t glz_enc_dictionary_get_size(GlzEncDictContext *);
-
-/* returns the current state of the dictionary.
-   NOTE - you should use it only when no encoder uses the dictionary. */
-void glz_enc_dictionary_get_restore_data(GlzEncDictContext *opaque_dict,
-                                         GlzEncDictRestoreData *out_data,
-                                         GlzEncoderUsrContext *usr);
-
-/* creates a dictionary and initialized it by use the given info */
-GlzEncDictContext *glz_enc_dictionary_restore(GlzEncDictRestoreData *restore_data,
-                                              GlzEncoderUsrContext *usr);
-
-/*  NOTE - you should use this routine only when no encoder uses the dictionary. */
-void glz_enc_dictionary_reset(GlzEncDictContext *opaque_dict, GlzEncoderUsrContext *usr);
-
-/* image: the context returned by the encoder when the image was encoded.
-   NOTE - you should use this routine only when no encoder uses the dictionary.*/
-void glz_enc_dictionary_remove_image(GlzEncDictContext *opaque_dict,
-                                     GlzEncDictImageContext *image, GlzEncoderUsrContext *usr);
-
-#endif // _H_GLZ_ENCODER_DICTIONARY
diff --git a/server/glz_encoder_dictionary_protected.h b/server/glz_encoder_dictionary_protected.h
deleted file mode 100644
index 098684f..0000000
--- a/server/glz_encoder_dictionary_protected.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_GLZ_ENCODER_DICTIONARY_PROTECTED
-#define _H_GLZ_ENCODER_DICTIONARY_PROTECTED
-
-/* Interface for using the dictionary for encoding.
-   Data structures are exposed for the encoder for efficiency
-   purposes. */
-typedef struct WindowImage WindowImage;
-typedef struct WindowImageSegment WindowImageSegment;
-
-
-//#define CHAINED_HASH
-
-#ifdef CHAINED_HASH
-#define HASH_SIZE_LOG 16
-#define HASH_CHAIN_SIZE 4
-#else
-#define HASH_SIZE_LOG 20
-#define HASH_CHAIN_SIZE 1
-#endif
-
-#define HASH_SIZE (1 << HASH_SIZE_LOG)
-#define HASH_MASK (HASH_SIZE - 1)
-
-typedef struct HashEntry HashEntry;
-
-typedef struct SharedDictionary SharedDictionary;
-
-struct WindowImage {
-    uint64_t id;
-    LzImageType type;
-    int size;                    // in pixels
-    uint32_t first_seg;
-    GlzUsrImageContext  *usr_context;
-    WindowImage*       next;
-    uint8_t is_alive;
-};
-
-#define MAX_IMAGE_SEGS_NUM (0xffffffff)
-#define NULL_IMAGE_SEG_ID MAX_IMAGE_SEGS_NUM
-#define INIT_IMAGE_SEGS_NUM 1000
-
-/* Images can be separated into several chunks. The basic unit of the
-   dictionary window is one image segment. Each segment is encoded separately.
-   An encoded match can refer to only one segment.*/
-struct WindowImageSegment {
-    WindowImage     *image;
-    void            *lines;
-    void            *lines_end;
-    uint32_t pixels_num;            // Number of pixels in the segment
-    uint64_t pixels_so_far;         // Total no. pixels passed through the window till this segment.
-                                    // NOTE - never use size delta independently. It should
-                                    // always be used with respect to a previous size delta
-    uint32_t next;
-};
-
-
-struct  __attribute__ ((__packed__)) HashEntry {
-    uint32_t image_seg_idx;
-    uint32_t ref_pix_idx;
-};
-
-
-struct SharedDictionary {
-    struct {
-        /* The segments storage. A dynamic array.
-           By referring to a segment by its index, instead of address,
-           we save space in the hash entries (32bit instead of 64bit) */
-        WindowImageSegment  *segs;
-        uint32_t segs_quota;
-
-        /* The window is manged as a linked list rather than as a cyclic
-           array in order to keep the indices of the segments consistent
-           after reallocation */
-
-        /* the window in a resolution of image segments */
-        uint32_t used_segs_head;             // the latest head
-        uint32_t used_segs_tail;
-        uint32_t free_segs_head;
-
-        uint32_t            *encoders_heads; // Holds for each encoder (by id), the window head when
-                                             // it started the encoding.
-                                             // The head is NULL_IMAGE_SEG_ID when the encoder is
-                                             // not encoding.
-
-        /* the window in a resolution of images. But here the head contains the oldest head*/
-        WindowImage*        used_images_tail;
-        WindowImage*        used_images_head;
-        WindowImage*        free_images;
-
-        uint64_t pixels_so_far;
-        uint32_t size_limit;                 // max number of pixels in a window (per encoder)
-    } window;
-
-    /* Concurrency issues: the reading/writing of each entry field should be atomic.
-       It is allowed that the reading/writing of the whole entry won't be atomic,
-       since before we access a reference we check its validity*/
-#ifdef CHAINED_HASH
-    HashEntry htab[HASH_SIZE][HASH_CHAIN_SIZE];
-    uint8_t htab_counter[HASH_SIZE];  //cyclic counter for the next entry in a chain to be assigned
-#else
-    HashEntry htab[HASH_SIZE];
-#endif
-
-    uint64_t last_image_id;
-    uint32_t max_encoders;
-    pthread_mutex_t lock;
-    pthread_rwlock_t rw_alloc_lock;
-    GlzEncoderUsrContext       *cur_usr; // each encoder has other context.
-};
-
-/*
-    Add the image to the tail of the window.
-    If possible, release images from the head of the window.
-    Also perform concurrency related operations.
-
-    usr_image_context: when an image is released from the window due to capacity overflow,
-                       usr_image_context is given as a parameter to the free_image callback.
-
-    image_head_dist  : the number of images between the current image and the head of the
-                       window that is associated with the encoder.
-*/
-WindowImage *glz_dictionary_pre_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
-                                       SharedDictionary *dict, LzImageType image_type,
-                                       int image_width, int image_height, int image_stride,
-                                       uint8_t *first_lines, unsigned int num_first_lines,
-                                       GlzUsrImageContext *usr_image_context,
-                                       uint32_t *image_head_dist);
-
-/*
-   Performs concurrency related operations.
-   If possible, release images from the head of the window.
-*/
-void glz_dictionary_post_encode(uint32_t encoder_id, GlzEncoderUsrContext *usr,
-                                SharedDictionary *dict);
-
-#define IMAGE_SEG_IS_EARLIER(dict, dst_seg, src_seg) (                     \
-    ((src_seg) == NULL_IMAGE_SEG_ID) || (((dst_seg) != NULL_IMAGE_SEG_ID)  \
-    && ((dict)->window.segs[(dst_seg)].pixels_so_far <                     \
-       (dict)->window.segs[(src_seg)].pixels_so_far)))
-
-
-#ifdef CHAINED_HASH
-#define UPDATE_HASH(dict, hval, seg, pix) {                \
-    uint8_t tmp_count = (dict)->htab_counter[hval];        \
-    (dict)->htab[hval][tmp_count].image_seg_idx = seg;     \
-    (dict)->htab[hval][tmp_count].ref_pix_idx = pix;       \
-    tmp_count = ((tmp_count) + 1) & (HASH_CHAIN_SIZE - 1); \
-    dict->htab_counter[hval] = tmp_count;                  \
-}
-#else
-#define UPDATE_HASH(dict, hval, seg, pix) { \
-    (dict)->htab[hval].image_seg_idx = seg; \
-    (dict)->htab[hval].ref_pix_idx = pix;   \
-}
-#endif
-
-/* checks if the reference segment is located in the range of the window
-   of the current encoder */
-#define REF_SEG_IS_VALID(dict, enc_id, ref_seg, src_seg) ( \
-    ((ref_seg) == (src_seg)) ||                            \
-    ((ref_seg)->image &&                                   \
-     (ref_seg)->image->is_alive &&                         \
-     (src_seg->image->type == ref_seg->image->type) &&     \
-     (ref_seg->pixels_so_far <= src_seg->pixels_so_far) && \
-     ((dict)->window.segs[                                 \
-        (dict)->window.encoders_heads[enc_id]].pixels_so_far <= \
-        ref_seg->pixels_so_far)))
-
-#endif // _H_GLZ_ENCODER_DICTIONARY_PROTECTED
diff --git a/server/image-cache.c b/server/image-cache.c
new file mode 100644
index 0000000..f4d2ee9
--- /dev/null
+++ b/server/image-cache.c
@@ -0,0 +1,214 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include "image-cache.h"
+#include "red_parse_qxl.h"
+#include "display-channel.h"
+
+static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
+{
+    ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
+
+    while (item) {
+        if (item->id == id) {
+            return item;
+        }
+        item = item->next;
+    }
+    return NULL;
+}
+
+int image_cache_hit(ImageCache *cache, uint64_t id)
+{
+    ImageCacheItem *item;
+    if (!(item = image_cache_find(cache, id))) {
+        return FALSE;
+    }
+#ifdef IMAGE_CACHE_AGE
+    item->age = cache->age;
+#endif
+    ring_remove(&item->lru_link);
+    ring_add(&cache->lru, &item->lru_link);
+    return TRUE;
+}
+
+static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
+{
+    ImageCacheItem **now;
+
+    now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
+    for (;;) {
+        spice_assert(*now);
+        if (*now == item) {
+            *now = item->next;
+            break;
+        }
+        now = &(*now)->next;
+    }
+    ring_remove(&item->lru_link);
+    pixman_image_unref(item->image);
+    free(item);
+#ifndef IMAGE_CACHE_AGE
+    cache->num_items--;
+#endif
+}
+
+#define IMAGE_CACHE_MAX_ITEMS 2
+
+static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
+{
+    ImageCache *cache = (ImageCache *)spice_cache;
+    ImageCacheItem *item;
+
+#ifndef IMAGE_CACHE_AGE
+    if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
+        ImageCacheItem *tail = (ImageCacheItem *)ring_get_tail(&cache->lru);
+        spice_assert(tail);
+        image_cache_remove(cache, tail);
+    }
+#endif
+
+    item = spice_new(ImageCacheItem, 1);
+    item->id = id;
+#ifdef IMAGE_CACHE_AGE
+    item->age = cache->age;
+#else
+    cache->num_items++;
+#endif
+    item->image = pixman_image_ref(image);
+    ring_item_init(&item->lru_link);
+
+    item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
+    cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
+
+    ring_add(&cache->lru, &item->lru_link);
+}
+
+static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
+{
+    ImageCache *cache = (ImageCache *)spice_cache;
+
+    ImageCacheItem *item = image_cache_find(cache, id);
+    if (!item) {
+        spice_error("not found");
+    }
+    return pixman_image_ref(item->image);
+}
+
+void image_cache_init(ImageCache *cache)
+{
+    static SpiceImageCacheOps image_cache_ops = {
+        image_cache_put,
+        image_cache_get,
+    };
+
+    cache->base.ops = &image_cache_ops;
+    memset(cache->hash_table, 0, sizeof(cache->hash_table));
+    ring_init(&cache->lru);
+#ifdef IMAGE_CACHE_AGE
+    cache->age = 0;
+#else
+    cache->num_items = 0;
+#endif
+}
+
+void image_cache_reset(ImageCache *cache)
+{
+    ImageCacheItem *item;
+
+    while ((item = (ImageCacheItem *)ring_get_head(&cache->lru))) {
+        image_cache_remove(cache, item);
+    }
+#ifdef IMAGE_CACHE_AGE
+    cache->age = 0;
+#endif
+}
+
+#define IMAGE_CACHE_DEPTH 4
+
+void image_cache_aging(ImageCache *cache)
+{
+#ifdef IMAGE_CACHE_AGE
+    ImageCacheItem *item;
+
+    cache->age++;
+    while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
+           cache->age - item->age > IMAGE_CACHE_DEPTH) {
+        image_cache_remove(cache, item);
+    }
+#endif
+}
+
+void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
+                          SpiceImage *image_store, Drawable *drawable)
+{
+    SpiceImage *image = *image_ptr;
+
+    if (image == NULL) {
+        spice_assert(drawable != NULL);
+        spice_assert(drawable->red_drawable->self_bitmap_image != NULL);
+        *image_ptr = drawable->red_drawable->self_bitmap_image;
+        return;
+    }
+
+    if (image_cache_hit(cache, image->descriptor.id)) {
+        image_store->descriptor = image->descriptor;
+        image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
+        image_store->descriptor.flags = 0;
+        *image_ptr = image_store;
+        return;
+    }
+
+    switch (image->descriptor.type) {
+    case SPICE_IMAGE_TYPE_QUIC: {
+        image_store->descriptor = image->descriptor;
+        image_store->u.quic = image->u.quic;
+        *image_ptr = image_store;
+#ifdef IMAGE_CACHE_AGE
+        image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+#else
+        if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
+            image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
+        }
+#endif
+        break;
+    }
+    case SPICE_IMAGE_TYPE_BITMAP:
+    case SPICE_IMAGE_TYPE_SURFACE:
+        /* nothing */
+        break;
+    default:
+        spice_error("invalid image type");
+    }
+}
+
+void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
+{
+    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
+        image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL);
+    }
+}
+
+void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
+{
+    if (mask->bitmap) {
+        image_cache_localize(cache, &mask->bitmap, image_store, NULL);
+    }
+}
diff --git a/server/image-cache.h b/server/image-cache.h
new file mode 100644
index 0000000..91ccc6d
--- /dev/null
+++ b/server/image-cache.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifndef H_SPICE_IMAGE_CACHE
+#define H_SPICE_IMAGE_CACHE
+
+#include <inttypes.h>
+
+#include "common/pixman_utils.h"
+#include "common/canvas_base.h"
+#include "common/ring.h"
+
+/* FIXME: move back to display-channel.h (once structs are private) */
+typedef struct Drawable Drawable;
+typedef struct DisplayChannelClient DisplayChannelClient;
+
+typedef struct ImageCacheItem {
+    RingItem lru_link;
+    uint64_t id;
+#ifdef IMAGE_CACHE_AGE
+    uint32_t age;
+#endif
+    struct ImageCacheItem *next;
+    pixman_image_t *image;
+} ImageCacheItem;
+
+#define IMAGE_CACHE_HASH_SIZE 1024
+
+typedef struct ImageCache {
+    SpiceImageCache base;
+    ImageCacheItem *hash_table[IMAGE_CACHE_HASH_SIZE];
+    Ring lru;
+#ifdef IMAGE_CACHE_AGE
+    uint32_t age;
+#else
+    uint32_t num_items;
+#endif
+} ImageCache;
+
+int          image_cache_hit               (ImageCache *cache, uint64_t id);
+void         image_cache_init              (ImageCache *cache);
+void         image_cache_reset             (ImageCache *cache);
+void         image_cache_aging             (ImageCache *cache);
+void         image_cache_localize          (ImageCache *cache, SpiceImage **image_ptr,
+                                            SpiceImage *image_store, Drawable *drawable);
+void         image_cache_localize_brush    (ImageCache *cache, SpiceBrush *brush,
+                                            SpiceImage *image_store);
+void         image_cache_localize_mask     (ImageCache *cache, SpiceQMask *mask,
+                                            SpiceImage *image_store);
+
+#endif
diff --git a/server/inputs-channel.c b/server/inputs-channel.c
new file mode 100644
index 0000000..3e8fccd
--- /dev/null
+++ b/server/inputs-channel.c
@@ -0,0 +1,679 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netinet/in.h> // IPPROTO_TCP
+#include <netinet/tcp.h> // TCP_NODELAY
+#include <fcntl.h>
+#include <stddef.h> // NULL
+#include <errno.h>
+#include <spice/macros.h>
+#include <spice/vd_agent.h>
+#include <spice/protocol.h>
+#include <stdbool.h>
+
+#include "common/marshaller.h"
+#include "common/messages.h"
+#include "common/generated_server_marshallers.h"
+
+#include "demarshallers.h"
+#include "spice.h"
+#include "red_common.h"
+#include "reds.h"
+#include "reds_stream.h"
+#include "red_channel.h"
+#include "main-channel.h"
+#include "inputs-channel.h"
+#include "migration-protocol.h"
+
+// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
+// since it was defined once in reds.c which contained both.
+// Now that they are split we can give a more fitting value for inputs - what
+// should it be?
+#define REDS_AGENT_WINDOW_SIZE 10
+#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
+
+// approximate max receive message size
+#define RECEIVE_BUF_SIZE \
+    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
+
+struct SpiceKbdState {
+    bool push_ext;
+
+    /* track key press state */
+    bool key[0x7f];
+    bool key_ext[0x7f];
+};
+
+struct SpiceMouseState {
+    int dummy;
+};
+
+struct SpiceTabletState {
+    int dummy;
+};
+
+typedef struct InputsChannelClient {
+    RedChannelClient base;
+    uint16_t motion_count;
+} InputsChannelClient;
+
+typedef struct InputsChannel {
+    RedChannel base;
+    uint8_t recv_buf[RECEIVE_BUF_SIZE];
+    VDAgentMouseState mouse_state;
+    int src_during_migrate;
+} InputsChannel;
+
+enum {
+    PIPE_ITEM_INPUTS_INIT = PIPE_ITEM_TYPE_CHANNEL_BASE,
+    PIPE_ITEM_MOUSE_MOTION_ACK,
+    PIPE_ITEM_KEY_MODIFIERS,
+    PIPE_ITEM_MIGRATE_DATA,
+};
+
+typedef struct InputsPipeItem {
+    PipeItem base;
+} InputsPipeItem;
+
+typedef struct KeyModifiersPipeItem {
+    PipeItem base;
+    uint8_t modifiers;
+} KeyModifiersPipeItem;
+
+typedef struct InputsInitPipeItem {
+    PipeItem base;
+    uint8_t modifiers;
+} InputsInitPipeItem;
+
+static SpiceKbdInstance *keyboard = NULL;
+static SpiceMouseInstance *mouse = NULL;
+static SpiceTabletInstance *tablet = NULL;
+
+static SpiceTimer *key_modifiers_timer;
+
+static InputsChannel *g_inputs_channel = NULL;
+
+#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
+
+#define SCROLL_LOCK_SCAN_CODE 0x46
+#define NUM_LOCK_SCAN_CODE 0x45
+#define CAPS_LOCK_SCAN_CODE 0x3a
+
+int inputs_inited(void)
+{
+    return !!g_inputs_channel;
+}
+
+int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
+{
+    if (keyboard) {
+        spice_printerr("already have keyboard");
+        return -1;
+    }
+    keyboard = _keyboard;
+    keyboard->st = spice_new0(SpiceKbdState, 1);
+    return 0;
+}
+
+int inputs_set_mouse(SpiceMouseInstance *_mouse)
+{
+    if (mouse) {
+        spice_printerr("already have mouse");
+        return -1;
+    }
+    mouse = _mouse;
+    mouse->st = spice_new0(SpiceMouseState, 1);
+    return 0;
+}
+
+int inputs_set_tablet(SpiceTabletInstance *_tablet)
+{
+    if (tablet) {
+        spice_printerr("already have tablet");
+        return -1;
+    }
+    tablet = _tablet;
+    tablet->st = spice_new0(SpiceTabletState, 1);
+    return 0;
+}
+
+int inputs_has_tablet(void)
+{
+    return !!tablet;
+}
+
+void inputs_detach_tablet(SpiceTabletInstance *_tablet)
+{
+    spice_printerr("");
+    tablet = NULL;
+}
+
+void inputs_set_tablet_logical_size(int x_res, int y_res)
+{
+    SpiceTabletInterface *sif;
+
+    sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+    sif->set_logical_size(tablet, x_res, y_res);
+}
+
+const VDAgentMouseState *inputs_get_mouse_state(void)
+{
+    spice_assert(g_inputs_channel);
+    return &g_inputs_channel->mouse_state;
+}
+
+static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
+                                                 uint16_t type,
+                                                 uint32_t size)
+{
+    InputsChannel *inputs_channel = SPICE_CONTAINEROF(rcc->channel, InputsChannel, base);
+
+    if (size > RECEIVE_BUF_SIZE) {
+        spice_printerr("error: too large incoming message");
+        return NULL;
+    }
+    return inputs_channel->recv_buf;
+}
+
+static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc,
+                                               uint16_t type,
+                                               uint32_t size,
+                                               uint8_t *msg)
+{
+}
+
+#define OUTGOING_OK 0
+#define OUTGOING_FAILED -1
+#define OUTGOING_BLOCKED 1
+
+#define RED_MOUSE_STATE_TO_LOCAL(state)     \
+    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
+     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
+     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))
+
+#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
+    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
+     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
+     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
+
+static void activate_modifiers_watch(void)
+{
+    core->timer_start(key_modifiers_timer, KEY_MODIFIERS_TTL);
+}
+
+static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
+{
+    SpiceKbdInterface *sif;
+
+    if (!sin) {
+        return;
+    }
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
+
+    /* track XT scan code set 1 key state */
+    if (scan == 0xe0) {
+        sin->st->push_ext = TRUE;
+    } else {
+        bool *state = sin->st->push_ext ? sin->st->key : sin->st->key_ext;
+        sin->st->push_ext = FALSE;
+        state[scan & 0x7f] = !(scan & 0x80);
+    }
+
+    sif->push_scan_freg(sin, scan);
+}
+
+static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
+{
+    SpiceKbdInterface *sif;
+
+    if (!sin) {
+        return 0;
+    }
+    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
+    return sif->get_leds(sin);
+}
+
+static PipeItem *inputs_key_modifiers_item_new(
+    RedChannelClient *rcc, void *data, int num)
+{
+    KeyModifiersPipeItem *item = spice_malloc(sizeof(KeyModifiersPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_KEY_MODIFIERS);
+    item->modifiers = *(uint8_t *)data;
+    return &item->base;
+}
+
+static void inputs_channel_send_migrate_data(RedChannelClient *rcc,
+                                             SpiceMarshaller *m,
+                                             PipeItem *item)
+{
+    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
+
+    g_inputs_channel->src_during_migrate = FALSE;
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
+
+    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC);
+    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION);
+    spice_marshaller_add_uint16(m, icc->motion_count);
+}
+
+static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
+    PipeItem *base, int item_pushed)
+{
+    free(base);
+}
+
+static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
+{
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+
+    switch (base->type) {
+        case PIPE_ITEM_KEY_MODIFIERS:
+        {
+            SpiceMsgInputsKeyModifiers key_modifiers;
+
+            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_KEY_MODIFIERS, base);
+            key_modifiers.modifiers =
+                SPICE_CONTAINEROF(base, KeyModifiersPipeItem, base)->modifiers;
+            spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
+            break;
+        }
+        case PIPE_ITEM_INPUTS_INIT:
+        {
+            SpiceMsgInputsInit inputs_init;
+
+            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_INIT, base);
+            inputs_init.keyboard_modifiers =
+                SPICE_CONTAINEROF(base, InputsInitPipeItem, base)->modifiers;
+            spice_marshall_msg_inputs_init(m, &inputs_init);
+            break;
+        }
+        case PIPE_ITEM_MOUSE_MOTION_ACK:
+            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, base);
+            break;
+        case PIPE_ITEM_MIGRATE_DATA:
+            inputs_channel_send_migrate_data(rcc, m, base);
+            break;
+        default:
+            spice_warning("invalid pipe iten %d", base->type);
+            break;
+    }
+    red_channel_client_begin_send_message(rcc);
+}
+
+static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
+                                        void *message)
+{
+    InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
+    InputsChannelClient *icc = (InputsChannelClient *)rcc;
+    uint32_t i;
+
+    spice_assert(g_inputs_channel == inputs_channel);
+    switch (type) {
+    case SPICE_MSGC_INPUTS_KEY_DOWN: {
+        SpiceMsgcKeyDown *key_down = message;
+        if (key_down->code == CAPS_LOCK_SCAN_CODE ||
+            key_down->code == NUM_LOCK_SCAN_CODE ||
+            key_down->code == SCROLL_LOCK_SCAN_CODE) {
+            activate_modifiers_watch();
+        }
+    }
+    case SPICE_MSGC_INPUTS_KEY_UP: {
+        SpiceMsgcKeyUp *key_up = message;
+        for (i = 0; i < 4; i++) {
+            uint8_t code = (key_up->code >> (i * 8)) & 0xff;
+            if (code == 0) {
+                break;
+            }
+            kbd_push_scan(keyboard, code);
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
+        uint8_t *code = message;
+        for (i = 0; i < size; i++) {
+            kbd_push_scan(keyboard, code[i]);
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
+        SpiceMsgcMouseMotion *mouse_motion = message;
+
+        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
+            !g_inputs_channel->src_during_migrate) {
+            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
+            icc->motion_count = 0;
+        }
+        if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->motion(mouse,
+                        mouse_motion->dx, mouse_motion->dy, 0,
+                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
+        SpiceMsgcMousePosition *pos = message;
+
+        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
+            !g_inputs_channel->src_during_migrate) {
+            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
+            icc->motion_count = 0;
+        }
+        if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
+            break;
+        }
+        spice_assert((reds_get_agent_mouse() && reds_has_vdagent()) || tablet);
+        if (!reds_get_agent_mouse() || !reds_has_vdagent()) {
+            SpiceTabletInterface *sif;
+            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
+            break;
+        }
+        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
+        mouse_state->x = pos->x;
+        mouse_state->y = pos->y;
+        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
+        mouse_state->display_id = pos->display_id;
+        reds_handle_agent_mouse_event(mouse_state);
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
+        SpiceMsgcMousePress *mouse_press = message;
+        int dz = 0;
+        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
+            dz = -1;
+        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
+            dz = 1;
+        }
+        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
+            if (reds_get_agent_mouse() && reds_has_vdagent()) {
+                inputs_channel->mouse_state.buttons =
+                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
+                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
+                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
+                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
+            } else if (tablet) {
+                SpiceTabletInterface *sif;
+                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
+            }
+        } else if (mouse) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->motion(mouse, 0, 0, dz,
+                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
+        SpiceMsgcMouseRelease *mouse_release = message;
+        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
+            if (reds_get_agent_mouse() && reds_has_vdagent()) {
+                inputs_channel->mouse_state.buttons =
+                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
+                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
+            } else if (tablet) {
+                SpiceTabletInterface *sif;
+                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
+                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
+            }
+        } else if (mouse) {
+            SpiceMouseInterface *sif;
+            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
+            sif->buttons(mouse,
+                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
+        }
+        break;
+    }
+    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
+        SpiceMsgcKeyModifiers *modifiers = message;
+        uint8_t leds;
+
+        if (!keyboard) {
+            break;
+        }
+        leds = kbd_get_leds(keyboard);
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
+            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
+        }
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
+            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
+        }
+        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
+            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
+            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
+            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
+        }
+        activate_modifiers_watch();
+        break;
+    }
+    case SPICE_MSGC_DISCONNECTING:
+        break;
+    default:
+        return red_channel_client_handle_message(rcc, size, type, message);
+    }
+    return TRUE;
+}
+
+static void inputs_release_keys(void)
+{
+    int i;
+    SpiceKbdState *st;
+
+    if (!keyboard) {
+        return;
+    }
+    st = keyboard->st;
+
+    for (i = 0; i < SPICE_N_ELEMENTS(st->key); i++) {
+        if (!st->key[i])
+            continue;
+
+        st->key[i] = FALSE;
+        kbd_push_scan(keyboard, i | 0x80);
+    }
+
+    for (i = 0; i < SPICE_N_ELEMENTS(st->key_ext); i++) {
+        if (!st->key_ext[i])
+            continue;
+
+        st->key_ext[i] = FALSE;
+        kbd_push_scan(keyboard, 0xe0);
+        kbd_push_scan(keyboard, i | 0x80);
+    }
+}
+
+static void inputs_channel_on_disconnect(RedChannelClient *rcc)
+{
+    if (!rcc) {
+        return;
+    }
+    inputs_release_keys();
+}
+
+static void inputs_pipe_add_init(RedChannelClient *rcc)
+{
+    InputsInitPipeItem *item = spice_malloc(sizeof(InputsInitPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_INPUTS_INIT);
+    item->modifiers = kbd_get_leds(keyboard);
+    red_channel_client_pipe_add_push(rcc, &item->base);
+}
+
+static int inputs_channel_config_socket(RedChannelClient *rcc)
+{
+    int delay_val = 1;
+    RedsStream *stream = red_channel_client_get_stream(rcc);
+
+    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
+            &delay_val, sizeof(delay_val)) == -1) {
+        if (errno != ENOTSUP && errno != ENOPROTOOPT) {
+            spice_printerr("setsockopt failed, %s", strerror(errno));
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
+{
+}
+
+static void inputs_connect(RedChannel *channel, RedClient *client,
+                           RedsStream *stream, int migration,
+                           int num_common_caps, uint32_t *common_caps,
+                           int num_caps, uint32_t *caps)
+{
+    InputsChannelClient *icc;
+
+    spice_assert(g_inputs_channel);
+    spice_assert(channel == &g_inputs_channel->base);
+
+    if (!reds_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
+        main_channel_client_push_notify(red_client_get_main(client),
+                                        "keyboard channel is insecure");
+    }
+
+    spice_printerr("inputs channel client create");
+    icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
+                                                          channel,
+                                                          client,
+                                                          stream,
+                                                          FALSE,
+                                                          num_common_caps, common_caps,
+                                                          num_caps, caps);
+    if (!icc) {
+        return;
+    }
+    icc->motion_count = 0;
+    inputs_pipe_add_init(&icc->base);
+}
+
+static void inputs_migrate(RedChannelClient *rcc)
+{
+    g_inputs_channel->src_during_migrate = TRUE;
+    red_channel_client_default_migrate(rcc);
+}
+
+static void inputs_push_keyboard_modifiers(uint8_t modifiers)
+{
+    if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base) ||
+        g_inputs_channel->src_during_migrate) {
+        return;
+    }
+    red_channel_pipes_new_add_push(&g_inputs_channel->base,
+        inputs_key_modifiers_item_new, (void*)&modifiers);
+}
+
+void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
+{
+    inputs_push_keyboard_modifiers(leds);
+}
+
+static void key_modifiers_sender(void *opaque)
+{
+    inputs_push_keyboard_modifiers(kbd_get_leds(keyboard));
+}
+
+static int inputs_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
+{
+    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE_DATA);
+    return TRUE;
+}
+
+static int inputs_channel_handle_migrate_data(RedChannelClient *rcc,
+                                              uint32_t size,
+                                              void *message)
+{
+    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
+    SpiceMigrateDataHeader *header;
+    SpiceMigrateDataInputs *mig_data;
+
+    header = (SpiceMigrateDataHeader *)message;
+    mig_data = (SpiceMigrateDataInputs *)(header + 1);
+
+    if (!migration_protocol_validate_header(header,
+                                            SPICE_MIGRATE_DATA_INPUTS_MAGIC,
+                                            SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
+        spice_error("bad header");
+        return FALSE;
+    }
+    key_modifiers_sender(NULL);
+    icc->motion_count = mig_data->motion_count;
+
+    for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
+           icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
+        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
+    }
+    return TRUE;
+}
+
+void inputs_init(void)
+{
+    ChannelCbs channel_cbs = { NULL, };
+    ClientCbs client_cbs = { NULL, };
+
+    spice_assert(!g_inputs_channel);
+
+    channel_cbs.config_socket = inputs_channel_config_socket;
+    channel_cbs.on_disconnect = inputs_channel_on_disconnect;
+    channel_cbs.send_item = inputs_channel_send_item;
+    channel_cbs.hold_item = inputs_channel_hold_pipe_item;
+    channel_cbs.release_item = inputs_channel_release_pipe_item;
+    channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
+    channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf;
+    channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data;
+    channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
+
+    g_inputs_channel = (InputsChannel *)red_channel_create_parser(
+                                    sizeof(InputsChannel),
+                                    core,
+                                    SPICE_CHANNEL_INPUTS, 0,
+                                    FALSE, /* handle_acks */
+                                    spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL),
+                                    inputs_channel_handle_parsed,
+                                    &channel_cbs,
+                                    SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
+
+    if (!g_inputs_channel) {
+        spice_error("failed to allocate Inputs Channel");
+    }
+
+    client_cbs.connect = inputs_connect;
+    client_cbs.migrate = inputs_migrate;
+    red_channel_register_client_cbs(&g_inputs_channel->base, &client_cbs);
+
+    red_channel_set_cap(&g_inputs_channel->base, SPICE_INPUTS_CAP_KEY_SCANCODE);
+    reds_register_channel(&g_inputs_channel->base);
+
+    if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
+        spice_error("key modifiers timer create failed");
+    }
+}
diff --git a/server/inputs-channel.h b/server/inputs-channel.h
new file mode 100644
index 0000000..672ca83
--- /dev/null
+++ b/server/inputs-channel.h
@@ -0,0 +1,38 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _INPUTS_CHANNEL_H_
+#define _INPUTS_CHANNEL_H_
+
+// Inputs channel, dealing with keyboard, mouse, tablet.
+// This include should only be used by reds.c and inputs_channel.c
+
+#include <stdint.h>
+#include <spice/vd_agent.h>
+
+void inputs_init(void);
+int inputs_inited(void);
+int inputs_has_tablet(void);
+const VDAgentMouseState *inputs_get_mouse_state(void);
+void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
+int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
+int inputs_set_mouse(SpiceMouseInstance *_mouse);
+int inputs_set_tablet(SpiceTabletInstance *_tablet);
+void inputs_detach_tablet(SpiceTabletInstance *_tablet);
+void inputs_set_tablet_logical_size(int x_res, int y_res);
+
+#endif
diff --git a/server/inputs_channel.c b/server/inputs_channel.c
deleted file mode 100644
index 2934572..0000000
--- a/server/inputs_channel.c
+++ /dev/null
@@ -1,679 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <netinet/in.h> // IPPROTO_TCP
-#include <netinet/tcp.h> // TCP_NODELAY
-#include <fcntl.h>
-#include <stddef.h> // NULL
-#include <errno.h>
-#include <spice/macros.h>
-#include <spice/vd_agent.h>
-#include <spice/protocol.h>
-#include <stdbool.h>
-
-#include "common/marshaller.h"
-#include "common/messages.h"
-#include "common/generated_server_marshallers.h"
-
-#include "demarshallers.h"
-#include "spice.h"
-#include "red_common.h"
-#include "reds.h"
-#include "reds_stream.h"
-#include "red_channel.h"
-#include "main_channel.h"
-#include "inputs_channel.h"
-#include "migration_protocol.h"
-
-// TODO: RECEIVE_BUF_SIZE used to be the same for inputs_channel and main_channel
-// since it was defined once in reds.c which contained both.
-// Now that they are split we can give a more fitting value for inputs - what
-// should it be?
-#define REDS_AGENT_WINDOW_SIZE 10
-#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
-
-// approximate max receive message size
-#define RECEIVE_BUF_SIZE \
-    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
-
-struct SpiceKbdState {
-    bool push_ext;
-
-    /* track key press state */
-    bool key[0x7f];
-    bool key_ext[0x7f];
-};
-
-struct SpiceMouseState {
-    int dummy;
-};
-
-struct SpiceTabletState {
-    int dummy;
-};
-
-typedef struct InputsChannelClient {
-    RedChannelClient base;
-    uint16_t motion_count;
-} InputsChannelClient;
-
-typedef struct InputsChannel {
-    RedChannel base;
-    uint8_t recv_buf[RECEIVE_BUF_SIZE];
-    VDAgentMouseState mouse_state;
-    int src_during_migrate;
-} InputsChannel;
-
-enum {
-    PIPE_ITEM_INPUTS_INIT = PIPE_ITEM_TYPE_CHANNEL_BASE,
-    PIPE_ITEM_MOUSE_MOTION_ACK,
-    PIPE_ITEM_KEY_MODIFIERS,
-    PIPE_ITEM_MIGRATE_DATA,
-};
-
-typedef struct InputsPipeItem {
-    PipeItem base;
-} InputsPipeItem;
-
-typedef struct KeyModifiersPipeItem {
-    PipeItem base;
-    uint8_t modifiers;
-} KeyModifiersPipeItem;
-
-typedef struct InputsInitPipeItem {
-    PipeItem base;
-    uint8_t modifiers;
-} InputsInitPipeItem;
-
-static SpiceKbdInstance *keyboard = NULL;
-static SpiceMouseInstance *mouse = NULL;
-static SpiceTabletInstance *tablet = NULL;
-
-static SpiceTimer *key_modifiers_timer;
-
-static InputsChannel *g_inputs_channel = NULL;
-
-#define KEY_MODIFIERS_TTL (1000 * 2) /*2sec*/
-
-#define SCROLL_LOCK_SCAN_CODE 0x46
-#define NUM_LOCK_SCAN_CODE 0x45
-#define CAPS_LOCK_SCAN_CODE 0x3a
-
-int inputs_inited(void)
-{
-    return !!g_inputs_channel;
-}
-
-int inputs_set_keyboard(SpiceKbdInstance *_keyboard)
-{
-    if (keyboard) {
-        spice_printerr("already have keyboard");
-        return -1;
-    }
-    keyboard = _keyboard;
-    keyboard->st = spice_new0(SpiceKbdState, 1);
-    return 0;
-}
-
-int inputs_set_mouse(SpiceMouseInstance *_mouse)
-{
-    if (mouse) {
-        spice_printerr("already have mouse");
-        return -1;
-    }
-    mouse = _mouse;
-    mouse->st = spice_new0(SpiceMouseState, 1);
-    return 0;
-}
-
-int inputs_set_tablet(SpiceTabletInstance *_tablet)
-{
-    if (tablet) {
-        spice_printerr("already have tablet");
-        return -1;
-    }
-    tablet = _tablet;
-    tablet->st = spice_new0(SpiceTabletState, 1);
-    return 0;
-}
-
-int inputs_has_tablet(void)
-{
-    return !!tablet;
-}
-
-void inputs_detach_tablet(SpiceTabletInstance *_tablet)
-{
-    spice_printerr("");
-    tablet = NULL;
-}
-
-void inputs_set_tablet_logical_size(int x_res, int y_res)
-{
-    SpiceTabletInterface *sif;
-
-    sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-    sif->set_logical_size(tablet, x_res, y_res);
-}
-
-const VDAgentMouseState *inputs_get_mouse_state(void)
-{
-    spice_assert(g_inputs_channel);
-    return &g_inputs_channel->mouse_state;
-}
-
-static uint8_t *inputs_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
-                                                 uint16_t type,
-                                                 uint32_t size)
-{
-    InputsChannel *inputs_channel = SPICE_CONTAINEROF(rcc->channel, InputsChannel, base);
-
-    if (size > RECEIVE_BUF_SIZE) {
-        spice_printerr("error: too large incoming message");
-        return NULL;
-    }
-    return inputs_channel->recv_buf;
-}
-
-static void inputs_channel_release_msg_rcv_buf(RedChannelClient *rcc,
-                                               uint16_t type,
-                                               uint32_t size,
-                                               uint8_t *msg)
-{
-}
-
-#define OUTGOING_OK 0
-#define OUTGOING_FAILED -1
-#define OUTGOING_BLOCKED 1
-
-#define RED_MOUSE_STATE_TO_LOCAL(state)     \
-    ((state & SPICE_MOUSE_BUTTON_MASK_LEFT) |          \
-     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) << 1) |   \
-     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) >> 1))
-
-#define RED_MOUSE_BUTTON_STATE_TO_AGENT(state)                      \
-    (((state & SPICE_MOUSE_BUTTON_MASK_LEFT) ? VD_AGENT_LBUTTON_MASK : 0) |    \
-     ((state & SPICE_MOUSE_BUTTON_MASK_MIDDLE) ? VD_AGENT_MBUTTON_MASK : 0) |    \
-     ((state & SPICE_MOUSE_BUTTON_MASK_RIGHT) ? VD_AGENT_RBUTTON_MASK : 0))
-
-static void activate_modifiers_watch(void)
-{
-    core->timer_start(key_modifiers_timer, KEY_MODIFIERS_TTL);
-}
-
-static void kbd_push_scan(SpiceKbdInstance *sin, uint8_t scan)
-{
-    SpiceKbdInterface *sif;
-
-    if (!sin) {
-        return;
-    }
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
-
-    /* track XT scan code set 1 key state */
-    if (scan == 0xe0) {
-        sin->st->push_ext = TRUE;
-    } else {
-        bool *state = sin->st->push_ext ? sin->st->key : sin->st->key_ext;
-        sin->st->push_ext = FALSE;
-        state[scan & 0x7f] = !(scan & 0x80);
-    }
-
-    sif->push_scan_freg(sin, scan);
-}
-
-static uint8_t kbd_get_leds(SpiceKbdInstance *sin)
-{
-    SpiceKbdInterface *sif;
-
-    if (!sin) {
-        return 0;
-    }
-    sif = SPICE_CONTAINEROF(sin->base.sif, SpiceKbdInterface, base);
-    return sif->get_leds(sin);
-}
-
-static PipeItem *inputs_key_modifiers_item_new(
-    RedChannelClient *rcc, void *data, int num)
-{
-    KeyModifiersPipeItem *item = spice_malloc(sizeof(KeyModifiersPipeItem));
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_KEY_MODIFIERS);
-    item->modifiers = *(uint8_t *)data;
-    return &item->base;
-}
-
-static void inputs_channel_send_migrate_data(RedChannelClient *rcc,
-                                             SpiceMarshaller *m,
-                                             PipeItem *item)
-{
-    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
-
-    g_inputs_channel->src_during_migrate = FALSE;
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
-
-    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_MAGIC);
-    spice_marshaller_add_uint32(m, SPICE_MIGRATE_DATA_INPUTS_VERSION);
-    spice_marshaller_add_uint16(m, icc->motion_count);
-}
-
-static void inputs_channel_release_pipe_item(RedChannelClient *rcc,
-    PipeItem *base, int item_pushed)
-{
-    free(base);
-}
-
-static void inputs_channel_send_item(RedChannelClient *rcc, PipeItem *base)
-{
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-
-    switch (base->type) {
-        case PIPE_ITEM_KEY_MODIFIERS:
-        {
-            SpiceMsgInputsKeyModifiers key_modifiers;
-
-            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_KEY_MODIFIERS, base);
-            key_modifiers.modifiers =
-                SPICE_CONTAINEROF(base, KeyModifiersPipeItem, base)->modifiers;
-            spice_marshall_msg_inputs_key_modifiers(m, &key_modifiers);
-            break;
-        }
-        case PIPE_ITEM_INPUTS_INIT:
-        {
-            SpiceMsgInputsInit inputs_init;
-
-            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_INIT, base);
-            inputs_init.keyboard_modifiers =
-                SPICE_CONTAINEROF(base, InputsInitPipeItem, base)->modifiers;
-            spice_marshall_msg_inputs_init(m, &inputs_init);
-            break;
-        }
-        case PIPE_ITEM_MOUSE_MOTION_ACK:
-            red_channel_client_init_send_data(rcc, SPICE_MSG_INPUTS_MOUSE_MOTION_ACK, base);
-            break;
-        case PIPE_ITEM_MIGRATE_DATA:
-            inputs_channel_send_migrate_data(rcc, m, base);
-            break;
-        default:
-            spice_warning("invalid pipe iten %d", base->type);
-            break;
-    }
-    red_channel_client_begin_send_message(rcc);
-}
-
-static int inputs_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
-                                        void *message)
-{
-    InputsChannel *inputs_channel = (InputsChannel *)rcc->channel;
-    InputsChannelClient *icc = (InputsChannelClient *)rcc;
-    uint32_t i;
-
-    spice_assert(g_inputs_channel == inputs_channel);
-    switch (type) {
-    case SPICE_MSGC_INPUTS_KEY_DOWN: {
-        SpiceMsgcKeyDown *key_down = message;
-        if (key_down->code == CAPS_LOCK_SCAN_CODE ||
-            key_down->code == NUM_LOCK_SCAN_CODE ||
-            key_down->code == SCROLL_LOCK_SCAN_CODE) {
-            activate_modifiers_watch();
-        }
-    }
-    case SPICE_MSGC_INPUTS_KEY_UP: {
-        SpiceMsgcKeyUp *key_up = message;
-        for (i = 0; i < 4; i++) {
-            uint8_t code = (key_up->code >> (i * 8)) & 0xff;
-            if (code == 0) {
-                break;
-            }
-            kbd_push_scan(keyboard, code);
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_KEY_SCANCODE: {
-        uint8_t *code = message;
-        for (i = 0; i < size; i++) {
-            kbd_push_scan(keyboard, code[i]);
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_MOTION: {
-        SpiceMsgcMouseMotion *mouse_motion = message;
-
-        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
-            !g_inputs_channel->src_during_migrate) {
-            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
-            icc->motion_count = 0;
-        }
-        if (mouse && reds_get_mouse_mode() == SPICE_MOUSE_MODE_SERVER) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->motion(mouse,
-                        mouse_motion->dx, mouse_motion->dy, 0,
-                        RED_MOUSE_STATE_TO_LOCAL(mouse_motion->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_POSITION: {
-        SpiceMsgcMousePosition *pos = message;
-
-        if (++icc->motion_count % SPICE_INPUT_MOTION_ACK_BUNCH == 0 &&
-            !g_inputs_channel->src_during_migrate) {
-            red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
-            icc->motion_count = 0;
-        }
-        if (reds_get_mouse_mode() != SPICE_MOUSE_MODE_CLIENT) {
-            break;
-        }
-        spice_assert((reds_get_agent_mouse() && reds_has_vdagent()) || tablet);
-        if (!reds_get_agent_mouse() || !reds_has_vdagent()) {
-            SpiceTabletInterface *sif;
-            sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-            sif->position(tablet, pos->x, pos->y, RED_MOUSE_STATE_TO_LOCAL(pos->buttons_state));
-            break;
-        }
-        VDAgentMouseState *mouse_state = &inputs_channel->mouse_state;
-        mouse_state->x = pos->x;
-        mouse_state->y = pos->y;
-        mouse_state->buttons = RED_MOUSE_BUTTON_STATE_TO_AGENT(pos->buttons_state);
-        mouse_state->display_id = pos->display_id;
-        reds_handle_agent_mouse_event(mouse_state);
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_PRESS: {
-        SpiceMsgcMousePress *mouse_press = message;
-        int dz = 0;
-        if (mouse_press->button == SPICE_MOUSE_BUTTON_UP) {
-            dz = -1;
-        } else if (mouse_press->button == SPICE_MOUSE_BUTTON_DOWN) {
-            dz = 1;
-        }
-        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
-            if (reds_get_agent_mouse() && reds_has_vdagent()) {
-                inputs_channel->mouse_state.buttons =
-                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_press->buttons_state) |
-                    (dz == -1 ? VD_AGENT_UBUTTON_MASK : 0) |
-                    (dz == 1 ? VD_AGENT_DBUTTON_MASK : 0);
-                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
-            } else if (tablet) {
-                SpiceTabletInterface *sif;
-                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-                sif->wheel(tablet, dz, RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
-            }
-        } else if (mouse) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->motion(mouse, 0, 0, dz,
-                        RED_MOUSE_STATE_TO_LOCAL(mouse_press->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_MOUSE_RELEASE: {
-        SpiceMsgcMouseRelease *mouse_release = message;
-        if (reds_get_mouse_mode() == SPICE_MOUSE_MODE_CLIENT) {
-            if (reds_get_agent_mouse() && reds_has_vdagent()) {
-                inputs_channel->mouse_state.buttons =
-                    RED_MOUSE_BUTTON_STATE_TO_AGENT(mouse_release->buttons_state);
-                reds_handle_agent_mouse_event(&inputs_channel->mouse_state);
-            } else if (tablet) {
-                SpiceTabletInterface *sif;
-                sif = SPICE_CONTAINEROF(tablet->base.sif, SpiceTabletInterface, base);
-                sif->buttons(tablet, RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
-            }
-        } else if (mouse) {
-            SpiceMouseInterface *sif;
-            sif = SPICE_CONTAINEROF(mouse->base.sif, SpiceMouseInterface, base);
-            sif->buttons(mouse,
-                         RED_MOUSE_STATE_TO_LOCAL(mouse_release->buttons_state));
-        }
-        break;
-    }
-    case SPICE_MSGC_INPUTS_KEY_MODIFIERS: {
-        SpiceMsgcKeyModifiers *modifiers = message;
-        uint8_t leds;
-
-        if (!keyboard) {
-            break;
-        }
-        leds = kbd_get_leds(keyboard);
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_SCROLL_LOCK)) {
-            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, SCROLL_LOCK_SCAN_CODE | 0x80);
-        }
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_NUM_LOCK)) {
-            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, NUM_LOCK_SCAN_CODE | 0x80);
-        }
-        if ((modifiers->modifiers & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK) !=
-            (leds & SPICE_KEYBOARD_MODIFIER_FLAGS_CAPS_LOCK)) {
-            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE);
-            kbd_push_scan(keyboard, CAPS_LOCK_SCAN_CODE | 0x80);
-        }
-        activate_modifiers_watch();
-        break;
-    }
-    case SPICE_MSGC_DISCONNECTING:
-        break;
-    default:
-        return red_channel_client_handle_message(rcc, size, type, message);
-    }
-    return TRUE;
-}
-
-static void inputs_release_keys(void)
-{
-    int i;
-    SpiceKbdState *st;
-
-    if (!keyboard) {
-        return;
-    }
-    st = keyboard->st;
-
-    for (i = 0; i < SPICE_N_ELEMENTS(st->key); i++) {
-        if (!st->key[i])
-            continue;
-
-        st->key[i] = FALSE;
-        kbd_push_scan(keyboard, i | 0x80);
-    }
-
-    for (i = 0; i < SPICE_N_ELEMENTS(st->key_ext); i++) {
-        if (!st->key_ext[i])
-            continue;
-
-        st->key_ext[i] = FALSE;
-        kbd_push_scan(keyboard, 0xe0);
-        kbd_push_scan(keyboard, i | 0x80);
-    }
-}
-
-static void inputs_channel_on_disconnect(RedChannelClient *rcc)
-{
-    if (!rcc) {
-        return;
-    }
-    inputs_release_keys();
-}
-
-static void inputs_pipe_add_init(RedChannelClient *rcc)
-{
-    InputsInitPipeItem *item = spice_malloc(sizeof(InputsInitPipeItem));
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_INPUTS_INIT);
-    item->modifiers = kbd_get_leds(keyboard);
-    red_channel_client_pipe_add_push(rcc, &item->base);
-}
-
-static int inputs_channel_config_socket(RedChannelClient *rcc)
-{
-    int delay_val = 1;
-    RedsStream *stream = red_channel_client_get_stream(rcc);
-
-    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY,
-            &delay_val, sizeof(delay_val)) == -1) {
-        if (errno != ENOTSUP && errno != ENOPROTOOPT) {
-            spice_printerr("setsockopt failed, %s", strerror(errno));
-            return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-static void inputs_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
-{
-}
-
-static void inputs_connect(RedChannel *channel, RedClient *client,
-                           RedsStream *stream, int migration,
-                           int num_common_caps, uint32_t *common_caps,
-                           int num_caps, uint32_t *caps)
-{
-    InputsChannelClient *icc;
-
-    spice_assert(g_inputs_channel);
-    spice_assert(channel == &g_inputs_channel->base);
-
-    if (!reds_stream_is_ssl(stream) && !red_client_during_migrate_at_target(client)) {
-        main_channel_client_push_notify(red_client_get_main(client),
-                                        "keyboard channel is insecure");
-    }
-
-    spice_printerr("inputs channel client create");
-    icc = (InputsChannelClient*)red_channel_client_create(sizeof(InputsChannelClient),
-                                                          channel,
-                                                          client,
-                                                          stream,
-                                                          FALSE,
-                                                          num_common_caps, common_caps,
-                                                          num_caps, caps);
-    if (!icc) {
-        return;
-    }
-    icc->motion_count = 0;
-    inputs_pipe_add_init(&icc->base);
-}
-
-static void inputs_migrate(RedChannelClient *rcc)
-{
-    g_inputs_channel->src_during_migrate = TRUE;
-    red_channel_client_default_migrate(rcc);
-}
-
-static void inputs_push_keyboard_modifiers(uint8_t modifiers)
-{
-    if (!g_inputs_channel || !red_channel_is_connected(&g_inputs_channel->base) ||
-        g_inputs_channel->src_during_migrate) {
-        return;
-    }
-    red_channel_pipes_new_add_push(&g_inputs_channel->base,
-        inputs_key_modifiers_item_new, (void*)&modifiers);
-}
-
-void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds)
-{
-    inputs_push_keyboard_modifiers(leds);
-}
-
-static void key_modifiers_sender(void *opaque)
-{
-    inputs_push_keyboard_modifiers(kbd_get_leds(keyboard));
-}
-
-static int inputs_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
-{
-    red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MIGRATE_DATA);
-    return TRUE;
-}
-
-static int inputs_channel_handle_migrate_data(RedChannelClient *rcc,
-                                              uint32_t size,
-                                              void *message)
-{
-    InputsChannelClient *icc = SPICE_CONTAINEROF(rcc, InputsChannelClient, base);
-    SpiceMigrateDataHeader *header;
-    SpiceMigrateDataInputs *mig_data;
-
-    header = (SpiceMigrateDataHeader *)message;
-    mig_data = (SpiceMigrateDataInputs *)(header + 1);
-
-    if (!migration_protocol_validate_header(header,
-                                            SPICE_MIGRATE_DATA_INPUTS_MAGIC,
-                                            SPICE_MIGRATE_DATA_INPUTS_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
-    }
-    key_modifiers_sender(NULL);
-    icc->motion_count = mig_data->motion_count;
-
-    for (; icc->motion_count >= SPICE_INPUT_MOTION_ACK_BUNCH;
-           icc->motion_count -= SPICE_INPUT_MOTION_ACK_BUNCH) {
-        red_channel_client_pipe_add_type(rcc, PIPE_ITEM_MOUSE_MOTION_ACK);
-    }
-    return TRUE;
-}
-
-void inputs_init(void)
-{
-    ChannelCbs channel_cbs = { NULL, };
-    ClientCbs client_cbs = { NULL, };
-
-    spice_assert(!g_inputs_channel);
-
-    channel_cbs.config_socket = inputs_channel_config_socket;
-    channel_cbs.on_disconnect = inputs_channel_on_disconnect;
-    channel_cbs.send_item = inputs_channel_send_item;
-    channel_cbs.hold_item = inputs_channel_hold_pipe_item;
-    channel_cbs.release_item = inputs_channel_release_pipe_item;
-    channel_cbs.alloc_recv_buf = inputs_channel_alloc_msg_rcv_buf;
-    channel_cbs.release_recv_buf = inputs_channel_release_msg_rcv_buf;
-    channel_cbs.handle_migrate_data = inputs_channel_handle_migrate_data;
-    channel_cbs.handle_migrate_flush_mark = inputs_channel_handle_migrate_flush_mark;
-
-    g_inputs_channel = (InputsChannel *)red_channel_create_parser(
-                                    sizeof(InputsChannel),
-                                    core,
-                                    SPICE_CHANNEL_INPUTS, 0,
-                                    FALSE, /* handle_acks */
-                                    spice_get_client_channel_parser(SPICE_CHANNEL_INPUTS, NULL),
-                                    inputs_channel_handle_parsed,
-                                    &channel_cbs,
-                                    SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
-
-    if (!g_inputs_channel) {
-        spice_error("failed to allocate Inputs Channel");
-    }
-
-    client_cbs.connect = inputs_connect;
-    client_cbs.migrate = inputs_migrate;
-    red_channel_register_client_cbs(&g_inputs_channel->base, &client_cbs);
-
-    red_channel_set_cap(&g_inputs_channel->base, SPICE_INPUTS_CAP_KEY_SCANCODE);
-    reds_register_channel(&g_inputs_channel->base);
-
-    if (!(key_modifiers_timer = core->timer_add(key_modifiers_sender, NULL))) {
-        spice_error("key modifiers timer create failed");
-    }
-}
diff --git a/server/inputs_channel.h b/server/inputs_channel.h
deleted file mode 100644
index 672ca83..0000000
--- a/server/inputs_channel.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _INPUTS_CHANNEL_H_
-#define _INPUTS_CHANNEL_H_
-
-// Inputs channel, dealing with keyboard, mouse, tablet.
-// This include should only be used by reds.c and inputs_channel.c
-
-#include <stdint.h>
-#include <spice/vd_agent.h>
-
-void inputs_init(void);
-int inputs_inited(void);
-int inputs_has_tablet(void);
-const VDAgentMouseState *inputs_get_mouse_state(void);
-void inputs_on_keyboard_leds_change(void *opaque, uint8_t leds);
-int inputs_set_keyboard(SpiceKbdInstance *_keyboard);
-int inputs_set_mouse(SpiceMouseInstance *_mouse);
-int inputs_set_tablet(SpiceTabletInstance *_tablet);
-void inputs_detach_tablet(SpiceTabletInstance *_tablet);
-void inputs_set_tablet_logical_size(int x_res, int y_res);
-
-#endif
diff --git a/server/jpeg-encoder.c b/server/jpeg-encoder.c
new file mode 100644
index 0000000..428fd5b
--- /dev/null
+++ b/server/jpeg-encoder.c
@@ -0,0 +1,249 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "red_common.h"
+#include "jpeg-encoder.h"
+
+#include <jpeglib.h>
+
+typedef struct JpegEncoder {
+    JpegEncoderUsrContext *usr;
+
+    struct jpeg_destination_mgr dest_mgr;
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+
+    struct {
+        JpegEncoderImageType type;
+        int width;
+        int height;
+        int stride;
+        unsigned int out_size;
+        void (*convert_line_to_RGB24) (void *line, int width, uint8_t **out_line);
+    } cur_image;
+} JpegEncoder;
+
+/* jpeg destination manager callbacks */
+
+static void dest_mgr_init_destination(j_compress_ptr cinfo)
+{
+    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+    if (enc->dest_mgr.free_in_buffer == 0) {
+        enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
+                                                            &enc->dest_mgr.next_output_byte);
+
+        if (enc->dest_mgr.free_in_buffer == 0) {
+            spice_error("not enough space");
+        }
+    }
+
+    enc->cur_image.out_size = enc->dest_mgr.free_in_buffer;
+}
+
+static boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
+{
+    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+    enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
+                                                        &enc->dest_mgr.next_output_byte);
+
+    if (enc->dest_mgr.free_in_buffer == 0) {
+        spice_error("not enough space");
+    }
+    enc->cur_image.out_size += enc->dest_mgr.free_in_buffer;
+    return TRUE;
+}
+
+static void dest_mgr_term_destination(j_compress_ptr cinfo)
+{
+    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
+    enc->cur_image.out_size -= enc->dest_mgr.free_in_buffer;
+}
+
+JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr)
+{
+    JpegEncoder *enc;
+    if (!usr->more_space || !usr->more_lines) {
+        return NULL;
+    }
+
+    enc = spice_new0(JpegEncoder, 1);
+
+    enc->usr = usr;
+
+    enc->dest_mgr.init_destination = dest_mgr_init_destination;
+    enc->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
+    enc->dest_mgr.term_destination = dest_mgr_term_destination;
+
+    enc->cinfo.err = jpeg_std_error(&enc->jerr);
+
+    jpeg_create_compress(&enc->cinfo);
+    enc->cinfo.client_data = enc;
+    enc->cinfo.dest = &enc->dest_mgr;
+    return (JpegEncoderContext*)enc;
+}
+
+void jpeg_encoder_destroy(JpegEncoderContext* encoder)
+{
+    jpeg_destroy_compress(&((JpegEncoder*)encoder)->cinfo);
+    free(encoder);
+}
+
+static void convert_RGB16_to_RGB24(void *line, int width, uint8_t **out_line)
+{
+    uint16_t *src_line = line;
+    uint8_t *out_pix;
+    int x;
+
+    spice_assert(out_line && *out_line);
+
+    out_pix = *out_line;
+
+    for (x = 0; x < width; x++) {
+       uint16_t pixel = *src_line++;
+       *out_pix++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
+       *out_pix++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
+       *out_pix++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
+   }
+}
+
+static void convert_BGR24_to_RGB24(void *in_line, int width, uint8_t **out_line)
+{
+    int x;
+    uint8_t *out_pix;
+    uint8_t *line = in_line;
+    spice_assert(out_line && *out_line);
+
+    out_pix = *out_line;
+
+    for (x = 0; x < width; x++) {
+        *out_pix++ = line[2];
+        *out_pix++ = line[1];
+        *out_pix++ = line[0];
+        line += 3;
+    }
+}
+
+static void convert_BGRX32_to_RGB24(void *line, int width, uint8_t **out_line)
+{
+    uint32_t *src_line = line;
+    uint8_t *out_pix;
+    int x;
+
+    spice_assert(out_line && *out_line);
+
+    out_pix = *out_line;
+
+    for (x = 0; x < width; x++) {
+        uint32_t pixel = *src_line++;
+        *out_pix++ = (pixel >> 16) & 0xff;
+        *out_pix++ = (pixel >> 8) & 0xff;
+        *out_pix++ = pixel & 0xff;
+    }
+}
+
+static void convert_RGB24_to_RGB24(void *line, int width, uint8_t **out_line)
+{
+    *out_line = line;
+}
+
+
+#define FILL_LINES() {                                                  \
+    if (lines == lines_end) {                                           \
+        int n = jpeg->usr->more_lines(jpeg->usr, &lines);               \
+        if (n <= 0) {                                                   \
+            spice_error("more lines failed");                           \
+        }                                                               \
+        lines_end = lines + n * stride;                                 \
+    }                                                                   \
+}
+
+static void do_jpeg_encode(JpegEncoder *jpeg, uint8_t *lines, unsigned int num_lines)
+{
+    uint8_t *lines_end;
+    uint8_t *RGB24_line;
+    int stride, width;
+    JSAMPROW row_pointer[1];
+    width = jpeg->cur_image.width;
+    stride = jpeg->cur_image.stride;
+
+    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
+        RGB24_line = (uint8_t *)spice_malloc(width*3);
+    }
+
+    lines_end = lines + (stride * num_lines);
+
+    for (;jpeg->cinfo.next_scanline < jpeg->cinfo.image_height; lines += stride) {
+        FILL_LINES();
+        jpeg->cur_image.convert_line_to_RGB24(lines, width, &RGB24_line);
+        row_pointer[0] = RGB24_line;
+        jpeg_write_scanlines(&jpeg->cinfo, row_pointer, 1);
+    }
+
+    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
+        free(RGB24_line);
+    }
+}
+
+int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
+                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
+                uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+    JpegEncoder *enc = (JpegEncoder *)jpeg;
+
+    enc->cur_image.type = type;
+    enc->cur_image.width = width;
+    enc->cur_image.height = height;
+    enc->cur_image.stride = stride;
+    enc->cur_image.out_size = 0;
+
+    switch (type) {
+    case JPEG_IMAGE_TYPE_RGB16:
+        enc->cur_image.convert_line_to_RGB24 = convert_RGB16_to_RGB24;
+        break;
+    case JPEG_IMAGE_TYPE_RGB24:
+        enc->cur_image.convert_line_to_RGB24 = convert_RGB24_to_RGB24;
+        break;
+    case JPEG_IMAGE_TYPE_BGR24:
+        enc->cur_image.convert_line_to_RGB24 = convert_BGR24_to_RGB24;
+        break;
+    case JPEG_IMAGE_TYPE_BGRX32:
+        enc->cur_image.convert_line_to_RGB24 = convert_BGRX32_to_RGB24;
+        break;
+    default:
+        spice_error("bad image type");
+    }
+
+    enc->cinfo.image_width = width;
+    enc->cinfo.image_height = height;
+    enc->cinfo.input_components = 3;
+    enc->cinfo.in_color_space = JCS_RGB;
+    jpeg_set_defaults(&enc->cinfo);
+    jpeg_set_quality(&enc->cinfo, quality, TRUE);
+
+    enc->dest_mgr.next_output_byte = io_ptr;
+    enc->dest_mgr.free_in_buffer = num_io_bytes;
+
+    jpeg_start_compress(&enc->cinfo, TRUE);
+
+    do_jpeg_encode(enc, lines, num_lines);
+
+    jpeg_finish_compress(&enc->cinfo);
+    return enc->cur_image.out_size;
+}
diff --git a/server/jpeg-encoder.h b/server/jpeg-encoder.h
new file mode 100644
index 0000000..690a029
--- /dev/null
+++ b/server/jpeg-encoder.h
@@ -0,0 +1,61 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in
+         the documentation and/or other materials provided with the
+         distribution.
+       * Neither the name of the copyright holder nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _H_JPEG_ENCODER
+#define _H_JPEG_ENCODER
+
+#include <spice/types.h>
+
+typedef enum {
+    JPEG_IMAGE_TYPE_INVALID,
+    JPEG_IMAGE_TYPE_RGB16,
+    /* in byte per color types, the notation is according to the order of the
+       colors in the memory */
+    JPEG_IMAGE_TYPE_RGB24,
+    JPEG_IMAGE_TYPE_BGR24,
+    JPEG_IMAGE_TYPE_BGRX32,
+} JpegEncoderImageType;
+
+typedef void* JpegEncoderContext;
+typedef struct JpegEncoderUsrContext JpegEncoderUsrContext;
+
+struct JpegEncoderUsrContext {
+    int (*more_space)(JpegEncoderUsrContext *usr, uint8_t **io_ptr);
+    int (*more_lines)(JpegEncoderUsrContext *usr, uint8_t **lines);
+};
+
+JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr);
+void jpeg_encoder_destroy(JpegEncoderContext *encoder);
+
+/* returns the total size of the encoded data. Images must be supplied from the
+   top line to the bottom */
+int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
+                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
+                uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
diff --git a/server/jpeg_encoder.c b/server/jpeg_encoder.c
deleted file mode 100644
index 0296e9b..0000000
--- a/server/jpeg_encoder.c
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "red_common.h"
-#include "jpeg_encoder.h"
-#include <jpeglib.h>
-
-typedef struct JpegEncoder {
-    JpegEncoderUsrContext *usr;
-
-    struct jpeg_destination_mgr dest_mgr;
-    struct jpeg_compress_struct cinfo;
-    struct jpeg_error_mgr jerr;
-
-    struct {
-        JpegEncoderImageType type;
-        int width;
-        int height;
-        int stride;
-        unsigned int out_size;
-        void (*convert_line_to_RGB24) (void *line, int width, uint8_t **out_line);
-    } cur_image;
-} JpegEncoder;
-
-/* jpeg destination manager callbacks */
-
-static void dest_mgr_init_destination(j_compress_ptr cinfo)
-{
-    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
-    if (enc->dest_mgr.free_in_buffer == 0) {
-        enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
-                                                            &enc->dest_mgr.next_output_byte);
-
-        if (enc->dest_mgr.free_in_buffer == 0) {
-            spice_error("not enough space");
-        }
-    }
-
-    enc->cur_image.out_size = enc->dest_mgr.free_in_buffer;
-}
-
-static boolean dest_mgr_empty_output_buffer(j_compress_ptr cinfo)
-{
-    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
-    enc->dest_mgr.free_in_buffer = enc->usr->more_space(enc->usr,
-                                                        &enc->dest_mgr.next_output_byte);
-
-    if (enc->dest_mgr.free_in_buffer == 0) {
-        spice_error("not enough space");
-    }
-    enc->cur_image.out_size += enc->dest_mgr.free_in_buffer;
-    return TRUE;
-}
-
-static void dest_mgr_term_destination(j_compress_ptr cinfo)
-{
-    JpegEncoder *enc = (JpegEncoder *)cinfo->client_data;
-    enc->cur_image.out_size -= enc->dest_mgr.free_in_buffer;
-}
-
-JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr)
-{
-    JpegEncoder *enc;
-    if (!usr->more_space || !usr->more_lines) {
-        return NULL;
-    }
-
-    enc = spice_new0(JpegEncoder, 1);
-
-    enc->usr = usr;
-
-    enc->dest_mgr.init_destination = dest_mgr_init_destination;
-    enc->dest_mgr.empty_output_buffer = dest_mgr_empty_output_buffer;
-    enc->dest_mgr.term_destination = dest_mgr_term_destination;
-
-    enc->cinfo.err = jpeg_std_error(&enc->jerr);
-
-    jpeg_create_compress(&enc->cinfo);
-    enc->cinfo.client_data = enc;
-    enc->cinfo.dest = &enc->dest_mgr;
-    return (JpegEncoderContext*)enc;
-}
-
-void jpeg_encoder_destroy(JpegEncoderContext* encoder)
-{
-    jpeg_destroy_compress(&((JpegEncoder*)encoder)->cinfo);
-    free(encoder);
-}
-
-static void convert_RGB16_to_RGB24(void *line, int width, uint8_t **out_line)
-{
-    uint16_t *src_line = line;
-    uint8_t *out_pix;
-    int x;
-
-    spice_assert(out_line && *out_line);
-
-    out_pix = *out_line;
-
-    for (x = 0; x < width; x++) {
-       uint16_t pixel = *src_line++;
-       *out_pix++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
-       *out_pix++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
-       *out_pix++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
-   }
-}
-
-static void convert_BGR24_to_RGB24(void *in_line, int width, uint8_t **out_line)
-{
-    int x;
-    uint8_t *out_pix;
-    uint8_t *line = in_line;
-    spice_assert(out_line && *out_line);
-
-    out_pix = *out_line;
-
-    for (x = 0; x < width; x++) {
-        *out_pix++ = line[2];
-        *out_pix++ = line[1];
-        *out_pix++ = line[0];
-        line += 3;
-    }
-}
-
-static void convert_BGRX32_to_RGB24(void *line, int width, uint8_t **out_line)
-{
-    uint32_t *src_line = line;
-    uint8_t *out_pix;
-    int x;
-
-    spice_assert(out_line && *out_line);
-
-    out_pix = *out_line;
-
-    for (x = 0; x < width; x++) {
-        uint32_t pixel = *src_line++;
-        *out_pix++ = (pixel >> 16) & 0xff;
-        *out_pix++ = (pixel >> 8) & 0xff;
-        *out_pix++ = pixel & 0xff;
-    }
-}
-
-static void convert_RGB24_to_RGB24(void *line, int width, uint8_t **out_line)
-{
-    *out_line = line;
-}
-
-
-#define FILL_LINES() {                                                  \
-    if (lines == lines_end) {                                           \
-        int n = jpeg->usr->more_lines(jpeg->usr, &lines);               \
-        if (n <= 0) {                                                   \
-            spice_error("more lines failed");                           \
-        }                                                               \
-        lines_end = lines + n * stride;                                 \
-    }                                                                   \
-}
-
-static void do_jpeg_encode(JpegEncoder *jpeg, uint8_t *lines, unsigned int num_lines)
-{
-    uint8_t *lines_end;
-    uint8_t *RGB24_line;
-    int stride, width;
-    JSAMPROW row_pointer[1];
-    width = jpeg->cur_image.width;
-    stride = jpeg->cur_image.stride;
-
-    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
-        RGB24_line = (uint8_t *)spice_malloc(width*3);
-    }
-
-    lines_end = lines + (stride * num_lines);
-
-    for (;jpeg->cinfo.next_scanline < jpeg->cinfo.image_height; lines += stride) {
-        FILL_LINES();
-        jpeg->cur_image.convert_line_to_RGB24(lines, width, &RGB24_line);
-        row_pointer[0] = RGB24_line;
-        jpeg_write_scanlines(&jpeg->cinfo, row_pointer, 1);
-    }
-
-    if (jpeg->cur_image.type != JPEG_IMAGE_TYPE_RGB24) {
-        free(RGB24_line);
-    }
-}
-
-int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
-                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
-                uint8_t *io_ptr, unsigned int num_io_bytes)
-{
-    JpegEncoder *enc = (JpegEncoder *)jpeg;
-
-    enc->cur_image.type = type;
-    enc->cur_image.width = width;
-    enc->cur_image.height = height;
-    enc->cur_image.stride = stride;
-    enc->cur_image.out_size = 0;
-
-    switch (type) {
-    case JPEG_IMAGE_TYPE_RGB16:
-        enc->cur_image.convert_line_to_RGB24 = convert_RGB16_to_RGB24;
-        break;
-    case JPEG_IMAGE_TYPE_RGB24:
-        enc->cur_image.convert_line_to_RGB24 = convert_RGB24_to_RGB24;
-        break;
-    case JPEG_IMAGE_TYPE_BGR24:
-        enc->cur_image.convert_line_to_RGB24 = convert_BGR24_to_RGB24;
-        break;
-    case JPEG_IMAGE_TYPE_BGRX32:
-        enc->cur_image.convert_line_to_RGB24 = convert_BGRX32_to_RGB24;
-        break;
-    default:
-        spice_error("bad image type");
-    }
-
-    enc->cinfo.image_width = width;
-    enc->cinfo.image_height = height;
-    enc->cinfo.input_components = 3;
-    enc->cinfo.in_color_space = JCS_RGB;
-    jpeg_set_defaults(&enc->cinfo);
-    jpeg_set_quality(&enc->cinfo, quality, TRUE);
-
-    enc->dest_mgr.next_output_byte = io_ptr;
-    enc->dest_mgr.free_in_buffer = num_io_bytes;
-
-    jpeg_start_compress(&enc->cinfo, TRUE);
-
-    do_jpeg_encode(enc, lines, num_lines);
-
-    jpeg_finish_compress(&enc->cinfo);
-    return enc->cur_image.out_size;
-}
diff --git a/server/jpeg_encoder.h b/server/jpeg_encoder.h
deleted file mode 100644
index 690a029..0000000
--- a/server/jpeg_encoder.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-         notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above copyright
-         notice, this list of conditions and the following disclaimer in
-         the documentation and/or other materials provided with the
-         distribution.
-       * Neither the name of the copyright holder nor the names of its
-         contributors may be used to endorse or promote products derived
-         from this software without specific prior written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef _H_JPEG_ENCODER
-#define _H_JPEG_ENCODER
-
-#include <spice/types.h>
-
-typedef enum {
-    JPEG_IMAGE_TYPE_INVALID,
-    JPEG_IMAGE_TYPE_RGB16,
-    /* in byte per color types, the notation is according to the order of the
-       colors in the memory */
-    JPEG_IMAGE_TYPE_RGB24,
-    JPEG_IMAGE_TYPE_BGR24,
-    JPEG_IMAGE_TYPE_BGRX32,
-} JpegEncoderImageType;
-
-typedef void* JpegEncoderContext;
-typedef struct JpegEncoderUsrContext JpegEncoderUsrContext;
-
-struct JpegEncoderUsrContext {
-    int (*more_space)(JpegEncoderUsrContext *usr, uint8_t **io_ptr);
-    int (*more_lines)(JpegEncoderUsrContext *usr, uint8_t **lines);
-};
-
-JpegEncoderContext* jpeg_encoder_create(JpegEncoderUsrContext *usr);
-void jpeg_encoder_destroy(JpegEncoderContext *encoder);
-
-/* returns the total size of the encoded data. Images must be supplied from the
-   top line to the bottom */
-int jpeg_encode(JpegEncoderContext *jpeg, int quality, JpegEncoderImageType type,
-                int width, int height, uint8_t *lines, unsigned int num_lines, int stride,
-                uint8_t *io_ptr, unsigned int num_io_bytes);
-#endif
diff --git a/server/main-channel.c b/server/main-channel.c
new file mode 100644
index 0000000..5ca5bba
--- /dev/null
+++ b/server/main-channel.c
@@ -0,0 +1,1345 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <limits.h>
+#include <time.h>
+#include <pthread.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "common/generated_server_marshallers.h"
+#include "common/messages.h"
+#include "common/ring.h"
+
+#include "demarshallers.h"
+#include "main-channel.h"
+#include "red_channel.h"
+#include "red_common.h"
+#include "reds.h"
+#include "migration-protocol.h"
+#include "main-dispatcher.h"
+#include "utils.h"
+
+#define ZERO_BUF_SIZE 4096
+
+#define NET_TEST_WARMUP_BYTES 0
+#define NET_TEST_BYTES (1024 * 250)
+
+#define PING_INTERVAL (1000 * 10)
+
+#define CLIENT_CONNECTIVITY_TIMEOUT (30*1000) // 30 seconds
+
+static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
+
+enum {
+    PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = PIPE_ITEM_TYPE_CHANNEL_BASE,
+    PIPE_ITEM_TYPE_MAIN_PING,
+    PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
+    PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
+    PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
+    PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
+    PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
+    PIPE_ITEM_TYPE_MAIN_INIT,
+    PIPE_ITEM_TYPE_MAIN_NOTIFY,
+    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
+    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
+    PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
+    PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
+    PIPE_ITEM_TYPE_MAIN_NAME,
+    PIPE_ITEM_TYPE_MAIN_UUID,
+    PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
+};
+
+typedef struct RefsPipeItem {
+    PipeItem base;
+    int *refs;
+} RefsPipeItem;
+
+typedef struct PingPipeItem {
+    PipeItem base;
+    int size;
+} PingPipeItem;
+
+typedef struct MouseModePipeItem {
+    PipeItem base;
+    int current_mode;
+    int is_client_mouse_allowed;
+} MouseModePipeItem;
+
+typedef struct TokensPipeItem {
+    PipeItem base;
+    int tokens;
+} TokensPipeItem;
+
+typedef struct AgentDataPipeItem {
+    PipeItem base;
+    uint8_t* data;
+    size_t len;
+    spice_marshaller_item_free_func free_data;
+    void *opaque;
+} AgentDataPipeItem;
+
+typedef struct InitPipeItem {
+    PipeItem base;
+    int connection_id;
+    int display_channels_hint;
+    int current_mouse_mode;
+    int is_client_mouse_allowed;
+    int multi_media_time;
+    int ram_hint;
+} InitPipeItem;
+
+typedef struct NamePipeItem {
+    PipeItem base;
+    SpiceMsgMainName msg;
+} NamePipeItem;
+
+typedef struct UuidPipeItem {
+    PipeItem base;
+    SpiceMsgMainUuid msg;
+} UuidPipeItem;
+
+typedef struct NotifyPipeItem {
+    PipeItem base;
+    char *msg;
+} NotifyPipeItem;
+
+typedef struct MultiMediaTimePipeItem {
+    PipeItem base;
+    int time;
+} MultiMediaTimePipeItem;
+
+struct MainChannelClient {
+    RedChannelClient base;
+    uint32_t connection_id;
+    uint32_t ping_id;
+    uint32_t net_test_id;
+    int net_test_stage;
+    uint64_t latency;
+    uint64_t bitrate_per_sec;
+#ifdef RED_STATISTICS
+    SpiceTimer *ping_timer;
+    int ping_interval;
+#endif
+    int mig_wait_connect;
+    int mig_connect_ok;
+    int mig_wait_prev_complete;
+    int mig_wait_prev_try_seamless;
+    int init_sent;
+    int seamless_mig_dst;
+};
+
+enum NetTestStage {
+    NET_TEST_STAGE_INVALID,
+    NET_TEST_STAGE_WARMUP,
+    NET_TEST_STAGE_LATENCY,
+    NET_TEST_STAGE_RATE,
+    NET_TEST_STAGE_COMPLETE,
+};
+
+static void main_channel_release_pipe_item(RedChannelClient *rcc,
+                                           PipeItem *base, int item_pushed);
+
+int main_channel_is_connected(MainChannel *main_chan)
+{
+    return red_channel_is_connected(&main_chan->base);
+}
+
+/*
+ * When the main channel is disconnected, disconnect the entire client.
+ */
+static void main_channel_client_on_disconnect(RedChannelClient *rcc)
+{
+    spice_printerr("rcc=%p", rcc);
+    main_dispatcher_client_disconnect(rcc->client);
+}
+
+RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
+{
+    RingItem *link;
+    MainChannelClient *mcc;
+
+    RING_FOREACH(link, &main_chan->base.clients) {
+        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
+        if (mcc->connection_id == connection_id) {
+            return mcc->base.client;
+        }
+    }
+    return NULL;
+}
+
+static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
+
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
+{
+    if (!mcc || mcc->net_test_id) {
+        return;
+    }
+    if (test_rate) {
+        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
+            && main_channel_client_push_ping(mcc, 0)
+            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
+            mcc->net_test_id = mcc->ping_id - 2;
+            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
+        }
+    } else {
+        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
+    }
+}
+
+typedef struct MainMouseModeItemInfo {
+    int current_mode;
+    int is_client_mouse_allowed;
+} MainMouseModeItemInfo;
+
+static PipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, int num)
+{
+    MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
+    MainMouseModeItemInfo *info = data;
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_MOUSE_MODE);
+    item->current_mode = info->current_mode;
+    item->is_client_mouse_allowed = info->is_client_mouse_allowed;
+    return &item->base;
+}
+
+static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
+{
+    PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
+
+    red_channel_pipe_item_init(mcc->base.channel, &item->base, PIPE_ITEM_TYPE_MAIN_PING);
+    item->size = size;
+    return &item->base;
+}
+
+static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
+{
+    TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
+    item->tokens = num_tokens;
+    return &item->base;
+}
+
+static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
+                                          spice_marshaller_item_free_func free_data,
+                                          void *opaque)
+{
+    AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
+    item->data = data;
+    item->len = len;
+    item->free_data = free_data;
+    item->opaque = opaque;
+    return &item->base;
+}
+
+static PipeItem *main_init_item_new(MainChannelClient *mcc,
+    int connection_id, int display_channels_hint, int current_mouse_mode,
+    int is_client_mouse_allowed, int multi_media_time,
+    int ram_hint)
+{
+    InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
+
+    red_channel_pipe_item_init(mcc->base.channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_INIT);
+    item->connection_id = connection_id;
+    item->display_channels_hint = display_channels_hint;
+    item->current_mouse_mode = current_mouse_mode;
+    item->is_client_mouse_allowed = is_client_mouse_allowed;
+    item->multi_media_time = multi_media_time;
+    item->ram_hint = ram_hint;
+    return &item->base;
+}
+
+static PipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
+{
+    NamePipeItem *item = spice_malloc(sizeof(NamePipeItem) + strlen(name) + 1);
+
+    red_channel_pipe_item_init(mcc->base.channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_NAME);
+    item->msg.name_len = strlen(name) + 1;
+    memcpy(&item->msg.name, name, item->msg.name_len);
+
+    return &item->base;
+}
+
+static PipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
+{
+    UuidPipeItem *item = spice_malloc(sizeof(UuidPipeItem));
+
+    red_channel_pipe_item_init(mcc->base.channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_UUID);
+    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
+
+    return &item->base;
+}
+
+static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
+{
+    NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
+    const char *msg = data;
+
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_NOTIFY);
+    item->msg = spice_strdup(msg);
+    return &item->base;
+}
+
+static PipeItem *main_multi_media_time_item_new(
+    RedChannelClient *rcc, void *data, int num)
+{
+    MultiMediaTimePipeItem *item, *info = data;
+
+    item = spice_malloc(sizeof(MultiMediaTimePipeItem));
+    red_channel_pipe_item_init(rcc->channel, &item->base,
+                               PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME);
+    item->time = info->time;
+    return &item->base;
+}
+
+static void main_channel_push_channels(MainChannelClient *mcc)
+{
+    if (red_client_during_migrate_at_target(mcc->base.client)) {
+        spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
+                   "during migration");
+        return;
+    }
+    red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
+}
+
+static void main_channel_marshall_channels(RedChannelClient *rcc,
+                                           SpiceMarshaller *m,
+                                           PipeItem *item)
+{
+    SpiceMsgChannels* channels_info;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item);
+    channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels)
+                            + reds_num_of_channels() * sizeof(SpiceChannelId));
+    reds_fill_channels(channels_info);
+    spice_marshall_msg_main_channels_list(m, channels_info);
+    free(channels_info);
+}
+
+int main_channel_client_push_ping(MainChannelClient *mcc, int size)
+{
+    PipeItem *item;
+
+    if (mcc == NULL) {
+        return FALSE;
+    }
+    item = main_ping_item_new(mcc, size);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+    return TRUE;
+}
+
+static void main_channel_marshall_ping(RedChannelClient *rcc,
+                                       SpiceMarshaller *m,
+                                       PingPipeItem *item)
+{
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    struct timespec time_space;
+    SpiceMsgPing ping;
+    int size_left = item->size;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
+    ping.id = ++(mcc->ping_id);
+    clock_gettime(CLOCK_MONOTONIC, &time_space);
+    ping.timestamp = time_space.tv_sec * 1000000LL + time_space.tv_nsec / 1000LL;
+    spice_marshall_msg_ping(m, &ping);
+
+    while (size_left > 0) {
+        int now = MIN(ZERO_BUF_SIZE, size_left);
+        size_left -= now;
+        spice_marshaller_add_ref(m, zero_page, now);
+    }
+}
+
+void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
+                                  int is_client_mouse_allowed)
+{
+    MainMouseModeItemInfo info = {
+        .current_mode=current_mode,
+        .is_client_mouse_allowed=is_client_mouse_allowed,
+    };
+
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_mouse_mode_item_new, &info);
+}
+
+static void main_channel_marshall_mouse_mode(RedChannelClient *rcc,
+                                             SpiceMarshaller *m,
+                                             MouseModePipeItem *item)
+{
+    SpiceMsgMainMouseMode mouse_mode;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MOUSE_MODE, &item->base);
+    mouse_mode.supported_modes = SPICE_MOUSE_MODE_SERVER;
+    if (item->is_client_mouse_allowed) {
+        mouse_mode.supported_modes |= SPICE_MOUSE_MODE_CLIENT;
+    }
+    mouse_mode.current_mode = item->current_mode;
+    spice_marshall_msg_main_mouse_mode(m, &mouse_mode);
+}
+
+void main_channel_push_agent_connected(MainChannel *main_chan)
+{
+    if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
+        red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
+    } else {
+        red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED);
+    }
+}
+
+static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
+                                                  RedChannelClient *rcc,
+                                                  PipeItem *item)
+{
+    SpiceMsgMainAgentConnectedTokens connected;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS, item);
+    connected.num_tokens = REDS_AGENT_WINDOW_SIZE;
+    spice_marshall_msg_main_agent_connected_tokens(m, &connected);
+}
+
+void main_channel_push_agent_disconnected(MainChannel *main_chan)
+{
+    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
+}
+
+static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
+                                                     SpiceMarshaller *m,
+                                                     PipeItem *item)
+{
+    SpiceMsgMainAgentDisconnect disconnect;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED, item);
+    disconnect.error_code = SPICE_LINK_ERR_OK;
+    spice_marshall_msg_main_agent_disconnected(m, &disconnect);
+}
+
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
+{
+    PipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
+
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static void main_channel_marshall_tokens(RedChannelClient *rcc,
+                                         SpiceMarshaller *m, TokensPipeItem *item)
+{
+    SpiceMsgMainAgentTokens tokens;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_TOKEN, &item->base);
+    tokens.num_tokens = item->tokens;
+    spice_marshall_msg_main_agent_token(m, &tokens);
+}
+
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+           spice_marshaller_item_free_func free_data, void *opaque)
+{
+    PipeItem *item;
+
+    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static void main_channel_marshall_agent_data(RedChannelClient *rcc,
+                                             SpiceMarshaller *m,
+                                             AgentDataPipeItem *item)
+{
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DATA, &item->base);
+    spice_marshaller_add_ref(m, item->data, item->len);
+}
+
+static void main_channel_push_migrate_data_item(MainChannel *main_chan)
+{
+    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
+}
+
+static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
+                                                    SpiceMarshaller *m, PipeItem *item)
+{
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
+    reds_marshall_migrate_data(m); // TODO: from reds split. ugly separation.
+}
+
+static int main_channel_handle_migrate_data(RedChannelClient *rcc,
+    uint32_t size, void *message)
+{
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
+
+    /* not supported with multi-clients */
+    spice_assert(rcc->channel->clients_num == 1);
+
+    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
+        spice_printerr("bad message size %u", size);
+        return FALSE;
+    }
+    if (!migration_protocol_validate_header(header,
+                                            SPICE_MIGRATE_DATA_MAIN_MAGIC,
+                                            SPICE_MIGRATE_DATA_MAIN_VERSION)) {
+        spice_error("bad header");
+        return FALSE;
+    }
+    return reds_handle_migrate_data(mcc, (SpiceMigrateDataMain *)(header + 1), size);
+}
+
+void main_channel_push_init(MainChannelClient *mcc,
+    int display_channels_hint, int current_mouse_mode,
+    int is_client_mouse_allowed, int multi_media_time,
+    int ram_hint)
+{
+    PipeItem *item;
+
+    item = main_init_item_new(mcc,
+             mcc->connection_id, display_channels_hint, current_mouse_mode,
+             is_client_mouse_allowed, multi_media_time, ram_hint);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static void main_channel_marshall_init(RedChannelClient *rcc,
+                                       SpiceMarshaller *m,
+                                       InitPipeItem *item)
+{
+    SpiceMsgMainInit init; // TODO - remove this copy, make InitPipeItem reuse SpiceMsgMainInit
+
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_INIT, &item->base);
+    init.session_id = item->connection_id;
+    init.display_channels_hint = item->display_channels_hint;
+    init.current_mouse_mode = item->current_mouse_mode;
+    init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
+    if (item->is_client_mouse_allowed) {
+        init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
+    }
+    init.agent_connected = reds_has_vdagent();
+    init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
+    init.multi_media_time = item->multi_media_time;
+    init.ram_hint = item->ram_hint;
+    spice_marshall_msg_main_init(m, &init);
+}
+
+void main_channel_push_name(MainChannelClient *mcc, const char *name)
+{
+    PipeItem *item;
+
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_NAME_AND_UUID))
+        return;
+
+    item = main_name_item_new(mcc, name);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
+{
+    PipeItem *item;
+
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_NAME_AND_UUID))
+        return;
+
+    item = main_uuid_item_new(mcc, uuid);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
+{
+    PipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
+    red_channel_client_pipe_add_push(&mcc->base, item);
+}
+
+static void main_channel_marshall_notify(RedChannelClient *rcc,
+                                         SpiceMarshaller *m, NotifyPipeItem *item)
+{
+    SpiceMsgNotify notify;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_NOTIFY, &item->base);
+    notify.time_stamp = red_get_monotonic_time(); // TODO - move to main_new_notify_item
+    notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
+    notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
+    notify.what = SPICE_WARN_GENERAL;
+    notify.message_len = strlen(item->msg);
+    spice_marshall_msg_notify(m, &notify);
+    spice_marshaller_add(m, (uint8_t *)item->msg, notify.message_len + 1);
+}
+
+static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
+                                               SpiceMigrationDstInfo *dst_info)
+{
+    RedsMigSpice *mig_dst = &main_channel->mig_target;
+    dst_info->port = mig_dst->port;
+    dst_info->sport = mig_dst->sport;
+    dst_info->host_size = strlen(mig_dst->host) + 1;
+    dst_info->host_data = (uint8_t *)mig_dst->host;
+    if (mig_dst->cert_subject) {
+        dst_info->cert_subject_size = strlen(mig_dst->cert_subject) + 1;
+        dst_info->cert_subject_data = (uint8_t *)mig_dst->cert_subject;
+    } else {
+        dst_info->cert_subject_size = 0;
+        dst_info->cert_subject_data = NULL;
+    }
+}
+
+static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc,
+                                                PipeItem *item)
+{
+    SpiceMsgMainMigrationBegin migrate;
+    MainChannel *main_ch;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item);
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
+    spice_marshall_msg_main_migrate_begin(m, &migrate);
+}
+
+static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
+                                                         RedChannelClient *rcc,
+                                                         PipeItem *item)
+{
+    SpiceMsgMainMigrateBeginSeamless migrate_seamless;
+    MainChannel *main_ch;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item);
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
+    migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
+    spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
+}
+
+void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
+{
+    MultiMediaTimePipeItem info = {
+        .time = time,
+    };
+
+    red_channel_pipes_new_add_push(&main_chan->base,
+        main_multi_media_time_item_new, &info);
+}
+
+static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
+{
+    spice_assert(mig_target);
+    free(main_channel->mig_target.host);
+    main_channel->mig_target.host = spice_strdup(mig_target->host);
+    free(main_channel->mig_target.cert_subject);
+    if (mig_target->cert_subject) {
+        main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject);
+    } else {
+        main_channel->mig_target.cert_subject = NULL;
+    }
+    main_channel->mig_target.port = mig_target->port;
+    main_channel->mig_target.sport = mig_target->sport;
+}
+
+void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
+{
+    main_channel_fill_mig_target(main_chan, mig_target);
+    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
+}
+
+static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc,
+                                                 PipeItem *item)
+{
+    SpiceMsgMainMigrationSwitchHost migrate;
+    MainChannel *main_ch;
+
+    spice_printerr("");
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item);
+    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    migrate.port = main_ch->mig_target.port;
+    migrate.sport = main_ch->mig_target.sport;
+    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
+    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
+    if (main_ch->mig_target.cert_subject) {
+        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
+        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
+    } else {
+        migrate.cert_subject_size = 0;
+        migrate.cert_subject_data = NULL;
+    }
+    spice_marshall_msg_main_migrate_switch_host(m, &migrate);
+}
+
+static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
+                                                   SpiceMarshaller *m,
+                                                   MultiMediaTimePipeItem *item)
+{
+    SpiceMsgMainMultiMediaTime time_mes;
+
+    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MULTI_MEDIA_TIME, &item->base);
+    time_mes.time = item->time;
+    spice_marshall_msg_main_multi_media_time(m, &time_mes);
+}
+
+static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
+{
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
+
+    /* In semi-seamless migration (dest side), the connection is started from scratch, and
+     * we ignore any pipe item that arrives before the INIT msg is sent.
+     * For seamless we don't send INIT, and the connection continues from the same place
+     * it stopped on the src side. */
+    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != PIPE_ITEM_TYPE_MAIN_INIT) {
+        spice_printerr("Init msg for client %p was not sent yet "
+                       "(client is probably during semi-seamless migration). Ignoring msg type %d",
+                   rcc->client, base->type);
+        main_channel_release_pipe_item(rcc, base, FALSE);
+        return;
+    }
+    switch (base->type) {
+        case PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
+            main_channel_marshall_channels(rcc, m, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_PING:
+            main_channel_marshall_ping(rcc, m,
+                SPICE_CONTAINEROF(base, PingPipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MOUSE_MODE:
+            {
+                MouseModePipeItem *item =
+                    SPICE_CONTAINEROF(base, MouseModePipeItem, base);
+                main_channel_marshall_mouse_mode(rcc, m, item);
+                break;
+            }
+        case PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED:
+            main_channel_marshall_agent_disconnected(rcc, m, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN:
+            main_channel_marshall_tokens(rcc, m,
+                SPICE_CONTAINEROF(base, TokensPipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA:
+            main_channel_marshall_agent_data(rcc, m,
+                SPICE_CONTAINEROF(base, AgentDataPipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA:
+            main_channel_marshall_migrate_data_item(rcc, m, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_INIT:
+            mcc->init_sent = TRUE;
+            main_channel_marshall_init(rcc, m,
+                SPICE_CONTAINEROF(base, InitPipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_NOTIFY:
+            main_channel_marshall_notify(rcc, m,
+                SPICE_CONTAINEROF(base, NotifyPipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN:
+            main_channel_marshall_migrate_begin(m, rcc, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS:
+            main_channel_marshall_migrate_begin_seamless(m, rcc, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME:
+            main_channel_marshall_multi_media_time(rcc, m,
+                SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
+            break;
+        case PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST:
+            main_channel_marshall_migrate_switch(m, rcc, base);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_NAME:
+            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_NAME, base);
+            spice_marshall_msg_main_name(m, &SPICE_CONTAINEROF(base, NamePipeItem, base)->msg);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_UUID:
+            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_UUID, base);
+            spice_marshall_msg_main_uuid(m, &SPICE_CONTAINEROF(base, UuidPipeItem, base)->msg);
+            break;
+        case PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
+            main_channel_marshall_agent_connected(m, rcc, base);
+            break;
+        default:
+            break;
+    };
+    red_channel_client_begin_send_message(rcc);
+}
+
+static void main_channel_release_pipe_item(RedChannelClient *rcc,
+    PipeItem *base, int item_pushed)
+{
+    switch (base->type) {
+        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA: {
+                AgentDataPipeItem *data = (AgentDataPipeItem *)base;
+
+                data->free_data(data->data, data->opaque);
+                break;
+        }
+        case PIPE_ITEM_TYPE_MAIN_NOTIFY: {
+                NotifyPipeItem *data = (NotifyPipeItem *)base;
+                free(data->msg);
+                break;
+        }
+        default:
+            break;
+    }
+    free(base);
+}
+
+static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
+                                                         int success,
+                                                         int seamless)
+{
+    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
+    if (mcc->mig_wait_connect) {
+        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
+
+        mcc->mig_wait_connect = FALSE;
+        mcc->mig_connect_ok = success;
+        spice_assert(main_channel->num_clients_mig_wait);
+        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
+        if (!--main_channel->num_clients_mig_wait) {
+            reds_on_main_migrate_connected(seamless && success);
+        }
+    } else {
+        if (success) {
+            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+        }
+    }
+}
+
+void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
+                                                        uint32_t src_version)
+{
+    if (reds_on_migrate_dst_set_seamless(mcc, src_version)) {
+        mcc->seamless_mig_dst = TRUE;
+        red_channel_client_pipe_add_empty_msg(&mcc->base,
+                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
+    } else {
+        red_channel_client_pipe_add_empty_msg(&mcc->base,
+                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
+    }
+}
+
+void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
+{
+    if (!red_client_during_migrate_at_target(mcc->base.client)) {
+        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
+        return;
+    }
+    if (!red_channel_client_test_remote_cap(&mcc->base,
+                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
+                   "client does not support semi-seamless migration");
+            return;
+    }
+    red_client_semi_seamless_migrate_complete(mcc->base.client);
+}
+
+void main_channel_migrate_dst_complete(MainChannelClient *mcc)
+{
+    if (mcc->mig_wait_prev_complete) {
+        if (mcc->mig_wait_prev_try_seamless) {
+            spice_assert(mcc->base.channel->clients_num == 1);
+            red_channel_client_pipe_add_type(&mcc->base,
+                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+        } else {
+            red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+        }
+        mcc->mig_wait_connect = TRUE;
+        mcc->mig_wait_prev_complete = FALSE;
+    }
+}
+
+static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
+                                      void *message)
+{
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+
+    switch (type) {
+    case SPICE_MSGC_MAIN_AGENT_START: {
+        SpiceMsgcMainAgentStart *tokens;
+
+        spice_printerr("agent start");
+        if (!main_chan) {
+            return FALSE;
+        }
+        tokens = (SpiceMsgcMainAgentStart *)message;
+        reds_on_main_agent_start(mcc, tokens->num_tokens);
+        break;
+    }
+    case SPICE_MSGC_MAIN_AGENT_DATA: {
+        reds_on_main_agent_data(mcc, message, size);
+        break;
+    }
+    case SPICE_MSGC_MAIN_AGENT_TOKEN: {
+        SpiceMsgcMainAgentTokens *tokens;
+
+        tokens = (SpiceMsgcMainAgentTokens *)message;
+        reds_on_main_agent_tokens(mcc, tokens->num_tokens);
+        break;
+    }
+    case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
+        main_channel_push_channels(mcc);
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
+        main_channel_client_handle_migrate_connected(mcc,
+                                                     TRUE /* success */,
+                                                     FALSE /* seamless */);
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS:
+        main_channel_client_handle_migrate_connected(mcc,
+                                                     TRUE /* success */,
+                                                     TRUE /* seamless */);
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
+        main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS:
+        main_channel_client_handle_migrate_dst_do_seamless(mcc,
+            ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
+        break;
+    case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
+        reds_on_main_mouse_mode_request(message, size);
+        break;
+    case SPICE_MSGC_PONG: {
+        SpiceMsgPing *ping = (SpiceMsgPing *)message;
+        uint64_t roundtrip;
+        struct timespec ts;
+
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+        roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp;
+
+        if (ping->id == mcc->net_test_id) {
+            switch (mcc->net_test_stage) {
+            case NET_TEST_STAGE_WARMUP:
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
+                mcc->latency = roundtrip;
+                break;
+            case NET_TEST_STAGE_LATENCY:
+                mcc->net_test_id++;
+                mcc->net_test_stage = NET_TEST_STAGE_RATE;
+                mcc->latency = MIN(mcc->latency, roundtrip);
+                break;
+            case NET_TEST_STAGE_RATE:
+                mcc->net_test_id = 0;
+                if (roundtrip <= mcc->latency) {
+                    // probably high load on client or server result with incorrect values
+                    spice_printerr("net test: invalid values, latency %" PRIu64
+                                   " roundtrip %" PRIu64 ". assuming high"
+                                   " bandwidth", mcc->latency, roundtrip);
+                    mcc->latency = 0;
+                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+                    red_channel_client_start_connectivity_monitoring(&mcc->base,
+                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
+                    break;
+                }
+                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
+                                        / (roundtrip - mcc->latency);
+                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
+                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
+                           (double)mcc->latency / 1000,
+                           mcc->bitrate_per_sec,
+                           (double)mcc->bitrate_per_sec / 1024 / 1024,
+                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
+                red_channel_client_start_connectivity_monitoring(&mcc->base,
+                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
+                break;
+            default:
+                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
+                           ping->id,
+                           mcc->net_test_id,
+                           mcc->net_test_stage);
+                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
+            }
+            break;
+        } else {
+            /*
+             * channel client monitors the connectivity using ping-pong messages
+             */
+            red_channel_client_handle_message(rcc, size, type, message);
+        }
+#ifdef RED_STATISTICS
+        reds_update_stat_value(roundtrip);
+#endif
+        break;
+    }
+    case SPICE_MSGC_DISCONNECTING:
+        break;
+    case SPICE_MSGC_MAIN_MIGRATE_END:
+        main_channel_client_handle_migrate_end(mcc);
+        break;
+    default:
+        return red_channel_client_handle_message(rcc, size, type, message);
+    }
+    return TRUE;
+}
+
+static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
+                                               uint16_t type,
+                                               uint32_t size)
+{
+    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
+    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
+
+    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
+        return reds_get_agent_data_buffer(mcc, size);
+    } else {
+        return main_chan->recv_buf;
+    }
+}
+
+static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
+                                               uint16_t type,
+                                               uint32_t size,
+                                               uint8_t *msg)
+{
+    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
+        reds_release_agent_data_buffer(msg);
+    }
+}
+
+static int main_channel_config_socket(RedChannelClient *rcc)
+{
+    return TRUE;
+}
+
+static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
+{
+}
+
+static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
+{
+    spice_debug(NULL);
+    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel,
+                                        MainChannel, base));
+    return TRUE;
+}
+
+#ifdef RED_STATISTICS
+static void do_ping_client(MainChannelClient *mcc,
+    const char *opt, int has_interval, int interval)
+{
+    spice_printerr("");
+    if (!opt) {
+        main_channel_client_push_ping(mcc, 0);
+    } else if (!strcmp(opt, "on")) {
+        if (has_interval && interval > 0) {
+            mcc->ping_interval = interval * 1000;
+        }
+        core->timer_start(mcc->ping_timer, mcc->ping_interval);
+    } else if (!strcmp(opt, "off")) {
+        core->timer_cancel(mcc->ping_timer);
+    } else {
+        return;
+    }
+}
+
+static void ping_timer_cb(void *opaque)
+{
+    MainChannelClient *mcc = opaque;
+
+    if (!red_channel_client_is_connected(&mcc->base)) {
+        spice_printerr("not connected to peer, ping off");
+        core->timer_cancel(mcc->ping_timer);
+        return;
+    }
+    do_ping_client(mcc, NULL, 0, 0);
+    core->timer_start(mcc->ping_timer, mcc->ping_interval);
+}
+#endif /* RED_STATISTICS */
+
+static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
+                                                     RedsStream *stream, uint32_t connection_id,
+                                                     int num_common_caps, uint32_t *common_caps,
+                                                     int num_caps, uint32_t *caps)
+{
+    MainChannelClient *mcc = (MainChannelClient*)
+                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
+                                                       client, stream, FALSE, num_common_caps,
+                                                       common_caps, num_caps, caps);
+    spice_assert(mcc != NULL);
+    mcc->connection_id = connection_id;
+    mcc->bitrate_per_sec = ~0;
+#ifdef RED_STATISTICS
+    if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
+        spice_error("ping timer create failed");
+    }
+    mcc->ping_interval = PING_INTERVAL;
+#endif
+    return mcc;
+}
+
+MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
+                                     RedsStream *stream, uint32_t connection_id, int migration,
+                                     int num_common_caps, uint32_t *common_caps, int num_caps,
+                                     uint32_t *caps)
+{
+    MainChannelClient *mcc;
+
+    spice_assert(channel);
+
+    // TODO - migration - I removed it from channel creation, now put it
+    // into usage somewhere (not an issue until we return migration to it's
+    // former glory)
+    spice_printerr("add main channel client");
+    mcc = main_channel_client_create(channel, client, stream, connection_id,
+                                     num_common_caps, common_caps,
+                                     num_caps, caps);
+    return mcc;
+}
+
+int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
+{
+    return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
+}
+
+int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
+{
+    return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
+}
+
+// TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels?
+void main_channel_close(MainChannel *main_chan)
+{
+    int socketfd;
+
+    if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) {
+        close(socketfd);
+    }
+}
+
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
+{
+    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
+}
+
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
+{
+    // TODO: configurable?
+    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
+}
+
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
+{
+    return mcc->bitrate_per_sec;
+}
+
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
+{
+    return mcc->latency / 1000;
+}
+
+static void main_channel_client_migrate(RedChannelClient *rcc)
+{
+    reds_on_main_channel_migrate(SPICE_CONTAINEROF(rcc, MainChannelClient, base));
+    red_channel_client_default_migrate(rcc);
+}
+
+MainChannel* main_channel_init(void)
+{
+    RedChannel *channel;
+    ChannelCbs channel_cbs = { NULL, };
+    ClientCbs client_cbs = {NULL, };
+
+    channel_cbs.config_socket = main_channel_config_socket;
+    channel_cbs.on_disconnect = main_channel_client_on_disconnect;
+    channel_cbs.send_item = main_channel_send_item;
+    channel_cbs.hold_item = main_channel_hold_pipe_item;
+    channel_cbs.release_item = main_channel_release_pipe_item;
+    channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
+    channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
+    channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
+    channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
+
+    // TODO: set the migration flag of the channel
+    channel = red_channel_create_parser(sizeof(MainChannel), core,
+                                        SPICE_CHANNEL_MAIN, 0,
+                                        FALSE, /* handle_acks */
+                                        spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL),
+                                        main_channel_handle_parsed,
+                                        &channel_cbs,
+                                        SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
+    spice_assert(channel);
+    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
+
+    client_cbs.migrate = main_channel_client_migrate;
+    red_channel_register_client_cbs(channel, &client_cbs);
+
+    return (MainChannel *)channel;
+}
+
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
+{
+    spice_assert(mcc);
+    return &mcc->base;
+}
+
+static int main_channel_connect_semi_seamless(MainChannel *main_channel)
+{
+    RingItem *client_link;
+
+    RING_FOREACH(client_link, &main_channel->base.clients) {
+        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
+                                                    base.channel_link);
+        if (red_channel_client_test_remote_cap(&mcc->base,
+                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
+            if (red_client_during_migrate_at_target(mcc->base.client)) {
+                spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
+                mcc->mig_wait_prev_complete = TRUE;
+                mcc->mig_wait_prev_try_seamless = FALSE;
+            } else {
+                red_channel_client_pipe_add_type(&mcc->base,
+                                                 PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
+                mcc->mig_wait_connect = TRUE;
+            }
+            mcc->mig_connect_ok = FALSE;
+            main_channel->num_clients_mig_wait++;
+        }
+    }
+    return main_channel->num_clients_mig_wait;
+}
+
+static int main_channel_connect_seamless(MainChannel *main_channel)
+{
+    RingItem *client_link;
+
+    spice_assert(main_channel->base.clients_num == 1);
+
+    RING_FOREACH(client_link, &main_channel->base.clients) {
+        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
+                                                    base.channel_link);
+        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
+                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
+        if (red_client_during_migrate_at_target(mcc->base.client)) {
+           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
+           mcc->mig_wait_prev_complete = TRUE;
+           mcc->mig_wait_prev_try_seamless = TRUE;
+        } else {
+            red_channel_client_pipe_add_type(&mcc->base,
+                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
+            mcc->mig_wait_connect = TRUE;
+        }
+        mcc->mig_connect_ok = FALSE;
+        main_channel->num_clients_mig_wait++;
+    }
+    return main_channel->num_clients_mig_wait;
+}
+
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
+                                 int try_seamless)
+{
+    main_channel_fill_mig_target(main_channel, mig_target);
+    main_channel->num_clients_mig_wait = 0;
+
+    if (!main_channel_is_connected(main_channel)) {
+        return 0;
+    }
+
+    if (!try_seamless) {
+        return main_channel_connect_semi_seamless(main_channel);
+    } else {
+        RingItem *client_item;
+        MainChannelClient *mcc;
+
+        client_item = ring_get_head(&main_channel->base.clients);
+        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
+
+        if (!red_channel_client_test_remote_cap(&mcc->base,
+                                                SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
+            return main_channel_connect_semi_seamless(main_channel);
+        } else {
+            return main_channel_connect_seamless(main_channel);
+        }
+    }
+
+}
+
+void main_channel_migrate_cancel_wait(MainChannel *main_chan)
+{
+    RingItem *client_link;
+
+    RING_FOREACH(client_link, &main_chan->base.clients) {
+        MainChannelClient *mcc;
+
+        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+        if (mcc->mig_wait_connect) {
+            spice_printerr("client %p cancel wait connect", mcc->base.client);
+            mcc->mig_wait_connect = FALSE;
+            mcc->mig_connect_ok = FALSE;
+        }
+        mcc->mig_wait_prev_complete = FALSE;
+    }
+    main_chan->num_clients_mig_wait = 0;
+}
+
+int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
+{
+    RingItem *client_link;
+    int semi_seamless_count = 0;
+
+    spice_printerr("");
+
+    if (ring_is_empty(&main_chan->base.clients)) {
+        spice_printerr("no peer connected");
+        return 0;
+    }
+
+    RING_FOREACH(client_link, &main_chan->base.clients) {
+        MainChannelClient *mcc;
+        int semi_seamless_support;
+
+        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
+        semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
+                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
+        if (semi_seamless_support && mcc->mig_connect_ok) {
+            if (success) {
+                spice_printerr("client %p MIGRATE_END", mcc->base.client);
+                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
+                semi_seamless_count++;
+            } else {
+                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
+                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
+            }
+        } else {
+            if (success) {
+                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
+                red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
+            }
+        }
+        mcc->mig_connect_ok = FALSE;
+        mcc->mig_wait_connect = FALSE;
+   }
+   return semi_seamless_count;
+}
diff --git a/server/main-channel.h b/server/main-channel.h
new file mode 100644
index 0000000..9bd20f1
--- /dev/null
+++ b/server/main-channel.h
@@ -0,0 +1,103 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef __MAIN_CHANNEL_H__
+#define __MAIN_CHANNEL_H__
+
+#include <stdint.h>
+#include <spice/vd_agent.h>
+#include "common/marshaller.h"
+#include "red_channel.h"
+
+// TODO: Defines used to calculate receive buffer size, and also by reds.c
+// other options: is to make a reds_main_consts.h, to duplicate defines.
+#define REDS_AGENT_WINDOW_SIZE 10
+#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
+
+// approximate max receive message size for main channel
+#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
+    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
+
+struct RedsMigSpice {
+    char *host;
+    char *cert_subject;
+    int port;
+    int sport;
+};
+typedef struct RedsMigSpice RedsMigSpice;
+
+typedef struct MainChannel {
+    RedChannel base;
+    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
+    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
+    int num_clients_mig_wait;
+} MainChannel;
+
+
+MainChannel *main_channel_init(void);
+RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
+/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
+MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
+     RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
+     uint32_t *common_caps, int num_caps, uint32_t *caps);
+void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
+void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
+void main_channel_push_agent_connected(MainChannel *main_chan);
+void main_channel_push_agent_disconnected(MainChannel *main_chan);
+void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
+void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
+                                         spice_marshaller_item_free_func free_data, void *opaque);
+void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
+// TODO: huge. Consider making a reds_* interface for these functions
+// and calling from main.
+void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
+    int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
+    int ram_hint);
+void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
+void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
+int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
+int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
+
+/*
+ * return TRUE if network test had been completed successfully.
+ * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
+ */
+int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
+int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
+uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
+uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
+
+int main_channel_is_connected(MainChannel *main_chan);
+RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
+
+/* switch host migration */
+void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
+
+/* semi seamless migration */
+
+/* returns the number of clients that we are waiting for their connection.
+ * try_seamless = 'true' when the seamless-migration=on in qemu command line */
+int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
+                                 int try_seamless);
+void main_channel_migrate_cancel_wait(MainChannel *main_chan);
+/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
+int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
+void main_channel_migrate_dst_complete(MainChannelClient *mcc);
+void main_channel_push_name(MainChannelClient *mcc, const char *name);
+void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
+
+#endif
diff --git a/server/main-dispatcher.c b/server/main-dispatcher.c
new file mode 100644
index 0000000..eb7cee6
--- /dev/null
+++ b/server/main-dispatcher.c
@@ -0,0 +1,217 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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 <config.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include "red_common.h"
+#include "dispatcher.h"
+#include "main-dispatcher.h"
+#include "red_channel.h"
+#include "reds.h"
+
+/*
+ * Main Dispatcher
+ * ===============
+ *
+ * Communication channel between any non main thread and the main thread.
+ *
+ * The main thread is that from which spice_server_init is called.
+ *
+ * Messages are single sized, sent from the non-main thread to the main-thread.
+ * No acknowledge is sent back. This prevents a possible deadlock with the main
+ * thread already waiting on a response for the existing red_dispatcher used
+ * by the worker thread.
+ *
+ * All events have three functions:
+ * main_dispatcher_<event_name> - non static, public function
+ * main_dispatcher_self_<event_name> - handler for main thread
+ * main_dispatcher_handle_<event_name> - handler for callback from main thread
+ *   seperate from self because it may send an ack or do other work in the future.
+ */
+
+typedef struct {
+    Dispatcher base;
+    SpiceCoreInterface *core;
+} MainDispatcher;
+
+MainDispatcher main_dispatcher;
+
+enum {
+    MAIN_DISPATCHER_CHANNEL_EVENT = 0,
+    MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
+    MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
+    MAIN_DISPATCHER_CLIENT_DISCONNECT,
+
+    MAIN_DISPATCHER_NUM_MESSAGES
+};
+
+typedef struct MainDispatcherChannelEventMessage {
+    int event;
+    SpiceChannelEventInfo *info;
+} MainDispatcherChannelEventMessage;
+
+typedef struct MainDispatcherMigrateSeamlessDstCompleteMessage {
+    RedClient *client;
+} MainDispatcherMigrateSeamlessDstCompleteMessage;
+
+typedef struct MainDispatcherMmTimeLatencyMessage {
+    RedClient *client;
+    uint32_t latency;
+} MainDispatcherMmTimeLatencyMessage;
+
+typedef struct MainDispatcherClientDisconnectMessage {
+    RedClient *client;
+} MainDispatcherClientDisconnectMessage;
+
+/* channel_event - calls core->channel_event, must be done in main thread */
+static void main_dispatcher_self_handle_channel_event(
+                                                int event,
+                                                SpiceChannelEventInfo *info)
+{
+    reds_handle_channel_event(event, info);
+}
+
+static void main_dispatcher_handle_channel_event(void *opaque,
+                                                 void *payload)
+{
+    MainDispatcherChannelEventMessage *channel_event = payload;
+
+    main_dispatcher_self_handle_channel_event(channel_event->event,
+                                              channel_event->info);
+}
+
+void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info)
+{
+    MainDispatcherChannelEventMessage msg = {0,};
+
+    if (pthread_self() == main_dispatcher.base.self) {
+        main_dispatcher_self_handle_channel_event(event, info);
+        return;
+    }
+    msg.event = event;
+    msg.info = info;
+    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
+                            &msg);
+}
+
+
+static void main_dispatcher_handle_migrate_complete(void *opaque,
+                                                    void *payload)
+{
+    MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
+
+    reds_on_client_seamless_migrate_complete(mig_complete->client);
+    red_client_unref(mig_complete->client);
+}
+
+static void main_dispatcher_handle_mm_time_latency(void *opaque,
+                                                   void *payload)
+{
+    MainDispatcherMmTimeLatencyMessage *msg = payload;
+    reds_set_client_mm_time_latency(msg->client, msg->latency);
+    red_client_unref(msg->client);
+}
+
+static void main_dispatcher_handle_client_disconnect(void *opaque,
+                                                     void *payload)
+{
+    MainDispatcherClientDisconnectMessage *msg = payload;
+
+    spice_debug("client=%p", msg->client);
+    reds_client_disconnect(msg->client);
+    red_client_unref(msg->client);
+}
+
+void main_dispatcher_seamless_migrate_dst_complete(RedClient *client)
+{
+    MainDispatcherMigrateSeamlessDstCompleteMessage msg;
+
+    if (pthread_self() == main_dispatcher.base.self) {
+        reds_on_client_seamless_migrate_complete(client);
+        return;
+    }
+
+    msg.client = red_client_ref(client);
+    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
+                            &msg);
+}
+
+void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency)
+{
+    MainDispatcherMmTimeLatencyMessage msg;
+
+    if (pthread_self() == main_dispatcher.base.self) {
+        reds_set_client_mm_time_latency(client, latency);
+        return;
+    }
+
+    msg.client = red_client_ref(client);
+    msg.latency = latency;
+    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
+                            &msg);
+}
+
+void main_dispatcher_client_disconnect(RedClient *client)
+{
+    MainDispatcherClientDisconnectMessage msg;
+
+    if (!client->disconnecting) {
+        spice_debug("client %p", client);
+        msg.client = red_client_ref(client);
+        dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
+                                &msg);
+    } else {
+        spice_debug("client %p already during disconnection", client);
+    }
+}
+
+static void dispatcher_handle_read(int fd, int event, void *opaque)
+{
+    Dispatcher *dispatcher = opaque;
+
+    dispatcher_handle_recv_read(dispatcher);
+}
+
+/*
+ * FIXME:
+ * Reds routines shouldn't be exposed. Instead reds.c should register the callbacks,
+ * and the corresponding operations should be made only via main_dispatcher.
+ */
+void main_dispatcher_init(SpiceCoreInterface *core)
+{
+    memset(&main_dispatcher, 0, sizeof(main_dispatcher));
+    main_dispatcher.core = core;
+    dispatcher_init(&main_dispatcher.base, MAIN_DISPATCHER_NUM_MESSAGES, &main_dispatcher.base);
+    core->watch_add(main_dispatcher.base.recv_fd, SPICE_WATCH_EVENT_READ,
+                    dispatcher_handle_read, &main_dispatcher.base);
+    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
+                                main_dispatcher_handle_channel_event,
+                                sizeof(MainDispatcherChannelEventMessage), 0 /* no ack */);
+    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
+                                main_dispatcher_handle_migrate_complete,
+                                sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), 0 /* no ack */);
+    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
+                                main_dispatcher_handle_mm_time_latency,
+                                sizeof(MainDispatcherMmTimeLatencyMessage), 0 /* no ack */);
+    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
+                                main_dispatcher_handle_client_disconnect,
+                                sizeof(MainDispatcherClientDisconnectMessage), 0 /* no ack */);
+}
diff --git a/server/main-dispatcher.h b/server/main-dispatcher.h
new file mode 100644
index 0000000..af40093
--- /dev/null
+++ b/server/main-dispatcher.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009-2015 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifndef MAIN_DISPATCHER_H
+#define MAIN_DISPATCHER_H
+
+#include <spice.h>
+#include "red_channel.h"
+
+void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info);
+void main_dispatcher_seamless_migrate_dst_complete(RedClient *client);
+void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency);
+/*
+ * Disconnecting the client is always executed asynchronously,
+ * in order to protect from expired references in the routines
+ * that triggered the client destruction.
+ */
+void main_dispatcher_client_disconnect(RedClient *client);
+
+void main_dispatcher_init(SpiceCoreInterface *core);
+
+#endif //MAIN_DISPATCHER_H
diff --git a/server/main_channel.c b/server/main_channel.c
deleted file mode 100644
index 1af6baa..0000000
--- a/server/main_channel.c
+++ /dev/null
@@ -1,1345 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <inttypes.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/socket.h>
-#include <netinet/in.h>
-#include <netinet/tcp.h>
-#include <arpa/inet.h>
-#include <netdb.h>
-#include <limits.h>
-#include <time.h>
-#include <pthread.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <ctype.h>
-
-#include "common/generated_server_marshallers.h"
-#include "common/messages.h"
-#include "common/ring.h"
-
-#include "demarshallers.h"
-#include "main_channel.h"
-#include "red_channel.h"
-#include "red_common.h"
-#include "reds.h"
-#include "migration_protocol.h"
-#include "main_dispatcher.h"
-#include "utils.h"
-
-#define ZERO_BUF_SIZE 4096
-
-#define NET_TEST_WARMUP_BYTES 0
-#define NET_TEST_BYTES (1024 * 250)
-
-#define PING_INTERVAL (1000 * 10)
-
-#define CLIENT_CONNECTIVITY_TIMEOUT (30*1000) // 30 seconds
-
-static uint8_t zero_page[ZERO_BUF_SIZE] = {0};
-
-enum {
-    PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST = PIPE_ITEM_TYPE_CHANNEL_BASE,
-    PIPE_ITEM_TYPE_MAIN_PING,
-    PIPE_ITEM_TYPE_MAIN_MOUSE_MODE,
-    PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED,
-    PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN,
-    PIPE_ITEM_TYPE_MAIN_AGENT_DATA,
-    PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA,
-    PIPE_ITEM_TYPE_MAIN_INIT,
-    PIPE_ITEM_TYPE_MAIN_NOTIFY,
-    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN,
-    PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS,
-    PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST,
-    PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME,
-    PIPE_ITEM_TYPE_MAIN_NAME,
-    PIPE_ITEM_TYPE_MAIN_UUID,
-    PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS,
-};
-
-typedef struct RefsPipeItem {
-    PipeItem base;
-    int *refs;
-} RefsPipeItem;
-
-typedef struct PingPipeItem {
-    PipeItem base;
-    int size;
-} PingPipeItem;
-
-typedef struct MouseModePipeItem {
-    PipeItem base;
-    int current_mode;
-    int is_client_mouse_allowed;
-} MouseModePipeItem;
-
-typedef struct TokensPipeItem {
-    PipeItem base;
-    int tokens;
-} TokensPipeItem;
-
-typedef struct AgentDataPipeItem {
-    PipeItem base;
-    uint8_t* data;
-    size_t len;
-    spice_marshaller_item_free_func free_data;
-    void *opaque;
-} AgentDataPipeItem;
-
-typedef struct InitPipeItem {
-    PipeItem base;
-    int connection_id;
-    int display_channels_hint;
-    int current_mouse_mode;
-    int is_client_mouse_allowed;
-    int multi_media_time;
-    int ram_hint;
-} InitPipeItem;
-
-typedef struct NamePipeItem {
-    PipeItem base;
-    SpiceMsgMainName msg;
-} NamePipeItem;
-
-typedef struct UuidPipeItem {
-    PipeItem base;
-    SpiceMsgMainUuid msg;
-} UuidPipeItem;
-
-typedef struct NotifyPipeItem {
-    PipeItem base;
-    char *msg;
-} NotifyPipeItem;
-
-typedef struct MultiMediaTimePipeItem {
-    PipeItem base;
-    int time;
-} MultiMediaTimePipeItem;
-
-struct MainChannelClient {
-    RedChannelClient base;
-    uint32_t connection_id;
-    uint32_t ping_id;
-    uint32_t net_test_id;
-    int net_test_stage;
-    uint64_t latency;
-    uint64_t bitrate_per_sec;
-#ifdef RED_STATISTICS
-    SpiceTimer *ping_timer;
-    int ping_interval;
-#endif
-    int mig_wait_connect;
-    int mig_connect_ok;
-    int mig_wait_prev_complete;
-    int mig_wait_prev_try_seamless;
-    int init_sent;
-    int seamless_mig_dst;
-};
-
-enum NetTestStage {
-    NET_TEST_STAGE_INVALID,
-    NET_TEST_STAGE_WARMUP,
-    NET_TEST_STAGE_LATENCY,
-    NET_TEST_STAGE_RATE,
-    NET_TEST_STAGE_COMPLETE,
-};
-
-static void main_channel_release_pipe_item(RedChannelClient *rcc,
-                                           PipeItem *base, int item_pushed);
-
-int main_channel_is_connected(MainChannel *main_chan)
-{
-    return red_channel_is_connected(&main_chan->base);
-}
-
-/*
- * When the main channel is disconnected, disconnect the entire client.
- */
-static void main_channel_client_on_disconnect(RedChannelClient *rcc)
-{
-    spice_printerr("rcc=%p", rcc);
-    main_dispatcher_client_disconnect(rcc->client);
-}
-
-RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t connection_id)
-{
-    RingItem *link;
-    MainChannelClient *mcc;
-
-    RING_FOREACH(link, &main_chan->base.clients) {
-        mcc = SPICE_CONTAINEROF(link, MainChannelClient, base.channel_link);
-        if (mcc->connection_id == connection_id) {
-            return mcc->base.client;
-        }
-    }
-    return NULL;
-}
-
-static int main_channel_client_push_ping(MainChannelClient *mcc, int size);
-
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate)
-{
-    if (!mcc || mcc->net_test_id) {
-        return;
-    }
-    if (test_rate) {
-        if (main_channel_client_push_ping(mcc, NET_TEST_WARMUP_BYTES)
-            && main_channel_client_push_ping(mcc, 0)
-            && main_channel_client_push_ping(mcc, NET_TEST_BYTES)) {
-            mcc->net_test_id = mcc->ping_id - 2;
-            mcc->net_test_stage = NET_TEST_STAGE_WARMUP;
-        }
-    } else {
-        red_channel_client_start_connectivity_monitoring(&mcc->base, CLIENT_CONNECTIVITY_TIMEOUT);
-    }
-}
-
-typedef struct MainMouseModeItemInfo {
-    int current_mode;
-    int is_client_mouse_allowed;
-} MainMouseModeItemInfo;
-
-static PipeItem *main_mouse_mode_item_new(RedChannelClient *rcc, void *data, int num)
-{
-    MouseModePipeItem *item = spice_malloc(sizeof(MouseModePipeItem));
-    MainMouseModeItemInfo *info = data;
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_MOUSE_MODE);
-    item->current_mode = info->current_mode;
-    item->is_client_mouse_allowed = info->is_client_mouse_allowed;
-    return &item->base;
-}
-
-static PipeItem *main_ping_item_new(MainChannelClient *mcc, int size)
-{
-    PingPipeItem *item = spice_malloc(sizeof(PingPipeItem));
-
-    red_channel_pipe_item_init(mcc->base.channel, &item->base, PIPE_ITEM_TYPE_MAIN_PING);
-    item->size = size;
-    return &item->base;
-}
-
-static PipeItem *main_agent_tokens_item_new(RedChannelClient *rcc, uint32_t num_tokens)
-{
-    TokensPipeItem *item = spice_malloc(sizeof(TokensPipeItem));
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN);
-    item->tokens = num_tokens;
-    return &item->base;
-}
-
-static PipeItem *main_agent_data_item_new(RedChannelClient *rcc, uint8_t* data, size_t len,
-                                          spice_marshaller_item_free_func free_data,
-                                          void *opaque)
-{
-    AgentDataPipeItem *item = spice_malloc(sizeof(AgentDataPipeItem));
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_AGENT_DATA);
-    item->data = data;
-    item->len = len;
-    item->free_data = free_data;
-    item->opaque = opaque;
-    return &item->base;
-}
-
-static PipeItem *main_init_item_new(MainChannelClient *mcc,
-    int connection_id, int display_channels_hint, int current_mouse_mode,
-    int is_client_mouse_allowed, int multi_media_time,
-    int ram_hint)
-{
-    InitPipeItem *item = spice_malloc(sizeof(InitPipeItem));
-
-    red_channel_pipe_item_init(mcc->base.channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_INIT);
-    item->connection_id = connection_id;
-    item->display_channels_hint = display_channels_hint;
-    item->current_mouse_mode = current_mouse_mode;
-    item->is_client_mouse_allowed = is_client_mouse_allowed;
-    item->multi_media_time = multi_media_time;
-    item->ram_hint = ram_hint;
-    return &item->base;
-}
-
-static PipeItem *main_name_item_new(MainChannelClient *mcc, const char *name)
-{
-    NamePipeItem *item = spice_malloc(sizeof(NamePipeItem) + strlen(name) + 1);
-
-    red_channel_pipe_item_init(mcc->base.channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_NAME);
-    item->msg.name_len = strlen(name) + 1;
-    memcpy(&item->msg.name, name, item->msg.name_len);
-
-    return &item->base;
-}
-
-static PipeItem *main_uuid_item_new(MainChannelClient *mcc, const uint8_t uuid[16])
-{
-    UuidPipeItem *item = spice_malloc(sizeof(UuidPipeItem));
-
-    red_channel_pipe_item_init(mcc->base.channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_UUID);
-    memcpy(item->msg.uuid, uuid, sizeof(item->msg.uuid));
-
-    return &item->base;
-}
-
-static PipeItem *main_notify_item_new(RedChannelClient *rcc, void *data, int num)
-{
-    NotifyPipeItem *item = spice_malloc(sizeof(NotifyPipeItem));
-    const char *msg = data;
-
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_NOTIFY);
-    item->msg = spice_strdup(msg);
-    return &item->base;
-}
-
-static PipeItem *main_multi_media_time_item_new(
-    RedChannelClient *rcc, void *data, int num)
-{
-    MultiMediaTimePipeItem *item, *info = data;
-
-    item = spice_malloc(sizeof(MultiMediaTimePipeItem));
-    red_channel_pipe_item_init(rcc->channel, &item->base,
-                               PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME);
-    item->time = info->time;
-    return &item->base;
-}
-
-static void main_channel_push_channels(MainChannelClient *mcc)
-{
-    if (red_client_during_migrate_at_target(mcc->base.client)) {
-        spice_printerr("warning: ignoring unexpected SPICE_MSGC_MAIN_ATTACH_CHANNELS"
-                   "during migration");
-        return;
-    }
-    red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST);
-}
-
-static void main_channel_marshall_channels(RedChannelClient *rcc,
-                                           SpiceMarshaller *m,
-                                           PipeItem *item)
-{
-    SpiceMsgChannels* channels_info;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST, item);
-    channels_info = (SpiceMsgChannels *)spice_malloc(sizeof(SpiceMsgChannels)
-                            + reds_num_of_channels() * sizeof(SpiceChannelId));
-    reds_fill_channels(channels_info);
-    spice_marshall_msg_main_channels_list(m, channels_info);
-    free(channels_info);
-}
-
-int main_channel_client_push_ping(MainChannelClient *mcc, int size)
-{
-    PipeItem *item;
-
-    if (mcc == NULL) {
-        return FALSE;
-    }
-    item = main_ping_item_new(mcc, size);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-    return TRUE;
-}
-
-static void main_channel_marshall_ping(RedChannelClient *rcc,
-                                       SpiceMarshaller *m,
-                                       PingPipeItem *item)
-{
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-    struct timespec time_space;
-    SpiceMsgPing ping;
-    int size_left = item->size;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_PING, &item->base);
-    ping.id = ++(mcc->ping_id);
-    clock_gettime(CLOCK_MONOTONIC, &time_space);
-    ping.timestamp = time_space.tv_sec * 1000000LL + time_space.tv_nsec / 1000LL;
-    spice_marshall_msg_ping(m, &ping);
-
-    while (size_left > 0) {
-        int now = MIN(ZERO_BUF_SIZE, size_left);
-        size_left -= now;
-        spice_marshaller_add_ref(m, zero_page, now);
-    }
-}
-
-void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode,
-                                  int is_client_mouse_allowed)
-{
-    MainMouseModeItemInfo info = {
-        .current_mode=current_mode,
-        .is_client_mouse_allowed=is_client_mouse_allowed,
-    };
-
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_mouse_mode_item_new, &info);
-}
-
-static void main_channel_marshall_mouse_mode(RedChannelClient *rcc,
-                                             SpiceMarshaller *m,
-                                             MouseModePipeItem *item)
-{
-    SpiceMsgMainMouseMode mouse_mode;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MOUSE_MODE, &item->base);
-    mouse_mode.supported_modes = SPICE_MOUSE_MODE_SERVER;
-    if (item->is_client_mouse_allowed) {
-        mouse_mode.supported_modes |= SPICE_MOUSE_MODE_CLIENT;
-    }
-    mouse_mode.current_mode = item->current_mode;
-    spice_marshall_msg_main_mouse_mode(m, &mouse_mode);
-}
-
-void main_channel_push_agent_connected(MainChannel *main_chan)
-{
-    if (red_channel_test_remote_cap(&main_chan->base, SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)) {
-        red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS);
-    } else {
-        red_channel_pipes_add_empty_msg(&main_chan->base, SPICE_MSG_MAIN_AGENT_CONNECTED);
-    }
-}
-
-static void main_channel_marshall_agent_connected(SpiceMarshaller *m,
-                                                  RedChannelClient *rcc,
-                                                  PipeItem *item)
-{
-    SpiceMsgMainAgentConnectedTokens connected;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS, item);
-    connected.num_tokens = REDS_AGENT_WINDOW_SIZE;
-    spice_marshall_msg_main_agent_connected_tokens(m, &connected);
-}
-
-void main_channel_push_agent_disconnected(MainChannel *main_chan)
-{
-    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED);
-}
-
-static void main_channel_marshall_agent_disconnected(RedChannelClient *rcc,
-                                                     SpiceMarshaller *m,
-                                                     PipeItem *item)
-{
-    SpiceMsgMainAgentDisconnect disconnect;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DISCONNECTED, item);
-    disconnect.error_code = SPICE_LINK_ERR_OK;
-    spice_marshall_msg_main_agent_disconnected(m, &disconnect);
-}
-
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens)
-{
-    PipeItem *item = main_agent_tokens_item_new(&mcc->base, num_tokens);
-
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-static void main_channel_marshall_tokens(RedChannelClient *rcc,
-                                         SpiceMarshaller *m, TokensPipeItem *item)
-{
-    SpiceMsgMainAgentTokens tokens;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_TOKEN, &item->base);
-    tokens.num_tokens = item->tokens;
-    spice_marshall_msg_main_agent_token(m, &tokens);
-}
-
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
-           spice_marshaller_item_free_func free_data, void *opaque)
-{
-    PipeItem *item;
-
-    item = main_agent_data_item_new(&mcc->base, data, len, free_data, opaque);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-static void main_channel_marshall_agent_data(RedChannelClient *rcc,
-                                             SpiceMarshaller *m,
-                                             AgentDataPipeItem *item)
-{
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_AGENT_DATA, &item->base);
-    spice_marshaller_add_ref(m, item->data, item->len);
-}
-
-static void main_channel_push_migrate_data_item(MainChannel *main_chan)
-{
-    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA);
-}
-
-static void main_channel_marshall_migrate_data_item(RedChannelClient *rcc,
-                                                    SpiceMarshaller *m, PipeItem *item)
-{
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MIGRATE_DATA, item);
-    reds_marshall_migrate_data(m); // TODO: from reds split. ugly separation.
-}
-
-static int main_channel_handle_migrate_data(RedChannelClient *rcc,
-    uint32_t size, void *message)
-{
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-    SpiceMigrateDataHeader *header = (SpiceMigrateDataHeader *)message;
-
-    /* not supported with multi-clients */
-    spice_assert(rcc->channel->clients_num == 1);
-
-    if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataMain)) {
-        spice_printerr("bad message size %u", size);
-        return FALSE;
-    }
-    if (!migration_protocol_validate_header(header,
-                                            SPICE_MIGRATE_DATA_MAIN_MAGIC,
-                                            SPICE_MIGRATE_DATA_MAIN_VERSION)) {
-        spice_error("bad header");
-        return FALSE;
-    }
-    return reds_handle_migrate_data(mcc, (SpiceMigrateDataMain *)(header + 1), size);
-}
-
-void main_channel_push_init(MainChannelClient *mcc,
-    int display_channels_hint, int current_mouse_mode,
-    int is_client_mouse_allowed, int multi_media_time,
-    int ram_hint)
-{
-    PipeItem *item;
-
-    item = main_init_item_new(mcc,
-             mcc->connection_id, display_channels_hint, current_mouse_mode,
-             is_client_mouse_allowed, multi_media_time, ram_hint);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-static void main_channel_marshall_init(RedChannelClient *rcc,
-                                       SpiceMarshaller *m,
-                                       InitPipeItem *item)
-{
-    SpiceMsgMainInit init; // TODO - remove this copy, make InitPipeItem reuse SpiceMsgMainInit
-
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_INIT, &item->base);
-    init.session_id = item->connection_id;
-    init.display_channels_hint = item->display_channels_hint;
-    init.current_mouse_mode = item->current_mouse_mode;
-    init.supported_mouse_modes = SPICE_MOUSE_MODE_SERVER;
-    if (item->is_client_mouse_allowed) {
-        init.supported_mouse_modes |= SPICE_MOUSE_MODE_CLIENT;
-    }
-    init.agent_connected = reds_has_vdagent();
-    init.agent_tokens = REDS_AGENT_WINDOW_SIZE;
-    init.multi_media_time = item->multi_media_time;
-    init.ram_hint = item->ram_hint;
-    spice_marshall_msg_main_init(m, &init);
-}
-
-void main_channel_push_name(MainChannelClient *mcc, const char *name)
-{
-    PipeItem *item;
-
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_NAME_AND_UUID))
-        return;
-
-    item = main_name_item_new(mcc, name);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16])
-{
-    PipeItem *item;
-
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_NAME_AND_UUID))
-        return;
-
-    item = main_uuid_item_new(mcc, uuid);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg)
-{
-    PipeItem *item = main_notify_item_new(&mcc->base, (void *)msg, 1);
-    red_channel_client_pipe_add_push(&mcc->base, item);
-}
-
-static void main_channel_marshall_notify(RedChannelClient *rcc,
-                                         SpiceMarshaller *m, NotifyPipeItem *item)
-{
-    SpiceMsgNotify notify;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_NOTIFY, &item->base);
-    notify.time_stamp = red_get_monotonic_time(); // TODO - move to main_new_notify_item
-    notify.severity = SPICE_NOTIFY_SEVERITY_WARN;
-    notify.visibilty = SPICE_NOTIFY_VISIBILITY_HIGH;
-    notify.what = SPICE_WARN_GENERAL;
-    notify.message_len = strlen(item->msg);
-    spice_marshall_msg_notify(m, &notify);
-    spice_marshaller_add(m, (uint8_t *)item->msg, notify.message_len + 1);
-}
-
-static void main_channel_fill_migrate_dst_info(MainChannel *main_channel,
-                                               SpiceMigrationDstInfo *dst_info)
-{
-    RedsMigSpice *mig_dst = &main_channel->mig_target;
-    dst_info->port = mig_dst->port;
-    dst_info->sport = mig_dst->sport;
-    dst_info->host_size = strlen(mig_dst->host) + 1;
-    dst_info->host_data = (uint8_t *)mig_dst->host;
-    if (mig_dst->cert_subject) {
-        dst_info->cert_subject_size = strlen(mig_dst->cert_subject) + 1;
-        dst_info->cert_subject_data = (uint8_t *)mig_dst->cert_subject;
-    } else {
-        dst_info->cert_subject_size = 0;
-        dst_info->cert_subject_data = NULL;
-    }
-}
-
-static void main_channel_marshall_migrate_begin(SpiceMarshaller *m, RedChannelClient *rcc,
-                                                PipeItem *item)
-{
-    SpiceMsgMainMigrationBegin migrate;
-    MainChannel *main_ch;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN, item);
-    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    main_channel_fill_migrate_dst_info(main_ch, &migrate.dst_info);
-    spice_marshall_msg_main_migrate_begin(m, &migrate);
-}
-
-static void main_channel_marshall_migrate_begin_seamless(SpiceMarshaller *m,
-                                                         RedChannelClient *rcc,
-                                                         PipeItem *item)
-{
-    SpiceMsgMainMigrateBeginSeamless migrate_seamless;
-    MainChannel *main_ch;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS, item);
-    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    main_channel_fill_migrate_dst_info(main_ch, &migrate_seamless.dst_info);
-    migrate_seamless.src_mig_version = SPICE_MIGRATION_PROTOCOL_VERSION;
-    spice_marshall_msg_main_migrate_begin_seamless(m, &migrate_seamless);
-}
-
-void main_channel_push_multi_media_time(MainChannel *main_chan, int time)
-{
-    MultiMediaTimePipeItem info = {
-        .time = time,
-    };
-
-    red_channel_pipes_new_add_push(&main_chan->base,
-        main_multi_media_time_item_new, &info);
-}
-
-static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice *mig_target)
-{
-    spice_assert(mig_target);
-    free(main_channel->mig_target.host);
-    main_channel->mig_target.host = spice_strdup(mig_target->host);
-    free(main_channel->mig_target.cert_subject);
-    if (mig_target->cert_subject) {
-        main_channel->mig_target.cert_subject = spice_strdup(mig_target->cert_subject);
-    } else {
-        main_channel->mig_target.cert_subject = NULL;
-    }
-    main_channel->mig_target.port = mig_target->port;
-    main_channel->mig_target.sport = mig_target->sport;
-}
-
-void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target)
-{
-    main_channel_fill_mig_target(main_chan, mig_target);
-    red_channel_pipes_add_type(&main_chan->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
-}
-
-static void main_channel_marshall_migrate_switch(SpiceMarshaller *m, RedChannelClient *rcc,
-                                                 PipeItem *item)
-{
-    SpiceMsgMainMigrationSwitchHost migrate;
-    MainChannel *main_ch;
-
-    spice_printerr("");
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST, item);
-    main_ch = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    migrate.port = main_ch->mig_target.port;
-    migrate.sport = main_ch->mig_target.sport;
-    migrate.host_size = strlen(main_ch->mig_target.host) + 1;
-    migrate.host_data = (uint8_t *)main_ch->mig_target.host;
-    if (main_ch->mig_target.cert_subject) {
-        migrate.cert_subject_size = strlen(main_ch->mig_target.cert_subject) + 1;
-        migrate.cert_subject_data = (uint8_t *)main_ch->mig_target.cert_subject;
-    } else {
-        migrate.cert_subject_size = 0;
-        migrate.cert_subject_data = NULL;
-    }
-    spice_marshall_msg_main_migrate_switch_host(m, &migrate);
-}
-
-static void main_channel_marshall_multi_media_time(RedChannelClient *rcc,
-                                                   SpiceMarshaller *m,
-                                                   MultiMediaTimePipeItem *item)
-{
-    SpiceMsgMainMultiMediaTime time_mes;
-
-    red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_MULTI_MEDIA_TIME, &item->base);
-    time_mes.time = item->time;
-    spice_marshall_msg_main_multi_media_time(m, &time_mes);
-}
-
-static void main_channel_send_item(RedChannelClient *rcc, PipeItem *base)
-{
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-    SpiceMarshaller *m = red_channel_client_get_marshaller(rcc);
-
-    /* In semi-seamless migration (dest side), the connection is started from scratch, and
-     * we ignore any pipe item that arrives before the INIT msg is sent.
-     * For seamless we don't send INIT, and the connection continues from the same place
-     * it stopped on the src side. */
-    if (!mcc->init_sent && !mcc->seamless_mig_dst && base->type != PIPE_ITEM_TYPE_MAIN_INIT) {
-        spice_printerr("Init msg for client %p was not sent yet "
-                       "(client is probably during semi-seamless migration). Ignoring msg type %d",
-                   rcc->client, base->type);
-        main_channel_release_pipe_item(rcc, base, FALSE);
-        return;
-    }
-    switch (base->type) {
-        case PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST:
-            main_channel_marshall_channels(rcc, m, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_PING:
-            main_channel_marshall_ping(rcc, m,
-                SPICE_CONTAINEROF(base, PingPipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MOUSE_MODE:
-            {
-                MouseModePipeItem *item =
-                    SPICE_CONTAINEROF(base, MouseModePipeItem, base);
-                main_channel_marshall_mouse_mode(rcc, m, item);
-                break;
-            }
-        case PIPE_ITEM_TYPE_MAIN_AGENT_DISCONNECTED:
-            main_channel_marshall_agent_disconnected(rcc, m, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_AGENT_TOKEN:
-            main_channel_marshall_tokens(rcc, m,
-                SPICE_CONTAINEROF(base, TokensPipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA:
-            main_channel_marshall_agent_data(rcc, m,
-                SPICE_CONTAINEROF(base, AgentDataPipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MIGRATE_DATA:
-            main_channel_marshall_migrate_data_item(rcc, m, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_INIT:
-            mcc->init_sent = TRUE;
-            main_channel_marshall_init(rcc, m,
-                SPICE_CONTAINEROF(base, InitPipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_NOTIFY:
-            main_channel_marshall_notify(rcc, m,
-                SPICE_CONTAINEROF(base, NotifyPipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN:
-            main_channel_marshall_migrate_begin(m, rcc, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS:
-            main_channel_marshall_migrate_begin_seamless(m, rcc, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MULTI_MEDIA_TIME:
-            main_channel_marshall_multi_media_time(rcc, m,
-                SPICE_CONTAINEROF(base, MultiMediaTimePipeItem, base));
-            break;
-        case PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST:
-            main_channel_marshall_migrate_switch(m, rcc, base);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_NAME:
-            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_NAME, base);
-            spice_marshall_msg_main_name(m, &SPICE_CONTAINEROF(base, NamePipeItem, base)->msg);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_UUID:
-            red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_UUID, base);
-            spice_marshall_msg_main_uuid(m, &SPICE_CONTAINEROF(base, UuidPipeItem, base)->msg);
-            break;
-        case PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS:
-            main_channel_marshall_agent_connected(m, rcc, base);
-            break;
-        default:
-            break;
-    };
-    red_channel_client_begin_send_message(rcc);
-}
-
-static void main_channel_release_pipe_item(RedChannelClient *rcc,
-    PipeItem *base, int item_pushed)
-{
-    switch (base->type) {
-        case PIPE_ITEM_TYPE_MAIN_AGENT_DATA: {
-                AgentDataPipeItem *data = (AgentDataPipeItem *)base;
-
-                data->free_data(data->data, data->opaque);
-                break;
-        }
-        case PIPE_ITEM_TYPE_MAIN_NOTIFY: {
-                NotifyPipeItem *data = (NotifyPipeItem *)base;
-                free(data->msg);
-                break;
-        }
-        default:
-            break;
-    }
-    free(base);
-}
-
-static void main_channel_client_handle_migrate_connected(MainChannelClient *mcc,
-                                                         int success,
-                                                         int seamless)
-{
-    spice_printerr("client %p connected: %d seamless %d", mcc->base.client, success, seamless);
-    if (mcc->mig_wait_connect) {
-        MainChannel *main_channel = SPICE_CONTAINEROF(mcc->base.channel, MainChannel, base);
-
-        mcc->mig_wait_connect = FALSE;
-        mcc->mig_connect_ok = success;
-        spice_assert(main_channel->num_clients_mig_wait);
-        spice_assert(!seamless || main_channel->num_clients_mig_wait == 1);
-        if (!--main_channel->num_clients_mig_wait) {
-            reds_on_main_migrate_connected(seamless && success);
-        }
-    } else {
-        if (success) {
-            spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
-            red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-        }
-    }
-}
-
-void main_channel_client_handle_migrate_dst_do_seamless(MainChannelClient *mcc,
-                                                        uint32_t src_version)
-{
-    if (reds_on_migrate_dst_set_seamless(mcc, src_version)) {
-        mcc->seamless_mig_dst = TRUE;
-        red_channel_client_pipe_add_empty_msg(&mcc->base,
-                                             SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK);
-    } else {
-        red_channel_client_pipe_add_empty_msg(&mcc->base,
-                                              SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK);
-    }
-}
-
-void main_channel_client_handle_migrate_end(MainChannelClient *mcc)
-{
-    if (!red_client_during_migrate_at_target(mcc->base.client)) {
-        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END");
-        return;
-    }
-    if (!red_channel_client_test_remote_cap(&mcc->base,
-                                            SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
-        spice_printerr("unexpected SPICE_MSGC_MIGRATE_END, "
-                   "client does not support semi-seamless migration");
-            return;
-    }
-    red_client_semi_seamless_migrate_complete(mcc->base.client);
-}
-
-void main_channel_migrate_dst_complete(MainChannelClient *mcc)
-{
-    if (mcc->mig_wait_prev_complete) {
-        if (mcc->mig_wait_prev_try_seamless) {
-            spice_assert(mcc->base.channel->clients_num == 1);
-            red_channel_client_pipe_add_type(&mcc->base,
-                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
-        } else {
-            red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
-        }
-        mcc->mig_wait_connect = TRUE;
-        mcc->mig_wait_prev_complete = FALSE;
-    }
-}
-
-static int main_channel_handle_parsed(RedChannelClient *rcc, uint32_t size, uint16_t type,
-                                      void *message)
-{
-    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-
-    switch (type) {
-    case SPICE_MSGC_MAIN_AGENT_START: {
-        SpiceMsgcMainAgentStart *tokens;
-
-        spice_printerr("agent start");
-        if (!main_chan) {
-            return FALSE;
-        }
-        tokens = (SpiceMsgcMainAgentStart *)message;
-        reds_on_main_agent_start(mcc, tokens->num_tokens);
-        break;
-    }
-    case SPICE_MSGC_MAIN_AGENT_DATA: {
-        reds_on_main_agent_data(mcc, message, size);
-        break;
-    }
-    case SPICE_MSGC_MAIN_AGENT_TOKEN: {
-        SpiceMsgcMainAgentTokens *tokens;
-
-        tokens = (SpiceMsgcMainAgentTokens *)message;
-        reds_on_main_agent_tokens(mcc, tokens->num_tokens);
-        break;
-    }
-    case SPICE_MSGC_MAIN_ATTACH_CHANNELS:
-        main_channel_push_channels(mcc);
-        break;
-    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED:
-        main_channel_client_handle_migrate_connected(mcc,
-                                                     TRUE /* success */,
-                                                     FALSE /* seamless */);
-        break;
-    case SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS:
-        main_channel_client_handle_migrate_connected(mcc,
-                                                     TRUE /* success */,
-                                                     TRUE /* seamless */);
-        break;
-    case SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR:
-        main_channel_client_handle_migrate_connected(mcc, FALSE, FALSE);
-        break;
-    case SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS:
-        main_channel_client_handle_migrate_dst_do_seamless(mcc,
-            ((SpiceMsgcMainMigrateDstDoSeamless *)message)->src_version);
-        break;
-    case SPICE_MSGC_MAIN_MOUSE_MODE_REQUEST:
-        reds_on_main_mouse_mode_request(message, size);
-        break;
-    case SPICE_MSGC_PONG: {
-        SpiceMsgPing *ping = (SpiceMsgPing *)message;
-        uint64_t roundtrip;
-        struct timespec ts;
-
-        clock_gettime(CLOCK_MONOTONIC, &ts);
-        roundtrip = ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL - ping->timestamp;
-
-        if (ping->id == mcc->net_test_id) {
-            switch (mcc->net_test_stage) {
-            case NET_TEST_STAGE_WARMUP:
-                mcc->net_test_id++;
-                mcc->net_test_stage = NET_TEST_STAGE_LATENCY;
-                mcc->latency = roundtrip;
-                break;
-            case NET_TEST_STAGE_LATENCY:
-                mcc->net_test_id++;
-                mcc->net_test_stage = NET_TEST_STAGE_RATE;
-                mcc->latency = MIN(mcc->latency, roundtrip);
-                break;
-            case NET_TEST_STAGE_RATE:
-                mcc->net_test_id = 0;
-                if (roundtrip <= mcc->latency) {
-                    // probably high load on client or server result with incorrect values
-                    spice_printerr("net test: invalid values, latency %" PRIu64
-                                   " roundtrip %" PRIu64 ". assuming high"
-                                   " bandwidth", mcc->latency, roundtrip);
-                    mcc->latency = 0;
-                    mcc->net_test_stage = NET_TEST_STAGE_INVALID;
-                    red_channel_client_start_connectivity_monitoring(&mcc->base,
-                                                                     CLIENT_CONNECTIVITY_TIMEOUT);
-                    break;
-                }
-                mcc->bitrate_per_sec = (uint64_t)(NET_TEST_BYTES * 8) * 1000000
-                                        / (roundtrip - mcc->latency);
-                mcc->net_test_stage = NET_TEST_STAGE_COMPLETE;
-                spice_printerr("net test: latency %f ms, bitrate %"PRIu64" bps (%f Mbps)%s",
-                           (double)mcc->latency / 1000,
-                           mcc->bitrate_per_sec,
-                           (double)mcc->bitrate_per_sec / 1024 / 1024,
-                           main_channel_client_is_low_bandwidth(mcc) ? " LOW BANDWIDTH" : "");
-                red_channel_client_start_connectivity_monitoring(&mcc->base,
-                                                                 CLIENT_CONNECTIVITY_TIMEOUT);
-                break;
-            default:
-                spice_printerr("invalid net test stage, ping id %d test id %d stage %d",
-                           ping->id,
-                           mcc->net_test_id,
-                           mcc->net_test_stage);
-                mcc->net_test_stage = NET_TEST_STAGE_INVALID;
-            }
-            break;
-        } else {
-            /*
-             * channel client monitors the connectivity using ping-pong messages
-             */
-            red_channel_client_handle_message(rcc, size, type, message);
-        }
-#ifdef RED_STATISTICS
-        reds_update_stat_value(roundtrip);
-#endif
-        break;
-    }
-    case SPICE_MSGC_DISCONNECTING:
-        break;
-    case SPICE_MSGC_MAIN_MIGRATE_END:
-        main_channel_client_handle_migrate_end(mcc);
-        break;
-    default:
-        return red_channel_client_handle_message(rcc, size, type, message);
-    }
-    return TRUE;
-}
-
-static uint8_t *main_channel_alloc_msg_rcv_buf(RedChannelClient *rcc,
-                                               uint16_t type,
-                                               uint32_t size)
-{
-    MainChannel *main_chan = SPICE_CONTAINEROF(rcc->channel, MainChannel, base);
-    MainChannelClient *mcc = SPICE_CONTAINEROF(rcc, MainChannelClient, base);
-
-    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
-        return reds_get_agent_data_buffer(mcc, size);
-    } else {
-        return main_chan->recv_buf;
-    }
-}
-
-static void main_channel_release_msg_rcv_buf(RedChannelClient *rcc,
-                                               uint16_t type,
-                                               uint32_t size,
-                                               uint8_t *msg)
-{
-    if (type == SPICE_MSGC_MAIN_AGENT_DATA) {
-        reds_release_agent_data_buffer(msg);
-    }
-}
-
-static int main_channel_config_socket(RedChannelClient *rcc)
-{
-    return TRUE;
-}
-
-static void main_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item)
-{
-}
-
-static int main_channel_handle_migrate_flush_mark(RedChannelClient *rcc)
-{
-    spice_debug(NULL);
-    main_channel_push_migrate_data_item(SPICE_CONTAINEROF(rcc->channel,
-                                        MainChannel, base));
-    return TRUE;
-}
-
-#ifdef RED_STATISTICS
-static void do_ping_client(MainChannelClient *mcc,
-    const char *opt, int has_interval, int interval)
-{
-    spice_printerr("");
-    if (!opt) {
-        main_channel_client_push_ping(mcc, 0);
-    } else if (!strcmp(opt, "on")) {
-        if (has_interval && interval > 0) {
-            mcc->ping_interval = interval * 1000;
-        }
-        core->timer_start(mcc->ping_timer, mcc->ping_interval);
-    } else if (!strcmp(opt, "off")) {
-        core->timer_cancel(mcc->ping_timer);
-    } else {
-        return;
-    }
-}
-
-static void ping_timer_cb(void *opaque)
-{
-    MainChannelClient *mcc = opaque;
-
-    if (!red_channel_client_is_connected(&mcc->base)) {
-        spice_printerr("not connected to peer, ping off");
-        core->timer_cancel(mcc->ping_timer);
-        return;
-    }
-    do_ping_client(mcc, NULL, 0, 0);
-    core->timer_start(mcc->ping_timer, mcc->ping_interval);
-}
-#endif /* RED_STATISTICS */
-
-static MainChannelClient *main_channel_client_create(MainChannel *main_chan, RedClient *client,
-                                                     RedsStream *stream, uint32_t connection_id,
-                                                     int num_common_caps, uint32_t *common_caps,
-                                                     int num_caps, uint32_t *caps)
-{
-    MainChannelClient *mcc = (MainChannelClient*)
-                             red_channel_client_create(sizeof(MainChannelClient), &main_chan->base,
-                                                       client, stream, FALSE, num_common_caps,
-                                                       common_caps, num_caps, caps);
-    spice_assert(mcc != NULL);
-    mcc->connection_id = connection_id;
-    mcc->bitrate_per_sec = ~0;
-#ifdef RED_STATISTICS
-    if (!(mcc->ping_timer = core->timer_add(ping_timer_cb, NULL))) {
-        spice_error("ping timer create failed");
-    }
-    mcc->ping_interval = PING_INTERVAL;
-#endif
-    return mcc;
-}
-
-MainChannelClient *main_channel_link(MainChannel *channel, RedClient *client,
-                                     RedsStream *stream, uint32_t connection_id, int migration,
-                                     int num_common_caps, uint32_t *common_caps, int num_caps,
-                                     uint32_t *caps)
-{
-    MainChannelClient *mcc;
-
-    spice_assert(channel);
-
-    // TODO - migration - I removed it from channel creation, now put it
-    // into usage somewhere (not an issue until we return migration to it's
-    // former glory)
-    spice_printerr("add main channel client");
-    mcc = main_channel_client_create(channel, client, stream, connection_id,
-                                     num_common_caps, common_caps,
-                                     num_caps, caps);
-    return mcc;
-}
-
-int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
-{
-    return main_chan ? getsockname(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
-}
-
-int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen)
-{
-    return main_chan ? getpeername(red_channel_get_first_socket(&main_chan->base), sa, salen) : -1;
-}
-
-// TODO: ? shouldn't it disonnect all clients? or shutdown all main_channels?
-void main_channel_close(MainChannel *main_chan)
-{
-    int socketfd;
-
-    if (main_chan && (socketfd = red_channel_get_first_socket(&main_chan->base)) != -1) {
-        close(socketfd);
-    }
-}
-
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc)
-{
-    return mcc->net_test_stage == NET_TEST_STAGE_COMPLETE;
-}
-
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc)
-{
-    // TODO: configurable?
-    return mcc->bitrate_per_sec < 10 * 1024 * 1024;
-}
-
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
-{
-    return mcc->bitrate_per_sec;
-}
-
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
-{
-    return mcc->latency / 1000;
-}
-
-static void main_channel_client_migrate(RedChannelClient *rcc)
-{
-    reds_on_main_channel_migrate(SPICE_CONTAINEROF(rcc, MainChannelClient, base));
-    red_channel_client_default_migrate(rcc);
-}
-
-MainChannel* main_channel_init(void)
-{
-    RedChannel *channel;
-    ChannelCbs channel_cbs = { NULL, };
-    ClientCbs client_cbs = {NULL, };
-
-    channel_cbs.config_socket = main_channel_config_socket;
-    channel_cbs.on_disconnect = main_channel_client_on_disconnect;
-    channel_cbs.send_item = main_channel_send_item;
-    channel_cbs.hold_item = main_channel_hold_pipe_item;
-    channel_cbs.release_item = main_channel_release_pipe_item;
-    channel_cbs.alloc_recv_buf = main_channel_alloc_msg_rcv_buf;
-    channel_cbs.release_recv_buf = main_channel_release_msg_rcv_buf;
-    channel_cbs.handle_migrate_flush_mark = main_channel_handle_migrate_flush_mark;
-    channel_cbs.handle_migrate_data = main_channel_handle_migrate_data;
-
-    // TODO: set the migration flag of the channel
-    channel = red_channel_create_parser(sizeof(MainChannel), core,
-                                        SPICE_CHANNEL_MAIN, 0,
-                                        FALSE, /* handle_acks */
-                                        spice_get_client_channel_parser(SPICE_CHANNEL_MAIN, NULL),
-                                        main_channel_handle_parsed,
-                                        &channel_cbs,
-                                        SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER);
-    spice_assert(channel);
-    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
-    red_channel_set_cap(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE);
-
-    client_cbs.migrate = main_channel_client_migrate;
-    red_channel_register_client_cbs(channel, &client_cbs);
-
-    return (MainChannel *)channel;
-}
-
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc)
-{
-    spice_assert(mcc);
-    return &mcc->base;
-}
-
-static int main_channel_connect_semi_seamless(MainChannel *main_channel)
-{
-    RingItem *client_link;
-
-    RING_FOREACH(client_link, &main_channel->base.clients) {
-        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
-                                                    base.channel_link);
-        if (red_channel_client_test_remote_cap(&mcc->base,
-                                               SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)) {
-            if (red_client_during_migrate_at_target(mcc->base.client)) {
-                spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
-                mcc->mig_wait_prev_complete = TRUE;
-                mcc->mig_wait_prev_try_seamless = FALSE;
-            } else {
-                red_channel_client_pipe_add_type(&mcc->base,
-                                                 PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN);
-                mcc->mig_wait_connect = TRUE;
-            }
-            mcc->mig_connect_ok = FALSE;
-            main_channel->num_clients_mig_wait++;
-        }
-    }
-    return main_channel->num_clients_mig_wait;
-}
-
-static int main_channel_connect_seamless(MainChannel *main_channel)
-{
-    RingItem *client_link;
-
-    spice_assert(main_channel->base.clients_num == 1);
-
-    RING_FOREACH(client_link, &main_channel->base.clients) {
-        MainChannelClient * mcc = SPICE_CONTAINEROF(client_link, MainChannelClient,
-                                                    base.channel_link);
-        spice_assert(red_channel_client_test_remote_cap(&mcc->base,
-                                                        SPICE_MAIN_CAP_SEAMLESS_MIGRATE));
-        if (red_client_during_migrate_at_target(mcc->base.client)) {
-           spice_printerr("client %p: wait till previous migration completes", mcc->base.client);
-           mcc->mig_wait_prev_complete = TRUE;
-           mcc->mig_wait_prev_try_seamless = TRUE;
-        } else {
-            red_channel_client_pipe_add_type(&mcc->base,
-                                             PIPE_ITEM_TYPE_MAIN_MIGRATE_BEGIN_SEAMLESS);
-            mcc->mig_wait_connect = TRUE;
-        }
-        mcc->mig_connect_ok = FALSE;
-        main_channel->num_clients_mig_wait++;
-    }
-    return main_channel->num_clients_mig_wait;
-}
-
-int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
-                                 int try_seamless)
-{
-    main_channel_fill_mig_target(main_channel, mig_target);
-    main_channel->num_clients_mig_wait = 0;
-
-    if (!main_channel_is_connected(main_channel)) {
-        return 0;
-    }
-
-    if (!try_seamless) {
-        return main_channel_connect_semi_seamless(main_channel);
-    } else {
-        RingItem *client_item;
-        MainChannelClient *mcc;
-
-        client_item = ring_get_head(&main_channel->base.clients);
-        mcc = SPICE_CONTAINEROF(client_item, MainChannelClient, base.channel_link);
-
-        if (!red_channel_client_test_remote_cap(&mcc->base,
-                                                SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) {
-            return main_channel_connect_semi_seamless(main_channel);
-        } else {
-            return main_channel_connect_seamless(main_channel);
-        }
-    }
-
-}
-
-void main_channel_migrate_cancel_wait(MainChannel *main_chan)
-{
-    RingItem *client_link;
-
-    RING_FOREACH(client_link, &main_chan->base.clients) {
-        MainChannelClient *mcc;
-
-        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
-        if (mcc->mig_wait_connect) {
-            spice_printerr("client %p cancel wait connect", mcc->base.client);
-            mcc->mig_wait_connect = FALSE;
-            mcc->mig_connect_ok = FALSE;
-        }
-        mcc->mig_wait_prev_complete = FALSE;
-    }
-    main_chan->num_clients_mig_wait = 0;
-}
-
-int main_channel_migrate_src_complete(MainChannel *main_chan, int success)
-{
-    RingItem *client_link;
-    int semi_seamless_count = 0;
-
-    spice_printerr("");
-
-    if (ring_is_empty(&main_chan->base.clients)) {
-        spice_printerr("no peer connected");
-        return 0;
-    }
-
-    RING_FOREACH(client_link, &main_chan->base.clients) {
-        MainChannelClient *mcc;
-        int semi_seamless_support;
-
-        mcc = SPICE_CONTAINEROF(client_link, MainChannelClient, base.channel_link);
-        semi_seamless_support = red_channel_client_test_remote_cap(&mcc->base,
-                                                   SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE);
-        if (semi_seamless_support && mcc->mig_connect_ok) {
-            if (success) {
-                spice_printerr("client %p MIGRATE_END", mcc->base.client);
-                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_END);
-                semi_seamless_count++;
-            } else {
-                spice_printerr("client %p MIGRATE_CANCEL", mcc->base.client);
-                red_channel_client_pipe_add_empty_msg(&mcc->base, SPICE_MSG_MAIN_MIGRATE_CANCEL);
-            }
-        } else {
-            if (success) {
-                spice_printerr("client %p SWITCH_HOST", mcc->base.client);
-                red_channel_client_pipe_add_type(&mcc->base, PIPE_ITEM_TYPE_MAIN_MIGRATE_SWITCH_HOST);
-            }
-        }
-        mcc->mig_connect_ok = FALSE;
-        mcc->mig_wait_connect = FALSE;
-   }
-   return semi_seamless_count;
-}
diff --git a/server/main_channel.h b/server/main_channel.h
deleted file mode 100644
index 9bd20f1..0000000
--- a/server/main_channel.h
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef __MAIN_CHANNEL_H__
-#define __MAIN_CHANNEL_H__
-
-#include <stdint.h>
-#include <spice/vd_agent.h>
-#include "common/marshaller.h"
-#include "red_channel.h"
-
-// TODO: Defines used to calculate receive buffer size, and also by reds.c
-// other options: is to make a reds_main_consts.h, to duplicate defines.
-#define REDS_AGENT_WINDOW_SIZE 10
-#define REDS_NUM_INTERNAL_AGENT_MESSAGES 1
-
-// approximate max receive message size for main channel
-#define MAIN_CHANNEL_RECEIVE_BUF_SIZE \
-    (4096 + (REDS_AGENT_WINDOW_SIZE + REDS_NUM_INTERNAL_AGENT_MESSAGES) * SPICE_AGENT_MAX_DATA_SIZE)
-
-struct RedsMigSpice {
-    char *host;
-    char *cert_subject;
-    int port;
-    int sport;
-};
-typedef struct RedsMigSpice RedsMigSpice;
-
-typedef struct MainChannel {
-    RedChannel base;
-    uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE];
-    RedsMigSpice mig_target; // TODO: add refs and release (afrer all clients completed migration in one way or the other?)
-    int num_clients_mig_wait;
-} MainChannel;
-
-
-MainChannel *main_channel_init(void);
-RedClient *main_channel_get_client_by_link_id(MainChannel *main_chan, uint32_t link_id);
-/* This is a 'clone' from the reds.h Channel.link callback to allow passing link_id */
-MainChannelClient *main_channel_link(MainChannel *, RedClient *client,
-     RedsStream *stream, uint32_t link_id, int migration, int num_common_caps,
-     uint32_t *common_caps, int num_caps, uint32_t *caps);
-void main_channel_close(MainChannel *main_chan); // not destroy, just socket close
-void main_channel_push_mouse_mode(MainChannel *main_chan, int current_mode, int is_client_mouse_allowed);
-void main_channel_push_agent_connected(MainChannel *main_chan);
-void main_channel_push_agent_disconnected(MainChannel *main_chan);
-void main_channel_client_push_agent_tokens(MainChannelClient *mcc, uint32_t num_tokens);
-void main_channel_client_push_agent_data(MainChannelClient *mcc, uint8_t* data, size_t len,
-                                         spice_marshaller_item_free_func free_data, void *opaque);
-void main_channel_client_start_net_test(MainChannelClient *mcc, int test_rate);
-// TODO: huge. Consider making a reds_* interface for these functions
-// and calling from main.
-void main_channel_push_init(MainChannelClient *mcc, int display_channels_hint,
-    int current_mouse_mode, int is_client_mouse_allowed, int multi_media_time,
-    int ram_hint);
-void main_channel_client_push_notify(MainChannelClient *mcc, const char *msg);
-void main_channel_push_multi_media_time(MainChannel *main_chan, int time);
-int main_channel_getsockname(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
-int main_channel_getpeername(MainChannel *main_chan, struct sockaddr *sa, socklen_t *salen);
-
-/*
- * return TRUE if network test had been completed successfully.
- * If FALSE, bitrate_per_sec is set to MAX_UINT64 and the roundtrip is set to 0
- */
-int main_channel_client_is_network_info_initialized(MainChannelClient *mcc);
-int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
-uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
-uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
-
-int main_channel_is_connected(MainChannel *main_chan);
-RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
-
-/* switch host migration */
-void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target);
-
-/* semi seamless migration */
-
-/* returns the number of clients that we are waiting for their connection.
- * try_seamless = 'true' when the seamless-migration=on in qemu command line */
-int main_channel_migrate_connect(MainChannel *main_channel, RedsMigSpice *mig_target,
-                                 int try_seamless);
-void main_channel_migrate_cancel_wait(MainChannel *main_chan);
-/* returns the number of clients for which SPICE_MSG_MAIN_MIGRATE_END was sent*/
-int main_channel_migrate_src_complete(MainChannel *main_chan, int success);
-void main_channel_migrate_dst_complete(MainChannelClient *mcc);
-void main_channel_push_name(MainChannelClient *mcc, const char *name);
-void main_channel_push_uuid(MainChannelClient *mcc, const uint8_t uuid[16]);
-
-#endif
diff --git a/server/main_dispatcher.c b/server/main_dispatcher.c
deleted file mode 100644
index 6ad9d89..0000000
--- a/server/main_dispatcher.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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 <config.h>
-#include <unistd.h>
-#include <string.h>
-#include <errno.h>
-#include <pthread.h>
-
-#include "red_common.h"
-#include "dispatcher.h"
-#include "main_dispatcher.h"
-#include "red_channel.h"
-#include "reds.h"
-
-/*
- * Main Dispatcher
- * ===============
- *
- * Communication channel between any non main thread and the main thread.
- *
- * The main thread is that from which spice_server_init is called.
- *
- * Messages are single sized, sent from the non-main thread to the main-thread.
- * No acknowledge is sent back. This prevents a possible deadlock with the main
- * thread already waiting on a response for the existing red_dispatcher used
- * by the worker thread.
- *
- * All events have three functions:
- * main_dispatcher_<event_name> - non static, public function
- * main_dispatcher_self_<event_name> - handler for main thread
- * main_dispatcher_handle_<event_name> - handler for callback from main thread
- *   seperate from self because it may send an ack or do other work in the future.
- */
-
-typedef struct {
-    Dispatcher base;
-    SpiceCoreInterface *core;
-} MainDispatcher;
-
-MainDispatcher main_dispatcher;
-
-enum {
-    MAIN_DISPATCHER_CHANNEL_EVENT = 0,
-    MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
-    MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
-    MAIN_DISPATCHER_CLIENT_DISCONNECT,
-
-    MAIN_DISPATCHER_NUM_MESSAGES
-};
-
-typedef struct MainDispatcherChannelEventMessage {
-    int event;
-    SpiceChannelEventInfo *info;
-} MainDispatcherChannelEventMessage;
-
-typedef struct MainDispatcherMigrateSeamlessDstCompleteMessage {
-    RedClient *client;
-} MainDispatcherMigrateSeamlessDstCompleteMessage;
-
-typedef struct MainDispatcherMmTimeLatencyMessage {
-    RedClient *client;
-    uint32_t latency;
-} MainDispatcherMmTimeLatencyMessage;
-
-typedef struct MainDispatcherClientDisconnectMessage {
-    RedClient *client;
-} MainDispatcherClientDisconnectMessage;
-
-/* channel_event - calls core->channel_event, must be done in main thread */
-static void main_dispatcher_self_handle_channel_event(
-                                                int event,
-                                                SpiceChannelEventInfo *info)
-{
-    reds_handle_channel_event(event, info);
-}
-
-static void main_dispatcher_handle_channel_event(void *opaque,
-                                                 void *payload)
-{
-    MainDispatcherChannelEventMessage *channel_event = payload;
-
-    main_dispatcher_self_handle_channel_event(channel_event->event,
-                                              channel_event->info);
-}
-
-void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info)
-{
-    MainDispatcherChannelEventMessage msg = {0,};
-
-    if (pthread_self() == main_dispatcher.base.self) {
-        main_dispatcher_self_handle_channel_event(event, info);
-        return;
-    }
-    msg.event = event;
-    msg.info = info;
-    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
-                            &msg);
-}
-
-
-static void main_dispatcher_handle_migrate_complete(void *opaque,
-                                                    void *payload)
-{
-    MainDispatcherMigrateSeamlessDstCompleteMessage *mig_complete = payload;
-
-    reds_on_client_seamless_migrate_complete(mig_complete->client);
-    red_client_unref(mig_complete->client);
-}
-
-static void main_dispatcher_handle_mm_time_latency(void *opaque,
-                                                   void *payload)
-{
-    MainDispatcherMmTimeLatencyMessage *msg = payload;
-    reds_set_client_mm_time_latency(msg->client, msg->latency);
-    red_client_unref(msg->client);
-}
-
-static void main_dispatcher_handle_client_disconnect(void *opaque,
-                                                     void *payload)
-{
-    MainDispatcherClientDisconnectMessage *msg = payload;
-
-    spice_debug("client=%p", msg->client);
-    reds_client_disconnect(msg->client);
-    red_client_unref(msg->client);
-}
-
-void main_dispatcher_seamless_migrate_dst_complete(RedClient *client)
-{
-    MainDispatcherMigrateSeamlessDstCompleteMessage msg;
-
-    if (pthread_self() == main_dispatcher.base.self) {
-        reds_on_client_seamless_migrate_complete(client);
-        return;
-    }
-
-    msg.client = red_client_ref(client);
-    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
-                            &msg);
-}
-
-void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency)
-{
-    MainDispatcherMmTimeLatencyMessage msg;
-
-    if (pthread_self() == main_dispatcher.base.self) {
-        reds_set_client_mm_time_latency(client, latency);
-        return;
-    }
-
-    msg.client = red_client_ref(client);
-    msg.latency = latency;
-    dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
-                            &msg);
-}
-
-void main_dispatcher_client_disconnect(RedClient *client)
-{
-    MainDispatcherClientDisconnectMessage msg;
-
-    if (!client->disconnecting) {
-        spice_debug("client %p", client);
-        msg.client = red_client_ref(client);
-        dispatcher_send_message(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
-                                &msg);
-    } else {
-        spice_debug("client %p already during disconnection", client);
-    }
-}
-
-static void dispatcher_handle_read(int fd, int event, void *opaque)
-{
-    Dispatcher *dispatcher = opaque;
-
-    dispatcher_handle_recv_read(dispatcher);
-}
-
-/*
- * FIXME:
- * Reds routines shouldn't be exposed. Instead reds.c should register the callbacks,
- * and the corresponding operations should be made only via main_dispatcher.
- */
-void main_dispatcher_init(SpiceCoreInterface *core)
-{
-    memset(&main_dispatcher, 0, sizeof(main_dispatcher));
-    main_dispatcher.core = core;
-    dispatcher_init(&main_dispatcher.base, MAIN_DISPATCHER_NUM_MESSAGES, &main_dispatcher.base);
-    core->watch_add(main_dispatcher.base.recv_fd, SPICE_WATCH_EVENT_READ,
-                    dispatcher_handle_read, &main_dispatcher.base);
-    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CHANNEL_EVENT,
-                                main_dispatcher_handle_channel_event,
-                                sizeof(MainDispatcherChannelEventMessage), 0 /* no ack */);
-    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
-                                main_dispatcher_handle_migrate_complete,
-                                sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), 0 /* no ack */);
-    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
-                                main_dispatcher_handle_mm_time_latency,
-                                sizeof(MainDispatcherMmTimeLatencyMessage), 0 /* no ack */);
-    dispatcher_register_handler(&main_dispatcher.base, MAIN_DISPATCHER_CLIENT_DISCONNECT,
-                                main_dispatcher_handle_client_disconnect,
-                                sizeof(MainDispatcherClientDisconnectMessage), 0 /* no ack */);
-}
diff --git a/server/main_dispatcher.h b/server/main_dispatcher.h
deleted file mode 100644
index af40093..0000000
--- a/server/main_dispatcher.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifndef MAIN_DISPATCHER_H
-#define MAIN_DISPATCHER_H
-
-#include <spice.h>
-#include "red_channel.h"
-
-void main_dispatcher_channel_event(int event, SpiceChannelEventInfo *info);
-void main_dispatcher_seamless_migrate_dst_complete(RedClient *client);
-void main_dispatcher_set_mm_time_latency(RedClient *client, uint32_t latency);
-/*
- * Disconnecting the client is always executed asynchronously,
- * in order to protect from expired references in the routines
- * that triggered the client destruction.
- */
-void main_dispatcher_client_disconnect(RedClient *client);
-
-void main_dispatcher_init(SpiceCoreInterface *core);
-
-#endif //MAIN_DISPATCHER_H
diff --git a/server/memslot.c b/server/memslot.c
new file mode 100644
index 0000000..6b5bb8d
--- /dev/null
+++ b/server/memslot.c
@@ -0,0 +1,183 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009,2010 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <inttypes.h>
+
+#include "memslot.h"
+
+static unsigned long __get_clean_virt(RedMemSlotInfo *info, QXLPHYSICAL addr)
+{
+    return addr & info->memslot_clean_virt_mask;
+}
+
+static void print_memslots(RedMemSlotInfo *info)
+{
+    int i;
+    int x;
+
+    for (i = 0; i < info->num_memslots_groups; ++i) {
+        for (x = 0; x < info->num_memslots; ++x) {
+            if (!info->mem_slots[i][x].virt_start_addr &&
+                !info->mem_slots[i][x].virt_end_addr) {
+                continue;
+            }
+            printf("id %d, group %d, virt start %lx, virt end %lx, generation %u, delta %lx\n",
+                   x, i, info->mem_slots[i][x].virt_start_addr,
+                   info->mem_slots[i][x].virt_end_addr, info->mem_slots[i][x].generation,
+                   info->mem_slots[i][x].address_delta);
+            }
+    }
+}
+
+/* return 1 if validation successfull, 0 otherwise */
+int memslot_validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
+                  uint32_t add_size, uint32_t group_id)
+{
+    MemSlot *slot;
+
+    slot = &info->mem_slots[group_id][slot_id];
+    if ((virt + add_size) < virt) {
+        spice_critical("virtual address overlap");
+        return 0;
+    }
+
+    if (virt < slot->virt_start_addr || (virt + add_size) > slot->virt_end_addr) {
+        print_memslots(info);
+        spice_critical("virtual address out of range\n"
+              "    virt=0x%lx+0x%x slot_id=%d group_id=%d\n"
+              "    slot=0x%lx-0x%lx delta=0x%lx",
+              virt, add_size, slot_id, group_id,
+              slot->virt_start_addr, slot->virt_end_addr, slot->address_delta);
+        return 0;
+    }
+    return 1;
+}
+
+/*
+ * return virtual address if successful, which may be 0.
+ * returns 0 and sets error to 1 if an error condition occurs.
+ */
+unsigned long memslot_get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
+                       int group_id, int *error)
+{
+    int slot_id;
+    int generation;
+    unsigned long h_virt;
+
+    MemSlot *slot;
+
+    *error = 0;
+    if (group_id > info->num_memslots_groups) {
+        spice_critical("group_id too big");
+        *error = 1;
+        return 0;
+    }
+
+    slot_id = memslot_get_id(info, addr);
+    if (slot_id > info->num_memslots) {
+        print_memslots(info);
+        spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr);
+        *error = 1;
+        return 0;
+    }
+
+    slot = &info->mem_slots[group_id][slot_id];
+
+    generation = memslot_get_generation(info, addr);
+    if (generation != slot->generation) {
+        print_memslots(info);
+        spice_critical("address generation is not valid, group_id %d, slot_id %d, gen %d, slot_gen %d\n",
+              group_id, slot_id, generation, slot->generation);
+        *error = 1;
+        return 0;
+    }
+
+    h_virt = __get_clean_virt(info, addr);
+    h_virt += slot->address_delta;
+
+    if (!memslot_validate_virt(info, h_virt, slot_id, add_size, group_id)) {
+        *error = 1;
+        return 0;
+    }
+
+    return h_virt;
+}
+
+void memslot_info_init(RedMemSlotInfo *info,
+                           uint32_t num_groups, uint32_t num_slots,
+                           uint8_t generation_bits,
+                           uint8_t id_bits,
+                           uint8_t internal_groupslot_id)
+{
+    uint32_t i;
+
+    spice_return_if_fail(num_slots > 0);
+    spice_return_if_fail(num_groups > 0);
+
+    info->num_memslots_groups = num_groups;
+    info->num_memslots = num_slots;
+    info->generation_bits = generation_bits;
+    info->mem_slot_bits = id_bits;
+    info->internal_groupslot_id = internal_groupslot_id;
+
+    info->mem_slots = spice_new(MemSlot *, num_groups);
+
+    for (i = 0; i < num_groups; ++i) {
+        info->mem_slots[i] = spice_new0(MemSlot, num_slots);
+    }
+
+    /* TODO: use QXLPHYSICAL_BITS */
+    info->memslot_id_shift = 64 - info->mem_slot_bits;
+    info->memslot_gen_shift = 64 - (info->mem_slot_bits + info->generation_bits);
+    info->memslot_gen_mask = ~((QXLPHYSICAL)-1 << info->generation_bits);
+    info->memslot_clean_virt_mask = (((QXLPHYSICAL)(-1)) >>
+                                       (info->mem_slot_bits + info->generation_bits));
+}
+
+void memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
+                               uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
+                               uint32_t generation)
+{
+    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
+    spice_return_if_fail(info->num_memslots > slot_id);
+
+    info->mem_slots[slot_group_id][slot_id].address_delta = addr_delta;
+    info->mem_slots[slot_group_id][slot_id].virt_start_addr = virt_start;
+    info->mem_slots[slot_group_id][slot_id].virt_end_addr = virt_end;
+    info->mem_slots[slot_group_id][slot_id].generation = generation;
+}
+
+void memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id)
+{
+    spice_return_if_fail(info->num_memslots_groups > slot_group_id);
+    spice_return_if_fail(info->num_memslots > slot_id);
+
+    info->mem_slots[slot_group_id][slot_id].virt_start_addr = 0;
+    info->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
+}
+
+void memslot_info_reset(RedMemSlotInfo *info)
+{
+        uint32_t i;
+        for (i = 0; i < info->num_memslots_groups; ++i) {
+            memset(info->mem_slots[i], 0, sizeof(MemSlot) * info->num_memslots);
+        }
+}
diff --git a/server/memslot.h b/server/memslot.h
new file mode 100644
index 0000000..6f02f90
--- /dev/null
+++ b/server/memslot.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009,2010 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_MEMSLOT
+#define _H_MEMSLOT
+
+#include "red_common.h"
+
+#include <spice/qxl_dev.h>
+
+typedef struct MemSlot {
+    int generation;
+    unsigned long virt_start_addr;
+    unsigned long virt_end_addr;
+    long address_delta;
+} MemSlot;
+
+typedef struct RedMemSlotInfo {
+    MemSlot **mem_slots;
+    uint32_t num_memslots_groups;
+    uint32_t num_memslots;
+    uint8_t mem_slot_bits;
+    uint8_t generation_bits;
+    uint8_t memslot_id_shift;
+    uint8_t memslot_gen_shift;
+    uint8_t internal_groupslot_id;
+    unsigned long memslot_gen_mask;
+    unsigned long memslot_clean_virt_mask;
+} RedMemSlotInfo;
+
+static inline int memslot_get_id           (RedMemSlotInfo *info, uint64_t addr)
+{
+    return addr >> info->memslot_id_shift;
+}
+
+static inline int memslot_get_generation   (RedMemSlotInfo *info, uint64_t addr)
+{
+    return (addr >> info->memslot_gen_shift) & info->memslot_gen_mask;
+}
+
+int               memslot_validate_virt    (RedMemSlotInfo *info, unsigned long virt, int slot_id,
+                                            uint32_t add_size, uint32_t group_id);
+unsigned long     memslot_get_virt         (RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
+                                            int group_id, int *error);
+void              memslot_info_init        (RedMemSlotInfo *info,
+                                            uint32_t num_groups, uint32_t num_slots,
+                                            uint8_t generation_bits,
+                                            uint8_t id_bits,
+                                            uint8_t internal_groupslot_id);
+void              memslot_info_add_slot    (RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
+                                            uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
+                                            uint32_t generation);
+void              memslot_info_del_slot    (RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id);
+void              memslot_info_reset       (RedMemSlotInfo *info);
+
+#endif /* MEMSLOT_H_ */
diff --git a/server/migration-protocol.h b/server/migration-protocol.h
new file mode 100644
index 0000000..c1d97ef
--- /dev/null
+++ b/server/migration-protocol.h
@@ -0,0 +1,213 @@
+/*
+   Copyright (C) 2012 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_MIGRATION_PROTOCOL
+#define _H_MIGRATION_PROTOCOL
+
+#include <spice/macros.h>
+#include <spice/vd_agent.h>
+#include "glz-encoder-dict.h"
+
+/* ************************************************
+ * src-server to dst-server migration data messages
+ * ************************************************/
+
+/* increase the version when the version of any
+ * of the migration data messages is increased */
+#define SPICE_MIGRATION_PROTOCOL_VERSION 1
+
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataHeader {
+    uint32_t magic;
+    uint32_t version;
+} SpiceMigrateDataHeader;
+
+/* ********************
+ * Char device base
+ * *******************/
+
+/* increase the version of descendent char devices when this
+ * version is increased */
+#define SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION 1
+
+/* Should be the first field of any of the char_devices migration data (see write_data_ptr) */
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataCharDevice {
+    uint32_t version;
+    uint8_t connected;
+    uint32_t num_client_tokens;
+    uint32_t num_send_tokens;
+    uint32_t write_size; /* write to dev */
+    uint32_t write_num_client_tokens; /* how many messages from the client are part of the write_data */
+    uint32_t write_data_ptr; /* offset from
+                                SpiceMigrateDataCharDevice - sizeof(SpiceMigrateDataHeader) */
+} SpiceMigrateDataCharDevice;
+
+/* ********
+ * spicevmc
+ * ********/
+
+#define SPICE_MIGRATE_DATA_SPICEVMC_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
+                                                 is increased */
+#define SPICE_MIGRATE_DATA_SPICEVMC_MAGIC SPICE_MAGIC_CONST("SVMD")
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSpiceVmc {
+    SpiceMigrateDataCharDevice base;
+} SpiceMigrateDataSpiceVmc;
+
+/* *********
+ * smartcard
+ * *********/
+
+#define SPICE_MIGRATE_DATA_SMARTCARD_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
+                                                  is increased */
+#define SPICE_MIGRATE_DATA_SMARTCARD_MAGIC SPICE_MAGIC_CONST("SCMD")
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSmartcard {
+    SpiceMigrateDataCharDevice base;
+    uint8_t reader_added;
+    uint32_t read_size; /* partial data read from dev */
+    uint32_t read_data_ptr;
+} SpiceMigrateDataSmartcard;
+
+/* *********************************
+ * main channel (mainly guest agent)
+ * *********************************/
+#define SPICE_MIGRATE_DATA_MAIN_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
+                                             is increased */
+#define SPICE_MIGRATE_DATA_MAIN_MAGIC SPICE_MAGIC_CONST("MNMD")
+
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataMain {
+    SpiceMigrateDataCharDevice agent_base;
+    uint8_t client_agent_started; /* for discarding messages */
+
+    struct __attribute__ ((__packed__)) {
+        /* partial data read from device. Such data is stored only
+         * if the chunk header or the entire msg header haven't yet been read completely.
+         * Once the headers are read, partial reads of chunks can be sent as
+         * smaller chunks to the client, without the roundtrip overhead of migration data */
+        uint32_t chunk_header_size;
+        VDIChunkHeader chunk_header;
+        uint8_t msg_header_done;
+        uint32_t msg_header_partial_len;
+        uint32_t msg_header_ptr;
+        uint32_t msg_remaining;
+        uint8_t msg_filter_result;
+    } agent2client;
+
+    struct __attribute__ ((__packed__)) {
+        uint32_t msg_remaining;
+        uint8_t msg_filter_result;
+    } client2agent;
+} SpiceMigrateDataMain;
+
+/* ****************
+ * display channel
+ * ***************/
+
+#define SPICE_MIGRATE_DATA_DISPLAY_VERSION 1
+#define SPICE_MIGRATE_DATA_DISPLAY_MAGIC SPICE_MAGIC_CONST("DCMD")
+
+/*
+ * TODO: store the cache and dictionary data only in one channel (the
+ *       freezer).
+ * TODO: optimizations: don't send surfaces information if it will be faster
+ *       to resend the surfaces on-demand.
+ * */
+#define MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS 4
+
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataDisplay {
+    uint64_t message_serial;
+    uint8_t low_bandwidth_setting;
+
+    /*
+     * Synchronizing the shared pixmap cache.
+     * For now, the cache is not migrated, and instead, we reset it and send
+     * SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS to the client.
+     * In order to keep the client and server caches consistent:
+     * The channel which freezed the cache on the src side, unfreezes it
+     * on the dest side, and increases its generation (see 'reset' in red_client_shared_cach.h).
+     * In order to enforce that images that are added to the cache by other channels
+     * will reach the client only after SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,
+     * we send SPICE_MSG_WAIT_FOR_CHANNELS
+     * (see the generation mismatch handling in 'add' in red_client_shared_cach.h).
+     */
+    uint8_t pixmap_cache_id;
+    int64_t pixmap_cache_size;
+    uint8_t pixmap_cache_freezer;
+    uint64_t pixmap_cache_clients[MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS];
+
+    uint8_t glz_dict_id;
+    GlzEncDictRestoreData glz_dict_data;
+
+    uint32_t surfaces_at_client_ptr; /* reference to MigrateDisplaySurfacesAtClientLossless/Lossy.
+                                        Lossy: when jpeg-wan-compression(qemu cmd line)=always
+                                        or when jpeg-wan-compression=auto,
+                                        and low_bandwidth_setting=TRUE */
+
+} SpiceMigrateDataDisplay;
+
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataRect {
+    int32_t left;
+    int32_t top;
+    int32_t right;
+    int32_t bottom;
+} SpiceMigrateDataRect;
+
+typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossless {
+    uint32_t id;
+} MigrateDisplaySurfaceLossless;
+
+typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossy {
+    uint32_t id;
+    SpiceMigrateDataRect lossy_rect;
+} MigrateDisplaySurfaceLossy;
+
+typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossless {
+    uint32_t num_surfaces;
+    MigrateDisplaySurfaceLossless surfaces[0];
+} MigrateDisplaySurfacesAtClientLossless;
+
+typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossy {
+    uint32_t num_surfaces;
+    MigrateDisplaySurfaceLossy surfaces[0];
+} MigrateDisplaySurfacesAtClientLossy;
+
+/* ****************
+ * inputs channel
+ * ***************/
+
+#define SPICE_MIGRATE_DATA_INPUTS_VERSION 1
+#define SPICE_MIGRATE_DATA_INPUTS_MAGIC SPICE_MAGIC_CONST("ICMD")
+
+
+typedef struct __attribute__ ((__packed__)) SpiceMigrateDataInputs {
+    uint16_t motion_count;
+} SpiceMigrateDataInputs;
+
+static inline int migration_protocol_validate_header(SpiceMigrateDataHeader *header,
+                                                     uint32_t magic,
+                                                     uint32_t version)
+{
+    if (header->magic != magic) {
+        spice_error("bad magic %u (!= %u)", header->magic, magic);
+        return FALSE;
+    }
+    if (header->version > version) {
+        spice_error("unsupported version %u (> %u)", header->version, version);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+#endif
diff --git a/server/migration_protocol.h b/server/migration_protocol.h
deleted file mode 100644
index 21d3ec8..0000000
--- a/server/migration_protocol.h
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
-   Copyright (C) 2012 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_MIGRATION_PROTOCOL
-#define _H_MIGRATION_PROTOCOL
-
-#include <spice/macros.h>
-#include <spice/vd_agent.h>
-#include "glz_encoder_dictionary.h"
-
-/* ************************************************
- * src-server to dst-server migration data messages
- * ************************************************/
-
-/* increase the version when the version of any
- * of the migration data messages is increased */
-#define SPICE_MIGRATION_PROTOCOL_VERSION 1
-
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataHeader {
-    uint32_t magic;
-    uint32_t version;
-} SpiceMigrateDataHeader;
-
-/* ********************
- * Char device base
- * *******************/
-
-/* increase the version of descendent char devices when this
- * version is increased */
-#define SPICE_MIGRATE_DATA_CHAR_DEVICE_VERSION 1
-
-/* Should be the first field of any of the char_devices migration data (see write_data_ptr) */
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataCharDevice {
-    uint32_t version;
-    uint8_t connected;
-    uint32_t num_client_tokens;
-    uint32_t num_send_tokens;
-    uint32_t write_size; /* write to dev */
-    uint32_t write_num_client_tokens; /* how many messages from the client are part of the write_data */
-    uint32_t write_data_ptr; /* offset from
-                                SpiceMigrateDataCharDevice - sizeof(SpiceMigrateDataHeader) */
-} SpiceMigrateDataCharDevice;
-
-/* ********
- * spicevmc
- * ********/
-
-#define SPICE_MIGRATE_DATA_SPICEVMC_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
-                                                 is increased */
-#define SPICE_MIGRATE_DATA_SPICEVMC_MAGIC SPICE_MAGIC_CONST("SVMD")
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSpiceVmc {
-    SpiceMigrateDataCharDevice base;
-} SpiceMigrateDataSpiceVmc;
-
-/* *********
- * smartcard
- * *********/
-
-#define SPICE_MIGRATE_DATA_SMARTCARD_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
-                                                  is increased */
-#define SPICE_MIGRATE_DATA_SMARTCARD_MAGIC SPICE_MAGIC_CONST("SCMD")
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataSmartcard {
-    SpiceMigrateDataCharDevice base;
-    uint8_t reader_added;
-    uint32_t read_size; /* partial data read from dev */
-    uint32_t read_data_ptr;
-} SpiceMigrateDataSmartcard;
-
-/* *********************************
- * main channel (mainly guest agent)
- * *********************************/
-#define SPICE_MIGRATE_DATA_MAIN_VERSION 1 /* NOTE: increase version when CHAR_DEVICE_VERSION
-                                             is increased */
-#define SPICE_MIGRATE_DATA_MAIN_MAGIC SPICE_MAGIC_CONST("MNMD")
-
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataMain {
-    SpiceMigrateDataCharDevice agent_base;
-    uint8_t client_agent_started; /* for discarding messages */
-
-    struct __attribute__ ((__packed__)) {
-        /* partial data read from device. Such data is stored only
-         * if the chunk header or the entire msg header haven't yet been read completely.
-         * Once the headers are read, partial reads of chunks can be sent as
-         * smaller chunks to the client, without the roundtrip overhead of migration data */
-        uint32_t chunk_header_size;
-        VDIChunkHeader chunk_header;
-        uint8_t msg_header_done;
-        uint32_t msg_header_partial_len;
-        uint32_t msg_header_ptr;
-        uint32_t msg_remaining;
-        uint8_t msg_filter_result;
-    } agent2client;
-
-    struct __attribute__ ((__packed__)) {
-        uint32_t msg_remaining;
-        uint8_t msg_filter_result;
-    } client2agent;
-} SpiceMigrateDataMain;
-
-/* ****************
- * display channel
- * ***************/
-
-#define SPICE_MIGRATE_DATA_DISPLAY_VERSION 1
-#define SPICE_MIGRATE_DATA_DISPLAY_MAGIC SPICE_MAGIC_CONST("DCMD")
-
-/*
- * TODO: store the cache and dictionary data only in one channel (the
- *       freezer).
- * TODO: optimizations: don't send surfaces information if it will be faster
- *       to resend the surfaces on-demand.
- * */
-#define MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS 4
-
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataDisplay {
-    uint64_t message_serial;
-    uint8_t low_bandwidth_setting;
-
-    /*
-     * Synchronizing the shared pixmap cache.
-     * For now, the cache is not migrated, and instead, we reset it and send
-     * SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS to the client.
-     * In order to keep the client and server caches consistent:
-     * The channel which freezed the cache on the src side, unfreezes it
-     * on the dest side, and increases its generation (see 'reset' in red_client_shared_cach.h).
-     * In order to enforce that images that are added to the cache by other channels
-     * will reach the client only after SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS,
-     * we send SPICE_MSG_WAIT_FOR_CHANNELS
-     * (see the generation mismatch handling in 'add' in red_client_shared_cach.h).
-     */
-    uint8_t pixmap_cache_id;
-    int64_t pixmap_cache_size;
-    uint8_t pixmap_cache_freezer;
-    uint64_t pixmap_cache_clients[MIGRATE_DATA_DISPLAY_MAX_CACHE_CLIENTS];
-
-    uint8_t glz_dict_id;
-    GlzEncDictRestoreData glz_dict_data;
-
-    uint32_t surfaces_at_client_ptr; /* reference to MigrateDisplaySurfacesAtClientLossless/Lossy.
-                                        Lossy: when jpeg-wan-compression(qemu cmd line)=always
-                                        or when jpeg-wan-compression=auto,
-                                        and low_bandwidth_setting=TRUE */
-
-} SpiceMigrateDataDisplay;
-
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataRect {
-    int32_t left;
-    int32_t top;
-    int32_t right;
-    int32_t bottom;
-} SpiceMigrateDataRect;
-
-typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossless {
-    uint32_t id;
-} MigrateDisplaySurfaceLossless;
-
-typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfaceLossy {
-    uint32_t id;
-    SpiceMigrateDataRect lossy_rect;
-} MigrateDisplaySurfaceLossy;
-
-typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossless {
-    uint32_t num_surfaces;
-    MigrateDisplaySurfaceLossless surfaces[0];
-} MigrateDisplaySurfacesAtClientLossless;
-
-typedef struct __attribute__ ((__packed__)) MigrateDisplaySurfacesAtClientLossy {
-    uint32_t num_surfaces;
-    MigrateDisplaySurfaceLossy surfaces[0];
-} MigrateDisplaySurfacesAtClientLossy;
-
-/* ****************
- * inputs channel
- * ***************/
-
-#define SPICE_MIGRATE_DATA_INPUTS_VERSION 1
-#define SPICE_MIGRATE_DATA_INPUTS_MAGIC SPICE_MAGIC_CONST("ICMD")
-
-
-typedef struct __attribute__ ((__packed__)) SpiceMigrateDataInputs {
-    uint16_t motion_count;
-} SpiceMigrateDataInputs;
-
-static inline int migration_protocol_validate_header(SpiceMigrateDataHeader *header,
-                                                     uint32_t magic,
-                                                     uint32_t version)
-{
-    if (header->magic != magic) {
-        spice_error("bad magic %u (!= %u)", header->magic, magic);
-        return FALSE;
-    }
-    if (header->version > version) {
-        spice_error("unsupported version %u (> %u)", header->version, version);
-        return FALSE;
-    }
-    return TRUE;
-}
-
-#endif
diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c
new file mode 100644
index 0000000..04c95a6
--- /dev/null
+++ b/server/mjpeg-encoder.c
@@ -0,0 +1,1375 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "red_common.h"
+#include "mjpeg-encoder.h"
+#include "utils.h"
+#include <jerror.h>
+#include <jpeglib.h>
+#include <inttypes.h>
+
+#define MJPEG_MAX_FPS 25
+#define MJPEG_MIN_FPS 1
+
+#define MJPEG_QUALITY_SAMPLE_NUM 7
+static const int mjpeg_quality_samples[MJPEG_QUALITY_SAMPLE_NUM] = {20, 30, 40, 50, 60, 70, 80};
+
+#define MJPEG_LEGACY_STATIC_QUALITY_ID 5 // jpeg quality 70
+
+#define MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH 10
+#define MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH 5
+
+#define MJPEG_AVERAGE_SIZE_WINDOW 3
+
+#define MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES 3
+#define MJPEG_LOW_FPS_RATE_TH 3
+
+#define MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL 1
+#define MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH 0.1
+
+/*
+ * acting on positive client reports only if enough frame mm time
+ * has passed since the last bit rate change and the report.
+ * time
+ */
+#define MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT 2000
+#define MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT 3000
+
+#define MJPEG_ADJUST_FPS_TIMEOUT 500
+
+/*
+ * avoid interrupting the playback when there are temporary
+ * incidents of instability (with respect to server and client drops)
+ */
+#define MJPEG_MAX_CLIENT_PLAYBACK_DELAY 5000 // 5 sec
+
+/*
+ * The stream starts after lossless frames were sent to the client,
+ * and without rate control (except for pipe congestion). Thus, on the beginning
+ * of the stream, we might observe frame drops on the client and server side which
+ * are not necessarily related to mis-estimation of the bit rate, and we would
+ * like to wait till the stream stabilizes.
+ */
+#define MJPEG_WARMUP_TIME 3000LL // 3 sec
+
+enum {
+    MJPEG_QUALITY_EVAL_TYPE_SET,
+    MJPEG_QUALITY_EVAL_TYPE_UPGRADE,
+    MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE,
+};
+
+enum {
+    MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
+    MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
+};
+
+typedef struct MJpegEncoderQualityEval {
+    int type;
+    int reason;
+
+    uint64_t encoded_size_by_quality[MJPEG_QUALITY_SAMPLE_NUM];
+    /* lower limit for the current evaluation round */
+    int min_quality_id;
+    int min_quality_fps; // min fps for the given quality
+    /* upper limit for the current evaluation round */
+    int max_quality_id;
+    int max_quality_fps; // max fps for the given quality
+    /* tracking the best sampled fps so far */
+    int max_sampled_fps;
+    int max_sampled_fps_quality_id;
+} MJpegEncoderQualityEval;
+
+typedef struct MJpegEncoderClientState {
+    int max_video_latency;
+    uint32_t max_audio_latency;
+} MJpegEncoderClientState;
+
+typedef struct MJpegEncoderServerState {
+    uint32_t num_frames_encoded;
+    uint32_t num_frames_dropped;
+} MJpegEncoderServerState;
+
+typedef struct MJpegEncoderBitRateInfo {
+    uint64_t change_start_time;
+    uint64_t last_frame_time;
+    uint32_t change_start_mm_time;
+    int was_upgraded;
+
+    /* gathering data about the frames that
+     * were encoded since the last bit rate change*/
+    uint32_t num_enc_frames;
+    uint64_t sum_enc_size;
+} MJpegEncoderBitRateInfo;
+
+/*
+ * Adjusting the stream jpeg quality and frame rate (fps):
+ * When during_quality_eval=TRUE, we compress different frames with different
+ * jpeg quality. By considering (1) the resulting compression ratio, and (2) the available
+ * bit rate, we evaluate the max frame frequency for the stream with the given quality,
+ * and we choose the highest quality that will allow a reasonable frame rate.
+ * during_quality_eval is set for new streams and can also be set any time we want
+ * to re-evaluate the stream parameters (e.g., when the bit rate and/or
+ * compressed frame size significantly change).
+ */
+typedef struct MJpegEncoderRateControl {
+    int during_quality_eval;
+    MJpegEncoderQualityEval quality_eval_data;
+    MJpegEncoderBitRateInfo bit_rate_info;
+    MJpegEncoderClientState client_state;
+    MJpegEncoderServerState server_state;
+
+    uint64_t byte_rate;
+    int quality_id;
+    uint32_t fps;
+    double adjusted_fps;
+    uint64_t adjusted_fps_start_time;
+    uint64_t adjusted_fps_num_frames;
+
+    /* the encoded frame size which the quality and the fps evaluation was based upon */
+    uint64_t base_enc_size;
+
+    uint64_t last_enc_size;
+
+    uint64_t sum_recent_enc_size;
+    uint32_t num_recent_enc_frames;
+
+    uint64_t warmup_start_time;
+} MJpegEncoderRateControl;
+
+struct MJpegEncoder {
+    uint8_t *row;
+    uint32_t row_size;
+    int first_frame;
+
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+
+    unsigned int bytes_per_pixel; /* bytes per pixel of the input buffer */
+    void (*pixel_converter)(void *src, uint8_t *dest);
+
+    MJpegEncoderRateControl rate_control;
+    MJpegEncoderRateControlCbs cbs;
+    void *cbs_opaque;
+
+    /* stats */
+    uint64_t starting_bit_rate;
+    uint64_t avg_quality;
+    uint32_t num_frames;
+};
+
+static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder);
+static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
+                                                uint64_t byte_rate,
+                                                uint32_t latency);
+
+static inline int rate_control_is_active(MJpegEncoder* encoder)
+{
+    return encoder->cbs.get_roundtrip_ms != NULL;
+}
+
+void mjpeg_encoder_destroy(MJpegEncoder *encoder)
+{
+    free(encoder->cinfo.dest);
+    jpeg_destroy_compress(&encoder->cinfo);
+    free(encoder->row);
+    free(encoder);
+}
+
+static uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
+{
+    return encoder->bytes_per_pixel;
+}
+
+#ifndef JCS_EXTENSIONS
+/* Pixel conversion routines */
+static void pixel_rgb24bpp_to_24(void *src_ptr, uint8_t *dest)
+{
+    uint8_t *src = src_ptr;
+    /* libjpegs stores rgb, spice/win32 stores bgr */
+    *dest++ = src[2]; /* red */
+    *dest++ = src[1]; /* green */
+    *dest++ = src[0]; /* blue */
+}
+
+static void pixel_rgb32bpp_to_24(void *src, uint8_t *dest)
+{
+    uint32_t pixel = *(uint32_t *)src;
+    *dest++ = (pixel >> 16) & 0xff;
+    *dest++ = (pixel >>  8) & 0xff;
+    *dest++ = (pixel >>  0) & 0xff;
+}
+#endif
+
+static void pixel_rgb16bpp_to_24(void *src, uint8_t *dest)
+{
+    uint16_t pixel = *(uint16_t *)src;
+    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
+    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
+    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
+}
+
+
+/* code from libjpeg 8 to handle compression to a memory buffer
+ *
+ * Copyright (C) 1994-1996, Thomas G. Lane.
+ * Modified 2009 by Guido Vollbeding.
+ * This file is part of the Independent JPEG Group's software.
+ */
+typedef struct {
+  struct jpeg_destination_mgr pub; /* public fields */
+
+  unsigned char ** outbuffer;      /* target buffer */
+  size_t * outsize;
+  uint8_t * buffer;                /* start of buffer */
+  size_t bufsize;
+} mem_destination_mgr;
+
+static void init_mem_destination(j_compress_ptr cinfo)
+{
+}
+
+static boolean empty_mem_output_buffer(j_compress_ptr cinfo)
+{
+  size_t nextsize;
+  uint8_t * nextbuffer;
+  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
+
+  /* Try to allocate new buffer with double size */
+  nextsize = dest->bufsize * 2;
+  nextbuffer = malloc(nextsize);
+
+  if (nextbuffer == NULL)
+    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
+
+  memcpy(nextbuffer, dest->buffer, dest->bufsize);
+
+  free(dest->buffer);
+
+  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
+  dest->pub.free_in_buffer = dest->bufsize;
+
+  dest->buffer = nextbuffer;
+  dest->bufsize = nextsize;
+
+  return TRUE;
+}
+
+static void term_mem_destination(j_compress_ptr cinfo)
+{
+  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
+
+  *dest->outbuffer = dest->buffer;
+  *dest->outsize = dest->bufsize;
+}
+
+/*
+ * Prepare for output to a memory buffer.
+ * The caller may supply an own initial buffer with appropriate size.
+ * Otherwise, or when the actual data output exceeds the given size,
+ * the library adapts the buffer size as necessary.
+ * The standard library functions malloc/free are used for allocating
+ * larger memory, so the buffer is available to the application after
+ * finishing compression, and then the application is responsible for
+ * freeing the requested memory.
+ */
+
+static void
+spice_jpeg_mem_dest(j_compress_ptr cinfo,
+                    unsigned char ** outbuffer, size_t * outsize)
+{
+  mem_destination_mgr *dest;
+#define OUTPUT_BUF_SIZE  4096 /* choose an efficiently fwrite'able size */
+
+  if (outbuffer == NULL || outsize == NULL) /* sanity check */
+    ERREXIT(cinfo, JERR_BUFFER_SIZE);
+
+  /* The destination object is made permanent so that multiple JPEG images
+   * can be written to the same buffer without re-executing jpeg_mem_dest.
+   */
+  if (cinfo->dest == NULL) { /* first time for this JPEG object? */
+    cinfo->dest = spice_malloc(sizeof(mem_destination_mgr));
+  }
+
+  dest = (mem_destination_mgr *) cinfo->dest;
+  dest->pub.init_destination = init_mem_destination;
+  dest->pub.empty_output_buffer = empty_mem_output_buffer;
+  dest->pub.term_destination = term_mem_destination;
+  dest->outbuffer = outbuffer;
+  dest->outsize = outsize;
+  if (*outbuffer == NULL || *outsize == 0) {
+    /* Allocate initial buffer */
+    *outbuffer = malloc(OUTPUT_BUF_SIZE);
+    if (*outbuffer == NULL)
+      ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
+    *outsize = OUTPUT_BUF_SIZE;
+  }
+
+  dest->pub.next_output_byte = dest->buffer = *outbuffer;
+  dest->pub.free_in_buffer = dest->bufsize = *outsize;
+}
+/* end of code from libjpeg */
+
+static inline uint32_t mjpeg_encoder_get_source_fps(MJpegEncoder *encoder)
+{
+    return encoder->cbs.get_source_fps ?
+        encoder->cbs.get_source_fps(encoder->cbs_opaque) : MJPEG_MAX_FPS;
+}
+
+static inline uint32_t mjpeg_encoder_get_latency(MJpegEncoder *encoder)
+{
+    return encoder->cbs.get_roundtrip_ms ?
+        encoder->cbs.get_roundtrip_ms(encoder->cbs_opaque) / 2 : 0;
+}
+
+static uint32_t get_max_fps(uint64_t frame_size, uint64_t bytes_per_sec)
+{
+    double fps;
+    double send_time_ms;
+
+    if (!bytes_per_sec) {
+        return 0;
+    }
+    send_time_ms = frame_size * 1000.0 / bytes_per_sec;
+    fps = send_time_ms ? 1000 / send_time_ms : MJPEG_MAX_FPS;
+    return fps;
+}
+
+static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,
+                                               int quality_id,
+                                               uint32_t fps,
+                                               uint64_t frame_enc_size)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    double fps_ratio;
+
+    rate_control->during_quality_eval = FALSE;
+
+    if (rate_control->quality_id != quality_id) {
+        rate_control->last_enc_size = 0;
+    }
+
+    if (rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
+        memset(&rate_control->server_state, 0, sizeof(MJpegEncoderServerState));
+    }
+    rate_control->quality_id = quality_id;
+    memset(&rate_control->quality_eval_data, 0, sizeof(MJpegEncoderQualityEval));
+    rate_control->quality_eval_data.max_quality_id = MJPEG_QUALITY_SAMPLE_NUM - 1;
+    rate_control->quality_eval_data.max_quality_fps = MJPEG_MAX_FPS;
+
+    if (rate_control->adjusted_fps) {
+        fps_ratio = rate_control->adjusted_fps / rate_control->fps;
+    } else {
+        fps_ratio = 1.5;
+    }
+    rate_control->fps = MAX(MJPEG_MIN_FPS, fps);
+    rate_control->fps = MIN(MJPEG_MAX_FPS, rate_control->fps);
+    rate_control->adjusted_fps = rate_control->fps*fps_ratio;
+    spice_debug("adjusted-fps-ratio=%.2f adjusted-fps=%.2f", fps_ratio, rate_control->adjusted_fps);
+    rate_control->adjusted_fps_start_time = 0;
+    rate_control->adjusted_fps_num_frames = 0;
+    rate_control->base_enc_size = frame_enc_size;
+
+    rate_control->sum_recent_enc_size = 0;
+    rate_control->num_recent_enc_frames = 0;
+}
+
+#define QUALITY_WAS_EVALUATED(encoder, quality) \
+    ((encoder)->rate_control.quality_eval_data.encoded_size_by_quality[(quality)] != 0)
+
+/*
+ * Adjust the stream's jpeg quality and frame rate.
+ * We evaluate the compression ratio of different jpeg qualities;
+ * We compress successive frames with different qualities,
+ * and then we estimate the stream frame rate according to the currently
+ * evaluated jpeg quality and available bit rate.
+ *
+ * During quality evaluation, mjpeg_encoder_eval_quality is called before a new
+ * frame is encoded. mjpeg_encoder_eval_quality examines the encoding size of
+ * the previously encoded frame, and determines whether to continue evaluation
+ * (and chnages the quality for the frame that is going to be encoded),
+ * or stop evaluation (and sets the quality and frame rate for the stream).
+ * When qualities are scanned, we assume monotonicity of compression ratio
+ * as a function of jpeg quality. When we reach a quality with too small, or
+ * big enough compression ratio, we stop the evaluation and set the stream parameters.
+*/
+static inline void mjpeg_encoder_eval_quality(MJpegEncoder *encoder)
+{
+    MJpegEncoderRateControl *rate_control;
+    MJpegEncoderQualityEval *quality_eval;
+    uint32_t fps, src_fps;
+    uint64_t enc_size;
+    uint32_t final_quality_id;
+    uint32_t final_fps;
+    uint64_t final_quality_enc_size;
+
+    rate_control = &encoder->rate_control;
+    quality_eval = &rate_control->quality_eval_data;
+
+    spice_assert(rate_control->during_quality_eval);
+
+    /* retrieving the encoded size of the last encoded frame */
+    enc_size = quality_eval->encoded_size_by_quality[rate_control->quality_id];
+    if (enc_size == 0) {
+        spice_debug("size info missing");
+        return;
+    }
+
+    src_fps = mjpeg_encoder_get_source_fps(encoder);
+
+    fps = get_max_fps(enc_size, rate_control->byte_rate);
+    spice_debug("mjpeg %p: jpeg %d: %.2f (KB) fps %d src-fps %u",
+                encoder,
+                mjpeg_quality_samples[rate_control->quality_id],
+                enc_size / 1024.0,
+                fps,
+                src_fps);
+
+    if (fps > quality_eval->max_sampled_fps ||
+        ((fps == quality_eval->max_sampled_fps || fps >= src_fps) &&
+         rate_control->quality_id > quality_eval->max_sampled_fps_quality_id)) {
+        quality_eval->max_sampled_fps = fps;
+        quality_eval->max_sampled_fps_quality_id = rate_control->quality_id;
+    }
+
+    /*
+     * Choosing whether to evaluate another quality, or to complete evaluation
+     * and set the stream parameters according to one of the qualities that
+     * were already sampled.
+     */
+
+    if (rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2 &&
+        fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
+        fps < src_fps) {
+        /*
+         * When the jpeg quality is bigger than the median quality, prefer a reasonable
+         * frame rate over improving the quality
+         */
+        spice_debug("fps < %d && (fps < src_fps), quality %d",
+                MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH,
+                mjpeg_quality_samples[rate_control->quality_id]);
+        if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
+            /* the next worse quality was already evaluated and it passed the frame
+             * rate thresholds (we know that, because we continued evaluating a better
+             * quality) */
+            rate_control->quality_id--;
+            goto complete_sample;
+        } else {
+            /* evaluate the next worse quality */
+            rate_control->quality_id--;
+        }
+    } else if ((fps > MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH &&
+                fps >= 0.66 * quality_eval->min_quality_fps) || fps >= src_fps) {
+        /* When the jpeg quality is worse than the median one (see first condition), we allow a less
+           strict threshold for fps, in order to improve the jpeg quality */
+        if (rate_control->quality_id + 1 == MJPEG_QUALITY_SAMPLE_NUM ||
+            rate_control->quality_id >= quality_eval->max_quality_id ||
+            QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id + 1)) {
+            /* best quality has been reached, or the next (better) quality was
+             * already evaluated and didn't pass the fps thresholds */
+            goto complete_sample;
+        } else {
+            if (rate_control->quality_id == MJPEG_QUALITY_SAMPLE_NUM / 2 &&
+                fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
+                fps < src_fps) {
+                goto complete_sample;
+            }
+            /* evaluate the next quality as well*/
+            rate_control->quality_id++;
+        }
+    } else { // very small frame rate, try to improve by downgrading the quality
+        if (rate_control->quality_id == 0 ||
+            rate_control->quality_id <= quality_eval->min_quality_id) {
+            goto complete_sample;
+        } else if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
+            rate_control->quality_id--;
+            goto complete_sample;
+        } else {
+            /* evaluate the next worse quality */
+            rate_control->quality_id--;
+        }
+    }
+    return;
+
+complete_sample:
+    if (quality_eval->max_sampled_fps != 0) {
+        /* covering a case were monotonicity was violated and we sampled
+           a better jepg quality, with better frame rate. */
+        final_quality_id = MAX(rate_control->quality_id,
+                               quality_eval->max_sampled_fps_quality_id);
+    } else {
+        final_quality_id = rate_control->quality_id;
+    }
+    final_quality_enc_size = quality_eval->encoded_size_by_quality[final_quality_id];
+    final_fps = get_max_fps(final_quality_enc_size,
+                            rate_control->byte_rate);
+
+    if (final_quality_id == quality_eval->min_quality_id) {
+        final_fps = MAX(final_fps, quality_eval->min_quality_fps);
+    }
+    if (final_quality_id == quality_eval->max_quality_id) {
+        final_fps = MIN(final_fps, quality_eval->max_quality_fps);
+    }
+    mjpeg_encoder_reset_quality(encoder, final_quality_id, final_fps, final_quality_enc_size);
+    rate_control->sum_recent_enc_size = final_quality_enc_size;
+    rate_control->num_recent_enc_frames = 1;
+
+    spice_debug("MJpeg quality sample end %p: quality %d fps %d",
+                encoder, mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
+    if (encoder->cbs.update_client_playback_delay) {
+        uint32_t latency = mjpeg_encoder_get_latency(encoder);
+        uint32_t min_delay = get_min_required_playback_delay(final_quality_enc_size,
+                                                             rate_control->byte_rate,
+                                                             latency);
+
+        encoder->cbs.update_client_playback_delay(encoder->cbs_opaque, min_delay);
+    }
+}
+
+static void mjpeg_encoder_quality_eval_set_upgrade(MJpegEncoder *encoder,
+                                                   int reason,
+                                                   uint32_t min_quality_id,
+                                                   uint32_t min_quality_fps)
+{
+    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
+
+    encoder->rate_control.during_quality_eval = TRUE;
+    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_UPGRADE;
+    quality_eval->reason = reason;
+    quality_eval->min_quality_id = min_quality_id;
+    quality_eval->min_quality_fps = min_quality_fps;
+}
+
+static void mjpeg_encoder_quality_eval_set_downgrade(MJpegEncoder *encoder,
+                                                     int reason,
+                                                     uint32_t max_quality_id,
+                                                     uint32_t max_quality_fps)
+{
+    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
+
+    encoder->rate_control.during_quality_eval = TRUE;
+    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE;
+    quality_eval->reason = reason;
+    quality_eval->max_quality_id = max_quality_id;
+    quality_eval->max_quality_fps = max_quality_fps;
+}
+
+static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder)
+{
+    MJpegEncoderRateControl *rate_control;
+    MJpegEncoderQualityEval *quality_eval;
+    uint64_t new_avg_enc_size = 0;
+    uint32_t new_fps;
+    uint32_t latency = 0;
+    uint32_t src_fps;
+
+    spice_assert(rate_control_is_active(encoder));
+
+    rate_control = &encoder->rate_control;
+    quality_eval = &rate_control->quality_eval_data;
+
+    if (!rate_control->last_enc_size) {
+        spice_debug("missing sample size");
+        return;
+    }
+
+    if (rate_control->during_quality_eval) {
+        quality_eval->encoded_size_by_quality[rate_control->quality_id] = rate_control->last_enc_size;
+        mjpeg_encoder_eval_quality(encoder);
+        return;
+    }
+
+    if (!rate_control->num_recent_enc_frames) {
+        spice_debug("No recent encoded frames");
+        return;
+    }
+
+    if (rate_control->num_recent_enc_frames < MJPEG_AVERAGE_SIZE_WINDOW &&
+        rate_control->num_recent_enc_frames < rate_control->fps) {
+        goto end;
+    }
+
+    latency = mjpeg_encoder_get_latency(encoder);
+    new_avg_enc_size = rate_control->sum_recent_enc_size /
+                       rate_control->num_recent_enc_frames;
+    new_fps = get_max_fps(new_avg_enc_size, rate_control->byte_rate);
+
+    spice_debug("cur-fps=%u new-fps=%u (new/old=%.2f) |"
+                "bit-rate=%.2f (Mbps) latency=%u (ms) quality=%d |"
+                " new-size-avg %"PRIu64" , base-size %"PRIu64", (new/old=%.2f) ",
+                rate_control->fps, new_fps, ((double)new_fps)/rate_control->fps,
+                ((double)rate_control->byte_rate*8)/1024/1024,
+                latency,
+                mjpeg_quality_samples[rate_control->quality_id],
+                new_avg_enc_size, rate_control->base_enc_size,
+                rate_control->base_enc_size ?
+                    ((double)new_avg_enc_size) / rate_control->base_enc_size :
+                    1);
+
+     src_fps = mjpeg_encoder_get_source_fps(encoder);
+
+    /*
+     * The ratio between the new_fps and the current fps reflects the changes
+     * in latency and frame size. When the change passes a threshold,
+     * we re-evaluate the quality and frame rate.
+     */
+    if (new_fps > rate_control->fps &&
+        (rate_control->fps < src_fps || rate_control->quality_id < MJPEG_QUALITY_SAMPLE_NUM - 1)) {
+        spice_debug("mjpeg %p FPS CHANGE >> :  re-evaluating params", encoder);
+        mjpeg_encoder_quality_eval_set_upgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
+                                               rate_control->quality_id, /* fps has improved -->
+                                                                            don't allow stream quality
+                                                                            to deteriorate */
+                                               rate_control->fps);
+    } else if (new_fps < rate_control->fps && new_fps < src_fps) {
+        spice_debug("mjpeg %p FPS CHANGE << : re-evaluating params", encoder);
+        mjpeg_encoder_quality_eval_set_downgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
+                                                 rate_control->quality_id,
+                                                 rate_control->fps);
+    }
+end:
+    if (rate_control->during_quality_eval) {
+        quality_eval->encoded_size_by_quality[rate_control->quality_id] = new_avg_enc_size;
+        mjpeg_encoder_eval_quality(encoder);
+    } else {
+        mjpeg_encoder_process_server_drops(encoder);
+    }
+}
+
+/*
+ * The actual frames distribution does not necessarily fit the condition "at least
+ * one frame every (1000/rate_contorl->fps) milliseconds".
+ * For keeping the average fps close to the defined fps, we periodically
+ * measure the current average fps, and modify rate_control->adjusted_fps accordingly.
+ * Then, we use (1000/rate_control->adjusted_fps) as the interval between frames.
+ */
+static void mjpeg_encoder_adjust_fps(MJpegEncoder *encoder, uint64_t now)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    uint64_t adjusted_fps_time_passed;
+
+    spice_assert(rate_control_is_active(encoder));
+
+    adjusted_fps_time_passed = (now - rate_control->adjusted_fps_start_time) / 1000 / 1000;
+
+    if (!rate_control->during_quality_eval &&
+        adjusted_fps_time_passed > MJPEG_ADJUST_FPS_TIMEOUT &&
+        adjusted_fps_time_passed > 1000 / rate_control->adjusted_fps) {
+        double avg_fps;
+        double fps_ratio;
+
+        avg_fps = ((double)rate_control->adjusted_fps_num_frames*1000) /
+                  adjusted_fps_time_passed;
+        spice_debug("#frames-adjust=%"PRIu64" #adjust-time=%"PRIu64" avg-fps=%.2f",
+                    rate_control->adjusted_fps_num_frames, adjusted_fps_time_passed, avg_fps);
+        spice_debug("defined=%u old-adjusted=%.2f", rate_control->fps, rate_control->adjusted_fps);
+        fps_ratio = avg_fps / rate_control->fps;
+        if (avg_fps + 0.5 < rate_control->fps &&
+            mjpeg_encoder_get_source_fps(encoder) > avg_fps) {
+            double new_adjusted_fps = avg_fps ?
+                                               (rate_control->adjusted_fps/fps_ratio) :
+                                               rate_control->adjusted_fps * 2;
+
+            rate_control->adjusted_fps = MIN(rate_control->fps*2, new_adjusted_fps);
+            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
+        } else if (rate_control->fps + 0.5 < avg_fps) {
+            double new_adjusted_fps = rate_control->adjusted_fps / fps_ratio;
+
+            rate_control->adjusted_fps = MAX(rate_control->fps, new_adjusted_fps);
+            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
+        }
+        rate_control->adjusted_fps_start_time = now;
+        rate_control->adjusted_fps_num_frames = 0;
+    }
+}
+
+/*
+ * dest must be either NULL or allocated by malloc, since it might be freed
+ * during the encoding, if its size is too small.
+ *
+ * return:
+ *  MJPEG_ENCODER_FRAME_UNSUPPORTED : frame cannot be encoded
+ *  MJPEG_ENCODER_FRAME_DROP        : frame should be dropped. This value can only be returned
+ *                                    if mjpeg rate control is active.
+ *  MJPEG_ENCODER_FRAME_ENCODE_DONE : frame encoding started. Continue with
+ *                                    mjpeg_encoder_encode_scanline.
+ */
+static int mjpeg_encoder_start_frame(MJpegEncoder *encoder,
+                                     SpiceBitmapFmt format,
+                                     int width, int height,
+                                     uint8_t **dest, size_t *dest_len,
+                                     uint32_t frame_mm_time)
+{
+    uint32_t quality;
+
+    if (rate_control_is_active(encoder)) {
+        MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+        uint64_t now;
+        uint64_t interval;
+
+        now = red_get_monotonic_time();
+
+        if (!rate_control->adjusted_fps_start_time) {
+            rate_control->adjusted_fps_start_time = now;
+        }
+        mjpeg_encoder_adjust_fps(encoder, now);
+        interval = (now - rate_control->bit_rate_info.last_frame_time);
+
+        if (interval < (1000*1000*1000) / rate_control->adjusted_fps) {
+            return MJPEG_ENCODER_FRAME_DROP;
+        }
+
+        mjpeg_encoder_adjust_params_to_bit_rate(encoder);
+
+        if (!rate_control->during_quality_eval ||
+            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
+            MJpegEncoderBitRateInfo *bit_rate_info;
+
+            bit_rate_info = &encoder->rate_control.bit_rate_info;
+
+            if (!bit_rate_info->change_start_time) {
+                bit_rate_info->change_start_time = now;
+                bit_rate_info->change_start_mm_time = frame_mm_time;
+            }
+            bit_rate_info->last_frame_time = now;
+        }
+    }
+
+    encoder->cinfo.in_color_space   = JCS_RGB;
+    encoder->cinfo.input_components = 3;
+    encoder->pixel_converter = NULL;
+
+    switch (format) {
+    case SPICE_BITMAP_FMT_32BIT:
+    case SPICE_BITMAP_FMT_RGBA:
+        encoder->bytes_per_pixel = 4;
+#ifdef JCS_EXTENSIONS
+        encoder->cinfo.in_color_space   = JCS_EXT_BGRX;
+        encoder->cinfo.input_components = 4;
+#else
+        encoder->pixel_converter = pixel_rgb32bpp_to_24;
+#endif
+        break;
+    case SPICE_BITMAP_FMT_16BIT:
+        encoder->bytes_per_pixel = 2;
+        encoder->pixel_converter = pixel_rgb16bpp_to_24;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        encoder->bytes_per_pixel = 3;
+#ifdef JCS_EXTENSIONS
+        encoder->cinfo.in_color_space = JCS_EXT_BGR;
+#else
+        encoder->pixel_converter = pixel_rgb24bpp_to_24;
+#endif
+        break;
+    default:
+        spice_debug("unsupported format %d", format);
+        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
+    }
+
+    if (encoder->pixel_converter != NULL) {
+        unsigned int stride = width * 3;
+        /* check for integer overflow */
+        if (stride < width) {
+            return MJPEG_ENCODER_FRAME_UNSUPPORTED;
+        }
+        if (encoder->row_size < stride) {
+            encoder->row = spice_realloc(encoder->row, stride);
+            encoder->row_size = stride;
+        }
+    }
+
+    spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
+
+    encoder->cinfo.image_width      = width;
+    encoder->cinfo.image_height     = height;
+    jpeg_set_defaults(&encoder->cinfo);
+    encoder->cinfo.dct_method       = JDCT_IFAST;
+    quality = mjpeg_quality_samples[encoder->rate_control.quality_id];
+    jpeg_set_quality(&encoder->cinfo, quality, TRUE);
+    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
+
+    encoder->num_frames++;
+    encoder->avg_quality += quality;
+    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
+}
+
+static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder,
+                                         uint8_t *src_pixels,
+                                         size_t image_width)
+{
+    unsigned int scanlines_written;
+    uint8_t *row;
+
+    row = encoder->row;
+    if (encoder->pixel_converter) {
+        unsigned int x;
+        for (x = 0; x < image_width; x++) {
+            /* src_pixels is expected to be 4 bytes aligned */
+            encoder->pixel_converter(src_pixels, row);
+            row += 3;
+            src_pixels += encoder->bytes_per_pixel;
+        }
+        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
+    } else {
+        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &src_pixels, 1);
+    }
+    if (scanlines_written == 0) { /* Not enough space */
+        jpeg_abort_compress(&encoder->cinfo);
+        encoder->rate_control.last_enc_size = 0;
+        return 0;
+    }
+
+    return scanlines_written;
+}
+
+static size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
+{
+    mem_destination_mgr *dest = (mem_destination_mgr *) encoder->cinfo.dest;
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+
+    jpeg_finish_compress(&encoder->cinfo);
+
+    encoder->first_frame = FALSE;
+    rate_control->last_enc_size = dest->pub.next_output_byte - dest->buffer;
+    rate_control->server_state.num_frames_encoded++;
+
+    if (!rate_control->during_quality_eval ||
+        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
+
+        if (!rate_control->during_quality_eval) {
+            if (rate_control->num_recent_enc_frames >= MJPEG_AVERAGE_SIZE_WINDOW) {
+                rate_control->num_recent_enc_frames = 0;
+                rate_control->sum_recent_enc_size = 0;
+            }
+            rate_control->sum_recent_enc_size += rate_control->last_enc_size;
+            rate_control->num_recent_enc_frames++;
+            rate_control->adjusted_fps_num_frames++;
+        }
+        rate_control->bit_rate_info.sum_enc_size += encoder->rate_control.last_enc_size;
+        rate_control->bit_rate_info.num_enc_frames++;
+    }
+    return encoder->rate_control.last_enc_size;
+}
+
+static inline uint8_t *get_image_line(SpiceChunks *chunks, size_t *offset,
+                                      int *chunk_nr, int stride)
+{
+    uint8_t *ret;
+    SpiceChunk *chunk;
+
+    chunk = &chunks->chunk[*chunk_nr];
+
+    if (*offset == chunk->len) {
+        if (*chunk_nr == chunks->num_chunks - 1) {
+            return NULL; /* Last chunk */
+        }
+        *offset = 0;
+        (*chunk_nr)++;
+        chunk = &chunks->chunk[*chunk_nr];
+    }
+
+    if (chunk->len - *offset < stride) {
+        spice_warning("bad chunk alignment");
+        return NULL;
+    }
+    ret = chunk->data + *offset;
+    *offset += stride;
+    return ret;
+}
+
+static int encode_frame(MJpegEncoder *encoder, const SpiceRect *src,
+                        const SpiceBitmap *image, int top_down)
+{
+    SpiceChunks *chunks;
+    uint32_t image_stride;
+    size_t offset;
+    int i, chunk;
+
+    chunks = image->data;
+    offset = 0;
+    chunk = 0;
+    image_stride = image->stride;
+
+    const int skip_lines = top_down ? src->top : image->y - (src->bottom - 0);
+    for (i = 0; i < skip_lines; i++) {
+        get_image_line(chunks, &offset, &chunk, image_stride);
+    }
+
+    const unsigned int stream_height = src->bottom - src->top;
+    const unsigned int stream_width = src->right - src->left;
+
+    for (i = 0; i < stream_height; i++) {
+        uint8_t *src_line = get_image_line(chunks, &offset, &chunk, image_stride);
+
+        if (!src_line) {
+            return FALSE;
+        }
+
+        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(encoder);
+        if (mjpeg_encoder_encode_scanline(encoder, src_line, stream_width) == 0) {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
+                               const SpiceBitmap *bitmap, int width, int height,
+                               const SpiceRect *src,
+                               int top_down, uint32_t frame_mm_time,
+                               uint8_t **outbuf, size_t *outbuf_size,
+                               int *data_size)
+{
+    int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
+                                    width, height, outbuf, outbuf_size,
+                                    frame_mm_time);
+    if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) {
+        return ret;
+    }
+
+    if (!encode_frame(encoder, src, bitmap, top_down)) {
+        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
+    }
+
+    *data_size = mjpeg_encoder_end_frame(encoder);
+
+    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
+}
+
+
+static void mjpeg_encoder_quality_eval_stop(MJpegEncoder *encoder)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    uint32_t quality_id;
+    uint32_t fps;
+
+    if (!rate_control->during_quality_eval) {
+        return;
+    }
+    switch (rate_control->quality_eval_data.type) {
+    case MJPEG_QUALITY_EVAL_TYPE_UPGRADE:
+        quality_id = rate_control->quality_eval_data.min_quality_id;
+        fps = rate_control->quality_eval_data.min_quality_fps;
+        break;
+    case MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE:
+        quality_id = rate_control->quality_eval_data.max_quality_id;
+        fps = rate_control->quality_eval_data.max_quality_fps;
+        break;
+    case MJPEG_QUALITY_EVAL_TYPE_SET:
+        quality_id = MJPEG_QUALITY_SAMPLE_NUM / 2;
+        fps = MJPEG_MAX_FPS / 2;
+        break;
+    default:
+        spice_warning("unexected");
+        return;
+    }
+    mjpeg_encoder_reset_quality(encoder, quality_id, fps, 0);
+    spice_debug("during quality evaluation: canceling."
+                "reset quality to %d fps %d",
+                mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
+}
+
+static void mjpeg_encoder_decrease_bit_rate(MJpegEncoder *encoder)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
+    uint64_t measured_byte_rate;
+    uint32_t measured_fps;
+    uint64_t decrease_size;
+
+    mjpeg_encoder_quality_eval_stop(encoder);
+
+    rate_control->client_state.max_video_latency = 0;
+    rate_control->client_state.max_audio_latency = 0;
+    if (rate_control->warmup_start_time) {
+        uint64_t now;
+
+        now = red_get_monotonic_time();
+        if (now - rate_control->warmup_start_time < MJPEG_WARMUP_TIME*1000*1000) {
+            spice_debug("during warmup. ignoring");
+            return;
+        } else {
+            rate_control->warmup_start_time = 0;
+        }
+    }
+
+    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
+        bit_rate_info->num_enc_frames > rate_control->fps) {
+        double duration_sec;
+
+        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
+        duration_sec /= (1000.0 * 1000.0 * 1000.0);
+        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
+        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
+        decrease_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
+        spice_debug("bit rate esitimation %.2f (Mbps) fps %u",
+                    measured_byte_rate*8/1024.0/1024,
+                    measured_fps);
+    } else {
+        measured_byte_rate = rate_control->byte_rate;
+        measured_fps = rate_control->fps;
+        decrease_size = measured_byte_rate/measured_fps;
+        spice_debug("bit rate not re-estimated %.2f (Mbps) fps %u",
+                    measured_byte_rate*8/1024.0/1024,
+                    measured_fps);
+    }
+
+    measured_byte_rate = MIN(rate_control->byte_rate, measured_byte_rate);
+
+    if (decrease_size >=  measured_byte_rate) {
+        decrease_size = measured_byte_rate / 2;
+    }
+
+    rate_control->byte_rate = measured_byte_rate - decrease_size;
+    bit_rate_info->change_start_time = 0;
+    bit_rate_info->change_start_mm_time = 0;
+    bit_rate_info->last_frame_time = 0;
+    bit_rate_info->num_enc_frames = 0;
+    bit_rate_info->sum_enc_size = 0;
+    bit_rate_info->was_upgraded = FALSE;
+
+    spice_debug("decrease bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
+    mjpeg_encoder_quality_eval_set_downgrade(encoder,
+                                             MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
+                                             rate_control->quality_id,
+                                             rate_control->fps);
+}
+
+static void mjpeg_encoder_handle_negative_client_stream_report(MJpegEncoder *encoder,
+                                                               uint32_t report_end_frame_mm_time)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+
+    spice_debug(NULL);
+
+    if ((rate_control->bit_rate_info.change_start_mm_time > report_end_frame_mm_time ||
+        !rate_control->bit_rate_info.change_start_mm_time) &&
+         !rate_control->bit_rate_info.was_upgraded) {
+        spice_debug("ignoring, a downgrade has already occurred later to the report time");
+        return;
+    }
+
+    mjpeg_encoder_decrease_bit_rate(encoder);
+}
+
+static void mjpeg_encoder_increase_bit_rate(MJpegEncoder *encoder)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
+    uint64_t measured_byte_rate;
+    uint32_t measured_fps;
+    uint64_t increase_size;
+
+
+    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
+        bit_rate_info->num_enc_frames > rate_control->fps) {
+        uint64_t avg_frame_size;
+        double duration_sec;
+
+        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
+        duration_sec /= (1000.0 * 1000.0 * 1000.0);
+        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
+        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
+        avg_frame_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
+        spice_debug("bit rate esitimation %.2f (Mbps) defined %.2f"
+                    " fps %u avg-frame-size=%.2f (KB)",
+                    measured_byte_rate*8/1024.0/1024,
+                    rate_control->byte_rate*8/1024.0/1024,
+                    measured_fps,
+                    avg_frame_size/1024.0);
+        increase_size = avg_frame_size;
+    } else {
+        spice_debug("not enough samples for measuring the bit rate. no change");
+        return;
+    }
+
+
+    mjpeg_encoder_quality_eval_stop(encoder);
+
+    if (measured_byte_rate + increase_size < rate_control->byte_rate) {
+        spice_debug("measured byte rate is small: not upgrading, just re-evaluating");
+    } else {
+        rate_control->byte_rate = MIN(measured_byte_rate, rate_control->byte_rate) + increase_size;
+    }
+
+    bit_rate_info->change_start_time = 0;
+    bit_rate_info->change_start_mm_time = 0;
+    bit_rate_info->last_frame_time = 0;
+    bit_rate_info->num_enc_frames = 0;
+    bit_rate_info->sum_enc_size = 0;
+    bit_rate_info->was_upgraded = TRUE;
+
+    spice_debug("increase bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
+    mjpeg_encoder_quality_eval_set_upgrade(encoder,
+                                           MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
+                                           rate_control->quality_id,
+                                           rate_control->fps);
+}
+
+static void mjpeg_encoder_handle_positive_client_stream_report(MJpegEncoder *encoder,
+                                                               uint32_t report_start_frame_mm_time)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
+    int stable_client_mm_time;
+    int timeout;
+
+    if (rate_control->during_quality_eval &&
+        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
+        spice_debug("during quality evaluation (rate change). ignoring report");
+        return;
+    }
+
+    if ((rate_control->fps > MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH ||
+         rate_control->fps >= mjpeg_encoder_get_source_fps(encoder)) &&
+         rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2) {
+        timeout = MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT;
+    } else {
+        timeout = MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT;
+    }
+
+    stable_client_mm_time = (int)report_start_frame_mm_time - bit_rate_info->change_start_mm_time;
+
+    if (!bit_rate_info->change_start_mm_time || stable_client_mm_time < timeout) {
+        /* assessing the stability of the current setting and only then
+         * respond to the report */
+        spice_debug("no drops, but not enough time has passed for assessing"
+                    "the playback stability since the last bit rate change");
+        return;
+    }
+    mjpeg_encoder_increase_bit_rate(encoder);
+}
+
+/*
+ * the video playback jitter buffer should be at least (send_time*2 + net_latency) for
+ * preventing underflow
+ */
+static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
+                                                uint64_t byte_rate,
+                                                uint32_t latency)
+{
+    uint32_t one_frame_time;
+    uint32_t min_delay;
+
+    if (!frame_enc_size || !byte_rate) {
+        return latency;
+    }
+    one_frame_time = (frame_enc_size*1000)/byte_rate;
+
+    min_delay = MIN(one_frame_time*2 + latency, MJPEG_MAX_CLIENT_PLAYBACK_DELAY);
+    return min_delay;
+}
+
+#define MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR 0.5
+#define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25
+#define MJPEG_VIDEO_DELAY_TH -15
+
+void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
+                                        uint32_t num_frames,
+                                        uint32_t num_drops,
+                                        uint32_t start_frame_mm_time,
+                                        uint32_t end_frame_mm_time,
+                                        int32_t end_frame_delay,
+                                        uint32_t audio_delay)
+{
+    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
+    MJpegEncoderClientState *client_state = &rate_control->client_state;
+    uint64_t avg_enc_size = 0;
+    uint32_t min_playback_delay;
+    int is_video_delay_small = FALSE;
+
+    spice_debug("client report: #frames %u, #drops %d, duration %u video-delay %d audio-delay %u",
+                num_frames, num_drops,
+                end_frame_mm_time - start_frame_mm_time,
+                end_frame_delay, audio_delay);
+
+    if (!rate_control_is_active(encoder)) {
+        spice_debug("rate control was not activated: ignoring");
+        return;
+    }
+    if (rate_control->during_quality_eval) {
+        if (rate_control->quality_eval_data.type == MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE &&
+            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
+            spice_debug("during rate downgrade evaluation");
+            return;
+        }
+    }
+
+    if (rate_control->num_recent_enc_frames) {
+        avg_enc_size = rate_control->sum_recent_enc_size /
+                       rate_control->num_recent_enc_frames;
+    }
+    spice_debug("recent size avg %.2f (KB)", avg_enc_size / 1024.0);
+    min_playback_delay = get_min_required_playback_delay(avg_enc_size, rate_control->byte_rate,
+                                                         mjpeg_encoder_get_latency(encoder));
+    spice_debug("min-delay %u client-delay %d", min_playback_delay, end_frame_delay);
+
+    if (min_playback_delay > end_frame_delay) {
+        uint32_t src_fps = mjpeg_encoder_get_source_fps(encoder);
+        /*
+        * if the stream is at its highest rate, we can't estimate the "real"
+        * network bit rate and the min_playback_delay
+        */
+        if (rate_control->quality_id != MJPEG_QUALITY_SAMPLE_NUM - 1 ||
+            rate_control->fps < MIN(src_fps, MJPEG_MAX_FPS) || end_frame_delay < 0) {
+            is_video_delay_small = TRUE;
+            if (encoder->cbs.update_client_playback_delay) {
+                encoder->cbs.update_client_playback_delay(encoder->cbs_opaque,
+                                                          min_playback_delay);
+            }
+        }
+    }
+
+
+    /*
+     * If the audio latency has decreased (since the start of the current
+     * sequence of positive reports), and the video latency is bigger, slow down
+     * the video rate
+     */
+    if (end_frame_delay > 0 &&
+        audio_delay < MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR*client_state->max_audio_latency &&
+        end_frame_delay > MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR*audio_delay) {
+        spice_debug("video_latency >> audio_latency && audio_latency << max (%u)",
+                    client_state->max_audio_latency);
+        mjpeg_encoder_handle_negative_client_stream_report(encoder,
+                                                           end_frame_mm_time);
+        return;
+    }
+
+    if (end_frame_delay < MJPEG_VIDEO_DELAY_TH) {
+        mjpeg_encoder_handle_negative_client_stream_report(encoder,
+                                                           end_frame_mm_time);
+    } else {
+        double major_delay_decrease_thresh;
+        double medium_delay_decrease_thresh;
+
+        client_state->max_video_latency = MAX(end_frame_delay, client_state->max_video_latency);
+        client_state->max_audio_latency = MAX(audio_delay, client_state->max_audio_latency);
+
+        medium_delay_decrease_thresh = client_state->max_video_latency;
+        medium_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
+
+        major_delay_decrease_thresh = medium_delay_decrease_thresh;
+        major_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
+        /*
+         * since the bit rate and the required latency are only evaluation based on the
+         * reports we got till now, we assume that the latency is too low only if it
+         * was higher during the time that passed since the last report that resulted
+         * in a bit rate decrement. If we find that the latency has decreased, it might
+         * suggest that the stream bit rate is too high.
+         */
+        if ((end_frame_delay < medium_delay_decrease_thresh &&
+            is_video_delay_small) || end_frame_delay < major_delay_decrease_thresh) {
+            spice_debug("downgrade due to short video delay (last=%u, past-max=%u",
+                end_frame_delay, client_state->max_video_latency);
+            mjpeg_encoder_handle_negative_client_stream_report(encoder,
+                                                               end_frame_mm_time);
+        } else if (!num_drops) {
+            mjpeg_encoder_handle_positive_client_stream_report(encoder,
+                                                               start_frame_mm_time);
+
+        }
+    }
+}
+
+void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder)
+{
+    encoder->rate_control.server_state.num_frames_dropped++;
+    mjpeg_encoder_process_server_drops(encoder);
+}
+
+/*
+ * decrease the bit rate if the drop rate on the sever side exceeds a pre defined
+ * threshold.
+ */
+static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder)
+{
+    MJpegEncoderServerState *server_state = &encoder->rate_control.server_state;
+    uint32_t num_frames_total;
+    double drop_factor;
+    uint32_t fps;
+
+    fps = MIN(encoder->rate_control.fps, mjpeg_encoder_get_source_fps(encoder));
+    if (server_state->num_frames_encoded < fps * MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL) {
+        return;
+    }
+
+    num_frames_total = server_state->num_frames_dropped + server_state->num_frames_encoded;
+    drop_factor = ((double)server_state->num_frames_dropped) / num_frames_total;
+
+    spice_debug("#drops %u total %u fps %u src-fps %u",
+                server_state->num_frames_dropped,
+                num_frames_total,
+                encoder->rate_control.fps,
+                mjpeg_encoder_get_source_fps(encoder));
+
+    if (drop_factor > MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH) {
+        mjpeg_encoder_decrease_bit_rate(encoder);
+    }
+    server_state->num_frames_encoded = 0;
+    server_state->num_frames_dropped = 0;
+}
+
+uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)
+{
+    return encoder->rate_control.byte_rate * 8;
+}
+
+void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats)
+{
+    spice_assert(encoder != NULL && stats != NULL);
+    stats->starting_bit_rate = encoder->starting_bit_rate;
+    stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder);
+    stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
+}
+
+MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
+                                MJpegEncoderRateControlCbs *cbs,
+                                void *cbs_opaque)
+{
+    MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
+
+    encoder->first_frame = TRUE;
+    encoder->rate_control.byte_rate = starting_bit_rate / 8;
+    encoder->starting_bit_rate = starting_bit_rate;
+
+    if (cbs) {
+        struct timespec time;
+
+        clock_gettime(CLOCK_MONOTONIC, &time);
+        encoder->cbs = *cbs;
+        encoder->cbs_opaque = cbs_opaque;
+        mjpeg_encoder_reset_quality(encoder, MJPEG_QUALITY_SAMPLE_NUM / 2, 5, 0);
+        encoder->rate_control.during_quality_eval = TRUE;
+        encoder->rate_control.quality_eval_data.type = MJPEG_QUALITY_EVAL_TYPE_SET;
+        encoder->rate_control.quality_eval_data.reason = MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE;
+        encoder->rate_control.warmup_start_time = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec;
+    } else {
+        encoder->cbs.get_roundtrip_ms = NULL;
+        mjpeg_encoder_reset_quality(encoder, MJPEG_LEGACY_STATIC_QUALITY_ID, MJPEG_MAX_FPS, 0);
+    }
+
+    encoder->cinfo.err = jpeg_std_error(&encoder->jerr);
+    jpeg_create_compress(&encoder->cinfo);
+
+    return encoder;
+}
diff --git a/server/mjpeg-encoder.h b/server/mjpeg-encoder.h
new file mode 100644
index 0000000..d070e70
--- /dev/null
+++ b/server/mjpeg-encoder.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_MJPEG_ENCODER
+#define _H_MJPEG_ENCODER
+
+#include "red_common.h"
+
+enum {
+    MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,
+    MJPEG_ENCODER_FRAME_DROP,
+    MJPEG_ENCODER_FRAME_ENCODE_DONE,
+};
+
+typedef struct MJpegEncoder MJpegEncoder;
+
+/*
+ * Callbacks required for controling and adjusting
+ * the stream bit rate:
+ * get_roundtrip_ms: roundtrip time in milliseconds
+ * get_source_fps: the input frame rate (#frames per second), i.e.,
+ * the rate of frames arriving from the guest to spice-server,
+ * before any drops.
+ */
+typedef struct MJpegEncoderRateControlCbs {
+    uint32_t (*get_roundtrip_ms)(void *opaque);
+    uint32_t (*get_source_fps)(void *opaque);
+    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
+} MJpegEncoderRateControlCbs;
+
+typedef struct MJpegEncoderStats {
+    uint64_t starting_bit_rate;
+    uint64_t cur_bit_rate;
+    double avg_quality;
+} MJpegEncoderStats;
+
+MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
+                                MJpegEncoderRateControlCbs *cbs, void *opaque);
+void mjpeg_encoder_destroy(MJpegEncoder *encoder);
+
+int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
+                               const SpiceBitmap *bitmap, int width, int height,
+                               const SpiceRect *src,
+                               int top_down, uint32_t frame_mm_time,
+                               uint8_t **outbuf, size_t *outbuf_size,
+                               int *data_size);
+
+/*
+ * bit rate control
+ */
+
+/*
+ * Data that should be periodically obtained from the client. The report contains:
+ * num_frames         : the number of frames that reached the client during the time
+ *                      the report is referring to.
+ * num_drops          : the part of the above frames that was dropped by the client due to
+ *                      late arrival time.
+ * start_frame_mm_time: the mm_time of the first frame included in the report
+ * end_frame_mm_time  : the mm_time of the last_frame included in the report
+ * end_frame_delay    : (end_frame_mm_time - client_mm_time)
+ * audio delay        : the latency of the audio playback.
+ *                      If there is no audio playback, set it to MAX_UINT.
+ *
+ */
+void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
+                                        uint32_t num_frames,
+                                        uint32_t num_drops,
+                                        uint32_t start_frame_mm_time,
+                                        uint32_t end_frame_mm_time,
+                                        int32_t end_frame_delay,
+                                        uint32_t audio_delay);
+
+/*
+ * Notify the encoder each time a frame is dropped due to pipe
+ * congestion.
+ * We can deduce the client state by the frame dropping rate in the server.
+ * Monitoring the frame drops can help in fine tuning the playback parameters
+ * when the client reports are delayed.
+ */
+void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
+
+uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
+void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats);
+
+#endif
diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
deleted file mode 100644
index 9b331c1..0000000
--- a/server/mjpeg_encoder.c
+++ /dev/null
@@ -1,1375 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "red_common.h"
-#include "mjpeg_encoder.h"
-#include "utils.h"
-#include <jerror.h>
-#include <jpeglib.h>
-#include <inttypes.h>
-
-#define MJPEG_MAX_FPS 25
-#define MJPEG_MIN_FPS 1
-
-#define MJPEG_QUALITY_SAMPLE_NUM 7
-static const int mjpeg_quality_samples[MJPEG_QUALITY_SAMPLE_NUM] = {20, 30, 40, 50, 60, 70, 80};
-
-#define MJPEG_LEGACY_STATIC_QUALITY_ID 5 // jpeg quality 70
-
-#define MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH 10
-#define MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH 5
-
-#define MJPEG_AVERAGE_SIZE_WINDOW 3
-
-#define MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES 3
-#define MJPEG_LOW_FPS_RATE_TH 3
-
-#define MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL 1
-#define MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH 0.1
-
-/*
- * acting on positive client reports only if enough frame mm time
- * has passed since the last bit rate change and the report.
- * time
- */
-#define MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT 2000
-#define MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT 3000
-
-#define MJPEG_ADJUST_FPS_TIMEOUT 500
-
-/*
- * avoid interrupting the playback when there are temporary
- * incidents of instability (with respect to server and client drops)
- */
-#define MJPEG_MAX_CLIENT_PLAYBACK_DELAY 5000 // 5 sec
-
-/*
- * The stream starts after lossless frames were sent to the client,
- * and without rate control (except for pipe congestion). Thus, on the beginning
- * of the stream, we might observe frame drops on the client and server side which
- * are not necessarily related to mis-estimation of the bit rate, and we would
- * like to wait till the stream stabilizes.
- */
-#define MJPEG_WARMUP_TIME 3000LL // 3 sec
-
-enum {
-    MJPEG_QUALITY_EVAL_TYPE_SET,
-    MJPEG_QUALITY_EVAL_TYPE_UPGRADE,
-    MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE,
-};
-
-enum {
-    MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
-    MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
-};
-
-typedef struct MJpegEncoderQualityEval {
-    int type;
-    int reason;
-
-    uint64_t encoded_size_by_quality[MJPEG_QUALITY_SAMPLE_NUM];
-    /* lower limit for the current evaluation round */
-    int min_quality_id;
-    int min_quality_fps; // min fps for the given quality
-    /* upper limit for the current evaluation round */
-    int max_quality_id;
-    int max_quality_fps; // max fps for the given quality
-    /* tracking the best sampled fps so far */
-    int max_sampled_fps;
-    int max_sampled_fps_quality_id;
-} MJpegEncoderQualityEval;
-
-typedef struct MJpegEncoderClientState {
-    int max_video_latency;
-    uint32_t max_audio_latency;
-} MJpegEncoderClientState;
-
-typedef struct MJpegEncoderServerState {
-    uint32_t num_frames_encoded;
-    uint32_t num_frames_dropped;
-} MJpegEncoderServerState;
-
-typedef struct MJpegEncoderBitRateInfo {
-    uint64_t change_start_time;
-    uint64_t last_frame_time;
-    uint32_t change_start_mm_time;
-    int was_upgraded;
-
-    /* gathering data about the frames that
-     * were encoded since the last bit rate change*/
-    uint32_t num_enc_frames;
-    uint64_t sum_enc_size;
-} MJpegEncoderBitRateInfo;
-
-/*
- * Adjusting the stream jpeg quality and frame rate (fps):
- * When during_quality_eval=TRUE, we compress different frames with different
- * jpeg quality. By considering (1) the resulting compression ratio, and (2) the available
- * bit rate, we evaluate the max frame frequency for the stream with the given quality,
- * and we choose the highest quality that will allow a reasonable frame rate.
- * during_quality_eval is set for new streams and can also be set any time we want
- * to re-evaluate the stream parameters (e.g., when the bit rate and/or
- * compressed frame size significantly change).
- */
-typedef struct MJpegEncoderRateControl {
-    int during_quality_eval;
-    MJpegEncoderQualityEval quality_eval_data;
-    MJpegEncoderBitRateInfo bit_rate_info;
-    MJpegEncoderClientState client_state;
-    MJpegEncoderServerState server_state;
-
-    uint64_t byte_rate;
-    int quality_id;
-    uint32_t fps;
-    double adjusted_fps;
-    uint64_t adjusted_fps_start_time;
-    uint64_t adjusted_fps_num_frames;
-
-    /* the encoded frame size which the quality and the fps evaluation was based upon */
-    uint64_t base_enc_size;
-
-    uint64_t last_enc_size;
-
-    uint64_t sum_recent_enc_size;
-    uint32_t num_recent_enc_frames;
-
-    uint64_t warmup_start_time;
-} MJpegEncoderRateControl;
-
-struct MJpegEncoder {
-    uint8_t *row;
-    uint32_t row_size;
-    int first_frame;
-
-    struct jpeg_compress_struct cinfo;
-    struct jpeg_error_mgr jerr;
-
-    unsigned int bytes_per_pixel; /* bytes per pixel of the input buffer */
-    void (*pixel_converter)(void *src, uint8_t *dest);
-
-    MJpegEncoderRateControl rate_control;
-    MJpegEncoderRateControlCbs cbs;
-    void *cbs_opaque;
-
-    /* stats */
-    uint64_t starting_bit_rate;
-    uint64_t avg_quality;
-    uint32_t num_frames;
-};
-
-static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder);
-static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
-                                                uint64_t byte_rate,
-                                                uint32_t latency);
-
-static inline int rate_control_is_active(MJpegEncoder* encoder)
-{
-    return encoder->cbs.get_roundtrip_ms != NULL;
-}
-
-void mjpeg_encoder_destroy(MJpegEncoder *encoder)
-{
-    free(encoder->cinfo.dest);
-    jpeg_destroy_compress(&encoder->cinfo);
-    free(encoder->row);
-    free(encoder);
-}
-
-static uint8_t mjpeg_encoder_get_bytes_per_pixel(MJpegEncoder *encoder)
-{
-    return encoder->bytes_per_pixel;
-}
-
-#ifndef JCS_EXTENSIONS
-/* Pixel conversion routines */
-static void pixel_rgb24bpp_to_24(void *src_ptr, uint8_t *dest)
-{
-    uint8_t *src = src_ptr;
-    /* libjpegs stores rgb, spice/win32 stores bgr */
-    *dest++ = src[2]; /* red */
-    *dest++ = src[1]; /* green */
-    *dest++ = src[0]; /* blue */
-}
-
-static void pixel_rgb32bpp_to_24(void *src, uint8_t *dest)
-{
-    uint32_t pixel = *(uint32_t *)src;
-    *dest++ = (pixel >> 16) & 0xff;
-    *dest++ = (pixel >>  8) & 0xff;
-    *dest++ = (pixel >>  0) & 0xff;
-}
-#endif
-
-static void pixel_rgb16bpp_to_24(void *src, uint8_t *dest)
-{
-    uint16_t pixel = *(uint16_t *)src;
-    *dest++ = ((pixel >> 7) & 0xf8) | ((pixel >> 12) & 0x7);
-    *dest++ = ((pixel >> 2) & 0xf8) | ((pixel >> 7) & 0x7);
-    *dest++ = ((pixel << 3) & 0xf8) | ((pixel >> 2) & 0x7);
-}
-
-
-/* code from libjpeg 8 to handle compression to a memory buffer
- *
- * Copyright (C) 1994-1996, Thomas G. Lane.
- * Modified 2009 by Guido Vollbeding.
- * This file is part of the Independent JPEG Group's software.
- */
-typedef struct {
-  struct jpeg_destination_mgr pub; /* public fields */
-
-  unsigned char ** outbuffer;      /* target buffer */
-  size_t * outsize;
-  uint8_t * buffer;                /* start of buffer */
-  size_t bufsize;
-} mem_destination_mgr;
-
-static void init_mem_destination(j_compress_ptr cinfo)
-{
-}
-
-static boolean empty_mem_output_buffer(j_compress_ptr cinfo)
-{
-  size_t nextsize;
-  uint8_t * nextbuffer;
-  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
-
-  /* Try to allocate new buffer with double size */
-  nextsize = dest->bufsize * 2;
-  nextbuffer = malloc(nextsize);
-
-  if (nextbuffer == NULL)
-    ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
-
-  memcpy(nextbuffer, dest->buffer, dest->bufsize);
-
-  free(dest->buffer);
-
-  dest->pub.next_output_byte = nextbuffer + dest->bufsize;
-  dest->pub.free_in_buffer = dest->bufsize;
-
-  dest->buffer = nextbuffer;
-  dest->bufsize = nextsize;
-
-  return TRUE;
-}
-
-static void term_mem_destination(j_compress_ptr cinfo)
-{
-  mem_destination_mgr *dest = (mem_destination_mgr *) cinfo->dest;
-
-  *dest->outbuffer = dest->buffer;
-  *dest->outsize = dest->bufsize;
-}
-
-/*
- * Prepare for output to a memory buffer.
- * The caller may supply an own initial buffer with appropriate size.
- * Otherwise, or when the actual data output exceeds the given size,
- * the library adapts the buffer size as necessary.
- * The standard library functions malloc/free are used for allocating
- * larger memory, so the buffer is available to the application after
- * finishing compression, and then the application is responsible for
- * freeing the requested memory.
- */
-
-static void
-spice_jpeg_mem_dest(j_compress_ptr cinfo,
-                    unsigned char ** outbuffer, size_t * outsize)
-{
-  mem_destination_mgr *dest;
-#define OUTPUT_BUF_SIZE  4096 /* choose an efficiently fwrite'able size */
-
-  if (outbuffer == NULL || outsize == NULL) /* sanity check */
-    ERREXIT(cinfo, JERR_BUFFER_SIZE);
-
-  /* The destination object is made permanent so that multiple JPEG images
-   * can be written to the same buffer without re-executing jpeg_mem_dest.
-   */
-  if (cinfo->dest == NULL) { /* first time for this JPEG object? */
-    cinfo->dest = spice_malloc(sizeof(mem_destination_mgr));
-  }
-
-  dest = (mem_destination_mgr *) cinfo->dest;
-  dest->pub.init_destination = init_mem_destination;
-  dest->pub.empty_output_buffer = empty_mem_output_buffer;
-  dest->pub.term_destination = term_mem_destination;
-  dest->outbuffer = outbuffer;
-  dest->outsize = outsize;
-  if (*outbuffer == NULL || *outsize == 0) {
-    /* Allocate initial buffer */
-    *outbuffer = malloc(OUTPUT_BUF_SIZE);
-    if (*outbuffer == NULL)
-      ERREXIT1(cinfo, JERR_OUT_OF_MEMORY, 10);
-    *outsize = OUTPUT_BUF_SIZE;
-  }
-
-  dest->pub.next_output_byte = dest->buffer = *outbuffer;
-  dest->pub.free_in_buffer = dest->bufsize = *outsize;
-}
-/* end of code from libjpeg */
-
-static inline uint32_t mjpeg_encoder_get_source_fps(MJpegEncoder *encoder)
-{
-    return encoder->cbs.get_source_fps ?
-        encoder->cbs.get_source_fps(encoder->cbs_opaque) : MJPEG_MAX_FPS;
-}
-
-static inline uint32_t mjpeg_encoder_get_latency(MJpegEncoder *encoder)
-{
-    return encoder->cbs.get_roundtrip_ms ?
-        encoder->cbs.get_roundtrip_ms(encoder->cbs_opaque) / 2 : 0;
-}
-
-static uint32_t get_max_fps(uint64_t frame_size, uint64_t bytes_per_sec)
-{
-    double fps;
-    double send_time_ms;
-
-    if (!bytes_per_sec) {
-        return 0;
-    }
-    send_time_ms = frame_size * 1000.0 / bytes_per_sec;
-    fps = send_time_ms ? 1000 / send_time_ms : MJPEG_MAX_FPS;
-    return fps;
-}
-
-static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,
-                                               int quality_id,
-                                               uint32_t fps,
-                                               uint64_t frame_enc_size)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    double fps_ratio;
-
-    rate_control->during_quality_eval = FALSE;
-
-    if (rate_control->quality_id != quality_id) {
-        rate_control->last_enc_size = 0;
-    }
-
-    if (rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
-        memset(&rate_control->server_state, 0, sizeof(MJpegEncoderServerState));
-    }
-    rate_control->quality_id = quality_id;
-    memset(&rate_control->quality_eval_data, 0, sizeof(MJpegEncoderQualityEval));
-    rate_control->quality_eval_data.max_quality_id = MJPEG_QUALITY_SAMPLE_NUM - 1;
-    rate_control->quality_eval_data.max_quality_fps = MJPEG_MAX_FPS;
-
-    if (rate_control->adjusted_fps) {
-        fps_ratio = rate_control->adjusted_fps / rate_control->fps;
-    } else {
-        fps_ratio = 1.5;
-    }
-    rate_control->fps = MAX(MJPEG_MIN_FPS, fps);
-    rate_control->fps = MIN(MJPEG_MAX_FPS, rate_control->fps);
-    rate_control->adjusted_fps = rate_control->fps*fps_ratio;
-    spice_debug("adjusted-fps-ratio=%.2f adjusted-fps=%.2f", fps_ratio, rate_control->adjusted_fps);
-    rate_control->adjusted_fps_start_time = 0;
-    rate_control->adjusted_fps_num_frames = 0;
-    rate_control->base_enc_size = frame_enc_size;
-
-    rate_control->sum_recent_enc_size = 0;
-    rate_control->num_recent_enc_frames = 0;
-}
-
-#define QUALITY_WAS_EVALUATED(encoder, quality) \
-    ((encoder)->rate_control.quality_eval_data.encoded_size_by_quality[(quality)] != 0)
-
-/*
- * Adjust the stream's jpeg quality and frame rate.
- * We evaluate the compression ratio of different jpeg qualities;
- * We compress successive frames with different qualities,
- * and then we estimate the stream frame rate according to the currently
- * evaluated jpeg quality and available bit rate.
- *
- * During quality evaluation, mjpeg_encoder_eval_quality is called before a new
- * frame is encoded. mjpeg_encoder_eval_quality examines the encoding size of
- * the previously encoded frame, and determines whether to continue evaluation
- * (and chnages the quality for the frame that is going to be encoded),
- * or stop evaluation (and sets the quality and frame rate for the stream).
- * When qualities are scanned, we assume monotonicity of compression ratio
- * as a function of jpeg quality. When we reach a quality with too small, or
- * big enough compression ratio, we stop the evaluation and set the stream parameters.
-*/
-static inline void mjpeg_encoder_eval_quality(MJpegEncoder *encoder)
-{
-    MJpegEncoderRateControl *rate_control;
-    MJpegEncoderQualityEval *quality_eval;
-    uint32_t fps, src_fps;
-    uint64_t enc_size;
-    uint32_t final_quality_id;
-    uint32_t final_fps;
-    uint64_t final_quality_enc_size;
-
-    rate_control = &encoder->rate_control;
-    quality_eval = &rate_control->quality_eval_data;
-
-    spice_assert(rate_control->during_quality_eval);
-
-    /* retrieving the encoded size of the last encoded frame */
-    enc_size = quality_eval->encoded_size_by_quality[rate_control->quality_id];
-    if (enc_size == 0) {
-        spice_debug("size info missing");
-        return;
-    }
-
-    src_fps = mjpeg_encoder_get_source_fps(encoder);
-
-    fps = get_max_fps(enc_size, rate_control->byte_rate);
-    spice_debug("mjpeg %p: jpeg %d: %.2f (KB) fps %d src-fps %u",
-                encoder,
-                mjpeg_quality_samples[rate_control->quality_id],
-                enc_size / 1024.0,
-                fps,
-                src_fps);
-
-    if (fps > quality_eval->max_sampled_fps ||
-        ((fps == quality_eval->max_sampled_fps || fps >= src_fps) &&
-         rate_control->quality_id > quality_eval->max_sampled_fps_quality_id)) {
-        quality_eval->max_sampled_fps = fps;
-        quality_eval->max_sampled_fps_quality_id = rate_control->quality_id;
-    }
-
-    /*
-     * Choosing whether to evaluate another quality, or to complete evaluation
-     * and set the stream parameters according to one of the qualities that
-     * were already sampled.
-     */
-
-    if (rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2 &&
-        fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
-        fps < src_fps) {
-        /*
-         * When the jpeg quality is bigger than the median quality, prefer a reasonable
-         * frame rate over improving the quality
-         */
-        spice_debug("fps < %d && (fps < src_fps), quality %d",
-                MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH,
-                mjpeg_quality_samples[rate_control->quality_id]);
-        if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
-            /* the next worse quality was already evaluated and it passed the frame
-             * rate thresholds (we know that, because we continued evaluating a better
-             * quality) */
-            rate_control->quality_id--;
-            goto complete_sample;
-        } else {
-            /* evaluate the next worse quality */
-            rate_control->quality_id--;
-        }
-    } else if ((fps > MJPEG_IMPROVE_QUALITY_FPS_PERMISSIVE_TH &&
-                fps >= 0.66 * quality_eval->min_quality_fps) || fps >= src_fps) {
-        /* When the jpeg quality is worse than the median one (see first condition), we allow a less
-           strict threshold for fps, in order to improve the jpeg quality */
-        if (rate_control->quality_id + 1 == MJPEG_QUALITY_SAMPLE_NUM ||
-            rate_control->quality_id >= quality_eval->max_quality_id ||
-            QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id + 1)) {
-            /* best quality has been reached, or the next (better) quality was
-             * already evaluated and didn't pass the fps thresholds */
-            goto complete_sample;
-        } else {
-            if (rate_control->quality_id == MJPEG_QUALITY_SAMPLE_NUM / 2 &&
-                fps < MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH &&
-                fps < src_fps) {
-                goto complete_sample;
-            }
-            /* evaluate the next quality as well*/
-            rate_control->quality_id++;
-        }
-    } else { // very small frame rate, try to improve by downgrading the quality
-        if (rate_control->quality_id == 0 ||
-            rate_control->quality_id <= quality_eval->min_quality_id) {
-            goto complete_sample;
-        } else if (QUALITY_WAS_EVALUATED(encoder, rate_control->quality_id - 1)) {
-            rate_control->quality_id--;
-            goto complete_sample;
-        } else {
-            /* evaluate the next worse quality */
-            rate_control->quality_id--;
-        }
-    }
-    return;
-
-complete_sample:
-    if (quality_eval->max_sampled_fps != 0) {
-        /* covering a case were monotonicity was violated and we sampled
-           a better jepg quality, with better frame rate. */
-        final_quality_id = MAX(rate_control->quality_id,
-                               quality_eval->max_sampled_fps_quality_id);
-    } else {
-        final_quality_id = rate_control->quality_id;
-    }
-    final_quality_enc_size = quality_eval->encoded_size_by_quality[final_quality_id];
-    final_fps = get_max_fps(final_quality_enc_size,
-                            rate_control->byte_rate);
-
-    if (final_quality_id == quality_eval->min_quality_id) {
-        final_fps = MAX(final_fps, quality_eval->min_quality_fps);
-    }
-    if (final_quality_id == quality_eval->max_quality_id) {
-        final_fps = MIN(final_fps, quality_eval->max_quality_fps);
-    }
-    mjpeg_encoder_reset_quality(encoder, final_quality_id, final_fps, final_quality_enc_size);
-    rate_control->sum_recent_enc_size = final_quality_enc_size;
-    rate_control->num_recent_enc_frames = 1;
-
-    spice_debug("MJpeg quality sample end %p: quality %d fps %d",
-                encoder, mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
-    if (encoder->cbs.update_client_playback_delay) {
-        uint32_t latency = mjpeg_encoder_get_latency(encoder);
-        uint32_t min_delay = get_min_required_playback_delay(final_quality_enc_size,
-                                                             rate_control->byte_rate,
-                                                             latency);
-
-        encoder->cbs.update_client_playback_delay(encoder->cbs_opaque, min_delay);
-    }
-}
-
-static void mjpeg_encoder_quality_eval_set_upgrade(MJpegEncoder *encoder,
-                                                   int reason,
-                                                   uint32_t min_quality_id,
-                                                   uint32_t min_quality_fps)
-{
-    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
-
-    encoder->rate_control.during_quality_eval = TRUE;
-    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_UPGRADE;
-    quality_eval->reason = reason;
-    quality_eval->min_quality_id = min_quality_id;
-    quality_eval->min_quality_fps = min_quality_fps;
-}
-
-static void mjpeg_encoder_quality_eval_set_downgrade(MJpegEncoder *encoder,
-                                                     int reason,
-                                                     uint32_t max_quality_id,
-                                                     uint32_t max_quality_fps)
-{
-    MJpegEncoderQualityEval *quality_eval = &encoder->rate_control.quality_eval_data;
-
-    encoder->rate_control.during_quality_eval = TRUE;
-    quality_eval->type = MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE;
-    quality_eval->reason = reason;
-    quality_eval->max_quality_id = max_quality_id;
-    quality_eval->max_quality_fps = max_quality_fps;
-}
-
-static void mjpeg_encoder_adjust_params_to_bit_rate(MJpegEncoder *encoder)
-{
-    MJpegEncoderRateControl *rate_control;
-    MJpegEncoderQualityEval *quality_eval;
-    uint64_t new_avg_enc_size = 0;
-    uint32_t new_fps;
-    uint32_t latency = 0;
-    uint32_t src_fps;
-
-    spice_assert(rate_control_is_active(encoder));
-
-    rate_control = &encoder->rate_control;
-    quality_eval = &rate_control->quality_eval_data;
-
-    if (!rate_control->last_enc_size) {
-        spice_debug("missing sample size");
-        return;
-    }
-
-    if (rate_control->during_quality_eval) {
-        quality_eval->encoded_size_by_quality[rate_control->quality_id] = rate_control->last_enc_size;
-        mjpeg_encoder_eval_quality(encoder);
-        return;
-    }
-
-    if (!rate_control->num_recent_enc_frames) {
-        spice_debug("No recent encoded frames");
-        return;
-    }
-
-    if (rate_control->num_recent_enc_frames < MJPEG_AVERAGE_SIZE_WINDOW &&
-        rate_control->num_recent_enc_frames < rate_control->fps) {
-        goto end;
-    }
-
-    latency = mjpeg_encoder_get_latency(encoder);
-    new_avg_enc_size = rate_control->sum_recent_enc_size /
-                       rate_control->num_recent_enc_frames;
-    new_fps = get_max_fps(new_avg_enc_size, rate_control->byte_rate);
-
-    spice_debug("cur-fps=%u new-fps=%u (new/old=%.2f) |"
-                "bit-rate=%.2f (Mbps) latency=%u (ms) quality=%d |"
-                " new-size-avg %"PRIu64" , base-size %"PRIu64", (new/old=%.2f) ",
-                rate_control->fps, new_fps, ((double)new_fps)/rate_control->fps,
-                ((double)rate_control->byte_rate*8)/1024/1024,
-                latency,
-                mjpeg_quality_samples[rate_control->quality_id],
-                new_avg_enc_size, rate_control->base_enc_size,
-                rate_control->base_enc_size ?
-                    ((double)new_avg_enc_size) / rate_control->base_enc_size :
-                    1);
-
-     src_fps = mjpeg_encoder_get_source_fps(encoder);
-
-    /*
-     * The ratio between the new_fps and the current fps reflects the changes
-     * in latency and frame size. When the change passes a threshold,
-     * we re-evaluate the quality and frame rate.
-     */
-    if (new_fps > rate_control->fps &&
-        (rate_control->fps < src_fps || rate_control->quality_id < MJPEG_QUALITY_SAMPLE_NUM - 1)) {
-        spice_debug("mjpeg %p FPS CHANGE >> :  re-evaluating params", encoder);
-        mjpeg_encoder_quality_eval_set_upgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
-                                               rate_control->quality_id, /* fps has improved -->
-                                                                            don't allow stream quality
-                                                                            to deteriorate */
-                                               rate_control->fps);
-    } else if (new_fps < rate_control->fps && new_fps < src_fps) {
-        spice_debug("mjpeg %p FPS CHANGE << : re-evaluating params", encoder);
-        mjpeg_encoder_quality_eval_set_downgrade(encoder, MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE,
-                                                 rate_control->quality_id,
-                                                 rate_control->fps);
-    }
-end:
-    if (rate_control->during_quality_eval) {
-        quality_eval->encoded_size_by_quality[rate_control->quality_id] = new_avg_enc_size;
-        mjpeg_encoder_eval_quality(encoder);
-    } else {
-        mjpeg_encoder_process_server_drops(encoder);
-    }
-}
-
-/*
- * The actual frames distribution does not necessarily fit the condition "at least
- * one frame every (1000/rate_contorl->fps) milliseconds".
- * For keeping the average fps close to the defined fps, we periodically
- * measure the current average fps, and modify rate_control->adjusted_fps accordingly.
- * Then, we use (1000/rate_control->adjusted_fps) as the interval between frames.
- */
-static void mjpeg_encoder_adjust_fps(MJpegEncoder *encoder, uint64_t now)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    uint64_t adjusted_fps_time_passed;
-
-    spice_assert(rate_control_is_active(encoder));
-
-    adjusted_fps_time_passed = (now - rate_control->adjusted_fps_start_time) / 1000 / 1000;
-
-    if (!rate_control->during_quality_eval &&
-        adjusted_fps_time_passed > MJPEG_ADJUST_FPS_TIMEOUT &&
-        adjusted_fps_time_passed > 1000 / rate_control->adjusted_fps) {
-        double avg_fps;
-        double fps_ratio;
-
-        avg_fps = ((double)rate_control->adjusted_fps_num_frames*1000) /
-                  adjusted_fps_time_passed;
-        spice_debug("#frames-adjust=%"PRIu64" #adjust-time=%"PRIu64" avg-fps=%.2f",
-                    rate_control->adjusted_fps_num_frames, adjusted_fps_time_passed, avg_fps);
-        spice_debug("defined=%u old-adjusted=%.2f", rate_control->fps, rate_control->adjusted_fps);
-        fps_ratio = avg_fps / rate_control->fps;
-        if (avg_fps + 0.5 < rate_control->fps &&
-            mjpeg_encoder_get_source_fps(encoder) > avg_fps) {
-            double new_adjusted_fps = avg_fps ?
-                                               (rate_control->adjusted_fps/fps_ratio) :
-                                               rate_control->adjusted_fps * 2;
-
-            rate_control->adjusted_fps = MIN(rate_control->fps*2, new_adjusted_fps);
-            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
-        } else if (rate_control->fps + 0.5 < avg_fps) {
-            double new_adjusted_fps = rate_control->adjusted_fps / fps_ratio;
-
-            rate_control->adjusted_fps = MAX(rate_control->fps, new_adjusted_fps);
-            spice_debug("new-adjusted-fps=%.2f", rate_control->adjusted_fps);
-        }
-        rate_control->adjusted_fps_start_time = now;
-        rate_control->adjusted_fps_num_frames = 0;
-    }
-}
-
-/*
- * dest must be either NULL or allocated by malloc, since it might be freed
- * during the encoding, if its size is too small.
- *
- * return:
- *  MJPEG_ENCODER_FRAME_UNSUPPORTED : frame cannot be encoded
- *  MJPEG_ENCODER_FRAME_DROP        : frame should be dropped. This value can only be returned
- *                                    if mjpeg rate control is active.
- *  MJPEG_ENCODER_FRAME_ENCODE_DONE : frame encoding started. Continue with
- *                                    mjpeg_encoder_encode_scanline.
- */
-static int mjpeg_encoder_start_frame(MJpegEncoder *encoder,
-                                     SpiceBitmapFmt format,
-                                     int width, int height,
-                                     uint8_t **dest, size_t *dest_len,
-                                     uint32_t frame_mm_time)
-{
-    uint32_t quality;
-
-    if (rate_control_is_active(encoder)) {
-        MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-        uint64_t now;
-        uint64_t interval;
-
-        now = red_get_monotonic_time();
-
-        if (!rate_control->adjusted_fps_start_time) {
-            rate_control->adjusted_fps_start_time = now;
-        }
-        mjpeg_encoder_adjust_fps(encoder, now);
-        interval = (now - rate_control->bit_rate_info.last_frame_time);
-
-        if (interval < (1000*1000*1000) / rate_control->adjusted_fps) {
-            return MJPEG_ENCODER_FRAME_DROP;
-        }
-
-        mjpeg_encoder_adjust_params_to_bit_rate(encoder);
-
-        if (!rate_control->during_quality_eval ||
-            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
-            MJpegEncoderBitRateInfo *bit_rate_info;
-
-            bit_rate_info = &encoder->rate_control.bit_rate_info;
-
-            if (!bit_rate_info->change_start_time) {
-                bit_rate_info->change_start_time = now;
-                bit_rate_info->change_start_mm_time = frame_mm_time;
-            }
-            bit_rate_info->last_frame_time = now;
-        }
-    }
-
-    encoder->cinfo.in_color_space   = JCS_RGB;
-    encoder->cinfo.input_components = 3;
-    encoder->pixel_converter = NULL;
-
-    switch (format) {
-    case SPICE_BITMAP_FMT_32BIT:
-    case SPICE_BITMAP_FMT_RGBA:
-        encoder->bytes_per_pixel = 4;
-#ifdef JCS_EXTENSIONS
-        encoder->cinfo.in_color_space   = JCS_EXT_BGRX;
-        encoder->cinfo.input_components = 4;
-#else
-        encoder->pixel_converter = pixel_rgb32bpp_to_24;
-#endif
-        break;
-    case SPICE_BITMAP_FMT_16BIT:
-        encoder->bytes_per_pixel = 2;
-        encoder->pixel_converter = pixel_rgb16bpp_to_24;
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        encoder->bytes_per_pixel = 3;
-#ifdef JCS_EXTENSIONS
-        encoder->cinfo.in_color_space = JCS_EXT_BGR;
-#else
-        encoder->pixel_converter = pixel_rgb24bpp_to_24;
-#endif
-        break;
-    default:
-        spice_debug("unsupported format %d", format);
-        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
-    }
-
-    if (encoder->pixel_converter != NULL) {
-        unsigned int stride = width * 3;
-        /* check for integer overflow */
-        if (stride < width) {
-            return MJPEG_ENCODER_FRAME_UNSUPPORTED;
-        }
-        if (encoder->row_size < stride) {
-            encoder->row = spice_realloc(encoder->row, stride);
-            encoder->row_size = stride;
-        }
-    }
-
-    spice_jpeg_mem_dest(&encoder->cinfo, dest, dest_len);
-
-    encoder->cinfo.image_width      = width;
-    encoder->cinfo.image_height     = height;
-    jpeg_set_defaults(&encoder->cinfo);
-    encoder->cinfo.dct_method       = JDCT_IFAST;
-    quality = mjpeg_quality_samples[encoder->rate_control.quality_id];
-    jpeg_set_quality(&encoder->cinfo, quality, TRUE);
-    jpeg_start_compress(&encoder->cinfo, encoder->first_frame);
-
-    encoder->num_frames++;
-    encoder->avg_quality += quality;
-    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
-}
-
-static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder,
-                                         uint8_t *src_pixels,
-                                         size_t image_width)
-{
-    unsigned int scanlines_written;
-    uint8_t *row;
-
-    row = encoder->row;
-    if (encoder->pixel_converter) {
-        unsigned int x;
-        for (x = 0; x < image_width; x++) {
-            /* src_pixels is expected to be 4 bytes aligned */
-            encoder->pixel_converter(src_pixels, row);
-            row += 3;
-            src_pixels += encoder->bytes_per_pixel;
-        }
-        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &encoder->row, 1);
-    } else {
-        scanlines_written = jpeg_write_scanlines(&encoder->cinfo, &src_pixels, 1);
-    }
-    if (scanlines_written == 0) { /* Not enough space */
-        jpeg_abort_compress(&encoder->cinfo);
-        encoder->rate_control.last_enc_size = 0;
-        return 0;
-    }
-
-    return scanlines_written;
-}
-
-static size_t mjpeg_encoder_end_frame(MJpegEncoder *encoder)
-{
-    mem_destination_mgr *dest = (mem_destination_mgr *) encoder->cinfo.dest;
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-
-    jpeg_finish_compress(&encoder->cinfo);
-
-    encoder->first_frame = FALSE;
-    rate_control->last_enc_size = dest->pub.next_output_byte - dest->buffer;
-    rate_control->server_state.num_frames_encoded++;
-
-    if (!rate_control->during_quality_eval ||
-        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_SIZE_CHANGE) {
-
-        if (!rate_control->during_quality_eval) {
-            if (rate_control->num_recent_enc_frames >= MJPEG_AVERAGE_SIZE_WINDOW) {
-                rate_control->num_recent_enc_frames = 0;
-                rate_control->sum_recent_enc_size = 0;
-            }
-            rate_control->sum_recent_enc_size += rate_control->last_enc_size;
-            rate_control->num_recent_enc_frames++;
-            rate_control->adjusted_fps_num_frames++;
-        }
-        rate_control->bit_rate_info.sum_enc_size += encoder->rate_control.last_enc_size;
-        rate_control->bit_rate_info.num_enc_frames++;
-    }
-    return encoder->rate_control.last_enc_size;
-}
-
-static inline uint8_t *get_image_line(SpiceChunks *chunks, size_t *offset,
-                                      int *chunk_nr, int stride)
-{
-    uint8_t *ret;
-    SpiceChunk *chunk;
-
-    chunk = &chunks->chunk[*chunk_nr];
-
-    if (*offset == chunk->len) {
-        if (*chunk_nr == chunks->num_chunks - 1) {
-            return NULL; /* Last chunk */
-        }
-        *offset = 0;
-        (*chunk_nr)++;
-        chunk = &chunks->chunk[*chunk_nr];
-    }
-
-    if (chunk->len - *offset < stride) {
-        spice_warning("bad chunk alignment");
-        return NULL;
-    }
-    ret = chunk->data + *offset;
-    *offset += stride;
-    return ret;
-}
-
-static int encode_frame(MJpegEncoder *encoder, const SpiceRect *src,
-                        const SpiceBitmap *image, int top_down)
-{
-    SpiceChunks *chunks;
-    uint32_t image_stride;
-    size_t offset;
-    int i, chunk;
-
-    chunks = image->data;
-    offset = 0;
-    chunk = 0;
-    image_stride = image->stride;
-
-    const int skip_lines = top_down ? src->top : image->y - (src->bottom - 0);
-    for (i = 0; i < skip_lines; i++) {
-        get_image_line(chunks, &offset, &chunk, image_stride);
-    }
-
-    const unsigned int stream_height = src->bottom - src->top;
-    const unsigned int stream_width = src->right - src->left;
-
-    for (i = 0; i < stream_height; i++) {
-        uint8_t *src_line = get_image_line(chunks, &offset, &chunk, image_stride);
-
-        if (!src_line) {
-            return FALSE;
-        }
-
-        src_line += src->left * mjpeg_encoder_get_bytes_per_pixel(encoder);
-        if (mjpeg_encoder_encode_scanline(encoder, src_line, stream_width) == 0) {
-            return FALSE;
-        }
-    }
-
-    return TRUE;
-}
-
-int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               const SpiceBitmap *bitmap, int width, int height,
-                               const SpiceRect *src,
-                               int top_down, uint32_t frame_mm_time,
-                               uint8_t **outbuf, size_t *outbuf_size,
-                               int *data_size)
-{
-    int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
-                                    width, height, outbuf, outbuf_size,
-                                    frame_mm_time);
-    if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) {
-        return ret;
-    }
-
-    if (!encode_frame(encoder, src, bitmap, top_down)) {
-        return MJPEG_ENCODER_FRAME_UNSUPPORTED;
-    }
-
-    *data_size = mjpeg_encoder_end_frame(encoder);
-
-    return MJPEG_ENCODER_FRAME_ENCODE_DONE;
-}
-
-
-static void mjpeg_encoder_quality_eval_stop(MJpegEncoder *encoder)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    uint32_t quality_id;
-    uint32_t fps;
-
-    if (!rate_control->during_quality_eval) {
-        return;
-    }
-    switch (rate_control->quality_eval_data.type) {
-    case MJPEG_QUALITY_EVAL_TYPE_UPGRADE:
-        quality_id = rate_control->quality_eval_data.min_quality_id;
-        fps = rate_control->quality_eval_data.min_quality_fps;
-        break;
-    case MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE:
-        quality_id = rate_control->quality_eval_data.max_quality_id;
-        fps = rate_control->quality_eval_data.max_quality_fps;
-        break;
-    case MJPEG_QUALITY_EVAL_TYPE_SET:
-        quality_id = MJPEG_QUALITY_SAMPLE_NUM / 2;
-        fps = MJPEG_MAX_FPS / 2;
-        break;
-    default:
-        spice_warning("unexected");
-        return;
-    }
-    mjpeg_encoder_reset_quality(encoder, quality_id, fps, 0);
-    spice_debug("during quality evaluation: canceling."
-                "reset quality to %d fps %d",
-                mjpeg_quality_samples[rate_control->quality_id], rate_control->fps);
-}
-
-static void mjpeg_encoder_decrease_bit_rate(MJpegEncoder *encoder)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
-    uint64_t measured_byte_rate;
-    uint32_t measured_fps;
-    uint64_t decrease_size;
-
-    mjpeg_encoder_quality_eval_stop(encoder);
-
-    rate_control->client_state.max_video_latency = 0;
-    rate_control->client_state.max_audio_latency = 0;
-    if (rate_control->warmup_start_time) {
-        uint64_t now;
-
-        now = red_get_monotonic_time();
-        if (now - rate_control->warmup_start_time < MJPEG_WARMUP_TIME*1000*1000) {
-            spice_debug("during warmup. ignoring");
-            return;
-        } else {
-            rate_control->warmup_start_time = 0;
-        }
-    }
-
-    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
-        bit_rate_info->num_enc_frames > rate_control->fps) {
-        double duration_sec;
-
-        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
-        duration_sec /= (1000.0 * 1000.0 * 1000.0);
-        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
-        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
-        decrease_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
-        spice_debug("bit rate esitimation %.2f (Mbps) fps %u",
-                    measured_byte_rate*8/1024.0/1024,
-                    measured_fps);
-    } else {
-        measured_byte_rate = rate_control->byte_rate;
-        measured_fps = rate_control->fps;
-        decrease_size = measured_byte_rate/measured_fps;
-        spice_debug("bit rate not re-estimated %.2f (Mbps) fps %u",
-                    measured_byte_rate*8/1024.0/1024,
-                    measured_fps);
-    }
-
-    measured_byte_rate = MIN(rate_control->byte_rate, measured_byte_rate);
-
-    if (decrease_size >=  measured_byte_rate) {
-        decrease_size = measured_byte_rate / 2;
-    }
-
-    rate_control->byte_rate = measured_byte_rate - decrease_size;
-    bit_rate_info->change_start_time = 0;
-    bit_rate_info->change_start_mm_time = 0;
-    bit_rate_info->last_frame_time = 0;
-    bit_rate_info->num_enc_frames = 0;
-    bit_rate_info->sum_enc_size = 0;
-    bit_rate_info->was_upgraded = FALSE;
-
-    spice_debug("decrease bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
-    mjpeg_encoder_quality_eval_set_downgrade(encoder,
-                                             MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
-                                             rate_control->quality_id,
-                                             rate_control->fps);
-}
-
-static void mjpeg_encoder_handle_negative_client_stream_report(MJpegEncoder *encoder,
-                                                               uint32_t report_end_frame_mm_time)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-
-    spice_debug(NULL);
-
-    if ((rate_control->bit_rate_info.change_start_mm_time > report_end_frame_mm_time ||
-        !rate_control->bit_rate_info.change_start_mm_time) &&
-         !rate_control->bit_rate_info.was_upgraded) {
-        spice_debug("ignoring, a downgrade has already occurred later to the report time");
-        return;
-    }
-
-    mjpeg_encoder_decrease_bit_rate(encoder);
-}
-
-static void mjpeg_encoder_increase_bit_rate(MJpegEncoder *encoder)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
-    uint64_t measured_byte_rate;
-    uint32_t measured_fps;
-    uint64_t increase_size;
-
-
-    if (bit_rate_info->num_enc_frames > MJPEG_BIT_RATE_EVAL_MIN_NUM_FRAMES ||
-        bit_rate_info->num_enc_frames > rate_control->fps) {
-        uint64_t avg_frame_size;
-        double duration_sec;
-
-        duration_sec = (bit_rate_info->last_frame_time - bit_rate_info->change_start_time);
-        duration_sec /= (1000.0 * 1000.0 * 1000.0);
-        measured_byte_rate = bit_rate_info->sum_enc_size / duration_sec;
-        measured_fps = bit_rate_info->num_enc_frames / duration_sec;
-        avg_frame_size = bit_rate_info->sum_enc_size / bit_rate_info->num_enc_frames;
-        spice_debug("bit rate esitimation %.2f (Mbps) defined %.2f"
-                    " fps %u avg-frame-size=%.2f (KB)",
-                    measured_byte_rate*8/1024.0/1024,
-                    rate_control->byte_rate*8/1024.0/1024,
-                    measured_fps,
-                    avg_frame_size/1024.0);
-        increase_size = avg_frame_size;
-    } else {
-        spice_debug("not enough samples for measuring the bit rate. no change");
-        return;
-    }
-
-
-    mjpeg_encoder_quality_eval_stop(encoder);
-
-    if (measured_byte_rate + increase_size < rate_control->byte_rate) {
-        spice_debug("measured byte rate is small: not upgrading, just re-evaluating");
-    } else {
-        rate_control->byte_rate = MIN(measured_byte_rate, rate_control->byte_rate) + increase_size;
-    }
-
-    bit_rate_info->change_start_time = 0;
-    bit_rate_info->change_start_mm_time = 0;
-    bit_rate_info->last_frame_time = 0;
-    bit_rate_info->num_enc_frames = 0;
-    bit_rate_info->sum_enc_size = 0;
-    bit_rate_info->was_upgraded = TRUE;
-
-    spice_debug("increase bit rate %.2f (Mbps)", rate_control->byte_rate * 8 / 1024.0/1024.0);
-    mjpeg_encoder_quality_eval_set_upgrade(encoder,
-                                           MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE,
-                                           rate_control->quality_id,
-                                           rate_control->fps);
-}
-
-static void mjpeg_encoder_handle_positive_client_stream_report(MJpegEncoder *encoder,
-                                                               uint32_t report_start_frame_mm_time)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    MJpegEncoderBitRateInfo *bit_rate_info = &rate_control->bit_rate_info;
-    int stable_client_mm_time;
-    int timeout;
-
-    if (rate_control->during_quality_eval &&
-        rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
-        spice_debug("during quality evaluation (rate change). ignoring report");
-        return;
-    }
-
-    if ((rate_control->fps > MJPEG_IMPROVE_QUALITY_FPS_STRICT_TH ||
-         rate_control->fps >= mjpeg_encoder_get_source_fps(encoder)) &&
-         rate_control->quality_id > MJPEG_QUALITY_SAMPLE_NUM / 2) {
-        timeout = MJPEG_CLIENT_POSITIVE_REPORT_STRICT_TIMEOUT;
-    } else {
-        timeout = MJPEG_CLIENT_POSITIVE_REPORT_TIMEOUT;
-    }
-
-    stable_client_mm_time = (int)report_start_frame_mm_time - bit_rate_info->change_start_mm_time;
-
-    if (!bit_rate_info->change_start_mm_time || stable_client_mm_time < timeout) {
-        /* assessing the stability of the current setting and only then
-         * respond to the report */
-        spice_debug("no drops, but not enough time has passed for assessing"
-                    "the playback stability since the last bit rate change");
-        return;
-    }
-    mjpeg_encoder_increase_bit_rate(encoder);
-}
-
-/*
- * the video playback jitter buffer should be at least (send_time*2 + net_latency) for
- * preventing underflow
- */
-static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size,
-                                                uint64_t byte_rate,
-                                                uint32_t latency)
-{
-    uint32_t one_frame_time;
-    uint32_t min_delay;
-
-    if (!frame_enc_size || !byte_rate) {
-        return latency;
-    }
-    one_frame_time = (frame_enc_size*1000)/byte_rate;
-
-    min_delay = MIN(one_frame_time*2 + latency, MJPEG_MAX_CLIENT_PLAYBACK_DELAY);
-    return min_delay;
-}
-
-#define MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR 0.5
-#define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25
-#define MJPEG_VIDEO_DELAY_TH -15
-
-void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
-                                        uint32_t num_frames,
-                                        uint32_t num_drops,
-                                        uint32_t start_frame_mm_time,
-                                        uint32_t end_frame_mm_time,
-                                        int32_t end_frame_delay,
-                                        uint32_t audio_delay)
-{
-    MJpegEncoderRateControl *rate_control = &encoder->rate_control;
-    MJpegEncoderClientState *client_state = &rate_control->client_state;
-    uint64_t avg_enc_size = 0;
-    uint32_t min_playback_delay;
-    int is_video_delay_small = FALSE;
-
-    spice_debug("client report: #frames %u, #drops %d, duration %u video-delay %d audio-delay %u",
-                num_frames, num_drops,
-                end_frame_mm_time - start_frame_mm_time,
-                end_frame_delay, audio_delay);
-
-    if (!rate_control_is_active(encoder)) {
-        spice_debug("rate control was not activated: ignoring");
-        return;
-    }
-    if (rate_control->during_quality_eval) {
-        if (rate_control->quality_eval_data.type == MJPEG_QUALITY_EVAL_TYPE_DOWNGRADE &&
-            rate_control->quality_eval_data.reason == MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE) {
-            spice_debug("during rate downgrade evaluation");
-            return;
-        }
-    }
-
-    if (rate_control->num_recent_enc_frames) {
-        avg_enc_size = rate_control->sum_recent_enc_size /
-                       rate_control->num_recent_enc_frames;
-    }
-    spice_debug("recent size avg %.2f (KB)", avg_enc_size / 1024.0);
-    min_playback_delay = get_min_required_playback_delay(avg_enc_size, rate_control->byte_rate,
-                                                         mjpeg_encoder_get_latency(encoder));
-    spice_debug("min-delay %u client-delay %d", min_playback_delay, end_frame_delay);
-
-    if (min_playback_delay > end_frame_delay) {
-        uint32_t src_fps = mjpeg_encoder_get_source_fps(encoder);
-        /*
-        * if the stream is at its highest rate, we can't estimate the "real"
-        * network bit rate and the min_playback_delay
-        */
-        if (rate_control->quality_id != MJPEG_QUALITY_SAMPLE_NUM - 1 ||
-            rate_control->fps < MIN(src_fps, MJPEG_MAX_FPS) || end_frame_delay < 0) {
-            is_video_delay_small = TRUE;
-            if (encoder->cbs.update_client_playback_delay) {
-                encoder->cbs.update_client_playback_delay(encoder->cbs_opaque,
-                                                          min_playback_delay);
-            }
-        }
-    }
-
-
-    /*
-     * If the audio latency has decreased (since the start of the current
-     * sequence of positive reports), and the video latency is bigger, slow down
-     * the video rate
-     */
-    if (end_frame_delay > 0 &&
-        audio_delay < MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR*client_state->max_audio_latency &&
-        end_frame_delay > MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR*audio_delay) {
-        spice_debug("video_latency >> audio_latency && audio_latency << max (%u)",
-                    client_state->max_audio_latency);
-        mjpeg_encoder_handle_negative_client_stream_report(encoder,
-                                                           end_frame_mm_time);
-        return;
-    }
-
-    if (end_frame_delay < MJPEG_VIDEO_DELAY_TH) {
-        mjpeg_encoder_handle_negative_client_stream_report(encoder,
-                                                           end_frame_mm_time);
-    } else {
-        double major_delay_decrease_thresh;
-        double medium_delay_decrease_thresh;
-
-        client_state->max_video_latency = MAX(end_frame_delay, client_state->max_video_latency);
-        client_state->max_audio_latency = MAX(audio_delay, client_state->max_audio_latency);
-
-        medium_delay_decrease_thresh = client_state->max_video_latency;
-        medium_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
-
-        major_delay_decrease_thresh = medium_delay_decrease_thresh;
-        major_delay_decrease_thresh *= MJPEG_PLAYBACK_LATENCY_DECREASE_FACTOR;
-        /*
-         * since the bit rate and the required latency are only evaluation based on the
-         * reports we got till now, we assume that the latency is too low only if it
-         * was higher during the time that passed since the last report that resulted
-         * in a bit rate decrement. If we find that the latency has decreased, it might
-         * suggest that the stream bit rate is too high.
-         */
-        if ((end_frame_delay < medium_delay_decrease_thresh &&
-            is_video_delay_small) || end_frame_delay < major_delay_decrease_thresh) {
-            spice_debug("downgrade due to short video delay (last=%u, past-max=%u",
-                end_frame_delay, client_state->max_video_latency);
-            mjpeg_encoder_handle_negative_client_stream_report(encoder,
-                                                               end_frame_mm_time);
-        } else if (!num_drops) {
-            mjpeg_encoder_handle_positive_client_stream_report(encoder,
-                                                               start_frame_mm_time);
-
-        }
-    }
-}
-
-void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder)
-{
-    encoder->rate_control.server_state.num_frames_dropped++;
-    mjpeg_encoder_process_server_drops(encoder);
-}
-
-/*
- * decrease the bit rate if the drop rate on the sever side exceeds a pre defined
- * threshold.
- */
-static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder)
-{
-    MJpegEncoderServerState *server_state = &encoder->rate_control.server_state;
-    uint32_t num_frames_total;
-    double drop_factor;
-    uint32_t fps;
-
-    fps = MIN(encoder->rate_control.fps, mjpeg_encoder_get_source_fps(encoder));
-    if (server_state->num_frames_encoded < fps * MJPEG_SERVER_STATUS_EVAL_FPS_INTERVAL) {
-        return;
-    }
-
-    num_frames_total = server_state->num_frames_dropped + server_state->num_frames_encoded;
-    drop_factor = ((double)server_state->num_frames_dropped) / num_frames_total;
-
-    spice_debug("#drops %u total %u fps %u src-fps %u",
-                server_state->num_frames_dropped,
-                num_frames_total,
-                encoder->rate_control.fps,
-                mjpeg_encoder_get_source_fps(encoder));
-
-    if (drop_factor > MJPEG_SERVER_STATUS_DOWNGRADE_DROP_FACTOR_TH) {
-        mjpeg_encoder_decrease_bit_rate(encoder);
-    }
-    server_state->num_frames_encoded = 0;
-    server_state->num_frames_dropped = 0;
-}
-
-uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder)
-{
-    return encoder->rate_control.byte_rate * 8;
-}
-
-void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats)
-{
-    spice_assert(encoder != NULL && stats != NULL);
-    stats->starting_bit_rate = encoder->starting_bit_rate;
-    stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder);
-    stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames;
-}
-
-MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
-                                MJpegEncoderRateControlCbs *cbs,
-                                void *cbs_opaque)
-{
-    MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1);
-
-    encoder->first_frame = TRUE;
-    encoder->rate_control.byte_rate = starting_bit_rate / 8;
-    encoder->starting_bit_rate = starting_bit_rate;
-
-    if (cbs) {
-        struct timespec time;
-
-        clock_gettime(CLOCK_MONOTONIC, &time);
-        encoder->cbs = *cbs;
-        encoder->cbs_opaque = cbs_opaque;
-        mjpeg_encoder_reset_quality(encoder, MJPEG_QUALITY_SAMPLE_NUM / 2, 5, 0);
-        encoder->rate_control.during_quality_eval = TRUE;
-        encoder->rate_control.quality_eval_data.type = MJPEG_QUALITY_EVAL_TYPE_SET;
-        encoder->rate_control.quality_eval_data.reason = MJPEG_QUALITY_EVAL_REASON_RATE_CHANGE;
-        encoder->rate_control.warmup_start_time = ((uint64_t) time.tv_sec) * 1000000000 + time.tv_nsec;
-    } else {
-        encoder->cbs.get_roundtrip_ms = NULL;
-        mjpeg_encoder_reset_quality(encoder, MJPEG_LEGACY_STATIC_QUALITY_ID, MJPEG_MAX_FPS, 0);
-    }
-
-    encoder->cinfo.err = jpeg_std_error(&encoder->jerr);
-    jpeg_create_compress(&encoder->cinfo);
-
-    return encoder;
-}
diff --git a/server/mjpeg_encoder.h b/server/mjpeg_encoder.h
deleted file mode 100644
index d070e70..0000000
--- a/server/mjpeg_encoder.h
+++ /dev/null
@@ -1,100 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_MJPEG_ENCODER
-#define _H_MJPEG_ENCODER
-
-#include "red_common.h"
-
-enum {
-    MJPEG_ENCODER_FRAME_UNSUPPORTED = -1,
-    MJPEG_ENCODER_FRAME_DROP,
-    MJPEG_ENCODER_FRAME_ENCODE_DONE,
-};
-
-typedef struct MJpegEncoder MJpegEncoder;
-
-/*
- * Callbacks required for controling and adjusting
- * the stream bit rate:
- * get_roundtrip_ms: roundtrip time in milliseconds
- * get_source_fps: the input frame rate (#frames per second), i.e.,
- * the rate of frames arriving from the guest to spice-server,
- * before any drops.
- */
-typedef struct MJpegEncoderRateControlCbs {
-    uint32_t (*get_roundtrip_ms)(void *opaque);
-    uint32_t (*get_source_fps)(void *opaque);
-    void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms);
-} MJpegEncoderRateControlCbs;
-
-typedef struct MJpegEncoderStats {
-    uint64_t starting_bit_rate;
-    uint64_t cur_bit_rate;
-    double avg_quality;
-} MJpegEncoderStats;
-
-MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
-                                MJpegEncoderRateControlCbs *cbs, void *opaque);
-void mjpeg_encoder_destroy(MJpegEncoder *encoder);
-
-int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
-                               const SpiceBitmap *bitmap, int width, int height,
-                               const SpiceRect *src,
-                               int top_down, uint32_t frame_mm_time,
-                               uint8_t **outbuf, size_t *outbuf_size,
-                               int *data_size);
-
-/*
- * bit rate control
- */
-
-/*
- * Data that should be periodically obtained from the client. The report contains:
- * num_frames         : the number of frames that reached the client during the time
- *                      the report is referring to.
- * num_drops          : the part of the above frames that was dropped by the client due to
- *                      late arrival time.
- * start_frame_mm_time: the mm_time of the first frame included in the report
- * end_frame_mm_time  : the mm_time of the last_frame included in the report
- * end_frame_delay    : (end_frame_mm_time - client_mm_time)
- * audio delay        : the latency of the audio playback.
- *                      If there is no audio playback, set it to MAX_UINT.
- *
- */
-void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
-                                        uint32_t num_frames,
-                                        uint32_t num_drops,
-                                        uint32_t start_frame_mm_time,
-                                        uint32_t end_frame_mm_time,
-                                        int32_t end_frame_delay,
-                                        uint32_t audio_delay);
-
-/*
- * Notify the encoder each time a frame is dropped due to pipe
- * congestion.
- * We can deduce the client state by the frame dropping rate in the server.
- * Monitoring the frame drops can help in fine tuning the playback parameters
- * when the client reports are delayed.
- */
-void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
-
-uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
-void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats *stats);
-
-#endif
diff --git a/server/red_channel.c b/server/red_channel.c
index 609c83f..948d354 100644
--- a/server/red_channel.c
+++ b/server/red_channel.c
@@ -42,7 +42,7 @@
 #include "red_channel.h"
 #include "reds.h"
 #include "reds_stream.h"
-#include "main_dispatcher.h"
+#include "main-dispatcher.h"
 #include "utils.h"
 
 typedef struct EmptyMsgPipeItem {
diff --git a/server/red_common.h b/server/red_common.h
index 7f1677e..2d3977b 100644
--- a/server/red_common.h
+++ b/server/red_common.h
@@ -18,15 +18,26 @@
 #ifndef _H_RED_COMMON
 #define _H_RED_COMMON
 
-#include <spice/macros.h>
+#include <glib.h>
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
 #include <string.h>
+#include <unistd.h>
 
+#include <spice/macros.h>
+#include "common/log.h"
+#include "common/lz_common.h"
+#include "common/marshaller.h"
 #include "common/mem.h"
-#include "common/spice_common.h"
 #include "common/messages.h"
-#include "common/lz_common.h"
+#include "common/ring.h"
+#include "common/spice_common.h"
+#include "common/draw.h"
 
 #include "spice.h"
+#include "utils.h"
 
 #define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
 
diff --git a/server/red_dispatcher.c b/server/red_dispatcher.c
index a7825f5..7baa197 100644
--- a/server/red_dispatcher.c
+++ b/server/red_dispatcher.c
@@ -32,7 +32,7 @@
 
 #include "spice.h"
 #include "red_worker.h"
-#include "reds_sw_canvas.h"
+#include "canvas.h"
 #include "reds.h"
 #include "dispatcher.h"
 #include "red_parse_qxl.h"
diff --git a/server/red_dispatcher.h b/server/red_dispatcher.h
index 9f3474f..13c0cee 100644
--- a/server/red_dispatcher.h
+++ b/server/red_dispatcher.h
@@ -18,8 +18,6 @@
 #ifndef _H_RED_DISPATCHER
 #define _H_RED_DISPATCHER
 
-#include <unistd.h>
-#include <errno.h>
 #include "red_channel.h"
 
 typedef struct RedDispatcher RedDispatcher;
diff --git a/server/red_memslots.c b/server/red_memslots.c
deleted file mode 100644
index 1b3ec62..0000000
--- a/server/red_memslots.c
+++ /dev/null
@@ -1,184 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009,2010 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <inttypes.h>
-
-#include "red_common.h"
-#include "red_memslots.h"
-
-static unsigned long __get_clean_virt(RedMemSlotInfo *info, QXLPHYSICAL addr)
-{
-    return addr & info->memslot_clean_virt_mask;
-}
-
-static void print_memslots(RedMemSlotInfo *info)
-{
-    int i;
-    int x;
-
-    for (i = 0; i < info->num_memslots_groups; ++i) {
-        for (x = 0; x < info->num_memslots; ++x) {
-            if (!info->mem_slots[i][x].virt_start_addr &&
-                !info->mem_slots[i][x].virt_end_addr) {
-                continue;
-            }
-            printf("id %d, group %d, virt start %lx, virt end %lx, generation %u, delta %lx\n",
-                   x, i, info->mem_slots[i][x].virt_start_addr,
-                   info->mem_slots[i][x].virt_end_addr, info->mem_slots[i][x].generation,
-                   info->mem_slots[i][x].address_delta);
-            }
-    }
-}
-
-/* return 1 if validation successfull, 0 otherwise */
-int validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
-                  uint32_t add_size, uint32_t group_id)
-{
-    MemSlot *slot;
-
-    slot = &info->mem_slots[group_id][slot_id];
-    if ((virt + add_size) < virt) {
-        spice_critical("virtual address overlap");
-        return 0;
-    }
-
-    if (virt < slot->virt_start_addr || (virt + add_size) > slot->virt_end_addr) {
-        print_memslots(info);
-        spice_critical("virtual address out of range\n"
-              "    virt=0x%lx+0x%x slot_id=%d group_id=%d\n"
-              "    slot=0x%lx-0x%lx delta=0x%lx",
-              virt, add_size, slot_id, group_id,
-              slot->virt_start_addr, slot->virt_end_addr, slot->address_delta);
-        return 0;
-    }
-    return 1;
-}
-
-/*
- * return virtual address if successful, which may be 0.
- * returns 0 and sets error to 1 if an error condition occurs.
- */
-unsigned long get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
-                       int group_id, int *error)
-{
-    int slot_id;
-    int generation;
-    unsigned long h_virt;
-
-    MemSlot *slot;
-
-    *error = 0;
-    if (group_id > info->num_memslots_groups) {
-        spice_critical("group_id too big");
-        *error = 1;
-        return 0;
-    }
-
-    slot_id = get_memslot_id(info, addr);
-    if (slot_id > info->num_memslots) {
-        print_memslots(info);
-        spice_critical("slot_id %d too big, addr=%" PRIx64, slot_id, addr);
-        *error = 1;
-        return 0;
-    }
-
-    slot = &info->mem_slots[group_id][slot_id];
-
-    generation = get_generation(info, addr);
-    if (generation != slot->generation) {
-        print_memslots(info);
-        spice_critical("address generation is not valid, group_id %d, slot_id %d, gen %d, slot_gen %d\n",
-              group_id, slot_id, generation, slot->generation);
-        *error = 1;
-        return 0;
-    }
-
-    h_virt = __get_clean_virt(info, addr);
-    h_virt += slot->address_delta;
-
-    if (!validate_virt(info, h_virt, slot_id, add_size, group_id)) {
-        *error = 1;
-        return 0;
-    }
-
-    return h_virt;
-}
-
-void red_memslot_info_init(RedMemSlotInfo *info,
-                           uint32_t num_groups, uint32_t num_slots,
-                           uint8_t generation_bits,
-                           uint8_t id_bits,
-                           uint8_t internal_groupslot_id)
-{
-    uint32_t i;
-
-    spice_assert(num_slots > 0);
-    spice_assert(num_groups > 0);
-
-    info->num_memslots_groups = num_groups;
-    info->num_memslots = num_slots;
-    info->generation_bits = generation_bits;
-    info->mem_slot_bits = id_bits;
-    info->internal_groupslot_id = internal_groupslot_id;
-
-    info->mem_slots = spice_new(MemSlot *, num_groups);
-
-    for (i = 0; i < num_groups; ++i) {
-        info->mem_slots[i] = spice_new0(MemSlot, num_slots);
-    }
-
-    /* TODO: use QXLPHYSICAL_BITS */
-    info->memslot_id_shift = 64 - info->mem_slot_bits;
-    info->memslot_gen_shift = 64 - (info->mem_slot_bits + info->generation_bits);
-    info->memslot_gen_mask = ~((QXLPHYSICAL)-1 << info->generation_bits);
-    info->memslot_clean_virt_mask = (((QXLPHYSICAL)(-1)) >>
-                                       (info->mem_slot_bits + info->generation_bits));
-}
-
-void red_memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
-                               uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
-                               uint32_t generation)
-{
-    spice_assert(info->num_memslots_groups > slot_group_id);
-    spice_assert(info->num_memslots > slot_id);
-
-    info->mem_slots[slot_group_id][slot_id].address_delta = addr_delta;
-    info->mem_slots[slot_group_id][slot_id].virt_start_addr = virt_start;
-    info->mem_slots[slot_group_id][slot_id].virt_end_addr = virt_end;
-    info->mem_slots[slot_group_id][slot_id].generation = generation;
-}
-
-void red_memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id)
-{
-    spice_assert(info->num_memslots_groups > slot_group_id);
-    spice_assert(info->num_memslots > slot_id);
-
-    info->mem_slots[slot_group_id][slot_id].virt_start_addr = 0;
-    info->mem_slots[slot_group_id][slot_id].virt_end_addr = 0;
-}
-
-void red_memslot_info_reset(RedMemSlotInfo *info)
-{
-        uint32_t i;
-        for (i = 0; i < info->num_memslots_groups; ++i) {
-            memset(info->mem_slots[i], 0, sizeof(MemSlot) * info->num_memslots);
-        }
-}
diff --git a/server/red_memslots.h b/server/red_memslots.h
deleted file mode 100644
index 27443a6..0000000
--- a/server/red_memslots.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009,2010 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_REDMEMSLOTS
-#define _H_REDMEMSLOTS
-
-#include "red_common.h"
-
-#include <spice/qxl_dev.h>
-
-typedef struct MemSlot {
-    int generation;
-    unsigned long virt_start_addr;
-    unsigned long virt_end_addr;
-    long address_delta;
-} MemSlot;
-
-typedef struct RedMemSlotInfo {
-    MemSlot **mem_slots;
-    uint32_t num_memslots_groups;
-    uint32_t num_memslots;
-    uint8_t mem_slot_bits;
-    uint8_t generation_bits;
-    uint8_t memslot_id_shift;
-    uint8_t memslot_gen_shift;
-    uint8_t internal_groupslot_id;
-    unsigned long memslot_gen_mask;
-    unsigned long memslot_clean_virt_mask;
-} RedMemSlotInfo;
-
-static inline int get_memslot_id(RedMemSlotInfo *info, uint64_t addr)
-{
-    return addr >> info->memslot_id_shift;
-}
-
-static inline int get_generation(RedMemSlotInfo *info, uint64_t addr)
-{
-    return (addr >> info->memslot_gen_shift) & info->memslot_gen_mask;
-}
-
-int validate_virt(RedMemSlotInfo *info, unsigned long virt, int slot_id,
-                  uint32_t add_size, uint32_t group_id);
-unsigned long get_virt(RedMemSlotInfo *info, QXLPHYSICAL addr, uint32_t add_size,
-                       int group_id, int *error);
-
-void red_memslot_info_init(RedMemSlotInfo *info,
-                           uint32_t num_groups, uint32_t num_slots,
-                           uint8_t generation_bits,
-                           uint8_t id_bits,
-                           uint8_t internal_groupslot_id);
-void red_memslot_info_add_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id,
-                               uint64_t addr_delta, unsigned long virt_start, unsigned long virt_end,
-                               uint32_t generation);
-void red_memslot_info_del_slot(RedMemSlotInfo *info, uint32_t slot_group_id, uint32_t slot_id);
-void red_memslot_info_reset(RedMemSlotInfo *info);
-
-#endif
diff --git a/server/red_parse_qxl.c b/server/red_parse_qxl.c
index 2cfd5ea..9464c7a 100644
--- a/server/red_parse_qxl.c
+++ b/server/red_parse_qxl.c
@@ -25,7 +25,7 @@
 #include "common/lz_common.h"
 #include "spice-bitmap-utils.h"
 #include "red_common.h"
-#include "red_memslots.h"
+#include "memslot.h"
 #include "red_parse_qxl.h"
 
 /* Max size in bytes for any data field used in a QXL command.
@@ -52,7 +52,7 @@ static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
     uint8_t *hex;
     int i;
 
-    hex = (uint8_t*)get_virt(slots, addr, bytes, group_id);
+    hex = (uint8_t*)memslot_get_virt(slots, addr, bytes, group_id);
     for (i = 0; i < bytes; i++) {
         if (0 == i % 16) {
             fprintf(stderr, "%lx: ", addr+i);
@@ -118,7 +118,7 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
     data_size += red->data_size;
     red->data = qxl->data;
     red->prev_chunk = red->next_chunk = NULL;
-    if (!validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id)) {
+    if (!memslot_validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id)) {
         red->data = NULL;
         return 0;
     }
@@ -132,8 +132,8 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
             goto error;
         }
 
-        memslot_id = get_memslot_id(slots, next_chunk);
-        qxl = (QXLDataChunk *)get_virt(slots, next_chunk, sizeof(*qxl),
+        memslot_id = memslot_get_id(slots, next_chunk);
+        qxl = (QXLDataChunk *)memslot_get_virt(slots, next_chunk, sizeof(*qxl),
                                        group_id, &error);
         if (error)
             goto error;
@@ -161,7 +161,7 @@ static size_t red_get_data_chunks_ptr(RedMemSlotInfo *slots, int group_id,
             spice_warning("too much data inside chunks, avoiding DoS\n");
             goto error;
         }
-        if (!validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id))
+        if (!memslot_validate_virt(slots, (intptr_t)red->data, memslot_id, red->data_size, group_id))
             goto error;
     }
 
@@ -185,9 +185,9 @@ static size_t red_get_data_chunks(RedMemSlotInfo *slots, int group_id,
 {
     QXLDataChunk *qxl;
     int error;
-    int memslot_id = get_memslot_id(slots, addr);
+    int memslot_id = memslot_get_id(slots, addr);
 
-    qxl = (QXLDataChunk *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLDataChunk *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return 0;
     }
@@ -242,12 +242,12 @@ static SpicePath *red_get_path(RedMemSlotInfo *slots, int group_id,
     uint32_t count;
     int error;
 
-    qxl = (QXLPath *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLPath *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return NULL;
     }
     size = red_get_data_chunks_ptr(slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &chunks, &qxl->chunk);
     data = red_linearize_chunk(&chunks, size, &free_data);
     red_put_data_chunks(&chunks);
@@ -321,12 +321,12 @@ static SpiceClipRects *red_get_clip_rects(RedMemSlotInfo *slots, int group_id,
     int error;
     uint32_t num_rects;
 
-    qxl = (QXLClipRects *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLClipRects *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return NULL;
     }
     size = red_get_data_chunks_ptr(slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &chunks, &qxl->chunk);
     data = red_linearize_chunk(&chunks, size, &free_data);
     red_put_data_chunks(&chunks);
@@ -355,7 +355,7 @@ static SpiceChunks *red_get_image_data_flat(RedMemSlotInfo *slots, int group_id,
 
     data = spice_chunks_new(1);
     data->data_size      = size;
-    data->chunk[0].data  = (void*)get_virt(slots, addr, size, group_id, &error);
+    data->chunk[0].data  = (void*)memslot_get_virt(slots, addr, size, group_id, &error);
     if (error) {
         return 0;
     }
@@ -445,7 +445,7 @@ static SpiceImage *red_get_image(RedMemSlotInfo *slots, int group_id,
         return NULL;
     }
 
-    qxl = (QXLImage *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLImage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return NULL;
     }
@@ -488,14 +488,14 @@ static SpiceImage *red_get_image(RedMemSlotInfo *slots, int group_id,
         if (palette) {
             QXLPalette *qp;
             int i, num_ents;
-            qp = (QXLPalette *)get_virt(slots, palette,
+            qp = (QXLPalette *)memslot_get_virt(slots, palette,
                                         sizeof(*qp), group_id, &error);
             if (error) {
                 goto error;
             }
             num_ents = qp->num_ents;
-            if (!validate_virt(slots, (intptr_t)qp->ents,
-                               get_memslot_id(slots, palette),
+            if (!memslot_validate_virt(slots, (intptr_t)qp->ents,
+                               memslot_get_id(slots, palette),
                                num_ents * sizeof(qp->ents[0]), group_id)) {
                 goto error;
             }
@@ -544,7 +544,7 @@ static SpiceImage *red_get_image(RedMemSlotInfo *slots, int group_id,
     case SPICE_IMAGE_TYPE_QUIC:
         red->u.quic.data_size = qxl->quic.data_size;
         size = red_get_data_chunks_ptr(slots, group_id,
-                                       get_memslot_id(slots, addr),
+                                       memslot_get_id(slots, addr),
                                        &chunks, (QXLDataChunk *)qxl->quic.data);
         spice_assert(size == red->u.quic.data_size);
         if (size != red->u.quic.data_size) {
@@ -741,7 +741,7 @@ static bool get_transform(RedMemSlotInfo *slots,
     if (qxl_transform == 0)
         return FALSE;
 
-    t = (uint32_t *)get_virt(slots, qxl_transform, sizeof(*dst_transform), group_id, &error);
+    t = (uint32_t *)memslot_get_virt(slots, qxl_transform, sizeof(*dst_transform), group_id, &error);
 
     if (!t || error)
         return FALSE;
@@ -816,7 +816,7 @@ static int red_get_stroke_ptr(RedMemSlotInfo *slots, int group_id,
         red->attr.style = spice_malloc_n(style_nseg, sizeof(SPICE_FIXED28_4));
         red->attr.style_nseg  = style_nseg;
         spice_assert(qxl->attr.style);
-        buf = (uint8_t *)get_virt(slots, qxl->attr.style,
+        buf = (uint8_t *)memslot_get_virt(slots, qxl->attr.style,
                                   style_nseg * sizeof(QXLFIXED), group_id, &error);
         if (error) {
             return error;
@@ -858,12 +858,12 @@ static SpiceString *red_get_string(RedMemSlotInfo *slots, int group_id,
     int error;
     uint16_t qxl_flags, qxl_length;
 
-    qxl = (QXLString *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLString *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return NULL;
     }
     chunk_size = red_get_data_chunks_ptr(slots, group_id,
-                                         get_memslot_id(slots, addr),
+                                         memslot_get_id(slots, addr),
                                          &chunks, &qxl->chunk);
     if (!chunk_size) {
         /* XXX could be a zero sized string.. */
@@ -1013,7 +1013,7 @@ static int red_get_native_drawable(RedMemSlotInfo *slots, int group_id,
     int i;
     int error = 0;
 
-    qxl = (QXLDrawable *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return error;
     }
@@ -1096,7 +1096,7 @@ static int red_get_compat_drawable(RedMemSlotInfo *slots, int group_id,
     QXLCompatDrawable *qxl;
     int error;
 
-    qxl = (QXLCompatDrawable *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLCompatDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return error;
     }
@@ -1244,7 +1244,7 @@ int red_get_update_cmd(RedMemSlotInfo *slots, int group_id,
     QXLUpdateCmd *qxl;
     int error;
 
-    qxl = (QXLUpdateCmd *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLUpdateCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return 1;
     }
@@ -1273,7 +1273,7 @@ int red_get_message(RedMemSlotInfo *slots, int group_id,
      *   luckily this is for debug logging only,
      *   so we can just ignore it by default.
      */
-    qxl = (QXLMessage *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLMessage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return 1;
     }
@@ -1312,7 +1312,7 @@ int red_get_surface_cmd(RedMemSlotInfo *slots, int group_id,
     int error;
     unsigned int bpp;
 
-    qxl = (QXLSurfaceCmd *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLSurfaceCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                     &error);
     if (error) {
         return 1;
@@ -1350,7 +1350,7 @@ int red_get_surface_cmd(RedMemSlotInfo *slots, int group_id,
             return 1;
         }
         red->u.surface_create.data =
-            (uint8_t*)get_virt(slots, qxl->u.surface_create.data, size, group_id, &error);
+            (uint8_t*)memslot_get_virt(slots, qxl->u.surface_create.data, size, group_id, &error);
         if (error) {
             return 1;
         }
@@ -1374,7 +1374,7 @@ static int red_get_cursor(RedMemSlotInfo *slots, int group_id,
     bool free_data;
     int error;
 
-    qxl = (QXLCursor *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLCursor *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return 1;
     }
@@ -1389,7 +1389,7 @@ static int red_get_cursor(RedMemSlotInfo *slots, int group_id,
     red->flags = 0;
     red->data_size = qxl->data_size;
     size = red_get_data_chunks_ptr(slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &chunks, &qxl->chunk);
     red->data_size = MIN(red->data_size, size);
     data = red_linearize_chunk(&chunks, size, &free_data);
@@ -1414,7 +1414,7 @@ int red_get_cursor_cmd(RedMemSlotInfo *slots, int group_id,
     QXLCursorCmd *qxl;
     int error;
 
-    qxl = (QXLCursorCmd *)get_virt(slots, addr, sizeof(*qxl), group_id, &error);
+    qxl = (QXLCursorCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id, &error);
     if (error) {
         return error;
     }
diff --git a/server/red_parse_qxl.h b/server/red_parse_qxl.h
index b3b28e1..09059f5 100644
--- a/server/red_parse_qxl.h
+++ b/server/red_parse_qxl.h
@@ -21,7 +21,7 @@
 
 #include <spice/qxl_dev.h>
 #include "red_common.h"
-#include "red_memslots.h"
+#include "memslot.h"
 
 typedef struct RedDrawable {
     int refs;
diff --git a/server/red_record_qxl.c b/server/red_record_qxl.c
index b35b462..0a29432 100644
--- a/server/red_record_qxl.c
+++ b/server/red_record_qxl.c
@@ -23,9 +23,9 @@
 #include <inttypes.h>
 #include "red_worker.h"
 #include "red_common.h"
-#include "red_memslots.h"
+#include "memslot.h"
 #include "red_parse_qxl.h"
-#include "zlib_encoder.h"
+#include "zlib-encoder.h"
 
 #if 0
 static void hexdump_qxl(RedMemSlotInfo *slots, int group_id,
@@ -139,21 +139,21 @@ static size_t red_record_data_chunks_ptr(FILE *fd, const char *prefix,
 
     while (cur->next_chunk) {
         cur =
-            (QXLDataChunk*)get_virt(slots, cur->next_chunk, sizeof(*cur), group_id,
+            (QXLDataChunk*)memslot_get_virt(slots, cur->next_chunk, sizeof(*cur), group_id,
                                     &error);
         data_size += cur->data_size;
         count_chunks++;
     }
     fprintf(fd, "data_chunks %d %ld\n", count_chunks, data_size);
-    validate_virt(slots, (intptr_t)qxl->data, memslot_id, qxl->data_size, group_id);
+    memslot_validate_virt(slots, (intptr_t)qxl->data, memslot_id, qxl->data_size, group_id);
     write_binary(fd, prefix, qxl->data_size, qxl->data);
 
     while (qxl->next_chunk) {
-        memslot_id = get_memslot_id(slots, qxl->next_chunk);
-        qxl = (QXLDataChunk*)get_virt(slots, qxl->next_chunk, sizeof(*qxl), group_id,
+        memslot_id = memslot_get_id(slots, qxl->next_chunk);
+        qxl = (QXLDataChunk*)memslot_get_virt(slots, qxl->next_chunk, sizeof(*qxl), group_id,
                                       &error);
 
-        validate_virt(slots, (intptr_t)qxl->data, memslot_id, qxl->data_size, group_id);
+        memslot_validate_virt(slots, (intptr_t)qxl->data, memslot_id, qxl->data_size, group_id);
         write_binary(fd, prefix, qxl->data_size, qxl->data);
     }
 
@@ -165,10 +165,10 @@ static size_t red_record_data_chunks(FILE *fd, const char *prefix,
                                      QXLPHYSICAL addr)
 {
     QXLDataChunk *qxl;
-    int memslot_id = get_memslot_id(slots, addr);
+    int memslot_id = memslot_get_id(slots, addr);
     int error;
 
-    qxl = (QXLDataChunk*)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLDataChunk*)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                   &error);
     return red_record_data_chunks_ptr(fd, prefix, slots, group_id, memslot_id, qxl);
 }
@@ -195,10 +195,10 @@ static void red_record_path(FILE *fd, RedMemSlotInfo *slots, int group_id,
     QXLPath *qxl;
     int error;
 
-    qxl = (QXLPath *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLPath *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                               &error);
     red_record_data_chunks_ptr(fd, "path", slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &qxl->chunk);
 }
 
@@ -208,11 +208,11 @@ static void red_record_clip_rects(FILE *fd, RedMemSlotInfo *slots, int group_id,
     QXLClipRects *qxl;
     int error;
 
-    qxl = (QXLClipRects *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLClipRects *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                    &error);
     fprintf(fd, "num_rects %d\n", qxl->num_rects);
     red_record_data_chunks_ptr(fd, "clip_rects", slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &qxl->chunk);
 }
 
@@ -223,7 +223,7 @@ static void red_record_virt_data_flat(FILE *fd, const char *prefix,
     int error;
 
     write_binary(fd, prefix,
-                 size, (uint8_t*)get_virt(slots, addr, size, group_id,
+                 size, (uint8_t*)memslot_get_virt(slots, addr, size, group_id,
                                           &error));
 }
 
@@ -253,7 +253,7 @@ static void red_record_image(FILE *fd, RedMemSlotInfo *slots, int group_id,
         return;
     }
 
-    qxl = (QXLImage *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLImage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                &error);
     fprintf(fd, "descriptor.id %ld\n", qxl->descriptor.id);
     fprintf(fd, "descriptor.type %d\n", qxl->descriptor.type);
@@ -273,12 +273,12 @@ static void red_record_image(FILE *fd, RedMemSlotInfo *slots, int group_id,
         if (qxl->bitmap.palette) {
             QXLPalette *qp;
             int i, num_ents;
-            qp = (QXLPalette *)get_virt(slots, qxl->bitmap.palette,
+            qp = (QXLPalette *)memslot_get_virt(slots, qxl->bitmap.palette,
                                         sizeof(*qp), group_id, &error);
             num_ents = qp->num_ents;
             fprintf(fd, "qp.num_ents %d\n", qp->num_ents);
-            validate_virt(slots, (intptr_t)qp->ents,
-                          get_memslot_id(slots, qxl->bitmap.palette),
+            memslot_validate_virt(slots, (intptr_t)qp->ents,
+                          memslot_get_id(slots, qxl->bitmap.palette),
                           num_ents * sizeof(qp->ents[0]), group_id);
             fprintf(fd, "unique %ld\n", qp->unique);
             for (i = 0; i < num_ents; i++) {
@@ -302,7 +302,7 @@ static void red_record_image(FILE *fd, RedMemSlotInfo *slots, int group_id,
     case SPICE_IMAGE_TYPE_QUIC:
         fprintf(fd, "quic.data_size %d\n", qxl->quic.data_size);
         size = red_record_data_chunks_ptr(fd, "quic.data", slots, group_id,
-                                       get_memslot_id(slots, addr),
+                                       memslot_get_id(slots, addr),
                                        (QXLDataChunk *)qxl->quic.data);
         spice_assert(size == qxl->quic.data_size);
         break;
@@ -426,7 +426,7 @@ static void red_record_stroke_ptr(FILE *fd, RedMemSlotInfo *slots, int group_id,
 
         fprintf(fd, "attr.style_nseg %d\n", qxl->attr.style_nseg);
         spice_assert(qxl->attr.style);
-        buf = (uint8_t *)get_virt(slots, qxl->attr.style,
+        buf = (uint8_t *)memslot_get_virt(slots, qxl->attr.style,
                                   style_nseg * sizeof(QXLFIXED), group_id,
                                   &error);
         write_binary(fd, "style", style_nseg * sizeof(QXLFIXED), buf);
@@ -443,13 +443,13 @@ static void red_record_string(FILE *fd, RedMemSlotInfo *slots, int group_id,
     size_t chunk_size;
     int error;
 
-    qxl = (QXLString *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLString *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                 &error);
     fprintf(fd, "data_size %d\n", qxl->data_size);
     fprintf(fd, "length %d\n", qxl->length);
     fprintf(fd, "flags %d\n", qxl->flags);
     chunk_size = red_record_data_chunks_ptr(fd, "string", slots, group_id,
-                                         get_memslot_id(slots, addr),
+                                         memslot_get_id(slots, addr),
                                          &qxl->chunk);
     spice_assert(chunk_size == qxl->data_size);
 }
@@ -521,7 +521,7 @@ static void red_record_native_drawable(FILE *fd, RedMemSlotInfo *slots, int grou
     int i;
     int error;
 
-    qxl = (QXLDrawable *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                   &error);
 
     red_record_rect_ptr(fd, "bbox", &qxl->bbox);
@@ -597,7 +597,7 @@ static void red_record_compat_drawable(FILE *fd, RedMemSlotInfo *slots, int grou
     QXLCompatDrawable *qxl;
     int error;
 
-    qxl = (QXLCompatDrawable *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLCompatDrawable *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                         &error);
 
     red_record_rect_ptr(fd, "bbox", &qxl->bbox);
@@ -676,7 +676,7 @@ static void red_record_update_cmd(FILE *fd, RedMemSlotInfo *slots, int group_id,
     QXLUpdateCmd *qxl;
     int error;
 
-    qxl = (QXLUpdateCmd *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLUpdateCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                    &error);
 
     fprintf(fd, "update\n");
@@ -697,7 +697,7 @@ static void red_record_message(FILE *fd, RedMemSlotInfo *slots, int group_id,
      *   luckily this is for debug logging only,
      *   so we can just ignore it by default.
      */
-    qxl = (QXLMessage *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLMessage *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                  &error);
     write_binary(fd, "message", strlen((char*)qxl->data), (uint8_t*)qxl->data);
 }
@@ -709,7 +709,7 @@ static void red_record_surface_cmd(FILE *fd, RedMemSlotInfo *slots, int group_id
     size_t size;
     int error;
 
-    qxl = (QXLSurfaceCmd *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLSurfaceCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                     &error);
 
     fprintf(fd, "surface_cmd\n");
@@ -726,7 +726,7 @@ static void red_record_surface_cmd(FILE *fd, RedMemSlotInfo *slots, int group_id
         size = qxl->u.surface_create.height * abs(qxl->u.surface_create.stride);
         if ((qxl->flags & QXL_SURF_FLAG_KEEP_DATA) != 0) {
             write_binary(fd, "data", size,
-                (uint8_t*)get_virt(slots, qxl->u.surface_create.data, size, group_id,
+                (uint8_t*)memslot_get_virt(slots, qxl->u.surface_create.data, size, group_id,
                                    &error));
         }
         break;
@@ -739,7 +739,7 @@ static void red_record_cursor(FILE *fd, RedMemSlotInfo *slots, int group_id,
     QXLCursor *qxl;
     int error;
 
-    qxl = (QXLCursor *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLCursor *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                 &error);
 
     fprintf(fd, "header.unique %ld\n", qxl->header.unique);
@@ -751,7 +751,7 @@ static void red_record_cursor(FILE *fd, RedMemSlotInfo *slots, int group_id,
 
     fprintf(fd, "data_size %d\n", qxl->data_size);
     red_record_data_chunks_ptr(fd, "cursor", slots, group_id,
-                                   get_memslot_id(slots, addr),
+                                   memslot_get_id(slots, addr),
                                    &qxl->chunk);
 }
 
@@ -761,7 +761,7 @@ void red_record_cursor_cmd(FILE *fd, RedMemSlotInfo *slots, int group_id,
     QXLCursorCmd *qxl;
     int error;
 
-    qxl = (QXLCursorCmd *)get_virt(slots, addr, sizeof(*qxl), group_id,
+    qxl = (QXLCursorCmd *)memslot_get_virt(slots, addr, sizeof(*qxl), group_id,
                                    &error);
 
     fprintf(fd, "cursor_cmd\n");
diff --git a/server/red_record_qxl.h b/server/red_record_qxl.h
index b737db8..6fcbec9 100644
--- a/server/red_record_qxl.h
+++ b/server/red_record_qxl.h
@@ -21,7 +21,7 @@
 
 #include <spice/qxl_dev.h>
 #include "red_common.h"
-#include "red_memslots.h"
+#include "memslot.h"
 
 void red_record_dev_input_primary_surface_create(
                            FILE *fd, QXLDevSurfaceCreate *surface, uint8_t *line_0);
diff --git a/server/red_replay_qxl.c b/server/red_replay_qxl.c
index ad1a8fd..6e32588 100644
--- a/server/red_replay_qxl.c
+++ b/server/red_replay_qxl.c
@@ -26,7 +26,7 @@
 #include "reds.h"
 #include "red_worker.h"
 #include "red_common.h"
-#include "red_memslots.h"
+#include "memslot.h"
 #include "red_parse_qxl.h"
 #include "red_replay_qxl.h"
 #include <glib.h>
diff --git a/server/red_worker.c b/server/red_worker.c
index e0128dc..d51e347 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -886,7 +886,7 @@ static void handle_dev_del_memslot(void *opaque, void *payload)
     uint32_t slot_id = msg->slot_id;
     uint32_t slot_group_id = msg->slot_group_id;
 
-    red_memslot_info_del_slot(&worker->mem_slots, slot_group_id, slot_id);
+    memslot_info_del_slot(&worker->mem_slots, slot_group_id, slot_id);
 }
 
 static void handle_dev_destroy_surface_wait(void *opaque, void *payload)
@@ -959,7 +959,7 @@ static void dev_create_primary_surface(RedWorker *worker, uint32_t surface_id,
     spice_warn_if(((uint64_t)abs(surface.stride) * (uint64_t)surface.height) !=
              abs(surface.stride) * surface.height);
 
-    line_0 = (uint8_t*)get_virt(&worker->mem_slots, surface.mem,
+    line_0 = (uint8_t*)memslot_get_virt(&worker->mem_slots, surface.mem,
                                 surface.height * abs(surface.stride),
                                 surface.group_id, &error);
     if (error) {
@@ -1228,7 +1228,7 @@ static void handle_dev_monitors_config_async(void *opaque, void *payload)
     int error;
     uint16_t count, max_allowed;
     QXLMonitorsConfig *dev_monitors_config =
-        (QXLMonitorsConfig*)get_virt(&worker->mem_slots, msg->monitors_config,
+        (QXLMonitorsConfig*)memslot_get_virt(&worker->mem_slots, msg->monitors_config,
                                      qxl_monitors_config_size(1),
                                      msg->group_id, &error);
 
@@ -1252,7 +1252,7 @@ static void handle_dev_monitors_config_async(void *opaque, void *payload)
     }
     /* get pointer again to check virtual size */
     dev_monitors_config =
-        (QXLMonitorsConfig*)get_virt(&worker->mem_slots, msg->monitors_config,
+        (QXLMonitorsConfig*)memslot_get_virt(&worker->mem_slots, msg->monitors_config,
                                      qxl_monitors_config_size(count),
                                      msg->group_id, &error);
     if (error) {
@@ -1356,7 +1356,7 @@ static void handle_dev_set_mouse_mode(void *opaque, void *payload)
 
 static void dev_add_memslot(RedWorker *worker, QXLDevMemSlot mem_slot)
 {
-    red_memslot_info_add_slot(&worker->mem_slots, mem_slot.slot_group_id, mem_slot.slot_id,
+    memslot_info_add_slot(&worker->mem_slots, mem_slot.slot_group_id, mem_slot.slot_id,
                               mem_slot.addr_delta, mem_slot.virt_start, mem_slot.virt_end,
                               mem_slot.generation);
 }
@@ -1367,7 +1367,7 @@ static void handle_dev_add_memslot(void *opaque, void *payload)
     RedWorkerMessageAddMemslot *msg = payload;
     QXLDevMemSlot mem_slot = msg->mem_slot;
 
-    red_memslot_info_add_slot(&worker->mem_slots, mem_slot.slot_group_id, mem_slot.slot_id,
+    memslot_info_add_slot(&worker->mem_slots, mem_slot.slot_group_id, mem_slot.slot_id,
                               mem_slot.addr_delta, mem_slot.virt_start, mem_slot.virt_end,
                               mem_slot.generation);
 }
@@ -1384,7 +1384,7 @@ static void handle_dev_reset_memslots(void *opaque, void *payload)
 {
     RedWorker *worker = opaque;
 
-    red_memslot_info_reset(&worker->mem_slots);
+    memslot_info_reset(&worker->mem_slots);
 }
 
 static void handle_dev_driver_unload(void *opaque, void *payload)
@@ -1695,7 +1695,7 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     worker->watches[0].watch_func = handle_dev_input;
     worker->watches[0].watch_func_opaque = worker;
 
-    red_memslot_info_init(&worker->mem_slots,
+    memslot_info_init(&worker->mem_slots,
                           init_info.num_memslots_groups,
                           init_info.num_memslots,
                           init_info.memslot_gen_bits,
diff --git a/server/red_worker.h b/server/red_worker.h
index a76c805..1e2c550 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -18,9 +18,6 @@
 #ifndef _H_REDWORKER
 #define _H_REDWORKER
 
-#include <unistd.h>
-#include <errno.h>
-#include "utils.h"
 #include "red_common.h"
 #include "red_dispatcher.h"
 #include "red_parse_qxl.h"
diff --git a/server/reds.c b/server/reds.c
index 8b3c3cb..509c346 100644
--- a/server/reds.c
+++ b/server/reds.c
@@ -56,16 +56,15 @@
 #include "spice.h"
 #include "reds.h"
 #include "agent-msg-filter.h"
-#include "inputs_channel.h"
-#include "main_channel.h"
-#include "red_common.h"
+#include "inputs-channel.h"
+#include "main-channel.h"
 #include "red_dispatcher.h"
-#include "main_dispatcher.h"
-#include "snd_worker.h"
+#include "main-dispatcher.h"
+#include "sound.h"
 #include "stat.h"
 #include "demarshallers.h"
-#include "char_device.h"
-#include "migration_protocol.h"
+#include "char-device.h"
+#include "migration-protocol.h"
 #ifdef USE_SMARTCARD
 #include "smartcard.h"
 #endif
diff --git a/server/reds.h b/server/reds.h
index fcdc5eb..b0168db 100644
--- a/server/reds.h
+++ b/server/reds.h
@@ -28,7 +28,10 @@
 #include "common/messages.h"
 #include "spice.h"
 #include "red_channel.h"
-#include "migration_protocol.h"
+#include "migration-protocol.h"
+
+#define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
+
 
 struct QXLState {
     QXLInterface          *qif;
diff --git a/server/reds_stream.c b/server/reds_stream.c
index 3b47391..6f5c43f 100644
--- a/server/reds_stream.c
+++ b/server/reds_stream.c
@@ -19,7 +19,7 @@
 #include <config.h>
 #endif
 
-#include "main_dispatcher.h"
+#include "main-dispatcher.h"
 #include "red_common.h"
 #include "reds_stream.h"
 #include "common/log.h"
diff --git a/server/reds_sw_canvas.c b/server/reds_sw_canvas.c
deleted file mode 100644
index 297df37..0000000
--- a/server/reds_sw_canvas.c
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
-   Copyright (C) 2011 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "common/spice_common.h"
-
-#include "reds_sw_canvas.h"
-#define SW_CANVAS_IMAGE_CACHE
-#include "common/sw_canvas.c"
-#undef SW_CANVAS_IMAGE_CACHE
diff --git a/server/reds_sw_canvas.h b/server/reds_sw_canvas.h
deleted file mode 100644
index 96a4c0c..0000000
--- a/server/reds_sw_canvas.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
-   Copyright (C) 2011 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifndef _H_REDS_SW_CANVAS
-#define _H_REDS_SW_CANVAS
-
-#define SW_CANVAS_IMAGE_CACHE
-#include "common/sw_canvas.h"
-#undef SW_CANVAS_IMAGE_CACHE
-
-#endif
diff --git a/server/smartcard.c b/server/smartcard.c
index aad22aa..928e27b8 100644
--- a/server/smartcard.c
+++ b/server/smartcard.c
@@ -23,10 +23,10 @@
 #include <vscard_common.h>
 
 #include "reds.h"
-#include "char_device.h"
+#include "char-device.h"
 #include "red_channel.h"
 #include "smartcard.h"
-#include "migration_protocol.h"
+#include "migration-protocol.h"
 
 /*
  * TODO: the code doesn't really support multiple readers.
diff --git a/server/snd_worker.c b/server/snd_worker.c
deleted file mode 100644
index b039939..0000000
--- a/server/snd_worker.c
+++ /dev/null
@@ -1,1625 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <fcntl.h>
-#include <errno.h>
-#include <limits.h>
-#include <sys/socket.h>
-#include <netinet/ip.h>
-#include <netinet/tcp.h>
-
-#include "common/marshaller.h"
-#include "common/generated_server_marshallers.h"
-
-#include "spice.h"
-#include "red_common.h"
-#include "main_channel.h"
-#include "reds.h"
-#include "red_dispatcher.h"
-#include "snd_worker.h"
-#include "common/snd_codec.h"
-#include "demarshallers.h"
-
-#ifndef IOV_MAX
-#define IOV_MAX 1024
-#endif
-
-#define SND_RECEIVE_BUF_SIZE     (16 * 1024 * 2)
-#define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2)
-
-enum PlaybackCommand {
-    SND_PLAYBACK_MIGRATE,
-    SND_PLAYBACK_MODE,
-    SND_PLAYBACK_CTRL,
-    SND_PLAYBACK_PCM,
-    SND_PLAYBACK_VOLUME,
-    SND_PLAYBACK_LATENCY,
-};
-
-enum RecordCommand {
-    SND_RECORD_MIGRATE,
-    SND_RECORD_CTRL,
-    SND_RECORD_VOLUME,
-};
-
-#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE)
-#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
-#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL)
-#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
-#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME)
-#define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
-
-#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE)
-#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL)
-#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
-
-typedef struct SndChannel SndChannel;
-typedef void (*snd_channel_send_messages_proc)(void *in_channel);
-typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
-typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel);
-typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel);
-
-typedef struct SndWorker SndWorker;
-
-struct SndChannel {
-    RedsStream *stream;
-    SndWorker *worker;
-    spice_parse_channel_func_t parser;
-    int refs;
-
-    RedChannelClient *channel_client;
-
-    int active;
-    int client_active;
-    int blocked;
-
-    uint32_t command;
-    uint32_t ack_generation;
-    uint32_t client_ack_generation;
-    uint32_t out_messages;
-    uint32_t ack_messages;
-
-    struct {
-        uint64_t serial;
-        SpiceMarshaller *marshaller;
-        uint32_t size;
-        uint32_t pos;
-    } send_data;
-
-    struct {
-        uint8_t buf[SND_RECEIVE_BUF_SIZE];
-        uint8_t *message_start;
-        uint8_t *now;
-        uint8_t *end;
-    } receive_data;
-
-    snd_channel_send_messages_proc send_messages;
-    snd_channel_handle_message_proc handle_message;
-    snd_channel_on_message_done_proc on_message_done;
-    snd_channel_cleanup_channel_proc cleanup;
-};
-
-typedef struct PlaybackChannel PlaybackChannel;
-
-typedef struct AudioFrame AudioFrame;
-struct AudioFrame {
-    uint32_t time;
-    uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
-    PlaybackChannel *channel;
-    AudioFrame *next;
-};
-
-struct PlaybackChannel {
-    SndChannel base;
-    AudioFrame frames[3];
-    AudioFrame *free_frames;
-    AudioFrame *in_progress;
-    AudioFrame *pending_frame;
-    uint32_t mode;
-    uint32_t latency;
-    SndCodec codec;
-    uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
-};
-
-struct SndWorker {
-    RedChannel *base_channel;
-    SndChannel *connection;
-    SndWorker *next;
-    int active;
-};
-
-typedef struct SpiceVolumeState {
-    uint8_t volume_nchannels;
-    uint16_t *volume;
-    int mute;
-} SpiceVolumeState;
-
-struct SpicePlaybackState {
-    struct SndWorker worker;
-    SpicePlaybackInstance *sin;
-    SpiceVolumeState volume;
-    uint32_t frequency;
-};
-
-struct SpiceRecordState {
-    struct SndWorker worker;
-    SpiceRecordInstance *sin;
-    SpiceVolumeState volume;
-    uint32_t frequency;
-};
-
-typedef struct RecordChannel {
-    SndChannel base;
-    uint32_t samples[RECORD_SAMPLES_SIZE];
-    uint32_t write_pos;
-    uint32_t read_pos;
-    uint32_t mode;
-    uint32_t mode_time;
-    uint32_t start_time;
-    SndCodec codec;
-    uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
-} RecordChannel;
-
-static SndWorker *workers;
-static uint32_t playback_compression = TRUE;
-
-static void snd_receive(void* data);
-
-static SndChannel *snd_channel_get(SndChannel *channel)
-{
-    channel->refs++;
-    return channel;
-}
-
-static SndChannel *snd_channel_put(SndChannel *channel)
-{
-    if (!--channel->refs) {
-        spice_printerr("SndChannel=%p freed", channel);
-        free(channel);
-        return NULL;
-    }
-    return channel;
-}
-
-static void snd_disconnect_channel(SndChannel *channel)
-{
-    SndWorker *worker;
-
-    if (!channel || !channel->stream) {
-        spice_debug("not connected");
-        return;
-    }
-    spice_debug("SndChannel=%p rcc=%p type=%d",
-                 channel, channel->channel_client, channel->channel_client->channel->type);
-    worker = channel->worker;
-    channel->cleanup(channel);
-    red_channel_client_disconnect(worker->connection->channel_client);
-    worker->connection->channel_client = NULL;
-    core->watch_remove(channel->stream->watch);
-    channel->stream->watch = NULL;
-    reds_stream_free(channel->stream);
-    channel->stream = NULL;
-    spice_marshaller_destroy(channel->send_data.marshaller);
-    snd_channel_put(channel);
-    worker->connection = NULL;
-}
-
-static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
-{
-    frame->channel = playback_channel;
-    frame->next = playback_channel->free_frames;
-    playback_channel->free_frames = frame;
-}
-
-static void snd_playback_on_message_done(SndChannel *channel)
-{
-    PlaybackChannel *playback_channel = (PlaybackChannel *)channel;
-    if (playback_channel->in_progress) {
-        snd_playback_free_frame(playback_channel, playback_channel->in_progress);
-        playback_channel->in_progress = NULL;
-        if (playback_channel->pending_frame) {
-            channel->command |= SND_PLAYBACK_PCM_MASK;
-        }
-    }
-}
-
-static void snd_record_on_message_done(SndChannel *channel)
-{
-}
-
-static int snd_send_data(SndChannel *channel)
-{
-    uint32_t n;
-
-    if (!channel) {
-        return FALSE;
-    }
-
-    if (!(n = channel->send_data.size - channel->send_data.pos)) {
-        return TRUE;
-    }
-
-    for (;;) {
-        struct iovec vec[IOV_MAX];
-        int vec_size;
-
-        if (!n) {
-            channel->on_message_done(channel);
-
-            if (channel->blocked) {
-                channel->blocked = FALSE;
-                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ);
-            }
-            break;
-        }
-
-        vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller,
-                                               vec, IOV_MAX, channel->send_data.pos);
-        n = reds_stream_writev(channel->stream, vec, vec_size);
-        if (n == -1) {
-            switch (errno) {
-            case EAGAIN:
-                channel->blocked = TRUE;
-                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ |
-                                        SPICE_WATCH_EVENT_WRITE);
-                return FALSE;
-            case EINTR:
-                break;
-            case EPIPE:
-                snd_disconnect_channel(channel);
-                return FALSE;
-            default:
-                spice_printerr("%s", strerror(errno));
-                snd_disconnect_channel(channel);
-                return FALSE;
-            }
-        } else {
-            channel->send_data.pos += n;
-        }
-        n = channel->send_data.size - channel->send_data.pos;
-    }
-    return TRUE;
-}
-
-static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message)
-{
-    SpiceMsgcRecordPacket *packet;
-    uint32_t write_pos;
-    uint32_t* data;
-    uint32_t len;
-    uint32_t now;
-
-    if (!record_channel) {
-        return FALSE;
-    }
-
-    packet = (SpiceMsgcRecordPacket *)message;
-
-    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
-        data = (uint32_t *)packet->data;
-        size = packet->data_size >> 2;
-        size = MIN(size, RECORD_SAMPLES_SIZE);
-     } else {
-        int decode_size;
-        decode_size = sizeof(record_channel->decode_buf);
-        if (snd_codec_decode(record_channel->codec, packet->data, packet->data_size,
-                    record_channel->decode_buf, &decode_size) != SND_CODEC_OK)
-            return FALSE;
-        data = (uint32_t *) record_channel->decode_buf;
-        size = decode_size >> 2;
-    }
-
-    write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE;
-    record_channel->write_pos += size;
-    len = RECORD_SAMPLES_SIZE - write_pos;
-    now = MIN(len, size);
-    size -= now;
-    memcpy(record_channel->samples + write_pos, data, now << 2);
-
-    if (size) {
-        memcpy(record_channel->samples, data + now, size << 2);
-    }
-
-    if (record_channel->write_pos - record_channel->read_pos > RECORD_SAMPLES_SIZE) {
-        record_channel->read_pos = record_channel->write_pos - RECORD_SAMPLES_SIZE;
-    }
-    return TRUE;
-}
-
-static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
-{
-    if (!channel) {
-        return FALSE;
-    }
-
-    switch (type) {
-    case SPICE_MSGC_DISCONNECTING:
-        break;
-    default:
-        spice_printerr("invalid message type %u", type);
-        return FALSE;
-    }
-    return TRUE;
-}
-
-static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
-{
-    RecordChannel *record_channel = (RecordChannel *)channel;
-
-    if (!channel) {
-        return FALSE;
-    }
-    switch (type) {
-    case SPICE_MSGC_RECORD_DATA:
-        return snd_record_handle_write((RecordChannel *)channel, size, message);
-    case SPICE_MSGC_RECORD_MODE: {
-        SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
-        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
-        record_channel->mode_time = mode->time;
-        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
-            if (snd_codec_is_capable(mode->mode, st->frequency)) {
-                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) {
-                    record_channel->mode = mode->mode;
-                } else {
-                    spice_printerr("create decoder failed");
-                    return FALSE;
-                }
-            }
-            else {
-                spice_printerr("unsupported mode %d", record_channel->mode);
-                return FALSE;
-            }
-        }
-        else
-            record_channel->mode = mode->mode;
-        break;
-    }
-
-    case SPICE_MSGC_RECORD_START_MARK: {
-        SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
-        record_channel->start_time = mark->time;
-        break;
-    }
-    case SPICE_MSGC_DISCONNECTING:
-        break;
-    default:
-        spice_printerr("invalid message type %u", type);
-        return FALSE;
-    }
-    return TRUE;
-}
-
-static void snd_receive(void* data)
-{
-    SndChannel *channel = (SndChannel*)data;
-    SpiceDataHeaderOpaque *header;
-
-    if (!channel) {
-        return;
-    }
-
-    header = &channel->channel_client->incoming.header;
-
-    for (;;) {
-        ssize_t n;
-        n = channel->receive_data.end - channel->receive_data.now;
-        spice_warn_if(n <= 0);
-        n = reds_stream_read(channel->stream, channel->receive_data.now, n);
-        if (n <= 0) {
-            if (n == 0) {
-                snd_disconnect_channel(channel);
-                return;
-            }
-            spice_assert(n == -1);
-            switch (errno) {
-            case EAGAIN:
-                return;
-            case EINTR:
-                break;
-            case EPIPE:
-                snd_disconnect_channel(channel);
-                return;
-            default:
-                spice_printerr("%s", strerror(errno));
-                snd_disconnect_channel(channel);
-                return;
-            }
-        } else {
-            channel->receive_data.now += n;
-            for (;;) {
-                uint8_t *msg_start = channel->receive_data.message_start;
-                uint8_t *data = msg_start + header->header_size;
-                size_t parsed_size;
-                uint8_t *parsed;
-                message_destructor_t parsed_free;
-
-                header->data = msg_start;
-                n = channel->receive_data.now - msg_start;
-
-                if (n < header->header_size ||
-                    n < header->header_size + header->get_msg_size(header)) {
-                    break;
-                }
-                parsed = channel->parser((void *)data, data + header->get_msg_size(header),
-                                         header->get_msg_type(header),
-                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
-                if (parsed == NULL) {
-                    spice_printerr("failed to parse message type %d", header->get_msg_type(header));
-                    snd_disconnect_channel(channel);
-                    return;
-                }
-                if (!channel->handle_message(channel, parsed_size,
-                                             header->get_msg_type(header), parsed)) {
-                    free(parsed);
-                    snd_disconnect_channel(channel);
-                    return;
-                }
-                parsed_free(parsed);
-                channel->receive_data.message_start = msg_start + header->header_size +
-                                                     header->get_msg_size(header);
-            }
-            if (channel->receive_data.now == channel->receive_data.message_start) {
-                channel->receive_data.now = channel->receive_data.buf;
-                channel->receive_data.message_start = channel->receive_data.buf;
-            } else if (channel->receive_data.now == channel->receive_data.end) {
-                memcpy(channel->receive_data.buf, channel->receive_data.message_start, n);
-                channel->receive_data.now = channel->receive_data.buf + n;
-                channel->receive_data.message_start = channel->receive_data.buf;
-            }
-        }
-    }
-}
-
-static void snd_event(int fd, int event, void *data)
-{
-    SndChannel *channel = data;
-
-    if (event & SPICE_WATCH_EVENT_READ) {
-        snd_receive(channel);
-    }
-    if (event & SPICE_WATCH_EVENT_WRITE) {
-        channel->send_messages(channel);
-    }
-}
-
-static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb)
-{
-    SpiceDataHeaderOpaque *header;
-
-    if (!channel) {
-        return FALSE;
-    }
-
-    header = &channel->channel_client->send_data.header;
-    spice_marshaller_reset(channel->send_data.marshaller);
-    header->data = spice_marshaller_reserve_space(channel->send_data.marshaller,
-                                                  header->header_size);
-    spice_marshaller_set_base(channel->send_data.marshaller,
-                              header->header_size);
-    channel->send_data.pos = 0;
-    header->set_msg_size(header, 0);
-    header->set_msg_type(header, verb);
-    channel->send_data.serial++;
-    if (!channel->channel_client->is_mini_header) {
-        header->set_msg_serial(header, channel->send_data.serial);
-        header->set_msg_sub_list(header, 0);
-    }
-
-    return TRUE;
-}
-
-static int snd_begin_send_message(SndChannel *channel)
-{
-    SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header;
-
-    spice_marshaller_flush(channel->send_data.marshaller);
-    channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller);
-    header->set_msg_size(header, channel->send_data.size - header->header_size);
-    return snd_send_data(channel);
-}
-
-static int snd_channel_send_migrate(SndChannel *channel)
-{
-    SpiceMsgMigrate migrate;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_MIGRATE)) {
-        return FALSE;
-    }
-    spice_debug(NULL);
-    migrate.flags = 0;
-    spice_marshall_msg_migrate(channel->send_data.marshaller, &migrate);
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_playback_send_migrate(PlaybackChannel *channel)
-{
-    return snd_channel_send_migrate(&channel->base);
-}
-
-static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg)
-{
-    SpiceMsgAudioVolume *vol;
-    uint8_t c;
-
-    vol = alloca(sizeof (SpiceMsgAudioVolume) +
-                 st->volume_nchannels * sizeof (uint16_t));
-    if (!snd_reset_send_data(channel, msg)) {
-        return FALSE;
-    }
-    vol->nchannels = st->volume_nchannels;
-    for (c = 0; c < st->volume_nchannels; ++c) {
-        vol->volume[c] = st->volume[c];
-    }
-    spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol);
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_playback_send_volume(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = &playback_channel->base;
-    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
-
-    if (!red_channel_client_test_remote_cap(channel->channel_client,
-                                            SPICE_PLAYBACK_CAP_VOLUME)) {
-        return TRUE;
-    }
-
-    return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
-}
-
-static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg)
-{
-    SpiceMsgAudioMute mute;
-
-    if (!snd_reset_send_data(channel, msg)) {
-        return FALSE;
-    }
-    mute.mute = st->mute;
-    spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute);
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_playback_send_mute(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = &playback_channel->base;
-    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
-
-    if (!red_channel_client_test_remote_cap(channel->channel_client,
-                                            SPICE_PLAYBACK_CAP_VOLUME)) {
-        return TRUE;
-    }
-
-    return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
-}
-
-static int snd_playback_send_latency(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = &playback_channel->base;
-    SpiceMsgPlaybackLatency latency_msg;
-
-    spice_debug("latency %u", playback_channel->latency);
-    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_LATENCY)) {
-        return FALSE;
-    }
-    latency_msg.latency_ms = playback_channel->latency;
-    spice_marshall_msg_playback_latency(channel->send_data.marshaller, &latency_msg);
-
-    return snd_begin_send_message(channel);
-}
-static int snd_playback_send_start(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = (SndChannel *)playback_channel;
-    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
-    SpiceMsgPlaybackStart start;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) {
-        return FALSE;
-    }
-
-    start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
-    start.frequency = st->frequency;
-    spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
-    start.format = SPICE_AUDIO_FMT_S16;
-    start.time = reds_get_mm_time();
-    spice_marshall_msg_playback_start(channel->send_data.marshaller, &start);
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_playback_send_stop(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = (SndChannel *)playback_channel;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_STOP)) {
-        return FALSE;
-    }
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_playback_send_ctl(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = (SndChannel *)playback_channel;
-
-    if ((channel->client_active = channel->active)) {
-        return snd_playback_send_start(playback_channel);
-    } else {
-        return snd_playback_send_stop(playback_channel);
-    }
-}
-
-static int snd_record_send_start(RecordChannel *record_channel)
-{
-    SndChannel *channel = (SndChannel *)record_channel;
-    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
-    SpiceMsgRecordStart start;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) {
-        return FALSE;
-    }
-
-    start.channels = SPICE_INTERFACE_RECORD_CHAN;
-    start.frequency = st->frequency;
-    spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
-    start.format = SPICE_AUDIO_FMT_S16;
-    spice_marshall_msg_record_start(channel->send_data.marshaller, &start);
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_record_send_stop(RecordChannel *record_channel)
-{
-    SndChannel *channel = (SndChannel *)record_channel;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_STOP)) {
-        return FALSE;
-    }
-
-    return snd_begin_send_message(channel);
-}
-
-static int snd_record_send_ctl(RecordChannel *record_channel)
-{
-    SndChannel *channel = (SndChannel *)record_channel;
-
-    if ((channel->client_active = channel->active)) {
-        return snd_record_send_start(record_channel);
-    } else {
-        return snd_record_send_stop(record_channel);
-    }
-}
-
-static int snd_record_send_volume(RecordChannel *record_channel)
-{
-    SndChannel *channel = &record_channel->base;
-    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
-
-    if (!red_channel_client_test_remote_cap(channel->channel_client,
-                                            SPICE_RECORD_CAP_VOLUME)) {
-        return TRUE;
-    }
-
-    return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
-}
-
-static int snd_record_send_mute(RecordChannel *record_channel)
-{
-    SndChannel *channel = &record_channel->base;
-    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
-
-    if (!red_channel_client_test_remote_cap(channel->channel_client,
-                                            SPICE_RECORD_CAP_VOLUME)) {
-        return TRUE;
-    }
-
-    return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
-}
-
-static int snd_record_send_migrate(RecordChannel *record_channel)
-{
-    /* No need for migration data: if recording has started before migration,
-     * the client receives RECORD_STOP from the src before the migration completion
-     * notification (when the vm is stopped).
-     * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
-    return snd_channel_send_migrate(&record_channel->base);
-}
-
-static int snd_playback_send_write(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = (SndChannel *)playback_channel;
-    AudioFrame *frame;
-    SpiceMsgPlaybackPacket msg;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_DATA)) {
-        return FALSE;
-    }
-
-    frame = playback_channel->in_progress;
-    msg.time = frame->time;
-
-    spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg);
-
-    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
-        spice_marshaller_add_ref(channel->send_data.marshaller,
-                                 (uint8_t *)frame->samples,
-                                 snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]));
-    }
-    else {
-        int n = sizeof(playback_channel->encode_buf);
-        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples,
-                                    snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]),
-                                    playback_channel->encode_buf, &n) != SND_CODEC_OK) {
-            spice_printerr("encode failed");
-            snd_disconnect_channel(channel);
-            return FALSE;
-        }
-        spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n);
-    }
-
-    return snd_begin_send_message(channel);
-}
-
-static int playback_send_mode(PlaybackChannel *playback_channel)
-{
-    SndChannel *channel = (SndChannel *)playback_channel;
-    SpiceMsgPlaybackMode mode;
-
-    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_MODE)) {
-        return FALSE;
-    }
-    mode.time = reds_get_mm_time();
-    mode.mode = playback_channel->mode;
-    spice_marshall_msg_playback_mode(channel->send_data.marshaller, &mode);
-
-    return snd_begin_send_message(channel);
-}
-
-static void snd_playback_send(void* data)
-{
-    PlaybackChannel *playback_channel = (PlaybackChannel*)data;
-    SndChannel *channel = (SndChannel*)playback_channel;
-
-    if (!playback_channel || !snd_send_data(data)) {
-        return;
-    }
-
-    while (channel->command) {
-        if (channel->command & SND_PLAYBACK_MODE_MASK) {
-            if (!playback_send_mode(playback_channel)) {
-                return;
-            }
-            channel->command &= ~SND_PLAYBACK_MODE_MASK;
-        }
-        if (channel->command & SND_PLAYBACK_PCM_MASK) {
-            spice_assert(!playback_channel->in_progress && playback_channel->pending_frame);
-            playback_channel->in_progress = playback_channel->pending_frame;
-            playback_channel->pending_frame = NULL;
-            channel->command &= ~SND_PLAYBACK_PCM_MASK;
-            if (!snd_playback_send_write(playback_channel)) {
-                spice_printerr("snd_send_playback_write failed");
-                return;
-            }
-        }
-        if (channel->command & SND_PLAYBACK_CTRL_MASK) {
-            if (!snd_playback_send_ctl(playback_channel)) {
-                return;
-            }
-            channel->command &= ~SND_PLAYBACK_CTRL_MASK;
-        }
-        if (channel->command & SND_PLAYBACK_VOLUME_MASK) {
-            if (!snd_playback_send_volume(playback_channel) ||
-                !snd_playback_send_mute(playback_channel)) {
-                return;
-            }
-            channel->command &= ~SND_PLAYBACK_VOLUME_MASK;
-        }
-        if (channel->command & SND_PLAYBACK_MIGRATE_MASK) {
-            if (!snd_playback_send_migrate(playback_channel)) {
-                return;
-            }
-            channel->command &= ~SND_PLAYBACK_MIGRATE_MASK;
-        }
-        if (channel->command & SND_PLAYBACK_LATENCY_MASK) {
-            if (!snd_playback_send_latency(playback_channel)) {
-                return;
-            }
-            channel->command &= ~SND_PLAYBACK_LATENCY_MASK;
-        }
-    }
-}
-
-static void snd_record_send(void* data)
-{
-    RecordChannel *record_channel = (RecordChannel*)data;
-    SndChannel *channel = (SndChannel*)record_channel;
-
-    if (!record_channel || !snd_send_data(data)) {
-        return;
-    }
-
-    while (channel->command) {
-        if (channel->command & SND_RECORD_CTRL_MASK) {
-            if (!snd_record_send_ctl(record_channel)) {
-                return;
-            }
-            channel->command &= ~SND_RECORD_CTRL_MASK;
-        }
-        if (channel->command & SND_RECORD_VOLUME_MASK) {
-            if (!snd_record_send_volume(record_channel) ||
-                !snd_record_send_mute(record_channel)) {
-                return;
-            }
-            channel->command &= ~SND_RECORD_VOLUME_MASK;
-        }
-        if (channel->command & SND_RECORD_MIGRATE_MASK) {
-            if (!snd_record_send_migrate(record_channel)) {
-                return;
-            }
-            channel->command &= ~SND_RECORD_MIGRATE_MASK;
-        }
-    }
-}
-
-static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
-                                 RedClient *client,
-                                 RedsStream *stream,
-                                 int migrate,
-                                 snd_channel_send_messages_proc send_messages,
-                                 snd_channel_handle_message_proc handle_message,
-                                 snd_channel_on_message_done_proc on_message_done,
-                                 snd_channel_cleanup_channel_proc cleanup,
-                                 uint32_t *common_caps, int num_common_caps,
-                                 uint32_t *caps, int num_caps)
-{
-    SndChannel *channel;
-    int delay_val;
-    int flags;
-#ifdef SO_PRIORITY
-    int priority;
-#endif
-    int tos;
-    MainChannelClient *mcc = red_client_get_main(client);
-
-    spice_assert(stream);
-    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
-        spice_printerr("accept failed, %s", strerror(errno));
-        goto error1;
-    }
-
-#ifdef SO_PRIORITY
-    priority = 6;
-    if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
-                   sizeof(priority)) == -1) {
-        if (errno != ENOTSUP) {
-            spice_printerr("setsockopt failed, %s", strerror(errno));
-        }
-    }
-#endif
-
-    tos = IPTOS_LOWDELAY;
-    if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
-        if (errno != ENOTSUP) {
-            spice_printerr("setsockopt failed, %s", strerror(errno));
-        }
-    }
-
-    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
-    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
-        if (errno != ENOTSUP) {
-            spice_printerr("setsockopt failed, %s", strerror(errno));
-        }
-    }
-
-    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
-        spice_printerr("accept failed, %s", strerror(errno));
-        goto error1;
-    }
-
-    spice_assert(size >= sizeof(*channel));
-    channel = spice_malloc0(size);
-    channel->refs = 1;
-    channel->parser = spice_get_client_channel_parser(channel_id, NULL);
-    channel->stream = stream;
-    channel->worker = worker;
-    channel->receive_data.message_start = channel->receive_data.buf;
-    channel->receive_data.now = channel->receive_data.buf;
-    channel->receive_data.end = channel->receive_data.buf + sizeof(channel->receive_data.buf);
-    channel->send_data.marshaller = spice_marshaller_new();
-
-    stream->watch = core->watch_add(stream->socket, SPICE_WATCH_EVENT_READ,
-                                  snd_event, channel);
-    if (stream->watch == NULL) {
-        spice_printerr("watch_add failed, %s", strerror(errno));
-        goto error2;
-    }
-
-    channel->send_messages = send_messages;
-    channel->handle_message = handle_message;
-    channel->on_message_done = on_message_done;
-    channel->cleanup = cleanup;
-
-    channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
-                                                              worker->base_channel,
-                                                              client,
-                                                              num_common_caps, common_caps,
-                                                              num_caps, caps);
-    if (!channel->channel_client) {
-        goto error2;
-    }
-    return channel;
-
-error2:
-    free(channel);
-
-error1:
-    reds_stream_free(stream);
-    return NULL;
-}
-
-static void snd_disconnect_channel_client(RedChannelClient *rcc)
-{
-    SndWorker *worker;
-
-    spice_assert(rcc->channel);
-    spice_assert(rcc->channel->data);
-    worker = (SndWorker *)rcc->channel->data;
-
-    spice_debug("channel-type=%d", rcc->channel->type);
-    if (worker->connection) {
-        spice_assert(worker->connection->channel_client == rcc);
-        snd_disconnect_channel(worker->connection);
-    }
-}
-
-static void snd_set_command(SndChannel *channel, uint32_t command)
-{
-    if (!channel) {
-        return;
-    }
-    channel->command |= command;
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
-                                                  uint8_t nchannels,
-                                                  uint16_t *volume)
-{
-    SpiceVolumeState *st = &sin->st->volume;
-    SndChannel *channel = sin->st->worker.connection;
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    st->volume_nchannels = nchannels;
-    free(st->volume);
-    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
-
-    if (!channel || nchannels == 0)
-        return;
-
-    snd_playback_send_volume(playback_channel);
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
-{
-    SpiceVolumeState *st = &sin->st->volume;
-    SndChannel *channel = sin->st->worker.connection;
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    st->mute = mute;
-
-    if (!channel)
-        return;
-
-    snd_playback_send_mute(playback_channel);
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    sin->st->worker.active = 1;
-    if (!channel)
-        return;
-    spice_assert(!playback_channel->base.active);
-    reds_disable_mm_time();
-    playback_channel->base.active = TRUE;
-    if (!playback_channel->base.client_active) {
-        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
-        snd_playback_send(&playback_channel->base);
-    } else {
-        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
-    }
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    sin->st->worker.active = 0;
-    if (!channel)
-        return;
-    spice_assert(playback_channel->base.active);
-    reds_enable_mm_time();
-    playback_channel->base.active = FALSE;
-    if (playback_channel->base.client_active) {
-        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
-        snd_playback_send(&playback_channel->base);
-    } else {
-        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
-        playback_channel->base.command &= ~SND_PLAYBACK_PCM_MASK;
-
-        if (playback_channel->pending_frame) {
-            spice_assert(!playback_channel->in_progress);
-            snd_playback_free_frame(playback_channel,
-                                    playback_channel->pending_frame);
-            playback_channel->pending_frame = NULL;
-        }
-    }
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *sin,
-                                                         uint32_t **frame, uint32_t *num_samples)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    if (!channel || !playback_channel->free_frames) {
-        *frame = NULL;
-        *num_samples = 0;
-        return;
-    }
-    spice_assert(playback_channel->base.active);
-    snd_channel_get(channel);
-
-    *frame = playback_channel->free_frames->samples;
-    playback_channel->free_frames = playback_channel->free_frames->next;
-    *num_samples = snd_codec_frame_size(playback_channel->codec);
-}
-
-SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples)
-{
-    PlaybackChannel *playback_channel;
-    AudioFrame *frame;
-
-    frame = SPICE_CONTAINEROF(samples, AudioFrame, samples);
-    playback_channel = frame->channel;
-    spice_assert(playback_channel);
-    if (!snd_channel_put(&playback_channel->base) ||
-        sin->st->worker.connection != &playback_channel->base) {
-        /* lost last reference, channel has been destroyed previously */
-        spice_info("audio samples belong to a disconnected channel");
-        return;
-    }
-    spice_assert(playback_channel->base.active);
-
-    if (playback_channel->pending_frame) {
-        snd_playback_free_frame(playback_channel, playback_channel->pending_frame);
-    }
-    frame->time = reds_get_mm_time();
-    playback_channel->pending_frame = frame;
-    snd_set_command(&playback_channel->base, SND_PLAYBACK_PCM_MASK);
-    snd_playback_send(&playback_channel->base);
-}
-
-void snd_set_playback_latency(RedClient *client, uint32_t latency)
-{
-    SndWorker *now = workers;
-
-    for (; now; now = now->next) {
-        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection &&
-            now->connection->channel_client->client == client) {
-
-            if (red_channel_client_test_remote_cap(now->connection->channel_client,
-                SPICE_PLAYBACK_CAP_LATENCY)) {
-                PlaybackChannel* playback = (PlaybackChannel*)now->connection;
-
-                playback->latency = latency;
-                snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK);
-                snd_playback_send(now->connection);
-            } else {
-                spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY");
-            }
-        }
-    }
-}
-
-static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus)
-{
-    if (! playback_compression)
-        return SPICE_AUDIO_DATA_MODE_RAW;
-
-    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
-        return SPICE_AUDIO_DATA_MODE_OPUS;
-
-    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency))
-        return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
-
-    return SPICE_AUDIO_DATA_MODE_RAW;
-}
-
-static void on_new_playback_channel(SndWorker *worker)
-{
-    PlaybackChannel *playback_channel =
-        SPICE_CONTAINEROF(worker->connection, PlaybackChannel, base);
-    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
-
-    spice_assert(playback_channel);
-
-    snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_MODE_MASK);
-    if (playback_channel->base.active) {
-        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK);
-    }
-    if (st->volume.volume_nchannels) {
-        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK);
-    }
-    if (playback_channel->base.active) {
-        reds_disable_mm_time();
-    }
-}
-
-static void snd_playback_cleanup(SndChannel *channel)
-{
-    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-
-    if (playback_channel->base.active) {
-        reds_enable_mm_time();
-    }
-
-    snd_codec_destroy(&playback_channel->codec);
-}
-
-static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
-                                  int migration, int num_common_caps, uint32_t *common_caps,
-                                  int num_caps, uint32_t *caps)
-{
-    SndWorker *worker = channel->data;
-    PlaybackChannel *playback_channel;
-    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
-
-    snd_disconnect_channel(worker->connection);
-
-    if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
-                                                              sizeof(*playback_channel),
-                                                              SPICE_CHANNEL_PLAYBACK,
-                                                              client,
-                                                              stream,
-                                                              migration,
-                                                              snd_playback_send,
-                                                              snd_playback_handle_message,
-                                                              snd_playback_on_message_done,
-                                                              snd_playback_cleanup,
-                                                              common_caps, num_common_caps,
-                                                              caps, num_caps))) {
-        return;
-    }
-    worker->connection = &playback_channel->base;
-    snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
-    snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
-    snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
-
-    int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
-                                          SPICE_PLAYBACK_CAP_CELT_0_5_1);
-    int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
-                                          SPICE_PLAYBACK_CAP_OPUS);
-    int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus);
-    playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
-    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
-        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) {
-            playback_channel->mode = desired_mode;
-        } else {
-            spice_printerr("create encoder failed");
-        }
-    }
-
-    if (!red_client_during_migrate_at_target(client)) {
-        on_new_playback_channel(worker);
-    }
-
-    if (worker->active) {
-        spice_server_playback_start(st->sin);
-    }
-    snd_playback_send(worker->connection);
-}
-
-static void snd_record_migrate_channel_client(RedChannelClient *rcc)
-{
-    SndWorker *worker;
-
-    spice_debug(NULL);
-    spice_assert(rcc->channel);
-    spice_assert(rcc->channel->data);
-    worker = (SndWorker *)rcc->channel->data;
-
-    if (worker->connection) {
-        spice_assert(worker->connection->channel_client == rcc);
-        snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK);
-        snd_record_send(worker->connection);
-    }
-}
-
-SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
-                                                uint8_t nchannels,
-                                                uint16_t *volume)
-{
-    SpiceVolumeState *st = &sin->st->volume;
-    SndChannel *channel = sin->st->worker.connection;
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-
-    st->volume_nchannels = nchannels;
-    free(st->volume);
-    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
-
-    if (!channel || nchannels == 0)
-        return;
-
-    snd_record_send_volume(record_channel);
-}
-
-SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
-{
-    SpiceVolumeState *st = &sin->st->volume;
-    SndChannel *channel = sin->st->worker.connection;
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-
-    st->mute = mute;
-
-    if (!channel)
-        return;
-
-    snd_record_send_mute(record_channel);
-}
-
-SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-
-    sin->st->worker.active = 1;
-    if (!channel)
-        return;
-    spice_assert(!record_channel->base.active);
-    record_channel->base.active = TRUE;
-    record_channel->read_pos = record_channel->write_pos = 0;   //todo: improve by
-                                                                //stream generation
-    if (!record_channel->base.client_active) {
-        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
-        snd_record_send(&record_channel->base);
-    } else {
-        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
-    }
-}
-
-SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-
-    sin->st->worker.active = 0;
-    if (!channel)
-        return;
-    spice_assert(record_channel->base.active);
-    record_channel->base.active = FALSE;
-    if (record_channel->base.client_active) {
-        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
-        snd_record_send(&record_channel->base);
-    } else {
-        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
-    }
-}
-
-SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin,
-                                                            uint32_t *samples, uint32_t bufsize)
-{
-    SndChannel *channel = sin->st->worker.connection;
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-    uint32_t read_pos;
-    uint32_t now;
-    uint32_t len;
-
-    if (!channel)
-        return 0;
-    spice_assert(record_channel->base.active);
-
-    if (record_channel->write_pos < RECORD_SAMPLES_SIZE / 2) {
-        return 0;
-    }
-
-    len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
-
-    if (len < bufsize) {
-        SndWorker *worker = record_channel->base.worker;
-        snd_receive(record_channel);
-        if (!worker->connection) {
-            return 0;
-        }
-        len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
-    }
-
-    read_pos = record_channel->read_pos % RECORD_SAMPLES_SIZE;
-    record_channel->read_pos += len;
-    now = MIN(len, RECORD_SAMPLES_SIZE - read_pos);
-    memcpy(samples, &record_channel->samples[read_pos], now * 4);
-    if (now < len) {
-        memcpy(samples + now, record_channel->samples, (len - now) * 4);
-    }
-    return len;
-}
-
-SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin)
-{
-    int client_can_opus = TRUE;
-    if (sin && sin->st->worker.connection) {
-        SndChannel *channel = sin->st->worker.connection;
-        PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
-        client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
-                                          SPICE_PLAYBACK_CAP_OPUS);
-    }
-
-    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
-        return SND_CODEC_OPUS_PLAYBACK_FREQ;
-
-    return SND_CODEC_CELT_PLAYBACK_FREQ;
-}
-
-SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency)
-{
-    RedChannel *channel = sin->st->worker.base_channel;
-    sin->st->frequency = frequency;
-    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
-        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
-}
-
-SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin)
-{
-    int client_can_opus = TRUE;
-    if (sin && sin->st->worker.connection) {
-        SndChannel *channel = sin->st->worker.connection;
-        RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-        client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client,
-                                          SPICE_RECORD_CAP_OPUS);
-    }
-
-    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
-        return SND_CODEC_OPUS_PLAYBACK_FREQ;
-
-    return SND_CODEC_CELT_PLAYBACK_FREQ;
-}
-
-SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency)
-{
-    RedChannel *channel = sin->st->worker.base_channel;
-    sin->st->frequency = frequency;
-    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
-        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
-}
-
-static void on_new_record_channel(SndWorker *worker)
-{
-    RecordChannel *record_channel = (RecordChannel *)worker->connection;
-    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
-
-    spice_assert(record_channel);
-
-    if (st->volume.volume_nchannels) {
-        snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK);
-    }
-    if (record_channel->base.active) {
-        snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK);
-    }
-}
-
-static void snd_record_cleanup(SndChannel *channel)
-{
-    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
-    snd_codec_destroy(&record_channel->codec);
-}
-
-static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
-                                int migration, int num_common_caps, uint32_t *common_caps,
-                                int num_caps, uint32_t *caps)
-{
-    SndWorker *worker = channel->data;
-    RecordChannel *record_channel;
-    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
-
-    snd_disconnect_channel(worker->connection);
-
-    if (!(record_channel = (RecordChannel *)__new_channel(worker,
-                                                          sizeof(*record_channel),
-                                                          SPICE_CHANNEL_RECORD,
-                                                          client,
-                                                          stream,
-                                                          migration,
-                                                          snd_record_send,
-                                                          snd_record_handle_message,
-                                                          snd_record_on_message_done,
-                                                          snd_record_cleanup,
-                                                          common_caps, num_common_caps,
-                                                          caps, num_caps))) {
-        return;
-    }
-
-    record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
-
-    worker->connection = &record_channel->base;
-
-    on_new_record_channel(worker);
-    if (worker->active) {
-        spice_server_record_start(st->sin);
-    }
-    snd_record_send(worker->connection);
-}
-
-static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
-{
-    SndWorker *worker;
-
-    spice_assert(rcc->channel);
-    spice_assert(rcc->channel->data);
-    worker = (SndWorker *)rcc->channel->data;
-    spice_debug(NULL);
-
-    if (worker->connection) {
-        spice_assert(worker->connection->channel_client == rcc);
-        snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK);
-        snd_playback_send(worker->connection);
-    }
-}
-
-static void add_worker(SndWorker *worker)
-{
-    worker->next = workers;
-    workers = worker;
-}
-
-static void remove_worker(SndWorker *worker)
-{
-    SndWorker **now = &workers;
-    while (*now) {
-        if (*now == worker) {
-            *now = worker->next;
-            return;
-        }
-        now = &(*now)->next;
-    }
-    spice_printerr("not found");
-}
-
-void snd_attach_playback(SpicePlaybackInstance *sin)
-{
-    SndWorker *playback_worker;
-    RedChannel *channel;
-    ClientCbs client_cbs = { NULL, };
-
-    sin->st = spice_new0(SpicePlaybackState, 1);
-    sin->st->sin = sin;
-    playback_worker = &sin->st->worker;
-    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
-
-    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
-    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
-
-    channel->data = playback_worker;
-    client_cbs.connect = snd_set_playback_peer;
-    client_cbs.disconnect = snd_disconnect_channel_client;
-    client_cbs.migrate = snd_playback_migrate_channel_client;
-    red_channel_register_client_cbs(channel, &client_cbs);
-    red_channel_set_data(channel, playback_worker);
-
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
-        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
-
-    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
-
-    playback_worker->base_channel = channel;
-    add_worker(playback_worker);
-    reds_register_channel(playback_worker->base_channel);
-}
-
-void snd_attach_record(SpiceRecordInstance *sin)
-{
-    SndWorker *record_worker;
-    RedChannel *channel;
-    ClientCbs client_cbs = { NULL, };
-
-    sin->st = spice_new0(SpiceRecordState, 1);
-    sin->st->sin = sin;
-    record_worker = &sin->st->worker;
-    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
-
-    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
-    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
-
-    channel->data = record_worker;
-    client_cbs.connect = snd_set_record_peer;
-    client_cbs.disconnect = snd_disconnect_channel_client;
-    client_cbs.migrate = snd_record_migrate_channel_client;
-    red_channel_register_client_cbs(channel, &client_cbs);
-    red_channel_set_data(channel, record_worker);
-    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
-        red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
-    red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
-
-    record_worker->base_channel = channel;
-    add_worker(record_worker);
-    reds_register_channel(record_worker->base_channel);
-}
-
-static void snd_detach_common(SndWorker *worker)
-{
-    if (!worker) {
-        return;
-    }
-    remove_worker(worker);
-    snd_disconnect_channel(worker->connection);
-    reds_unregister_channel(worker->base_channel);
-    red_channel_destroy(worker->base_channel);
-}
-
-static void spice_playback_state_free(SpicePlaybackState *st)
-{
-    free(st->volume.volume);
-    free(st);
-}
-
-void snd_detach_playback(SpicePlaybackInstance *sin)
-{
-    snd_detach_common(&sin->st->worker);
-    spice_playback_state_free(sin->st);
-}
-
-static void spice_record_state_free(SpiceRecordState *st)
-{
-    free(st->volume.volume);
-    free(st);
-}
-
-void snd_detach_record(SpiceRecordInstance *sin)
-{
-    snd_detach_common(&sin->st->worker);
-    spice_record_state_free(sin->st);
-}
-
-void snd_set_playback_compression(int on)
-{
-    SndWorker *now = workers;
-
-    playback_compression = !!on;
-
-    for (; now; now = now->next) {
-        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
-            PlaybackChannel* playback = (PlaybackChannel*)now->connection;
-            SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker);
-            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
-                                    SPICE_PLAYBACK_CAP_CELT_0_5_1);
-            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
-                                    SPICE_PLAYBACK_CAP_OPUS);
-            int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt);
-            if (playback->mode != desired_mode) {
-                playback->mode = desired_mode;
-                snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
-            }
-        }
-    }
-}
diff --git a/server/snd_worker.h b/server/snd_worker.h
deleted file mode 100644
index 7cc4db5..0000000
--- a/server/snd_worker.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-
-#ifndef _H_SND_WORKER
-#define _H_SND_WORKER
-
-#include "spice.h"
-
-void snd_attach_playback(SpicePlaybackInstance *sin);
-void snd_detach_playback(SpicePlaybackInstance *sin);
-
-void snd_attach_record(SpiceRecordInstance *sin);
-void snd_detach_record(SpiceRecordInstance *sin);
-
-void snd_set_playback_compression(int on);
-
-void snd_set_playback_latency(RedClient *client, uint32_t latency);
-
-#endif
diff --git a/server/sound.c b/server/sound.c
new file mode 100644
index 0000000..4b1ec7a
--- /dev/null
+++ b/server/sound.c
@@ -0,0 +1,1625 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <sys/socket.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "common/marshaller.h"
+#include "common/generated_server_marshallers.h"
+#include "common/snd_codec.h"
+
+#include "spice.h"
+#include "red_common.h"
+#include "main-channel.h"
+#include "reds.h"
+#include "red_dispatcher.h"
+#include "sound.h"
+#include "demarshallers.h"
+
+#ifndef IOV_MAX
+#define IOV_MAX 1024
+#endif
+
+#define SND_RECEIVE_BUF_SIZE     (16 * 1024 * 2)
+#define RECORD_SAMPLES_SIZE (SND_RECEIVE_BUF_SIZE >> 2)
+
+enum PlaybackCommand {
+    SND_PLAYBACK_MIGRATE,
+    SND_PLAYBACK_MODE,
+    SND_PLAYBACK_CTRL,
+    SND_PLAYBACK_PCM,
+    SND_PLAYBACK_VOLUME,
+    SND_PLAYBACK_LATENCY,
+};
+
+enum RecordCommand {
+    SND_RECORD_MIGRATE,
+    SND_RECORD_CTRL,
+    SND_RECORD_VOLUME,
+};
+
+#define SND_PLAYBACK_MIGRATE_MASK (1 << SND_PLAYBACK_MIGRATE)
+#define SND_PLAYBACK_MODE_MASK (1 << SND_PLAYBACK_MODE)
+#define SND_PLAYBACK_CTRL_MASK (1 << SND_PLAYBACK_CTRL)
+#define SND_PLAYBACK_PCM_MASK (1 << SND_PLAYBACK_PCM)
+#define SND_PLAYBACK_VOLUME_MASK (1 << SND_PLAYBACK_VOLUME)
+#define SND_PLAYBACK_LATENCY_MASK ( 1 << SND_PLAYBACK_LATENCY)
+
+#define SND_RECORD_MIGRATE_MASK (1 << SND_RECORD_MIGRATE)
+#define SND_RECORD_CTRL_MASK (1 << SND_RECORD_CTRL)
+#define SND_RECORD_VOLUME_MASK (1 << SND_RECORD_VOLUME)
+
+typedef struct SndChannel SndChannel;
+typedef void (*snd_channel_send_messages_proc)(void *in_channel);
+typedef int (*snd_channel_handle_message_proc)(SndChannel *channel, size_t size, uint32_t type, void *message);
+typedef void (*snd_channel_on_message_done_proc)(SndChannel *channel);
+typedef void (*snd_channel_cleanup_channel_proc)(SndChannel *channel);
+
+typedef struct SndWorker SndWorker;
+
+struct SndChannel {
+    RedsStream *stream;
+    SndWorker *worker;
+    spice_parse_channel_func_t parser;
+    int refs;
+
+    RedChannelClient *channel_client;
+
+    int active;
+    int client_active;
+    int blocked;
+
+    uint32_t command;
+    uint32_t ack_generation;
+    uint32_t client_ack_generation;
+    uint32_t out_messages;
+    uint32_t ack_messages;
+
+    struct {
+        uint64_t serial;
+        SpiceMarshaller *marshaller;
+        uint32_t size;
+        uint32_t pos;
+    } send_data;
+
+    struct {
+        uint8_t buf[SND_RECEIVE_BUF_SIZE];
+        uint8_t *message_start;
+        uint8_t *now;
+        uint8_t *end;
+    } receive_data;
+
+    snd_channel_send_messages_proc send_messages;
+    snd_channel_handle_message_proc handle_message;
+    snd_channel_on_message_done_proc on_message_done;
+    snd_channel_cleanup_channel_proc cleanup;
+};
+
+typedef struct PlaybackChannel PlaybackChannel;
+
+typedef struct AudioFrame AudioFrame;
+struct AudioFrame {
+    uint32_t time;
+    uint32_t samples[SND_CODEC_MAX_FRAME_SIZE];
+    PlaybackChannel *channel;
+    AudioFrame *next;
+};
+
+struct PlaybackChannel {
+    SndChannel base;
+    AudioFrame frames[3];
+    AudioFrame *free_frames;
+    AudioFrame *in_progress;
+    AudioFrame *pending_frame;
+    uint32_t mode;
+    uint32_t latency;
+    SndCodec codec;
+    uint8_t  encode_buf[SND_CODEC_MAX_COMPRESSED_BYTES];
+};
+
+struct SndWorker {
+    RedChannel *base_channel;
+    SndChannel *connection;
+    SndWorker *next;
+    int active;
+};
+
+typedef struct SpiceVolumeState {
+    uint8_t volume_nchannels;
+    uint16_t *volume;
+    int mute;
+} SpiceVolumeState;
+
+struct SpicePlaybackState {
+    struct SndWorker worker;
+    SpicePlaybackInstance *sin;
+    SpiceVolumeState volume;
+    uint32_t frequency;
+};
+
+struct SpiceRecordState {
+    struct SndWorker worker;
+    SpiceRecordInstance *sin;
+    SpiceVolumeState volume;
+    uint32_t frequency;
+};
+
+typedef struct RecordChannel {
+    SndChannel base;
+    uint32_t samples[RECORD_SAMPLES_SIZE];
+    uint32_t write_pos;
+    uint32_t read_pos;
+    uint32_t mode;
+    uint32_t mode_time;
+    uint32_t start_time;
+    SndCodec codec;
+    uint8_t  decode_buf[SND_CODEC_MAX_FRAME_BYTES];
+} RecordChannel;
+
+static SndWorker *workers;
+static uint32_t playback_compression = TRUE;
+
+static void snd_receive(void* data);
+
+static SndChannel *snd_channel_get(SndChannel *channel)
+{
+    channel->refs++;
+    return channel;
+}
+
+static SndChannel *snd_channel_put(SndChannel *channel)
+{
+    if (!--channel->refs) {
+        spice_printerr("SndChannel=%p freed", channel);
+        free(channel);
+        return NULL;
+    }
+    return channel;
+}
+
+static void snd_disconnect_channel(SndChannel *channel)
+{
+    SndWorker *worker;
+
+    if (!channel || !channel->stream) {
+        spice_debug("not connected");
+        return;
+    }
+    spice_debug("SndChannel=%p rcc=%p type=%d",
+                 channel, channel->channel_client, channel->channel_client->channel->type);
+    worker = channel->worker;
+    channel->cleanup(channel);
+    red_channel_client_disconnect(worker->connection->channel_client);
+    worker->connection->channel_client = NULL;
+    core->watch_remove(channel->stream->watch);
+    channel->stream->watch = NULL;
+    reds_stream_free(channel->stream);
+    channel->stream = NULL;
+    spice_marshaller_destroy(channel->send_data.marshaller);
+    snd_channel_put(channel);
+    worker->connection = NULL;
+}
+
+static void snd_playback_free_frame(PlaybackChannel *playback_channel, AudioFrame *frame)
+{
+    frame->channel = playback_channel;
+    frame->next = playback_channel->free_frames;
+    playback_channel->free_frames = frame;
+}
+
+static void snd_playback_on_message_done(SndChannel *channel)
+{
+    PlaybackChannel *playback_channel = (PlaybackChannel *)channel;
+    if (playback_channel->in_progress) {
+        snd_playback_free_frame(playback_channel, playback_channel->in_progress);
+        playback_channel->in_progress = NULL;
+        if (playback_channel->pending_frame) {
+            channel->command |= SND_PLAYBACK_PCM_MASK;
+        }
+    }
+}
+
+static void snd_record_on_message_done(SndChannel *channel)
+{
+}
+
+static int snd_send_data(SndChannel *channel)
+{
+    uint32_t n;
+
+    if (!channel) {
+        return FALSE;
+    }
+
+    if (!(n = channel->send_data.size - channel->send_data.pos)) {
+        return TRUE;
+    }
+
+    for (;;) {
+        struct iovec vec[IOV_MAX];
+        int vec_size;
+
+        if (!n) {
+            channel->on_message_done(channel);
+
+            if (channel->blocked) {
+                channel->blocked = FALSE;
+                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ);
+            }
+            break;
+        }
+
+        vec_size = spice_marshaller_fill_iovec(channel->send_data.marshaller,
+                                               vec, IOV_MAX, channel->send_data.pos);
+        n = reds_stream_writev(channel->stream, vec, vec_size);
+        if (n == -1) {
+            switch (errno) {
+            case EAGAIN:
+                channel->blocked = TRUE;
+                core->watch_update_mask(channel->stream->watch, SPICE_WATCH_EVENT_READ |
+                                        SPICE_WATCH_EVENT_WRITE);
+                return FALSE;
+            case EINTR:
+                break;
+            case EPIPE:
+                snd_disconnect_channel(channel);
+                return FALSE;
+            default:
+                spice_printerr("%s", strerror(errno));
+                snd_disconnect_channel(channel);
+                return FALSE;
+            }
+        } else {
+            channel->send_data.pos += n;
+        }
+        n = channel->send_data.size - channel->send_data.pos;
+    }
+    return TRUE;
+}
+
+static int snd_record_handle_write(RecordChannel *record_channel, size_t size, void *message)
+{
+    SpiceMsgcRecordPacket *packet;
+    uint32_t write_pos;
+    uint32_t* data;
+    uint32_t len;
+    uint32_t now;
+
+    if (!record_channel) {
+        return FALSE;
+    }
+
+    packet = (SpiceMsgcRecordPacket *)message;
+
+    if (record_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
+        data = (uint32_t *)packet->data;
+        size = packet->data_size >> 2;
+        size = MIN(size, RECORD_SAMPLES_SIZE);
+     } else {
+        int decode_size;
+        decode_size = sizeof(record_channel->decode_buf);
+        if (snd_codec_decode(record_channel->codec, packet->data, packet->data_size,
+                    record_channel->decode_buf, &decode_size) != SND_CODEC_OK)
+            return FALSE;
+        data = (uint32_t *) record_channel->decode_buf;
+        size = decode_size >> 2;
+    }
+
+    write_pos = record_channel->write_pos % RECORD_SAMPLES_SIZE;
+    record_channel->write_pos += size;
+    len = RECORD_SAMPLES_SIZE - write_pos;
+    now = MIN(len, size);
+    size -= now;
+    memcpy(record_channel->samples + write_pos, data, now << 2);
+
+    if (size) {
+        memcpy(record_channel->samples, data + now, size << 2);
+    }
+
+    if (record_channel->write_pos - record_channel->read_pos > RECORD_SAMPLES_SIZE) {
+        record_channel->read_pos = record_channel->write_pos - RECORD_SAMPLES_SIZE;
+    }
+    return TRUE;
+}
+
+static int snd_playback_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
+{
+    if (!channel) {
+        return FALSE;
+    }
+
+    switch (type) {
+    case SPICE_MSGC_DISCONNECTING:
+        break;
+    default:
+        spice_printerr("invalid message type %u", type);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static int snd_record_handle_message(SndChannel *channel, size_t size, uint32_t type, void *message)
+{
+    RecordChannel *record_channel = (RecordChannel *)channel;
+
+    if (!channel) {
+        return FALSE;
+    }
+    switch (type) {
+    case SPICE_MSGC_RECORD_DATA:
+        return snd_record_handle_write((RecordChannel *)channel, size, message);
+    case SPICE_MSGC_RECORD_MODE: {
+        SpiceMsgcRecordMode *mode = (SpiceMsgcRecordMode *)message;
+        SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+        record_channel->mode_time = mode->time;
+        if (mode->mode != SPICE_AUDIO_DATA_MODE_RAW) {
+            if (snd_codec_is_capable(mode->mode, st->frequency)) {
+                if (snd_codec_create(&record_channel->codec, mode->mode, st->frequency, SND_CODEC_DECODE) == SND_CODEC_OK) {
+                    record_channel->mode = mode->mode;
+                } else {
+                    spice_printerr("create decoder failed");
+                    return FALSE;
+                }
+            }
+            else {
+                spice_printerr("unsupported mode %d", record_channel->mode);
+                return FALSE;
+            }
+        }
+        else
+            record_channel->mode = mode->mode;
+        break;
+    }
+
+    case SPICE_MSGC_RECORD_START_MARK: {
+        SpiceMsgcRecordStartMark *mark = (SpiceMsgcRecordStartMark *)message;
+        record_channel->start_time = mark->time;
+        break;
+    }
+    case SPICE_MSGC_DISCONNECTING:
+        break;
+    default:
+        spice_printerr("invalid message type %u", type);
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static void snd_receive(void* data)
+{
+    SndChannel *channel = (SndChannel*)data;
+    SpiceDataHeaderOpaque *header;
+
+    if (!channel) {
+        return;
+    }
+
+    header = &channel->channel_client->incoming.header;
+
+    for (;;) {
+        ssize_t n;
+        n = channel->receive_data.end - channel->receive_data.now;
+        spice_warn_if(n <= 0);
+        n = reds_stream_read(channel->stream, channel->receive_data.now, n);
+        if (n <= 0) {
+            if (n == 0) {
+                snd_disconnect_channel(channel);
+                return;
+            }
+            spice_assert(n == -1);
+            switch (errno) {
+            case EAGAIN:
+                return;
+            case EINTR:
+                break;
+            case EPIPE:
+                snd_disconnect_channel(channel);
+                return;
+            default:
+                spice_printerr("%s", strerror(errno));
+                snd_disconnect_channel(channel);
+                return;
+            }
+        } else {
+            channel->receive_data.now += n;
+            for (;;) {
+                uint8_t *msg_start = channel->receive_data.message_start;
+                uint8_t *data = msg_start + header->header_size;
+                size_t parsed_size;
+                uint8_t *parsed;
+                message_destructor_t parsed_free;
+
+                header->data = msg_start;
+                n = channel->receive_data.now - msg_start;
+
+                if (n < header->header_size ||
+                    n < header->header_size + header->get_msg_size(header)) {
+                    break;
+                }
+                parsed = channel->parser((void *)data, data + header->get_msg_size(header),
+                                         header->get_msg_type(header),
+                                         SPICE_VERSION_MINOR, &parsed_size, &parsed_free);
+                if (parsed == NULL) {
+                    spice_printerr("failed to parse message type %d", header->get_msg_type(header));
+                    snd_disconnect_channel(channel);
+                    return;
+                }
+                if (!channel->handle_message(channel, parsed_size,
+                                             header->get_msg_type(header), parsed)) {
+                    free(parsed);
+                    snd_disconnect_channel(channel);
+                    return;
+                }
+                parsed_free(parsed);
+                channel->receive_data.message_start = msg_start + header->header_size +
+                                                     header->get_msg_size(header);
+            }
+            if (channel->receive_data.now == channel->receive_data.message_start) {
+                channel->receive_data.now = channel->receive_data.buf;
+                channel->receive_data.message_start = channel->receive_data.buf;
+            } else if (channel->receive_data.now == channel->receive_data.end) {
+                memcpy(channel->receive_data.buf, channel->receive_data.message_start, n);
+                channel->receive_data.now = channel->receive_data.buf + n;
+                channel->receive_data.message_start = channel->receive_data.buf;
+            }
+        }
+    }
+}
+
+static void snd_event(int fd, int event, void *data)
+{
+    SndChannel *channel = data;
+
+    if (event & SPICE_WATCH_EVENT_READ) {
+        snd_receive(channel);
+    }
+    if (event & SPICE_WATCH_EVENT_WRITE) {
+        channel->send_messages(channel);
+    }
+}
+
+static inline int snd_reset_send_data(SndChannel *channel, uint16_t verb)
+{
+    SpiceDataHeaderOpaque *header;
+
+    if (!channel) {
+        return FALSE;
+    }
+
+    header = &channel->channel_client->send_data.header;
+    spice_marshaller_reset(channel->send_data.marshaller);
+    header->data = spice_marshaller_reserve_space(channel->send_data.marshaller,
+                                                  header->header_size);
+    spice_marshaller_set_base(channel->send_data.marshaller,
+                              header->header_size);
+    channel->send_data.pos = 0;
+    header->set_msg_size(header, 0);
+    header->set_msg_type(header, verb);
+    channel->send_data.serial++;
+    if (!channel->channel_client->is_mini_header) {
+        header->set_msg_serial(header, channel->send_data.serial);
+        header->set_msg_sub_list(header, 0);
+    }
+
+    return TRUE;
+}
+
+static int snd_begin_send_message(SndChannel *channel)
+{
+    SpiceDataHeaderOpaque *header = &channel->channel_client->send_data.header;
+
+    spice_marshaller_flush(channel->send_data.marshaller);
+    channel->send_data.size = spice_marshaller_get_total_size(channel->send_data.marshaller);
+    header->set_msg_size(header, channel->send_data.size - header->header_size);
+    return snd_send_data(channel);
+}
+
+static int snd_channel_send_migrate(SndChannel *channel)
+{
+    SpiceMsgMigrate migrate;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_MIGRATE)) {
+        return FALSE;
+    }
+    spice_debug(NULL);
+    migrate.flags = 0;
+    spice_marshall_msg_migrate(channel->send_data.marshaller, &migrate);
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_migrate(PlaybackChannel *channel)
+{
+    return snd_channel_send_migrate(&channel->base);
+}
+
+static int snd_send_volume(SndChannel *channel, SpiceVolumeState *st, int msg)
+{
+    SpiceMsgAudioVolume *vol;
+    uint8_t c;
+
+    vol = alloca(sizeof (SpiceMsgAudioVolume) +
+                 st->volume_nchannels * sizeof (uint16_t));
+    if (!snd_reset_send_data(channel, msg)) {
+        return FALSE;
+    }
+    vol->nchannels = st->volume_nchannels;
+    for (c = 0; c < st->volume_nchannels; ++c) {
+        vol->volume[c] = st->volume[c];
+    }
+    spice_marshall_SpiceMsgAudioVolume(channel->send_data.marshaller, vol);
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_volume(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = &playback_channel->base;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
+
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_PLAYBACK_CAP_VOLUME)) {
+        return TRUE;
+    }
+
+    return snd_send_volume(channel, &st->volume, SPICE_MSG_PLAYBACK_VOLUME);
+}
+
+static int snd_send_mute(SndChannel *channel, SpiceVolumeState *st, int msg)
+{
+    SpiceMsgAudioMute mute;
+
+    if (!snd_reset_send_data(channel, msg)) {
+        return FALSE;
+    }
+    mute.mute = st->mute;
+    spice_marshall_SpiceMsgAudioMute(channel->send_data.marshaller, &mute);
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_mute(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = &playback_channel->base;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
+
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_PLAYBACK_CAP_VOLUME)) {
+        return TRUE;
+    }
+
+    return snd_send_mute(channel, &st->volume, SPICE_MSG_PLAYBACK_MUTE);
+}
+
+static int snd_playback_send_latency(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = &playback_channel->base;
+    SpiceMsgPlaybackLatency latency_msg;
+
+    spice_debug("latency %u", playback_channel->latency);
+    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_LATENCY)) {
+        return FALSE;
+    }
+    latency_msg.latency_ms = playback_channel->latency;
+    spice_marshall_msg_playback_latency(channel->send_data.marshaller, &latency_msg);
+
+    return snd_begin_send_message(channel);
+}
+static int snd_playback_send_start(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = (SndChannel *)playback_channel;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(channel->worker, SpicePlaybackState, worker);
+    SpiceMsgPlaybackStart start;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_START)) {
+        return FALSE;
+    }
+
+    start.channels = SPICE_INTERFACE_PLAYBACK_CHAN;
+    start.frequency = st->frequency;
+    spice_assert(SPICE_INTERFACE_PLAYBACK_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
+    start.format = SPICE_AUDIO_FMT_S16;
+    start.time = reds_get_mm_time();
+    spice_marshall_msg_playback_start(channel->send_data.marshaller, &start);
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_stop(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = (SndChannel *)playback_channel;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_STOP)) {
+        return FALSE;
+    }
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_playback_send_ctl(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = (SndChannel *)playback_channel;
+
+    if ((channel->client_active = channel->active)) {
+        return snd_playback_send_start(playback_channel);
+    } else {
+        return snd_playback_send_stop(playback_channel);
+    }
+}
+
+static int snd_record_send_start(RecordChannel *record_channel)
+{
+    SndChannel *channel = (SndChannel *)record_channel;
+    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+    SpiceMsgRecordStart start;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_START)) {
+        return FALSE;
+    }
+
+    start.channels = SPICE_INTERFACE_RECORD_CHAN;
+    start.frequency = st->frequency;
+    spice_assert(SPICE_INTERFACE_RECORD_FMT == SPICE_INTERFACE_AUDIO_FMT_S16);
+    start.format = SPICE_AUDIO_FMT_S16;
+    spice_marshall_msg_record_start(channel->send_data.marshaller, &start);
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_record_send_stop(RecordChannel *record_channel)
+{
+    SndChannel *channel = (SndChannel *)record_channel;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_RECORD_STOP)) {
+        return FALSE;
+    }
+
+    return snd_begin_send_message(channel);
+}
+
+static int snd_record_send_ctl(RecordChannel *record_channel)
+{
+    SndChannel *channel = (SndChannel *)record_channel;
+
+    if ((channel->client_active = channel->active)) {
+        return snd_record_send_start(record_channel);
+    } else {
+        return snd_record_send_stop(record_channel);
+    }
+}
+
+static int snd_record_send_volume(RecordChannel *record_channel)
+{
+    SndChannel *channel = &record_channel->base;
+    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_RECORD_CAP_VOLUME)) {
+        return TRUE;
+    }
+
+    return snd_send_volume(channel, &st->volume, SPICE_MSG_RECORD_VOLUME);
+}
+
+static int snd_record_send_mute(RecordChannel *record_channel)
+{
+    SndChannel *channel = &record_channel->base;
+    SpiceRecordState *st = SPICE_CONTAINEROF(channel->worker, SpiceRecordState, worker);
+
+    if (!red_channel_client_test_remote_cap(channel->channel_client,
+                                            SPICE_RECORD_CAP_VOLUME)) {
+        return TRUE;
+    }
+
+    return snd_send_mute(channel, &st->volume, SPICE_MSG_RECORD_MUTE);
+}
+
+static int snd_record_send_migrate(RecordChannel *record_channel)
+{
+    /* No need for migration data: if recording has started before migration,
+     * the client receives RECORD_STOP from the src before the migration completion
+     * notification (when the vm is stopped).
+     * Afterwards, when the vm starts on the dest, the client receives RECORD_START. */
+    return snd_channel_send_migrate(&record_channel->base);
+}
+
+static int snd_playback_send_write(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = (SndChannel *)playback_channel;
+    AudioFrame *frame;
+    SpiceMsgPlaybackPacket msg;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_DATA)) {
+        return FALSE;
+    }
+
+    frame = playback_channel->in_progress;
+    msg.time = frame->time;
+
+    spice_marshall_msg_playback_data(channel->send_data.marshaller, &msg);
+
+    if (playback_channel->mode == SPICE_AUDIO_DATA_MODE_RAW) {
+        spice_marshaller_add_ref(channel->send_data.marshaller,
+                                 (uint8_t *)frame->samples,
+                                 snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]));
+    }
+    else {
+        int n = sizeof(playback_channel->encode_buf);
+        if (snd_codec_encode(playback_channel->codec, (uint8_t *) frame->samples,
+                                    snd_codec_frame_size(playback_channel->codec) * sizeof(frame->samples[0]),
+                                    playback_channel->encode_buf, &n) != SND_CODEC_OK) {
+            spice_printerr("encode failed");
+            snd_disconnect_channel(channel);
+            return FALSE;
+        }
+        spice_marshaller_add_ref(channel->send_data.marshaller, playback_channel->encode_buf, n);
+    }
+
+    return snd_begin_send_message(channel);
+}
+
+static int playback_send_mode(PlaybackChannel *playback_channel)
+{
+    SndChannel *channel = (SndChannel *)playback_channel;
+    SpiceMsgPlaybackMode mode;
+
+    if (!snd_reset_send_data(channel, SPICE_MSG_PLAYBACK_MODE)) {
+        return FALSE;
+    }
+    mode.time = reds_get_mm_time();
+    mode.mode = playback_channel->mode;
+    spice_marshall_msg_playback_mode(channel->send_data.marshaller, &mode);
+
+    return snd_begin_send_message(channel);
+}
+
+static void snd_playback_send(void* data)
+{
+    PlaybackChannel *playback_channel = (PlaybackChannel*)data;
+    SndChannel *channel = (SndChannel*)playback_channel;
+
+    if (!playback_channel || !snd_send_data(data)) {
+        return;
+    }
+
+    while (channel->command) {
+        if (channel->command & SND_PLAYBACK_MODE_MASK) {
+            if (!playback_send_mode(playback_channel)) {
+                return;
+            }
+            channel->command &= ~SND_PLAYBACK_MODE_MASK;
+        }
+        if (channel->command & SND_PLAYBACK_PCM_MASK) {
+            spice_assert(!playback_channel->in_progress && playback_channel->pending_frame);
+            playback_channel->in_progress = playback_channel->pending_frame;
+            playback_channel->pending_frame = NULL;
+            channel->command &= ~SND_PLAYBACK_PCM_MASK;
+            if (!snd_playback_send_write(playback_channel)) {
+                spice_printerr("snd_send_playback_write failed");
+                return;
+            }
+        }
+        if (channel->command & SND_PLAYBACK_CTRL_MASK) {
+            if (!snd_playback_send_ctl(playback_channel)) {
+                return;
+            }
+            channel->command &= ~SND_PLAYBACK_CTRL_MASK;
+        }
+        if (channel->command & SND_PLAYBACK_VOLUME_MASK) {
+            if (!snd_playback_send_volume(playback_channel) ||
+                !snd_playback_send_mute(playback_channel)) {
+                return;
+            }
+            channel->command &= ~SND_PLAYBACK_VOLUME_MASK;
+        }
+        if (channel->command & SND_PLAYBACK_MIGRATE_MASK) {
+            if (!snd_playback_send_migrate(playback_channel)) {
+                return;
+            }
+            channel->command &= ~SND_PLAYBACK_MIGRATE_MASK;
+        }
+        if (channel->command & SND_PLAYBACK_LATENCY_MASK) {
+            if (!snd_playback_send_latency(playback_channel)) {
+                return;
+            }
+            channel->command &= ~SND_PLAYBACK_LATENCY_MASK;
+        }
+    }
+}
+
+static void snd_record_send(void* data)
+{
+    RecordChannel *record_channel = (RecordChannel*)data;
+    SndChannel *channel = (SndChannel*)record_channel;
+
+    if (!record_channel || !snd_send_data(data)) {
+        return;
+    }
+
+    while (channel->command) {
+        if (channel->command & SND_RECORD_CTRL_MASK) {
+            if (!snd_record_send_ctl(record_channel)) {
+                return;
+            }
+            channel->command &= ~SND_RECORD_CTRL_MASK;
+        }
+        if (channel->command & SND_RECORD_VOLUME_MASK) {
+            if (!snd_record_send_volume(record_channel) ||
+                !snd_record_send_mute(record_channel)) {
+                return;
+            }
+            channel->command &= ~SND_RECORD_VOLUME_MASK;
+        }
+        if (channel->command & SND_RECORD_MIGRATE_MASK) {
+            if (!snd_record_send_migrate(record_channel)) {
+                return;
+            }
+            channel->command &= ~SND_RECORD_MIGRATE_MASK;
+        }
+    }
+}
+
+static SndChannel *__new_channel(SndWorker *worker, int size, uint32_t channel_id,
+                                 RedClient *client,
+                                 RedsStream *stream,
+                                 int migrate,
+                                 snd_channel_send_messages_proc send_messages,
+                                 snd_channel_handle_message_proc handle_message,
+                                 snd_channel_on_message_done_proc on_message_done,
+                                 snd_channel_cleanup_channel_proc cleanup,
+                                 uint32_t *common_caps, int num_common_caps,
+                                 uint32_t *caps, int num_caps)
+{
+    SndChannel *channel;
+    int delay_val;
+    int flags;
+#ifdef SO_PRIORITY
+    int priority;
+#endif
+    int tos;
+    MainChannelClient *mcc = red_client_get_main(client);
+
+    spice_assert(stream);
+    if ((flags = fcntl(stream->socket, F_GETFL)) == -1) {
+        spice_printerr("accept failed, %s", strerror(errno));
+        goto error1;
+    }
+
+#ifdef SO_PRIORITY
+    priority = 6;
+    if (setsockopt(stream->socket, SOL_SOCKET, SO_PRIORITY, (void*)&priority,
+                   sizeof(priority)) == -1) {
+        if (errno != ENOTSUP) {
+            spice_printerr("setsockopt failed, %s", strerror(errno));
+        }
+    }
+#endif
+
+    tos = IPTOS_LOWDELAY;
+    if (setsockopt(stream->socket, IPPROTO_IP, IP_TOS, (void*)&tos, sizeof(tos)) == -1) {
+        if (errno != ENOTSUP) {
+            spice_printerr("setsockopt failed, %s", strerror(errno));
+        }
+    }
+
+    delay_val = main_channel_client_is_low_bandwidth(mcc) ? 0 : 1;
+    if (setsockopt(stream->socket, IPPROTO_TCP, TCP_NODELAY, &delay_val, sizeof(delay_val)) == -1) {
+        if (errno != ENOTSUP) {
+            spice_printerr("setsockopt failed, %s", strerror(errno));
+        }
+    }
+
+    if (fcntl(stream->socket, F_SETFL, flags | O_NONBLOCK) == -1) {
+        spice_printerr("accept failed, %s", strerror(errno));
+        goto error1;
+    }
+
+    spice_assert(size >= sizeof(*channel));
+    channel = spice_malloc0(size);
+    channel->refs = 1;
+    channel->parser = spice_get_client_channel_parser(channel_id, NULL);
+    channel->stream = stream;
+    channel->worker = worker;
+    channel->receive_data.message_start = channel->receive_data.buf;
+    channel->receive_data.now = channel->receive_data.buf;
+    channel->receive_data.end = channel->receive_data.buf + sizeof(channel->receive_data.buf);
+    channel->send_data.marshaller = spice_marshaller_new();
+
+    stream->watch = core->watch_add(stream->socket, SPICE_WATCH_EVENT_READ,
+                                  snd_event, channel);
+    if (stream->watch == NULL) {
+        spice_printerr("watch_add failed, %s", strerror(errno));
+        goto error2;
+    }
+
+    channel->send_messages = send_messages;
+    channel->handle_message = handle_message;
+    channel->on_message_done = on_message_done;
+    channel->cleanup = cleanup;
+
+    channel->channel_client = red_channel_client_create_dummy(sizeof(RedChannelClient),
+                                                              worker->base_channel,
+                                                              client,
+                                                              num_common_caps, common_caps,
+                                                              num_caps, caps);
+    if (!channel->channel_client) {
+        goto error2;
+    }
+    return channel;
+
+error2:
+    free(channel);
+
+error1:
+    reds_stream_free(stream);
+    return NULL;
+}
+
+static void snd_disconnect_channel_client(RedChannelClient *rcc)
+{
+    SndWorker *worker;
+
+    spice_assert(rcc->channel);
+    spice_assert(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
+
+    spice_debug("channel-type=%d", rcc->channel->type);
+    if (worker->connection) {
+        spice_assert(worker->connection->channel_client == rcc);
+        snd_disconnect_channel(worker->connection);
+    }
+}
+
+static void snd_set_command(SndChannel *channel, uint32_t command)
+{
+    if (!channel) {
+        return;
+    }
+    channel->command |= command;
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_set_volume(SpicePlaybackInstance *sin,
+                                                  uint8_t nchannels,
+                                                  uint16_t *volume)
+{
+    SpiceVolumeState *st = &sin->st->volume;
+    SndChannel *channel = sin->st->worker.connection;
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    st->volume_nchannels = nchannels;
+    free(st->volume);
+    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
+
+    if (!channel || nchannels == 0)
+        return;
+
+    snd_playback_send_volume(playback_channel);
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_set_mute(SpicePlaybackInstance *sin, uint8_t mute)
+{
+    SpiceVolumeState *st = &sin->st->volume;
+    SndChannel *channel = sin->st->worker.connection;
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    st->mute = mute;
+
+    if (!channel)
+        return;
+
+    snd_playback_send_mute(playback_channel);
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_start(SpicePlaybackInstance *sin)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    sin->st->worker.active = 1;
+    if (!channel)
+        return;
+    spice_assert(!playback_channel->base.active);
+    reds_disable_mm_time();
+    playback_channel->base.active = TRUE;
+    if (!playback_channel->base.client_active) {
+        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
+        snd_playback_send(&playback_channel->base);
+    } else {
+        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
+    }
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_stop(SpicePlaybackInstance *sin)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    sin->st->worker.active = 0;
+    if (!channel)
+        return;
+    spice_assert(playback_channel->base.active);
+    reds_enable_mm_time();
+    playback_channel->base.active = FALSE;
+    if (playback_channel->base.client_active) {
+        snd_set_command(&playback_channel->base, SND_PLAYBACK_CTRL_MASK);
+        snd_playback_send(&playback_channel->base);
+    } else {
+        playback_channel->base.command &= ~SND_PLAYBACK_CTRL_MASK;
+        playback_channel->base.command &= ~SND_PLAYBACK_PCM_MASK;
+
+        if (playback_channel->pending_frame) {
+            spice_assert(!playback_channel->in_progress);
+            snd_playback_free_frame(playback_channel,
+                                    playback_channel->pending_frame);
+            playback_channel->pending_frame = NULL;
+        }
+    }
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_get_buffer(SpicePlaybackInstance *sin,
+                                                         uint32_t **frame, uint32_t *num_samples)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    if (!channel || !playback_channel->free_frames) {
+        *frame = NULL;
+        *num_samples = 0;
+        return;
+    }
+    spice_assert(playback_channel->base.active);
+    snd_channel_get(channel);
+
+    *frame = playback_channel->free_frames->samples;
+    playback_channel->free_frames = playback_channel->free_frames->next;
+    *num_samples = snd_codec_frame_size(playback_channel->codec);
+}
+
+SPICE_GNUC_VISIBLE void spice_server_playback_put_samples(SpicePlaybackInstance *sin, uint32_t *samples)
+{
+    PlaybackChannel *playback_channel;
+    AudioFrame *frame;
+
+    frame = SPICE_CONTAINEROF(samples, AudioFrame, samples);
+    playback_channel = frame->channel;
+    spice_assert(playback_channel);
+    if (!snd_channel_put(&playback_channel->base) ||
+        sin->st->worker.connection != &playback_channel->base) {
+        /* lost last reference, channel has been destroyed previously */
+        spice_info("audio samples belong to a disconnected channel");
+        return;
+    }
+    spice_assert(playback_channel->base.active);
+
+    if (playback_channel->pending_frame) {
+        snd_playback_free_frame(playback_channel, playback_channel->pending_frame);
+    }
+    frame->time = reds_get_mm_time();
+    playback_channel->pending_frame = frame;
+    snd_set_command(&playback_channel->base, SND_PLAYBACK_PCM_MASK);
+    snd_playback_send(&playback_channel->base);
+}
+
+void snd_set_playback_latency(RedClient *client, uint32_t latency)
+{
+    SndWorker *now = workers;
+
+    for (; now; now = now->next) {
+        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection &&
+            now->connection->channel_client->client == client) {
+
+            if (red_channel_client_test_remote_cap(now->connection->channel_client,
+                SPICE_PLAYBACK_CAP_LATENCY)) {
+                PlaybackChannel* playback = (PlaybackChannel*)now->connection;
+
+                playback->latency = latency;
+                snd_set_command(now->connection, SND_PLAYBACK_LATENCY_MASK);
+                snd_playback_send(now->connection);
+            } else {
+                spice_debug("client doesn't not support SPICE_PLAYBACK_CAP_LATENCY");
+            }
+        }
+    }
+}
+
+static int snd_desired_audio_mode(int frequency, int client_can_celt, int client_can_opus)
+{
+    if (! playback_compression)
+        return SPICE_AUDIO_DATA_MODE_RAW;
+
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        return SPICE_AUDIO_DATA_MODE_OPUS;
+
+    if (client_can_celt && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, frequency))
+        return SPICE_AUDIO_DATA_MODE_CELT_0_5_1;
+
+    return SPICE_AUDIO_DATA_MODE_RAW;
+}
+
+static void on_new_playback_channel(SndWorker *worker)
+{
+    PlaybackChannel *playback_channel =
+        SPICE_CONTAINEROF(worker->connection, PlaybackChannel, base);
+    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
+
+    spice_assert(playback_channel);
+
+    snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_MODE_MASK);
+    if (playback_channel->base.active) {
+        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_CTRL_MASK);
+    }
+    if (st->volume.volume_nchannels) {
+        snd_set_command((SndChannel *)playback_channel, SND_PLAYBACK_VOLUME_MASK);
+    }
+    if (playback_channel->base.active) {
+        reds_disable_mm_time();
+    }
+}
+
+static void snd_playback_cleanup(SndChannel *channel)
+{
+    PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+
+    if (playback_channel->base.active) {
+        reds_enable_mm_time();
+    }
+
+    snd_codec_destroy(&playback_channel->codec);
+}
+
+static void snd_set_playback_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
+                                  int migration, int num_common_caps, uint32_t *common_caps,
+                                  int num_caps, uint32_t *caps)
+{
+    SndWorker *worker = channel->data;
+    PlaybackChannel *playback_channel;
+    SpicePlaybackState *st = SPICE_CONTAINEROF(worker, SpicePlaybackState, worker);
+
+    snd_disconnect_channel(worker->connection);
+
+    if (!(playback_channel = (PlaybackChannel *)__new_channel(worker,
+                                                              sizeof(*playback_channel),
+                                                              SPICE_CHANNEL_PLAYBACK,
+                                                              client,
+                                                              stream,
+                                                              migration,
+                                                              snd_playback_send,
+                                                              snd_playback_handle_message,
+                                                              snd_playback_on_message_done,
+                                                              snd_playback_cleanup,
+                                                              common_caps, num_common_caps,
+                                                              caps, num_caps))) {
+        return;
+    }
+    worker->connection = &playback_channel->base;
+    snd_playback_free_frame(playback_channel, &playback_channel->frames[0]);
+    snd_playback_free_frame(playback_channel, &playback_channel->frames[1]);
+    snd_playback_free_frame(playback_channel, &playback_channel->frames[2]);
+
+    int client_can_celt = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_CELT_0_5_1);
+    int client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_OPUS);
+    int desired_mode = snd_desired_audio_mode(st->frequency, client_can_celt, client_can_opus);
+    playback_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
+    if (desired_mode != SPICE_AUDIO_DATA_MODE_RAW) {
+        if (snd_codec_create(&playback_channel->codec, desired_mode, st->frequency, SND_CODEC_ENCODE) == SND_CODEC_OK) {
+            playback_channel->mode = desired_mode;
+        } else {
+            spice_printerr("create encoder failed");
+        }
+    }
+
+    if (!red_client_during_migrate_at_target(client)) {
+        on_new_playback_channel(worker);
+    }
+
+    if (worker->active) {
+        spice_server_playback_start(st->sin);
+    }
+    snd_playback_send(worker->connection);
+}
+
+static void snd_record_migrate_channel_client(RedChannelClient *rcc)
+{
+    SndWorker *worker;
+
+    spice_debug(NULL);
+    spice_assert(rcc->channel);
+    spice_assert(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
+
+    if (worker->connection) {
+        spice_assert(worker->connection->channel_client == rcc);
+        snd_set_command(worker->connection, SND_RECORD_MIGRATE_MASK);
+        snd_record_send(worker->connection);
+    }
+}
+
+SPICE_GNUC_VISIBLE void spice_server_record_set_volume(SpiceRecordInstance *sin,
+                                                uint8_t nchannels,
+                                                uint16_t *volume)
+{
+    SpiceVolumeState *st = &sin->st->volume;
+    SndChannel *channel = sin->st->worker.connection;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+    st->volume_nchannels = nchannels;
+    free(st->volume);
+    st->volume = spice_memdup(volume, sizeof(uint16_t) * nchannels);
+
+    if (!channel || nchannels == 0)
+        return;
+
+    snd_record_send_volume(record_channel);
+}
+
+SPICE_GNUC_VISIBLE void spice_server_record_set_mute(SpiceRecordInstance *sin, uint8_t mute)
+{
+    SpiceVolumeState *st = &sin->st->volume;
+    SndChannel *channel = sin->st->worker.connection;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+    st->mute = mute;
+
+    if (!channel)
+        return;
+
+    snd_record_send_mute(record_channel);
+}
+
+SPICE_GNUC_VISIBLE void spice_server_record_start(SpiceRecordInstance *sin)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+    sin->st->worker.active = 1;
+    if (!channel)
+        return;
+    spice_assert(!record_channel->base.active);
+    record_channel->base.active = TRUE;
+    record_channel->read_pos = record_channel->write_pos = 0;   //todo: improve by
+                                                                //stream generation
+    if (!record_channel->base.client_active) {
+        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
+        snd_record_send(&record_channel->base);
+    } else {
+        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
+    }
+}
+
+SPICE_GNUC_VISIBLE void spice_server_record_stop(SpiceRecordInstance *sin)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+
+    sin->st->worker.active = 0;
+    if (!channel)
+        return;
+    spice_assert(record_channel->base.active);
+    record_channel->base.active = FALSE;
+    if (record_channel->base.client_active) {
+        snd_set_command(&record_channel->base, SND_RECORD_CTRL_MASK);
+        snd_record_send(&record_channel->base);
+    } else {
+        record_channel->base.command &= ~SND_RECORD_CTRL_MASK;
+    }
+}
+
+SPICE_GNUC_VISIBLE uint32_t spice_server_record_get_samples(SpiceRecordInstance *sin,
+                                                            uint32_t *samples, uint32_t bufsize)
+{
+    SndChannel *channel = sin->st->worker.connection;
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+    uint32_t read_pos;
+    uint32_t now;
+    uint32_t len;
+
+    if (!channel)
+        return 0;
+    spice_assert(record_channel->base.active);
+
+    if (record_channel->write_pos < RECORD_SAMPLES_SIZE / 2) {
+        return 0;
+    }
+
+    len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
+
+    if (len < bufsize) {
+        SndWorker *worker = record_channel->base.worker;
+        snd_receive(record_channel);
+        if (!worker->connection) {
+            return 0;
+        }
+        len = MIN(record_channel->write_pos - record_channel->read_pos, bufsize);
+    }
+
+    read_pos = record_channel->read_pos % RECORD_SAMPLES_SIZE;
+    record_channel->read_pos += len;
+    now = MIN(len, RECORD_SAMPLES_SIZE - read_pos);
+    memcpy(samples, &record_channel->samples[read_pos], now * 4);
+    if (now < len) {
+        memcpy(samples + now, record_channel->samples, (len - now) * 4);
+    }
+    return len;
+}
+
+SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_playback_rate(SpicePlaybackInstance *sin)
+{
+    int client_can_opus = TRUE;
+    if (sin && sin->st->worker.connection) {
+        SndChannel *channel = sin->st->worker.connection;
+        PlaybackChannel *playback_channel = SPICE_CONTAINEROF(channel, PlaybackChannel, base);
+        client_can_opus = red_channel_client_test_remote_cap(playback_channel->base.channel_client,
+                                          SPICE_PLAYBACK_CAP_OPUS);
+    }
+
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        return SND_CODEC_OPUS_PLAYBACK_FREQ;
+
+    return SND_CODEC_CELT_PLAYBACK_FREQ;
+}
+
+SPICE_GNUC_VISIBLE void spice_server_set_playback_rate(SpicePlaybackInstance *sin, uint32_t frequency)
+{
+    RedChannel *channel = sin->st->worker.base_channel;
+    sin->st->frequency = frequency;
+    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_OPUS);
+}
+
+SPICE_GNUC_VISIBLE uint32_t spice_server_get_best_record_rate(SpiceRecordInstance *sin)
+{
+    int client_can_opus = TRUE;
+    if (sin && sin->st->worker.connection) {
+        SndChannel *channel = sin->st->worker.connection;
+        RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+        client_can_opus = red_channel_client_test_remote_cap(record_channel->base.channel_client,
+                                          SPICE_RECORD_CAP_OPUS);
+    }
+
+    if (client_can_opus && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, SND_CODEC_ANY_FREQUENCY))
+        return SND_CODEC_OPUS_PLAYBACK_FREQ;
+
+    return SND_CODEC_CELT_PLAYBACK_FREQ;
+}
+
+SPICE_GNUC_VISIBLE void spice_server_set_record_rate(SpiceRecordInstance *sin, uint32_t frequency)
+{
+    RedChannel *channel = sin->st->worker.base_channel;
+    sin->st->frequency = frequency;
+    if (channel && snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_OPUS, frequency))
+        red_channel_set_cap(channel, SPICE_RECORD_CAP_OPUS);
+}
+
+static void on_new_record_channel(SndWorker *worker)
+{
+    RecordChannel *record_channel = (RecordChannel *)worker->connection;
+    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
+
+    spice_assert(record_channel);
+
+    if (st->volume.volume_nchannels) {
+        snd_set_command((SndChannel *)record_channel, SND_RECORD_VOLUME_MASK);
+    }
+    if (record_channel->base.active) {
+        snd_set_command((SndChannel *)record_channel, SND_RECORD_CTRL_MASK);
+    }
+}
+
+static void snd_record_cleanup(SndChannel *channel)
+{
+    RecordChannel *record_channel = SPICE_CONTAINEROF(channel, RecordChannel, base);
+    snd_codec_destroy(&record_channel->codec);
+}
+
+static void snd_set_record_peer(RedChannel *channel, RedClient *client, RedsStream *stream,
+                                int migration, int num_common_caps, uint32_t *common_caps,
+                                int num_caps, uint32_t *caps)
+{
+    SndWorker *worker = channel->data;
+    RecordChannel *record_channel;
+    SpiceRecordState *st = SPICE_CONTAINEROF(worker, SpiceRecordState, worker);
+
+    snd_disconnect_channel(worker->connection);
+
+    if (!(record_channel = (RecordChannel *)__new_channel(worker,
+                                                          sizeof(*record_channel),
+                                                          SPICE_CHANNEL_RECORD,
+                                                          client,
+                                                          stream,
+                                                          migration,
+                                                          snd_record_send,
+                                                          snd_record_handle_message,
+                                                          snd_record_on_message_done,
+                                                          snd_record_cleanup,
+                                                          common_caps, num_common_caps,
+                                                          caps, num_caps))) {
+        return;
+    }
+
+    record_channel->mode = SPICE_AUDIO_DATA_MODE_RAW;
+
+    worker->connection = &record_channel->base;
+
+    on_new_record_channel(worker);
+    if (worker->active) {
+        spice_server_record_start(st->sin);
+    }
+    snd_record_send(worker->connection);
+}
+
+static void snd_playback_migrate_channel_client(RedChannelClient *rcc)
+{
+    SndWorker *worker;
+
+    spice_assert(rcc->channel);
+    spice_assert(rcc->channel->data);
+    worker = (SndWorker *)rcc->channel->data;
+    spice_debug(NULL);
+
+    if (worker->connection) {
+        spice_assert(worker->connection->channel_client == rcc);
+        snd_set_command(worker->connection, SND_PLAYBACK_MIGRATE_MASK);
+        snd_playback_send(worker->connection);
+    }
+}
+
+static void add_worker(SndWorker *worker)
+{
+    worker->next = workers;
+    workers = worker;
+}
+
+static void remove_worker(SndWorker *worker)
+{
+    SndWorker **now = &workers;
+    while (*now) {
+        if (*now == worker) {
+            *now = worker->next;
+            return;
+        }
+        now = &(*now)->next;
+    }
+    spice_printerr("not found");
+}
+
+void snd_attach_playback(SpicePlaybackInstance *sin)
+{
+    SndWorker *playback_worker;
+    RedChannel *channel;
+    ClientCbs client_cbs = { NULL, };
+
+    sin->st = spice_new0(SpicePlaybackState, 1);
+    sin->st->sin = sin;
+    playback_worker = &sin->st->worker;
+    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
+
+    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
+    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_PLAYBACK, 0);
+
+    channel->data = playback_worker;
+    client_cbs.connect = snd_set_playback_peer;
+    client_cbs.disconnect = snd_disconnect_channel_client;
+    client_cbs.migrate = snd_playback_migrate_channel_client;
+    red_channel_register_client_cbs(channel, &client_cbs);
+    red_channel_set_data(channel, playback_worker);
+
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
+        red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_CELT_0_5_1);
+
+    red_channel_set_cap(channel, SPICE_PLAYBACK_CAP_VOLUME);
+
+    playback_worker->base_channel = channel;
+    add_worker(playback_worker);
+    reds_register_channel(playback_worker->base_channel);
+}
+
+void snd_attach_record(SpiceRecordInstance *sin)
+{
+    SndWorker *record_worker;
+    RedChannel *channel;
+    ClientCbs client_cbs = { NULL, };
+
+    sin->st = spice_new0(SpiceRecordState, 1);
+    sin->st->sin = sin;
+    record_worker = &sin->st->worker;
+    sin->st->frequency = SND_CODEC_CELT_PLAYBACK_FREQ; /* Default to the legacy rate */
+
+    // TODO: Make RedChannel base of worker? instead of assigning it to channel->data
+    channel = red_channel_create_dummy(sizeof(RedChannel), SPICE_CHANNEL_RECORD, 0);
+
+    channel->data = record_worker;
+    client_cbs.connect = snd_set_record_peer;
+    client_cbs.disconnect = snd_disconnect_channel_client;
+    client_cbs.migrate = snd_record_migrate_channel_client;
+    red_channel_register_client_cbs(channel, &client_cbs);
+    red_channel_set_data(channel, record_worker);
+    if (snd_codec_is_capable(SPICE_AUDIO_DATA_MODE_CELT_0_5_1, SND_CODEC_ANY_FREQUENCY))
+        red_channel_set_cap(channel, SPICE_RECORD_CAP_CELT_0_5_1);
+    red_channel_set_cap(channel, SPICE_RECORD_CAP_VOLUME);
+
+    record_worker->base_channel = channel;
+    add_worker(record_worker);
+    reds_register_channel(record_worker->base_channel);
+}
+
+static void snd_detach_common(SndWorker *worker)
+{
+    if (!worker) {
+        return;
+    }
+    remove_worker(worker);
+    snd_disconnect_channel(worker->connection);
+    reds_unregister_channel(worker->base_channel);
+    red_channel_destroy(worker->base_channel);
+}
+
+static void spice_playback_state_free(SpicePlaybackState *st)
+{
+    free(st->volume.volume);
+    free(st);
+}
+
+void snd_detach_playback(SpicePlaybackInstance *sin)
+{
+    snd_detach_common(&sin->st->worker);
+    spice_playback_state_free(sin->st);
+}
+
+static void spice_record_state_free(SpiceRecordState *st)
+{
+    free(st->volume.volume);
+    free(st);
+}
+
+void snd_detach_record(SpiceRecordInstance *sin)
+{
+    snd_detach_common(&sin->st->worker);
+    spice_record_state_free(sin->st);
+}
+
+void snd_set_playback_compression(int on)
+{
+    SndWorker *now = workers;
+
+    playback_compression = !!on;
+
+    for (; now; now = now->next) {
+        if (now->base_channel->type == SPICE_CHANNEL_PLAYBACK && now->connection) {
+            PlaybackChannel* playback = (PlaybackChannel*)now->connection;
+            SpicePlaybackState *st = SPICE_CONTAINEROF(now, SpicePlaybackState, worker);
+            int client_can_celt = red_channel_client_test_remote_cap(playback->base.channel_client,
+                                    SPICE_PLAYBACK_CAP_CELT_0_5_1);
+            int client_can_opus = red_channel_client_test_remote_cap(playback->base.channel_client,
+                                    SPICE_PLAYBACK_CAP_OPUS);
+            int desired_mode = snd_desired_audio_mode(st->frequency, client_can_opus, client_can_celt);
+            if (playback->mode != desired_mode) {
+                playback->mode = desired_mode;
+                snd_set_command(now->connection, SND_PLAYBACK_MODE_MASK);
+            }
+        }
+    }
+}
diff --git a/server/sound.h b/server/sound.h
new file mode 100644
index 0000000..7cc4db5
--- /dev/null
+++ b/server/sound.h
@@ -0,0 +1,33 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+
+#ifndef _H_SND_WORKER
+#define _H_SND_WORKER
+
+#include "spice.h"
+
+void snd_attach_playback(SpicePlaybackInstance *sin);
+void snd_detach_playback(SpicePlaybackInstance *sin);
+
+void snd_attach_record(SpiceRecordInstance *sin);
+void snd_detach_record(SpiceRecordInstance *sin);
+
+void snd_set_playback_compression(int on);
+
+void snd_set_playback_latency(RedClient *client, uint32_t latency);
+
+#endif
diff --git a/server/spice-bitmap-utils.c b/server/spice-bitmap-utils.c
index 03d7694..8d6e7c6 100644
--- a/server/spice-bitmap-utils.c
+++ b/server/spice-bitmap-utils.c
@@ -117,3 +117,165 @@ int bitmap_has_extra_stride(SpiceBitmap *bitmap)
     }
     return 0;
 }
+
+int spice_bitmap_from_surface_type(uint32_t surface_format)
+{
+    switch (surface_format) {
+    case SPICE_SURFACE_FMT_16_555:
+        return SPICE_BITMAP_FMT_16BIT;
+    case SPICE_SURFACE_FMT_32_xRGB:
+        return SPICE_BITMAP_FMT_32BIT;
+    case SPICE_SURFACE_FMT_32_ARGB:
+        return SPICE_BITMAP_FMT_RGBA;
+    case SPICE_SURFACE_FMT_8_A:
+        return SPICE_BITMAP_FMT_8BIT_A;
+    default:
+        spice_critical("Unsupported surface format");
+    }
+    return 0;
+}
+
+#define RAM_PATH "/tmp/tmpfs"
+
+static void dump_palette(FILE *f, SpicePalette* plt)
+{
+    int i;
+    for (i = 0; i < plt->num_ents; i++) {
+        fwrite(plt->ents + i, sizeof(uint32_t), 1, f);
+    }
+}
+
+static void dump_line(FILE *f, uint8_t* line, uint16_t n_pixel_bits, int width, int row_size)
+{
+    int i;
+    int copy_bytes_size = SPICE_ALIGN(n_pixel_bits * width, 8) / 8;
+
+    fwrite(line, 1, copy_bytes_size, f);
+    if (row_size > copy_bytes_size) {
+        // each line should be 4 bytes aligned
+        for (i = copy_bytes_size; i < row_size; i++) {
+            fprintf(f, "%c", 0);
+        }
+    }
+}
+void dump_bitmap(SpiceBitmap *bitmap)
+{
+    static uint32_t file_id = 0;
+
+    char file_str[200];
+    int rgb = TRUE;
+    uint16_t n_pixel_bits;
+    SpicePalette *plt = NULL;
+    uint32_t id;
+    int row_size;
+    uint32_t file_size;
+    int alpha = 0;
+    uint32_t header_size = 14 + 40;
+    uint32_t bitmap_data_offset;
+    uint32_t tmp_u32;
+    int32_t tmp_32;
+    uint16_t tmp_u16;
+    FILE *f;
+    int i, j;
+
+    switch (bitmap->format) {
+    case SPICE_BITMAP_FMT_1BIT_BE:
+    case SPICE_BITMAP_FMT_1BIT_LE:
+        rgb = FALSE;
+        n_pixel_bits = 1;
+        break;
+    case SPICE_BITMAP_FMT_4BIT_BE:
+    case SPICE_BITMAP_FMT_4BIT_LE:
+        rgb = FALSE;
+        n_pixel_bits = 4;
+        break;
+    case SPICE_BITMAP_FMT_8BIT:
+        rgb = FALSE;
+        n_pixel_bits = 8;
+        break;
+    case SPICE_BITMAP_FMT_16BIT:
+        n_pixel_bits = 16;
+        break;
+    case SPICE_BITMAP_FMT_24BIT:
+        n_pixel_bits = 24;
+        break;
+    case SPICE_BITMAP_FMT_32BIT:
+        n_pixel_bits = 32;
+        break;
+    case SPICE_BITMAP_FMT_RGBA:
+        n_pixel_bits = 32;
+        alpha = 1;
+        break;
+    default:
+        spice_error("invalid bitmap format  %u", bitmap->format);
+        return;
+    }
+
+    if (!rgb) {
+        if (!bitmap->palette) {
+            return; // dont dump masks.
+        }
+        plt = bitmap->palette;
+    }
+    row_size = (((bitmap->x * n_pixel_bits) + 31) / 32) * 4;
+    bitmap_data_offset = header_size;
+
+    if (plt) {
+        bitmap_data_offset += plt->num_ents * 4;
+    }
+    file_size = bitmap_data_offset + (bitmap->y * row_size);
+
+    id = ++file_id;
+    sprintf(file_str, "%s/%u.bmp", RAM_PATH, id);
+
+    f = fopen(file_str, "wb");
+    if (!f) {
+        spice_error("Error creating bmp");
+        return;
+    }
+
+    /* writing the bmp v3 header */
+    fprintf(f, "BM");
+    fwrite(&file_size, sizeof(file_size), 1, f);
+    tmp_u16 = alpha ? 1 : 0;
+    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f); // reserved for application
+    tmp_u16 = 0;
+    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f);
+    fwrite(&bitmap_data_offset, sizeof(bitmap_data_offset), 1, f);
+    tmp_u32 = header_size - 14;
+    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // sub header size
+    tmp_32 = bitmap->x;
+    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
+    tmp_32 = bitmap->y;
+    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
+
+    tmp_u16 = 1;
+    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f); // color plane
+    fwrite(&n_pixel_bits, sizeof(n_pixel_bits), 1, f); // pixel depth
+
+    tmp_u32 = 0;
+    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // compression method
+
+    tmp_u32 = 0; //file_size - bitmap_data_offset;
+    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // image size
+    tmp_32 = 0;
+    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
+    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
+    tmp_u32 = (!plt) ? 0 : plt->num_ents; // plt entries
+    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f);
+    tmp_u32 = 0;
+    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f);
+
+    if (plt) {
+        dump_palette(f, plt);
+    }
+    /* writing the data */
+    for (i = 0; i < bitmap->data->num_chunks; i++) {
+        SpiceChunk *chunk = &bitmap->data->chunk[i];
+        int num_lines = chunk->len / bitmap->stride;
+        for (j = 0; j < num_lines; j++) {
+            dump_line(f, chunk->data + (j * bitmap->stride), n_pixel_bits, bitmap->x, row_size);
+        }
+    }
+    fclose(f);
+}
diff --git a/server/spice-bitmap-utils.h b/server/spice-bitmap-utils.h
index 38cb88a..beaa96f 100644
--- a/server/spice-bitmap-utils.h
+++ b/server/spice-bitmap-utils.h
@@ -15,13 +15,10 @@
    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/>.
 */
-#ifndef RED_BITMAP_UTILS_H_
-# define RED_BITMAP_UTILS_H_
+#ifndef H_SPICE_BITMAP_UTILS
+#define H_SPICE_BITMAP_UTILS
 
-#include <glib.h>
-#include <stdint.h>
-#include "common/draw.h"
-#include "common/log.h"
+#include "red_common.h"
 
 typedef enum {
     BITMAP_GRADUAL_INVALID,
@@ -88,4 +85,8 @@ static inline int bitmap_fmt_has_graduality(uint8_t fmt)
 BitmapGradualType bitmap_get_graduality_level     (SpiceBitmap *bitmap);
 int               bitmap_has_extra_stride         (SpiceBitmap *bitmap);
 
-#endif /* RED_BITMAP_UTILS_H_ */
+void dump_bitmap(SpiceBitmap *bitmap);
+
+int spice_bitmap_from_surface_type(uint32_t surface_format);
+
+#endif
diff --git a/server/spice_bitmap_utils.c b/server/spice_bitmap_utils.c
deleted file mode 100644
index ae3fc8b..0000000
--- a/server/spice_bitmap_utils.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include <stdio.h>
-
-#include "common/log.h"
-#include "common/draw.h"
-
-#include "spice_bitmap_utils.h"
-
-int spice_bitmap_from_surface_type(uint32_t surface_format)
-{
-    switch (surface_format) {
-    case SPICE_SURFACE_FMT_16_555:
-        return SPICE_BITMAP_FMT_16BIT;
-    case SPICE_SURFACE_FMT_32_xRGB:
-        return SPICE_BITMAP_FMT_32BIT;
-    case SPICE_SURFACE_FMT_32_ARGB:
-        return SPICE_BITMAP_FMT_RGBA;
-    case SPICE_SURFACE_FMT_8_A:
-        return SPICE_BITMAP_FMT_8BIT_A;
-    default:
-        spice_critical("Unsupported surface format");
-    }
-    return 0;
-}
-
-#define RAM_PATH "/tmp/tmpfs"
-
-static void dump_palette(FILE *f, SpicePalette* plt)
-{
-    int i;
-    for (i = 0; i < plt->num_ents; i++) {
-        fwrite(plt->ents + i, sizeof(uint32_t), 1, f);
-    }
-}
-
-static void dump_line(FILE *f, uint8_t* line, uint16_t n_pixel_bits, int width, int row_size)
-{
-    int i;
-    int copy_bytes_size = SPICE_ALIGN(n_pixel_bits * width, 8) / 8;
-
-    fwrite(line, 1, copy_bytes_size, f);
-    if (row_size > copy_bytes_size) {
-        // each line should be 4 bytes aligned
-        for (i = copy_bytes_size; i < row_size; i++) {
-            fprintf(f, "%c", 0);
-        }
-    }
-}
-void dump_bitmap(SpiceBitmap *bitmap)
-{
-    static uint32_t file_id = 0;
-
-    char file_str[200];
-    int rgb = TRUE;
-    uint16_t n_pixel_bits;
-    SpicePalette *plt = NULL;
-    uint32_t id;
-    int row_size;
-    uint32_t file_size;
-    int alpha = 0;
-    uint32_t header_size = 14 + 40;
-    uint32_t bitmap_data_offset;
-    uint32_t tmp_u32;
-    int32_t tmp_32;
-    uint16_t tmp_u16;
-    FILE *f;
-    int i, j;
-
-    switch (bitmap->format) {
-    case SPICE_BITMAP_FMT_1BIT_BE:
-    case SPICE_BITMAP_FMT_1BIT_LE:
-        rgb = FALSE;
-        n_pixel_bits = 1;
-        break;
-    case SPICE_BITMAP_FMT_4BIT_BE:
-    case SPICE_BITMAP_FMT_4BIT_LE:
-        rgb = FALSE;
-        n_pixel_bits = 4;
-        break;
-    case SPICE_BITMAP_FMT_8BIT:
-        rgb = FALSE;
-        n_pixel_bits = 8;
-        break;
-    case SPICE_BITMAP_FMT_16BIT:
-        n_pixel_bits = 16;
-        break;
-    case SPICE_BITMAP_FMT_24BIT:
-        n_pixel_bits = 24;
-        break;
-    case SPICE_BITMAP_FMT_32BIT:
-        n_pixel_bits = 32;
-        break;
-    case SPICE_BITMAP_FMT_RGBA:
-        n_pixel_bits = 32;
-        alpha = 1;
-        break;
-    default:
-        spice_error("invalid bitmap format  %u", bitmap->format);
-        return;
-    }
-
-    if (!rgb) {
-        if (!bitmap->palette) {
-            return; // dont dump masks.
-        }
-        plt = bitmap->palette;
-    }
-    row_size = (((bitmap->x * n_pixel_bits) + 31) / 32) * 4;
-    bitmap_data_offset = header_size;
-
-    if (plt) {
-        bitmap_data_offset += plt->num_ents * 4;
-    }
-    file_size = bitmap_data_offset + (bitmap->y * row_size);
-
-    id = ++file_id;
-    sprintf(file_str, "%s/%u.bmp", RAM_PATH, id);
-
-    f = fopen(file_str, "wb");
-    if (!f) {
-        spice_error("Error creating bmp");
-        return;
-    }
-
-    /* writing the bmp v3 header */
-    fprintf(f, "BM");
-    fwrite(&file_size, sizeof(file_size), 1, f);
-    tmp_u16 = alpha ? 1 : 0;
-    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f); // reserved for application
-    tmp_u16 = 0;
-    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f);
-    fwrite(&bitmap_data_offset, sizeof(bitmap_data_offset), 1, f);
-    tmp_u32 = header_size - 14;
-    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // sub header size
-    tmp_32 = bitmap->x;
-    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
-    tmp_32 = bitmap->y;
-    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
-
-    tmp_u16 = 1;
-    fwrite(&tmp_u16, sizeof(tmp_u16), 1, f); // color plane
-    fwrite(&n_pixel_bits, sizeof(n_pixel_bits), 1, f); // pixel depth
-
-    tmp_u32 = 0;
-    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // compression method
-
-    tmp_u32 = 0; //file_size - bitmap_data_offset;
-    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f); // image size
-    tmp_32 = 0;
-    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
-    fwrite(&tmp_32, sizeof(tmp_32), 1, f);
-    tmp_u32 = (!plt) ? 0 : plt->num_ents; // plt entries
-    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f);
-    tmp_u32 = 0;
-    fwrite(&tmp_u32, sizeof(tmp_u32), 1, f);
-
-    if (plt) {
-        dump_palette(f, plt);
-    }
-    /* writing the data */
-    for (i = 0; i < bitmap->data->num_chunks; i++) {
-        SpiceChunk *chunk = &bitmap->data->chunk[i];
-        int num_lines = chunk->len / bitmap->stride;
-        for (j = 0; j < num_lines; j++) {
-            dump_line(f, chunk->data + (j * bitmap->stride), n_pixel_bits, bitmap->x, row_size);
-        }
-    }
-    fclose(f);
-}
diff --git a/server/spice_bitmap_utils.h b/server/spice_bitmap_utils.h
deleted file mode 100644
index 69860e5..0000000
--- a/server/spice_bitmap_utils.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef H_SPICE_BITMAP_UTILS
-#define H_SPICE_BITMAP_UTILS
-
-void dump_bitmap(SpiceBitmap *bitmap);
-
-int spice_bitmap_from_surface_type(uint32_t surface_format);
-
-#endif
diff --git a/server/spice_image_cache.c b/server/spice_image_cache.c
deleted file mode 100644
index 1c5de24..0000000
--- a/server/spice_image_cache.c
+++ /dev/null
@@ -1,214 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-#include "spice_image_cache.h"
-#include "red_parse_qxl.h"
-#include "display-channel.h"
-
-static ImageCacheItem *image_cache_find(ImageCache *cache, uint64_t id)
-{
-    ImageCacheItem *item = cache->hash_table[id % IMAGE_CACHE_HASH_SIZE];
-
-    while (item) {
-        if (item->id == id) {
-            return item;
-        }
-        item = item->next;
-    }
-    return NULL;
-}
-
-int image_cache_hit(ImageCache *cache, uint64_t id)
-{
-    ImageCacheItem *item;
-    if (!(item = image_cache_find(cache, id))) {
-        return FALSE;
-    }
-#ifdef IMAGE_CACHE_AGE
-    item->age = cache->age;
-#endif
-    ring_remove(&item->lru_link);
-    ring_add(&cache->lru, &item->lru_link);
-    return TRUE;
-}
-
-static void image_cache_remove(ImageCache *cache, ImageCacheItem *item)
-{
-    ImageCacheItem **now;
-
-    now = &cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
-    for (;;) {
-        spice_assert(*now);
-        if (*now == item) {
-            *now = item->next;
-            break;
-        }
-        now = &(*now)->next;
-    }
-    ring_remove(&item->lru_link);
-    pixman_image_unref(item->image);
-    free(item);
-#ifndef IMAGE_CACHE_AGE
-    cache->num_items--;
-#endif
-}
-
-#define IMAGE_CACHE_MAX_ITEMS 2
-
-static void image_cache_put(SpiceImageCache *spice_cache, uint64_t id, pixman_image_t *image)
-{
-    ImageCache *cache = (ImageCache *)spice_cache;
-    ImageCacheItem *item;
-
-#ifndef IMAGE_CACHE_AGE
-    if (cache->num_items == IMAGE_CACHE_MAX_ITEMS) {
-        ImageCacheItem *tail = (ImageCacheItem *)ring_get_tail(&cache->lru);
-        spice_assert(tail);
-        image_cache_remove(cache, tail);
-    }
-#endif
-
-    item = spice_new(ImageCacheItem, 1);
-    item->id = id;
-#ifdef IMAGE_CACHE_AGE
-    item->age = cache->age;
-#else
-    cache->num_items++;
-#endif
-    item->image = pixman_image_ref(image);
-    ring_item_init(&item->lru_link);
-
-    item->next = cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE];
-    cache->hash_table[item->id % IMAGE_CACHE_HASH_SIZE] = item;
-
-    ring_add(&cache->lru, &item->lru_link);
-}
-
-static pixman_image_t *image_cache_get(SpiceImageCache *spice_cache, uint64_t id)
-{
-    ImageCache *cache = (ImageCache *)spice_cache;
-
-    ImageCacheItem *item = image_cache_find(cache, id);
-    if (!item) {
-        spice_error("not found");
-    }
-    return pixman_image_ref(item->image);
-}
-
-void image_cache_init(ImageCache *cache)
-{
-    static SpiceImageCacheOps image_cache_ops = {
-        image_cache_put,
-        image_cache_get,
-    };
-
-    cache->base.ops = &image_cache_ops;
-    memset(cache->hash_table, 0, sizeof(cache->hash_table));
-    ring_init(&cache->lru);
-#ifdef IMAGE_CACHE_AGE
-    cache->age = 0;
-#else
-    cache->num_items = 0;
-#endif
-}
-
-void image_cache_reset(ImageCache *cache)
-{
-    ImageCacheItem *item;
-
-    while ((item = (ImageCacheItem *)ring_get_head(&cache->lru))) {
-        image_cache_remove(cache, item);
-    }
-#ifdef IMAGE_CACHE_AGE
-    cache->age = 0;
-#endif
-}
-
-#define IMAGE_CACHE_DEPTH 4
-
-void image_cache_aging(ImageCache *cache)
-{
-#ifdef IMAGE_CACHE_AGE
-    ImageCacheItem *item;
-
-    cache->age++;
-    while ((item = (ImageCacheItem *)ring_get_tail(&cache->lru)) &&
-           cache->age - item->age > IMAGE_CACHE_DEPTH) {
-        image_cache_remove(cache, item);
-    }
-#endif
-}
-
-void image_cache_localize(ImageCache *cache, SpiceImage **image_ptr,
-                          SpiceImage *image_store, Drawable *drawable)
-{
-    SpiceImage *image = *image_ptr;
-
-    if (image == NULL) {
-        spice_assert(drawable != NULL);
-        spice_assert(drawable->red_drawable->self_bitmap_image != NULL);
-        *image_ptr = drawable->red_drawable->self_bitmap_image;
-        return;
-    }
-
-    if (image_cache_hit(cache, image->descriptor.id)) {
-        image_store->descriptor = image->descriptor;
-        image_store->descriptor.type = SPICE_IMAGE_TYPE_FROM_CACHE;
-        image_store->descriptor.flags = 0;
-        *image_ptr = image_store;
-        return;
-    }
-
-    switch (image->descriptor.type) {
-    case SPICE_IMAGE_TYPE_QUIC: {
-        image_store->descriptor = image->descriptor;
-        image_store->u.quic = image->u.quic;
-        *image_ptr = image_store;
-#ifdef IMAGE_CACHE_AGE
-        image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
-#else
-        if (image_store->descriptor.width * image->descriptor.height >= 640 * 480) {
-            image_store->descriptor.flags |= SPICE_IMAGE_FLAGS_CACHE_ME;
-        }
-#endif
-        break;
-    }
-    case SPICE_IMAGE_TYPE_BITMAP:
-    case SPICE_IMAGE_TYPE_SURFACE:
-        /* nothing */
-        break;
-    default:
-        spice_error("invalid image type");
-    }
-}
-
-void image_cache_localize_brush(ImageCache *cache, SpiceBrush *brush, SpiceImage *image_store)
-{
-    if (brush->type == SPICE_BRUSH_TYPE_PATTERN) {
-        image_cache_localize(cache, &brush->u.pattern.pat, image_store, NULL);
-    }
-}
-
-void image_cache_localize_mask(ImageCache *cache, SpiceQMask *mask, SpiceImage *image_store)
-{
-    if (mask->bitmap) {
-        image_cache_localize(cache, &mask->bitmap, image_store, NULL);
-    }
-}
diff --git a/server/spice_image_cache.h b/server/spice_image_cache.h
deleted file mode 100644
index 6d6b32d..0000000
--- a/server/spice_image_cache.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2009-2015 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifndef H_SPICE_IMAGE_CACHE
-#define H_SPICE_IMAGE_CACHE
-
-#include <inttypes.h>
-
-#include "common/pixman_utils.h"
-#include "common/canvas_base.h"
-#include "common/ring.h"
-
-/* FIXME: move back to display_channel.h (once structs are private) */
-typedef struct Drawable Drawable;
-typedef struct DisplayChannelClient DisplayChannelClient;
-
-typedef struct ImageCacheItem {
-    RingItem lru_link;
-    uint64_t id;
-#ifdef IMAGE_CACHE_AGE
-    uint32_t age;
-#endif
-    struct ImageCacheItem *next;
-    pixman_image_t *image;
-} ImageCacheItem;
-
-#define IMAGE_CACHE_HASH_SIZE 1024
-
-typedef struct ImageCache {
-    SpiceImageCache base;
-    ImageCacheItem *hash_table[IMAGE_CACHE_HASH_SIZE];
-    Ring lru;
-#ifdef IMAGE_CACHE_AGE
-    uint32_t age;
-#else
-    uint32_t num_items;
-#endif
-} ImageCache;
-
-int          image_cache_hit               (ImageCache *cache, uint64_t id);
-void         image_cache_init              (ImageCache *cache);
-void         image_cache_reset             (ImageCache *cache);
-void         image_cache_aging             (ImageCache *cache);
-void         image_cache_localize          (ImageCache *cache, SpiceImage **image_ptr,
-                                            SpiceImage *image_store, Drawable *drawable);
-void         image_cache_localize_brush    (ImageCache *cache, SpiceBrush *brush,
-                                            SpiceImage *image_store);
-void         image_cache_localize_mask     (ImageCache *cache, SpiceQMask *mask,
-                                            SpiceImage *image_store);
-
-#endif
diff --git a/server/spicevmc.c b/server/spicevmc.c
index d37b1ec..52a29a4 100644
--- a/server/spicevmc.c
+++ b/server/spicevmc.c
@@ -30,10 +30,10 @@
 
 #include "common/generated_server_marshallers.h"
 
-#include "char_device.h"
+#include "char-device.h"
 #include "red_channel.h"
 #include "reds.h"
-#include "migration_protocol.h"
+#include "migration-protocol.h"
 
 /* todo: add flow control. i.e.,
  * (a) limit the tokens available for the client
diff --git a/server/stream.h b/server/stream.h
index c436c2e..cd77df7 100644
--- a/server/stream.h
+++ b/server/stream.h
@@ -20,10 +20,10 @@
 
 #include <glib.h>
 #include "utils.h"
-#include "mjpeg_encoder.h"
+#include "mjpeg-encoder.h"
 #include "common/region.h"
 #include "red_channel.h"
-#include "spice_image_cache.h"
+#include "image-cache.h"
 
 #define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
 #define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000)
diff --git a/server/utils.h b/server/utils.h
index aba85f9..20ebe00 100644
--- a/server/utils.h
+++ b/server/utils.h
@@ -18,11 +18,9 @@
 #ifndef UTILS_H_
 # define UTILS_H_
 
-#include <glib.h>
-#include <time.h>
+#include "red_common.h"
 
-#include "common/ring.h"
-#include "common/log.h"
+#define SPICE_GNUC_VISIBLE __attribute__ ((visibility ("default")))
 
 static inline void set_bit(int index, uint32_t *addr)
 {
diff --git a/server/zlib-encoder.c b/server/zlib-encoder.c
new file mode 100644
index 0000000..069a448
--- /dev/null
+++ b/server/zlib-encoder.c
@@ -0,0 +1,125 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2010 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   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/>.
+*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "red_common.h"
+#include "zlib-encoder.h"
+#include <zlib.h>
+
+struct ZlibEncoder {
+    ZlibEncoderUsrContext *usr;
+
+    z_stream strm;
+    int last_level;
+};
+
+ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level)
+{
+    ZlibEncoder *enc;
+    int z_ret;
+
+    if (!usr->more_space || !usr->more_input) {
+        return NULL;
+    }
+
+    enc = spice_new0(ZlibEncoder, 1);
+
+    enc->usr = usr;
+
+    enc->strm.zalloc = Z_NULL;
+    enc->strm.zfree = Z_NULL;
+    enc->strm.opaque = Z_NULL;
+
+    z_ret = deflateInit(&enc->strm, level);
+    enc->last_level = level;
+    if (z_ret != Z_OK) {
+        spice_printerr("zlib error");
+        free(enc);
+        return NULL;
+    }
+
+    return enc;
+}
+
+void zlib_encoder_destroy(ZlibEncoder *encoder)
+{
+    deflateEnd(&encoder->strm);
+    free(encoder);
+}
+
+/* returns the total size of the encoded data */
+int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
+                uint8_t *io_ptr, unsigned int num_io_bytes)
+{
+    int flush;
+    int enc_size = 0;
+    int out_size = 0;
+    int z_ret;
+
+    z_ret = deflateReset(&zlib->strm);
+
+    if (z_ret != Z_OK) {
+        spice_error("deflateReset failed");
+    }
+
+    zlib->strm.next_out = io_ptr;
+    zlib->strm.avail_out = num_io_bytes;
+
+    if (level != zlib->last_level) {
+        if (zlib->strm.avail_out == 0) {
+            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
+            if (zlib->strm.avail_out == 0) {
+                spice_error("not enough space");
+            }
+        }
+        z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY);
+        if (z_ret != Z_OK) {
+            spice_error("deflateParams failed");
+        }
+        zlib->last_level = level;
+    }
+
+
+    do {
+        zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in);
+        if (zlib->strm.avail_in <= 0) {
+            spice_error("more input failed");
+        }
+        enc_size += zlib->strm.avail_in;
+        flush = (enc_size == input_size) ?  Z_FINISH : Z_NO_FLUSH;
+        while (1) {
+            int deflate_size = zlib->strm.avail_out;
+            z_ret = deflate(&zlib->strm, flush);
+            spice_assert(z_ret != Z_STREAM_ERROR);
+            out_size += deflate_size - zlib->strm.avail_out;
+            if (zlib->strm.avail_out) {
+                break;
+            }
+
+            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
+            if (zlib->strm.avail_out == 0) {
+                spice_error("not enough space");
+            }
+        }
+    } while (flush != Z_FINISH);
+
+    spice_assert(z_ret == Z_STREAM_END);
+    return out_size;
+}
diff --git a/server/zlib-encoder.h b/server/zlib-encoder.h
new file mode 100644
index 0000000..0620fc7
--- /dev/null
+++ b/server/zlib-encoder.h
@@ -0,0 +1,47 @@
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+         notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above copyright
+         notice, this list of conditions and the following disclaimer in
+         the documentation and/or other materials provided with the
+         distribution.
+       * Neither the name of the copyright holder nor the names of its
+         contributors may be used to endorse or promote products derived
+         from this software without specific prior written permission.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
+   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _H_ZLIB_ENCODER
+#define _H_ZLIB_ENCODER
+
+typedef struct ZlibEncoder ZlibEncoder;
+typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext;
+
+struct ZlibEncoderUsrContext {
+    int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr);
+    int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input);
+};
+
+ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level);
+void zlib_encoder_destroy(ZlibEncoder *encoder);
+
+/* returns the total size of the encoded data */
+int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
+                uint8_t *io_ptr, unsigned int num_io_bytes);
+#endif
diff --git a/server/zlib_encoder.c b/server/zlib_encoder.c
deleted file mode 100644
index a3d2aa6..0000000
--- a/server/zlib_encoder.c
+++ /dev/null
@@ -1,125 +0,0 @@
-/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/*
-   Copyright (C) 2010 Red Hat, Inc.
-
-   This library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Lesser General Public
-   License as published by the Free Software Foundation; either
-   version 2.1 of the License, or (at your option) any later version.
-
-   This library is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Lesser General Public License for more details.
-
-   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/>.
-*/
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include "red_common.h"
-#include "zlib_encoder.h"
-#include <zlib.h>
-
-struct ZlibEncoder {
-    ZlibEncoderUsrContext *usr;
-
-    z_stream strm;
-    int last_level;
-};
-
-ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level)
-{
-    ZlibEncoder *enc;
-    int z_ret;
-
-    if (!usr->more_space || !usr->more_input) {
-        return NULL;
-    }
-
-    enc = spice_new0(ZlibEncoder, 1);
-
-    enc->usr = usr;
-
-    enc->strm.zalloc = Z_NULL;
-    enc->strm.zfree = Z_NULL;
-    enc->strm.opaque = Z_NULL;
-
-    z_ret = deflateInit(&enc->strm, level);
-    enc->last_level = level;
-    if (z_ret != Z_OK) {
-        spice_printerr("zlib error");
-        free(enc);
-        return NULL;
-    }
-
-    return enc;
-}
-
-void zlib_encoder_destroy(ZlibEncoder *encoder)
-{
-    deflateEnd(&encoder->strm);
-    free(encoder);
-}
-
-/* returns the total size of the encoded data */
-int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
-                uint8_t *io_ptr, unsigned int num_io_bytes)
-{
-    int flush;
-    int enc_size = 0;
-    int out_size = 0;
-    int z_ret;
-
-    z_ret = deflateReset(&zlib->strm);
-
-    if (z_ret != Z_OK) {
-        spice_error("deflateReset failed");
-    }
-
-    zlib->strm.next_out = io_ptr;
-    zlib->strm.avail_out = num_io_bytes;
-
-    if (level != zlib->last_level) {
-        if (zlib->strm.avail_out == 0) {
-            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
-            if (zlib->strm.avail_out == 0) {
-                spice_error("not enough space");
-            }
-        }
-        z_ret = deflateParams(&zlib->strm, level, Z_DEFAULT_STRATEGY);
-        if (z_ret != Z_OK) {
-            spice_error("deflateParams failed");
-        }
-        zlib->last_level = level;
-    }
-
-
-    do {
-        zlib->strm.avail_in = zlib->usr->more_input(zlib->usr, &zlib->strm.next_in);
-        if (zlib->strm.avail_in <= 0) {
-            spice_error("more input failed");
-        }
-        enc_size += zlib->strm.avail_in;
-        flush = (enc_size == input_size) ?  Z_FINISH : Z_NO_FLUSH;
-        while (1) {
-            int deflate_size = zlib->strm.avail_out;
-            z_ret = deflate(&zlib->strm, flush);
-            spice_assert(z_ret != Z_STREAM_ERROR);
-            out_size += deflate_size - zlib->strm.avail_out;
-            if (zlib->strm.avail_out) {
-                break;
-            }
-
-            zlib->strm.avail_out = zlib->usr->more_space(zlib->usr, &zlib->strm.next_out);
-            if (zlib->strm.avail_out == 0) {
-                spice_error("not enough space");
-            }
-        }
-    } while (flush != Z_FINISH);
-
-    spice_assert(z_ret == Z_STREAM_END);
-    return out_size;
-}
diff --git a/server/zlib_encoder.h b/server/zlib_encoder.h
deleted file mode 100644
index 0620fc7..0000000
--- a/server/zlib_encoder.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
-   Copyright (C) 2009 Red Hat, Inc.
-
-   Redistribution and use in source and binary forms, with or without
-   modification, are permitted provided that the following conditions are
-   met:
-
-       * Redistributions of source code must retain the above copyright
-         notice, this list of conditions and the following disclaimer.
-       * Redistributions in binary form must reproduce the above copyright
-         notice, this list of conditions and the following disclaimer in
-         the documentation and/or other materials provided with the
-         distribution.
-       * Neither the name of the copyright holder nor the names of its
-         contributors may be used to endorse or promote products derived
-         from this software without specific prior written permission.
-
-   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS
-   IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-   TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-   PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-   HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-#ifndef _H_ZLIB_ENCODER
-#define _H_ZLIB_ENCODER
-
-typedef struct ZlibEncoder ZlibEncoder;
-typedef struct ZlibEncoderUsrContext ZlibEncoderUsrContext;
-
-struct ZlibEncoderUsrContext {
-    int (*more_space)(ZlibEncoderUsrContext *usr, uint8_t **io_ptr);
-    int (*more_input)(ZlibEncoderUsrContext *usr, uint8_t **input);
-};
-
-ZlibEncoder* zlib_encoder_create(ZlibEncoderUsrContext *usr, int level);
-void zlib_encoder_destroy(ZlibEncoder *encoder);
-
-/* returns the total size of the encoded data */
-int zlib_encode(ZlibEncoder *zlib, int level, int input_size,
-                uint8_t *io_ptr, unsigned int num_io_bytes);
-#endif
-- 
2.4.3



More information about the Spice-devel mailing list