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

Jonathon Jongsma jjongsma at redhat.com
Fri Nov 6 14:38:13 PST 2015


NACK

There's a lot of stuff going on in this patch, but I think it would
be hard to split out (other than a few minor include changes I noted
below). So I'm OK with that part. But there seems to be one behavior
change that I've noted below.



On Fri, 2015-11-06 at 11:01 +0000, Frediano Ziglio wrote:

> 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;
>      }

This appears to be a change in behavior. It looks to be related to
the change just below in the next hunk, but it seems incorrect.

In the pervious version, we were calling this function only if
item->stream was NULL (see below). In the new version, we're calling
this function unconditionally (see below), but we're returning early
if item->stream is NULL.  This effectively reverses the test:

old: call function if item->stream is NULL
new: call function if item->stream is not NULL

I'm afraid I don't know enough about this code to determine what
effect this might have...

>  
> -    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"


This include is unrelated and shouldn't be part of this commit. 

>  
> 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;


This last hunk seems unrelated. Should be split into a separate
commit. 


More information about the Spice-devel mailing list