[Spice-devel] [PATCH] stream-device: handle_data: send whole message

Frediano Ziglio fziglio at redhat.com
Thu Jun 14 14:24:14 UTC 2018


> 
> On 06/14/2018 12:56 PM, Frediano Ziglio wrote:
> >>
> >> SPICE expect to have each frame in a single message.
> >> So far the stream-device did not do that.
> >> That works fine for H264 streams but not for mjpeg.
> >> The client handles by itself mjpeg streams, and not via
> >> gstreamer, and is assuming that a message contains the
> >> whole frame. Since it currently not, using spice-streaming-agent
> >> with mjpeg plugin, it confuses the client which burns CPU
> >> till it fails.
> >>
> >> This patch fixes that, but reading the whole message from the
> >> device (the streaming agent) and sending it over to the client.
> >>
> >> BTW, why realloc up and realloc down for every message ?
> >>
> > 
> > I don't know, before this patch it was done only for cursor shape which
> > should be not that often. So the question is why you want to do it
> > for every message?
> 
> Yes, before it was done only for cursor shape.
> 
> I do not want to do it for every message.
> I think we better leave the "big" buffer as is, possibly
> shrink it upon reset/disconnect.
> 
> > 
> >> Signed-off-by: Uri Lublin <uril at redhat.com>
> >> ---
> >>
> >> This is an alternative solution to Frediano's patch
> >> "stream-channel: Send the full frame in a single message" (v3)
> >>
> >> The 32MB limitation is the same as in Frediano's patch.
> >> Perhaps it's too big (?)
> >>
> > 
> > That's slightly different, this patch hard limit to 32mb considering
> > more than 32mb a protocol error, on my patch was falling back to
> > split.
> 
> That's indeed a difference between the two patches.
> I'm not sure what's the right thing to do.
> If you split the message, the client loses its mind
> (when an mjpeg/no-gstreamer codec is used).
> 
> > 
> >> The BTW above should have appeared here, next time ...
> >> Also why use realloc at all, and not free + alloc (no memcpy) ?
> >>
> > 
> > For the shrink case, considered not so often and considering
> > the buffer so small a single function call can be quicker, the
> > allocator is free to reuse the same memory buffer.
> > For expanding with same considerations (not so often and small
> > starting buffer) more or less the same.
> 
> But that also adds a memcpy of memory we do not care about.
> 
> > 
> >> ---
> >>   server/red-stream-device.c | 40 +++++++++++++++++++++++++-------------
> >>   1 file changed, 27 insertions(+), 13 deletions(-)
> >>
> >> diff --git a/server/red-stream-device.c b/server/red-stream-device.c
> >> index df6a366f4..432632f14 100644
> >> --- a/server/red-stream-device.c
> >> +++ b/server/red-stream-device.c
> >> @@ -146,7 +146,11 @@ stream_device_partial_read(StreamDevice *dev,
> >> SpiceCharDeviceInstance *sin)
> >>           }
> >>           break;
> >>       case STREAM_TYPE_DATA:
> >> -        handled = handle_msg_data(dev, sin);
> >> +        if (dev->hdr.size > 32*1024*1024) {
> >> +            handled = handle_msg_invalid(dev, sin, "STREAM_DATA too
> >> large");
> >> +        } else {
> >> +            handled = handle_msg_data(dev, sin);
> >> +        }
> >>           break;
> >>       case STREAM_TYPE_CURSOR_SET:
> >>           handled = handle_msg_cursor_set(dev, sin);
> >> @@ -308,20 +312,30 @@ handle_msg_data(StreamDevice *dev,
> >> SpiceCharDeviceInstance *sin)
> >>           spice_assert(dev->hdr.type == STREAM_TYPE_DATA);
> >>       }
> >>   
> >> -    while (1) {
> >> -        uint8_t buf[16 * 1024];
> >> -        n = sif->read(sin, buf, MIN(sizeof(buf), dev->hdr.size));
> >> -        if (n <= 0) {
> >> -            break;
> >> -        }
> >> -        // TODO collect all message ??
> >> -        // up: we send a single frame together
> >> -        // down: guest can cause a crash
> >> -        stream_channel_send_data(dev->stream_channel, buf, n,
> >> reds_get_mm_time());
> >> -        dev->hdr.size -= n;
> >> +    /* make sure we have a large enough buffer for the whole frame */
> >> +    if (dev->msg_pos == 0 && dev->msg_len < dev->hdr.size) {
> >> +        g_free(dev->msg);
> >> +        dev->msg = g_malloc(dev->hdr.size);
> >> +        dev->msg_len = dev->hdr.size;
> >>       }
> >>   
> >> -    return dev->hdr.size == 0;
> >> +    /* read from device */
> >> +    n = sif->read(sin, dev->msg->buf + dev->msg_pos, dev->hdr.size -
> >> dev->msg_pos);
> >> +    if (n <= 0) {
> >> +        return false;
> >> +    }
> >> +
> >> +    dev->msg_pos += n;
> >> +    if (dev->msg_pos != dev->hdr.size) { /* some bytes are still missing
> >> */
> >> +        return false;
> >> +    }
> >> +
> >> +    /* The whole frame was read from the device, send it */
> >> +    stream_channel_send_data(dev->stream_channel, dev->msg->buf,
> >> dev->hdr.size, reds_get_mm_time());
> > 
> > OT, not a regression, the time should be taken when the guest starts
> > sending the message, not at the end.
> 
> Yes.
> 
> > 
> >> +    dev->hdr.size = 0;
> >> +
> >> +
> >> +    return true;
> >>   }
> >>   
> >>   /*
> > 
> > Note that this patch is increasing processing latency (as probably
> > mine).
> 
> I know. The solution of reading the whole frame before sending it
> (in other words waiting for the last byte of the frame to arrive),
> is adding latency. We can think of other solutions to this problem.
> 
> Uri.
> 

That's why I prefer the code to collapse reading being in StreamChannel,
StreamChannel knows the clients, which messages they support and how to
send the information.

Maybe a patch mixing the two? Kind of:
- defining a message limit (we agree 32Mb is sensible, is about 20 Mpixel
  of uncompressed jpeg);
- collapse message in stream-channel.c and send. Later support different
  way (either multiple partial messages or pipe through).

Frediano


More information about the Spice-devel mailing list