[Spice-devel] [PATCH 10/11] Move stream functions to stream.[ch]

Frediano Ziglio fziglio at redhat.com
Mon Nov 16 03:06:56 PST 2015


From: Jonathon Jongsma <jjongsma at redhat.com>

---
 server/display-channel.h |   5 +
 server/red_worker.c      | 376 +----------------------------------------------
 server/stream.c          | 359 ++++++++++++++++++++++++++++++++++++++++++++
 server/stream.h          |   9 ++
 4 files changed, 378 insertions(+), 371 deletions(-)

diff --git a/server/display-channel.h b/server/display-channel.h
index ae8a900..26b4e4e 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -166,6 +166,11 @@ struct Drawable {
     uint32_t process_commands_generation;
 };
 
+#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
+#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi)            \
+    SAFE_FOREACH(link, next, drawable,  &(drawable)->pipes, dpi, LINK_TO_DPI(link))
+
+
 struct DisplayChannelClient {
     CommonChannelClient common;
     SpiceImageCompression image_compression;
diff --git a/server/red_worker.c b/server/red_worker.c
index e63828b..6a29e00 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -79,9 +79,6 @@
 
 #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
 
-#define FPS_TEST_INTERVAL 1
-#define MAX_FPS 30
-
 #define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
 #define MIN_GLZ_SIZE_FOR_ZLIB 100
 
@@ -286,8 +283,6 @@ static void red_draw_drawable(DisplayChannel *display, Drawable *item);
 static void red_update_area(DisplayChannel *display, const SpiceRect *area, int surface_id);
 static void red_update_area_till(DisplayChannel *display, const SpiceRect *area, int surface_id,
                                  Drawable *last);
-static void detach_stream(DisplayChannel *display, Stream *stream, int detach_sized);
-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);
@@ -305,10 +300,6 @@ static void red_create_surface(DisplayChannel *display, uint32_t surface_id, uin
                                uint32_t height, int32_t stride, uint32_t format,
                                void *line_0, int data_is_valid, int send_client);
 
-#define LINK_TO_DPI(ptr) SPICE_CONTAINEROF((ptr), DrawablePipeItem, base)
-#define DRAWABLE_FOREACH_DPI_SAFE(drawable, link, next, dpi)          \
-    SAFE_FOREACH(link, next, drawable,  &(drawable)->pipes, dpi, LINK_TO_DPI(link))
-
 
 #define LINK_TO_GLZ(ptr) SPICE_CONTAINEROF((ptr), RedGlzDrawable, \
                                            drawable_link)
@@ -1025,7 +1016,7 @@ static void __exclude_region(DisplayChannel *display, Ring *ring, TreeItem *item
             } else {
                 if (frame_candidate) {
                     Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item);
-                    display_channel_stream_maintenance(display, frame_candidate, drawable);
+                    stream_maintenance(display, frame_candidate, drawable);
                 }
                 region_exclude(&draw->base.rgn, &and_rgn);
             }
@@ -1130,8 +1121,8 @@ static void current_add_drawable(DisplayChannel *display,
     drawable->refs++;
 }
 
-static void detach_stream(DisplayChannel *display, Stream *stream,
-                          int detach_sized)
+void detach_stream(DisplayChannel *display, Stream *stream,
+                   int detach_sized)
 {
     spice_assert(stream->current && stream->current->stream);
     spice_assert(stream->current->stream == stream);
@@ -1342,17 +1333,6 @@ static void display_channel_streams_timeout(DisplayChannel *display)
     }
 }
 
-static Stream *display_channel_stream_try_new(DisplayChannel *display)
-{
-    Stream *stream;
-    if (!display->free_streams) {
-        return NULL;
-    }
-    stream = display->free_streams;
-    display->free_streams = display->free_streams->next;
-    return stream;
-}
-
 static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc,
                                                 Stream *stream)
 {
@@ -1528,47 +1508,6 @@ void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream)
 #endif
 }
 
