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

Frediano Ziglio fziglio at redhat.com
Tue Nov 10 03:19:34 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.
> 
> 

Agree that code change (if stream one) looks wrong (and probably untested).
I'll try to split the patch.

Frediano


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