[Spice-devel] [spice v10 09/27] server: Let the video encoder manage the compressed buffer

Christophe Fergeau cfergeau at redhat.com
Thu Mar 3 16:58:36 UTC 2016


Hey,

On Tue, Mar 01, 2016 at 04:53:03PM +0100, Francois Gouget wrote:
> This way the video encoder is not forced to use malloc()/free().
> This also allows more flexibility in how the video encoder manages the
> buffer which allows for a zero-copy implementation in both video
> encoders.

Yes, this is quite nice :)

> 
> Signed-off-by: Francois Gouget <fgouget at codeweavers.com>
> ---
>  server/dcc-send.c          | 25 +++++++-------
>  server/dcc.c               |  5 ---
>  server/dcc.h               |  3 --
>  server/gstreamer-encoder.c | 55 +++++++++++++++++++-----------
>  server/mjpeg-encoder.c     | 85 +++++++++++++++++++++++++++++-----------------
>  server/video-encoder.h     | 26 ++++++++++----
>  6 files changed, 122 insertions(+), 77 deletions(-)
> 
> diff --git a/server/dcc-send.c b/server/dcc-send.c
> index 6cf16e2..68ea085 100644
> --- a/server/dcc-send.c
> +++ b/server/dcc-send.c
> @@ -1649,6 +1649,12 @@ static void red_lossy_marshall_qxl_draw_text(RedChannelClient *rcc,
>      }
>  }
>  
> +static void red_release_video_encoder_buffer(uint8_t *data, void *opaque)
> +{
> +    VideoBuffer *buffer = (VideoBuffer*)opaque;
> +    buffer->free(buffer);
> +}
> +
>  static int red_marshall_stream_data(RedChannelClient *rcc,
>                                      SpiceMarshaller *base_marshaller, Drawable *drawable)
>  {
> @@ -1657,7 +1663,6 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>      Stream *stream = drawable->stream;
>      SpiceImage *image;
>      uint32_t frame_mm_time;
> -    int n;
>      int width, height;
>      int ret;
>  
> @@ -1689,7 +1694,6 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>  
>      StreamAgent *agent = &dcc->stream_agents[get_stream_id(display, stream)];
>      uint64_t time_now = spice_get_monotonic_time_ns();
> -    size_t outbuf_size;
>  
>      if (!dcc->use_video_encoder_rate_control) {
>          if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / agent->fps) {
> @@ -1701,18 +1705,16 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>          }
>      }
>  
> +    VideoBuffer *outbuf;
>      /* workaround for vga streams */
>      frame_mm_time =  drawable->red_drawable->mm_time ?
>                          drawable->red_drawable->mm_time :
>                          reds_get_mm_time();
> -    outbuf_size = dcc->send_data.stream_outbuf_size;
>      ret = agent->video_encoder->encode_frame(agent->video_encoder,
>                                               frame_mm_time,
>                                               &image->u.bitmap, width, height,
>                                               &drawable->red_drawable->u.copy.src_area,
> -                                             stream->top_down,
> -                                             &dcc->send_data.stream_outbuf,
> -                                             &outbuf_size, &n);
> +                                             stream->top_down, &outbuf);
>      switch (ret) {
>      case VIDEO_ENCODER_FRAME_DROP:
>          spice_assert(dcc->use_video_encoder_rate_control);
> @@ -1728,7 +1730,6 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>          spice_error("bad return value (%d) from VideoEncoder::encode_frame", ret);
>          return FALSE;
>      }
> -    dcc->send_data.stream_outbuf_size = outbuf_size;
>  
>      if (!drawable->sized_stream) {
>          SpiceMsgDisplayStreamData stream_data;
> @@ -1737,7 +1738,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>  
>          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.data_size = outbuf->size;
>  
>          spice_marshall_msg_display_stream_data(base_marshaller, &stream_data);
>      } else {
> @@ -1747,7 +1748,7 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>  
>          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.data_size = outbuf->size;
>          stream_data.width = width;
>          stream_data.height = height;
>          stream_data.dest = drawable->red_drawable->bbox;
> @@ -1756,12 +1757,12 @@ static int red_marshall_stream_data(RedChannelClient *rcc,
>          rect_debug(&stream_data.dest);
>          spice_marshall_msg_display_stream_data_sized(base_marshaller, &stream_data);
>      }
> -    spice_marshaller_add_ref(base_marshaller,
> -                             dcc->send_data.stream_outbuf, n);
> +    spice_marshaller_add_ref_full(base_marshaller, outbuf->data, outbuf->size,
> +                                  &red_release_video_encoder_buffer, outbuf);
>      agent->last_send_time = time_now;
>  #ifdef STREAM_STATS
>      agent->stats.num_frames_sent++;
> -    agent->stats.size_sent += n;
> +    agent->stats.size_sent += outbuf->size;
>      agent->stats.end = frame_mm_time;
>  #endif
>  
> diff --git a/server/dcc.c b/server/dcc.c
> index df650c9..88196f9 100644
> --- a/server/dcc.c
> +++ b/server/dcc.c
> @@ -385,10 +385,6 @@ DisplayChannelClient *dcc_new(DisplayChannel *display,
>      // TODO: tune quality according to bandwidth
>      dcc->jpeg_quality = 85;
>  
> -    size_t stream_buf_size;
> -    stream_buf_size = 32*1024;
> -    dcc->send_data.stream_outbuf = spice_malloc(stream_buf_size);
> -    dcc->send_data.stream_outbuf_size = stream_buf_size;
>      dcc->send_data.free_list.res =
>          spice_malloc(sizeof(SpiceResourceList) +
>                       DISPLAY_FREE_LIST_DEFAULT_SIZE * sizeof(SpiceResourceID));
> @@ -504,7 +500,6 @@ void dcc_stop(DisplayChannelClient *dcc)
>      dcc->pixmap_cache = NULL;
>      dcc_release_glz(dcc);
>      dcc_palette_cache_reset(dcc);
> -    free(dcc->send_data.stream_outbuf);
>      free(dcc->send_data.free_list.res);
>      dcc_destroy_stream_agents(dcc);
>      dcc_encoders_free(dcc);
> diff --git a/server/dcc.h b/server/dcc.h
> index c2ef871..35cb2a4 100644
> --- a/server/dcc.h
> +++ b/server/dcc.h
> @@ -88,9 +88,6 @@ struct DisplayChannelClient {
>      uint32_t palette_cache_items;
>  
>      struct {
> -        uint32_t stream_outbuf_size;
> -        uint8_t *stream_outbuf; // caution stream buffer is also used as compress bufs!!!
> -
>          FreeList free_list;
>          uint64_t pixmap_cache_items[MAX_DRAWABLE_PIXMAP_CACHE_ITEMS];
>          int num_pixmap_cache_items;
> diff --git a/server/gstreamer-encoder.c b/server/gstreamer-encoder.c
> index 3dfbdfb..17dd0cb 100644
> --- a/server/gstreamer-encoder.c
> +++ b/server/gstreamer-encoder.c
> @@ -37,6 +37,12 @@ typedef struct {
>      uint32_t bpp;
>  } SpiceFormatForGStreamer;
>  
> +typedef struct SpiceGstVideoBuffer {
> +    VideoBuffer base;
> +    GstBuffer *gst_buffer;
> +    GstMapInfo map;
> +} SpiceGstVideoBuffer;
> +
>  typedef struct SpiceGstEncoder {
>      VideoEncoder base;
>  
> @@ -80,6 +86,23 @@ typedef struct SpiceGstEncoder {
>  } SpiceGstEncoder;
>  
>  
> +/* ---------- The SpiceGstVideoBuffer implementation ---------- */
> +
> +static void gst_video_buffer_free(VideoBuffer *video_buffer)
> +{
> +    SpiceGstVideoBuffer *buffer = (SpiceGstVideoBuffer*)video_buffer;
> +    gst_buffer_unref(buffer->gst_buffer);

You need a if (buffer->gst_buffer != NULL) test.
pull_compressed_buffer() can call this method with a NULL gst_buffer in
error cases, and gst_buffer_unref() warns on NULL.

> +    free(buffer);
> +}
> +
> +static SpiceGstVideoBuffer* create_gst_video_buffer(void)
> +{
> +    SpiceGstVideoBuffer *buffer = spice_new0(SpiceGstVideoBuffer, 1);
> +    buffer->base.free = &gst_video_buffer_free;
> +    return buffer;
> +}
> +
> +
>  /* ---------- Miscellaneous SpiceGstEncoder helpers ---------- */
>  
>  static inline double get_mbps(uint64_t bit_rate)
> @@ -458,29 +481,22 @@ static int push_raw_frame(SpiceGstEncoder *encoder, const SpiceBitmap *bitmap,
>  
>  /* A helper for spice_gst_encoder_encode_frame() */
>  static int pull_compressed_buffer(SpiceGstEncoder *encoder,
> -                                  uint8_t **outbuf, size_t *outbuf_size,
> -                                  int *data_size)
> +                                  VideoBuffer **outbuf)
>  {
> -    spice_return_val_if_fail(outbuf && outbuf_size, VIDEO_ENCODER_FRAME_UNSUPPORTED);

Maybe g_return_val_if_fail(video_buffer != NULL, VIDEO_ENCODER_FRAME_UNSUPPORTED); ?

> -
>      GstSample *sample = gst_app_sink_pull_sample(encoder->appsink);
>      if (sample) {
> -        GstMapInfo map;
> -        GstBuffer *buffer = gst_sample_get_buffer(sample);
> -        if (buffer && gst_buffer_map(buffer, &map, GST_MAP_READ)) {
> -            int size = gst_buffer_get_size(buffer);
> -            if (!*outbuf || *outbuf_size < size) {
> -                free(*outbuf);
> -                *outbuf = spice_malloc(size);
> -                *outbuf_size = size;
> -            }
> -            /* TODO Try to avoid this copy by changing the GstBuffer handling */
> -            memcpy(*outbuf, map.data, size);
> -            *data_size = size;
> -            gst_buffer_unmap(buffer, &map);
> +        SpiceGstVideoBuffer *buffer = create_gst_video_buffer();
> +        buffer->gst_buffer = gst_sample_get_buffer(sample);
> +        if (buffer->gst_buffer &&
> +            gst_buffer_map(buffer->gst_buffer, &buffer->map, GST_MAP_READ)) {
> +            buffer->base.data = buffer->map.data;
> +            buffer->base.size = gst_buffer_get_size(buffer->gst_buffer);
> +            *outbuf = (VideoBuffer*)buffer;
> +            gst_buffer_ref(buffer->gst_buffer);
>              gst_sample_unref(sample);
>              return VIDEO_ENCODER_FRAME_ENCODE_DONE;
>          }
> +        buffer->base.free((VideoBuffer*)buffer);
>          gst_sample_unref(sample);

A *video_buffer = NULL; would not hurt in error cases in case the
caller is careless and tries to use the result without checking for
errors.

>      }
>      spice_debug("failed to pull the compressed buffer");
> @@ -502,8 +518,7 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
>                                            const SpiceBitmap *bitmap,
>                                            int width, int height,
>                                            const SpiceRect *src, int top_down,
> -                                          uint8_t **outbuf, size_t *outbuf_size,
> -                                          int *data_size)
> +                                          VideoBuffer **outbuf)
>  {
>      SpiceGstEncoder *encoder = (SpiceGstEncoder*)video_encoder;
>  
> @@ -531,7 +546,7 @@ static int spice_gst_encoder_encode_frame(VideoEncoder *video_encoder,
>  
>      int rc = push_raw_frame(encoder, bitmap, src, top_down);
>      if (rc == VIDEO_ENCODER_FRAME_ENCODE_DONE) {
> -        rc = pull_compressed_buffer(encoder, outbuf, outbuf_size, data_size);
> +        rc = pull_compressed_buffer(encoder, outbuf);
>      }
>      return rc;
>  }
> diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c
> index 7e66106..57ce3c3 100644
> --- a/server/mjpeg-encoder.c
> +++ b/server/mjpeg-encoder.c
> @@ -930,26 +949,30 @@ static int mjpeg_encoder_encode_frame(VideoEncoder *video_encoder,
>                                        const SpiceBitmap *bitmap,
>                                        int width, int height,
>                                        const SpiceRect *src, int top_down,
> -                                      uint8_t **outbuf, size_t *outbuf_size,
> -                                      int *data_size)
> +                                      VideoBuffer **outbuf)
>  {
>      MJpegEncoder *encoder = (MJpegEncoder*)video_encoder;
> +    MJpegVideoBuffer *buffer = create_mjpeg_video_buffer();
> +    if (!buffer) {
> +        return VIDEO_ENCODER_FRAME_UNSUPPORTED;
> +    }
>  
>      int ret = mjpeg_encoder_start_frame(encoder, bitmap->format,
>                                          width, height,
> -                                        outbuf, outbuf_size,
> -                                        frame_mm_time);
> -    if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE) {
> -        return ret;
> +                                        buffer, frame_mm_time);
> +    if (ret == VIDEO_ENCODER_FRAME_ENCODE_DONE) {
> +        if (encode_frame(encoder, src, bitmap, top_down)) {
> +            buffer->base.size = mjpeg_encoder_end_frame(encoder);
> +            *outbuf = (VideoBuffer*)buffer;
> +        } else {
> +            ret = VIDEO_ENCODER_FRAME_UNSUPPORTED;
> +        }
>      }
>  
> -    if (!encode_frame(encoder, src, bitmap, top_down)) {
> -        return VIDEO_ENCODER_FRAME_UNSUPPORTED;
> +    if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE) {
> +        buffer->base.free((VideoBuffer*)buffer);
>      }
> -
> -    *data_size = mjpeg_encoder_end_frame(encoder);
> -
> -    return VIDEO_ENCODER_FRAME_ENCODE_DONE;
> +    return ret;
>  }

Same comment here about setting *video_buffer to NULL in error cases.

Apart from these minor issues,
Acked-by: Christophe Fergeau <cfergeau at redhat.com>

Christophe
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20160303/d042d5c8/attachment.sig>


More information about the Spice-devel mailing list