[Spice-devel] [PATCH spice-server 16/28] red_worker: start using mjpeg_encoder rate control capabilities

Alon Levy alevy at redhat.com
Sun Apr 14 06:29:44 PDT 2013


On Tue, Feb 26, 2013 at 01:04:02PM -0500, Yonit Halperin wrote:
> This patch only employs setting the stream parameters based on
> the initial given bit-rate, the latency, and the encoding size.
> Later patches will also employ mjpeg_encoder response to client reports,
> and its control over frame drops.
> 
> The patch also removes old stream bit rate calculations that weren't
> used.

ACK

> ---
>  server/main_channel.c |   5 +++
>  server/main_channel.h |   1 +
>  server/red_worker.c   | 121 ++++++++++++++++++++++++++------------------------
>  3 files changed, 68 insertions(+), 59 deletions(-)
> 
> diff --git a/server/main_channel.c b/server/main_channel.c
> index 4fdd869..8cc7555 100644
> --- a/server/main_channel.c
> +++ b/server/main_channel.c
> @@ -1152,6 +1152,11 @@ uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc)
>      return mcc->bitrate_per_sec;
>  }
>  
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc)
> +{
> +    return mcc->latency / 1000;
> +}
> +
>  static void main_channel_client_migrate(RedChannelClient *rcc)
>  {
>      reds_on_main_channel_migrate(SPICE_CONTAINEROF(rcc, MainChannelClient, base));
> diff --git a/server/main_channel.h b/server/main_channel.h
> index 285a009..08f919c 100644
> --- a/server/main_channel.h
> +++ b/server/main_channel.h
> @@ -68,6 +68,7 @@ uint32_t main_channel_client_get_link_id(MainChannelClient *mcc);
>  
>  int main_channel_client_is_low_bandwidth(MainChannelClient *mcc);
>  uint64_t main_channel_client_get_bitrate_per_sec(MainChannelClient *mcc);
> +uint64_t main_channel_client_get_roundtrip_ms(MainChannelClient *mcc);
>  int main_channel_is_connected(MainChannel *main_chan);
>  RedChannelClient* main_channel_client_get_base(MainChannelClient* mcc);
>  
> diff --git a/server/red_worker.c b/server/red_worker.c
> index 5043c10..470ad7a 100644
> --- a/server/red_worker.c
> +++ b/server/red_worker.c
> @@ -116,14 +116,11 @@
>  #define RED_STREAM_FRAMES_RESET_CONDITION 100
>  #define RED_STREAM_MIN_SIZE (96 * 96)
>  #define RED_STREAM_INPUT_FPS_TIMEOUT (5 * 1000) // 5 sec
> +#define RED_STREAM_CHANNEL_CAPACITY 0.8
>  
>  #define FPS_TEST_INTERVAL 1
>  #define MAX_FPS 30
>  
> -//best bit rate per pixel base on 13000000 bps for frame size 720x576 pixels and 25 fps
> -#define BEST_BIT_RATE_PER_PIXEL 38
> -#define WORST_BIT_RATE_PER_PIXEL 4
> -
>  #define RED_COMPRESS_BUF_SIZE (1024 * 64)
>  
>  #define ZLIB_DEFAULT_COMPRESSION_LEVEL 3
> @@ -415,6 +412,9 @@ typedef struct ImageItem {
>  
>  typedef struct Drawable Drawable;
>  
> +typedef struct DisplayChannel DisplayChannel;
> +typedef struct DisplayChannelClient DisplayChannelClient;
> +
>  enum {
>      STREAM_FRAME_NONE,
>      STREAM_FRAME_NATIVE,
> @@ -432,7 +432,6 @@ struct Stream {
>      int top_down;
>      Stream *next;
>      RingItem link;
> -    int bit_rate;
>  
>      SpiceTimer *input_fps_timer;
>      uint32_t num_input_frames;
> @@ -455,6 +454,7 @@ typedef struct StreamAgent {
>      Stream *stream;
>      uint64_t last_send_time;
>      MJpegEncoder *mjpeg_encoder;
> +    DisplayChannelClient *dcc;
>  
>      int frames;
>      int drops;
> @@ -526,9 +526,6 @@ typedef struct FreeList {
>      WaitForChannels wait;
>  } FreeList;
>  
> -typedef struct DisplayChannel DisplayChannel;
> -typedef struct DisplayChannelClient DisplayChannelClient;
> -
>  typedef struct  {
>      DisplayChannelClient *dcc;
>      RedCompressBuf *bufs_head;
> @@ -681,6 +678,7 @@ struct DisplayChannelClient {
>      QRegion surface_client_lossy_region[NUM_SURFACES];
>  
>      StreamAgent stream_agents[NUM_STREAMS];
> +    int use_mjpeg_encoder_rate_control;
>  };
>  
>  struct DisplayChannel {
> @@ -974,6 +972,7 @@ typedef struct RedWorker {
>      Ring streams;
>      ItemTrace items_trace[NUM_TRACE_ITEMS];
>      uint32_t next_item_trace;
> +    uint64_t streams_size_total;
>  
>      QuicData quic_data;
>      QuicContext *quic;
> @@ -1049,7 +1048,6 @@ static int red_display_free_some_independent_glz_drawables(DisplayChannelClient
>  static void red_display_free_glz_drawable(DisplayChannelClient *dcc, RedGlzDrawable *drawable);
>  static ImageItem *red_add_surface_area_image(DisplayChannelClient *dcc, int surface_id,
>                                               SpiceRect *area, PipeItem *pos, int can_lossy);
> -static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent);
>  static BitmapGradualType _get_bitmap_graduality_level(RedWorker *worker, SpiceBitmap *bitmap,
>                                                        uint32_t group_id);
>  static inline int _stride_is_extra(SpiceBitmap *bitmap);
> @@ -2604,6 +2602,7 @@ static void red_stop_stream(RedWorker *worker, Stream *stream)
>          stream->refs++;
>          red_channel_client_pipe_add(&dcc->common.base, &stream_agent->destroy_item);
>      }
> +    worker->streams_size_total -= stream->width * stream->height;
>      ring_remove(&stream->link);
>      red_release_stream(worker, stream);
>  }
> @@ -2849,38 +2848,43 @@ static inline Stream *red_alloc_stream(RedWorker *worker)
>      return stream;
>  }
>  
> -static int get_bit_rate(DisplayChannelClient *dcc,
> -    int width, int height)
> +static uint64_t red_stream_get_initial_bit_rate(DisplayChannelClient *dcc,
> +                                                Stream *stream)
>  {
> -    uint64_t bit_rate = width * height * BEST_BIT_RATE_PER_PIXEL;
> +    uint64_t max_bit_rate;
>      MainChannelClient *mcc;
> -    int is_low_bandwidth = 0;
>  
> -    if (dcc) {
> -        mcc = red_client_get_main(dcc->common.base.client);
> -        is_low_bandwidth = main_channel_client_is_low_bandwidth(mcc);
> -    }
> +    mcc = red_client_get_main(dcc->common.base.client);
> +    max_bit_rate = main_channel_client_get_bitrate_per_sec(mcc);
>  
> -    if (is_low_bandwidth) {
> -        bit_rate = MIN(main_channel_client_get_bitrate_per_sec(mcc) * 70 / 100, bit_rate);
> -        bit_rate = MAX(bit_rate, width * height * WORST_BIT_RATE_PER_PIXEL);
> -    }
> -    return bit_rate;
> +
> +    /* 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 * max_bit_rate *
> +           stream->width * stream->height) / dcc->common.worker->streams_size_total;
>  }
>  
> -static int get_minimal_bit_rate(RedWorker *worker, int width, int height)
> +static uint32_t red_stream_mjpeg_encoder_get_roundtrip(void *opaque)
>  {
> -    RingItem *item;
> -    DisplayChannelClient *dcc;
> -    int ret = INT_MAX;
> +    StreamAgent *agent = opaque;
> +    int roundtrip;
>  
> -    WORKER_FOREACH_DCC(worker, item, dcc) {
> -        int bit_rate = get_bit_rate(dcc, width, height);
> -        if (bit_rate < ret) {
> -            ret = bit_rate;
> -        }
> +    spice_assert(agent);
> +    roundtrip = red_channel_client_get_roundtrip_ms(&agent->dcc->common.base);
> +    if (roundtrip < 0) {
> +        MainChannelClient *mcc = red_client_get_main(agent->dcc->common.base.client);
> +        roundtrip = main_channel_client_get_roundtrip_ms(mcc);
>      }
> -    return ret;
> +
> +    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_create_stream(DisplayChannelClient *dcc, Stream *stream)
> @@ -2898,8 +2902,20 @@ static void red_display_create_stream(DisplayChannelClient *dcc, Stream *stream)
>      }
>      agent->drops = 0;
>      agent->fps = MAX_FPS;
> -    reset_rate(dcc, agent);
> -    agent->mjpeg_encoder = mjpeg_encoder_new(FALSE, 0, NULL, NULL);
> +    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;
> +
> +        initial_bit_rate = red_stream_get_initial_bit_rate(dcc, stream);
> +        agent->mjpeg_encoder = mjpeg_encoder_new(TRUE, initial_bit_rate, &mjpeg_cbs, agent);
> +    } else {
> +        agent->mjpeg_encoder = mjpeg_encoder_new(FALSE, 0, NULL, NULL);
> +    }
>      red_channel_client_pipe_add(&dcc->common.base, &agent->create_item);
>  }
>  
> @@ -2929,8 +2945,6 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
>      RingItem *dcc_ring_item;
>      Stream *stream;
>      SpiceRect* src_rect;
> -    int stream_width;
> -    int stream_height;
>  
>      spice_assert(!drawable->stream);
>  
> @@ -2940,8 +2954,6 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
>  
>      spice_assert(drawable->red_drawable->type == QXL_DRAW_COPY);
>      src_rect = &drawable->red_drawable->u.copy.src_area;
> -    stream_width = src_rect->right - src_rect->left;
> -    stream_height = src_rect->bottom - src_rect->top;
>  
>      ring_add(&worker->streams, &stream->link);
>      stream->current = drawable;
> @@ -2950,7 +2962,6 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
>      stream->height = src_rect->bottom - src_rect->top;
>      stream->dest_area = drawable->red_drawable->bbox;
>      stream->refs = 1;
> -    stream->bit_rate = get_minimal_bit_rate(worker, stream_width, stream_height);
>      SpiceBitmap *bitmap = &drawable->red_drawable->u.copy.src_bitmap->u.bitmap;
>      stream->top_down = !!(bitmap->flags & SPICE_BITMAP_FLAGS_TOP_DOWN);
>      drawable->stream = stream;
> @@ -2960,13 +2971,14 @@ static void red_create_stream(RedWorker *worker, Drawable *drawable)
>      stream->num_input_frames = 0;
>      stream->input_fps_timer_start = red_now();
>      stream->input_fps = MAX_FPS;
> +    worker->streams_size_total += stream->width * stream->height;
> +    worker->stream_count++;
>      WORKER_FOREACH_DCC(worker, dcc_ring_item, dcc) {
>          red_display_create_stream(dcc, stream);
>      }
> -    worker->stream_count++;
>      spice_debug("stream %d %dx%d (%d, %d) (%d, %d)", (int)(stream - worker->streams_buf), stream->width,
> -    stream->height, stream->dest_area.left, stream->dest_area.top,
> -    stream->dest_area.right, stream->dest_area.bottom);
> +                stream->height, stream->dest_area.left, stream->dest_area.top,
> +                stream->dest_area.right, stream->dest_area.bottom);
>      return;
>  }
>  
> @@ -2995,6 +3007,7 @@ static void red_display_client_init_streams(DisplayChannelClient *dcc)
>          red_channel_pipe_item_init(channel, &agent->create_item, PIPE_ITEM_TYPE_STREAM_CREATE);
>          red_channel_pipe_item_init(channel, &agent->destroy_item, PIPE_ITEM_TYPE_STREAM_DESTROY);
>      }
> +    dcc->use_mjpeg_encoder_rate_control = TRUE;
>  }
>  
>  static void red_display_destroy_streams(DisplayChannelClient *dcc)
> @@ -3110,19 +3123,6 @@ static inline int red_is_next_stream_frame(RedWorker *worker, const Drawable *ca
>                                        FALSE);
>  }
>  
> -static void reset_rate(DisplayChannelClient *dcc, StreamAgent *stream_agent)
> -{
> -    Stream *stream = stream_agent->stream;
> -    int rate;
> -
> -    rate = get_bit_rate(dcc, stream->width, stream->height);
> -    if (rate == stream->bit_rate) {
> -        return;
> -    }
> -
> -    /* MJpeg has no rate limiting anyway, so do nothing */
> -}
> -
>  static int display_channel_client_is_low_bandwidth(DisplayChannelClient *dcc)
>  {
>      return main_channel_client_is_low_bandwidth(
> @@ -8419,9 +8419,12 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
>      StreamAgent *agent = &dcc->stream_agents[get_stream_id(worker, stream)];
>      uint64_t time_now = red_now();
>      size_t outbuf_size;
> -    if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
> -        agent->frames--;
> -        return TRUE;
> +
> +    if (!dcc->use_mjpeg_encoder_rate_control) {
> +        if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
> +            agent->frames--;
> +            return TRUE;
> +        }
>      }
>  
>      outbuf_size = dcc->send_data.stream_outbuf_size;
> @@ -8432,7 +8435,7 @@ static inline int red_marshall_stream_data(RedChannelClient *rcc,
>                                      drawable->red_drawable->mm_time);
>      switch (ret) {
>      case MJPEG_ENCODER_FRAME_DROP:
> -        spice_warning("mjpeg rate control is not supported yet");
> +        spice_assert(dcc->use_mjpeg_encoder_rate_control);
>          return TRUE;
>      case MJPEG_ENCODER_FRAME_UNSUPPORTED:
>          return FALSE;
> -- 
> 1.8.1
> 
> _______________________________________________
> Spice-devel mailing list
> Spice-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/spice-devel


More information about the Spice-devel mailing list