[Spice-devel] [PATCH 03/10] worker: move stream to display channel

Frediano Ziglio fziglio at redhat.com
Fri Nov 6 03:01:59 PST 2015


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

---
 server/Makefile.am       |   2 +
 server/display-channel.c |  44 +++
 server/display-channel.h |  82 ++---
 server/red_worker.c      | 916 ++++++++++++++++++++---------------------------
 server/red_worker.h      |   1 +
 server/stream.c          |  66 ++++
 server/stream.h          | 143 ++++++++
 server/utils.h           |   1 +
 8 files changed, 664 insertions(+), 591 deletions(-)
 create mode 100644 server/stream.c
 create mode 100644 server/stream.h

diff --git a/server/Makefile.am b/server/Makefile.am
index 7216ab0..52703c9 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -136,6 +136,8 @@ libspice_server_la_SOURCES =			\
 	spice-bitmap-utils.h			\
 	spice-bitmap-utils.c			\
 	utils.h					\
+	stream.c					\
+	stream.h					\
 	$(NULL)
 
 if HAVE_GL
diff --git a/server/display-channel.c b/server/display-channel.c
index 5deab13..3df6a31 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -221,3 +221,47 @@ void dcc_push_monitors_config(DisplayChannelClient *dcc)
     red_monitors_config_item_add(dcc);
     red_channel_client_push(&dcc->common.base);
 }
+
+int display_channel_get_streams_timeout(DisplayChannel *display)
+{
+    int timeout = INT_MAX;
+    Ring *ring = &display->streams;
+    RingItem *item = ring;
+
+    red_time_t now = red_get_monotonic_time();
+    while ((item = ring_next(ring, item))) {
+        Stream *stream;
+
+        stream = SPICE_CONTAINEROF(item, Stream, link);
+        red_time_t delta = (stream->last_time + RED_STREAM_TIMEOUT) - now;
+
+        if (delta < 1000 * 1000) {
+            return 0;
+        }
+        timeout = MIN(timeout, (unsigned int)(delta / (1000 * 1000)));
+    }
+    return timeout;
+}
+
+void display_channel_set_stream_video(DisplayChannel *display, int stream_video)
+{
+    spice_return_if_fail(display);
+    spice_return_if_fail(stream_video != SPICE_STREAM_VIDEO_INVALID);
+
+    switch (stream_video) {
+    case SPICE_STREAM_VIDEO_ALL:
+        spice_info("sv all");
+        break;
+    case SPICE_STREAM_VIDEO_FILTER:
+        spice_info("sv filter");
+        break;
+    case SPICE_STREAM_VIDEO_OFF:
+        spice_info("sv off");
+        break;
+    default:
+        spice_warn_if_reached();
+        return;
+    }
+
+    display->stream_video = stream_video;
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index 827a9d4..b92bc5c 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -54,11 +54,9 @@
 #include "spice_image_cache.h"
 #include "utils.h"
 #include "tree.h"
+#include "stream.h"
 
 typedef struct DisplayChannel DisplayChannel;
-typedef struct DisplayChannelClient DisplayChannelClient;
-
-typedef struct Drawable Drawable;
 
 #define PALETTE_CACHE_HASH_SHIFT 8
 #define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT)
@@ -128,23 +126,6 @@ typedef struct {
     EncoderData data;
 } GlzData;
 
-typedef struct Stream Stream;
-struct Stream {
-    uint8_t refs;
-    Drawable *current;
-    red_time_t last_time;
-    int width;
-    int height;
-    SpiceRect dest_area;
-    int top_down;
-    Stream *next;
-    RingItem link;
-
-    uint32_t num_input_frames;
-    uint64_t input_fps_start_time;
-    uint32_t input_fps;
-};
-
 typedef struct DependItem {
     Drawable *drawable;
     RingItem ring_item;
@@ -179,48 +160,6 @@ struct Drawable {
     uint32_t process_commands_generation;
 };
 
-#define STREAM_STATS
-#ifdef STREAM_STATS
-typedef struct StreamStats {
-   uint64_t num_drops_pipe;
-   uint64_t num_drops_fps;
-   uint64_t num_frames_sent;
-   uint64_t num_input_frames;
-   uint64_t size_sent;
-
-   uint64_t start;
-   uint64_t end;
-} StreamStats;
-#endif
-
-typedef struct StreamAgent {
-    QRegion vis_region; /* the part of the surface area that is currently occupied by video
-                           fragments */
-    QRegion clip;       /* the current video clipping. It can be different from vis_region:
-                           for example, let c1 be the clip area at time t1, and c2
-                           be the clip area at time t2, where t1 < t2. If c1 contains c2, and
-                           at least part of c1/c2, hasn't been covered by a non-video images,
-                           vis_region will contain c2 and also the part of c1/c2 that still
-                           displays fragments of the video */
-
-    PipeItem create_item;
-    PipeItem destroy_item;
-    Stream *stream;
-    uint64_t last_send_time;
-    MJpegEncoder *mjpeg_encoder;
-    DisplayChannelClient *dcc;
-
-    int frames;
-    int drops;
-    int fps;
-
-    uint32_t report_id;
-    uint32_t client_required_latency;
-#ifdef STREAM_STATS
-    StreamStats stats;
-#endif
-} StreamAgent;
-
 struct DisplayChannelClient {
     CommonChannelClient common;
 
@@ -328,6 +267,10 @@ MonitorsConfig*            monitors_config_new                       (QXLHead *h
 MonitorsConfig *           monitors_config_ref                       (MonitorsConfig *config);
 void                       monitors_config_unref                     (MonitorsConfig *config);
 
+#define TRACE_ITEMS_SHIFT 3
+#define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
+#define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
+
 struct DisplayChannel {
     CommonChannel common; // Must be the first thing
 
@@ -344,6 +287,15 @@ struct DisplayChannel {
 
     RedCompressBuf *free_compress_bufs;
 
+    int stream_video;
+    uint32_t stream_count;
+    Stream streams_buf[NUM_STREAMS];
+    Stream *free_streams;
+    Ring streams;
+    ItemTrace items_trace[NUM_TRACE_ITEMS];
+    uint32_t next_item_trace;
+    uint64_t streams_size_total;
+
 #ifdef RED_STATISTICS
     uint64_t *cache_hits_counter;
     uint64_t *add_to_cache_counter;
@@ -360,6 +312,12 @@ struct DisplayChannel {
 #endif
 };
 
+void                       display_channel_set_stream_video          (DisplayChannel *display,
+                                                                      int stream_video);
+void                       display_channel_attach_stream             (DisplayChannel *display,
+                                                                      Drawable *drawable,
+                                                                      Stream *stream);
+int                        display_channel_get_streams_timeout       (DisplayChannel *display);
 void                       display_channel_compress_stats_print      (const DisplayChannel *display);
 void                       display_channel_compress_stats_reset      (DisplayChannel *display);
 
diff --git a/server/red_worker.c b/server/red_worker.c
index df5be98..7a4822a 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -58,6 +58,7 @@
 #include "common/generated_server_marshallers.h"
 
 #include "display-channel.h"
+#include "stream.h"
 
 #include "spice.h"
 #include "red_worker.h"
@@ -80,21 +81,6 @@
 
 #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
 
-#define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
-#define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000)
-#define RED_STREAM_TIMOUT (1000 * 1000 * 1000)
-#define RED_STREAM_FRAMES_START_CONDITION 20
-#define RED_STREAM_GRADUAL_FRAMES_START_CONDITION 0.2
-#define RED_STREAM_FRAMES_RESET_CONDITION 100
-#define RED_STREAM_MIN_SIZE (96 * 96)
-#define RED_STREAM_INPUT_FPS_TIMEOUT ((uint64_t)5 * 1000 * 1000 * 1000) // 5 sec
-#define RED_STREAM_CHANNEL_CAPACITY 0.8
-/* the client's stream report frequency is the minimum of the 2 values below */
-#define RED_STREAM_CLIENT_REPORT_WINDOW 5 // #frames
-#define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds
-#define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps
-#define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps
-
 #define FPS_TEST_INTERVAL 1
 #define MAX_FPS 30
 
@@ -241,11 +227,6 @@ typedef struct SurfaceDestroyItem {
     PipeItem pipe_item;
 } SurfaceDestroyItem;
 
-typedef struct StreamActivateReportItem {
-    PipeItem pipe_item;
-    uint32_t stream_id;
-} StreamActivateReportItem;
-
 #define MAX_PIPE_SIZE 50
 
 #define WIDE_CLIENT_ACK_WINDOW 40
@@ -266,20 +247,6 @@ typedef struct ImageItem {
     uint8_t data[0];
 } ImageItem;
 
-enum {
-    STREAM_FRAME_NONE,
-    STREAM_FRAME_NATIVE,
-    STREAM_FRAME_CONTAINER,
-};
-
-typedef struct StreamClipItem {
-    PipeItem base;
-    int refs;
-    StreamAgent *stream_agent;
-    int clip_type;
-    SpiceClipRects *rects;
-} StreamClipItem;
-
 typedef struct {
     QuicUsrContext usr;
     EncoderData data;
@@ -382,20 +349,6 @@ typedef struct RedSurface {
     QXLReleaseInfoExt create, destroy;
 } RedSurface;
 
-typedef struct ItemTrace {
-    red_time_t time;
-    int frames_count;
-    int gradual_frames_count;
-    int last_gradual_frame;
-    int width;
-    int height;
-    SpiceRect dest_area;
-} ItemTrace;
-
-#define TRACE_ITEMS_SHIFT 3
-#define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
-#define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
-
 #define NUM_DRAWABLES 1000
 #define NUM_CURSORS 100
 
@@ -428,7 +381,6 @@ typedef struct RedWorker {
 
     uint32_t shadows_count;
     uint32_t containers_count;
-    uint32_t stream_count;
 
     uint32_t bits_unique;
 
@@ -443,14 +395,6 @@ typedef struct RedWorker {
     spice_wan_compression_t jpeg_state;
     spice_wan_compression_t zlib_glz_state;
 
-    uint32_t streaming_video;
-    Stream streams_buf[NUM_STREAMS];
-    Stream *free_streams;
-    Ring streams;
-    ItemTrace items_trace[NUM_TRACE_ITEMS];
-    uint32_t next_item_trace;
-    uint64_t streams_size_total;
-
     QuicData quic_data;
     QuicContext *quic;
 
@@ -519,16 +463,12 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
 static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int surface_id,
                                  Drawable *last);
 static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable);
-static void red_display_release_stream(RedWorker *worker, StreamAgent *agent);
-static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized);
-static void red_stop_stream(RedWorker *worker, Stream *stream);
-static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect);
+static inline void display_channel_stream_maintenance(DisplayChannel *display, Drawable *candidate, Drawable *sect);
 static inline void display_begin_send_message(RedChannelClient *rcc);
 static void red_release_glz(DisplayChannelClient *dcc);
 static void red_freeze_glz(DisplayChannelClient *dcc);
 static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
                                          uint64_t* sync_data);
-static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item);
 static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
 static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
 static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
@@ -565,6 +505,136 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient
     SAFE_FOREACH(link, next, drawable, &(drawable)->glz_ring, glz, LINK_TO_GLZ(link))
 
 
+static int get_stream_id(DisplayChannel *display, Stream *stream)
+{
+    return (int)(stream - display->streams_buf);
+}
+
+static void display_stream_free(DisplayChannel *display, Stream *stream)
+{
+    stream->next = display->free_streams;
+    display->free_streams = stream;
+}
+
+static void display_stream_unref(DisplayChannel *display, Stream *stream)
+{
+    if (--stream->refs != 0)
+        return;
+
+    spice_warn_if_fail(!ring_item_is_linked(&stream->link));
+    display_stream_free(display, stream);
+    display->stream_count--;
+}
+
+static void display_stream_agent_unref(DisplayChannel *display, StreamAgent *agent)
+{
+    display_stream_unref(display, agent->stream);
+}
+
+static void display_stream_clip_unref(DisplayChannel *display, StreamClipItem *item)
+{
+    if (--item->refs != 0)
+        return;
+
+    display_stream_agent_unref(display, item->stream_agent);
+    free(item->rects);
+    free(item);
+}
+
+static void dcc_push_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent *agent)
+{
+    StreamClipItem *item = stream_clip_item_new(dcc, agent);
+    int n_rects;
+
+    item->clip_type = SPICE_CLIP_TYPE_RECTS;
+
+    n_rects = pixman_region32_n_rects(&agent->clip);
+    item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
+    item->rects->num_rects = n_rects;
+    region_ret_rects(&agent->clip, item->rects->rects, n_rects);
+
+    red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item);
+}
+
+
+void attach_stream(DisplayChannel *display, Drawable *drawable, Stream *stream)
+{
+    DisplayChannelClient *dcc;
+    RingItem *item, *next;
+
+    spice_assert(!drawable->stream && !stream->current);
+    spice_assert(drawable && stream);
+    stream->current = drawable;
+    drawable->stream = stream;
+    stream->last_time = drawable->creation_time;
+
+    uint64_t duration = drawable->creation_time - stream->input_fps_start_time;
+    if (duration >= RED_STREAM_INPUT_FPS_TIMEOUT) {
+        /* Round to the nearest integer, for instance 24 for 23.976 */
+        stream->input_fps = ((uint64_t)stream->num_input_frames * 1000 * 1000 * 1000 + duration / 2) / duration;
+        spice_debug("input-fps=%u", stream->input_fps);
+        stream->num_input_frames = 0;
+        stream->input_fps_start_time = drawable->creation_time;
+    } else {
+        stream->num_input_frames++;
+    }
+
+    FOREACH_DCC(display, item, next, dcc) {
+        StreamAgent *agent;
+        QRegion clip_in_draw_dest;
+
+        agent = &dcc->stream_agents[get_stream_id(display, stream)];
+        region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
+
+        region_init(&clip_in_draw_dest);
+        region_add(&clip_in_draw_dest, &drawable->red_drawable->bbox);
+        region_and(&clip_in_draw_dest, &agent->clip);
+
+        if (!region_is_equal(&clip_in_draw_dest, &drawable->tree_item.base.rgn)) {
+            region_remove(&agent->clip, &drawable->red_drawable->bbox);
+            region_or(&agent->clip, &drawable->tree_item.base.rgn);
+            dcc_push_stream_agent_clip(dcc, agent);
+        }
+#ifdef STREAM_STATS
+        agent->stats.num_input_frames++;
+#endif
+    }
+}
+
+static void stop_stream(DisplayChannel *display, Stream *stream)
+{
+    DisplayChannelClient *dcc;
+    RingItem *item, *next;
+
+    spice_assert(ring_item_is_linked(&stream->link));
+    spice_assert(!stream->current);
+    spice_debug("stream %d", get_stream_id(display, stream));
+    FOREACH_DCC(display, item, next, dcc) {
+        StreamAgent *stream_agent;
+
+        stream_agent = &dcc->stream_agents[get_stream_id(display, stream)];
+        region_clear(&stream_agent->vis_region);
+        region_clear(&stream_agent->clip);
+        spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));
+        if (stream_agent->mjpeg_encoder && dcc->use_mjpeg_encoder_rate_control) {
+            uint64_t stream_bit_rate = mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder);
+
+            if (stream_bit_rate > dcc->streams_max_bit_rate) {
+                spice_debug("old max-bit-rate=%.2f new=%.2f",
+                dcc->streams_max_bit_rate / 8.0 / 1024.0 / 1024.0,
+                stream_bit_rate / 8.0 / 1024.0 / 1024.0);
+                dcc->streams_max_bit_rate = stream_bit_rate;
+            }
+        }
+        stream->refs++;
+        red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &stream_agent->destroy_item);
+        stream_agent_stats_print(stream_agent);
+    }
+    display->streams_size_total -= stream->width * stream->height;
+    ring_remove(&stream->link);
+    display_stream_unref(display, stream);
+}
+
 /* fixme: move to display channel */
 DrawablePipeItem *drawable_pipe_item_new(DisplayChannelClient *dcc,
                                          Drawable *drawable)
@@ -610,7 +680,7 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker)
     return worker->qxl;
 }
 
-static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id)
+static inline int is_primary_surface(DisplayChannel *display, uint32_t surface_id)
 {
     if (surface_id == 0) {
         return TRUE;
@@ -896,8 +966,6 @@ static void drawables_init(RedWorker *worker)
 }
 
 
-static void red_reset_stream_trace(RedWorker *worker);
-
 static SurfaceDestroyItem *get_surface_destroy_item(RedChannel *channel,
                                                     uint32_t surface_id)
 {
@@ -929,16 +997,36 @@ static inline void red_destroy_surface_item(RedWorker *worker,
     red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item);
 }
 
+static void stop_streams(DisplayChannel *display)
+{
+    Ring *ring = &display->streams;
+    RingItem *item = ring_get_head(ring);
+
+    while (item) {
+        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
+        item = ring_next(ring, item);
+        if (!stream->current) {
+            stop_stream(display, stream);
+        } else {
+            spice_info("attached stream");
+        }
+    }
+
+    display->next_item_trace = 0;
+    memset(display->items_trace, 0, sizeof(display->items_trace));
+}
+
 static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
 {
+    DisplayChannel *display = worker->display_channel;
     RedSurface *surface = &worker->surfaces[surface_id];
     DisplayChannelClient *dcc;
     RingItem *link, *next;
 
     if (!--surface->refs) {
         // only primary surface streams are supported
-        if (is_primary_surface(worker, surface_id)) {
-            red_reset_stream_trace(worker);
+        if (is_primary_surface(worker->display_channel, surface_id)) {
+            stop_streams(display);
         }
         spice_assert(surface->context.canvas);
 
@@ -1025,6 +1113,9 @@ static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
     }
 }
 
+static void detach_stream(DisplayChannel *display, Stream *stream,
+                          int detach_sized);
+
 static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable)
 {
     RingItem *item, *next;
@@ -1036,7 +1127,7 @@ static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable)
     spice_warn_if_fail(ring_is_empty(&drawable->pipes));
 
     if (drawable->stream) {
-        red_detach_stream(worker, drawable->stream, TRUE);
+        detach_stream(worker->display_channel, drawable->stream, TRUE);
     }
     region_destroy(&drawable->tree_item.base.rgn);
 
@@ -1094,14 +1185,15 @@ static inline void container_cleanup(RedWorker *worker, Container *container)
     }
 }
 
-static inline void red_add_item_trace(RedWorker *worker, Drawable *item)
+static void display_stream_trace_add_drawable(DisplayChannel *display, Drawable *item)
 {
     ItemTrace *trace;
-    if (!item->streamable) {
+
+    if (!item->stream || !item->streamable) {
         return;
     }
 
-    trace = &worker->items_trace[worker->next_item_trace++ & ITEMS_TRACE_MASK];
+    trace = &display->items_trace[display->next_item_trace++ & ITEMS_TRACE_MASK];
     trace->time = item->creation_time;
     trace->frames_count = item->frames_count;
     trace->gradual_frames_count = item->gradual_frames_count;
@@ -1136,9 +1228,8 @@ static inline void current_remove_drawable(RedWorker *worker, Drawable *item)
     if (item->tree_item.effect != QXL_EFFECT_OPAQUE) {
         worker->transparent_count--;
     }
-    if (!item->stream) {
-        red_add_item_trace(worker, item);
-    }
+
+    display_stream_trace_add_drawable(worker->display_channel, item);
     remove_shadow(worker, &item->tree_item);
     ring_remove(&item->tree_item.base.siblings_link);
     ring_remove(&item->list_link);
@@ -1366,7 +1457,7 @@ static inline void __exclude_region(RedWorker *worker, Ring *ring, TreeItem *ite
             } else {
                 if (frame_candidate) {
                     Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
-                    red_stream_maintenance(worker, frame_candidate, drawable);
+                    display_channel_stream_maintenance(worker->display_channel, frame_candidate, drawable);
                 }
                 region_exclude(&draw->base.rgn, &and_rgn);
             }
@@ -1556,22 +1647,8 @@ static int is_same_drawable(RedWorker *worker, Drawable *d1, Drawable *d2)
     }
 }
 
