From: Marc-André Lureau <marcandre.lureau@xxxxxxxxx> --- server/display-channel.h | 2 + server/red_worker.c | 178 +---------------------------------------------- server/stream.c | 174 +++++++++++++++++++++++++++++++++++++++++++++ server/stream.h | 3 + 4 files changed, 180 insertions(+), 177 deletions(-) diff --git a/server/display-channel.h b/server/display-channel.h index edbd4b9..ae8a900 100644 --- a/server/display-channel.h +++ b/server/display-channel.h @@ -252,6 +252,8 @@ void dcc_push_destroy_surface (DisplayCha uint32_t surface_id); void dcc_add_stream_agent_clip (DisplayChannelClient* dcc, StreamAgent *agent); +void dcc_create_stream (DisplayChannelClient *dcc, + Stream *stream); typedef struct DrawablePipeItem { RingItem base; /* link for a list of pipe items held by Drawable */ diff --git a/server/red_worker.c b/server/red_worker.c index 6a331cb..5269752 100644 --- a/server/red_worker.c +++ b/server/red_worker.c @@ -80,7 +80,6 @@ #define DISPLAY_FREE_LIST_DEFAULT_SIZE 128 #define FPS_TEST_INTERVAL 1 -#define MAX_FPS 30 #define ZLIB_DEFAULT_COMPRESSION_LEVEL 3 #define MIN_GLZ_SIZE_FOR_ZLIB 100 @@ -1353,181 +1352,6 @@ static Stream *display_channel_stream_try_new(DisplayChannel *display) return stream; } -static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc, - Stream *stream) -{ - char *env_bit_rate_str; - uint64_t bit_rate = 0; - - env_bit_rate_str = getenv("SPICE_BIT_RATE"); - if (env_bit_rate_str != NULL) { - double env_bit_rate; - - errno = 0; - env_bit_rate = strtod(env_bit_rate_str, NULL); - if (errno == 0) { - bit_rate = env_bit_rate * 1024 * 1024; - } else { - spice_warning("error parsing SPICE_BIT_RATE: %s", strerror(errno)); - } - } - - if (!bit_rate) { - MainChannelClient *mcc; - uint64_t net_test_bit_rate; - - mcc = red_client_get_main(RED_CHANNEL_CLIENT(dcc)->client); - net_test_bit_rate = main_channel_client_is_network_info_initialized(mcc) ? - main_channel_client_get_bitrate_per_sec(mcc) : - 0; - bit_rate = MAX(dcc->streams_max_bit_rate, net_test_bit_rate); - if (bit_rate == 0) { - /* - * In case we are after a spice session migration, - * the low_bandwidth flag is retrieved from migration data. - * If the network info is not initialized due to another reason, - * the low_bandwidth flag is FALSE. - */ - bit_rate = dcc->common.is_low_bandwidth ? - RED_STREAM_DEFAULT_LOW_START_BIT_RATE : - RED_STREAM_DEFAULT_HIGH_START_BIT_RATE; - } - } - - spice_debug("base-bit-rate %.2f (Mbps)", bit_rate / 1024.0 / 1024.0); - /* 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_DC(dcc)->streams_size_total; -} - -static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque) -{ - StreamAgent *agent = opaque; - int roundtrip; - - spice_assert(agent); - roundtrip = red_channel_client_get_roundtrip_ms(RED_CHANNEL_CLIENT(agent->dcc)); - if (roundtrip < 0) { - MainChannelClient *mcc = red_client_get_main(RED_CHANNEL_CLIENT(agent->dcc)->client); - - /* - * the main channel client roundtrip might not have been - * calculated (e.g., after migration). In such case, - * main_channel_client_get_roundtrip_ms returns 0. - */ - roundtrip = main_channel_client_get_roundtrip_ms(mcc); - } - - return roundtrip; -} - -static uint32_t red_stream_mjpeg_encoder_get_source_fps(void *opaque) -{ - StreamAgent *agent = opaque; - - spice_assert(agent); - return agent->stream->input_fps; -} - -static void red_display_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgent *remove_agent) -{ - uint32_t new_max_latency = 0; - int i; - - if (dcc->streams_max_latency != remove_agent->client_required_latency) { - return; - } - - dcc->streams_max_latency = 0; - if (DCC_TO_DC(dcc)->stream_count == 1) { - return; - } - for (i = 0; i < NUM_STREAMS; i++) { - StreamAgent *other_agent = &dcc->stream_agents[i]; - if (other_agent == remove_agent || !other_agent->mjpeg_encoder) { - continue; - } - if (other_agent->client_required_latency > new_max_latency) { - new_max_latency = other_agent->client_required_latency; - } - } - dcc->streams_max_latency = new_max_latency; -} - -static void red_display_stream_agent_stop(DisplayChannelClient *dcc, StreamAgent *agent) -{ - red_display_update_streams_max_latency(dcc, agent); - if (agent->mjpeg_encoder) { - mjpeg_encoder_destroy(agent->mjpeg_encoder); - agent->mjpeg_encoder = NULL; - } -} - -static void red_stream_update_client_playback_latency(void *opaque, uint32_t delay_ms) -{ - StreamAgent *agent = opaque; - DisplayChannelClient *dcc = agent->dcc; - - red_display_update_streams_max_latency(dcc, agent); - - agent->client_required_latency = delay_ms; - if (delay_ms > agent->dcc->streams_max_latency) { - agent->dcc->streams_max_latency = delay_ms; - } - spice_debug("resetting client latency: %u", agent->dcc->streams_max_latency); - main_dispatcher_set_mm_time_latency(RED_CHANNEL_CLIENT(agent->dcc)->client, agent->dcc->streams_max_latency); -} - -static void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) -{ - StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_DC(dcc), stream)]; - - stream->refs++; - spice_assert(region_is_empty(&agent->vis_region)); - if (stream->current) { - agent->frames = 1; - region_clone(&agent->vis_region, &stream->current->tree_item.base.rgn); - region_clone(&agent->clip, &agent->vis_region); - } else { - agent->frames = 0; - } - agent->drops = 0; - agent->fps = MAX_FPS; - agent->dcc = dcc; - - if (dcc->use_mjpeg_encoder_rate_control) { - MJpegEncoderRateControlCbs mjpeg_cbs; - uint64_t initial_bit_rate; - - mjpeg_cbs.get_roundtrip_ms = red_stream_mjpeg_encoder_get_roundtrip; - mjpeg_cbs.get_source_fps = red_stream_mjpeg_encoder_get_source_fps; - mjpeg_cbs.update_client_playback_delay = red_stream_update_client_playback_latency; - - initial_bit_rate = red_stream_get_initial_bit_rate(dcc, stream); - agent->mjpeg_encoder = mjpeg_encoder_new(initial_bit_rate, &mjpeg_cbs, agent); - } else { - agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL, NULL); - } - red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &agent->create_item); - - if (red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_STREAM_REPORT)) { - StreamActivateReportItem *report_pipe_item = spice_malloc0(sizeof(*report_pipe_item)); - - 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_DC(dcc), stream); - red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &report_pipe_item->pipe_item); - } -#ifdef STREAM_STATS - memset(&agent->stats, 0, sizeof(StreamStats)); - if (stream->current) { - agent->stats.start = stream->current->red_drawable->mm_time; - } -#endif -} - static void display_channel_create_stream(DisplayChannel *display, Drawable *drawable) { DisplayChannelClient *dcc; @@ -6872,7 +6696,7 @@ static void red_display_marshall_stream_end(RedChannelClient *rcc, red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY, NULL); destroy.id = get_stream_id(DCC_TO_DC(dcc), agent->stream); - red_display_stream_agent_stop(dcc, agent); + stream_agent_stop(dcc, agent); spice_marshall_msg_display_stream_destroy(base_marshaller, &destroy); } diff --git a/server/stream.c b/server/stream.c index 406111c..27d6d36 100644 --- a/server/stream.c +++ b/server/stream.c @@ -138,3 +138,177 @@ StreamClipItem *stream_clip_item_new(DisplayChannelClient* dcc, StreamAgent *age item->refs = 1; return item; } + +static void dcc_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgent *remove_agent) +{ + uint32_t new_max_latency = 0; + int i; + + if (dcc->streams_max_latency != remove_agent->client_required_latency) { + return; + } + + dcc->streams_max_latency = 0; + if (DCC_TO_DC(dcc)->stream_count == 1) { + return; + } + for (i = 0; i < NUM_STREAMS; i++) { + StreamAgent *other_agent = &dcc->stream_agents[i]; + if (other_agent == remove_agent || !other_agent->mjpeg_encoder) { + continue; + } + if (other_agent->client_required_latency > new_max_latency) { + new_max_latency = other_agent->client_required_latency; + } + } + dcc->streams_max_latency = new_max_latency; +} + +static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc, + Stream *stream) +{ + char *env_bit_rate_str; + uint64_t bit_rate = 0; + + env_bit_rate_str = getenv("SPICE_BIT_RATE"); + if (env_bit_rate_str != NULL) { + double env_bit_rate; + + errno = 0; + env_bit_rate = strtod(env_bit_rate_str, NULL); + if (errno == 0) { + bit_rate = env_bit_rate * 1024 * 1024; + } else { + spice_warning("error parsing SPICE_BIT_RATE: %s", strerror(errno)); + } + } + + if (!bit_rate) { + MainChannelClient *mcc; + uint64_t net_test_bit_rate; + + mcc = red_client_get_main(RED_CHANNEL_CLIENT(dcc)->client); + net_test_bit_rate = main_channel_client_is_network_info_initialized(mcc) ? + main_channel_client_get_bitrate_per_sec(mcc) : + 0; + bit_rate = MAX(dcc->streams_max_bit_rate, net_test_bit_rate); + if (bit_rate == 0) { + /* + * In case we are after a spice session migration, + * the low_bandwidth flag is retrieved from migration data. + * If the network info is not initialized due to another reason, + * the low_bandwidth flag is FALSE. + */ + bit_rate = dcc->common.is_low_bandwidth ? + RED_STREAM_DEFAULT_LOW_START_BIT_RATE : + RED_STREAM_DEFAULT_HIGH_START_BIT_RATE; + } + } + + spice_debug("base-bit-rate %.2f (Mbps)", bit_rate / 1024.0 / 1024.0); + /* 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_DC(dcc)->streams_size_total; +} + +static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque) +{ + StreamAgent *agent = opaque; + int roundtrip; + + roundtrip = red_channel_client_get_roundtrip_ms(RED_CHANNEL_CLIENT(agent->dcc)); + if (roundtrip < 0) { + MainChannelClient *mcc = red_client_get_main(RED_CHANNEL_CLIENT(agent->dcc)->client); + + /* + * the main channel client roundtrip might not have been + * calculated (e.g., after migration). In such case, + * main_channel_client_get_roundtrip_ms returns 0. + */ + roundtrip = main_channel_client_get_roundtrip_ms(mcc); + } + + return roundtrip; +} + +static uint32_t red_stream_mjpeg_encoder_get_source_fps(void *opaque) +{ + StreamAgent *agent = opaque; + + return agent->stream->input_fps; +} + +static void red_stream_update_client_playback_latency(void *opaque, uint32_t delay_ms) +{ + StreamAgent *agent = opaque; + DisplayChannelClient *dcc = agent->dcc; + + dcc_update_streams_max_latency(dcc, agent); + + agent->client_required_latency = delay_ms; + if (delay_ms > agent->dcc->streams_max_latency) { + agent->dcc->streams_max_latency = delay_ms; + } + spice_debug("resetting client latency: %u", agent->dcc->streams_max_latency); + main_dispatcher_set_mm_time_latency(RED_CHANNEL_CLIENT(agent->dcc)->client, agent->dcc->streams_max_latency); +} + +void dcc_create_stream(DisplayChannelClient *dcc, Stream *stream) +{ + StreamAgent *agent = &dcc->stream_agents[get_stream_id(DCC_TO_DC(dcc), stream)]; + + spice_return_if_fail(region_is_empty(&agent->vis_region)); + + stream->refs++; + if (stream->current) { + agent->frames = 1; + region_clone(&agent->vis_region, &stream->current->tree_item.base.rgn); + region_clone(&agent->clip, &agent->vis_region); + } else { + agent->frames = 0; + } + agent->drops = 0; + agent->fps = MAX_FPS; + agent->dcc = dcc; + + if (dcc->use_mjpeg_encoder_rate_control) { + MJpegEncoderRateControlCbs mjpeg_cbs; + uint64_t initial_bit_rate; + + mjpeg_cbs.get_roundtrip_ms = red_stream_mjpeg_encoder_get_roundtrip; + mjpeg_cbs.get_source_fps = red_stream_mjpeg_encoder_get_source_fps; + mjpeg_cbs.update_client_playback_delay = red_stream_update_client_playback_latency; + + initial_bit_rate = red_stream_get_initial_bit_rate(dcc, stream); + agent->mjpeg_encoder = mjpeg_encoder_new(initial_bit_rate, &mjpeg_cbs, agent); + } else { + agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL, NULL); + } + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &agent->create_item); + + if (red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), SPICE_DISPLAY_CAP_STREAM_REPORT)) { + StreamActivateReportItem *report_pipe_item = spice_malloc0(sizeof(*report_pipe_item)); + + 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_DC(dcc), stream); + red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &report_pipe_item->pipe_item); + } +#ifdef STREAM_STATS + memset(&agent->stats, 0, sizeof(StreamStats)); + if (stream->current) { + agent->stats.start = stream->current->red_drawable->mm_time; + } +#endif +} + +void stream_agent_stop(DisplayChannelClient *dcc, StreamAgent *agent) +{ + dcc_update_streams_max_latency(dcc, agent); + if (agent->mjpeg_encoder) { + mjpeg_encoder_destroy(agent->mjpeg_encoder); + agent->mjpeg_encoder = NULL; + } +} diff --git a/server/stream.h b/server/stream.h index 09df31b..c2007af 100644 --- a/server/stream.h +++ b/server/stream.h @@ -39,6 +39,7 @@ #define RED_STREAM_CLIENT_REPORT_TIMEOUT 1000 // milliseconds #define RED_STREAM_DEFAULT_HIGH_START_BIT_RATE (10 * 1024 * 1024) // 10Mbps #define RED_STREAM_DEFAULT_LOW_START_BIT_RATE (2.5 * 1024 * 1024) // 2.5Mbps +#define MAX_FPS 30 /* move back to display_channel once struct private */ typedef struct DisplayChannel DisplayChannel; @@ -143,5 +144,7 @@ void stream_unref (DisplayChan void stream_agent_unref (DisplayChannel *display, StreamAgent *agent); void stream_agent_stats_print (StreamAgent *agent); +void stream_agent_stop (DisplayChannelClient *dcc, + StreamAgent *agent); #endif /* STREAM_H */ -- 2.4.3 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel