Hi, On Wed, 2016-05-04 at 11:44 +0200, Francois Gouget wrote: > This replaces the original channel-display-mjpeg API with a VideoDecoder > base class which can be reimplemented by other decoders. > Furthermore this moves the MJPEG-specific state information from the > display_stream struct to a derived class of VideoDecoder. > > Signed-off-by: Francois Gouget <fgouget@xxxxxxxxxxxxxxx> I would just change the prefix from 'spice-gtk' to 'channel-display' or 'video- streaming' (same applies to other patches)... Thanks, Pavel Acked-by: Pavel Grunt <pgrunt@xxxxxxxxxx> > --- > src/channel-display-mjpeg.c | 130 +++++++++++++++++++++++++++++------------ > --- > src/channel-display-priv.h | 53 +++++++++++++----- > src/channel-display.c | 63 ++++++++++----------- > 3 files changed, 151 insertions(+), 95 deletions(-) > > diff --git a/src/channel-display-mjpeg.c b/src/channel-display-mjpeg.c > index 5ea3371..c7e1c6f 100644 > --- a/src/channel-display-mjpeg.c > +++ b/src/channel-display-mjpeg.c > @@ -23,12 +23,33 @@ > > #include "channel-display-priv.h" > > + > +/* MJpeg decoder implementation */ > + > +typedef struct MJpegDecoder { > + VideoDecoder base; > + > + /* ---------- The builtin mjpeg decoder ---------- */ > + > + SpiceMsgIn *frame_msg; > + struct jpeg_source_mgr mjpeg_src; > + struct jpeg_decompress_struct mjpeg_cinfo; > + struct jpeg_error_mgr mjpeg_jerr; > + > + /* ---------- Output frame data ---------- */ > + > + uint8_t *out_frame; > +} MJpegDecoder; > + > + > +/* ---------- The JPEG library callbacks ---------- */ > + > static void mjpeg_src_init(struct jpeg_decompress_struct *cinfo) > { > - display_stream *st = SPICE_CONTAINEROF(cinfo->src, display_stream, > mjpeg_src); > - uint8_t *data; > + MJpegDecoder *decoder = SPICE_CONTAINEROF(cinfo->src, MJpegDecoder, > mjpeg_src); > > - cinfo->src->bytes_in_buffer = stream_get_current_frame(st, &data); > + uint8_t *data; > + cinfo->src->bytes_in_buffer = spice_msg_in_frame_data(decoder->frame_msg, > &data); > cinfo->src->next_input_byte = data; > } > > @@ -49,68 +70,57 @@ static void mjpeg_src_term(struct jpeg_decompress_struct > *cinfo) > /* nothing */ > } > > -G_GNUC_INTERNAL > -void stream_mjpeg_init(display_stream *st) > -{ > - st->mjpeg_cinfo.err = jpeg_std_error(&st->mjpeg_jerr); > - jpeg_create_decompress(&st->mjpeg_cinfo); > - > - st->mjpeg_src.init_source = mjpeg_src_init; > - st->mjpeg_src.fill_input_buffer = mjpeg_src_fill; > - st->mjpeg_src.skip_input_data = mjpeg_src_skip; > - st->mjpeg_src.resync_to_restart = jpeg_resync_to_restart; > - st->mjpeg_src.term_source = mjpeg_src_term; > - st->mjpeg_cinfo.src = &st->mjpeg_src; > -} > > -G_GNUC_INTERNAL > -void stream_mjpeg_data(display_stream *st) > +/* ---------- VideoDecoder's public API ---------- */ > + > +static uint8_t* mjpeg_decoder_decode_frame(VideoDecoder *video_decoder, > + SpiceMsgIn *frame_msg) > { > - gboolean back_compat = st->channel->priv->peer_hdr.major_version == 1; > + MJpegDecoder *decoder = (MJpegDecoder*)video_decoder; > + gboolean back_compat = decoder->base.stream->channel->priv- > >peer_hdr.major_version == 1; > int width; > int height; > uint8_t *dest; > uint8_t *lines[4]; > > - stream_get_dimensions(st, &width, &height); > - dest = g_malloc0(width * height * 4); > + decoder->frame_msg = frame_msg; > + stream_get_dimensions(decoder->base.stream, frame_msg, &width, &height); > + g_free(decoder->out_frame); > + dest = decoder->out_frame = g_malloc0(width * height * 4); > > - g_free(st->out_frame); > - st->out_frame = dest; > - > - jpeg_read_header(&st->mjpeg_cinfo, 1); > + jpeg_read_header(&decoder->mjpeg_cinfo, 1); > #ifdef JCS_EXTENSIONS > // requires jpeg-turbo > if (back_compat) > - st->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX; > + decoder->mjpeg_cinfo.out_color_space = JCS_EXT_RGBX; > else > - st->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX; > + decoder->mjpeg_cinfo.out_color_space = JCS_EXT_BGRX; > #else > #warning "You should consider building with libjpeg-turbo" > - st->mjpeg_cinfo.out_color_space = JCS_RGB; > + decoder->mjpeg_cinfo.out_color_space = JCS_RGB; > #endif > > #ifndef SPICE_QUALITY > - st->mjpeg_cinfo.dct_method = JDCT_IFAST; > - st->mjpeg_cinfo.do_fancy_upsampling = FALSE; > - st->mjpeg_cinfo.do_block_smoothing = FALSE; > - st->mjpeg_cinfo.dither_mode = JDITHER_ORDERED; > + decoder->mjpeg_cinfo.dct_method = JDCT_IFAST; > + decoder->mjpeg_cinfo.do_fancy_upsampling = FALSE; > + decoder->mjpeg_cinfo.do_block_smoothing = FALSE; > + decoder->mjpeg_cinfo.dither_mode = JDITHER_ORDERED; > #endif > // TODO: in theory should check cinfo.output_height match with our height > - jpeg_start_decompress(&st->mjpeg_cinfo); > + jpeg_start_decompress(&decoder->mjpeg_cinfo); > /* rec_outbuf_height is the recommended size of the output buffer we > * pass to libjpeg for optimum performance > */ > - if (st->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) { > - jpeg_abort_decompress(&st->mjpeg_cinfo); > - g_return_if_reached(); > + if (decoder->mjpeg_cinfo.rec_outbuf_height > G_N_ELEMENTS(lines)) { > + jpeg_abort_decompress(&decoder->mjpeg_cinfo); > + g_return_val_if_reached(NULL); > } > > - while (st->mjpeg_cinfo.output_scanline < st->mjpeg_cinfo.output_height) { > + while (decoder->mjpeg_cinfo.output_scanline < decoder- > >mjpeg_cinfo.output_height) { > /* only used when JCS_EXTENSIONS is undefined */ > G_GNUC_UNUSED unsigned int lines_read; > > - for (unsigned int j = 0; j < st->mjpeg_cinfo.rec_outbuf_height; j++) > { > + for (unsigned int j = 0; j < decoder->mjpeg_cinfo.rec_outbuf_height; > j++) { > lines[j] = dest; > #ifdef JCS_EXTENSIONS > dest += 4 * width; > @@ -118,8 +128,8 @@ void stream_mjpeg_data(display_stream *st) > dest += 3 * width; > #endif > } > - lines_read = jpeg_read_scanlines(&st->mjpeg_cinfo, lines, > - st->mjpeg_cinfo.rec_outbuf_height); > + lines_read = jpeg_read_scanlines(&decoder->mjpeg_cinfo, lines, > + decoder->mjpeg_cinfo.rec_outbuf_height); > #ifndef JCS_EXTENSIONS > { > uint8_t *s = lines[0]; > @@ -142,14 +152,44 @@ void stream_mjpeg_data(display_stream *st) > } > } > #endif > - dest = &st->out_frame[st->mjpeg_cinfo.output_scanline * width * 4]; > + dest = &(decoder->out_frame[decoder->mjpeg_cinfo.output_scanline * > width * 4]); > } > - jpeg_finish_decompress(&st->mjpeg_cinfo); > + jpeg_finish_decompress(&decoder->mjpeg_cinfo); > + > + return decoder->out_frame; > +} > + > +static void mjpeg_decoder_destroy(VideoDecoder* video_decoder) > +{ > + MJpegDecoder *decoder = (MJpegDecoder*)video_decoder; > + jpeg_destroy_decompress(&decoder->mjpeg_cinfo); > + g_free(decoder->out_frame); > + free(decoder); > } > > G_GNUC_INTERNAL > -void stream_mjpeg_cleanup(display_stream *st) > +VideoDecoder* create_mjpeg_decoder(int codec_type, display_stream *stream) > { > - jpeg_destroy_decompress(&st->mjpeg_cinfo); > - g_clear_pointer(&st->out_frame, g_free); > + g_return_val_if_fail(codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG, NULL); > + > + MJpegDecoder *decoder = spice_new0(MJpegDecoder, 1); > + > + decoder->base.destroy = mjpeg_decoder_destroy; > + decoder->base.decode_frame = mjpeg_decoder_decode_frame; > + decoder->base.codec_type = codec_type; > + decoder->base.stream = stream; > + > + decoder->mjpeg_cinfo.err = jpeg_std_error(&decoder->mjpeg_jerr); > + jpeg_create_decompress(&decoder->mjpeg_cinfo); > + > + decoder->mjpeg_src.init_source = mjpeg_src_init; > + decoder->mjpeg_src.fill_input_buffer = mjpeg_src_fill; > + decoder->mjpeg_src.skip_input_data = mjpeg_src_skip; > + decoder->mjpeg_src.resync_to_restart = jpeg_resync_to_restart; > + decoder->mjpeg_src.term_source = mjpeg_src_term; > + decoder->mjpeg_cinfo.src = &decoder->mjpeg_src; > + > + /* All the other fields are initialized to zero by spice_new0(). */ > + > + return (VideoDecoder*)decoder; > } > diff --git a/src/channel-display-priv.h b/src/channel-display-priv.h > index f92477b..7f1c520 100644 > --- a/src/channel-display-priv.h > +++ b/src/channel-display-priv.h > @@ -34,6 +34,39 @@ > > G_BEGIN_DECLS > > +typedef struct display_stream display_stream; > + > +typedef struct VideoDecoder VideoDecoder; > +struct VideoDecoder { > + /* Releases the video decoder's resources */ > + void (*destroy)(VideoDecoder *decoder); > + > + /* Decompresses the specified frame. > + * > + * @decoder: The video decoder. > + * @frame_msg: The Spice message containing the compressed frame. > + * @return: A pointer to the buffer holding the decoded frame. This > + * buffer will be invalidated by the next call to > + * decode_frame(). > + */ > + uint8_t* (*decode_frame)(VideoDecoder *decoder, SpiceMsgIn *frame_msg); > + > + /* The format of the encoded video. */ > + int codec_type; > + > + /* The associated display stream. */ > + display_stream *stream; > +}; > + > + > +/* Instantiates the video decoder for the specified codec. > + * > + * @codec_type: The format of the video. > + * @stream: The associated video stream. > + * @return: A pointer to a structure implementing the VideoDecoder > methods. > + */ > +VideoDecoder* create_mjpeg_decoder(int codec_type, display_stream *stream); > + > > typedef struct display_surface { > guint32 surface_id; > @@ -53,24 +86,18 @@ typedef struct drops_sequence_stats { > uint32_t duration; > } drops_sequence_stats; > > -typedef struct display_stream { > +struct display_stream { > SpiceMsgIn *msg_create; > SpiceMsgIn *msg_clip; > - SpiceMsgIn *msg_data; > > /* from messages */ > display_surface *surface; > SpiceClip *clip; > QRegion region; > int have_region; > - int codec; > > - /* mjpeg decoder */ > - struct jpeg_source_mgr mjpeg_src; > - struct jpeg_decompress_struct mjpeg_cinfo; > - struct jpeg_error_mgr mjpeg_jerr; > + VideoDecoder *video_decoder; > > - uint8_t *out_frame; > GQueue *msgq; > guint timeout; > SpiceChannel *channel; > @@ -97,15 +124,11 @@ typedef struct display_stream { > uint32_t report_num_frames; > uint32_t report_num_drops; > uint32_t report_drops_seq_len; > -} display_stream; > +}; > > -void stream_get_dimensions(display_stream *st, int *width, int *height); > -uint32_t stream_get_current_frame(display_stream *st, uint8_t **data); > +void stream_get_dimensions(display_stream *st, SpiceMsgIn *frame_msg, int > *width, int *height); > +uint32_t spice_msg_in_frame_data(SpiceMsgIn *frame_msg, uint8_t **data); > > -/* channel-display-mjpeg.c */ > -void stream_mjpeg_init(display_stream *st); > -void stream_mjpeg_data(display_stream *st); > -void stream_mjpeg_cleanup(display_stream *st); > > G_END_DECLS > > diff --git a/src/channel-display.c b/src/channel-display.c > index b6c79e0..c681a4e 100644 > --- a/src/channel-display.c > +++ b/src/channel-display.c > @@ -1086,7 +1086,6 @@ static void display_handle_stream_create(SpiceChannel > *channel, SpiceMsgIn *in) > st->msg_create = in; > spice_msg_in_ref(in); > st->clip = &op->clip; > - st->codec = op->codec_type; > st->surface = find_surface(c, op->surface_id); > st->msgq = g_queue_new(); > st->channel = channel; > @@ -1095,10 +1094,15 @@ static void display_handle_stream_create(SpiceChannel > *channel, SpiceMsgIn *in) > region_init(&st->region); > display_update_stream_region(st); > > - switch (st->codec) { > + switch (op->codec_type) { > case SPICE_VIDEO_CODEC_TYPE_MJPEG: > - stream_mjpeg_init(st); > + st->video_decoder = create_mjpeg_decoder(op->codec_type, st); > break; > + default: > + st->video_decoder = NULL; > + } > + if (st->video_decoder == NULL) { > + spice_printerr("could not create a video decoder for codec %d", op- > >codec_type); > } > } > > @@ -1141,15 +1145,15 @@ static gboolean display_stream_schedule(display_stream > *st) > return FALSE; > } > > -static SpiceRect *stream_get_dest(display_stream *st) > +static SpiceRect *stream_get_dest(display_stream *st, SpiceMsgIn *frame_msg) > { > - if (st->msg_data == NULL || > - spice_msg_in_type(st->msg_data) != > SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) { > + if (frame_msg == NULL || > + spice_msg_in_type(frame_msg) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) > { > SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st- > >msg_create); > > return &info->dest; > } else { > - SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st- > >msg_data); > + SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg); > > return &op->dest; > } > @@ -1164,21 +1168,16 @@ static uint32_t stream_get_flags(display_stream *st) > } > > G_GNUC_INTERNAL > -uint32_t stream_get_current_frame(display_stream *st, uint8_t **data) > +uint32_t spice_msg_in_frame_data(SpiceMsgIn *frame_msg, uint8_t **data) > { > - if (st->msg_data == NULL) { > - *data = NULL; > - return 0; > - } > - > - switch (spice_msg_in_type(st->msg_data)) { > + switch (spice_msg_in_type(frame_msg)) { > case SPICE_MSG_DISPLAY_STREAM_DATA: { > - SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(st->msg_data); > + SpiceMsgDisplayStreamData *op = spice_msg_in_parsed(frame_msg); > *data = op->data; > return op->data_size; > } > case SPICE_MSG_DISPLAY_STREAM_DATA_SIZED: { > - SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st- > >msg_data); > + SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg); > *data = op->data; > return op->data_size; > } > @@ -1189,19 +1188,19 @@ uint32_t stream_get_current_frame(display_stream *st, > uint8_t **data) > } > > G_GNUC_INTERNAL > -void stream_get_dimensions(display_stream *st, int *width, int *height) > +void stream_get_dimensions(display_stream *st, SpiceMsgIn *frame_msg, int > *width, int *height) > { > g_return_if_fail(width != NULL); > g_return_if_fail(height != NULL); > > - if (st->msg_data == NULL || > - spice_msg_in_type(st->msg_data) != > SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) { > + if (frame_msg == NULL || > + spice_msg_in_type(frame_msg) != SPICE_MSG_DISPLAY_STREAM_DATA_SIZED) > { > SpiceMsgDisplayStreamCreate *info = spice_msg_in_parsed(st- > >msg_create); > > *width = info->stream_width; > *height = info->stream_height; > } else { > - SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(st- > >msg_data); > + SpiceMsgDisplayStreamDataSized *op = spice_msg_in_parsed(frame_msg); > > *width = op->width; > *height = op->height; > @@ -1219,24 +1218,21 @@ static gboolean display_stream_render(display_stream > *st) > > g_return_val_if_fail(in != NULL, FALSE); > > - st->msg_data = in; > - switch (st->codec) { > - case SPICE_VIDEO_CODEC_TYPE_MJPEG: > - stream_mjpeg_data(st); > - break; > + uint8_t *out_frame = NULL; > + if (st->video_decoder) { > + out_frame = st->video_decoder->decode_frame(st->video_decoder, > in); > } > - > - if (st->out_frame) { > + if (out_frame) { > int width; > int height; > SpiceRect *dest; > uint8_t *data; > int stride; > > - stream_get_dimensions(st, &width, &height); > - dest = stream_get_dest(st); > + stream_get_dimensions(st, in, &width, &height); > + dest = stream_get_dest(st, in); > > - data = st->out_frame; > + data = out_frame; > stride = width * sizeof(uint32_t); > if (!(stream_get_flags(st) & SPICE_STREAM_FLAGS_TOP_DOWN)) { > data += stride * (height - 1); > @@ -1259,7 +1255,6 @@ static gboolean display_stream_render(display_stream > *st) > dest->bottom - dest->top); > } > > - st->msg_data = NULL; > spice_msg_in_unref(in); > > in = g_queue_peek_head(st->msgq); > @@ -1568,10 +1563,8 @@ static void destroy_stream(SpiceChannel *channel, int > id) > > g_array_free(st->drops_seqs_stats_arr, TRUE); > > - switch (st->codec) { > - case SPICE_VIDEO_CODEC_TYPE_MJPEG: > - stream_mjpeg_cleanup(st); > - break; > + if (st->video_decoder) { > + st->video_decoder->destroy(st->video_decoder); > } > > if (st->msg_clip) _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel