[Spice-devel] [PATCH 03/10] worker: move stream to display channel
Frediano Ziglio
fziglio at redhat.com
Fri Nov 6 03:01:59 PST 2015
From: Marc-André Lureau <marcandre.lureau at gmail.com>
---
server/Makefile.am | 2 +
server/display-channel.c | 44 +++
server/display-channel.h | 82 ++---
server/red_worker.c | 916 ++++++++++++++++++++---------------------------
server/red_worker.h | 1 +
server/stream.c | 66 ++++
server/stream.h | 143 ++++++++
server/utils.h | 1 +
8 files changed, 664 insertions(+), 591 deletions(-)
create mode 100644 server/stream.c
create mode 100644 server/stream.h
diff --git a/server/Makefile.am b/server/Makefile.am
index 7216ab0..52703c9 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -136,6 +136,8 @@ libspice_server_la_SOURCES = \
spice-bitmap-utils.h \
spice-bitmap-utils.c \
utils.h \
+ stream.c \
+ stream.h \
$(NULL)
if HAVE_GL
diff --git a/server/display-channel.c b/server/display-channel.c
index 5deab13..3df6a31 100644
--- a/server/display-channel.c
+++ b/server/display-channel.c
@@ -221,3 +221,47 @@ void dcc_push_monitors_config(DisplayChannelClient *dcc)
red_monitors_config_item_add(dcc);
red_channel_client_push(&dcc->common.base);
}
+
+int display_channel_get_streams_timeout(DisplayChannel *display)
+{
+ int timeout = INT_MAX;
+ Ring *ring = &display->streams;
+ RingItem *item = ring;
+
+ red_time_t now = red_get_monotonic_time();
+ while ((item = ring_next(ring, item))) {
+ Stream *stream;
+
+ stream = SPICE_CONTAINEROF(item, Stream, link);
+ red_time_t delta = (stream->last_time + RED_STREAM_TIMEOUT) - now;
+
+ if (delta < 1000 * 1000) {
+ return 0;
+ }
+ timeout = MIN(timeout, (unsigned int)(delta / (1000 * 1000)));
+ }
+ return timeout;
+}
+
+void display_channel_set_stream_video(DisplayChannel *display, int stream_video)
+{
+ spice_return_if_fail(display);
+ spice_return_if_fail(stream_video != SPICE_STREAM_VIDEO_INVALID);
+
+ switch (stream_video) {
+ case SPICE_STREAM_VIDEO_ALL:
+ spice_info("sv all");
+ break;
+ case SPICE_STREAM_VIDEO_FILTER:
+ spice_info("sv filter");
+ break;
+ case SPICE_STREAM_VIDEO_OFF:
+ spice_info("sv off");
+ break;
+ default:
+ spice_warn_if_reached();
+ return;
+ }
+
+ display->stream_video = stream_video;
+}
diff --git a/server/display-channel.h b/server/display-channel.h
index 827a9d4..b92bc5c 100644
--- a/server/display-channel.h
+++ b/server/display-channel.h
@@ -54,11 +54,9 @@
#include "spice_image_cache.h"
#include "utils.h"
#include "tree.h"
+#include "stream.h"
typedef struct DisplayChannel DisplayChannel;
-typedef struct DisplayChannelClient DisplayChannelClient;
-
-typedef struct Drawable Drawable;
#define PALETTE_CACHE_HASH_SHIFT 8
#define PALETTE_CACHE_HASH_SIZE (1 << PALETTE_CACHE_HASH_SHIFT)
@@ -128,23 +126,6 @@ typedef struct {
EncoderData data;
} GlzData;
-typedef struct Stream Stream;
-struct Stream {
- uint8_t refs;
- Drawable *current;
- red_time_t last_time;
- int width;
- int height;
- SpiceRect dest_area;
- int top_down;
- Stream *next;
- RingItem link;
-
- uint32_t num_input_frames;
- uint64_t input_fps_start_time;
- uint32_t input_fps;
-};
-
typedef struct DependItem {
Drawable *drawable;
RingItem ring_item;
@@ -179,48 +160,6 @@ struct Drawable {
uint32_t process_commands_generation;
};
-#define STREAM_STATS
-#ifdef STREAM_STATS
-typedef struct StreamStats {
- uint64_t num_drops_pipe;
- uint64_t num_drops_fps;
- uint64_t num_frames_sent;
- uint64_t num_input_frames;
- uint64_t size_sent;
-
- uint64_t start;
- uint64_t end;
-} StreamStats;
-#endif
-
-typedef struct StreamAgent {
- QRegion vis_region; /* the part of the surface area that is currently occupied by video
- fragments */
- QRegion clip; /* the current video clipping. It can be different from vis_region:
- for example, let c1 be the clip area at time t1, and c2
- be the clip area at time t2, where t1 < t2. If c1 contains c2, and
- at least part of c1/c2, hasn't been covered by a non-video images,
- vis_region will contain c2 and also the part of c1/c2 that still
- displays fragments of the video */
-
- PipeItem create_item;
- PipeItem destroy_item;
- Stream *stream;
- uint64_t last_send_time;
- MJpegEncoder *mjpeg_encoder;
- DisplayChannelClient *dcc;
-
- int frames;
- int drops;
- int fps;
-
- uint32_t report_id;
- uint32_t client_required_latency;
-#ifdef STREAM_STATS
- StreamStats stats;
-#endif
-} StreamAgent;
-
struct DisplayChannelClient {
CommonChannelClient common;
@@ -328,6 +267,10 @@ MonitorsConfig* monitors_config_new (QXLHead *h
MonitorsConfig * monitors_config_ref (MonitorsConfig *config);
void monitors_config_unref (MonitorsConfig *config);
+#define TRACE_ITEMS_SHIFT 3
+#define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
+#define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
+
struct DisplayChannel {
CommonChannel common; // Must be the first thing
@@ -344,6 +287,15 @@ struct DisplayChannel {
RedCompressBuf *free_compress_bufs;
+ int stream_video;
+ uint32_t stream_count;
+ Stream streams_buf[NUM_STREAMS];
+ Stream *free_streams;
+ Ring streams;
+ ItemTrace items_trace[NUM_TRACE_ITEMS];
+ uint32_t next_item_trace;
+ uint64_t streams_size_total;
+
#ifdef RED_STATISTICS
uint64_t *cache_hits_counter;
uint64_t *add_to_cache_counter;
@@ -360,6 +312,12 @@ struct DisplayChannel {
#endif
};
+void display_channel_set_stream_video (DisplayChannel *display,
+ int stream_video);
+void display_channel_attach_stream (DisplayChannel *display,
+ Drawable *drawable,
+ Stream *stream);
+int display_channel_get_streams_timeout (DisplayChannel *display);
void display_channel_compress_stats_print (const DisplayChannel *display);
void display_channel_compress_stats_reset (DisplayChannel *display);
diff --git a/server/red_worker.c b/server/red_worker.c
index df5be98..7a4822a 100644
--- a/server/red_worker.c
+++ b/server/red_worker.c
@@ -58,6 +58,7 @@
#include "common/generated_server_marshallers.h"
#include "display-channel.h"
+#include "stream.h"
#include "spice.h"
#include "red_worker.h"
@@ -80,21 +81,6 @@
#define DISPLAY_FREE_LIST_DEFAULT_SIZE 128
-#define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
-#define RED_STREAM_CONTINUS_MAX_DELTA (1000 * 1000 * 1000)
-#define RED_STREAM_TIMOUT (1000 * 1000 * 1000)
-#define RED_STREAM_FRAMES_START_CONDITION 20
-#define RED_STREAM_GRADUAL_FRAMES_START_CONDITION 0.2
-#define RED_STREAM_FRAMES_RESET_CONDITION 100
-#define RED_STREAM_MIN_SIZE (96 * 96)
-#define RED_STREAM_INPUT_FPS_TIMEOUT ((uint64_t)5 * 1000 * 1000 * 1000) // 5 sec
-#define RED_STREAM_CHANNEL_CAPACITY 0.8
-/* the client's stream report frequency is the minimum of the 2 values below */
-#define RED_STREAM_CLIENT_REPORT_WINDOW 5 // #frames
-#define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds
-#define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps
-#define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps
-
#define FPS_TEST_INTERVAL 1
#define MAX_FPS 30
@@ -241,11 +227,6 @@ typedef struct SurfaceDestroyItem {
PipeItem pipe_item;
} SurfaceDestroyItem;
-typedef struct StreamActivateReportItem {
- PipeItem pipe_item;
- uint32_t stream_id;
-} StreamActivateReportItem;
-
#define MAX_PIPE_SIZE 50
#define WIDE_CLIENT_ACK_WINDOW 40
@@ -266,20 +247,6 @@ typedef struct ImageItem {
uint8_t data[0];
} ImageItem;
-enum {
- STREAM_FRAME_NONE,
- STREAM_FRAME_NATIVE,
- STREAM_FRAME_CONTAINER,
-};
-
-typedef struct StreamClipItem {
- PipeItem base;
- int refs;
- StreamAgent *stream_agent;
- int clip_type;
- SpiceClipRects *rects;
-} StreamClipItem;
-
typedef struct {
QuicUsrContext usr;
EncoderData data;
@@ -382,20 +349,6 @@ typedef struct RedSurface {
QXLReleaseInfoExt create, destroy;
} RedSurface;
-typedef struct ItemTrace {
- red_time_t time;
- int frames_count;
- int gradual_frames_count;
- int last_gradual_frame;
- int width;
- int height;
- SpiceRect dest_area;
-} ItemTrace;
-
-#define TRACE_ITEMS_SHIFT 3
-#define NUM_TRACE_ITEMS (1 << TRACE_ITEMS_SHIFT)
-#define ITEMS_TRACE_MASK (NUM_TRACE_ITEMS - 1)
-
#define NUM_DRAWABLES 1000
#define NUM_CURSORS 100
@@ -428,7 +381,6 @@ typedef struct RedWorker {
uint32_t shadows_count;
uint32_t containers_count;
- uint32_t stream_count;
uint32_t bits_unique;
@@ -443,14 +395,6 @@ typedef struct RedWorker {
spice_wan_compression_t jpeg_state;
spice_wan_compression_t zlib_glz_state;
- uint32_t streaming_video;
- Stream streams_buf[NUM_STREAMS];
- Stream *free_streams;
- Ring streams;
- ItemTrace items_trace[NUM_TRACE_ITEMS];
- uint32_t next_item_trace;
- uint64_t streams_size_total;
-
QuicData quic_data;
QuicContext *quic;
@@ -519,16 +463,12 @@ static void red_update_area(RedWorker *worker, const SpiceRect *area, int surfac
static void red_update_area_till(RedWorker *worker, const SpiceRect *area, int surface_id,
Drawable *last);
static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable);
-static void red_display_release_stream(RedWorker *worker, StreamAgent *agent);
-static inline void red_detach_stream(RedWorker *worker, Stream *stream, int detach_sized);
-static void red_stop_stream(RedWorker *worker, Stream *stream);
-static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *sect);
+static inline void display_channel_stream_maintenance(DisplayChannel *display, Drawable *candidate, Drawable *sect);
static inline void display_begin_send_message(RedChannelClient *rcc);
static void red_release_glz(DisplayChannelClient *dcc);
static void red_freeze_glz(DisplayChannelClient *dcc);
static void display_channel_push_release(DisplayChannelClient *dcc, uint8_t type, uint64_t id,
uint64_t* sync_data);
-static void red_display_release_stream_clip(RedWorker *worker, StreamClipItem *item);
static int red_display_free_some_independent_glz_drawables(DisplayChannelClient *dcc);
static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
@@ -565,6 +505,136 @@ static void display_channel_client_release_item_after_push(DisplayChannelClient
SAFE_FOREACH(link, next, drawable, &(drawable)->glz_ring, glz, LINK_TO_GLZ(link))
+static int get_stream_id(DisplayChannel *display, Stream *stream)
+{
+ return (int)(stream - display->streams_buf);
+}
+
+static void display_stream_free(DisplayChannel *display, Stream *stream)
+{
+ stream->next = display->free_streams;
+ display->free_streams = stream;
+}
+
+static void display_stream_unref(DisplayChannel *display, Stream *stream)
+{
+ if (--stream->refs != 0)
+ return;
+
+ spice_warn_if_fail(!ring_item_is_linked(&stream->link));
+ display_stream_free(display, stream);
+ display->stream_count--;
+}
+
+static void display_stream_agent_unref(DisplayChannel *display, StreamAgent *agent)
+{
+ display_stream_unref(display, agent->stream);
+}
+
+static void display_stream_clip_unref(DisplayChannel *display, StreamClipItem *item)
+{
+ if (--item->refs != 0)
+ return;
+
+ display_stream_agent_unref(display, item->stream_agent);
+ free(item->rects);
+ free(item);
+}
+
+static void dcc_push_stream_agent_clip(DisplayChannelClient* dcc, StreamAgent *agent)
+{
+ StreamClipItem *item = stream_clip_item_new(dcc, agent);
+ int n_rects;
+
+ item->clip_type = SPICE_CLIP_TYPE_RECTS;
+
+ n_rects = pixman_region32_n_rects(&agent->clip);
+ item->rects = spice_malloc_n_m(n_rects, sizeof(SpiceRect), sizeof(SpiceClipRects));
+ item->rects->num_rects = n_rects;
+ region_ret_rects(&agent->clip, item->rects->rects, n_rects);
+
+ red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), (PipeItem *)item);
+}
+
+
+void attach_stream(DisplayChannel *display, Drawable *drawable, Stream *stream)
+{
+ DisplayChannelClient *dcc;
+ RingItem *item, *next;
+
+ spice_assert(!drawable->stream && !stream->current);
+ spice_assert(drawable && stream);
+ stream->current = drawable;
+ drawable->stream = stream;
+ stream->last_time = drawable->creation_time;
+
+ uint64_t duration = drawable->creation_time - stream->input_fps_start_time;
+ if (duration >= RED_STREAM_INPUT_FPS_TIMEOUT) {
+ /* Round to the nearest integer, for instance 24 for 23.976 */
+ stream->input_fps = ((uint64_t)stream->num_input_frames * 1000 * 1000 * 1000 + duration / 2) / duration;
+ spice_debug("input-fps=%u", stream->input_fps);
+ stream->num_input_frames = 0;
+ stream->input_fps_start_time = drawable->creation_time;
+ } else {
+ stream->num_input_frames++;
+ }
+
+ FOREACH_DCC(display, item, next, dcc) {
+ StreamAgent *agent;
+ QRegion clip_in_draw_dest;
+
+ agent = &dcc->stream_agents[get_stream_id(display, stream)];
+ region_or(&agent->vis_region, &drawable->tree_item.base.rgn);
+
+ region_init(&clip_in_draw_dest);
+ region_add(&clip_in_draw_dest, &drawable->red_drawable->bbox);
+ region_and(&clip_in_draw_dest, &agent->clip);
+
+ if (!region_is_equal(&clip_in_draw_dest, &drawable->tree_item.base.rgn)) {
+ region_remove(&agent->clip, &drawable->red_drawable->bbox);
+ region_or(&agent->clip, &drawable->tree_item.base.rgn);
+ dcc_push_stream_agent_clip(dcc, agent);
+ }
+#ifdef STREAM_STATS
+ agent->stats.num_input_frames++;
+#endif
+ }
+}
+
+static void stop_stream(DisplayChannel *display, Stream *stream)
+{
+ DisplayChannelClient *dcc;
+ RingItem *item, *next;
+
+ spice_assert(ring_item_is_linked(&stream->link));
+ spice_assert(!stream->current);
+ spice_debug("stream %d", get_stream_id(display, stream));
+ FOREACH_DCC(display, item, next, dcc) {
+ StreamAgent *stream_agent;
+
+ stream_agent = &dcc->stream_agents[get_stream_id(display, stream)];
+ region_clear(&stream_agent->vis_region);
+ region_clear(&stream_agent->clip);
+ spice_assert(!pipe_item_is_linked(&stream_agent->destroy_item));
+ if (stream_agent->mjpeg_encoder && dcc->use_mjpeg_encoder_rate_control) {
+ uint64_t stream_bit_rate = mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder);
+
+ if (stream_bit_rate > dcc->streams_max_bit_rate) {
+ spice_debug("old max-bit-rate=%.2f new=%.2f",
+ dcc->streams_max_bit_rate / 8.0 / 1024.0 / 1024.0,
+ stream_bit_rate / 8.0 / 1024.0 / 1024.0);
+ dcc->streams_max_bit_rate = stream_bit_rate;
+ }
+ }
+ stream->refs++;
+ red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &stream_agent->destroy_item);
+ stream_agent_stats_print(stream_agent);
+ }
+ display->streams_size_total -= stream->width * stream->height;
+ ring_remove(&stream->link);
+ display_stream_unref(display, stream);
+}
+
/* fixme: move to display channel */
DrawablePipeItem *drawable_pipe_item_new(DisplayChannelClient *dcc,
Drawable *drawable)
@@ -610,7 +680,7 @@ QXLInstance* red_worker_get_qxl(RedWorker *worker)
return worker->qxl;
}
-static inline int is_primary_surface(RedWorker *worker, uint32_t surface_id)
+static inline int is_primary_surface(DisplayChannel *display, uint32_t surface_id)
{
if (surface_id == 0) {
return TRUE;
@@ -896,8 +966,6 @@ static void drawables_init(RedWorker *worker)
}
-static void red_reset_stream_trace(RedWorker *worker);
-
static SurfaceDestroyItem *get_surface_destroy_item(RedChannel *channel,
uint32_t surface_id)
{
@@ -929,16 +997,36 @@ static inline void red_destroy_surface_item(RedWorker *worker,
red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &destroy->pipe_item);
}
+static void stop_streams(DisplayChannel *display)
+{
+ Ring *ring = &display->streams;
+ RingItem *item = ring_get_head(ring);
+
+ while (item) {
+ Stream *stream = SPICE_CONTAINEROF(item, Stream, link);
+ item = ring_next(ring, item);
+ if (!stream->current) {
+ stop_stream(display, stream);
+ } else {
+ spice_info("attached stream");
+ }
+ }
+
+ display->next_item_trace = 0;
+ memset(display->items_trace, 0, sizeof(display->items_trace));
+}
+
static inline void red_destroy_surface(RedWorker *worker, uint32_t surface_id)
{
+ DisplayChannel *display = worker->display_channel;
RedSurface *surface = &worker->surfaces[surface_id];
DisplayChannelClient *dcc;
RingItem *link, *next;
if (!--surface->refs) {
// only primary surface streams are supported
- if (is_primary_surface(worker, surface_id)) {
- red_reset_stream_trace(worker);
+ if (is_primary_surface(worker->display_channel, surface_id)) {
+ stop_streams(display);
}
spice_assert(surface->context.canvas);
@@ -1025,6 +1113,9 @@ static void remove_drawable_dependencies(RedWorker *worker, Drawable *drawable)
}
}
+static void detach_stream(DisplayChannel *display, Stream *stream,
+ int detach_sized);
+
static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable)
{
RingItem *item, *next;
@@ -1036,7 +1127,7 @@ static void red_worker_drawable_unref(RedWorker *worker, Drawable *drawable)
spice_warn_if_fail(ring_is_empty(&drawable->pipes));
if (drawable->stream) {
- red_detach_stream(worker, drawable->stream, TRUE);
+ detach_stream(worker->display_channel, drawable->stream, TRUE);
}
region_destroy(&drawable->tree_item.base.rgn);
@@ -1094,14 +1185,15 @@ static inline void container_cleanup(RedWorker *worker, Container *container)
}
}
-static inline void red_add_item_trace(RedWorker *worker, Drawable *item)
+static void display_stream_trace_add_drawable(DisplayChannel *display, Drawable *item)
{
ItemTrace *trace;
- if (!item->streamable) {
+
+ if (!item->stream || !item->streamable) {
return;
}
- 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
More information about the Spice-devel
mailing list