> > This replaces the original mjpeg_encoder API with a VideoEncoder base > class which can be reimplemented by other encoders. > This also renames the members and enums from mjpeg_* to video_*. > > Signed-off-by: Francois Gouget <fgouget@xxxxxxxxxxxxxxx> Merged Frediano > --- > server/Makefile.am | 2 +- > server/dcc-send.c | 27 +++++---- > server/dcc.c | 24 ++++---- > server/dcc.h | 2 +- > server/mjpeg-encoder.c | 85 +++++++++++++++----------- > server/mjpeg-encoder.h | 102 ------------------------------- > server/stream.c | 42 ++++++------- > server/stream.h | 4 +- > server/video-encoder.h | 161 > +++++++++++++++++++++++++++++++++++++++++++++++++ > 9 files changed, 263 insertions(+), 186 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 a119c86..b548205 100644 > --- a/server/Makefile.am > +++ b/server/Makefile.am > @@ -87,7 +87,6 @@ libserver_la_SOURCES = \ > main-channel.c \ > main-channel.h \ > mjpeg-encoder.c \ > - mjpeg-encoder.h \ > red-channel.c \ > red-channel.h \ > red-common.h \ > @@ -125,6 +124,7 @@ libserver_la_SOURCES = \ > sound.h \ > stat.h \ > spicevmc.c \ > + video-encoder.h \ > zlib-encoder.c \ > zlib-encoder.h \ > image-cache.h \ > diff --git a/server/dcc-send.c b/server/dcc-send.c > index efbc454..fcec8d3 100644 > --- a/server/dcc-send.c > +++ b/server/dcc-send.c > @@ -1659,7 +1659,7 @@ static int red_marshall_stream_data(RedChannelClient > *rcc, > Stream *stream = drawable->stream; > SpiceImage *image; > uint32_t frame_mm_time; > - int n; > + uint32_t n; > int width, height; > int ret; > > @@ -1693,7 +1693,7 @@ static int red_marshall_stream_data(RedChannelClient > *rcc, > uint64_t time_now = spice_get_monotonic_time_ns(); > size_t outbuf_size; > > - if (!dcc->use_mjpeg_encoder_rate_control) { > + if (!dcc->use_video_encoder_rate_control) { > if (time_now - agent->last_send_time < (1000 * 1000 * 1000) / > agent->fps) { > agent->frames--; > #ifdef STREAM_STATS > @@ -1708,25 +1708,26 @@ static int red_marshall_stream_data(RedChannelClient > *rcc, > drawable->red_drawable->mm_time : > reds_get_mm_time(); > outbuf_size = dcc->send_data.stream_outbuf_size; > - ret = mjpeg_encoder_encode_frame(agent->mjpeg_encoder, > - &image->u.bitmap, width, height, > - > &drawable->red_drawable->u.copy.src_area, > - stream->top_down, frame_mm_time, > - &dcc->send_data.stream_outbuf, > - &outbuf_size, &n); > + 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); > switch (ret) { > - case MJPEG_ENCODER_FRAME_DROP: > - spice_assert(dcc->use_mjpeg_encoder_rate_control); > + case VIDEO_ENCODER_FRAME_DROP: > + spice_assert(dcc->use_video_encoder_rate_control); > #ifdef STREAM_STATS > agent->stats.num_drops_fps++; > #endif > return TRUE; > - case MJPEG_ENCODER_FRAME_UNSUPPORTED: > + case VIDEO_ENCODER_FRAME_UNSUPPORTED: > return FALSE; > - case MJPEG_ENCODER_FRAME_ENCODE_DONE: > + case VIDEO_ENCODER_FRAME_ENCODE_DONE: > break; > default: > - spice_error("bad return value (%d) from mjpeg_encoder_encode_frame", > ret); > + spice_error("bad return value (%d) from VideoEncoder::encode_frame", > ret); > return FALSE; > } > dcc->send_data.stream_outbuf_size = outbuf_size; > diff --git a/server/dcc.c b/server/dcc.c > index 3be1e96..db77cc7 100644 > --- a/server/dcc.c > +++ b/server/dcc.c > @@ -346,7 +346,7 @@ static void dcc_init_stream_agents(DisplayChannelClient > *dcc) > red_pipe_item_init(&agent->create_item, > RED_PIPE_ITEM_TYPE_STREAM_CREATE); > red_pipe_item_init(&agent->destroy_item, > RED_PIPE_ITEM_TYPE_STREAM_DESTROY); > } > - dcc->use_mjpeg_encoder_rate_control = > + dcc->use_video_encoder_rate_control = > red_channel_client_test_remote_cap(RED_CHANNEL_CLIENT(dcc), > SPICE_DISPLAY_CAP_STREAM_REPORT); > } > > @@ -477,9 +477,9 @@ static void > dcc_destroy_stream_agents(DisplayChannelClient *dcc) > StreamAgent *agent = &dcc->stream_agents[i]; > region_destroy(&agent->vis_region); > region_destroy(&agent->clip); > - if (agent->mjpeg_encoder) { > - mjpeg_encoder_destroy(agent->mjpeg_encoder); > - agent->mjpeg_encoder = NULL; > + if (agent->video_encoder) { > + agent->video_encoder->destroy(agent->video_encoder); > + agent->video_encoder = NULL; > } > } > } > @@ -1393,7 +1393,7 @@ static int > dcc_handle_stream_report(DisplayChannelClient *dcc, > } > > agent = &dcc->stream_agents[report->stream_id]; > - if (!agent->mjpeg_encoder) { > + if (!agent->video_encoder) { > spice_info("stream_report: no encoder for stream id %u. " > "The stream has probably been destroyed", > report->stream_id); > @@ -1407,13 +1407,13 @@ static int > dcc_handle_stream_report(DisplayChannelClient *dcc, > return TRUE; > } > > - mjpeg_encoder_client_stream_report(agent->mjpeg_encoder, > - report->num_frames, > - report->num_drops, > - report->start_frame_mm_time, > - report->end_frame_mm_time, > - report->last_frame_delay, > - report->audio_delay); > + agent->video_encoder->client_stream_report(agent->video_encoder, > + report->num_frames, > + report->num_drops, > + report->start_frame_mm_time, > + report->end_frame_mm_time, > + report->last_frame_delay, > + report->audio_delay); > return TRUE; > } > > diff --git a/server/dcc.h b/server/dcc.h > index 509a6c6..1dd53bd 100644 > --- a/server/dcc.h > +++ b/server/dcc.h > @@ -109,7 +109,7 @@ struct DisplayChannelClient { > QRegion surface_client_lossy_region[NUM_SURFACES]; > > StreamAgent stream_agents[NUM_STREAMS]; > - int use_mjpeg_encoder_rate_control; > + int use_video_encoder_rate_control; > uint32_t streams_max_latency; > uint64_t streams_max_bit_rate; > bool gl_draw_ongoing; > diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c > index c80febd..e3646db 100644 > --- a/server/mjpeg-encoder.c > +++ b/server/mjpeg-encoder.c > @@ -20,7 +20,7 @@ > #endif > > #include "red-common.h" > -#include "mjpeg-encoder.h" > +#include "video-encoder.h" > #include "utils.h" > #include <jerror.h> > #include <jpeglib.h> > @@ -154,7 +154,8 @@ typedef struct MJpegEncoderRateControl { > uint64_t warmup_start_time; > } MJpegEncoderRateControl; > > -struct MJpegEncoder { > +typedef struct MJpegEncoder { > + VideoEncoder base; > uint8_t *row; > uint32_t row_size; > int first_frame; > @@ -166,13 +167,13 @@ struct MJpegEncoder { > void (*pixel_converter)(void *src, uint8_t *dest); > > MJpegEncoderRateControl rate_control; > - MJpegEncoderRateControlCbs cbs; > + VideoEncoderRateControlCbs cbs; > > /* stats */ > uint64_t starting_bit_rate; > uint64_t avg_quality; > uint32_t num_frames; > -}; > +} MJpegEncoder; > > static void mjpeg_encoder_process_server_drops(MJpegEncoder *encoder); > static uint32_t get_min_required_playback_delay(uint64_t frame_enc_size, > @@ -184,8 +185,9 @@ static inline int rate_control_is_active(MJpegEncoder* > encoder) > return encoder->cbs.get_roundtrip_ms != NULL; > } > > -void mjpeg_encoder_destroy(MJpegEncoder *encoder) > +static void mjpeg_encoder_destroy(VideoEncoder *video_encoder) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > free(encoder->cinfo.dest); > jpeg_destroy_compress(&encoder->cinfo); > free(encoder->row); > @@ -724,7 +726,7 @@ static int mjpeg_encoder_start_frame(MJpegEncoder > *encoder, > interval = (now - rate_control->bit_rate_info.last_frame_time); > > if (interval < NSEC_PER_SEC / rate_control->adjusted_fps) { > - return MJPEG_ENCODER_FRAME_DROP; > + return VIDEO_ENCODER_FRAME_DROP; > } > > mjpeg_encoder_adjust_params_to_bit_rate(encoder); > @@ -772,14 +774,14 @@ static int mjpeg_encoder_start_frame(MJpegEncoder > *encoder, > break; > default: > spice_debug("unsupported format %d", format); > - return MJPEG_ENCODER_FRAME_UNSUPPORTED; > + return VIDEO_ENCODER_FRAME_UNSUPPORTED; > } > > if (encoder->pixel_converter != NULL) { > unsigned int stride = width * 3; > /* check for integer overflow */ > if (stride < width) { > - return MJPEG_ENCODER_FRAME_UNSUPPORTED; > + return VIDEO_ENCODER_FRAME_UNSUPPORTED; > } > if (encoder->row_size < stride) { > encoder->row = spice_realloc(encoder->row, stride); > @@ -799,7 +801,7 @@ static int mjpeg_encoder_start_frame(MJpegEncoder > *encoder, > > encoder->num_frames++; > encoder->avg_quality += quality; > - return MJPEG_ENCODER_FRAME_ENCODE_DONE; > + return VIDEO_ENCODER_FRAME_ENCODE_DONE; > } > > static int mjpeg_encoder_encode_scanline(MJpegEncoder *encoder, > @@ -923,27 +925,31 @@ static int encode_frame(MJpegEncoder *encoder, const > SpiceRect *src, > return TRUE; > } > > -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 int mjpeg_encoder_encode_frame(VideoEncoder *video_encoder, > + uint32_t frame_mm_time, > + const SpiceBitmap *bitmap, > + int width, int height, > + const SpiceRect *src, int top_down, > + uint8_t **outbuf, size_t *outbuf_size, > + uint32_t *data_size) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > + > int ret = mjpeg_encoder_start_frame(encoder, bitmap->format, > - width, height, outbuf, outbuf_size, > - frame_mm_time); > - if (ret != MJPEG_ENCODER_FRAME_ENCODE_DONE) { > + width, height, > + outbuf, outbuf_size, > + frame_mm_time); > + if (ret != VIDEO_ENCODER_FRAME_ENCODE_DONE) { > return ret; > } > > if (!encode_frame(encoder, src, bitmap, top_down)) { > - return MJPEG_ENCODER_FRAME_UNSUPPORTED; > + return VIDEO_ENCODER_FRAME_UNSUPPORTED; > } > > *data_size = mjpeg_encoder_end_frame(encoder); > > - return MJPEG_ENCODER_FRAME_ENCODE_DONE; > + return VIDEO_ENCODER_FRAME_ENCODE_DONE; > } > > > @@ -1174,14 +1180,15 @@ static uint32_t > get_min_required_playback_delay(uint64_t frame_enc_size, > #define MJPEG_VIDEO_VS_AUDIO_LATENCY_FACTOR 1.25 > #define MJPEG_VIDEO_DELAY_TH -15 > > -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_client_stream_report(VideoEncoder *video_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) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > MJpegEncoderRateControl *rate_control = &encoder->rate_control; > MJpegEncoderClientState *client_state = &rate_control->client_state; > uint64_t avg_enc_size = 0; > @@ -1282,8 +1289,9 @@ void mjpeg_encoder_client_stream_report(MJpegEncoder > *encoder, > } > } > > -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder) > +static void mjpeg_encoder_notify_server_frame_drop(VideoEncoder > *video_encoder) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > encoder->rate_control.server_state.num_frames_dropped++; > mjpeg_encoder_process_server_drops(encoder); > } > @@ -1320,24 +1328,33 @@ static void > mjpeg_encoder_process_server_drops(MJpegEncoder *encoder) > server_state->num_frames_dropped = 0; > } > > -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder) > +static uint64_t mjpeg_encoder_get_bit_rate(VideoEncoder *video_encoder) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > return encoder->rate_control.byte_rate * 8; > } > > -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats > *stats) > +static void mjpeg_encoder_get_stats(VideoEncoder *video_encoder, > + VideoEncoderStats *stats) > { > + MJpegEncoder *encoder = (MJpegEncoder*)video_encoder; > spice_assert(encoder != NULL && stats != NULL); > stats->starting_bit_rate = encoder->starting_bit_rate; > - stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(encoder); > + stats->cur_bit_rate = mjpeg_encoder_get_bit_rate(video_encoder); > stats->avg_quality = (double)encoder->avg_quality / encoder->num_frames; > } > > -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate, > - MJpegEncoderRateControlCbs *cbs) > +VideoEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate, > + VideoEncoderRateControlCbs *cbs) > { > MJpegEncoder *encoder = spice_new0(MJpegEncoder, 1); > > + encoder->base.destroy = mjpeg_encoder_destroy; > + encoder->base.encode_frame = mjpeg_encoder_encode_frame; > + encoder->base.client_stream_report = mjpeg_encoder_client_stream_report; > + encoder->base.notify_server_frame_drop = > mjpeg_encoder_notify_server_frame_drop; > + encoder->base.get_bit_rate = mjpeg_encoder_get_bit_rate; > + encoder->base.get_stats = mjpeg_encoder_get_stats; > encoder->first_frame = TRUE; > encoder->rate_control.byte_rate = starting_bit_rate / 8; > encoder->starting_bit_rate = starting_bit_rate; > @@ -1357,5 +1374,5 @@ MJpegEncoder *mjpeg_encoder_new(uint64_t > starting_bit_rate, > encoder->cinfo.err = jpeg_std_error(&encoder->jerr); > jpeg_create_compress(&encoder->cinfo); > > - return encoder; > + return (VideoEncoder*)encoder; > } > diff --git a/server/mjpeg-encoder.h b/server/mjpeg-encoder.h > deleted file mode 100644 > index 4d871ff..0000000 > --- a/server/mjpeg-encoder.h > +++ /dev/null > @@ -1,102 +0,0 @@ > -/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > -/* > - Copyright (C) 2009 Red Hat, Inc. > - > - This library is free software; you can redistribute it and/or > - modify it under the terms of the GNU Lesser General Public > - License as published by the Free Software Foundation; either > - version 2.1 of the License, or (at your option) any later version. > - > - This library is distributed in the hope that it will be useful, > - but WITHOUT ANY WARRANTY; without even the implied warranty of > - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > - Lesser General Public License for more details. > - > - You should have received a copy of the GNU Lesser General Public > - License along with this library; if not, see > <http://www.gnu.org/licenses/>. > -*/ > - > -#ifndef _H_MJPEG_ENCODER > -#define _H_MJPEG_ENCODER > - > -#include "red-common.h" > - > -enum { > - MJPEG_ENCODER_FRAME_UNSUPPORTED = -1, > - MJPEG_ENCODER_FRAME_DROP, > - MJPEG_ENCODER_FRAME_ENCODE_DONE, > -}; > - > -typedef struct MJpegEncoder MJpegEncoder; > - > -/* > - * Callbacks required for controling and adjusting > - * the stream bit rate: > - * @opaque: a pointer to be passed to the rate control callbacks. > - * get_roundtrip_ms: roundtrip time in milliseconds > - * get_source_fps: the input frame rate (#frames per second), i.e., > - * the rate of frames arriving from the guest to spice-server, > - * before any drops. > - */ > -typedef struct MJpegEncoderRateControlCbs { > - void *opaque; > - uint32_t (*get_roundtrip_ms)(void *opaque); > - uint32_t (*get_source_fps)(void *opaque); > - void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms); > -} MJpegEncoderRateControlCbs; > - > -typedef struct MJpegEncoderStats { > - uint64_t starting_bit_rate; > - uint64_t cur_bit_rate; > - double avg_quality; > -} MJpegEncoderStats; > - > -MJpegEncoder *mjpeg_encoder_new(uint64_t starting_bit_rate, > - MJpegEncoderRateControlCbs *cbs); > -void mjpeg_encoder_destroy(MJpegEncoder *encoder); > - > -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); > - > -/* > - * bit rate control > - */ > - > -/* > - * Data that should be periodically obtained from the client. The report > contains: > - * num_frames : the number of frames that reached the client during > the time > - * the report is referring to. > - * num_drops : the part of the above frames that was dropped by the > client due to > - * late arrival time. > - * start_frame_mm_time: the mm_time of the first frame included in the > report > - * end_frame_mm_time : the mm_time of the last_frame included in the report > - * end_frame_delay : (end_frame_mm_time - client_mm_time) > - * audio delay : the latency of the audio playback. > - * If there is no audio playback, set it to MAX_UINT. > - * > - */ > -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); > - > -/* > - * Notify the encoder each time a frame is dropped due to pipe > - * congestion. > - * We can deduce the client state by the frame dropping rate in the server. > - * Monitoring the frame drops can help in fine tuning the playback > parameters > - * when the client reports are delayed. > - */ > -void mjpeg_encoder_notify_server_frame_drop(MJpegEncoder *encoder); > - > -uint64_t mjpeg_encoder_get_bit_rate(MJpegEncoder *encoder); > -void mjpeg_encoder_get_stats(MJpegEncoder *encoder, MJpegEncoderStats > *stats); > - > -#endif > diff --git a/server/stream.c b/server/stream.c > index 5020eb0..8884480 100644 > --- a/server/stream.c > +++ b/server/stream.c > @@ -32,10 +32,10 @@ void stream_agent_stats_print(StreamAgent *agent) > #ifdef STREAM_STATS > StreamStats *stats = &agent->stats; > double passed_mm_time = (stats->end - stats->start) / 1000.0; > - MJpegEncoderStats encoder_stats = {0}; > + VideoEncoderStats encoder_stats = {0}; > > - if (agent->mjpeg_encoder) { > - mjpeg_encoder_get_stats(agent->mjpeg_encoder, &encoder_stats); > + if (agent->video_encoder) { > + agent->video_encoder->get_stats(agent->video_encoder, > &encoder_stats); > } > > spice_debug("stream=%p dim=(%dx%d) #in-frames=%"PRIu64" #in-avg-fps=%.2f > #out-frames=%"PRIu64" " > @@ -79,8 +79,8 @@ void stream_stop(DisplayChannel *display, Stream *stream) > region_clear(&stream_agent->vis_region); > region_clear(&stream_agent->clip); > spice_assert(!red_pipe_item_is_linked(&stream_agent->destroy_item)); > - if (stream_agent->mjpeg_encoder && > dcc->use_mjpeg_encoder_rate_control) { > - uint64_t stream_bit_rate = > mjpeg_encoder_get_bit_rate(stream_agent->mjpeg_encoder); > + if (stream_agent->video_encoder && > dcc->use_video_encoder_rate_control) { > + uint64_t stream_bit_rate = > stream_agent->video_encoder->get_bit_rate(stream_agent->video_encoder); > > if (stream_bit_rate > dcc->streams_max_bit_rate) { > spice_debug("old max-bit-rate=%.2f new=%.2f", > @@ -338,7 +338,7 @@ static void before_reattach_stream(DisplayChannel > *display, > dcc = dpi->dcc; > agent = &dcc->stream_agents[index]; > > - if (!dcc->use_mjpeg_encoder_rate_control && > + if (!dcc->use_video_encoder_rate_control && > !dcc->common.is_low_bandwidth) { > continue; > } > @@ -347,8 +347,8 @@ static void before_reattach_stream(DisplayChannel > *display, > #ifdef STREAM_STATS > agent->stats.num_drops_pipe++; > #endif > - if (dcc->use_mjpeg_encoder_rate_control) { > - > mjpeg_encoder_notify_server_frame_drop(agent->mjpeg_encoder); > + if (dcc->use_video_encoder_rate_control) { > + > agent->video_encoder->notify_server_frame_drop(agent->video_encoder); > } else { > ++agent->drops; > } > @@ -361,7 +361,7 @@ static void before_reattach_stream(DisplayChannel > *display, > > agent = &dcc->stream_agents[index]; > > - if (dcc->use_mjpeg_encoder_rate_control) { > + if (dcc->use_video_encoder_rate_control) { > continue; > } > if (agent->frames / agent->fps < FPS_TEST_INTERVAL) { > @@ -594,7 +594,7 @@ static void > dcc_update_streams_max_latency(DisplayChannelClient *dcc, StreamAgen > } > for (i = 0; i < NUM_STREAMS; i++) { > StreamAgent *other_agent = &dcc->stream_agents[i]; > - if (other_agent == remove_agent || !other_agent->mjpeg_encoder) { > + if (other_agent == remove_agent || !other_agent->video_encoder) { > continue; > } > if (other_agent->client_required_latency > new_max_latency) { > @@ -714,19 +714,19 @@ void dcc_create_stream(DisplayChannelClient *dcc, > Stream *stream) > agent->fps = MAX_FPS; > agent->dcc = dcc; > > - if (dcc->use_mjpeg_encoder_rate_control) { > - MJpegEncoderRateControlCbs mjpeg_cbs; > + if (dcc->use_video_encoder_rate_control) { > + VideoEncoderRateControlCbs video_cbs; > uint64_t initial_bit_rate; > > - mjpeg_cbs.opaque = agent; > - mjpeg_cbs.get_roundtrip_ms = get_roundtrip_ms; > - mjpeg_cbs.get_source_fps = get_source_fps; > - mjpeg_cbs.update_client_playback_delay = > update_client_playback_delay; > + video_cbs.opaque = agent; > + video_cbs.get_roundtrip_ms = get_roundtrip_ms; > + video_cbs.get_source_fps = get_source_fps; > + video_cbs.update_client_playback_delay = > update_client_playback_delay; > > initial_bit_rate = get_initial_bit_rate(dcc, stream); > - agent->mjpeg_encoder = mjpeg_encoder_new(initial_bit_rate, > &mjpeg_cbs); > + agent->video_encoder = mjpeg_encoder_new(initial_bit_rate, > &video_cbs); > } else { > - agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL); > + agent->video_encoder = mjpeg_encoder_new(0, NULL); > } > red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), > &agent->create_item); > > @@ -752,9 +752,9 @@ void stream_agent_stop(StreamAgent *agent) > DisplayChannelClient *dcc = agent->dcc; > > dcc_update_streams_max_latency(dcc, agent); > - if (agent->mjpeg_encoder) { > - mjpeg_encoder_destroy(agent->mjpeg_encoder); > - agent->mjpeg_encoder = NULL; > + if (agent->video_encoder) { > + agent->video_encoder->destroy(agent->video_encoder); > + agent->video_encoder = NULL; > } > } > > diff --git a/server/stream.h b/server/stream.h > index bff7ca7..de6a132 100644 > --- a/server/stream.h > +++ b/server/stream.h > @@ -20,7 +20,7 @@ > > #include <glib.h> > #include "utils.h" > -#include "mjpeg-encoder.h" > +#include "video-encoder.h" > #include "common/region.h" > #include "red-channel.h" > #include "image-cache.h" > @@ -85,7 +85,7 @@ typedef struct StreamAgent { > RedPipeItem destroy_item; > Stream *stream; > uint64_t last_send_time; > - MJpegEncoder *mjpeg_encoder; > + VideoEncoder *video_encoder; > DisplayChannelClient *dcc; > > int frames; > diff --git a/server/video-encoder.h b/server/video-encoder.h > new file mode 100644 > index 0000000..9833e13 > --- /dev/null > +++ b/server/video-encoder.h > @@ -0,0 +1,161 @@ > +/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ > +/* > + Copyright (C) 2009 Red Hat, Inc. > + Copyright (C) 2015 Jeremy White > + Copyright (C) 2015 Francois Gouget > + > + This library is free software; you can redistribute it and/or > + modify it under the terms of the GNU Lesser General Public > + License as published by the Free Software Foundation; either > + version 2.1 of the License, or (at your option) any later version. > + > + This library is distributed in the hope that it will be useful, > + but WITHOUT ANY WARRANTY; without even the implied warranty of > + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + Lesser General Public License for more details. > + > + You should have received a copy of the GNU Lesser General Public > + License along with this library; if not, see > <http://www.gnu.org/licenses/>. > +*/ > + > +#ifndef _H_VIDEO_ENCODER > +#define _H_VIDEO_ENCODER > + > +enum { > + VIDEO_ENCODER_FRAME_UNSUPPORTED = -1, > + VIDEO_ENCODER_FRAME_DROP, > + VIDEO_ENCODER_FRAME_ENCODE_DONE, > +}; > + > +typedef struct VideoEncoderStats { > + uint64_t starting_bit_rate; > + uint64_t cur_bit_rate; > + double avg_quality; > +} VideoEncoderStats; > + > +typedef struct VideoEncoder VideoEncoder; > +struct VideoEncoder { > + /* Releases the video encoder's resources */ > + void (*destroy)(VideoEncoder *encoder); > + > + /* Compresses the specified src image area into the outbuf buffer. > + * > + * @encoder: The video encoder. > + * @frame_mm_time: The frame's mm-time timestamp in milliseconds. > + * @bitmap: The Spice screen. > + * @src: A rectangle specifying the area occupied by the > video. > + * @top_down: If true the first video line is specified by src.top. > + * @outbuf: The buffer for the compressed frame. This must either > + * be NULL or point to a buffer allocated by malloc > + * since it may be reallocated, if its size is too > small. > + * @outbuf_size: The size of the outbuf buffer. > + * @data_size: The size of the compressed frame. > + * @return: > + * VIDEO_ENCODER_FRAME_ENCODE_DONE if successful. > + * VIDEO_ENCODER_FRAME_UNSUPPORTED if the frame cannot be encoded. > + * VIDEO_ENCODER_FRAME_DROP if the frame was dropped. This value can > + * only happen if rate control is active. > + */ > + int (*encode_frame)(VideoEncoder *encoder, uint32_t frame_mm_time, > + const SpiceBitmap *bitmap, int width, int height, > + const SpiceRect *src, int top_down, > + uint8_t **outbuf, size_t *outbuf_size, > + uint32_t *data_size); > + > + /* > + * Bit rate control methods. > + */ > + > + /* When rate control is active statistics are periodically obtained from > + * the client and sent to the video encoder through this method. > + * > + * @encoder: The video encoder. > + * @num_frames: The number of frames that reached the client during > + * the time period the report is referring to. > + * @num_drops: The part of the above frames that was dropped by the > + * client due to late arrival time. > + * @start_frame_mm_time: The mm_time of the first frame included in the > + * report. > + * @end_frame_mm_time: The mm_time of the last frame included in the > + * report. > + * @end_frame_delay: This indicates how long in advance the client > + * received the last frame before having to display it. > + * @audio delay: The latency of the audio playback or MAX_UINT if it > + * is not tracked. > + */ > + void (*client_stream_report)(VideoEncoder *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); > + > + /* This notifies the video encoder each time a frame is dropped due to > + * pipe congestion. > + * > + * Note that frames are being dropped before they are encoded and that > + * there may be any number of encoded frames in the network queue. > + * The client reports provide richer and typically more reactive > + * information for fine tuning the playback parameters but this function > + * provides a fallback when client reports are getting delayed or are > not > + * supported by the client. > + * > + * @encoder: The video encoder. > + */ > + void (*notify_server_frame_drop)(VideoEncoder *encoder); > + > + /* This queries the video encoder's current bit rate. > + * > + * @encoder: The video encoder. > + * @return: The current bit rate in bits per second. > + */ > + uint64_t (*get_bit_rate)(VideoEncoder *encoder); > + > + /* Collects video statistics. > + * > + * @encoder: The video encoder. > + * @stats: A VideoEncoderStats structure to fill with the collected > + * statistics. > + */ > + void (*get_stats)(VideoEncoder *encoder, VideoEncoderStats *stats); > +}; > + > + > +/* When rate control is active the video encoder can use these callbacks to > + * figure out how to adjust the stream bit rate and adjust some stream > + * parameters. > + */ > +typedef struct VideoEncoderRateControlCbs { > + /* The opaque parameter for the callbacks */ > + void *opaque; > + > + /* Returns the stream's estimated roundtrip time in milliseconds. */ > + uint32_t (*get_roundtrip_ms)(void *opaque); > + > + /* Returns the estimated input frame rate. > + * > + * This is the number of frames per second arriving from the guest to > + * spice-server, before any drops. > + */ > + uint32_t (*get_source_fps)(void *opaque); > + > + /* Informs the client of the minimum playback delay. > + * > + * @delay_ms: The minimum number of milliseconds required for the > + * frames to reach the client. > + */ > + void (*update_client_playback_delay)(void *opaque, uint32_t delay_ms); > +} VideoEncoderRateControlCbs; > + > + > +/* Instantiates the video encoder. > + * > + * @starting_bit_rate: An initial estimate of the available stream bit rate > + * or zero if the client does not support rate control. > + * @cbs: A set of callback methods to be used for rate > control. > + * @return: A pointer to a structure implementing the > VideoEncoder > + * methods. > + */ > +VideoEncoder* mjpeg_encoder_new(uint64_t starting_bit_rate, > + VideoEncoderRateControlCbs *cbs); > + > +#endif _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.freedesktop.org/mailman/listinfo/spice-devel