-static inline void red_free_stream(RedWorker *worker, Stream *stream)
-{
-    stream->next = worker->free_streams;
-    worker->free_streams = stream;
-}
-
-static void red_release_stream(RedWorker *worker, Stream *stream)
-{
-    if (!--stream->refs) {
-        spice_assert(!ring_item_is_linked(&stream->link));
-        red_free_stream(worker, stream);
-        worker->stream_count--;
-    }
-}
-
-static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized)
+static void detach_stream(DisplayChannel *display, Stream *stream,
+                          int detach_sized)
 {
     spice_assert(stream->current && stream->current->stream);
     spice_assert(stream->current->stream == stream);
@@ -1582,164 +1659,6 @@ static inline void red_detach_stream(RedWorker *worker, Stream *stream, int deta
     stream->current = NULL;
 }
 
-static StreamClipItem *__new_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
-{
-    StreamClipItem *item = spice_new(StreamClipItem, 1);
-    red_channel_pipe_item_init(RED_CHANNEL_CLIENT(dcc)->channel,
-                    (PipeItem *)item, PIPE_ITEM_TYPE_STREAM_CLIP);
-
-    item->stream_agent = agent;
-    agent->stream->refs++;
-    item->refs = 1;
-    return item;
-}
-
-static void push_stream_clip(DisplayChannelClient* dcc, StreamAgent *agent)
-{
-    StreamClipItem *item = __new_stream_clip(dcc, agent);
-    int n_rects;
-
-    if (!item) {
-        spice_critical("alloc failed");
-    }
-    item->clip_type = SPICE_CLIP_TYPE_RECTS;
-
-    n_rects = pixman_region32_n_rects(&agent->clip);
-
-    item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
-    item->rects->num_rects = n_rects;
-    region_ret_rects(&agent->clip, item->rects->rects, n_rects);
-    red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item);
-}
-
-static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item)
-{
-    if (!--item->refs) {
-        red_display_release_stream(worker, item->stream_agent);
-        free(item->rects);
-        free(item);
-    }
-}
-
-static inline int get_stream_id(RedWorker *worker, Stream *stream)
-{
-    return (int)(stream - worker->streams_buf);
-}
-
-static void red_attach_stream(RedWorker *worker, Drawable *drawable, Stream *stream)
-{
-    DisplayChannelClient *dcc;
-    RingItem *item, *next;
-
-    spice_assert(!drawable->stream && !stream->current);
-    spice_assert(drawable && stream);
-    stream->current = drawable;
-    drawable->stream = stream;
-    stream->last_time = drawable->creation_time;
-
-    uint64_t duration = drawable->creation_time - stream->input_fps_start_time;
-    if (duration >= RED_STREAM_INPUT_FPS_TIMEOUT) {
-        /* Round to the nearest integer, for instance 24 for 23.976 */
-        stream->input_fps = ((uint64_t)stream->num_input_frames * 1000 * 1000 * 1000 + duration / 2) / duration;
-        spice_debug("input-fps=%u", stream->input_fps);
-        stream->num_input_frames = 0;
-        stream->input_fps_start_time = drawable->creation_time;
-    } else {
-        stream->num_input_frames++;
-    }
-
-    FOREACH_DCC(worker->display_channel, item, next, dcc) {
-        StreamAgent *agent;
-        QRegion clip_in_draw_dest;
-
-        agent = &dcc->stream_agents[get_stream_id(worker, stream)];
-        region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
-
-        region_init(&clip_in_draw_dest);
-        region_add(&clip_in_draw_dest, &drawable->red_drawable->bbox);
-        region_and(&clip_in_draw_dest, &agent->clip);
-
-        if (!region_is_equal(&clip_in_draw_dest, &drawable->tree_item.base.rgn)) {
-            region_remove(&agent->clip, &drawable->red_drawable->bbox);
-            region_or(&agent->clip, &drawable->tree_item.base.rgn);
-            push_stream_clip(dcc, agent);
-        }
-#ifdef STREAM_STATS
-        agent->stats.num_input_frames++;
-#endif
-    }
-}
-
-static void red_print_stream_stats(DisplayChannelClient *dcc, StreamAgent *agent)
-{
-#ifdef STREAM_STATS
-    StreamStats *stats = &agent->stats;
-    double passed_mm_time = (stats->end - stats->start) / 1000.0;
-    MJpegEncoderStats encoder_stats = {0};
-
-    if (agent->mjpeg_encoder) {
-        mjpeg_encoder_get_stats(agent->mjpeg_encoder, &encoder_stats);
-    }
-
-    spice_debug("stream=%"PRIdPTR" dim=(%dx%d) #in-frames=%"PRIu64" #in-avg-fps=%.2f #out-frames=%"PRIu64" "
-                "out/in=%.2f #drops=%"PRIu64" (#pipe=%"PRIu64" #fps=%"PRIu64") out-avg-fps=%.2f "
-                "passed-mm-time(sec)=%.2f size-total(MB)=%.2f size-per-sec(Mbps)=%.2f "
-                "size-per-frame(KBpf)=%.2f avg-quality=%.2f "
-                "start-bit-rate(Mbps)=%.2f end-bit-rate(Mbps)=%.2f",
-                agent - dcc->stream_agents, agent->stream->width, agent->stream->height,
-                stats->num_input_frames,
-                stats->num_input_frames / passed_mm_time,
-                stats->num_frames_sent,
-                (stats->num_frames_sent + 0.0) / stats->num_input_frames,
-                stats->num_drops_pipe +
-                stats->num_drops_fps,
-                stats->num_drops_pipe,
-                stats->num_drops_fps,
-                stats->num_frames_sent / passed_mm_time,
-                passed_mm_time,
-                stats->size_sent / 1024.0 / 1024.0,
-                ((stats->size_sent * 8.0) / (1024.0 * 1024)) / passed_mm_time,
-                stats->size_sent / 1000.0 / stats->num_frames_sent,
-                encoder_stats.avg_quality,
-                encoder_stats.starting_bit_rate / (1024.0 * 1024),
-                encoder_stats.cur_bit_rate / (1024.0 * 1024));
-#endif
-}
-
-static void red_stop_stream(RedWorker *worker, Stream *stream)
-{
-    DisplayChannelClient *dcc;
-    RingItem *item, *next;
-
-    spice_assert(ring_item_is_linked(&stream->link));
-    spice_assert(!stream->current);
-    spice_debug("stream %d", get_stream_id(worker, stream));
-    FOREACH_DCC(worker->display_channel, item, next, dcc) {
-        StreamAgent *stream_agent;
-
-        stream_agent = &dcc->stream_agents[get_stream_id(worker, stream)];
-        region_clear(&stream_agent->vis_region);
-        region_clear(&stream_agent->clip);
-        spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));
-        if (stream_agent->mjpeg_encoder && dcc->use_mjpeg_encoder_rate_control) {
-            uint64_t stream_bit_rate = mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder);
-
-            if (stream_bit_rate > dcc->streams_max_bit_rate) {
-                spice_debug("old max-bit-rate=%.2f new=%.2f",
-                dcc->streams_max_bit_rate / 8.0 / 1024.0 / 1024.0,
-                stream_bit_rate / 8.0 / 1024.0 / 1024.0);
-                dcc->streams_max_bit_rate = stream_bit_rate;
-            }
-        }
-        stream->refs++;
-        red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &stream_agent->destroy_item);
-        red_print_stream_stats(dcc, stream_agent);
-    }
-    worker->streams_size_total -= stream->width * stream->height;
-    ring_remove(&stream->link);
-    red_release_stream(worker, stream);
-}
-
 static int red_display_drawable_is_in_pipe(DisplayChannelClient *dcc, Drawable *drawable)
 {
     DrawablePipeItem *dpi;
@@ -1756,18 +1675,19 @@ static int red_display_drawable_is_in_pipe(DisplayChannelClient *dcc, Drawable *
 
 /*
  * after red_display_detach_stream_gracefully is called for all the display channel clients,
- * red_detach_stream should be called. See comment (1).
+ * detach_stream should be called. See comment (1).
  */
-static inline void red_display_detach_stream_gracefully(DisplayChannelClient *dcc,
-                                                        Stream *stream,
-                                                        Drawable *update_area_limit)
+static void dcc_detach_stream_gracefully(DisplayChannelClient *dcc,
+                                         Stream *stream,
+                                         Drawable *update_area_limit)
 {
-    int stream_id = get_stream_id(DCC_TO_WORKER(dcc), stream);
+    DisplayChannel *display = DCC_TO_DC(dcc);
+    int stream_id = get_stream_id(display, stream);
     StreamAgent *agent = &dcc->stream_agents[stream_id];
 
     /* stopping the client from playing older frames at once*/
     region_clear(&agent->clip);
-    push_stream_clip(dcc, agent);
+    dcc_push_stream_agent_clip(dcc, agent);
 
     if (region_is_empty(&agent->vis_region)) {
         spice_debug("stream %d: vis region empty", stream_id);
@@ -1825,17 +1745,17 @@ clear_vis_region:
     region_clear(&agent->vis_region);
 }
 
-static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *stream,
-                                                Drawable *update_area_limit)
+static void detach_stream_gracefully(DisplayChannel *display, Stream *stream,
+                                     Drawable *update_area_limit)
 {
     RingItem *item, *next;
     DisplayChannelClient *dcc;
 
-    FOREACH_DCC(worker->display_channel, item, next, dcc) {
-        red_display_detach_stream_gracefully(dcc, stream, update_area_limit);
+    FOREACH_DCC(display, item, next, dcc) {
+        dcc_detach_stream_gracefully(dcc, stream, update_area_limit);
     }
     if (stream->current) {
-        red_detach_stream(worker, stream, TRUE);
+        detach_stream(display, stream, TRUE);
     }
 }
 
@@ -1849,55 +1769,55 @@ static inline void red_detach_stream_gracefully(RedWorker *worker, Stream *strea
  *           involves sending an upgrade image to the client, this drawable won't be rendered
  *           (see red_display_detach_stream_gracefully).
  */
-static void red_detach_streams_behind(RedWorker *worker, QRegion *region, Drawable *drawable)
+static void detach_streams_behind(DisplayChannel *display, QRegion *region, Drawable *drawable)
 {
-    Ring *ring = &worker->streams;
+    Ring *ring = &display->streams;
     RingItem *item = ring_get_head(ring);
     RingItem *dcc_ring_item, *next;
     DisplayChannelClient *dcc;
-    int has_clients = display_is_connected(worker);
+    bool is_connected = red_channel_is_connected(RED_CHANNEL(display));
 
     while (item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        int detach_stream = 0;
+        int detach = 0;
         item = ring_next(ring, item);
 
-        FOREACH_DCC(worker->display_channel, dcc_ring_item, next, dcc) {
-            StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
+            StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)];
 
             if (region_intersects(&agent->vis_region, region)) {
-                red_display_detach_stream_gracefully(dcc, stream, drawable);
-                detach_stream = 1;
-                spice_debug("stream %d", get_stream_id(worker, stream));
+                dcc_detach_stream_gracefully(dcc, stream, drawable);
+                detach = 1;
+                spice_debug("stream %d", get_stream_id(display, stream));
             }
         }
-        if (detach_stream && stream->current) {
-            red_detach_stream(worker, stream, TRUE);
-        } else if (!has_clients) {
+        if (detach && stream->current) {
+            detach_stream(display, stream, TRUE);
+        } else if (!is_connected) {
             if (stream->current &&
                 region_intersects(&stream->current->tree_item.base.rgn, region)) {
-                red_detach_stream(worker, stream, TRUE);
+                detach_stream(display, stream, TRUE);
             }
         }
     }
 }
 
-static void red_streams_update_visible_region(RedWorker *worker, Drawable *drawable)
+static void streams_update_visible_region(DisplayChannel *display, Drawable *drawable)
 {
     Ring *ring;
     RingItem *item;
     RingItem *dcc_ring_item, *next;
     DisplayChannelClient *dcc;
 
-    if (!display_is_connected(worker)) {
+    if (!red_channel_is_connected(RED_CHANNEL(display))) {
         return;
     }
 
-    if (!is_primary_surface(worker, drawable->surface_id)) {
+    if (!is_primary_surface(display, drawable->surface_id)) {
         return;
     }
 
-    ring = &worker->streams;
+    ring = &display->streams;
     item = ring_get_head(ring);
 
     while (item) {
@@ -1910,42 +1830,21 @@ static void red_streams_update_visible_region(RedWorker *worker, Drawable *drawa
             continue;
         }
 
-        FOREACH_DCC(worker->display_channel, dcc_ring_item, next, dcc) {
-            agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+        FOREACH_DCC(display, dcc_ring_item, next, dcc) {
+            agent = &dcc->stream_agents[get_stream_id(display, stream)];
 
             if (region_intersects(&agent->vis_region, &drawable->tree_item.base.rgn)) {
                 region_exclude(&agent->vis_region, &drawable->tree_item.base.rgn);
                 region_exclude(&agent->clip, &drawable->tree_item.base.rgn);
-                push_stream_clip(dcc, agent);
+                dcc_push_stream_agent_clip(dcc, agent);
             }
         }
     }
 }
 
-static inline unsigned int red_get_streams_timout(RedWorker *worker)
+static void display_channel_streams_timeout(DisplayChannel *display)
 {
-    unsigned int timout = -1;
-    Ring *ring = &worker->streams;
-    RingItem *item = ring;
-
-    red_time_t now = red_get_monotonic_time();
-    while ((item = ring_next(ring, item))) {
-        Stream *stream;
-
-        stream = SPICE_CONTAINEROF(item, Stream, link);
-        red_time_t delta = (stream->last_time + RED_STREAM_TIMOUT) - now;
-
-        if (delta < 1000 * 1000) {
-            return 0;
-        }
-        timout = MIN(timout, (unsigned int)(delta / (1000 * 1000)));
-    }
-    return timout;
-}
-
-static inline void red_handle_streams_timout(RedWorker *worker)
-{
-    Ring *ring = &worker->streams;
+    Ring *ring = &display->streams;
     RingItem *item;
 
     red_time_t now = red_get_monotonic_time();
@@ -1953,27 +1852,21 @@ static inline void red_handle_streams_timout(RedWorker *worker)
     while (item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
         item = ring_next(ring, item);
-        if (now >= (stream->last_time + RED_STREAM_TIMOUT)) {
-            red_detach_stream_gracefully(worker, stream, NULL);
-            red_stop_stream(worker, stream);
+        if (now >= (stream->last_time + RED_STREAM_TIMEOUT)) {
+            detach_stream_gracefully(display, stream, NULL);
+            stop_stream(display, stream);
         }
     }
 }
 
-static void red_display_release_stream(RedWorker *worker, StreamAgent *agent)
-{
-    spice_assert(agent->stream);
-    red_release_stream(worker, agent->stream);
-}
-
-static inline Stream *red_alloc_stream(RedWorker *worker)
+static Stream *display_channel_stream_try_new(DisplayChannel *display)
 {
     Stream *stream;
-    if (!worker->free_streams) {
+    if (!display->free_streams) {
         return NULL;
     }
-    stream = worker->free_streams;
-    worker->free_streams = worker->free_streams->next;
+    stream = display->free_streams;
+    display->free_streams = display->free_streams->next;
     return stream;
 }
 
@@ -2022,7 +1915,7 @@ static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc,
     /* dividing the available bandwidth among the active streams, and saving
      * (1-RED_STREAM_CHANNEL_CAPACITY) of it for other messages */
     return (RED_STREAM_CHANNEL_CAPACITY * bit_rate *
-            stream->width * stream->height) / DCC_TO_WORKER(dcc)->streams_size_total;
+            stream->width * stream->height) / DCC_TO_DC(dcc)->streams_size_total;
 }
 
 static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque)
@@ -2064,7 +1957,7 @@ static void red_display_update_streams_max_latency(DisplayChannelClient *dcc, St
     }
 
     dcc->streams_max_latency = 0;
-    if (DCC_TO_WORKER(dcc)->stream_count == 1) {
+    if (DCC_TO_DC(dcc)->stream_count == 1) {
         return;
     }
     for (i = 0; i < NUM_STREAMS; i++) {
@@ -2103,9 +1996,9 @@ static void red_stream_update_client_playback_latency(void *opaque, uint32_t del
     main_dispatcher_set_mm_time_latency(RED_CHANNEL_CLIENT(agent->dcc)->client, agent->dcc->streams_max_latency);
 }
 
-static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
+static void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream)
 {
-    StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_WORKER(dcc), stream)];
+    StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_DC(dcc), stream)];
 
     stream->refs++;
     spice_assert(region_is_empty(&agent->vis_region));
@@ -2141,7 +2034,7 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
         agent->report_id = rand();
         red_channel_pipe_item_init(RED_CHANNEL_CLIENT(dcc)->channel, &report_pipe_item->pipe_item,
                                    PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT);
-        report_pipe_item->stream_id = get_stream_id(DCC_TO_WORKER(dcc), stream);
+        report_pipe_item->stream_id = get_stream_id(DCC_TO_DC(dcc), stream);
         red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &report_pipe_item->pipe_item);
     }
 #ifdef STREAM_STATS
@@ -2152,7 +2045,7 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
 #endif
 }
 
-static void red_create_stream(RedWorker *worker, Drawable *drawable)
+static void display_channel_create_stream(DisplayChannel *display, Drawable *drawable)
 {
     DisplayChannelClient *dcc;
     RingItem *dcc_ring_item, *next;
@@ -2161,14 +2054,14 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
 
     spice_assert(!drawable->stream);
 
-    if (!(stream = red_alloc_stream(worker))) {
+    if (!(stream = display_channel_stream_try_new(display))) {
         return;
     }
 
     spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
     src_rect = &drawable->red_drawable->u.copy.src_area;
 
-    ring_add(&worker->streams, &stream->link);
+    ring_add(&display->streams, &stream->link);
     stream->current = drawable;
     stream->last_time = drawable->creation_time;
     stream->width = src_rect->right - src_rect->left;
@@ -2181,37 +2074,38 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
     stream->input_fps = MAX_FPS;
     stream->num_input_frames = 0;
     stream->input_fps_start_time = drawable->creation_time;
-    worker->streams_size_total += stream->width * stream->height;
-    worker->stream_count++;
-    FOREACH_DCC(worker->display_channel, dcc_ring_item, next, dcc) {
-        red_display_create_stream(dcc, stream);
+    display->streams_size_total += stream->width * stream->height;
+    display->stream_count++;
+    FOREACH_DCC(display, dcc_ring_item, next, dcc) {
+        dcc_create_stream(dcc, stream);
     }
-    spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", (int)(stream - worker->streams_buf), stream->width,
+    spice_debug("stream %d %dx%d (%d, %d) (%d, %d)",
+                (int)(stream - display->streams_buf), stream->width,
                 stream->height, stream->dest_area.left, stream->dest_area.top,
                 stream->dest_area.right, stream->dest_area.bottom);
     return;
 }
 
-static void red_disply_start_streams(DisplayChannelClient *dcc)
+static void dcc_create_all_streams(DisplayChannelClient *dcc)
 {
-    Ring *ring = &DCC_TO_WORKER(dcc)->streams;
+    Ring *ring = &DCC_TO_DC(dcc)->streams;
     RingItem *item = ring;
 
     while ((item = ring_next(ring, item))) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        red_display_create_stream(dcc, stream);
+        dcc_create_stream(dcc, stream);
     }
 }
 
-static void red_display_client_init_streams(DisplayChannelClient *dcc)
+static void dcc_init_stream_agents(DisplayChannelClient *dcc)
 {
     int i;
-    RedWorker *worker = DCC_TO_WORKER(dcc);
+    DisplayChannel *display = DCC_TO_DC(dcc);
     RedChannel *channel = RED_CHANNEL_CLIENT(dcc)->channel;
 
     for (i = 0; i < NUM_STREAMS; i++) {
         StreamAgent *agent = &dcc->stream_agents[i];
-        agent->stream = &worker->streams_buf[i];
+        agent->stream = &display->streams_buf[i];
         region_init(&agent->vis_region);
         region_init(&agent->clip);
         red_channel_pipe_item_init(channel, &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);
@@ -2221,7 +2115,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)
         red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_STREAM_REPORT);
 }
 
-static void red_display_destroy_streams_agents(DisplayChannelClient *dcc)
+static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
 {
     int i;
 
@@ -2236,27 +2130,14 @@ static void red_display_destroy_streams_agents(DisplayChannelClient *dcc)
     }
 }
 
-static void red_init_streams(RedWorker *worker)
-{
-    int i;
-
-    ring_init(&worker->streams);
-    worker->free_streams = NULL;
-    for (i = 0; i < NUM_STREAMS; i++) {
-        Stream *stream = &worker->streams_buf[i];
-        ring_item_init(&stream->link);
-        red_free_stream(worker, stream);
-    }
-}
-
-static inline int __red_is_next_stream_frame(RedWorker *worker,
-                                             const Drawable *candidate,
-                                             const int other_src_width,
-                                             const int other_src_height,
-                                             const SpiceRect *other_dest,
-                                             const red_time_t other_time,
-                                             const Stream *stream,
-                                             int container_candidate_allowed)
+static int is_next_stream_frame(DisplayChannel *display,
+                                const Drawable *candidate,
+                                const int other_src_width,
+                                const int other_src_height,
+                                const SpiceRect *other_dest,
+                                const red_time_t other_time,
+                                const Stream *stream,
+                                int container_candidate_allowed)
 {
     RedDrawable *red_drawable;
     int is_frame_container = FALSE;
@@ -2319,22 +2200,8 @@ static inline int __red_is_next_stream_frame(RedWorker *worker,
     }
 }
 
