diff options
author | Marc-André Lureau <marcandre.lureau@gmail.com> | 2013-09-17 14:52:53 +0200 |
---|---|---|
committer | Fabiano Fidêncio <fidencio@redhat.com> | 2015-02-23 23:00:38 +0100 |
commit | c0fa5717c6454f3dc9e971c44398558079b9077c (patch) | |
tree | 41fe1624ed6d8dbd20ceb7c3ca6bf9538f87feb4 /server/stream.c | |
parent | 6689788288ddd05a34596f101b018c62ee59a418 (diff) | |
download | spice-c0fa5717c6454f3dc9e971c44398558079b9077c.tar.gz spice-c0fa5717c6454f3dc9e971c44398558079b9077c.tar.xz spice-c0fa5717c6454f3dc9e971c44398558079b9077c.zip |
worker: painfully move display_channel_add_drawable
Diffstat (limited to 'server/stream.c')
-rw-r--r-- | server/stream.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/server/stream.c b/server/stream.c index d04a07e7..6c4876d1 100644 --- a/server/stream.c +++ b/server/stream.c @@ -1,6 +1,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 @@ -122,3 +128,382 @@ 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(Drawable *drawable) +{ + SpiceBitmap *bitmap; + spice_return_if_fail(drawable->red_drawable->type == QXL_DRAW_COPY); + + /* TODO: global property -> per dc/dcc */ + if (streaming_video != 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 gboolean red_stream_input_fps_timer_cb(void *opaque) +{ + Stream *stream = opaque; + uint64_t now = red_get_monotonic_time(); + double duration_sec; + + spice_return_val_if_fail(opaque != NULL, TRUE); + spice_return_val_if_fail(now != stream->input_fps_timer_start, TRUE); + + duration_sec = (now - stream->input_fps_timer_start)/(1000.0*1000*1000); + stream->input_fps = stream->num_input_frames / duration_sec; + spice_debug("input-fps=%u", stream->input_fps); + stream->num_input_frames = 0; + stream->input_fps_timer_start = now; + + return TRUE; +} + +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; + + GSource *source = g_timeout_source_new(RED_STREAM_INPUT_FPS_TIMEOUT); + g_source_set_callback(source, red_stream_input_fps_timer_cb, stream, NULL); + stream->input_fps_timer = + g_source_attach(source, red_worker_get_context(COMMON_CHANNEL(display)->worker)); + g_source_unref(source); + + stream->num_input_frames = 0; + stream->input_fps_timer_start = red_get_monotonic_time(); + stream->input_fps = MAX_FPS; + 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(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); + } + } +} |