Hi, On Tue, 2016-01-12 at 11:58 +0100, Francois Gouget wrote: > 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> > --- > server/Makefile.am | 2 +- > server/dcc-send.c | 24 ++++---- > server/dcc.c | 24 ++++---- > server/dcc.h | 2 +- > server/mjpeg-encoder.c | 84 +++++++++++++++----------- > server/stream.c | 40 ++++++------- > server/stream.h | 4 +- > server/video-encoder.h | 160 > +++++++++++++++++++++++++++++++++++++++++++++++++ > 8 files changed, 258 insertions(+), 82 deletions(-) > create mode 100644 server/video-encoder.h > > diff --git a/server/Makefile.am b/server/Makefile.am > index 32ab8eb..e2c4e55 100644 > --- a/server/Makefile.am > +++ b/server/Makefile.am > @@ -83,7 +83,6 @@ libspice_server_la_SOURCES = > \ > main-channel.c \ > main-channel.h \ > mjpeg-encoder.c \ > - mjpeg-encoder.h \ > red-channel.c \ > red-channel.h \ > red-common.h \ > @@ -121,6 +120,7 @@ libspice_server_la_SOURCES = > \ > spicevmc.c \ > spice_timer_queue.c \ > spice_timer_queue.h \ > + video-encoder.h \ mjpeg-encoder.h is not used after this patch. Does it make a sense to rename it to video-encoder.h and do the changes on top of it? Reviewed-by: Pavel Grunt <pgrunt@xxxxxxxxxx> Pavel > zlib-encoder.c \ > zlib-encoder.h \ > image-cache.h \ > diff --git a/server/dcc-send.c b/server/dcc-send.c > index c3f79ef..fd11b09 100644 > --- a/server/dcc-send.c > +++ b/server/dcc-send.c > @@ -1691,7 +1691,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 > @@ -1706,25 +1706,25 @@ 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, > + &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); > 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 eb5e4d1..25c18d8 100644 > --- a/server/dcc.c > +++ b/server/dcc.c > @@ -350,7 +350,7 @@ static void > dcc_init_stream_agents(DisplayChannelClient *dcc) > red_channel_pipe_item_init(channel, &agent->create_item, > PIPE_ITEM_TYPE_STREAM_CREATE); > red_channel_pipe_item_init(channel, &agent->destroy_item, > 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); > } > > @@ -474,9 +474,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; > } > } > } > @@ -1319,19 +1319,19 @@ static int > dcc_handle_stream_report(DisplayChannelClient *dcc, > } > > agent = &dcc->stream_agents[report->stream_id]; > - if (!agent->mjpeg_encoder) { > + if (!agent->video_encoder) { > return TRUE; > } > > spice_return_val_if_fail(report->unique_id == agent->report_id, > 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 2a12226..b510254 100644 > --- a/server/dcc.h > +++ b/server/dcc.h > @@ -108,7 +108,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; > }; > diff --git a/server/mjpeg-encoder.c b/server/mjpeg-encoder.c > index 3bd1a04..eb1a333 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,14 +167,14 @@ struct MJpegEncoder { > void (*pixel_converter)(void *src, uint8_t *dest); > > MJpegEncoderRateControl rate_control; > - MJpegEncoderRateControlCbs cbs; > + VideoEncoderRateControlCbs cbs; > void *cbs_opaque; > > /* 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, > @@ -185,8 +186,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); > @@ -725,7 +727,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); > @@ -773,14 +775,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); > @@ -800,7 +802,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, > @@ -924,27 +926,30 @@ 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, > + 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) > { > + 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; > } > > > @@ -1175,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; > @@ -1283,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); > } > @@ -1321,25 +1328,34 @@ 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, > void *cbs_opaque) > { > 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; > @@ -1360,5 +1376,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/stream.c b/server/stream.c > index 3120860..2bfa3a5 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(!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", > @@ -339,7 +339,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; > } > @@ -348,8 +348,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; > } > @@ -362,7 +362,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) { > @@ -595,7 +595,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) > { > @@ -712,18 +712,18 @@ 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.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.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); > + agent->video_encoder = mjpeg_encoder_new(initial_bit_rate, > &video_cbs, agent); > } else { > - agent->mjpeg_encoder = mjpeg_encoder_new(0, NULL, NULL); > + agent->video_encoder = mjpeg_encoder_new(0, NULL, NULL); > } > red_channel_client_pipe_add(RED_CHANNEL_CLIENT(dcc), &agent- > >create_item); > > @@ -749,9 +749,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 a3e84ed..59df9bd 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 { > PipeItem 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..3b1d541 > --- /dev/null > +++ b/server/video-encoder.h > @@ -0,0 +1,160 @@ > +/* -*- 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. > + * @bitmap: The Spice screen. > + * @width: The width of the video area. This always matches > src. > + * @height: The height of the video area. This always matches > src. > + * @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, 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 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 { > + /* 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. > + * @cbs_opaque: A pointer to be passed to the rate control > callbacks. > + * @return: A pointer to a structure implementing the > VideoEncoder > + * methods. > + */ > +VideoEncoder* mjpeg_encoder_new(uint64_t starting_bit_rate, > + VideoEncoderRateControlCbs *cbs, > + void *cbs_opaque); > + > +#endif _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel