From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> --- 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; } - 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" 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; -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel