From cdb6b92f97679412503f4b726fdddeb892c46f56 Mon Sep 17 00:00:00 2001 From: Jonathon Jongsma Date: Fri, 13 Nov 2015 18:14:48 +0100 Subject: Move stream functions to stream.[ch] --- 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 9c8c7b6d..f311d888 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -168,6 +168,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; diff --git a/server/red_worker.c b/server/red_worker.c index 9a00ee63..7560b9d0 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 @@ -314,8 +311,6 @@ static void red_draw_drawable(RedWorker *worker, Drawable *item); static void red_update_area(RedWorker *worker, const SpiceRect *area, int surface_id); static void red_update_area_till(RedWorker *worker, 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); @@ -346,10 +341,6 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient DCC_FOREACH_SAFE(link, next, dcc, RED_CHANNEL(display_channel)) -#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) @@ -1180,7 +1171,7 @@ static inline void __exclude_region(RedWorker *worker, Ring *ring, TreeItem *ite } else { if (frame_candidate) { Drawable *drawable = SPICE_CONTAINEROF(draw, Drawable, tree_item); - display_channel_stream_maintenance(worker->display_channel, frame_candidate, drawable); + stream_maintenance(worker->display_channel, frame_candidate, drawable); } region_exclude(&draw->base.rgn, &and_rgn); } @@ -1284,8 +1275,8 @@ static inline void current_add_drawable(RedWorker *worker, Drawable *drawable, R 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); @@ -1496,17 +1487,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) { @@ -1682,47 +1662,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; @@ -1767,256 +1706,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(RedWorker *worker, DrawItem *item, TreeItem *other) { DisplayChannel *display = worker->display_channel; @@ -2039,7 +1728,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); - display_channel_stream_maintenance(worker->display_channel, drawable, other_drawable); + 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); @@ -2113,61 +1802,6 @@ static inline int red_current_add_equal(RedWorker *worker, DrawItem *item, TreeI 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 inline int current_add(RedWorker *worker, Ring *ring, Drawable *drawable) { DrawItem *item = &drawable->tree_item; @@ -2263,7 +1897,7 @@ static inline int current_add(RedWorker *worker, Ring *ring, Drawable *drawable) 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->display_channel, drawable); + stream_trace_update(worker->display_channel, drawable); streams_update_visible_region(worker->display_channel, drawable); /* * Performing the insertion after exclude_region for diff --git a/server/stream.c b/server/stream.c index 7ef78767..97b9e0a8 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 @@ -68,3 +74,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 f77fa96a..e4178c27 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 typedef struct Stream Stream; @@ -133,5 +134,13 @@ struct Stream { }; 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 */ -- cgit