-static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *candidate,
-                                           const Drawable *prev)
-{
-    if (!candidate->streamable) {
-        return FALSE;
-    }
-
-    SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area;
-    return __red_is_next_stream_frame(worker, candidate, prev_src->right - prev_src->left,
-                                      prev_src->bottom - prev_src->top,
-                                      &prev->red_drawable->bbox, prev->creation_time,
-                                      prev->stream,
-                                      FALSE);
-}
-
-static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawable *new_frame)
+static void before_reattach_stream(DisplayChannel *display,
+                                   Stream *stream, Drawable *new_frame)
 {
     DrawablePipeItem *dpi;
     DisplayChannelClient *dcc;
@@ -2342,9 +2209,9 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa
     StreamAgent *agent;
     RingItem *ring_item, *next;
 
-    spice_assert(stream->current);
+    spice_return_if_fail(stream->current);
 
-    if (!display_is_connected(worker)) {
+    if (!red_channel_is_connected(RED_CHANNEL(display))) {
         return;
     }
 
@@ -2353,7 +2220,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa
         return;
     }
 
-    index = get_stream_id(worker, stream);
+    index = get_stream_id(display, stream);
     DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
         dcc = dpi->dcc;
         agent = &dcc->stream_agents[index];
@@ -2376,7 +2243,7 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa
     }
 
 
-    FOREACH_DCC(worker->display_channel, ring_item, next, dcc) {
+    FOREACH_DCC(display, ring_item, next, dcc) {
         double drop_factor;
 
         agent = &dcc->stream_agents[index];
@@ -2407,12 +2274,13 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream, Drawa
     }
 }
 
-static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawable)
+static void update_copy_graduality(Drawable *drawable)
 {
     SpiceBitmap *bitmap;
-    spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
+    spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
 
-    if (worker->streaming_video != SPICE_STREAM_VIDEO_FILTER) {
+    /* TODO: global property -> per dc/dcc */
+    if (streaming_video != SPICE_STREAM_VIDEO_FILTER) {
         drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
         return;
     }
@@ -2431,7 +2299,7 @@ static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawa
     }
 }
 
-static inline int red_is_stream_start(Drawable *drawable)
+static int is_stream_start(Drawable *drawable)
 {
     return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) &&
             (drawable->gradual_frames_count >=
@@ -2439,13 +2307,13 @@ static inline int red_is_stream_start(Drawable *drawable)
 }
 
 // returns whether a stream was created
-static int red_stream_add_frame(RedWorker *worker,
-                                Drawable *frame_drawable,
-                                int frames_count,
-                                int gradual_frames_count,
-                                int last_gradual_frame)
+static int display_channel_stream_add_frame(DisplayChannel *display,
+                                            Drawable *frame_drawable,
+                                            int frames_count,
+                                            int gradual_frames_count,
+                                            int last_gradual_frame)
 {
-    red_update_copy_graduality(worker, frame_drawable);
+    update_copy_graduality(frame_drawable);
     frame_drawable->frames_count = frames_count + 1;
     frame_drawable->gradual_frames_count  = gradual_frames_count;
 
@@ -2463,45 +2331,52 @@ static int red_stream_add_frame(RedWorker *worker,
         frame_drawable->last_gradual_frame = last_gradual_frame;
     }
 
-    if (red_is_stream_start(frame_drawable)) {
-        red_create_stream(worker, frame_drawable);
+    if (is_stream_start(frame_drawable)) {
+        display_channel_create_stream(display, frame_drawable);
         return TRUE;
     }
     return FALSE;
 }
 
-static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *prev)
+static void display_channel_stream_maintenance(DisplayChannel *display,
+                                               Drawable *candidate, Drawable *prev)
 {
-    Stream *stream;
+    int is_next_frame;
 
     if (candidate->stream) {
         return;
     }
 
-    if ((stream = prev->stream)) {
-        int is_next_frame = __red_is_next_stream_frame(worker,
-                                                       candidate,
-                                                       stream->width,
-                                                       stream->height,
-                                                       &stream->dest_area,
-                                                       stream->last_time,
-                                                       stream,
-                                                       TRUE);
+    if (prev->stream) {
+        Stream *stream = prev->stream;
+
+        is_next_frame = is_next_stream_frame(display, candidate,
+                                             stream->width, stream->height,
+                                             &stream->dest_area, stream->last_time,
+                                             stream, TRUE);
         if (is_next_frame != STREAM_FRAME_NONE) {
-            pre_stream_item_swap(worker, stream, candidate);
-            red_detach_stream(worker, stream, FALSE);
+            before_reattach_stream(display, stream, candidate);
+            detach_stream(display, stream, FALSE);
             prev->streamable = FALSE; //prevent item trace
-            red_attach_stream(worker, candidate, stream);
+            attach_stream(display, candidate, stream);
             if (is_next_frame == STREAM_FRAME_CONTAINER) {
                 candidate->sized_stream = stream;
             }
         }
-    } else {
-        if (red_is_next_stream_frame(worker, candidate, prev) != STREAM_FRAME_NONE) {
-            red_stream_add_frame(worker, candidate,
-                                 prev->frames_count,
-                                 prev->gradual_frames_count,
-                                 prev->last_gradual_frame);
+    } else if (candidate->streamable) {
+        SpiceRect* prev_src = &prev->red_drawable->u.copy.src_area;
+
+        is_next_frame =
+            is_next_stream_frame(display, candidate, prev_src->right - prev_src->left,
+                                 prev_src->bottom - prev_src->top,
+                                 &prev->red_drawable->bbox, prev->creation_time,
+                                 prev->stream,
+                                 FALSE);
+        if (is_next_frame != STREAM_FRAME_NONE) {
+            display_channel_stream_add_frame(display, candidate,
+                                             prev->frames_count,
+                                             prev->gradual_frames_count,
+                                             prev->last_gradual_frame);
         }
     }
 }
@@ -2539,7 +2414,7 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
     if (item->effect == QXL_EFFECT_OPAQUE) {
         int add_after = !!other_drawable->stream &&
                         is_drawable_independent_from_surfaces(drawable);
-        red_stream_maintenance(worker, drawable, other_drawable);
+        display_channel_stream_maintenance(worker->display_channel, drawable, other_drawable);
         __current_add_drawable(worker, drawable, &other->siblings_link);
         other_drawable->refs++;
         current_remove_drawable(worker, other_drawable);
@@ -2613,81 +2488,61 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI
     return FALSE;
 }
 
-static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable)
+#define FOREACH_STREAMS(display, item)                  \
+    for (item = ring_get_head(&(display)->streams);     \
+         item != NULL;                                  \
+         item = ring_next(&(display)->streams, item))
+
+static void red_use_stream_trace(DisplayChannel *display, Drawable *drawable)
 {
     ItemTrace *trace;
     ItemTrace *trace_end;
-    Ring *ring;
     RingItem *item;
 
     if (drawable->stream || !drawable->streamable || drawable->frames_count) {
         return;
     }
 
-
-    ring = &worker->streams;
-    item = ring_get_head(ring);
-
-    while (item) {
+    FOREACH_STREAMS(display, item) {
         Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        int is_next_frame = __red_is_next_stream_frame(worker,
-                                                       drawable,
-                                                       stream->width,
-                                                       stream->height,
-                                                       &stream->dest_area,
-                                                       stream->last_time,
-                                                       stream,
-                                                       TRUE);
+        int is_next_frame = is_next_stream_frame(display,
+                                                 drawable,
+                                                 stream->width,
+                                                 stream->height,
+                                                 &stream->dest_area,
+                                                 stream->last_time,
+                                                 stream,
+                                                 TRUE);
         if (is_next_frame != STREAM_FRAME_NONE) {
             if (stream->current) {
                 stream->current->streamable = FALSE; //prevent item trace
-                pre_stream_item_swap(worker, stream, drawable);
-                red_detach_stream(worker, stream, FALSE);
+                before_reattach_stream(display, stream, drawable);
+                detach_stream(display, stream, FALSE);
             }
-            red_attach_stream(worker, drawable, stream);
+            attach_stream(display, drawable, stream);
             if (is_next_frame == STREAM_FRAME_CONTAINER) {
                 drawable->sized_stream = stream;
             }
             return;
         }
-        item = ring_next(ring, item);
     }
 
-    trace = worker->items_trace;
+    trace = display->items_trace;
     trace_end = trace + NUM_TRACE_ITEMS;
     for (; trace < trace_end; trace++) {
-        if (__red_is_next_stream_frame(worker, drawable, trace->width, trace->height,
+        if (is_next_stream_frame(display, drawable, trace->width, trace->height,
                                        &trace->dest_area, trace->time, NULL, FALSE) !=
                                        STREAM_FRAME_NONE) {
-            if (red_stream_add_frame(worker, drawable,
-                                     trace->frames_count,
-                                     trace->gradual_frames_count,
-                                     trace->last_gradual_frame)) {
+            if (display_channel_stream_add_frame(display, drawable,
+                                                 trace->frames_count,
+                                                 trace->gradual_frames_count,
+                                                 trace->last_gradual_frame)) {
                 return;
             }
         }
     }
 }
 
-static void red_reset_stream_trace(RedWorker *worker)
-{
-    Ring *ring = &worker->streams;
-    RingItem *item = ring_get_head(ring);
-
-    while (item) {
-        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        item = ring_next(ring, item);
-        if (!stream->current) {
-            red_stop_stream(worker, stream);
-        } else {
-            spice_info("attached stream");
-        }
-    }
-
-    worker->next_item_trace = 0;
-    memset(worker->items_trace, 0, sizeof(worker->items_trace));
-}
-
 static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
@@ -2784,8 +2639,8 @@ static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawa
     if (item->effect == QXL_EFFECT_OPAQUE) {
         region_or(&exclude_rgn, &item->base.rgn);
         exclude_region(worker, ring, exclude_base, &exclude_rgn, NULL, drawable);
-        red_use_stream_trace(worker, drawable);
-        red_streams_update_visible_region(worker, drawable);
+        red_use_stream_trace(worker->display_channel, drawable);
+        streams_update_visible_region(worker->display_channel, drawable);
         /*
          * Performing the insertion after exclude_region for
          * safety (todo: Not sure if exclude_region can affect the drawable
@@ -2799,8 +2654,8 @@ static inline int red_current_add(RedWorker *worker, Ring *ring, Drawable *drawa
          * before calling red_detach_streams_behind
          */
         __current_add_drawable(worker, drawable, ring);
-        if (is_primary_surface(worker, drawable->surface_id)) {
-            red_detach_streams_behind(worker, &drawable->tree_item.base.rgn, drawable);
+        if (is_primary_surface(worker->display_channel, drawable->surface_id)) {
+            detach_streams_behind(worker->display_channel, &drawable->tree_item.base.rgn, drawable);
         }
     }
     region_destroy(&exclude_rgn);
@@ -2819,6 +2674,7 @@ static void add_clip_rects(QRegion *rgn, SpiceClipRects *data)
 
 static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Drawable *item)
 {
+    DisplayChannel *display = worker->display_channel;
 #ifdef RED_WORKER_STAT
     stat_time_t start_time = stat_now(worker);
     ++worker->add_with_shadow_count;
@@ -2840,8 +2696,8 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Dra
     // for now putting them on root.
 
     // only primary surface streams are supported
-    if (is_primary_surface(worker, item->surface_id)) {
-        red_detach_streams_behind(worker, &shadow->base.rgn, NULL);
+    if (is_primary_surface(display, item->surface_id)) {
+        detach_streams_behind(display, &shadow->base.rgn, NULL);
     }
 
     ring_add(ring, &shadow->base.siblings_link);
@@ -2851,10 +2707,10 @@ static inline int red_current_add_with_shadow(RedWorker *worker, Ring *ring, Dra
         region_clone(&exclude_rgn, &item->tree_item.base.rgn);
         exclude_region(worker, ring, &shadow->base.siblings_link, &exclude_rgn, NULL, NULL);
         region_destroy(&exclude_rgn);
-        red_streams_update_visible_region(worker, item);
+        streams_update_visible_region(display, item);
     } else {
-        if (is_primary_surface(worker, item->surface_id)) {
-            red_detach_streams_behind(worker, &item->tree_item.base.rgn, item);
+        if (is_primary_surface(display, item->surface_id)) {
+            detach_streams_behind(display, &item->tree_item.base.rgn, item);
         }
     }
     stat_add(&worker->add_stat, start_time);
@@ -2866,22 +2722,22 @@ static inline int has_shadow(RedDrawable *drawable)
     return drawable->type == QXL_COPY_BITS;
 }
 
-static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
-                                         RedDrawable *red_drawable)
+static void drawable_update_streamable(DisplayChannel *display, Drawable *drawable)
 {
+    RedDrawable *red_drawable = drawable->red_drawable;
     SpiceImage *image;
 
-    if (worker->streaming_video == SPICE_STREAM_VIDEO_OFF) {
+    if (display->stream_video == SPICE_STREAM_VIDEO_OFF) {
         return;
     }
 
-    if (!is_primary_surface(worker, drawable->surface_id)) {
+    if (!is_primary_surface(display, drawable->surface_id)) {
         return;
     }
 
     if (drawable->tree_item.effect != QXL_EFFECT_OPAQUE ||
-                                        red_drawable->type != QXL_DRAW_COPY ||
-                                        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
+        red_drawable->type != QXL_DRAW_COPY ||
+        red_drawable->u.copy.rop_descriptor != SPICE_ROPD_OP_PUT) {
         return;
     }
 
@@ -2891,7 +2747,7 @@ static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
         return;
     }
 
-    if (worker->streaming_video == SPICE_STREAM_VIDEO_FILTER) {
+    if (display->stream_video == SPICE_STREAM_VIDEO_FILTER) {
         SpiceRect* rect;
         int size;
 
@@ -2935,6 +2791,7 @@ static void red_print_stats(RedWorker *worker)
 
 static int red_add_drawable(RedWorker *worker, Drawable *drawable)
 {
+    DisplayChannel *display = worker->display_channel;
     int ret = FALSE, surface_id = drawable->surface_id;
     RedDrawable *red_drawable = drawable->red_drawable;
     Ring *ring = &worker->surfaces[surface_id].current;
@@ -2942,7 +2799,7 @@ static int red_add_drawable(RedWorker *worker, Drawable *drawable)
     if (has_shadow(red_drawable)) {
         ret = red_current_add_with_shadow(worker, ring, drawable);
     } else {
-        red_update_streamable(worker, drawable, red_drawable);
+        drawable_update_streamable(display, drawable);
         ret = red_current_add(worker, ring, drawable);
     }
 
@@ -2995,6 +2852,7 @@ static int rgb32_data_has_alpha(int width, int height, size_t stride,
 
 static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
 {
+    DisplayChannel *display = worker->display_channel;
     SpiceImage *image;
     int32_t width;
     int32_t height;
@@ -3040,7 +2898,7 @@ static inline int red_handle_self_bitmap(RedWorker *worker, Drawable *drawable)
 
     /* For 32bit non-primary surfaces we need to keep any non-zero
        high bytes as the surface may be used as source to an alpha_blend */
-    if (!is_primary_surface(worker, drawable->surface_id) &&
+    if (!is_primary_surface(display, drawable->surface_id) &&
         image->u.bitmap.format == SPICE_BITMAP_FMT_32BIT &&
         rgb32_data_has_alpha(width, height, dest_stride, dest, &all_set)) {
         if (all_set) {
@@ -3158,6 +3016,7 @@ static inline void add_to_surface_dependency(RedWorker *worker, int depend_on_su
 
 static inline int red_handle_surfaces_dependencies(RedWorker *worker, Drawable *drawable)
 {
+    DisplayChannel *display = worker->display_channel;
     int x;
 
     for (x = 0; x < 3; ++x) {
@@ -3171,7 +3030,7 @@ static inline int red_handle_surfaces_dependencies(RedWorker *worker, Drawable *
                 QRegion depend_region;
                 region_init(&depend_region);
                 region_add(&depend_region, &drawable->red_drawable->surfaces_rects[x]);
-                red_detach_streams_behind(worker, &depend_region, NULL);
+                detach_streams_behind(display, &depend_region, NULL);
             }
         }
     }
@@ -3910,9 +3769,9 @@ static void red_current_flush(RedWorker *worker, int surface_id)
 static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
                                              SpiceRect *area, PipeItem *pos, int can_lossy)
 {
-    DisplayChannel *display_channel = DCC_TO_DC(dcc);
-    RedWorker *worker = display_channel->common.worker;
-    RedChannel *channel = RED_CHANNEL(display_channel);
+    DisplayChannel *display = DCC_TO_DC(dcc);
+    RedChannel *channel = RED_CHANNEL(display);
+    RedWorker *worker = DCC_TO_WORKER(dcc);
     RedSurface *surface = &worker->surfaces[surface_id];
     SpiceCanvas *canvas = surface->context.canvas;
     ImageItem *item;
@@ -3950,7 +3809,7 @@ static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surf
 
     /* For 32bit non-primary surfaces we need to keep any non-zero
        high bytes as the surface may be used as source to an alpha_blend */
-    if (!is_primary_surface(worker, surface_id) &&
+    if (!is_primary_surface(display, surface_id) &&
         item->image_format == SPICE_BITMAP_FMT_32BIT &&
         rgb32_data_has_alpha(item->width, item->height, item->stride, item->data, &all_set)) {
         if (all_set) {
@@ -7133,10 +6992,9 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
                   SpiceMarshaller *base_marshaller, Drawable *drawable)
 {
     DisplayChannelClient *dcc = RCC_TO_DCC(rcc);
-    DisplayChannel *display_channel = SPICE_CONTAINEROF(rcc->channel, DisplayChannel, common.base);
+    DisplayChannel *display = DCC_TO_DC(dcc);
     Stream *stream = drawable->stream;
     SpiceImage *image;
-    RedWorker *worker = DCC_TO_WORKER(dcc);
     uint32_t frame_mm_time;
     int n;
     int width, height;
@@ -7148,7 +7006,6 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
     }
     spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
 
-    worker = display_channel->common.worker;
     image = drawable->red_drawable->u.copy.src_bitmap;
 
     if (image->descriptor.type != SPICE_IMAGE_TYPE_BITMAP) {
@@ -7169,7 +7026,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
         height = stream->height;
     }
 
-    StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
+    StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)];
     uint64_t time_now = red_get_monotonic_time();
     size_t outbuf_size;
 
@@ -7217,7 +7074,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA, NULL);
 
-        stream_data.base.id = get_stream_id(worker, stream);
+        stream_data.base.id = get_stream_id(display, stream);
         stream_data.base.multi_media_time = frame_mm_time;
         stream_data.data_size = n;
 
@@ -7227,7 +7084,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
 
         red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA_SIZED, NULL);
 
-        stream_data.base.id = get_stream_id(worker, stream);
+        stream_data.base.id = get_stream_id(display, stream);
         stream_data.base.multi_media_time = frame_mm_time;
         stream_data.data_size = n;
         stream_data.width = width;
@@ -7599,7 +7456,7 @@ static void red_display_marshall_stream_start(RedChannelClient *rcc,
     SpiceClipRects clip_rects;
 
     stream_create.surface_id = 0;
-    stream_create.id = get_stream_id(DCC_TO_WORKER(dcc), stream);
+    stream_create.id = get_stream_id(DCC_TO_DC(dcc), stream);
     stream_create.flags = stream->top_down ? SPICE_STREAM_FLAGS_TOP_DOWN : 0;
     stream_create.codec_type = SPICE_VIDEO_CODEC_TYPE_MJPEG;
 
@@ -7635,7 +7492,7 @@ static void red_display_marshall_stream_clip(RedChannelClient *rcc,
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CLIP, &item->base);
     SpiceMsgDisplayStreamClip stream_clip;
 
-    stream_clip.id = get_stream_id(DCC_TO_WORKER(dcc), agent->stream);
+    stream_clip.id = get_stream_id(DCC_TO_DC(dcc), agent->stream);
     stream_clip.clip.type = item->clip_type;
     stream_clip.clip.rects = item->rects;
 
@@ -7649,7 +7506,7 @@ static void red_display_marshall_stream_end(RedChannelClient *rcc,
     SpiceMsgDisplayStreamDestroy destroy;
 
     red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL);
-    destroy.id = get_stream_id(DCC_TO_WORKER(dcc), agent->stream);
+    destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream);
     red_display_stream_agent_stop(dcc, agent);
     spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy);
 }
@@ -7862,7 +7719,7 @@ static void display_channel_client_on_disconnect(RedChannelClient *rcc)
     free(dcc->send_data.stream_outbuf);
     red_display_reset_compress_buf(dcc);
     free(dcc->send_data.free_list.res);
-    red_display_destroy_streams_agents(dcc);
+    dcc_destroy_stream_agents(dcc);
 
     // this was the last channel client
     if (!red_channel_is_connected(rcc->channel)) {
@@ -7884,20 +7741,20 @@ void red_disconnect_all_display_TODO_remove_me(RedChannel *channel)
     red_channel_apply_clients(channel, red_channel_client_disconnect);
 }
 
-static void red_destroy_streams(RedWorker *worker)
+static void detach_and_stop_streams(DisplayChannel *display)
 {
     RingItem *stream_item;
 
     spice_debug(NULL);
-    while ((stream_item = ring_get_head(&worker->streams))) {
+    while ((stream_item = ring_get_head(&display->streams))) {
         Stream *stream = SPICE_CONTAINEROF(stream_item, Stream, link);
 
-        red_detach_stream_gracefully(worker, stream, NULL);
-        red_stop_stream(worker, stream);
+        detach_stream_gracefully(display, stream, NULL);
+        stop_stream(display, stream);
     }
 }
 
-static void red_migrate_display(RedWorker *worker, RedChannelClient *rcc)
+static void red_migrate_display(DisplayChannel *display, RedChannelClient *rcc)
 {
     /* We need to stop the streams, and to send upgrade_items to the client.
      * Otherwise, (1) the client might display lossy regions that we don't track
@@ -7906,10 +7763,10 @@ static void red_migrate_display(RedWorker *worker, RedChannelClient *rcc)
      * being sent to the client after MSG_MIGRATE and before MSG_MIGRATE_DATA (e.g.,
      * STREAM_CLIP, STREAM_DESTROY, DRAW_COPY)
      * No message besides MSG_MIGRATE_DATA should be sent after MSG_MIGRATE.
-     * Notice that red_destroy_streams won't lead to any dev ram changes, since
+     * Notice that detach_and_stop_streams won't lead to any dev ram changes, since
      * handle_dev_stop already took care of releasing all the dev ram resources.
      */
-    red_destroy_streams(worker);
+    detach_and_stop_streams(display);
     if (red_channel_client_is_connected(rcc)) {
         red_channel_client_default_migrate(rcc);
     }
@@ -8029,7 +7886,7 @@ static inline void red_create_surface_item(DisplayChannelClient *dcc, int surfac
     RedSurface *surface;
     SurfaceCreateItem *create;
     RedWorker *worker = dcc ? DCC_TO_WORKER(dcc) : NULL;
-    uint32_t flags = is_primary_surface(worker, surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0;
+    uint32_t flags = is_primary_surface(DCC_TO_DC(dcc), surface_id) ? SPICE_SURFACE_FLAGS_PRIMARY : 0;
 
     /* don't send redundant create surface commands to client */
     if (!dcc || worker->display_channel->common.during_target_migrate ||
@@ -8288,7 +8145,7 @@ static void on_new_display_channel_client(DisplayChannelClient *dcc)
         red_push_surface_image(dcc, 0);
         dcc_push_monitors_config(dcc);
         red_pipe_add_verb(rcc, SPICE_MSG_DISPLAY_MARK);
-        red_disply_start_streams(dcc);
+        dcc_create_all_streams(dcc);
     }
 }
 
@@ -8908,17 +8765,17 @@ static void display_channel_hold_pipe_item(RedChannelClient *rcc, PipeItem *item
 static void display_channel_client_release_item_after_push(DisplayChannelClient *dcc,
                                                            PipeItem *item)
 {
-    RedWorker *worker = DCC_TO_WORKER(dcc);
+    DisplayChannel *display = DCC_TO_DC(dcc);
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW:
         drawable_pipe_item_unref(SPICE_CONTAINEROF(item, DrawablePipeItem, dpi_pipe_item));
         break;
     case PIPE_ITEM_TYPE_STREAM_CLIP:
-        red_display_release_stream_clip(worker, (StreamClipItem *)item);
+        display_stream_clip_unref(display, (StreamClipItem *)item);
         break;
     case PIPE_ITEM_TYPE_UPGRADE:
-        release_upgrade_item(worker, (UpgradeItem *)item);
+        release_upgrade_item(DCC_TO_WORKER(dcc), (UpgradeItem *)item);
         break;
     case PIPE_ITEM_TYPE_IMAGE:
         release_image_item((ImageItem *)item);
@@ -8943,7 +8800,7 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient
 static void display_channel_client_release_item_before_push(DisplayChannelClient *dcc,
                                                             PipeItem *item)
 {
-    RedWorker *worker = DCC_TO_WORKER(dcc);
+    DisplayChannel *display = DCC_TO_DC(dcc);
 
     switch (item->type) {
     case PIPE_ITEM_TYPE_DRAW: {
@@ -8954,19 +8811,19 @@ static void display_channel_client_release_item_before_push(DisplayChannelClient
     }
     case PIPE_ITEM_TYPE_STREAM_CREATE: {
         StreamAgent *agent = SPICE_CONTAINEROF(item, StreamAgent, create_item);
-        red_display_release_stream(worker, agent);
+        display_stream_agent_unref(display, agent);
         break;
     }
     case PIPE_ITEM_TYPE_STREAM_CLIP:
-        red_display_release_stream_clip(worker, (StreamClipItem *)item);
+        display_stream_clip_unref(display, (StreamClipItem *)item);
         break;
     case PIPE_ITEM_TYPE_STREAM_DESTROY: {
         StreamAgent *agent = SPICE_CONTAINEROF(item, StreamAgent, destroy_item);
-        red_display_release_stream(worker, agent);
+        display_stream_agent_unref(display, agent);
         break;
     }
     case PIPE_ITEM_TYPE_UPGRADE:
-        release_upgrade_item(worker, (UpgradeItem *)item);
+        release_upgrade_item(DCC_TO_WORKER(dcc), (UpgradeItem *)item);
         break;
     case PIPE_ITEM_TYPE_IMAGE:
         release_image_item((ImageItem *)item);
@@ -9017,6 +8874,19 @@ static void display_channel_release_item(RedChannelClient *rcc, PipeItem *item,
     }
 }
 
+static void init_streams(DisplayChannel *display)
+{
+    int i;
+
+    ring_init(&display->streams);
+    display->free_streams = NULL;
+    for (i = 0; i < NUM_STREAMS; i++) {
+        Stream *stream = &display->streams_buf[i];
+        ring_item_init(&stream->link);
+        display_stream_free(display, stream);
+    }
+}
+
 static void display_channel_create(RedWorker *worker, int migrate)
 {
     DisplayChannel *display_channel;
@@ -9033,7 +8903,7 @@ static void display_channel_create(RedWorker *worker, int migrate)
     spice_return_if_fail(num_renderers > 0);
 
     spice_info("create display channel");
-    if (!(worker->display_channel = (DisplayChannel *)red_worker_new_channel(
+    if (!(display_channel = (DisplayChannel *)red_worker_new_channel(
             worker, sizeof(*display_channel), "display_channel",
             SPICE_CHANNEL_DISPLAY,
             SPICE_MIGRATE_NEED_FLUSH | SPICE_MIGRATE_NEED_DATA_TRANSFER,
@@ -9041,7 +8911,7 @@ static void display_channel_create(RedWorker *worker, int migrate)
         spice_warning("failed to create display channel");
         return;
     }
-    display_channel = worker->display_channel;
+    worker->display_channel = display_channel;
 #ifdef RED_STATISTICS
     RedChannel *channel = RED_CHANNEL(display_channel);
     display_channel->cache_hits_counter = stat_add_counter(channel->stat,
@@ -9062,6 +8932,7 @@ static void display_channel_create(RedWorker *worker, int migrate)
     display_channel->num_renderers = num_renderers;
     memcpy(display_channel->renderers, renderers, sizeof(display_channel->renderers));
     display_channel->renderer = RED_RENDERER_INVALID;
+    init_streams(display_channel);
 }
 
 static void guest_set_client_capabilities(RedWorker *worker)
@@ -9166,7 +9037,7 @@ static void handle_new_display_channel(RedWorker *worker, RedClient *client, Red
 
     // todo: tune level according to bandwidth
     display_channel->zlib_level = ZLIB_DEFAULT_COMPRESSION_LEVEL;
-    red_display_client_init_streams(dcc);
+    dcc_init_stream_agents(dcc);
     on_new_display_channel_client(dcc);
 }
 
@@ -9354,6 +9225,7 @@ void handle_dev_destroy_surface_wait(void *opaque, void *payload)
 /* TODO: split me*/
 static inline void dev_destroy_surfaces(RedWorker *worker)
 {
+    DisplayChannel *display = worker->display_channel;
     int i;
 
     spice_debug(NULL);
@@ -9368,7 +9240,7 @@ static inline void dev_destroy_surfaces(RedWorker *worker)
             spice_assert(!worker->surfaces[i].context.canvas);
         }
     }
-    spice_assert(ring_is_empty(&worker->streams));
+    spice_assert(ring_is_empty(&display->streams));
 
     if (display_is_connected(worker)) {
         red_channel_pipes_add_type(RED_CHANNEL(worker->display_channel),
@@ -9482,6 +9354,8 @@ void handle_dev_create_primary_surface(void *opaque, void *payload)
 
 static void dev_destroy_primary_surface(RedWorker *worker, uint32_t surface_id)
 {
+    DisplayChannel *display = worker->display_channel;
+
     VALIDATE_SURFACE_RET(worker, surface_id);
     spice_warn_if(surface_id != 0);
 
@@ -9494,7 +9368,7 @@ static void dev_destroy_primary_surface(RedWorker *worker, uint32_t surface_id)
     flush_all_qxl_commands(worker);
     dev_destroy_surface_wait(worker, 0);
     red_destroy_surface(worker, 0);
-    spice_assert(ring_is_empty(&worker->streams));
+    spice_warn_if_fail(ring_is_empty(&display->streams));
 
     spice_assert(!worker->surfaces[surface_id].context.canvas);
 
@@ -9739,7 +9613,7 @@ void handle_dev_display_migrate(void *opaque, void *payload)
     RedChannelClient *rcc = msg->rcc;
     spice_info("migrate display client");
     spice_assert(rcc);
-    red_migrate_display(worker, rcc);
+    red_migrate_display(worker->display_channel, rcc);
 }
 
 static inline uint32_t qxl_monitors_config_size(uint32_t heads)
@@ -9870,21 +9744,7 @@ void handle_dev_set_streaming_video(void *opaque, void *payload)
     RedWorkerMessageSetStreamingVideo *msg = payload;
     RedWorker *worker = opaque;
 
-    worker->streaming_video = msg->streaming_video;
-    spice_assert(worker->streaming_video != SPICE_STREAM_VIDEO_INVALID);
-    switch(worker->streaming_video) {
-        case SPICE_STREAM_VIDEO_ALL:
-            spice_info("sv all");
-            break;
-        case SPICE_STREAM_VIDEO_FILTER:
-            spice_info("sv filter");
-            break;
-        case SPICE_STREAM_VIDEO_OFF:
-            spice_info("sv off");
-            break;
-        default:
-            spice_warning("sv invalid");
-    }
+    display_channel_set_stream_video(worker->display_channel, msg->streaming_video);
 }
 
 void handle_dev_set_mouse_mode(void *opaque, void *payload)
@@ -10199,13 +10059,11 @@ RedWorker* red_worker_new(QXLInstance *qxl, RedDispatcher *red_dispatcher)
     worker->image_compression = image_compression;
     worker->jpeg_state = jpeg_state;
     worker->zlib_glz_state = zlib_glz_state;
-    worker->streaming_video = streaming_video;
     worker->driver_cap_monitors_config = 0;
     ring_init(&worker->current_list);
     image_cache_init(&worker->image_cache);
     image_surface_init(worker);
     drawables_init(worker);
-    red_init_streams(worker);
     stat_init(&worker->add_stat, add_stat_name);
     stat_init(&worker->exclude_stat, exclude_stat_name);
     stat_init(&worker->__exclude_stat, __exclude_stat_name);
@@ -10284,10 +10142,10 @@ SPICE_GNUC_NORETURN static void *red_worker_main(void *arg)
 
         timeout = spice_timer_queue_get_timeout_ms();
         worker->event_timeout = MIN(timeout, worker->event_timeout);
-        timeout = red_get_streams_timout(worker);
+        timeout = display_channel_get_streams_timeout(worker->display_channel);
         worker->event_timeout = MIN(timeout, worker->event_timeout);
         num_events = poll(worker->poll_fds, MAX_EVENT_SOURCES, worker->event_timeout);
-        red_handle_streams_timout(worker);
+        display_channel_streams_timeout(worker->display_channel);
         spice_timer_queue_cb();
 
         if (worker->display_channel) {
diff --git a/server/red_worker.h b/server/red_worker.h
index 2995b8f..d2f2206 100644
--- a/server/red_worker.h
+++ b/server/red_worker.h
@@ -20,6 +20,7 @@
 
 #include <unistd.h>
 #include <errno.h>
+#include <glib.h>
 #include "red_common.h"
 #include "red_dispatcher.h"
 
diff --git a/server/stream.c b/server/stream.c
new file mode 100644
index 0000000..6203f3d
--- /dev/null
+++ b/server/stream.c
@@ -0,0 +1,66 @@
+/*
+   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 "stream.h"
+#include "display-channel.h"
+
+void stream_agent_stats_print(StreamAgent *agent)
+{
+#ifdef STREAM_STATS
+    StreamStats *stats = &agent->stats;
+    double passed_mm_time = (stats->end - stats->start) / 1000.0;
+    MJpegEncoderStats encoder_stats = {0};
+
+    if (agent->mjpeg_encoder) {
+        mjpeg_encoder_get_stats(agent->mjpeg_encoder, &encoder_stats);
+    }
+
+    spice_debug("stream=%p dim=(%dx%d) #in-frames=%lu #in-avg-fps=%.2f #out-frames=%lu "
+                "out/in=%.2f #drops=%lu (#pipe=%lu #fps=%lu) out-avg-fps=%.2f "
+                "passed-mm-time(sec)=%.2f size-total(MB)=%.2f size-per-sec(Mbps)=%.2f "
+                "size-per-frame(KBpf)=%.2f avg-quality=%.2f "
+                "start-bit-rate(Mbps)=%.2f end-bit-rate(Mbps)=%.2f",
+                agent, agent->stream->width, agent->stream->height,
+                stats->num_input_frames,
+                stats->num_input_frames / passed_mm_time,
+                stats->num_frames_sent,
+                (stats->num_frames_sent + 0.0) / stats->num_input_frames,
+                stats->num_drops_pipe +
+                stats->num_drops_fps,
+                stats->num_drops_pipe,
+                stats->num_drops_fps,
+                stats->num_frames_sent / passed_mm_time,
+                passed_mm_time,
+                stats->size_sent / 1024.0 / 1024.0,
+                ((stats->size_sent * 8.0) / (1024.0 * 1024)) / passed_mm_time,
+                stats->size_sent / 1000.0 / stats->num_frames_sent,
+                encoder_stats.avg_quality,
+                encoder_stats.starting_bit_rate / (1024.0 * 1024),
+                encoder_stats.cur_bit_rate / (1024.0 * 1024));
+#endif
+}
+
+StreamClipItem *stream_clip_item_new(DisplayChannelClient* dcc, StreamAgent *agent)
+{
+    StreamClipItem *item = spice_new(StreamClipItem, 1);
+    red_channel_pipe_item_init(RED_CHANNEL_CLIENT(dcc)->channel,
+                               (PipeItem *)item, PIPE_ITEM_TYPE_STREAM_CLIP);
+
+    item->stream_agent = agent;
+    agent->stream->refs++;
+    item->refs = 1;
+    return item;
+}
diff --git a/server/stream.h b/server/stream.h
new file mode 100644
index 0000000..4b85fb8
--- /dev/null
+++ b/server/stream.h
@@ -0,0 +1,143 @@
+/* -*- 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 STREAM_H_
+#define STREAM_H_
+
+#include <glib.h>
+#include "utils.h"
+#include "mjpeg_encoder.h"
+#include "common/region.h"
+#include "red_channel.h"
+
+/* FIXME: move back to display_channel.h (once structs are private) */
+typedef struct Drawable Drawable;
+typedef struct DisplayChannelClient DisplayChannelClient;
+
+
+#define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
+#define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000)
+#define RED_STREAM_TIMEOUT (1000 * 1000 * 1000)
+#define RED_STREAM_FRAMES_START_CONDITION 20
+#define RED_STREAM_GRADUAL_FRAMES_START_CONDITION 0.2
+#define RED_STREAM_FRAMES_RESET_CONDITION 100
+#define RED_STREAM_MIN_SIZE (96 * 96)
+#define RED_STREAM_INPUT_FPS_TIMEOUT ((uint64_t)5 * 1000 * 1000 * 1000) // 5 sec
+#define RED_STREAM_CHANNEL_CAPACITY 0.8
+/* the client's stream report frequency is the minimum of the 2 values below */
+#define RED_STREAM_CLIENT_REPORT_WINDOW 5 // #frames
+#define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds
+#define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps
+#define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps
+
+typedef struct Stream Stream;
+
+typedef struct StreamActivateReportItem {
+    PipeItem pipe_item;
+    uint32_t stream_id;
+} StreamActivateReportItem;
+
+enum {
+    STREAM_FRAME_NONE,
+    STREAM_FRAME_NATIVE,
+    STREAM_FRAME_CONTAINER,
+};
+
+#define STREAM_STATS
+#ifdef STREAM_STATS
+typedef struct StreamStats {
+    uint64_t num_drops_pipe;
+    uint64_t num_drops_fps;
+    uint64_t num_frames_sent;
+    uint64_t num_input_frames;
+    uint64_t size_sent;
+
+    uint64_t start;
+    uint64_t end;
+} StreamStats;
+#endif
+
+typedef struct StreamAgent {
+    QRegion vis_region; /* the part of the surface area that is currently occupied by video
+                           fragments */
+    QRegion clip;       /* the current video clipping. It can be different from vis_region:
+                           for example, let c1 be the clip area at time t1, and c2
+                           be the clip area at time t2, where t1 < t2. If c1 contains c2, and
+                           at least part of c1/c2, hasn't been covered by a non-video images,
+                           vis_region will contain c2 and also the part of c1/c2 that still
+                           displays fragments of the video */
+
+    PipeItem create_item;
+    PipeItem destroy_item;
+    Stream *stream;
+    uint64_t last_send_time;
+    MJpegEncoder *mjpeg_encoder;
+    DisplayChannelClient *dcc;
+
+    int frames;
+    int drops;
+    int fps;
+
+    uint32_t report_id;
+    uint32_t client_required_latency;
+#ifdef STREAM_STATS
+    StreamStats stats;
+#endif
+} StreamAgent;
+
+typedef struct StreamClipItem {
+    PipeItem base;
+    int refs;
+    StreamAgent *stream_agent;
+    int clip_type;
+    SpiceClipRects *rects;
+} StreamClipItem;
+
+StreamClipItem *      stream_clip_item_new                          (DisplayChannelClient* dcc,
+                                                                     StreamAgent *agent);
+
+typedef struct ItemTrace {
+    red_time_t time;
+    int frames_count;
+    int gradual_frames_count;
+    int last_gradual_frame;
+    int width;
+    int height;
+    SpiceRect dest_area;
+} ItemTrace;
+
+typedef struct Stream Stream;
+struct Stream {
+    uint8_t refs;
+    Drawable *current;
+    red_time_t last_time;
+    int width;
+    int height;
+    SpiceRect dest_area;
+    int top_down;
+    Stream *next;
+    RingItem link;
+
+    uint32_t num_input_frames;
+    uint64_t input_fps_start_time;
+    uint32_t input_fps;
+};
+
+
+void                  stream_agent_stats_print                      (StreamAgent *agent);
+
+#endif /* STREAM_H */
diff --git a/server/utils.h b/server/utils.h
index 1ebc32f..6750c1c 100644
--- a/server/utils.h
+++ b/server/utils.h
@@ -18,6 +18,7 @@
 #ifndef UTILS_H_
 # define UTILS_H_
 
+#include <stdint.h>
 #include <time.h>
 
 typedef int64_t red_time_t;
-- 
2.4.3



More information about the Spice-devel mailing list