-static void display_channel_create_stream(DisplayChannel *display, Drawable *drawable)
-{
-    DisplayChannelClient *dcc;
-    RingItem *dcc_ring_item, *next;
-    Stream *stream;
-    SpiceRect* src_rect;
-
-    spice_assert(!drawable->stream);
-
-    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(&display->streams, &stream->link);
-    stream->current = drawable;
-    stream->last_time = drawable->creation_time;
-    stream->width = src_rect->right - src_rect->left;
-    stream->height = src_rect->bottom - src_rect->top;
-    stream->dest_area = drawable->red_drawable->bbox;
-    stream->refs = 1;
-    SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
-    stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
-    drawable->stream = stream;
-    stream->input_fps = MAX_FPS;
-    stream->num_input_frames = 0;
-    stream->input_fps_start_time = drawable->creation_time;
-    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 - 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 dcc_create_all_streams(DisplayChannelClient *dcc)
 {
     Ring *ring = &DCC_TO_DC(dcc)->streams;
@@ -1613,256 +1552,6 @@ static void dcc_destroy_stream_agents(DisplayChannelClient *dcc)
     }
 }
 
-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;
-
-    if (!candidate->streamable) {
-        return STREAM_FRAME_NONE;
-    }
-
-    if (candidate->creation_time - other_time >
-            (stream ? RED_STREAM_CONTINUS_MAX_DELTA : RED_STREAM_DETACTION_MAX_DELTA)) {
-        return STREAM_FRAME_NONE;
-    }
-
-    red_drawable = candidate->red_drawable;
-    if (!container_candidate_allowed) {
-        SpiceRect* candidate_src;
-
-        if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
-            return STREAM_FRAME_NONE;
-        }
-
-        candidate_src = &red_drawable->u.copy.src_area;
-        if (candidate_src->right - candidate_src->left != other_src_width ||
-            candidate_src->bottom - candidate_src->top != other_src_height) {
-            return STREAM_FRAME_NONE;
-        }
-    } else {
-        if (rect_contains(&red_drawable->bbox, other_dest)) {
-            int candidate_area = rect_get_area(&red_drawable->bbox);
-            int other_area = rect_get_area(other_dest);
-            /* do not stream drawables that are significantly
-             * bigger than the original frame */
-            if (candidate_area > 2 * other_area) {
-                spice_debug("too big candidate:");
-                spice_debug("prev box ==>");
-                rect_debug(other_dest);
-                spice_debug("new box ==>");
-                rect_debug(&red_drawable->bbox);
-                return STREAM_FRAME_NONE;
-            }
-
-            if (candidate_area > other_area) {
-                is_frame_container = TRUE;
-            }
-        } else {
-            return STREAM_FRAME_NONE;
-        }
-    }
-
-    if (stream) {
-        SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
-        if (stream->top_down != !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
-            return STREAM_FRAME_NONE;
-        }
-    }
-    if (is_frame_container) {
-        return STREAM_FRAME_CONTAINER;
-    } else {
-        return STREAM_FRAME_NATIVE;
-    }
-}
-
-static void before_reattach_stream(DisplayChannel *display,
-                                   Stream *stream, Drawable *new_frame)
-{
-    DrawablePipeItem *dpi;
-    DisplayChannelClient *dcc;
-    int index;
-    StreamAgent *agent;
-    RingItem *ring_item, *next;
-
-    spice_return_if_fail(stream->current);
-
-    if (!red_channel_is_connected(RED_CHANNEL(display))) {
-        return;
-    }
-
-    if (new_frame->process_commands_generation == stream->current->process_commands_generation) {
-        spice_debug("ignoring drop, same process_commands_generation as previous frame");
-        return;
-    }
-
-    index = get_stream_id(display, stream);
-    DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
-        dcc = dpi->dcc;
-        agent = &dcc->stream_agents[index];
-
-        if (!dcc->use_mjpeg_encoder_rate_control &&
-            !dcc->common.is_low_bandwidth) {
-            continue;
-        }
-
-        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
-#ifdef STREAM_STATS
-            agent->stats.num_drops_pipe++;
-#endif
-            if (dcc->use_mjpeg_encoder_rate_control) {
-                mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder);
-            } else {
-                ++agent->drops;
-            }
-        }
-    }
-
-
-    FOREACH_DCC(display, ring_item, next, dcc) {
-        double drop_factor;
-
-        agent = &dcc->stream_agents[index];
-
-        if (dcc->use_mjpeg_encoder_rate_control) {
-            continue;
-        }
-        if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
-            agent->frames++;
-            continue;
-        }
-        drop_factor = ((double)agent->frames - (double)agent->drops) /
-            (double)agent->frames;
-        spice_debug("stream %d: #frames %u #drops %u", index, agent->frames, agent->drops);
-        if (drop_factor == 1) {
-            if (agent->fps < MAX_FPS) {
-                agent->fps++;
-                spice_debug("stream %d: fps++ %u", index, agent->fps);
-            }
-        } else if (drop_factor < 0.9) {
-            if (agent->fps > 1) {
-                agent->fps--;
-                spice_debug("stream %d: fps--%u", index, agent->fps);
-            }
-        }
-        agent->frames = 1;
-        agent->drops = 0;
-    }
-}
-
-static void update_copy_graduality(DisplayChannel *display, Drawable *drawable)
-{
-    SpiceBitmap *bitmap;
-    spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
-
-    if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) {
-        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
-        return;
-    }
-
-    if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) {
-        return; // already set
-    }
-
-    bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
-
-    if (!bitmap_fmt_has_graduality(bitmap->format) || bitmap_has_extra_stride(bitmap) ||
-        (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
-        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
-    } else  {
-        drawable->copy_bitmap_graduality = bitmap_get_graduality_level(bitmap);
-    }
-}
-
-static int is_stream_start(Drawable *drawable)
-{
-    return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) &&
-            (drawable->gradual_frames_count >=
-            (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable->frames_count)));
-}
-
-// returns whether a stream was created
-static int display_channel_stream_add_frame(DisplayChannel *display,
-                                            Drawable *frame_drawable,
-                                            int frames_count,
-                                            int gradual_frames_count,
-                                            int last_gradual_frame)
-{
-    update_copy_graduality(display, frame_drawable);
-    frame_drawable->frames_count = frames_count + 1;
-    frame_drawable->gradual_frames_count  = gradual_frames_count;
-
-    if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) {
-        if ((frame_drawable->frames_count - last_gradual_frame) >
-            RED_STREAM_FRAMES_RESET_CONDITION) {
-            frame_drawable->frames_count = 1;
-            frame_drawable->gradual_frames_count = 1;
-        } else {
-            frame_drawable->gradual_frames_count++;
-        }
-
-        frame_drawable->last_gradual_frame = frame_drawable->frames_count;
-    } else {
-        frame_drawable->last_gradual_frame = last_gradual_frame;
-    }
-
-    if (is_stream_start(frame_drawable)) {
-        display_channel_create_stream(display, frame_drawable);
-        return TRUE;
-    }
-    return FALSE;
-}
-
-static void display_channel_stream_maintenance(DisplayChannel *display,
-                                               Drawable *candidate, Drawable *prev)
-{
-    int is_next_frame;
-
-    if (candidate->stream) {
-        return;
-    }
-
-    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) {
-            before_reattach_stream(display, stream, candidate);
-            detach_stream(display, stream, FALSE);
-            prev->streamable = FALSE; //prevent item trace
-            attach_stream(display, candidate, stream);
-            if (is_next_frame == STREAM_FRAME_CONTAINER) {
-                candidate->sized_stream = stream;
-            }
-        }
-    } 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);
-        }
-    }
-}
-
 static inline int red_current_add_equal(DisplayChannel *display, DrawItem *item, TreeItem *other)
 {
     DrawItem *other_draw_item;
@@ -1884,7 +1573,7 @@ static inline int red_current_add_equal(DisplayChannel *display, DrawItem *item,
     if (item->effect == QXL_EFFECT_OPAQUE) {
         int add_after = !!other_drawable->stream &&
                         is_drawable_independent_from_surfaces(drawable);
-        display_channel_stream_maintenance(display, drawable, other_drawable);
+        stream_maintenance(display, drawable, other_drawable);
         current_add_drawable(display, drawable, &other->siblings_link);
         other_drawable->refs++;
         current_remove_drawable(display, other_drawable);
@@ -1958,61 +1647,6 @@ static inline int red_current_add_equal(DisplayChannel *display, DrawItem *item,
     return FALSE;
 }
 
-#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;
-    RingItem *item;
-
-    if (drawable->stream || !drawable->streamable || drawable->frames_count) {
-        return;
-    }
-
-    FOREACH_STREAMS(display, item) {
-        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
-        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
-                before_reattach_stream(display, stream, drawable);
-                detach_stream(display, stream, FALSE);
-            }
-            attach_stream(display, drawable, stream);
-            if (is_next_frame == STREAM_FRAME_CONTAINER) {
-                drawable->sized_stream = stream;
-            }
-            return;
-        }
-    }
-
-    trace = display->items_trace;
-    trace_end = trace + NUM_TRACE_ITEMS;
-    for (; trace < trace_end; trace++) {
-        if (is_next_stream_frame(display, drawable, trace->width, trace->height,
-                                       &trace->dest_area, trace->time, NULL, FALSE) !=
-                                       STREAM_FRAME_NONE) {
-            if (display_channel_stream_add_frame(display, drawable,
-                                                 trace->frames_count,
-                                                 trace->gradual_frames_count,
-                                                 trace->last_gradual_frame)) {
-                return;
-            }
-        }
-    }
-}
-
 static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
 {
     DrawItem *item = &drawable->tree_item;
@@ -2109,7 +1743,7 @@ static int current_add(DisplayChannel *display, Ring *ring, Drawable *drawable)
     if (item->effect == QXL_EFFECT_OPAQUE) {
         region_or(&exclude_rgn, &item->base.rgn);
         exclude_region(display, ring, exclude_base, &exclude_rgn, NULL, drawable);
-        red_use_stream_trace(display, drawable);
+        stream_trace_update(display, drawable);
         streams_update_visible_region(display, drawable);
         /*
          * Performing the insertion after exclude_region for
diff --git a/server/stream.c b/server/stream.c
index d61cec1..54f81a8 100644
--- a/server/stream.c
+++ b/server/stream.c
@@ -21,6 +21,12 @@
 #include "stream.h"
 #include "display-channel.h"
 
+#define FPS_TEST_INTERVAL 1
+#define FOREACH_STREAMS(display, item)                  \
+    for (item = ring_get_head(&(display)->streams);     \
+         item != NULL;                                  \
+         item = ring_next(&(display)->streams, item))
+
 void stream_agent_stats_print(StreamAgent *agent)
 {
 #ifdef STREAM_STATS
@@ -138,3 +144,356 @@ StreamClipItem *stream_clip_item_new(DisplayChannelClient* dcc, StreamAgent *age
     item->refs = 1;
     return item;
 }
+
+static int is_stream_start(Drawable *drawable)
+{
+    return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) &&
+            (drawable->gradual_frames_count >=
+             (RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable->frames_count)));
+}
+
+static void update_copy_graduality(DisplayChannel *display, Drawable *drawable)
+{
+    SpiceBitmap *bitmap;
+    spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY);
+
+    if (display->stream_video != SPICE_STREAM_VIDEO_FILTER) {
+        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
+        return;
+    }
+
+    if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) {
+        return; // already set
+    }
+
+    bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
+
+    if (!bitmap_fmt_has_graduality(bitmap->format) || bitmap_has_extra_stride(bitmap) ||
+        (bitmap->data->flags & SPICE_CHUNKS_FLAGS_UNSTABLE)) {
+        drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
+    } else  {
+        drawable->copy_bitmap_graduality = bitmap_get_graduality_level(bitmap);
+    }
+}
+
+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;
+
+    if (!candidate->streamable) {
+        return STREAM_FRAME_NONE;
+    }
+
+    if (candidate->creation_time - other_time >
+            (stream ? RED_STREAM_CONTINUS_MAX_DELTA : RED_STREAM_DETACTION_MAX_DELTA)) {
+        return STREAM_FRAME_NONE;
+    }
+
+    red_drawable = candidate->red_drawable;
+    if (!container_candidate_allowed) {
+        SpiceRect* candidate_src;
+
+        if (!rect_is_equal(&red_drawable->bbox, other_dest)) {
+            return STREAM_FRAME_NONE;
+        }
+
+        candidate_src = &red_drawable->u.copy.src_area;
+        if (candidate_src->right - candidate_src->left != other_src_width ||
+            candidate_src->bottom - candidate_src->top != other_src_height) {
+            return STREAM_FRAME_NONE;
+        }
+    } else {
+        if (rect_contains(&red_drawable->bbox, other_dest)) {
+            int candidate_area = rect_get_area(&red_drawable->bbox);
+            int other_area = rect_get_area(other_dest);
+            /* do not stream drawables that are significantly
+             * bigger than the original frame */
+            if (candidate_area > 2 * other_area) {
+                spice_debug("too big candidate:");
+                spice_debug("prev box ==>");
+                rect_debug(other_dest);
+                spice_debug("new box ==>");
+                rect_debug(&red_drawable->bbox);
+                return STREAM_FRAME_NONE;
+            }
+
+            if (candidate_area > other_area) {
+                is_frame_container = TRUE;
+            }
+        } else {
+            return STREAM_FRAME_NONE;
+        }
+    }
+
+    if (stream) {
+        SpiceBitmap *bitmap = &red_drawable->u.copy.src_bitmap->u.bitmap;
+        if (stream->top_down != !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN)) {
+            return STREAM_FRAME_NONE;
+        }
+    }
+    if (is_frame_container) {
+        return STREAM_FRAME_CONTAINER;
+    } else {
+        return STREAM_FRAME_NATIVE;
+    }
+}
+
+static void before_reattach_stream(DisplayChannel *display,
+                                   Stream *stream, Drawable *new_frame)
+{
+    DrawablePipeItem *dpi;
+    DisplayChannelClient *dcc;
+    int index;
+    StreamAgent *agent;
+    RingItem *ring_item, *next;
+
+    spice_return_if_fail(stream->current);
+
+    if (!red_channel_is_connected(RED_CHANNEL(display))) {
+        return;
+    }
+
+    if (new_frame->process_commands_generation == stream->current->process_commands_generation) {
+        spice_debug("ignoring drop, same process_commands_generation as previous frame");
+        return;
+    }
+
+    index = get_stream_id(display, stream);
+    DRAWABLE_FOREACH_DPI_SAFE(stream->current, ring_item, next, dpi) {
+        dcc = dpi->dcc;
+        agent = &dcc->stream_agents[index];
+
+        if (!dcc->use_mjpeg_encoder_rate_control &&
+            !dcc->common.is_low_bandwidth) {
+            continue;
+        }
+
+        if (pipe_item_is_linked(&dpi->dpi_pipe_item)) {
+#ifdef STREAM_STATS
+            agent->stats.num_drops_pipe++;
+#endif
+            if (dcc->use_mjpeg_encoder_rate_control) {
+                mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder);
+            } else {
+                ++agent->drops;
+            }
+        }
+    }
+
+
+    FOREACH_DCC(display, ring_item, next, dcc) {
+        double drop_factor;
+
+        agent = &dcc->stream_agents[index];
+
+        if (dcc->use_mjpeg_encoder_rate_control) {
+            continue;
+        }
+        if (agent->frames / agent->fps < FPS_TEST_INTERVAL) {
+            agent->frames++;
+            continue;
+        }
+        drop_factor = ((double)agent->frames - (double)agent->drops) /
+            (double)agent->frames;
+        spice_debug("stream %d: #frames %u #drops %u", index, agent->frames, agent->drops);
+        if (drop_factor == 1) {
+            if (agent->fps < MAX_FPS) {
+                agent->fps++;
+                spice_debug("stream %d: fps++ %u", index, agent->fps);
+            }
+        } else if (drop_factor < 0.9) {
+            if (agent->fps > 1) {
+                agent->fps--;
+                spice_debug("stream %d: fps--%u", index, agent->fps);
+            }
+        }
+        agent->frames = 1;
+        agent->drops = 0;
+    }
+}
+
+static Stream *display_channel_stream_try_new(DisplayChannel *display)
+{
+    Stream *stream;
+    if (!display->free_streams) {
+        return NULL;
+    }
+    stream = display->free_streams;
+    display->free_streams = display->free_streams->next;
+    return stream;
+}
+
+static void display_channel_create_stream(DisplayChannel *display, Drawable *drawable)
+{
+    DisplayChannelClient *dcc;
+    RingItem *dcc_ring_item, *next;
+    Stream *stream;
+    SpiceRect* src_rect;
+
+    spice_assert(!drawable->stream);
+
+    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(&display->streams, &stream->link);
+    stream->current = drawable;
+    stream->last_time = drawable->creation_time;
+    stream->width = src_rect->right - src_rect->left;
+    stream->height = src_rect->bottom - src_rect->top;
+    stream->dest_area = drawable->red_drawable->bbox;
+    stream->refs = 1;
+    SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
+    stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
+    drawable->stream = stream;
+    stream->input_fps = MAX_FPS;
+    stream->num_input_frames = 0;
+    stream->input_fps_start_time = drawable->creation_time;
+    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 - display->streams_buf), stream->width,
+                stream->height, stream->dest_area.left, stream->dest_area.top,
+                stream->dest_area.right, stream->dest_area.bottom);
+    return;
+}
+
+// returns whether a stream was created
+static int stream_add_frame(DisplayChannel *display,
+                            Drawable *frame_drawable,
+                            int frames_count,
+                            int gradual_frames_count,
+                            int last_gradual_frame)
+{
+    update_copy_graduality(display, frame_drawable);
+    frame_drawable->frames_count = frames_count + 1;
+    frame_drawable->gradual_frames_count  = gradual_frames_count;
+
+    if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_LOW) {
+        if ((frame_drawable->frames_count - last_gradual_frame) >
+            RED_STREAM_FRAMES_RESET_CONDITION) {
+            frame_drawable->frames_count = 1;
+            frame_drawable->gradual_frames_count = 1;
+        } else {
+            frame_drawable->gradual_frames_count++;
+        }
+
+        frame_drawable->last_gradual_frame = frame_drawable->frames_count;
+    } else {
+        frame_drawable->last_gradual_frame = last_gradual_frame;
+    }
+
+    if (is_stream_start(frame_drawable)) {
+        display_channel_create_stream(display, frame_drawable);
+        return TRUE;
+    }
+    return FALSE;
+}
+
+/* TODO: document the difference between the 2 functions below */
+void stream_trace_update(DisplayChannel *display, Drawable *drawable)
+{
+    ItemTrace *trace;
+    ItemTrace *trace_end;
+    RingItem *item;
+
+    if (drawable->stream || !drawable->streamable || drawable->frames_count) {
+        return;
+    }
+
+    FOREACH_STREAMS(display, item) {
+        Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
+        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
+                before_reattach_stream(display, stream, drawable);
+                detach_stream(display, stream, FALSE);
+            }
+            attach_stream(display, drawable, stream);
+            if (is_next_frame == STREAM_FRAME_CONTAINER) {
+                drawable->sized_stream = stream;
+            }
+            return;
+        }
+    }
+
+    trace = display->items_trace;
+    trace_end = trace + NUM_TRACE_ITEMS;
+    for (; trace < trace_end; trace++) {
+        if (is_next_stream_frame(display, drawable, trace->width, trace->height,
+                                       &trace->dest_area, trace->time, NULL, FALSE) !=
+                                       STREAM_FRAME_NONE) {
+            if (stream_add_frame(display, drawable,
+                                 trace->frames_count,
+                                 trace->gradual_frames_count,
+                                 trace->last_gradual_frame)) {
+                return;
+            }
+        }
+    }
+}
+
+void stream_maintenance(DisplayChannel *display,
+                        Drawable *candidate, Drawable *prev)
+{
+    int is_next_frame;
+
+    if (candidate->stream) {
+        return;
+    }
+
+    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) {
+            before_reattach_stream(display, stream, candidate);
+            detach_stream(display, stream, FALSE);
+            prev->streamable = FALSE; //prevent item trace
+            attach_stream(display, candidate, stream);
+            if (is_next_frame == STREAM_FRAME_CONTAINER) {
+                candidate->sized_stream = stream;
+            }
+        }
+    } 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) {
+            stream_add_frame(display, candidate,
+                             prev->frames_count,
+                             prev->gradual_frames_count,
+                             prev->last_gradual_frame);
+        }
+    }
+}
diff --git a/server/stream.h b/server/stream.h
index 4704937..bf78137 100644
--- a/server/stream.h
+++ b/server/stream.h
@@ -39,6 +39,7 @@
 #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 MAX_FPS 30
 
 /* move back to display_channel once struct private */
 typedef struct DisplayChannel DisplayChannel;
@@ -143,5 +144,13 @@ void                  stream_unref                                  (DisplayChan
 void                  stream_agent_unref                            (DisplayChannel *display,
                                                                      StreamAgent *agent);
 void                  stream_agent_stats_print                      (StreamAgent *agent);
+void                  stream_trace_update                           (DisplayChannel *display,
+                                                                     Drawable *drawable);
+void                  stream_maintenance                            (DisplayChannel *display,
+                                                                     Drawable *candidate,
+                                                                     Drawable *prev);
+
+void attach_stream(DisplayChannel *display, Drawable *drawable, Stream *stream);
+void detach_stream(DisplayChannel *display, Stream *stream, int detach_sized);
 
 #endif /* STREAM_H */
-- 
2.4.3



More information about the Spice-devel mailing list