Re: [Spice-devel] [PATCH spice 3/11] server: Refactor the Spice server video encoding so alternative implementations can be added.

2015-05-26 Thread Francois Gouget
On Tue, 19 May 2015, Marc-André Lureau wrote:
[...]
  +struct VideoEncoder {
  +/* Releases the video encoder's resources */
  +void (*destroy)(VIDEO_ENCODER_T *encoder);
  +
  +/* Compresses the specified src image area into the outbuf buffer.
  + *
  + * @encoder:   The video encoder.
  + * @bitmap:The Spice screen.
  + * @width: The width of the Spice screen.
  + * @height:The heigth of the Spice screen.
 
 
 It's not the screen, it's the video area. It's not clear to me what 
 would happen if the src size doesn't match the widthheight.

Ah right. I'll fix the comment. I was confused about that but after 
looking again I must say I'm still unsure as to why we get both src and 
width+height.

My understanding is that this has been introduced to deal with the 
YouTube progress bar:
https://bugzilla.redhat.com/show_bug.cgi?id=813826

To sum up, the size of the video area detected by the Spice server 
changes when the progress bar pops up or down. As a result some of the 
'video frames' it gets don't match the size it detected when it created 
the video stream.

 * It used to be that this caused such frames to go through the regular 
   update mechanism instead of through the video stream. This is still 
   the case with the spicec client for instance and results in the video 
   seeming to jump back and forth because the video stream is delayed 
   due to buffering while the regular path is not.

   Looking at the impact on src  height, either the size of the video 
   frame, aka src, matches the stream size and then so do width and 
   height (lines 9  10 below), or it does not and then we don't go 
   through the video encoder (line 6 below).


 * Now clients can advertise support for changing stream sizes with the 
   SIZED_STREAM capability. This does not totally fix the back and forth 
   issue but restricts it to the progress bar area as it comes in and 
   out of the video frame.

   In that case width  height are either computed from the actual video 
   frame size, aka src, (lines 3  4 below), or default to the initial 
   stream size when the video frame size matches (lines 9  10 below).

From red_marshall_stream_data():
 1 if (drawable-sized_stream) {
 2   if (red_channel_client_test_remote_cap(rcc, 
SPICE_DISPLAY_CAP_SIZED_STREAM)) {
 3 width = src_rect-right - src_rect-left;
 4 height = src_rect-bottom - src_rect-top;
 5   } else {
 6 return FALSE;
 7   }
 8 } else {
 9   width = stream-width;
10   height = stream-height;
11 }

So in the end width and height *always* match src. So they're redundant, 
unless some larger purpose is planned for them.

Now must say having a continuously changing stream size is quite 
annoying in the context of GStreamer:
 - At a minimum it forces a reconfiguration of the pipeline which, 
   depending on the encoder, may require rebuilding it. Either cases 
   seem to cause a reset of the GStreamer's encoder bitrate control 
   algorithm which tends to cause it to overshoot and may cause frame 
   drops.
 - Ultimately the GStreamer video encoder should support inter-frame 
   compression (with VP8 or H264 for instance) and a changing stream 
   size will also cause some disruption in this context.

Things can still work as is so there's not hurry but maybe a better 
solution can be devised in time.


For instance it may be better to increase the stream size as the video 
frame size increases but then peg it so it does not shrink again. This 
would solve the remaining back and forth issue with the YouTube progress 
bar, and limit the number of resizes the video encoders have to go 
through. It supposes that regular frame updates know to ignore changes 
happening inside the stream area even if they don't go through the 
regular 'video frame' update path but I have no idea if that's easy to 
do.

Alternatively one could split oversize video frames in two: the stream 
area which would go through the video encoder, and the remainder which 
would go through the regular path. This would also solve the remaining 
back and forth issue with the YouTube progress bar and would even be 
compatible with non SIZED_STREAM capable clients.

-- 
Francois Gouget fgou...@codeweavers.com
___
Spice-devel mailing list
Spice-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/spice-devel


Re: [Spice-devel] [PATCH spice 3/11] server: Refactor the Spice server video encoding so alternative implementations can be added.

2015-05-19 Thread Marc-André Lureau
looks good

On Wed, May 13, 2015 at 10:27 PM, Francois Gouget fgou...@codeweavers.com
wrote:

 This patch simply replaces the mjpeg_encoder_xxx() call points with a
 more object oriented design.
 ---
  server/Makefile.am |   2 +-
  server/mjpeg_encoder.c | 134 +---
  server/mjpeg_encoder.h | 114 --
  server/red_worker.c| 156
 ++-
  server/video_encoder.h | 162
 +
  5 files changed, 331 insertions(+), 237 deletions(-)
  delete mode 100644 server/mjpeg_encoder.h
  create mode 100644 server/video_encoder.h

 diff --git a/server/Makefile.am b/server/Makefile.am
 index 89a375c..36b6d45 100644
 --- a/server/Makefile.am
 +++ b/server/Makefile.am
 @@ -81,7 +81,6 @@ libspice_server_la_SOURCES =  \
 main_channel.c  \
 main_channel.h  \
 mjpeg_encoder.c \
 -   mjpeg_encoder.h \
 red_bitmap_utils.h  \
 red_channel.c   \
 red_channel.h   \
 @@ -115,6 +114,7 @@ libspice_server_la_SOURCES =\
 spicevmc.c  \
 spice_timer_queue.c \
 spice_timer_queue.h \
 +   video_encoder.h \
 zlib_encoder.c  \
 zlib_encoder.h  \
 spice_bitmap_utils.h\
 diff --git a/server/mjpeg_encoder.c b/server/mjpeg_encoder.c
 index 9a41ef3..d734520 100644
 --- a/server/mjpeg_encoder.c
 +++ b/server/mjpeg_encoder.c
 @@ -20,7 +20,11 @@
  #endif

  #include red_common.h
 -#include mjpeg_encoder.h
 +
 +typedef struct MJpegEncoder MJpegEncoder;
 +#define VIDEO_ENCODER_T MJpegEncoder
 +#include video_encoder.h
 +
  #include jerror.h
  #include jpeglib.h
  #include inttypes.h
 @@ -154,6 +158,7 @@ typedef struct MJpegEncoderRateControl {
  } MJpegEncoderRateControl;

  struct MJpegEncoder {
 +VideoEncoder base;
  uint8_t *row;
  uint32_t row_size;
  int first_frame;
 @@ -165,7 +170,7 @@ struct MJpegEncoder {
  void (*pixel_converter)(uint8_t *src, uint8_t *dest);

  MJpegEncoderRateControl rate_control;
 -MJpegEncoderRateControlCbs cbs;
 +VideoEncoderRateControlCbs cbs;
  void *cbs_opaque;

  /* stats */
 @@ -174,6 +179,22 @@ struct MJpegEncoder {
  uint32_t num_frames;
  };

 +static void mjpeg_encoder_destroy(MJpegEncoder *encoder);
 +static int mjpeg_encoder_encode_frame(MJpegEncoder *encoder,
 +const SpiceBitmap *bitmap, int width, int height,
 +const SpiceRect *src, int top_down, uint32_t
 frame_mm_time,
 +uint8_t **outbuf, size_t *outbuf_size, int *data_size);
 +static void mjpeg_encoder_client_stream_report(MJpegEncoder *encoder,
 +uint32_t num_frames,
 +uint32_t num_drops,
 +uint32_t start_frame_mm_time,
 +uint32_t end_frame_mm_time,
 +int32_t end_frame_delay,
 +uint32_t audio_delay);
 +static void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder);
 +static uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder);
 +static void mjpeg_encoder_get_stats(MJpegEncoder *encoder,
 VideoEncoderStats *stats);
 +
  static inline void mjpeg_encoder_reset_quality(MJpegEncoder *encoder,
 int quality_id,
 uint32_t fps,
 @@ -189,8 +210,9 @@ static inline int rate_control_is_active(MJpegEncoder*
 encoder)
  return encoder-cbs.get_roundtrip_ms != NULL;
  }

 -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate,
 -MJpegEncoderRateControlCbs *cbs, void
 *opaque)
 +MJpegEncoder *create_mjpeg_encoder(uint64_t starting_bit_rate,
 +   VideoEncoderRateControlCbs *cbs,
 +   void *cbs_opaque)
  {
  MJpegEncoder *enc;

 @@ -198,6 +220,12 @@ MJpegEncoder *mjpeg_encoder_new(uint64_t
 starting_bit_rate,

  enc = spice_new0(MJpegEncoder, 1);

 +enc-base.destroy = mjpeg_encoder_destroy;
 +enc-base.encode_frame = mjpeg_encoder_encode_frame;
 +enc-base.client_stream_report = mjpeg_encoder_client_stream_report;
 +enc-base.notify_server_frame_drop =
 mjpeg_encoder_notify_server_frame_drop;
 +enc-base.get_bit_rate = mjpeg_encoder_get_bit_rate;
 +enc-base.get_stats = mjpeg_encoder_get_stats;
  enc-first_frame = TRUE;
  enc-rate_control.byte_rate = starting_bit_rate / 8;