[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